Sorry-I've been waiting to review this, but the DPCD backlight support helper series is -still- blocked on getting reviews upstream :\ On Tue, 2021-05-25 at 13:00 +0530, Rajeev Nandan wrote: > 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); > -- Cheers, Lyude Paul (she/her) Software Engineer at Red Hat