Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 33f14235 authored by Liu Ying's avatar Liu Ying Committed by Philipp Zabel
Browse files

drm/imx: atomic phase 1: Use transitional atomic CRTC and plane helpers



Use the drm_plane_helper_update/disable() and drm_helper_crtc_mode_set()
transitional atomic helpers.  The crtc->mode_set_nofb callback is added
so that the primary plane is no longer tied to the CRTC.  Check/update
logics are separated to make sure crtc->mode_set_nofb and plane->atomic_update
are always successful.  Also, some necessary logics are tweaked for a smooth
transition.

Signed-off-by: default avatarLiu Ying <gnuiyl@gmail.com>
Acked-by: default avatarDaniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: default avatarPhilipp Zabel <p.zabel@pengutronix.de>
parent d7868cb7
Loading
Loading
Loading
Loading
+103 −94
Original line number Diff line number Diff line
@@ -73,7 +73,7 @@ struct ipu_crtc {

#define to_ipu_crtc(x) container_of(x, struct ipu_crtc, base)

static void ipu_fb_enable(struct ipu_crtc *ipu_crtc)
static void ipu_crtc_enable(struct ipu_crtc *ipu_crtc)
{
	struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent);

@@ -81,30 +81,30 @@ static void ipu_fb_enable(struct ipu_crtc *ipu_crtc)
		return;

	ipu_dc_enable(ipu);
	ipu_plane_enable(ipu_crtc->plane[0]);
	/* Start DC channel and DI after IDMAC */
	ipu_dc_enable_channel(ipu_crtc->dc);
	ipu_di_enable(ipu_crtc->di);
	drm_crtc_vblank_on(&ipu_crtc->base);

	ipu_crtc->enabled = 1;

	/*
	 * In order not to be warned on enabling vblank failure,
	 * we should call drm_crtc_vblank_on() after ->enabled is set to 1.
	 */
	drm_crtc_vblank_on(&ipu_crtc->base);
}

static void ipu_fb_disable(struct ipu_crtc *ipu_crtc)
static void ipu_crtc_disable(struct ipu_crtc *ipu_crtc)
{
	struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent);

	if (!ipu_crtc->enabled)
		return;

	/* Stop DC channel and DI before IDMAC */
	ipu_dc_disable_channel(ipu_crtc->dc);
	ipu_di_disable(ipu_crtc->di);
	ipu_plane_disable(ipu_crtc->plane[0]);
	ipu_dc_disable(ipu);
	drm_crtc_vblank_off(&ipu_crtc->base);

	ipu_crtc->enabled = 0;

	drm_crtc_vblank_off(&ipu_crtc->base);
}

static void ipu_crtc_dpms(struct drm_crtc *crtc, int mode)
@@ -115,12 +115,12 @@ static void ipu_crtc_dpms(struct drm_crtc *crtc, int mode)

	switch (mode) {
	case DRM_MODE_DPMS_ON:
		ipu_fb_enable(ipu_crtc);
		ipu_crtc_enable(ipu_crtc);
		break;
	case DRM_MODE_DPMS_STANDBY:
	case DRM_MODE_DPMS_SUSPEND:
	case DRM_MODE_DPMS_OFF:
		ipu_fb_disable(ipu_crtc);
		ipu_crtc_disable(ipu_crtc);
		break;
	}
}
@@ -234,79 +234,6 @@ static const struct drm_crtc_funcs ipu_crtc_funcs = {
	.page_flip = ipu_page_flip,
};

static int ipu_crtc_mode_set(struct drm_crtc *crtc,
			       struct drm_display_mode *orig_mode,
			       struct drm_display_mode *mode,
			       int x, int y,
			       struct drm_framebuffer *old_fb)
{
	struct drm_device *dev = crtc->dev;
	struct drm_encoder *encoder;
	struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
	struct ipu_di_signal_cfg sig_cfg = {};
	unsigned long encoder_types = 0;
	int ret;

	dev_dbg(ipu_crtc->dev, "%s: mode->hdisplay: %d\n", __func__,
			mode->hdisplay);
	dev_dbg(ipu_crtc->dev, "%s: mode->vdisplay: %d\n", __func__,
			mode->vdisplay);

	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
		if (encoder->crtc == crtc)
			encoder_types |= BIT(encoder->encoder_type);

