[v4 1/4] drm/panel-simple: Add basic DPCD backlight support
From: Rajeev Nandan
Date: Tue May 25 2021 - 03:31:31 EST
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,
+ ¤t_level, ¤t_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