On 13/07/17 16:51, Kieran Bingham wrote: > Hi Laurent, > > I've just seen Maxime's latest series "[PATCH 0/4] drm/sun4i: Fix a register > access bug" and it relates directly to a comment I had in this patch: > > On 12/07/17 17:35, Kieran Bingham wrote: >> Hi Laurent, >> >> On 28/06/17 19:50, Laurent Pinchart wrote: >>> Commit 52055bafa1ff ("drm: rcar-du: Move plane commit code from CRTC >>> start to CRTC resume") changed the order of the plane commit and CRTC >>> enable operations to accommodate the runtime PM requirements. However, >>> this introduced corruption in the first displayed frame, as the CRTC is >>> now enabled without any plane configured. On Gen2 hardware the first >>> frame will be black and likely unnoticed, but on Gen3 hardware we end up >>> starting the display before the VSP compositor, which is more >>> noticeable. >>> >>> To fix this, revert the order of the commit operations back, and handle >>> runtime PM requirements in the CRTC .atomic_begin() and .atomic_enable() >>> helper operation handlers. >>> >>> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@xxxxxxxxxxxxxxxx> >> >> I only have code reduction or comment suggestions below - so either with or >> without those changes, feel free to add my: >> >> Reviewed-by: Kieran Bingham <kieran.bingham+renesas@xxxxxxxxxxxxxxxx> >> >>> --- >>> drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 66 ++++++++++++++++++++-------------- >>> drivers/gpu/drm/rcar-du/rcar_du_crtc.h | 4 +-- >>> drivers/gpu/drm/rcar-du/rcar_du_kms.c | 2 +- >>> 3 files changed, 43 insertions(+), 29 deletions(-) >>> >>> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c >>> index 6b5219ef0ad2..76cdb88b2b8e 100644 >>> --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c >>> +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c >>> @@ -448,14 +448,8 @@ static void rcar_du_crtc_wait_page_flip(struct rcar_du_crtc *rcrtc) >>> * Start/Stop and Suspend/Resume >>> */ >>> >>> -static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc) >>> +static void rcar_du_crtc_setup(struct rcar_du_crtc *rcrtc) >>> { >>> - struct drm_crtc *crtc = &rcrtc->crtc; >>> - bool interlaced; >>> - >>> - if (rcrtc->started) >>> - return; >>> - >>> /* Set display off and background to black */ >>> rcar_du_crtc_write(rcrtc, DOOR, DOOR_RGB(0, 0, 0)); >>> rcar_du_crtc_write(rcrtc, BPOR, BPOR_RGB(0, 0, 0)); >>> @@ -467,6 +461,18 @@ static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc) >>> /* Start with all planes disabled. */ >>> rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? DS2PR : DS1PR, 0); >>> >>> + /* Enable the VSP compositor. */ >>> + if (rcar_du_has(rcrtc->group->dev, RCAR_DU_FEATURE_VSP1_SOURCE)) >>> + rcar_du_vsp_enable(rcrtc); >>> + >>> + /* Turn vertical blanking interrupt reporting on. */ >>> + drm_crtc_vblank_on(&rcrtc->crtc); >>> +} >>> + >>> +static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc) >>> +{ >>> + bool interlaced; >>> + >>> /* Select master sync mode. This enables display operation in master >> >> Are we close enough here to fix this multiline comment style ? >> (Not worth doing unless the patch is respun for other reasons ...) >> >> Actually - there are a lot in this file, so it would be better to do them all in >> one hit/patch at a point of least conflicts ... >> >> >>> * sync mode (with the HSYNC and VSYNC signals configured as outputs and >>> * actively driven). >>> @@ -477,24 +483,12 @@ static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc) >>> DSYSR_TVM_MASTER); >>> >>> rcar_du_group_start_stop(rcrtc->group, true); >>> - >>> - /* Enable the VSP compositor. */ >>> - if (rcar_du_has(rcrtc->group->dev, RCAR_DU_FEATURE_VSP1_SOURCE)) >>> - rcar_du_vsp_enable(rcrtc); >>> - >>> - /* Turn vertical blanking interrupt reporting back on. */ >>> - drm_crtc_vblank_on(crtc); >>> - >>> - rcrtc->started = true; >>> } >>> >>> static void rcar_du_crtc_stop(struct rcar_du_crtc *rcrtc) >>> { >>> struct drm_crtc *crtc = &rcrtc->crtc; >>> >>> - if (!rcrtc->started) >>> - return; >>> - >>> /* Disable all planes and wait for the change to take effect. This is >>> * required as the DSnPR registers are updated on vblank, and no vblank >>> * will occur once the CRTC is stopped. Disabling planes when starting >>> @@ -525,8 +519,6 @@ static void rcar_du_crtc_stop(struct rcar_du_crtc *rcrtc) >>> rcar_du_crtc_clr_set(rcrtc, DSYSR, DSYSR_TVM_MASK, DSYSR_TVM_SWITCH); >>> >>> rcar_du_group_start_stop(rcrtc->group, false); >>> - >>> - rcrtc->started = false; >>> } >>> >>> void rcar_du_crtc_suspend(struct rcar_du_crtc *rcrtc) >>> @@ -546,12 +538,10 @@ void rcar_du_crtc_resume(struct rcar_du_crtc *rcrtc) >>> return; >>> >>> rcar_du_crtc_get(rcrtc); >>> - rcar_du_crtc_start(rcrtc); >>> + rcar_du_crtc_setup(rcrtc); >> >> Every call to _setup is immediately prefixed by a call to _get() >> >> Could the _get() be done in the _setup() for code reduction? >> >> I'm entirely open to that not happening here as it might be preferred to keep >> the _get() and _start() separate for style purposes. >> >>> >>> /* Commit the planes state. */ >>> - if (rcar_du_has(rcrtc->group->dev, RCAR_DU_FEATURE_VSP1_SOURCE)) { >>> - rcar_du_vsp_enable(rcrtc); >>> - } else { >>> + if (!rcar_du_has(rcrtc->group->dev, RCAR_DU_FEATURE_VSP1_SOURCE)) { >>> for (i = 0; i < rcrtc->group->num_planes; ++i) { >>> struct rcar_du_plane *plane = &rcrtc->group->planes[i]; >>> >>> @@ -563,6 +553,7 @@ void rcar_du_crtc_resume(struct rcar_du_crtc *rcrtc) >>> } >>> >>> rcar_du_crtc_update_planes(rcrtc); >>> + rcar_du_crtc_start(rcrtc); >>> } >>> >>> /* ----------------------------------------------------------------------------- >>> @@ -574,7 +565,16 @@ static void rcar_du_crtc_atomic_enable(struct drm_crtc *crtc, >>> { >>> struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); >>> >>> - rcar_du_crtc_get(rcrtc); >>> + /* >>> + * If the CRTC has already been setup by the .atomic_begin() handler we >>> + * can skip the setup stage. >>> + */ >>> + if (!rcrtc->initialized) { >>> + rcar_du_crtc_get(rcrtc); >>> + rcar_du_crtc_setup(rcrtc); >>> + rcrtc->initialized = true; >>> + } >>> + >>> rcar_du_crtc_start(rcrtc); >>> } >>> >>> @@ -593,6 +593,7 @@ static void rcar_du_crtc_atomic_disable(struct drm_crtc *crtc, >>> } >>> spin_unlock_irq(&crtc->dev->event_lock); >>> >>> + rcrtc->initialized = false; >>> rcrtc->outputs = 0; >>> } >>> >>> @@ -601,6 +602,19 @@ static void rcar_du_crtc_atomic_begin(struct drm_crtc *crtc, >>> { >>> struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); >>> >>> + WARN_ON(!crtc->state->enable); >> >> Is this necessary if it's handled by the rcrtc->initialized flag? or is it so >> that we find out if it happens ? >> >> (Or is this due to the re-ordering of the _commit_tail() function below?) >> >> >>> + >>> + /* >>> + * If a mode set is in progress we can be called with the CRTC disabled. >>> + * We then need to first setup the CRTC in order to configure planes. >>> + * The .atomic_enable() handler will notice and skip the CRTC setup. >>> + */ >> >> I'm assuming this comment is the reason for the WARN_ON above ... >> >> >>> + if (!rcrtc->initialized) { >>> + rcar_du_crtc_get(rcrtc); >>> + rcar_du_crtc_setup(rcrtc); >>> + rcrtc->initialized = true; >>> + } >> >> >> If the _get() was moved into the _setup(), and _setup() was protected by the >> rcrtc->initialized flag, then _atomic_begin() _enable() and _resume() could all >> just simply call _setup(). The _resume() should only ever be called with >> rcrtc->initialized = false anyway, as that is set in _suspend() >> >>> + >>> if (rcar_du_has(rcrtc->group->dev, RCAR_DU_FEATURE_VSP1_SOURCE)) >>> rcar_du_vsp_atomic_begin(rcrtc); >>> } >>> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h >>> index 0b6d26ecfc38..3cc376826592 100644 >>> --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h >>> +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h >>> @@ -30,7 +30,7 @@ struct rcar_du_vsp; >>> * @extclock: external pixel dot clock (optional) >>> * @mmio_offset: offset of the CRTC registers in the DU MMIO block >>> * @index: CRTC software and hardware index >>> - * @started: whether the CRTC has been started and is running >>> + * @initialized: whether the CRTC has been initialized and clocks enabled >>> * @event: event to post when the pending page flip completes >>> * @flip_wait: wait queue used to signal page flip completion >>> * @outputs: bitmask of the outputs (enum rcar_du_output) driven by this CRTC >>> @@ -45,7 +45,7 @@ struct rcar_du_crtc { >>> struct clk *extclock; >>> unsigned int mmio_offset; >>> unsigned int index; >>> - bool started; >>> + bool initialized; >>> >>> struct drm_pending_vblank_event *event; >>> wait_queue_head_t flip_wait; >>> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c >>> index 82b978a5dae6..c2f382feca07 100644 >>> --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c >>> +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c >>> @@ -255,9 +255,9 @@ static void rcar_du_atomic_commit_tail(struct drm_atomic_state *old_state) >>> >>> /* Apply the atomic update. */ >>> drm_atomic_helper_commit_modeset_disables(dev, old_state); >>> - drm_atomic_helper_commit_modeset_enables(dev, old_state); >>> drm_atomic_helper_commit_planes(dev, old_state, >>> DRM_PLANE_COMMIT_ACTIVE_ONLY); >> >> Except for DRM_PLANE_COMMIT_ACTIVE_ONLY, this function now looks very much like >> the default drm_atomic_helper_commit_tail() code. >> >> Reading around other uses /variants of commit_tail() style functions in other >> drivers has left me confused as to how the ordering affects things here. >> >> Could be worth adding a comment at least to describe why we can't use the >> default helper... > > Or better still ... Use Maxime's new : > > [PATCH 1/4] drm/atomic: implement drm_atomic_helper_commit_tail for runtime_pm users Never mind - I've just looked again, and seen that this new helper function is the ordering previous to *this* patch, and therefore isn't the same. However - it's worth noting that Maxime's patch converts this function to the new helper - which contradicts this patch's motivations. >> >> >>> + drm_atomic_helper_commit_modeset_enables(dev, old_state); >>> > drm_atomic_helper_commit_hw_done(old_state); >>> drm_atomic_helper_wait_for_vblanks(dev, old_state); >>>