	dev_dbg(ipu_crtc->dev, "%s: attached to encoder types 0x%lx\n",
		__func__, encoder_types);

	/*
	 * If we have DAC or LDB, then we need the IPU DI clock to be
	 * the same as the LDB DI clock. For TVDAC, derive the IPU DI
	 * clock from 27 MHz TVE_DI clock, but allow to divide it.
	 */
	if (encoder_types & (BIT(DRM_MODE_ENCODER_DAC) |
			     BIT(DRM_MODE_ENCODER_LVDS)))
		sig_cfg.clkflags = IPU_DI_CLKMODE_SYNC | IPU_DI_CLKMODE_EXT;
	else if (encoder_types & BIT(DRM_MODE_ENCODER_TVDAC))
		sig_cfg.clkflags = IPU_DI_CLKMODE_EXT;
	else
		sig_cfg.clkflags = 0;

	sig_cfg.enable_pol = !(ipu_crtc->bus_flags & DRM_BUS_FLAG_DE_LOW);
	/* Default to driving pixel data on negative clock edges */
	sig_cfg.clk_pol = !!(ipu_crtc->bus_flags &
			     DRM_BUS_FLAG_PIXDATA_POSEDGE);
	sig_cfg.bus_format = ipu_crtc->bus_format;
	sig_cfg.v_to_h_sync = 0;
	sig_cfg.hsync_pin = ipu_crtc->di_hsync_pin;
	sig_cfg.vsync_pin = ipu_crtc->di_vsync_pin;

	drm_display_mode_to_videomode(mode, &sig_cfg.mode);

	ret = ipu_dc_init_sync(ipu_crtc->dc, ipu_crtc->di,
			       mode->flags & DRM_MODE_FLAG_INTERLACE,
			       ipu_crtc->bus_format, mode->hdisplay);
	if (ret) {
		dev_err(ipu_crtc->dev,
				"initializing display controller failed with %d\n",
				ret);
		return ret;
	}

	ret = ipu_di_init_sync_panel(ipu_crtc->di, &sig_cfg);
	if (ret) {
		dev_err(ipu_crtc->dev,
				"initializing panel failed with %d\n", ret);
		return ret;
	}

	return ipu_plane_mode_set(ipu_crtc->plane[0], crtc, mode,
				  crtc->primary->fb,
				  0, 0, mode->hdisplay, mode->vdisplay,
				  x, y, mode->hdisplay, mode->vdisplay,
				  mode->flags & DRM_MODE_FLAG_INTERLACE);
}

