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