[PATCH 02/10] drm: Add backlight helper

Noralf Trønnes noralf at tronnes.org
Wed Apr 29 12:48:22 UTC 2020


This adds a function that creates a backlight device for a connector.
It does not deal with the KMS backlight ABI proposition[1] to add a
connector property. It only takes the current best practise to standardise
the creation of a backlight device for DRM drivers while we wait for the
property.

The brightness value is set using a connector state variable and an atomic
commit.

I have looked through some of the backlight users and this is what I've found:

GNOME [2]
---------

Brightness range: 0-100
Scale: Assumes perceptual

Avoids setting the sysfs brightness value to zero if max_brightness >= 99.
Can connect connector and backlight using the sysfs device.

KDE [3]
-------

Brightness range: 0-100
Scale: Assumes perceptual

Weston [4]
----------

Brightness range: 0-255
Scale: Assumes perceptual

Chromium OS [5]
---------------

Brightness range: 0-100
Scale: Depends on the sysfs file 'scale' which is a recent addition (2019)

xserver [6]
-----------

Brightness range: 0-x (driver specific) (1 is minimum, 0 is OFF)
Scale: Assumes perceptual

The builtin modesetting driver[7] does not support Backlight, Intel[8] does.

[1] https://lore.kernel.org/dri-devel/4b17ba08-39f3-57dd-5aad-d37d844b02c6@linux.intel.com/
[2] https://gitlab.gnome.org/GNOME/gnome-settings-daemon/-/blob/master/plugins/power/gsd-backlight.c
[3] https://github.com/KDE/powerdevil/blob/master/daemon/backends/upower/backlighthelper.cpp
[4] https://gitlab.freedesktop.org/wayland/weston/-/blob/master/libweston/backend-drm/drm.c
[5] https://chromium.googlesource.com/chromiumos/platform2/+/refs/heads/master/power_manager/powerd/system/internal_backlight.cc
[6] https://github.com/freedesktop/xorg-randrproto/blob/master/randrproto.txt
[7] https://gitlab.freedesktop.org/xorg/xserver/-/blob/master/hw/xfree86/drivers/modesetting/drmmode_display.c
[8] https://gitlab.freedesktop.org/xorg/driver/xf86-video-intel/-/blob/master/src/backlight.c

Cc: Hans de Goede <hdegoede at redhat.com>
Cc: Jani Nikula <jani.nikula at linux.intel.com>
Cc: Martin Peres <martin.peres at linux.intel.com>
Cc: Daniel Thompson <daniel.thompson at linaro.org>
Signed-off-by: Noralf Trønnes <noralf at tronnes.org>
---
 Documentation/gpu/drm-kms-helpers.rst  |   6 +
 drivers/gpu/drm/Kconfig                |   7 ++
 drivers/gpu/drm/Makefile               |   1 +
 drivers/gpu/drm/drm_backlight_helper.c | 154 +++++++++++++++++++++++++
 include/drm/drm_backlight_helper.h     |   9 ++
 include/drm/drm_connector.h            |  10 ++
 6 files changed, 187 insertions(+)
 create mode 100644 drivers/gpu/drm/drm_backlight_helper.c
 create mode 100644 include/drm/drm_backlight_helper.h

diff --git a/Documentation/gpu/drm-kms-helpers.rst b/Documentation/gpu/drm-kms-helpers.rst
index 9668a7fe2408..29a2f4b49fd2 100644
--- a/Documentation/gpu/drm-kms-helpers.rst
+++ b/Documentation/gpu/drm-kms-helpers.rst
@@ -411,3 +411,9 @@ SHMEM GEM Helper Reference
 
 .. kernel-doc:: drivers/gpu/drm/drm_gem_shmem_helper.c
    :export:
+
+Backlight Helper Reference
+==========================
+
+.. kernel-doc:: drivers/gpu/drm/drm_backlight_helper.c
+   :export:
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index d0aa6cff2e02..f6e13e18c9ca 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -224,6 +224,13 @@ config DRM_GEM_SHMEM_HELPER
 	help
 	  Choose this if you need the GEM shmem helper functions
 