static void ipu_crtc_handle_pageflip(struct ipu_crtc *ipu_crtc)
{
	unsigned long flags;
@@ -330,8 +257,7 @@ static irqreturn_t ipu_irq_handler(int irq, void *dev_id)
	if (ipu_crtc->flip_state == IPU_FLIP_SUBMITTED) {
		struct ipu_plane *plane = ipu_crtc->plane[0];

		ipu_plane_set_base(plane, ipu_crtc->base.primary->fb,
				   plane->x, plane->y);
		ipu_plane_set_base(plane, ipu_crtc->base.primary->fb);
		ipu_crtc_handle_pageflip(ipu_crtc);
		queue_work(ipu_crtc->flip_queue,
			   &ipu_crtc->flip_work->unref_work);
@@ -355,6 +281,9 @@ static bool ipu_crtc_mode_fixup(struct drm_crtc *crtc,
	if (ret)
		return false;

	if ((vm.vsync_len == 0) || (vm.hsync_len == 0))
		return false;

	drm_display_mode_from_videomode(&vm, adjusted_mode);

	return true;
@@ -364,28 +293,95 @@ static void ipu_crtc_prepare(struct drm_crtc *crtc)
{
	struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);

	ipu_fb_disable(ipu_crtc);
	ipu_crtc_disable(ipu_crtc);
}

static void ipu_crtc_commit(struct drm_crtc *crtc)
{
	struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);

	ipu_fb_enable(ipu_crtc);
	ipu_crtc_enable(ipu_crtc);
}

static int ipu_crtc_atomic_check(struct drm_crtc *crtc,
				 struct drm_crtc_state *state)
{
	return 0;
}

static void ipu_crtc_mode_set_nofb(struct drm_crtc *crtc)
{
	struct drm_device *dev = crtc->dev;
	struct drm_encoder *encoder;
	struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
	struct ipu_di_signal_cfg sig_cfg = {};
	unsigned long encoder_types = 0;

	dev_dbg(ipu_crtc->dev, "%s: mode->hdisplay: %d\n", __func__,
			mode->hdisplay);
	dev_dbg(ipu_crtc->dev, "%s: mode->vdisplay: %d\n", __func__,
			mode->vdisplay);

	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
		if (encoder->crtc == crtc)
			encoder_types |= BIT(encoder->encoder_type);

	dev_dbg(ipu_crtc->dev, "%s: attached to encoder types 0x%lx\n",
		__func__, encoder_types);

	/*
	 * If we have DAC or LDB, then we need the IPU DI clock to be
	 * the same as the LDB DI clock. For TVDAC, derive the IPU DI
	 * clock from 27 MHz TVE_DI clock, but allow to divide it.
	 */
	if (encoder_types & (BIT(DRM_MODE_ENCODER_DAC) |
			     BIT(DRM_MODE_ENCODER_LVDS)))
		sig_cfg.clkflags = IPU_DI_CLKMODE_SYNC | IPU_DI_CLKMODE_EXT;
	else if (encoder_types & BIT(DRM_MODE_ENCODER_TVDAC))
		sig_cfg.clkflags = IPU_DI_CLKMODE_EXT;
	else
		sig_cfg.clkflags = 0;

	sig_cfg.enable_pol = !(ipu_crtc->bus_flags & DRM_BUS_FLAG_DE_LOW);
	/* Default to driving pixel data on negative clock edges */
	sig_cfg.clk_pol = !!(ipu_crtc->bus_flags &
			     DRM_BUS_FLAG_PIXDATA_POSEDGE);
	sig_cfg.bus_format = ipu_crtc->bus_format;
	sig_cfg.v_to_h_sync = 0;
	sig_cfg.hsync_pin = ipu_crtc->di_hsync_pin;
	sig_cfg.vsync_pin = ipu_crtc->di_vsync_pin;

	drm_display_mode_to_videomode(mode, &sig_cfg.mode);

	ipu_dc_init_sync(ipu_crtc->dc, ipu_crtc->di,
			 mode->flags & DRM_MODE_FLAG_INTERLACE,
			 ipu_crtc->bus_format, mode->hdisplay);
	ipu_di_init_sync_panel(ipu_crtc->di, &sig_cfg);
}

static const struct drm_crtc_helper_funcs ipu_helper_funcs = {
	.dpms = ipu_crtc_dpms,
	.mode_fixup = ipu_crtc_mode_fixup,
	.mode_set = ipu_crtc_mode_set,
	.mode_set = drm_helper_crtc_mode_set,
	.mode_set_nofb = ipu_crtc_mode_set_nofb,
	.prepare = ipu_crtc_prepare,
	.commit = ipu_crtc_commit,
	.atomic_check = ipu_crtc_atomic_check,
};

static int ipu_enable_vblank(struct drm_crtc *crtc)
{
	struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);

	/*
	 * ->commit is done after ->mode_set in drm_crtc_helper_set_mode(),
	 * so waiting for vblank in drm_plane_helper_commit() will timeout.
	 * Check the state here to avoid the waiting.
	 */
	if (!ipu_crtc->enabled)
		return -EINVAL;

	enable_irq(ipu_crtc->irq);

	return 0;
@@ -496,8 +492,16 @@ static int ipu_crtc_init(struct ipu_crtc *ipu_crtc,
						IPU_DP_FLOW_SYNC_FG,
						drm_crtc_mask(&ipu_crtc->base),
						DRM_PLANE_TYPE_OVERLAY);
		if (IS_ERR(ipu_crtc->plane[1]))
		if (IS_ERR(ipu_crtc->plane[1])) {
			ipu_crtc->plane[1] = NULL;
		} else {
			ret = ipu_plane_get_resources(ipu_crtc->plane[1]);
			if (ret) {
				dev_err(ipu_crtc->dev, "getting plane 1 "
					"resources failed with %d.\n", ret);
				goto err_put_plane0_res;
			}
		}
	}

	ipu_crtc->irq = ipu_plane_irq(ipu_crtc->plane[0]);
