Download raw body.
sys/qcdpc: blank eDP panels during suspend
A note below.
> My HONOR Snapdragon panel remains light up when AUX backlight enable is
> cleared; also request black video during quiesce; save the original display
> control register and restore it on wake, preserving brightness and the
> firmware configured display link.
>
> Ok?
>
> Index: sys/dev/fdt/qcdpc.c
> ===================================================================
> RCS file: /home/cvs/src/sys/dev/fdt/qcdpc.c,v
> diff -u -p -r1.2 qcdpc.c
> --- sys/dev/fdt/qcdpc.c 9 Mar 2026 23:57:53 -0000 1.2
> +++ sys/dev/fdt/qcdpc.c 5 Oct 2026 00:50:24 -0000
> @@ -71,15 +71,19 @@ struct qcdpc_softc {
>
> struct drm_edp_backlight_info sc_bl;
> uint32_t sc_bl_level;
> + uint8_t sc_bl_control;
> + int sc_bl_suspended;
> };
>
> struct qcdpc_softc *qcdpc_bl;
>
> int qcdpc_match(struct device *, void *, void *);
> void qcdpc_attach(struct device *, struct device *, void *);
> +int qcdpc_activate(struct device *, int);
>
> const struct cfattach qcdpc_ca = {
> - sizeof(struct qcdpc_softc), qcdpc_match, qcdpc_attach
> + sizeof(struct qcdpc_softc), qcdpc_match, qcdpc_attach,
> + NULL, qcdpc_activate
> };
>
> struct cfdriver qcdpc_cd = {
> @@ -174,6 +178,47 @@ qcdpc_attach(struct device *parent, stru
> fa.fa_node = node;
> config_found(self, &fa, qcdpc_print);
> }
> +}
> +
> +int
> +qcdpc_activate(struct device *self, int act)
> +{
> + struct qcdpc_softc *sc = (struct qcdpc_softc *)self;
> + int ret;
> +
> + if (sc != qcdpc_bl || !sc->sc_bl.aux_enable)
> + return config_activate_children(self, act);
> +
> + switch (act) {
> + case DVACT_QUIESCE:
> + ret = config_activate_children(self, act);
> + if (ret)
> + return ret;
> +
> + if (!sc->sc_bl_suspended) {
> + ret = drm_dp_dpcd_read_byte(&sc->sc_aux,
> + DP_EDP_DISPLAY_CONTROL_REGISTER, &sc->sc_bl_control);
> + if (ret < 0)
> + return ret;
> + sc->sc_bl_suspended = 1;
> + }
> +
> + return drm_dp_dpcd_write_byte(&sc->sc_aux,
> + DP_EDP_DISPLAY_CONTROL_REGISTER,
> + (sc->sc_bl_control & ~DP_EDP_BACKLIGHT_ENABLE) |
> + DP_EDP_BLACK_VIDEO_ENABLE);
^^ please don't return early like this. Drop out of the loop and...
> +
> + case DVACT_WAKEUP:
> + if (sc->sc_bl_suspended) {
> + ret = drm_dp_dpcd_write_byte(&sc->sc_aux,
> + DP_EDP_DISPLAY_CONTROL_REGISTER, sc->sc_bl_control);
> + if (ret == 0)
> + sc->sc_bl_suspended = 0;
> + }
> + break;
> + }
allow it to reach this:
> +
> + return config_activate_children(self, act);
> }
The reasons are:
In the early days, all the activate routines were written differently,
and we missed so many children sub-tree walks, it was just ridiculous.
So I unified as many as possible to follow this idiom of switch (),
handle children, only return children value.
Secondly, I don't know what drm_dp_dpcd_write_byte() returns. If it
returns non-zero, the DVACT_QUIESCE step of suspend is cancelled.
The device-tree walking function unravels and does a terrible job of
repairing things. If you improved the unravel routine, you would
find that the activate functions are still not written correctly for
halfway recovery. It is a mess. But there is a strategy. Always
try your best to let all the routines run, and if some step doesn't
do the right thing, that's ok you'll fix it when the machine returns
because the resume code is supposed to be paranoid and "work hard"
to repair the machine.
Thirdly, this is rare, but people have forgotten that some devices
have children. And sometimes drivers were refactored to have
children added later! So always call config_activate_children().
So that should almost assuredly be
(void) drm_dp_dpcd_write_byte(&sc->sc_aux,
DP_EDP_DISPLAY_CONTROL_REGISTER,
(sc->sc_bl_control & ~DP_EDP_BACKLIGHT_ENABLE) |
DP_EDP_BLACK_VIDEO_ENABLE);
break;
sys/qcdpc: blank eDP panels during suspend