+config DRM_BACKLIGHT_HELPER
+	bool
+	depends on DRM
+	select BACKLIGHT_CLASS_DEVICE
+	help
+	  Choose this if you need the backlight device helper functions
+
 config DRM_VM
 	bool
 	depends on DRM && MMU
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 6493088a0fdd..0d17662dde0a 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -52,6 +52,7 @@ drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o
 drm_kms_helper-$(CONFIG_DRM_KMS_CMA_HELPER) += drm_fb_cma_helper.o
 drm_kms_helper-$(CONFIG_DRM_DP_AUX_CHARDEV) += drm_dp_aux_dev.o
 drm_kms_helper-$(CONFIG_DRM_DP_CEC) += drm_dp_cec.o
+drm_kms_helper-$(CONFIG_DRM_BACKLIGHT_HELPER) += drm_backlight_helper.o
 
 obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o
 obj-$(CONFIG_DRM_DEBUG_SELFTEST) += selftests/
diff --git a/drivers/gpu/drm/drm_backlight_helper.c b/drivers/gpu/drm/drm_backlight_helper.c
new file mode 100644
index 000000000000..06e6a75d1d0a
--- /dev/null
+++ b/drivers/gpu/drm/drm_backlight_helper.c
@@ -0,0 +1,154 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * Copyright 2020 Noralf Trønnes
+ */
+
+#include <linux/backlight.h>
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_file.h>
+
+static int drm_backlight_update_status(struct backlight_device *bd)
+{
+	struct drm_connector *connector = bl_get_data(bd);
+	struct drm_connector_state *connector_state;
+	struct drm_device *dev = connector->dev;
+	struct drm_modeset_acquire_ctx ctx;
+	struct drm_atomic_state *state;
+	int ret;
+
+	state = drm_atomic_state_alloc(dev);
+	if (!state)
+		return -ENOMEM;
+
+	drm_modeset_acquire_init(&ctx, 0);
+	state->acquire_ctx = &ctx;
+retry:
+	connector_state = drm_atomic_get_connector_state(state, connector);
+	if (IS_ERR(connector_state)) {
+		ret = PTR_ERR(connector_state);
+		goto out;
+	}
+
+	connector_state->backlight_brightness = bd->props.brightness;
+
+	ret = drm_atomic_commit(state);
+out:
+	if (ret == -EDEADLK) {
+		drm_atomic_state_clear(state);
+		drm_modeset_backoff(&ctx);
+		goto retry;
+	}
+
+	drm_atomic_state_put(state);
+
+	drm_modeset_drop_locks(&ctx);
+	drm_modeset_acquire_fini(&ctx);
+
+	return ret;
+}
+
+static int drm_backlight_get_brightness(struct backlight_device *bd)
+{
+	struct drm_connector *connector = bl_get_data(bd);
+	struct drm_device *dev = connector->dev;
+	int brightness;
+
+	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
+	brightness = connector->state->backlight_brightness;
+	drm_modeset_unlock(&dev->mode_config.connection_mutex);
+
+	return brightness;
+}
+
+static const struct backlight_ops drm_backlight_ops = {
+	.get_brightness = drm_backlight_get_brightness,
+	.update_status	= drm_backlight_update_status,
+};
+
+/* Can be exported for drivers carrying a legacy name */
+static int drm_backlight_register_with_name(struct drm_connector *connector, const char *name)
+{
+	struct backlight_device *bd;
+	const struct backlight_properties props = {
+		.type = BACKLIGHT_RAW,
+		.scale = BACKLIGHT_SCALE_NON_LINEAR,
+		.max_brightness = 100,
+	};
+
+	if (WARN_ON(!drm_core_check_feature(connector->dev, DRIVER_MODESET) ||
+		    !drm_drv_uses_atomic_modeset(connector->dev) ||
+		    !connector->kdev))
+		return -EINVAL;
+
+	bd = backlight_device_register(name, connector->kdev, connector,
+				       &drm_backlight_ops, &props);
+	if (IS_ERR(bd))
+		return PTR_ERR(bd);
+
+	connector->backlight = bd;
+
+	return 0;
+}
+
+/**
+ * drm_backlight_register() - Register a backlight device for a connector
+ * @connector: Connector
+ *
+ * This function registers a backlight device for @connector with the following
+ * characteristics:
+ *
+ * - The connector sysfs device is used as a parent device for the backlight device.
+ *   Userspace can use this to connect backlight device and connector.
+ * - Name will be on the form: **card0-HDMI-A-1-backlight**
+ * - Type is "raw"
+ * - Scale is "non-linear" (perceptual)
+ * - Max brightness is 100 giving a range of 0-100 inclusive
+ * - Reading sysfs **brightness** returns the backlight device property
+ * - Reading sysfs **actual_brightness** returns the connector state value
+ * - Writing sysfs **bl_power** is ignored, the DPMS connector property should
+ *   be used to control power.
+ * - Backlight device suspend/resume events are ignored.
+ *
+ * Note:
+ *
+ * Brightness zero should not turn off backlight it should be the minimum
+ * brightness, DPMS handles power.
+ *
+ * This function must be called from &drm_connector_funcs->late_register() since
+ * it depends on the sysfs device.
+ *
+ * Returns:
+ * Zero on success or negative error code on failure.
+ */
+int drm_backlight_register(struct drm_connector *connector)
+{
+	const char *name = NULL;
+	int ret;
+
+	name = kasprintf(GFP_KERNEL, "card%d-%s-backlight",
+			 connector->dev->primary->index, connector->name);
+	if (!name)
+		return -ENOMEM;
+
+	ret = drm_backlight_register_with_name(connector, name);
+	kfree(name);
+
+	return ret;
+}
+EXPORT_SYMBOL(drm_backlight_register);
+
+/**
+ * drm_backlight_unregister() - Unregister backlight device
+ * @connector: Connector
+ *
+ * Unregister a backlight device. This must be called from the
+ * &drm_connector_funcs->early_unregister() callback.
+ */
+void drm_backlight_unregister(struct drm_connector *connector)
+{
+	backlight_device_unregister(connector->backlight);
+}
+EXPORT_SYMBOL(drm_backlight_unregister);
diff --git a/include/drm/drm_backlight_helper.h b/include/drm/drm_backlight_helper.h
new file mode 100644
index 000000000000..4151b66eb0b4
--- /dev/null
+++ b/include/drm/drm_backlight_helper.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+
+#ifndef _LINUX_DRM_BACKLIGHT_HELPER_H
+#define _LINUX_DRM_BACKLIGHT_HELPER_H
+
+int drm_backlight_register(struct drm_connector *connector);
+void drm_backlight_unregister(struct drm_connector *connector);
+
+#endif
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index 221910948b37..ce678b694f45 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -32,6 +32,7 @@
 
 #include <uapi/drm/drm_mode.h>
 
+struct backlight_device;
 struct drm_connector_helper_funcs;
 struct drm_modeset_acquire_ctx;
 struct drm_device;
@@ -656,6 +657,12 @@ struct drm_connector_state {
 	 */
 	u8 max_bpc;
 
+	/**
+	 * @backlight_brightness: Brightness value of the connector backlight
+	 * device. See drm_backlight_register().
+	 */
+	u8 backlight_brightness;
+
 	/**
 	 * @hdr_output_metadata:
 	 * DRM blob property for HDR output metadata
@@ -1422,6 +1429,9 @@ struct drm_connector {
 
 	/** @hdr_sink_metadata: HDR Metadata Information read from sink */
 	struct hdr_sink_metadata hdr_sink_metadata;
+
+	/** @backlight: Backlight device. See drm_backlight_register() */
+	struct backlight_device *backlight;
 };
 
 #define obj_to_connector(x) container_of(x, struct drm_connector, base)
-- 
2.23.0



More information about the dri-devel mailing list