@@ -505,7 +509,7 @@ static int ipu_crtc_init(struct ipu_crtc *ipu_crtc,
			"imx_drm", ipu_crtc);
	if (ret < 0) {
		dev_err(ipu_crtc->dev, "irq request failed with %d.\n", ret);
		goto err_put_plane_res;
		goto err_put_plane1_res;
	}
	/* Only enable IRQ when we actually need it to trigger work. */
	disable_irq(ipu_crtc->irq);
@@ -514,7 +518,10 @@ static int ipu_crtc_init(struct ipu_crtc *ipu_crtc,

	return 0;

err_put_plane_res:
err_put_plane1_res:
	if (ipu_crtc->plane[1])
		ipu_plane_put_resources(ipu_crtc->plane[1]);
err_put_plane0_res:
	ipu_plane_put_resources(ipu_crtc->plane[0]);
err_remove_crtc:
	imx_drm_remove_crtc(ipu_crtc->imx_crtc);
@@ -554,8 +561,10 @@ static void ipu_drm_unbind(struct device *dev, struct device *master,
	imx_drm_remove_crtc(ipu_crtc->imx_crtc);

	destroy_workqueue(ipu_crtc->flip_queue);
	ipu_plane_put_resources(ipu_crtc->plane[0]);
	ipu_put_resources(ipu_crtc);
	if (ipu_crtc->plane[1])
		ipu_plane_put_resources(ipu_crtc->plane[1]);
	ipu_plane_put_resources(ipu_crtc->plane[0]);
}

static const struct component_ops ipu_crtc_ops = {
+305 −206

File changed.

Preview size limit exceeded, changes collapsed.

+1 −15
Original line number Diff line number Diff line
@@ -23,17 +23,6 @@ struct ipu_plane {

	int			dma;
	int			dp_flow;

	int			x;
	int			y;
	int			w;
	int			h;

	unsigned int		u_offset;
	unsigned int		v_offset;
	unsigned int		stride[2];

	bool			enabled;
};

struct ipu_plane *ipu_plane_init(struct drm_device *dev, struct ipu_soc *ipu,
@@ -48,10 +37,7 @@ int ipu_plane_mode_set(struct ipu_plane *plane, struct drm_crtc *crtc,
		       uint32_t src_x, uint32_t src_y, uint32_t src_w,
		       uint32_t src_h, bool interlaced);

void ipu_plane_enable(struct ipu_plane *plane);
void ipu_plane_disable(struct ipu_plane *plane);
int ipu_plane_set_base(struct ipu_plane *plane, struct drm_framebuffer *fb,
		       int x, int y);
int ipu_plane_set_base(struct ipu_plane *plane, struct drm_framebuffer *fb);

int ipu_plane_get_resources(struct ipu_plane *plane);
void ipu_plane_put_resources(struct ipu_plane *plane);
+1 −4
Original line number Diff line number Diff line
@@ -178,10 +178,7 @@ int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced,
	dc->di = ipu_di_get_num(di);

	map = ipu_bus_format_to_map(bus_format);
	if (map < 0) {
		dev_dbg(priv->dev, "IPU_DISP: No MAP\n");
		return map;
	}
	BUG_ON(map < 0);

	/*
	 * In interlaced mode we need more counters to create the asymmetric
+0 −3
Original line number Diff line number Diff line
@@ -572,9 +572,6 @@ int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig)
	dev_dbg(di->ipu->dev, "disp %d: panel size = %d x %d\n",
		di->id, sig->mode.hactive, sig->mode.vactive);

	if ((sig->mode.vsync_len == 0) || (sig->mode.hsync_len == 0))
		return -EINVAL;

	dev_dbg(di->ipu->dev, "Clocks: IPU %luHz DI %luHz Needed %luHz\n",
		clk_get_rate(di->clk_ipu),
		clk_get_rate(di->clk_di),