drm/imx: handle pending updates better, add plane zpos property support

- Add a mechanism to only send commit done events once all pending
   updates have been applied. This closes a small race window where
   already armed events could fire even though the double buffered
   hardware update just missed the update window.
 - Add plane zpos property support to allow placing the overlay plane
   behind the primary plane.
 - Allow building imx-drm on all platforms under COMPILE_TEST.
 -----BEGIN PGP SIGNATURE-----
 
 iI4EABYIADYWIQRRO6F6WdpH1R0vGibVhaclGDdiwAUCXG/bAhgccGhpbGlwcC56
 YWJlbEBnbWFpbC5jb20ACgkQ1YWnJRg3YsAz6wEA4ThGF7nvouYeI2jLEuLmkIpr
 wXdyGA5XScfoSQUgFisBAPGl+g578KjIq7PrazKAEoKeencLytlf8mWCl99YZukB
 =Veu5
 -----END PGP SIGNATURE-----

Merge tag 'imx-drm-next-2019-02-22' of git://git.pengutronix.de/pza/linux into drm-next

drm/imx: handle pending updates better, add plane zpos property support

- Add a mechanism to only send commit done events once all pending
  updates have been applied. This closes a small race window where
  already armed events could fire even though the double buffered
  hardware update just missed the update window.
- Add plane zpos property support to allow placing the overlay plane
  behind the primary plane.
- Allow building imx-drm on all platforms under COMPILE_TEST.

Signed-off-by: Dave Airlie <airlied@redhat.com>

From: Philipp Zabel <pza@pengutronix.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20190222112350.m3ucezilqx6cyest@pengutronix.de
This commit is contained in:
Dave Airlie 2019-02-28 12:53:11 +10:00
commit 7fbd5d784f
9 changed files with 109 additions and 33 deletions

View File

@ -4,7 +4,7 @@ config DRM_IMX
select VIDEOMODE_HELPERS
select DRM_GEM_CMA_HELPER
select DRM_KMS_CMA_HELPER
depends on DRM && (ARCH_MXC || ARCH_MULTIPLATFORM)
depends on DRM && (ARCH_MXC || ARCH_MULTIPLATFORM || COMPILE_TEST)
depends on IMX_IPUV3_CORE
help
enable i.MX graphics support
@ -18,6 +18,7 @@ config DRM_IMX_PARALLEL_DISPLAY
config DRM_IMX_TVE
tristate "Support for TV and VGA displays"
depends on DRM_IMX
depends on COMMON_CLK
select REGMAP_MMIO
help
Choose this to enable the internal Television Encoder (TVe)

View File

@ -49,11 +49,7 @@ static int imx_drm_atomic_check(struct drm_device *dev,
{
int ret;
ret = drm_atomic_helper_check_modeset(dev, state);
if (ret)
return ret;
ret = drm_atomic_helper_check_planes(dev, state);
ret = drm_atomic_helper_check(dev, state);
if (ret)
return ret;
@ -229,6 +225,7 @@ static int imx_drm_bind(struct device *dev)
drm->mode_config.funcs = &imx_drm_mode_config_funcs;
drm->mode_config.helper_private = &imx_drm_mode_config_helpers;
drm->mode_config.allow_fb_modifiers = true;
drm->mode_config.normalize_zpos = true;
drm_mode_config_init(drm);

View File

@ -34,6 +34,7 @@ struct ipu_crtc {
struct ipu_dc *dc;
struct ipu_di *di;
int irq;
struct drm_pending_vblank_event *event;
};
static inline struct ipu_crtc *to_ipu_crtc(struct drm_crtc *crtc)
@ -173,8 +174,31 @@ static const struct drm_crtc_funcs ipu_crtc_funcs = {
static irqreturn_t ipu_irq_handler(int irq, void *dev_id)
{
struct ipu_crtc *ipu_crtc = dev_id;
struct drm_crtc *crtc = &ipu_crtc->base;
unsigned long flags;
int i;
drm_crtc_handle_vblank(&ipu_crtc->base);
drm_crtc_handle_vblank(crtc);
if (ipu_crtc->event) {
for (i = 0; i < ARRAY_SIZE(ipu_crtc->plane); i++) {
struct ipu_plane *plane = ipu_crtc->plane[i];
if (!plane)
continue;
if (ipu_plane_atomic_update_pending(&plane->base))
break;
}
if (i == ARRAY_SIZE(ipu_crtc->plane)) {
spin_lock_irqsave(&crtc->dev->event_lock, flags);
drm_crtc_send_vblank_event(crtc, ipu_crtc->event);
ipu_crtc->event = NULL;
drm_crtc_vblank_put(crtc);
spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
}
}
return IRQ_HANDLED;
}
@ -223,8 +247,10 @@ static void ipu_crtc_atomic_flush(struct drm_crtc *crtc,
{
spin_lock_irq(&crtc->dev->event_lock);
if (crtc->state->event) {
struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
WARN_ON(drm_crtc_vblank_get(crtc));
drm_crtc_arm_vblank_event(crtc, crtc->state->event);
ipu_crtc->event = crtc->state->event;
crtc->state->event = NULL;
}
spin_unlock_irq(&crtc->dev->event_lock);

View File

@ -273,6 +273,7 @@ static void ipu_plane_destroy(struct drm_plane *plane)
static void ipu_plane_state_reset(struct drm_plane *plane)
{
unsigned int zpos = (plane->type == DRM_PLANE_TYPE_PRIMARY) ? 0 : 1;
struct ipu_plane_state *ipu_state;
if (plane->state) {
@ -284,8 +285,11 @@ static void ipu_plane_state_reset(struct drm_plane *plane)
ipu_state = kzalloc(sizeof(*ipu_state), GFP_KERNEL);
if (ipu_state)
if (ipu_state) {
__drm_atomic_helper_plane_reset(plane, &ipu_state->base);
ipu_state->base.zpos = zpos;
ipu_state->base.normalized_zpos = zpos;
}
}
static struct drm_plane_state *
@ -560,6 +564,25 @@ static void ipu_plane_atomic_update(struct drm_plane *plane,
if (ipu_plane->dp_flow == IPU_DP_FLOW_SYNC_FG)
ipu_dp_set_window_pos(ipu_plane->dp, dst->x1, dst->y1);
switch (ipu_plane->dp_flow) {
case IPU_DP_FLOW_SYNC_BG:
if (state->normalized_zpos == 1) {
ipu_dp_set_global_alpha(ipu_plane->dp,
!fb->format->has_alpha, 0xff,
true);
} else {
ipu_dp_set_global_alpha(ipu_plane->dp, true, 0, true);
}
break;
case IPU_DP_FLOW_SYNC_FG:
if (state->normalized_zpos == 1) {
ipu_dp_set_global_alpha(ipu_plane->dp,
!fb->format->has_alpha, 0xff,
false);
}
break;
}
eba = drm_plane_state_to_eba(state, 0);
/*
@ -582,6 +605,7 @@ static void ipu_plane_atomic_update(struct drm_plane *plane,
active = ipu_idmac_get_current_buffer(ipu_plane->ipu_ch);
ipu_cpmem_set_buffer(ipu_plane->ipu_ch, !active, eba);
ipu_idmac_select_buffer(ipu_plane->ipu_ch, !active);
ipu_plane->next_buf = !active;
if (ipu_plane_separate_alpha(ipu_plane)) {
active = ipu_idmac_get_current_buffer(ipu_plane->alpha_ch);
ipu_cpmem_set_buffer(ipu_plane->alpha_ch, !active,
@ -595,34 +619,11 @@ static void ipu_plane_atomic_update(struct drm_plane *plane,
switch (ipu_plane->dp_flow) {
case IPU_DP_FLOW_SYNC_BG:
ipu_dp_setup_channel(ipu_plane->dp, ics, IPUV3_COLORSPACE_RGB);
ipu_dp_set_global_alpha(ipu_plane->dp, true, 0, true);
break;
case IPU_DP_FLOW_SYNC_FG:
ipu_dp_setup_channel(ipu_plane->dp, ics,
IPUV3_COLORSPACE_UNKNOWN);
/* Enable local alpha on partial plane */
switch (fb->format->format) {
case DRM_FORMAT_ARGB1555:
case DRM_FORMAT_ABGR1555:
case DRM_FORMAT_RGBA5551:
case DRM_FORMAT_BGRA5551:
case DRM_FORMAT_ARGB4444:
case DRM_FORMAT_ARGB8888:
case DRM_FORMAT_ABGR8888:
case DRM_FORMAT_RGBA8888:
case DRM_FORMAT_BGRA8888:
case DRM_FORMAT_RGB565_A8:
case DRM_FORMAT_BGR565_A8:
case DRM_FORMAT_RGB888_A8:
case DRM_FORMAT_BGR888_A8:
case DRM_FORMAT_RGBX8888_A8:
case DRM_FORMAT_BGRX8888_A8:
ipu_dp_set_global_alpha(ipu_plane->dp, false, 0, false);
break;
default:
ipu_dp_set_global_alpha(ipu_plane->dp, true, 0, true);
break;
}
break;
}
ipu_dmfc_config_wait4eot(ipu_plane->dmfc, drm_rect_width(dst));
@ -709,6 +710,7 @@ static void ipu_plane_atomic_update(struct drm_plane *plane,
ipu_cpmem_set_buffer(ipu_plane->ipu_ch, 1, eba);
ipu_idmac_lock_enable(ipu_plane->ipu_ch, num_bursts);
ipu_plane_enable(ipu_plane);
ipu_plane->next_buf = -1;
}
static const struct drm_plane_helper_funcs ipu_plane_helper_funcs = {
@ -718,6 +720,24 @@ static const struct drm_plane_helper_funcs ipu_plane_helper_funcs = {
.atomic_update = ipu_plane_atomic_update,
};
bool ipu_plane_atomic_update_pending(struct drm_plane *plane)
{
struct ipu_plane *ipu_plane = to_ipu_plane(plane);
struct drm_plane_state *state = plane->state;
struct ipu_plane_state *ipu_state = to_ipu_plane_state(state);
/* disabled crtcs must not block the update */
if (!state->crtc)
return false;
if (ipu_state->use_pre)
return ipu_prg_channel_configure_pending(ipu_plane->ipu_ch);
else if (ipu_plane->next_buf >= 0)
return ipu_idmac_get_current_buffer(ipu_plane->ipu_ch) !=
ipu_plane->next_buf;
return false;
}
int ipu_planes_assign_pre(struct drm_device *dev,
struct drm_atomic_state *state)
{
@ -806,6 +826,7 @@ struct ipu_plane *ipu_plane_init(struct drm_device *dev, struct ipu_soc *ipu,
{
struct ipu_plane *ipu_plane;
const uint64_t *modifiers = ipu_format_modifiers;
unsigned int zpos = (type == DRM_PLANE_TYPE_PRIMARY) ? 0 : 1;
int ret;
DRM_DEBUG_KMS("channel %d, dp flow %d, possible_crtcs=0x%x\n",
@ -836,5 +857,10 @@ struct ipu_plane *ipu_plane_init(struct drm_device *dev, struct ipu_soc *ipu,
drm_plane_helper_add(&ipu_plane->base, &ipu_plane_helper_funcs);
if (dp == IPU_DP_FLOW_SYNC_BG || dp == IPU_DP_FLOW_SYNC_FG)
drm_plane_create_zpos_property(&ipu_plane->base, zpos, 0, 1);
else
drm_plane_create_zpos_immutable_property(&ipu_plane->base, 0);
return ipu_plane;
}

View File

@ -27,6 +27,7 @@ struct ipu_plane {
int dp_flow;
bool disabling;
int next_buf;
};
struct ipu_plane *ipu_plane_init(struct drm_device *dev, struct ipu_soc *ipu,
@ -48,5 +49,6 @@ 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);
bool ipu_plane_atomic_update_pending(struct drm_plane *plane);
#endif

View File

@ -265,6 +265,12 @@ void ipu_pre_update(struct ipu_pre *pre, unsigned int bufaddr)
writel(IPU_PRE_CTRL_SDW_UPDATE, pre->regs + IPU_PRE_CTRL_SET);
}
bool ipu_pre_update_pending(struct ipu_pre *pre)
{
return !!(readl_relaxed(pre->regs + IPU_PRE_CTRL) &
IPU_PRE_CTRL_SDW_UPDATE);
}
u32 ipu_pre_get_baddr(struct ipu_pre *pre)
{
return (u32)pre->buffer_paddr;

View File

@ -347,6 +347,22 @@ int ipu_prg_channel_configure(struct ipuv3_channel *ipu_chan,
}
EXPORT_SYMBOL_GPL(ipu_prg_channel_configure);
bool ipu_prg_channel_configure_pending(struct ipuv3_channel *ipu_chan)
{
int prg_chan = ipu_prg_ipu_to_prg_chan(ipu_chan->num);
struct ipu_prg *prg = ipu_chan->ipu->prg_priv;
struct ipu_prg_channel *chan;
if (prg_chan < 0)
return false;
chan = &prg->chan[prg_chan];
WARN_ON(!chan->enabled);
return ipu_pre_update_pending(prg->pres[chan->used_pre]);
}
EXPORT_SYMBOL_GPL(ipu_prg_channel_configure_pending);
static int ipu_prg_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;

View File

@ -272,6 +272,7 @@ void ipu_pre_configure(struct ipu_pre *pre, unsigned int width,
unsigned int height, unsigned int stride, u32 format,
uint64_t modifier, unsigned int bufaddr);
void ipu_pre_update(struct ipu_pre *pre, unsigned int bufaddr);
bool ipu_pre_update_pending(struct ipu_pre *pre);
struct ipu_prg *ipu_prg_lookup_by_phandle(struct device *dev, const char *name,
int ipu_id);

View File

@ -348,6 +348,7 @@ int ipu_prg_channel_configure(struct ipuv3_channel *ipu_chan,
unsigned int axi_id, unsigned int width,
unsigned int height, unsigned int stride,
u32 format, uint64_t modifier, unsigned long *eba);
bool ipu_prg_channel_configure_pending(struct ipuv3_channel *ipu_chan);
/*
* IPU CMOS Sensor Interface (csi) functions