summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Herrmann <dh.herrmann at gmail.com>2014-09-10 08:54:23 -0700
committerMartin Peres <martin.peres@linux.intel.com>2017-01-23 10:26:55 +0200
commit7c9be67b0f41f69ef46398593802040b4e4c37c6 (patch)
tree74e0703790de17a3344002cd650a4b1bb07eb361
parent093ae6e2e32a3cbbc6c046df0085d9ca07a602cb (diff)
drm: link connectors to backlight devices
Backlight devices have always been managed independently of display controllers. They're often controlled via different hardware interfaces and their relationship to display-controllers varies vastly between different boards. However, display brightness is obviously a property of a display, and thus of a DRM connector. Therefore, it'd be really appreciated if user-space APIs would highlight this relationship. The main runtime users of backlight interfaces are user-space compositors. But currently they have to jump through hoops to find the correct backlight device for a given connector. Furthermore, they need root privileges to write to sysfs. sysfs has never been designed as run-time non-root API. It does not provide file-contexts, run-time management or any kind of API control. There is no way to control access to sysfs via different links (in that case: mounts). Char-devs provide all this! So far, backlight APIs have been fairly trivial, so adding char-devs to backlights is rather heavy-weight. Therefore, this patch introduces a new API interface to modify backlight brightness via DRM: A "BRIGHTNESS" property on DRM connectors. Instead of adding backlight hardware support to DRM, we rely on the backlight-class and simply add a new API. Each DRM Connector can optionally be linked to a backlight class device. Modifying the connector property will have the same effect as writing into the "brightness" sysfs file of the linked backlight class device. However, we now can manage access to backlight devices via the same interface as access to mode-setting on the underlying display. Furthermore, the connection between displays and their backlight devices are visible in user-space. Obviously, matching backlights to displays cannot be solved magically with this link. Therefore, we also add a user-space attribute to DRM connectors called 'backlight'. If a DRM driver is incapable of matching existing backlights to a connector, or if a given board has just crappy backlight drivers, udev can write the name of a backlight-device into this attribute and the connector-property will be re-linked to this backlight device. The udev hwdb can be easily employed to track such quirks and fixups for different board+GPU combinations. Note that the name written into the 'backlight' attribute is saved on the connector, so in case the real backlight device is probed after the DRM card, the backlight will still get properly attached once probed. V2: Marta Lofstedt <marta.lofstedt@intel.com> - rebase - minor edits for checkpatch errors and warnings - fix drm_backlight_real_changed should use the scaled value in call to drm_object_property_set_value - fix drm_backlight_real_changed should scale to inclusive values of the [0...U16_MAX] interval - fix NULL check before calling strcmp - fix store function should return size Signed-off-by: David Herrmann <dh.herrmann@gmail.com> Signed-off-by: Marta Lofstedt <marta.lofstedt@intel.com>
-rw-r--r--drivers/gpu/drm/Kconfig1
-rw-r--r--drivers/gpu/drm/Makefile2
-rw-r--r--drivers/gpu/drm/drm_backlight.c388
-rw-r--r--drivers/gpu/drm/drm_connector.c13
-rw-r--r--drivers/gpu/drm/drm_drv.c8
-rw-r--r--drivers/gpu/drm/drm_mode_config.c7
-rw-r--r--drivers/gpu/drm/drm_sysfs.c54
-rw-r--r--drivers/video/backlight/backlight.c3
-rw-r--r--include/drm/drm_backlight.h43
-rw-r--r--include/drm/drm_connector.h3
-rw-r--r--include/drm/drm_mode_config.h5
-rw-r--r--include/linux/backlight.h1
12 files changed, 527 insertions, 1 deletions
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 90bc65d07a35..1a88a65e3866 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -13,6 +13,7 @@ menuconfig DRM
select I2C_ALGOBIT
select DMA_SHARED_BUFFER
select SYNC_FILE
+ select BACKLIGHT_CLASS_DEVICE
help
Kernel-level support for the Direct Rendering Infrastructure (DRI)
introduced in XFree86 4.0. If you say Y here, you need to select
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 92de3991fa56..0ef76906d0e5 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -16,7 +16,7 @@ drm-y := drm_auth.o drm_bufs.o drm_cache.o \
drm_framebuffer.o drm_connector.o drm_blend.o \
drm_encoder.o drm_mode_object.o drm_property.o \
drm_plane.o drm_color_mgmt.o drm_print.o \
- drm_dumb_buffers.o drm_mode_config.o
+ drm_dumb_buffers.o drm_mode_config.o drm_backlight.o
drm-$(CONFIG_DRM_LIB_RANDOM) += lib/drm_random.o
drm-$(CONFIG_DRM_VM) += drm_vm.o
diff --git a/drivers/gpu/drm/drm_backlight.c b/drivers/gpu/drm/drm_backlight.c
new file mode 100644
index 000000000000..7ef19f51dcfc
--- /dev/null
+++ b/drivers/gpu/drm/drm_backlight.c
@@ -0,0 +1,388 @@
+/*
+ * DRM Backlight Helpers
+ * Copyright (c) 2014 David Herrmann
+ */
+
+#include <linux/backlight.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/notifier.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <drm/drmP.h>
+#include <drm/drm_backlight.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 generic backlight properties on
+ * connectors. It does not handle any hardware backends but simply forwards
+ * the requests to an available and linked backlight device. The links between
+ * connectors and backlight devices have to be established by DRM drivers and
+ * can be modified by user-space via sysfs (and udev rules). The name of the
+ * backlight device can be written to a sysfs attribute called 'backlight'.
+ * The device is looked up and linked to the connector (replacing a possible
+ * previous backlight device). A 'change' uevent is sent whenever a link is
+ * modified.
+ *
+ * Drivers have to call drm_backlight_alloc() after allocating a connector via
+ * drm_connector_init(). This will automatically add a backlight device to the
+ * given connector. No hardware device is linked to the connector by default.
+ * Drivers can set up a default device via drm_backlight_set_name(), but are
+ * free to leave it empty. User-space will then have to set up the link.
+ */
+
+struct drm_backlight {
+ struct list_head list;
+ struct drm_connector *connector;
+ char *link_name;
+ struct backlight_device *link;
+ struct work_struct work;
+ unsigned int set_value;
+ bool changed : 1;
+};
+
+static LIST_HEAD(drm_backlight_list);
+static DEFINE_SPINLOCK(drm_backlight_lock);
+
+/* caller must hold @drm_backlight_lock */
+static bool __drm_backlight_is_registered(struct drm_backlight *b)
+{
+ /* a device is live if it is linked to @drm_backlight_list */
+ return !list_empty(&b->list);
+}
+
+/* caller must hold @drm_backlight_lock */
+static void __drm_backlight_schedule(struct drm_backlight *b)
+{
+ if (__drm_backlight_is_registered(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 };
+ struct backlight_device *bd;
+ bool send_uevent;
+ unsigned int v;
+
+ spin_lock(&drm_backlight_lock);
+ send_uevent = b->changed;
+ b->changed = false;
+ v = b->set_value;
+ bd = b->link;
+ backlight_device_ref(bd);
+ spin_unlock(&drm_backlight_lock);
+
+ if (bd) {
+ backlight_set_brightness(bd, v, BACKLIGHT_UPDATE_DRM);
+ backlight_device_unref(bd);
+ }
+
+ if (send_uevent)
+ kobject_uevent_env(&b->connector->kdev->kobj, KOBJ_CHANGE, ep);
+}
+
+/* caller must hold @drm_backlight_lock */
+static void __drm_backlight_prop_changed(struct drm_backlight *b, uint64_t v)
+{
+ uint64_t max;
+
+ if (!b->link)
+ return;
+
+ max = b->link->props.max_brightness;
+ if (v >= U16_MAX)
+ b->set_value = max;
+ else
+ b->set_value = (v * max) >> 16;
+ __drm_backlight_schedule(b);
+}
+
+/* caller must hold @drm_backlight_lock */
+static void __drm_backlight_real_changed(struct drm_backlight *b, uint64_t v)
+{
+ struct drm_mode_config *config = &b->connector->dev->mode_config;
+ unsigned int max, set;
+
+ if (!b->link)
+ return;
+
+ set = v;
+ max = b->link->props.max_brightness;
+ if (max < 1)
+ return;
+
+ if (set >= max)
+ set = U16_MAX;
+ else if (max <= U16_MAX)
+ set = v * ((U16_MAX + max + 1)/(max + 1));
+ else
+ set = div_u64(v << 16, max);
+
+ drm_object_property_set_value(&b->connector->base,
+ config->brightness_property, set);
+}
+
+/* caller must hold @drm_backlight_lock */
+static void __drm_backlight_link(struct drm_backlight *b,
+ struct backlight_device *bd)
+{
+ if (bd != b->link) {
+ backlight_device_unref(b->link);
+ b->link = bd;
+ backlight_device_ref(b->link);
+ if (bd)
+ __drm_backlight_real_changed(b, bd->props.brightness);
+ b->changed = true;
+ __drm_backlight_schedule(b);
+ }
+}
+
+/* caller must hold @drm_backlight_lock */
+static void __drm_backlight_lookup(struct drm_backlight *b)
+{
+ struct backlight_device *bd;
+
+ if (b->link_name)
+ bd = backlight_device_lookup(b->link_name);
+ else
+ bd = NULL;
+
+ __drm_backlight_link(b, bd);
+ backlight_device_unref(bd);
+}
+
+/**
+ * drm_backlight_alloc - add backlight capability to a connector
+ * @connector: connector to add backlight to
+ *
+ * This allocates a new DRM-backlight device and links it to @connector. This
+ * *must* be called before registering the connector. The backlight device will
+ * be automatically registered in sync with the connector. It will also get
+ * removed once the connector is removed.
+ *
+ * The connector will not have any hardware backlight linked by default. You
+ * need to call drm_backlight_set_name() if you want to set a default
+ * backlight. User-space can overwrite those via sysfs.
+ *
+ * Returns: 0 on success, negative error code on failure.
+ */
+int drm_backlight_alloc(struct drm_connector *connector)
+{
+ struct drm_mode_config *config = &connector->dev->mode_config;
+ struct drm_backlight *b;
+
+ b = kzalloc(sizeof(*b), GFP_KERNEL);
+ if (!b)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&b->list);
+ INIT_WORK(&b->work, __drm_backlight_worker);
+ b->connector = connector;
+ connector->backlight = b;
+
+ drm_object_attach_property(&connector->base,
+ config->brightness_property, U16_MAX);
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_backlight_alloc);
+
+void drm_backlight_free(struct drm_connector *connector)
+{
+ struct drm_backlight *b = connector->backlight;
+
+ if (b) {
+ WARN_ON(__drm_backlight_is_registered(b));
+ WARN_ON(b->link);
+
+ kfree(b->link_name);
+ kfree(b);
+ connector->backlight = NULL;
+ }
+}
+
+void drm_backlight_register(struct drm_backlight *b)
+{
+ if (!b)
+ return;
+
+ WARN_ON(__drm_backlight_is_registered(b));
+
+ spin_lock(&drm_backlight_lock);
+ list_add(&b->list, &drm_backlight_list);
+ __drm_backlight_lookup(b);
+ spin_unlock(&drm_backlight_lock);
+}
+
+void drm_backlight_unregister(struct drm_backlight *b)
+{
+ if (!b)
+ return;
+
+ WARN_ON(!__drm_backlight_is_registered(b));
+
+ spin_lock(&drm_backlight_lock);
+ list_del_init(&b->list);
+ __drm_backlight_link(b, NULL);
+ spin_unlock(&drm_backlight_lock);
+
+ cancel_work_sync(&b->work);
+}
+
+/**
+ * drm_backlight_get_name - retrieve name of linked backlight device
+ * @b: DRM backlight to retrieve name of
+ * @buf: target buffer for name
+ * @max: size of the target buffer
+ *
+ * This retrieves the name of the backlight device linked to @b and writes it
+ * into @buf. If @buf is NULL or @max is 0, no name will be retrieved, but this
+ * function only tests whether a link is set.
+ * Otherwise, the name will always be written into @buf and will always be
+ * zero-terminated (truncated if too long).
+ *
+ * If no backlight device is linked to @b, this returns -ENOENT. Otherwise, the
+ * length of the written name (excluding the terminating 0 character) is
+ * returned.
+ * Note that if a device name has been set but the underlying backlight device
+ * does not exist, this will still return the linked name. -ENOENT is only
+ * returned if no device name has been set, yet (or has been cleared).
+ *
+ * Returns: On success the length of the written name, on failure a negative
+ * error code.
+ */
+int drm_backlight_get_name(struct drm_backlight *b, char *buf, size_t max)
+{
+ int r;
+
+ spin_lock(&drm_backlight_lock);
+
+ if (!b->link_name) {
+ r = -ENOENT;
+ goto unlock;
+ }
+
+ r = 0;
+ if (buf && max > 0) {
+ r = strlen(b->link_name);
+ if (r + 1 > max)
+ r = max - 1;
+ buf[r] = 0;
+ memcpy(buf, b->link_name, r);
+ }
+
+unlock:
+ spin_unlock(&drm_backlight_lock);
+ return r;
+}
+EXPORT_SYMBOL(drm_backlight_get_name);
+
+/**
+ * drm_backlight_set_name - Change the device link of a DRM backlight
+ * @b: DRM backlight to modify
+ * @name: name of backlight device
+ *
+ * This changes the backlight device-link on @b to the hardware device with
+ * name @name. @name is stored on the backlight device, even if no such
+ * hardware device is registered, yet. If a backlight device appears later on,
+ * it will be automatically linked to all matching DRM backlight devices. If a
+ * real hardware backlight device already exists with such a name, it is linked
+ * with immediate effect.
+ *
+ * Whenever a real hardware backlight is linked or unlinked from a DRM connector
+ * an uevent with "BACKLIGHT=1" is generated on the connector.
+ *
+ * Returns: 0 on success, negative error code on failure.
+ */
+int drm_backlight_set_name(struct drm_backlight *b, const char *name)
+{
+ char *namecopy;
+
+ if (name && *name) {
+ namecopy = kstrdup(name, GFP_KERNEL);
+ if (!namecopy)
+ return -ENOMEM;
+ } else {
+ namecopy = NULL;
+ }
+
+ spin_lock(&drm_backlight_lock);
+
+ kfree(b->link_name);
+ b->link_name = namecopy;
+ if (__drm_backlight_is_registered(b))
+ __drm_backlight_lookup(b);
+
+ spin_unlock(&drm_backlight_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_backlight_set_name);
+
+void drm_backlight_set_brightness(struct drm_backlight *b, uint64_t value)
+{
+ spin_lock(&drm_backlight_lock);
+ __drm_backlight_prop_changed(b, value);
+ spin_unlock(&drm_backlight_lock);
+}
+
+static int drm_backlight_notify(struct notifier_block *self,
+ unsigned long event, void *data)
+{
+ struct backlight_device *bd = data;
+ struct drm_backlight *b;
+ const char *name;
+
+ spin_lock(&drm_backlight_lock);
+
+ switch (event) {
+ case BACKLIGHT_REGISTERED:
+ name = dev_name(&bd->dev);
+ if (!name)
+ break;
+
+ list_for_each_entry(b, &drm_backlight_list, list)
+ if (!b->link && b->link_name &&
+ !strcmp(name, b->link_name))
+ __drm_backlight_link(b, bd);
+
+ break;
+ case BACKLIGHT_UNREGISTERED:
+ list_for_each_entry(b, &drm_backlight_list, list)
+ if (b->link == bd)
+ __drm_backlight_link(b, NULL);
+
+ break;
+ }
+
+ spin_unlock(&drm_backlight_lock);
+
+ 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 799edd0d308e..f94be9adb32a 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -24,6 +24,7 @@
#include <drm/drm_connector.h>
#include <drm/drm_edid.h>
#include <drm/drm_encoder.h>
+#include <drm/drm_backlight.h>
#include "drm_crtc_internal.h"
#include "drm_internal.h"
@@ -323,6 +324,7 @@ void drm_connector_cleanup(struct drm_connector *connector)
struct drm_device *dev = connector->dev;
struct drm_display_mode *mode, *t;
+ drm_backlight_free(connector);
/* The connector should have been removed from userspace long before
* it is finally destroyed.
*/
@@ -383,6 +385,8 @@ int drm_connector_register(struct drm_connector *connector)
if (connector->registered)
goto unlock;
+ drm_backlight_register(connector->backlight);
+
ret = drm_sysfs_connector_add(connector);
if (ret)
goto unlock;
@@ -422,6 +426,9 @@ EXPORT_SYMBOL(drm_connector_register);
void drm_connector_unregister(struct drm_connector *connector)
{
mutex_lock(&connector->mutex);
+
+ drm_backlight_unregister(connector->backlight);
+
if (!connector->registered) {
mutex_unlock(&connector->mutex);
return;
@@ -1092,10 +1099,16 @@ int drm_mode_connector_set_obj_prop(struct drm_mode_object *obj,
{
int ret = -EINVAL;
struct drm_connector *connector = obj_to_connector(obj);
+ struct drm_mode_config *config = &connector->dev->mode_config;
/* Do DPMS ourselves */
if (property == connector->dev->mode_config.dpms_property) {
ret = (*connector->funcs->dpms)(connector, (int)value);
+ } else if (property == config->brightness_property) {
+ if (connector->backlight)
+ drm_backlight_set_brightness(connector->backlight,
+ value);
+ ret = 0;
} else if (connector->funcs->set_property)
ret = connector->funcs->set_property(connector, property, value);
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 72116978ec06..5e0b3d5d00a2 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -35,6 +35,7 @@
#include <drm/drm_drv.h>
#include <drm/drmP.h>
+#include <drm/drm_backlight.h>
#include "drm_crtc_internal.h"
#include "drm_legacy.h"
@@ -899,6 +900,7 @@ static const struct file_operations drm_stub_fops = {
static void drm_core_exit(void)
{
+ drm_backlight_exit();
unregister_chrdev(DRM_MAJOR, "drm");
debugfs_remove(drm_debugfs_root);
drm_sysfs_destroy();
@@ -928,6 +930,12 @@ static int __init drm_core_init(void)
goto error;
}
+ 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 ed1ee5a44a7b..ae156747d8e2 100644
--- a/drivers/gpu/drm/drm_mode_config.c
+++ b/drivers/gpu/drm/drm_mode_config.c
@@ -23,6 +23,7 @@
#include <drm/drm_encoder.h>
#include <drm/drm_mode_config.h>
#include <drm/drmP.h>
+#include <drm/drm_backlight.h>
#include "drm_crtc_internal.h"
#include "drm_internal.h"
@@ -337,6 +338,12 @@ static int drm_mode_create_standard_properties(struct drm_device *dev)
return -ENOMEM;
dev->mode_config.gamma_lut_size_property = prop;
+ prop = drm_property_create_range(dev, DRM_MODE_PROP_RANGE,
+ "BRIGHTNESS", 0, U16_MAX);
+ if (!prop)
+ return -ENOMEM;
+ dev->mode_config.brightness_property = prop;
+
return 0;
}
diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c
index 9a37196c1bf1..88c4e5fed22c 100644
--- a/drivers/gpu/drm/drm_sysfs.c
+++ b/drivers/gpu/drm/drm_sysfs.c
@@ -20,6 +20,7 @@
#include <drm/drm_sysfs.h>
#include <drm/drmP.h>
+#include <drm/drm_backlight.h>
#include "drm_internal.h"
#define to_drm_minor(d) dev_get_drvdata(d)
@@ -215,16 +216,69 @@ static ssize_t modes_show(struct device *device,
return written;
}
+static ssize_t backlight_show(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct drm_connector *connector = to_drm_connector(device);
+ int r;
+
+ if (!connector->backlight)
+ return -ENOTSUPP;
+
+ r = drm_backlight_get_name(connector->backlight, buf, PAGE_SIZE);
+ if (r < 0)
+ return r;
+
+ if (r + 1 < PAGE_SIZE) {
+ buf[r++] = '\n';
+ buf[r] = 0;
+ }
+
+ return r;
+}
+
+static ssize_t backlight_store(struct device *device,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct drm_connector *connector = to_drm_connector(device);
+ const char *t;
+ char *name;
+ size_t len;
+ int r;
+
+ if (!connector->backlight)
+ return -ENOTSUPP;
+
+ t = strchrnul(buf, '\n');
+ len = t - buf;
+
+ name = kstrndup(buf, len, GFP_TEMPORARY);
+ if (!name)
+ return -ENOMEM;
+
+ r = drm_backlight_set_name(connector->backlight, name);
+ kfree(name);
+
+ if (r < 0)
+ return r;
+
+ return size;
+}
+
static DEVICE_ATTR_RW(status);
static DEVICE_ATTR_RO(enabled);
static DEVICE_ATTR_RO(dpms);
static DEVICE_ATTR_RO(modes);
+static DEVICE_ATTR_RW(backlight);
static struct attribute *connector_dev_attrs[] = {
&dev_attr_status.attr,
&dev_attr_enabled.attr,
&dev_attr_dpms.attr,
&dev_attr_modes.attr,
+ &dev_attr_backlight.attr,
NULL
};
diff --git a/drivers/video/backlight/backlight.c b/drivers/video/backlight/backlight.c
index 04e28d5f045f..f241e05d25fb 100644
--- a/drivers/video/backlight/backlight.c
+++ b/drivers/video/backlight/backlight.c
@@ -112,6 +112,9 @@ static void backlight_generate_event(struct backlight_device *bd,
case BACKLIGHT_UPDATE_HOTKEY:
envp[0] = "SOURCE=hotkey";
break;
+ case BACKLIGHT_UPDATE_DRM:
+ envp[0] = "SOURCE=drm";
+ break;
default:
envp[0] = "SOURCE=unknown";
break;
diff --git a/include/drm/drm_backlight.h b/include/drm/drm_backlight.h
new file mode 100644
index 000000000000..17b038411e64
--- /dev/null
+++ b/include/drm/drm_backlight.h
@@ -0,0 +1,43 @@
+#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/kernel.h>
+#include <linux/types.h>
+
+struct drm_backlight;
+struct drm_connector;
+
+int drm_backlight_init(void);
+void drm_backlight_exit(void);
+
+int drm_backlight_alloc(struct drm_connector *connector);
+void drm_backlight_free(struct drm_connector *connector);
+void drm_backlight_register(struct drm_backlight *b);
+void drm_backlight_unregister(struct drm_backlight *b);
+
+int drm_backlight_get_name(struct drm_backlight *b, char *buf, size_t max);
+int drm_backlight_set_name(struct drm_backlight *b, const char *name);
+void drm_backlight_set_brightness(struct drm_backlight *b, uint64_t value);
+#endif /* __DRM_BACKLIGHT_H__ */
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index d489cc003b7e..974ee18afb95 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -759,6 +759,9 @@ struct drm_connector {
uint8_t num_h_tile, num_v_tile;
uint8_t tile_h_loc, tile_v_loc;
uint16_t tile_h_size, tile_v_size;
+
+ /* backlight link */
+ struct drm_backlight *backlight;
};
#define obj_to_connector(x) container_of(x, struct drm_connector, base)
diff --git a/include/drm/drm_mode_config.h b/include/drm/drm_mode_config.h
index fe230f161efc..050340bd9ff6 100644
--- a/include/drm/drm_mode_config.h
+++ b/include/drm/drm_mode_config.h
@@ -641,6 +641,11 @@ struct drm_mode_config {
* the position of the output on the host's screen.
*/
struct drm_property *suggested_y_property;
+ /**
+ * @brightness_property: Default connector property to control the
+ * connector's backlight brightness.
+ */
+ struct drm_property *brightness_property;
/* dumb ioctl parameters */
uint32_t preferred_depth, prefer_shadow;
diff --git a/include/linux/backlight.h b/include/linux/backlight.h
index 5adb2ea7e163..8456fabbbcf8 100644
--- a/include/linux/backlight.h
+++ b/include/linux/backlight.h
@@ -31,6 +31,7 @@
enum backlight_update_reason {
BACKLIGHT_UPDATE_HOTKEY,
BACKLIGHT_UPDATE_SYSFS,
+ BACKLIGHT_UPDATE_DRM,
};
enum backlight_type {