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

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
...@@ -73,7 +73,7 @@ struct ipu_crtc { ...@@ -73,7 +73,7 @@ struct ipu_crtc {
#define to_ipu_crtc(x) container_of(x, struct ipu_crtc, base) #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); 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) ...@@ -81,30 +81,30 @@ static void ipu_fb_enable(struct ipu_crtc *ipu_crtc)
return; return;
ipu_dc_enable(ipu); 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_dc_enable_channel(ipu_crtc->dc);
ipu_di_enable(ipu_crtc->di); ipu_di_enable(ipu_crtc->di);
drm_crtc_vblank_on(&ipu_crtc->base);
ipu_crtc->enabled = 1; 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); struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent);
if (!ipu_crtc->enabled) if (!ipu_crtc->enabled)
return; return;
/* Stop DC channel and DI before IDMAC */
ipu_dc_disable_channel(ipu_crtc->dc); ipu_dc_disable_channel(ipu_crtc->dc);
ipu_di_disable(ipu_crtc->di); ipu_di_disable(ipu_crtc->di);
ipu_plane_disable(ipu_crtc->plane[0]);
ipu_dc_disable(ipu); ipu_dc_disable(ipu);
drm_crtc_vblank_off(&ipu_crtc->base);
ipu_crtc->enabled = 0; ipu_crtc->enabled = 0;
drm_crtc_vblank_off(&ipu_crtc->base);
} }
static void ipu_crtc_dpms(struct drm_crtc *crtc, int mode) 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) ...@@ -115,12 +115,12 @@ static void ipu_crtc_dpms(struct drm_crtc *crtc, int mode)
switch (mode) { switch (mode) {
case DRM_MODE_DPMS_ON: case DRM_MODE_DPMS_ON:
ipu_fb_enable(ipu_crtc); ipu_crtc_enable(ipu_crtc);
break; break;
case DRM_MODE_DPMS_STANDBY: case DRM_MODE_DPMS_STANDBY:
case DRM_MODE_DPMS_SUSPEND: case DRM_MODE_DPMS_SUSPEND:
case DRM_MODE_DPMS_OFF: case DRM_MODE_DPMS_OFF:
ipu_fb_disable(ipu_crtc); ipu_crtc_disable(ipu_crtc);
break; break;
} }
} }
...@@ -234,79 +234,6 @@ static const struct drm_crtc_funcs ipu_crtc_funcs = { ...@@ -234,79 +234,6 @@ static const struct drm_crtc_funcs ipu_crtc_funcs = {
.page_flip = ipu_page_flip, .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) static void ipu_crtc_handle_pageflip(struct ipu_crtc *ipu_crtc)
{ {
unsigned long flags; unsigned long flags;
...@@ -330,8 +257,7 @@ static irqreturn_t ipu_irq_handler(int irq, void *dev_id) ...@@ -330,8 +257,7 @@ static irqreturn_t ipu_irq_handler(int irq, void *dev_id)
if (ipu_crtc->flip_state == IPU_FLIP_SUBMITTED) { if (ipu_crtc->flip_state == IPU_FLIP_SUBMITTED) {
struct ipu_plane *plane = ipu_crtc->plane[0]; struct ipu_plane *plane = ipu_crtc->plane[0];
ipu_plane_set_base(plane, ipu_crtc->base.primary->fb, ipu_plane_set_base(plane, ipu_crtc->base.primary->fb);
plane->x, plane->y);
ipu_crtc_handle_pageflip(ipu_crtc); ipu_crtc_handle_pageflip(ipu_crtc);
queue_work(ipu_crtc->flip_queue, queue_work(ipu_crtc->flip_queue,
&ipu_crtc->flip_work->unref_work); &ipu_crtc->flip_work->unref_work);
...@@ -355,6 +281,9 @@ static bool ipu_crtc_mode_fixup(struct drm_crtc *crtc, ...@@ -355,6 +281,9 @@ static bool ipu_crtc_mode_fixup(struct drm_crtc *crtc,
if (ret) if (ret)
return false; return false;
if ((vm.vsync_len == 0) || (vm.hsync_len == 0))
return false;
drm_display_mode_from_videomode(&vm, adjusted_mode); drm_display_mode_from_videomode(&vm, adjusted_mode);
return true; return true;
...@@ -364,28 +293,95 @@ static void ipu_crtc_prepare(struct drm_crtc *crtc) ...@@ -364,28 +293,95 @@ static void ipu_crtc_prepare(struct drm_crtc *crtc)
{ {
struct ipu_crtc *ipu_crtc = to_ipu_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) static void ipu_crtc_commit(struct drm_crtc *crtc)
{ {
struct ipu_crtc *ipu_crtc = to_ipu_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 = { static const struct drm_crtc_helper_funcs ipu_helper_funcs = {
.dpms = ipu_crtc_dpms, .dpms = ipu_crtc_dpms,
.mode_fixup = ipu_crtc_mode_fixup, .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, .prepare = ipu_crtc_prepare,
.commit = ipu_crtc_commit, .commit = ipu_crtc_commit,
.atomic_check = ipu_crtc_atomic_check,
}; };
static int ipu_enable_vblank(struct drm_crtc *crtc) static int ipu_enable_vblank(struct drm_crtc *crtc)
{ {
struct ipu_crtc *ipu_crtc = to_ipu_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); enable_irq(ipu_crtc->irq);
return 0; return 0;
...@@ -496,8 +492,16 @@ static int ipu_crtc_init(struct ipu_crtc *ipu_crtc, ...@@ -496,8 +492,16 @@ static int ipu_crtc_init(struct ipu_crtc *ipu_crtc,
IPU_DP_FLOW_SYNC_FG, IPU_DP_FLOW_SYNC_FG,
drm_crtc_mask(&ipu_crtc->base), drm_crtc_mask(&ipu_crtc->base),
DRM_PLANE_TYPE_OVERLAY); DRM_PLANE_TYPE_OVERLAY);
if (IS_ERR(ipu_crtc->plane[1])) if (IS_ERR(ipu_crtc->plane[1])) {
ipu_crtc->plane[1] = NULL; 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]); ipu_crtc->irq = ipu_plane_irq(ipu_crtc->plane[0]);
...@@ -505,7 +509,7 @@ static int ipu_crtc_init(struct ipu_crtc *ipu_crtc, ...@@ -505,7 +509,7 @@ static int ipu_crtc_init(struct ipu_crtc *ipu_crtc,
"imx_drm", ipu_crtc); "imx_drm", ipu_crtc);
if (ret < 0) { if (ret < 0) {
dev_err(ipu_crtc->dev, "irq request failed with %d.\n", ret); 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. */ /* Only enable IRQ when we actually need it to trigger work. */
disable_irq(ipu_crtc->irq); disable_irq(ipu_crtc->irq);
...@@ -514,7 +518,10 @@ static int ipu_crtc_init(struct ipu_crtc *ipu_crtc, ...@@ -514,7 +518,10 @@ static int ipu_crtc_init(struct ipu_crtc *ipu_crtc,
return 0; 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]); ipu_plane_put_resources(ipu_crtc->plane[0]);
err_remove_crtc: err_remove_crtc:
imx_drm_remove_crtc(ipu_crtc->imx_crtc); imx_drm_remove_crtc(ipu_crtc->imx_crtc);
...@@ -554,8 +561,10 @@ static void ipu_drm_unbind(struct device *dev, struct device *master, ...@@ -554,8 +561,10 @@ static void ipu_drm_unbind(struct device *dev, struct device *master,
imx_drm_remove_crtc(ipu_crtc->imx_crtc); imx_drm_remove_crtc(ipu_crtc->imx_crtc);
destroy_workqueue(ipu_crtc->flip_queue); destroy_workqueue(ipu_crtc->flip_queue);
ipu_plane_put_resources(ipu_crtc->plane[0]);
ipu_put_resources(ipu_crtc); 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 = { static const struct component_ops ipu_crtc_ops = {
......
This diff is collapsed.
...@@ -23,17 +23,6 @@ struct ipu_plane { ...@@ -23,17 +23,6 @@ struct ipu_plane {
int dma; int dma;
int dp_flow; 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, 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, ...@@ -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_x, uint32_t src_y, uint32_t src_w,
uint32_t src_h, bool interlaced); uint32_t src_h, bool interlaced);
void ipu_plane_enable(struct ipu_plane *plane); int ipu_plane_set_base(struct ipu_plane *plane, struct drm_framebuffer *fb);
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_get_resources(struct ipu_plane *plane); int ipu_plane_get_resources(struct ipu_plane *plane);
void ipu_plane_put_resources(struct ipu_plane *plane); void ipu_plane_put_resources(struct ipu_plane *plane);
......
...@@ -178,10 +178,7 @@ int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced, ...@@ -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); dc->di = ipu_di_get_num(di);
map = ipu_bus_format_to_map(bus_format); map = ipu_bus_format_to_map(bus_format);
if (map < 0) { BUG_ON(map < 0);
dev_dbg(priv->dev, "IPU_DISP: No MAP\n");
return map;
}
/* /*
* In interlaced mode we need more counters to create the asymmetric * In interlaced mode we need more counters to create the asymmetric
......
...@@ -572,9 +572,6 @@ int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig) ...@@ -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", dev_dbg(di->ipu->dev, "disp %d: panel size = %d x %d\n",
di->id, sig->mode.hactive, sig->mode.vactive); 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", dev_dbg(di->ipu->dev, "Clocks: IPU %luHz DI %luHz Needed %luHz\n",
clk_get_rate(di->clk_ipu), clk_get_rate(di->clk_ipu),
clk_get_rate(di->clk_di), clk_get_rate(di->clk_di),
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment