[v4 1/4] drm/panel-simple: Add basic DPCD backlight support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Add basic support of panel backlight control over eDP aux channel
using VESA's standard backlight control interface.

Signed-off-by: Rajeev Nandan <rajeevny@xxxxxxxxxxxxxx>
---

This patch depends on [1] (drm/panel: panel-simple: Stash DP AUX bus; 
allow using it for DDC) 

Changes in v4:
- New

[1] https://lore.kernel.org/dri-devel/20210524165920.v8.7.I18e60221f6d048d14d6c50a770b15f356fa75092@changeid/

 drivers/gpu/drm/panel/panel-simple.c | 99 ++++++++++++++++++++++++++++++++++--
 1 file changed, 96 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c
index b09be6e..f9e4e60 100644
--- a/drivers/gpu/drm/panel/panel-simple.c
+++ b/drivers/gpu/drm/panel/panel-simple.c
@@ -21,6 +21,7 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
+#include <linux/backlight.h>
 #include <linux/delay.h>
 #include <linux/gpio/consumer.h>
 #include <linux/iopoll.h>
@@ -171,6 +172,19 @@ struct panel_desc {
 
 	/** @connector_type: LVDS, eDP, DSI, DPI, etc. */
 	int connector_type;
+
+	/**
+	 * @uses_dpcd_backlight: Panel supports eDP dpcd backlight control.
+	 *
+	 * Set true, if the panel supports backlight control over eDP AUX channel
+	 * using DPCD registers as per VESA's standard.
+	 */
+	bool uses_dpcd_backlight;
+};
+
+struct edp_backlight {
+	struct backlight_device *dev;
+	struct drm_edp_backlight_info info;
 };
 
 struct panel_simple {
@@ -194,6 +208,8 @@ struct panel_simple {
 
 	struct edid *edid;
 
+	struct edp_backlight *edp_bl;
+
 	struct drm_display_mode override_mode;
 
 	enum drm_panel_orientation orientation;
@@ -330,10 +346,14 @@ static void panel_simple_wait(ktime_t start_ktime, unsigned int min_ms)
 static int panel_simple_disable(struct drm_panel *panel)
 {
 	struct panel_simple *p = to_panel_simple(panel);
+	struct edp_backlight *bl = p->edp_bl;
 
 	if (!p->enabled)
 		return 0;
 
+	if (p->desc->uses_dpcd_backlight && bl)
+		drm_edp_backlight_disable(p->aux, &bl->info);
+
 	if (p->desc->delay.disable)
 		msleep(p->desc->delay.disable);
 
@@ -496,6 +516,7 @@ static int panel_simple_prepare(struct drm_panel *panel)
 static int panel_simple_enable(struct drm_panel *panel)
 {
 	struct panel_simple *p = to_panel_simple(panel);
+	struct edp_backlight *bl = p->edp_bl;
 
 	if (p->enabled)
 		return 0;
@@ -505,6 +526,10 @@ static int panel_simple_enable(struct drm_panel *panel)
 
 	panel_simple_wait(p->prepared_time, p->desc->delay.prepare_to_enable);
 
+	if (p->desc->uses_dpcd_backlight && bl)
+		drm_edp_backlight_enable(p->aux, &bl->info,
+					 bl->dev->props.brightness);
+
 	p->enabled = true;
 
 	return 0;
@@ -565,6 +590,59 @@ static const struct drm_panel_funcs panel_simple_funcs = {
 	.get_timings = panel_simple_get_timings,
 };
 
+static int edp_backlight_update_status(struct backlight_device *bd)
+{
+	struct panel_simple *p = bl_get_data(bd);
+	struct edp_backlight *bl = p->edp_bl;
+
+	if (!p->enabled)
+		return 0;
+
+	return drm_edp_backlight_set_level(p->aux, &bl->info, bd->props.brightness);
+}
+
+static const struct backlight_ops edp_backlight_ops = {
+	.update_status = edp_backlight_update_status,
+};
+
+static int edp_backlight_register(struct device *dev, struct panel_simple *panel)
+{
+	struct edp_backlight *bl;
+	struct backlight_properties props = { 0 };
+	u16 current_level;
+	u8 current_mode;
+	u8 edp_dpcd[EDP_DISPLAY_CTL_CAP_SIZE];
+	int ret;
+
+	bl = devm_kzalloc(dev, sizeof(*bl), GFP_KERNEL);
+	if (!bl)
+		return -ENOMEM;
+
+	ret = drm_dp_dpcd_read(panel->aux, DP_EDP_DPCD_REV, edp_dpcd,
+			       EDP_DISPLAY_CTL_CAP_SIZE);
+	if (ret < 0)
+		return ret;
+
+	ret = drm_edp_backlight_init(panel->aux, &bl->info, 0, edp_dpcd,
+				     &current_level, &current_mode);
+	if (ret < 0)
+		return ret;
+
+	props.type = BACKLIGHT_RAW;
+	props.brightness = current_level;
+	props.max_brightness = bl->info.max;
+
+	bl->dev = devm_backlight_device_register(dev, "edp_backlight",
+						dev, panel,
+						&edp_backlight_ops, &props);
+	if (IS_ERR(bl->dev))
+		return PTR_ERR(bl->dev);
+
+	panel->edp_bl = bl;
+
+	return 0;
+}
+
 static struct panel_desc panel_dpi;
 
 static int panel_dpi_probe(struct device *dev,
@@ -796,9 +874,24 @@ static int panel_simple_probe(struct device *dev, const struct panel_desc *desc,
 
 	drm_panel_init(&panel->base, dev, &panel_simple_funcs, connector_type);
 
-	err = drm_panel_of_backlight(&panel->base);
-	if (err)
-		goto disable_pm_runtime;
+	if (panel->desc->uses_dpcd_backlight) {
+		if (!panel->aux) {
+			dev_err(dev, "edp backlight needs DP aux\n");
+			err = -EINVAL;
+			goto disable_pm_runtime;
+		}
+
+		err = edp_backlight_register(dev, panel);
+		if (err) {
+			dev_err(dev, "failed to register edp backlight %d\n", err);
+			goto disable_pm_runtime;
+		}
+
+	} else {
+		err = drm_panel_of_backlight(&panel->base);
+		if (err)
+			goto disable_pm_runtime;
+	}
 
 	drm_panel_add(&panel->base);
 
-- 
2.7.4




[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]


  Powered by Linux