Skip to content

Commit

Permalink
drm/imx: add deferred plane disabling
Browse files Browse the repository at this point in the history
The DP (display processor) channel disable code tried to busy wait for
the DP sync flow end interrupt status bit when disabling the partial
plane without a full modeset. That never worked reliably, and it was
disabled completely by the recent "gpu: ipu-v3: remove IRQ dance on DC
channel disable" patch, causing ipu_wait_interrupt to always time out
after 50 ms, which in turn would trigger a timeout in
drm_atomic_helper_wait_for_vblanks.

This patch changes ipu_plane_atomic_disable to only queue a DP channel
register update at the next frame boundary and set a flag, which can be
done without any waiting whatsoever. The imx_drm_atomic_commit_tail then
calls a new ipu_plane_disable_deferred function that does the actual
IDMAC teardown of the planes that are flagged for deferred disabling,
after waiting for the vblank.

Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
Reviewed-by: Lucas Stach <l.stach@pengutronix.de>
  • Loading branch information
Philipp Zabel committed Mar 15, 2017
1 parent cf92fef commit eb8c888
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 11 deletions.
18 changes: 18 additions & 0 deletions drivers/gpu/drm/imx/imx-drm-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include <video/imx-ipu-v3.h>

#include "imx-drm.h"
#include "ipuv3-plane.h"

#define MAX_CRTC 4

Expand Down Expand Up @@ -122,6 +123,10 @@ static const struct drm_mode_config_funcs imx_drm_mode_config_funcs = {
static void imx_drm_atomic_commit_tail(struct drm_atomic_state *state)
{
struct drm_device *dev = state->dev;
struct drm_plane *plane;
struct drm_plane_state *old_plane_state;
bool plane_disabling = false;
int i;

drm_atomic_helper_commit_modeset_disables(dev, state);

Expand All @@ -131,6 +136,19 @@ static void imx_drm_atomic_commit_tail(struct drm_atomic_state *state)

drm_atomic_helper_commit_modeset_enables(dev, state);

for_each_plane_in_state(state, plane, old_plane_state, i) {
if (drm_atomic_plane_disabling(old_plane_state, plane->state))
plane_disabling = true;
}

if (plane_disabling) {
drm_atomic_helper_wait_for_vblanks(dev, state);

for_each_plane_in_state(state, plane, old_plane_state, i)
ipu_plane_disable_deferred(plane);

}

drm_atomic_helper_commit_hw_done(state);
}

Expand Down
22 changes: 21 additions & 1 deletion drivers/gpu/drm/imx/ipuv3-crtc.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,26 @@ static void ipu_crtc_enable(struct drm_crtc *crtc)
ipu_di_enable(ipu_crtc->di);
}

static void ipu_crtc_disable_planes(struct ipu_crtc *ipu_crtc,
struct drm_crtc_state *old_crtc_state)
{
bool disable_partial = false;
bool disable_full = false;
struct drm_plane *plane;

drm_atomic_crtc_state_for_each_plane(plane, old_crtc_state) {
if (plane == &ipu_crtc->plane[0]->base)
disable_full = true;
if (&ipu_crtc->plane[1] && plane == &ipu_crtc->plane[1]->base)
disable_partial = true;
}

if (disable_partial)
ipu_plane_disable(ipu_crtc->plane[1], true);
if (disable_full)
ipu_plane_disable(ipu_crtc->plane[0], false);
}

static void ipu_crtc_atomic_disable(struct drm_crtc *crtc,
struct drm_crtc_state *old_crtc_state)
{
Expand All @@ -73,7 +93,7 @@ static void ipu_crtc_atomic_disable(struct drm_crtc *crtc,
* attached IDMACs will be left in undefined state, possibly hanging
* the IPU or even system.
*/
drm_atomic_helper_disable_planes_on_crtc(old_crtc_state, false);
ipu_crtc_disable_planes(ipu_crtc, old_crtc_state);
ipu_dc_disable(ipu);

spin_lock_irq(&crtc->dev->event_lock);
Expand Down
25 changes: 18 additions & 7 deletions drivers/gpu/drm/imx/ipuv3-plane.c
Original file line number Diff line number Diff line change
Expand Up @@ -172,23 +172,30 @@ static void ipu_plane_enable(struct ipu_plane *ipu_plane)
ipu_dp_enable_channel(ipu_plane->dp);
}

static int ipu_disable_plane(struct drm_plane *plane)
void ipu_plane_disable(struct ipu_plane *ipu_plane, bool disable_dp_channel)
{
struct ipu_plane *ipu_plane = to_ipu_plane(plane);

DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);

ipu_idmac_wait_busy(ipu_plane->ipu_ch, 50);

if (ipu_plane->dp)
ipu_dp_disable_channel(ipu_plane->dp, true);
if (ipu_plane->dp && disable_dp_channel)
ipu_dp_disable_channel(ipu_plane->dp, false);
ipu_idmac_disable_channel(ipu_plane->ipu_ch);
ipu_dmfc_disable_channel(ipu_plane->dmfc);
if (ipu_plane->dp)
ipu_dp_disable(ipu_plane->ipu);
}

return 0;
void ipu_plane_disable_deferred(struct drm_plane *plane)
{
struct ipu_plane *ipu_plane = to_ipu_plane(plane);

if (ipu_plane->disabling) {
ipu_plane->disabling = false;
ipu_plane_disable(ipu_plane, false);
}
}
EXPORT_SYMBOL_GPL(ipu_plane_disable_deferred);

static void ipu_plane_destroy(struct drm_plane *plane)
{
Expand Down Expand Up @@ -356,7 +363,11 @@ static int ipu_plane_atomic_check(struct drm_plane *plane,
static void ipu_plane_atomic_disable(struct drm_plane *plane,
struct drm_plane_state *old_state)
{
ipu_disable_plane(plane);
struct ipu_plane *ipu_plane = to_ipu_plane(plane);

if (ipu_plane->dp)
ipu_dp_disable_channel(ipu_plane->dp, true);
ipu_plane->disabling = true;
}

static void ipu_plane_atomic_update(struct drm_plane *plane,
Expand Down
5 changes: 5 additions & 0 deletions drivers/gpu/drm/imx/ipuv3-plane.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ struct ipu_plane {

int dma;
int dp_flow;

bool disabling;
};

struct ipu_plane *ipu_plane_init(struct drm_device *dev, struct ipu_soc *ipu,
Expand All @@ -42,4 +44,7 @@ void ipu_plane_put_resources(struct ipu_plane *plane);

int ipu_plane_irq(struct ipu_plane *plane);

void ipu_plane_disable(struct ipu_plane *ipu_plane, bool disable_dp_channel);
void ipu_plane_disable_deferred(struct drm_plane *plane);

#endif
3 changes: 0 additions & 3 deletions drivers/gpu/ipu-v3/ipu-dp.c
Original file line number Diff line number Diff line change
Expand Up @@ -277,9 +277,6 @@ void ipu_dp_disable_channel(struct ipu_dp *dp, bool sync)
writel(0, flow->base + DP_FG_POS);
ipu_srm_dp_update(priv->ipu, sync);

if (ipu_idmac_channel_busy(priv->ipu, IPUV3_CHANNEL_MEM_BG_SYNC))
ipu_wait_interrupt(priv->ipu, IPU_IRQ_DP_SF_END, 50);

mutex_unlock(&priv->mutex);
}
EXPORT_SYMBOL_GPL(ipu_dp_disable_channel);
Expand Down

0 comments on commit eb8c888

Please sign in to comment.