Commit 879a37d0 authored by Dave Airlie's avatar Dave Airlie

Merge branch 'exynos-drm-next' of...

Merge branch 'exynos-drm-next' of git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos into drm-next

This is a second pull-request which adds last part of
atomic modeset/pageflip support, render node support,
clean-up, and fix-up.

* 'exynos-drm-next' of git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos:
  drm/exynos: fix build warning to exynos_drm_gem.c
  drm/exynos: Properly report supported formats for each device
  drm/exynos: add render node support
  drm/exynos: implement atomic_{begin/flush} of DECON
  drm/exynos: remove legacy ->suspend()/resume()
  drm/exynos: Enable atomic modesetting feature
  drm/exynos: remove wait queue for pending page flip
  drm/exynos: wait all planes updates to finish
  drm/exynos: add atomic asynchronous commit
  drm/exynos: fimd: only finish update if START == START_S
  drm/exynos: add macro to get the address of START_S reg
  drm/exynos: check for pending fb before finish update
  drm/exynos: fimd: move window protect code to prepare/cleanup_plane
  drm/exynos: add prepare and cleanup phases for planes
  drm/exynos: fimd: unify call to exynos_drm_crtc_finish_pageflip()
  drm/exynos: don't track enabled state at exynos_crtc
parents 701078d5 50002d4c
...@@ -54,6 +54,13 @@ static const char * const decon_clks_name[] = { ...@@ -54,6 +54,13 @@ static const char * const decon_clks_name[] = {
"sclk_decon_eclk", "sclk_decon_eclk",
}; };
static const uint32_t decon_formats[] = {
DRM_FORMAT_XRGB1555,
DRM_FORMAT_RGB565,
DRM_FORMAT_XRGB8888,
DRM_FORMAT_ARGB8888,
};
static int decon_enable_vblank(struct exynos_drm_crtc *crtc) static int decon_enable_vblank(struct exynos_drm_crtc *crtc)
{ {
struct decon_context *ctx = crtc->ctx; struct decon_context *ctx = crtc->ctx;
...@@ -219,6 +226,17 @@ static void decon_shadow_protect_win(struct decon_context *ctx, int win, ...@@ -219,6 +226,17 @@ static void decon_shadow_protect_win(struct decon_context *ctx, int win,
writel(val, ctx->addr + DECON_SHADOWCON); writel(val, ctx->addr + DECON_SHADOWCON);
} }
static void decon_atomic_begin(struct exynos_drm_crtc *crtc,
struct exynos_drm_plane *plane)
{
struct decon_context *ctx = crtc->ctx;
if (ctx->suspended)
return;
decon_shadow_protect_win(ctx, plane->zpos, true);
}
static void decon_update_plane(struct exynos_drm_crtc *crtc, static void decon_update_plane(struct exynos_drm_crtc *crtc,
struct exynos_drm_plane *plane) struct exynos_drm_plane *plane)
{ {
...@@ -232,8 +250,6 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc, ...@@ -232,8 +250,6 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc,
if (ctx->suspended) if (ctx->suspended)
return; return;
decon_shadow_protect_win(ctx, win, true);
val = COORDINATE_X(plane->crtc_x) | COORDINATE_Y(plane->crtc_y); val = COORDINATE_X(plane->crtc_x) | COORDINATE_Y(plane->crtc_y);
writel(val, ctx->addr + DECON_VIDOSDxA(win)); writel(val, ctx->addr + DECON_VIDOSDxA(win));
...@@ -265,15 +281,10 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc, ...@@ -265,15 +281,10 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc,
val |= WINCONx_ENWIN_F; val |= WINCONx_ENWIN_F;
writel(val, ctx->addr + DECON_WINCONx(win)); writel(val, ctx->addr + DECON_WINCONx(win));
decon_shadow_protect_win(ctx, win, false);
/* standalone update */ /* standalone update */
val = readl(ctx->addr + DECON_UPDATE); val = readl(ctx->addr + DECON_UPDATE);
val |= STANDALONE_UPDATE_F; val |= STANDALONE_UPDATE_F;
writel(val, ctx->addr + DECON_UPDATE); writel(val, ctx->addr + DECON_UPDATE);
if (ctx->i80_if)
atomic_set(&ctx->win_updated, 1);
} }
static void decon_disable_plane(struct exynos_drm_crtc *crtc, static void decon_disable_plane(struct exynos_drm_crtc *crtc,
...@@ -301,6 +312,20 @@ static void decon_disable_plane(struct exynos_drm_crtc *crtc, ...@@ -301,6 +312,20 @@ static void decon_disable_plane(struct exynos_drm_crtc *crtc,
writel(val, ctx->addr + DECON_UPDATE); writel(val, ctx->addr + DECON_UPDATE);
} }
static void decon_atomic_flush(struct exynos_drm_crtc *crtc,
struct exynos_drm_plane *plane)
{
struct decon_context *ctx = crtc->ctx;
if (ctx->suspended)
return;
decon_shadow_protect_win(ctx, plane->zpos, false);
if (ctx->i80_if)
atomic_set(&ctx->win_updated, 1);
}
static void decon_swreset(struct decon_context *ctx) static void decon_swreset(struct decon_context *ctx)
{ {
unsigned int tries; unsigned int tries;
...@@ -455,8 +480,10 @@ static struct exynos_drm_crtc_ops decon_crtc_ops = { ...@@ -455,8 +480,10 @@ static struct exynos_drm_crtc_ops decon_crtc_ops = {
.enable_vblank = decon_enable_vblank, .enable_vblank = decon_enable_vblank,
.disable_vblank = decon_disable_vblank, .disable_vblank = decon_disable_vblank,
.commit = decon_commit, .commit = decon_commit,
.atomic_begin = decon_atomic_begin,
.update_plane = decon_update_plane, .update_plane = decon_update_plane,
.disable_plane = decon_disable_plane, .disable_plane = decon_disable_plane,
.atomic_flush = decon_atomic_flush,
.te_handler = decon_te_irq_handler, .te_handler = decon_te_irq_handler,
}; };
...@@ -477,7 +504,8 @@ static int decon_bind(struct device *dev, struct device *master, void *data) ...@@ -477,7 +504,8 @@ static int decon_bind(struct device *dev, struct device *master, void *data)
type = (zpos == ctx->default_win) ? DRM_PLANE_TYPE_PRIMARY : type = (zpos == ctx->default_win) ? DRM_PLANE_TYPE_PRIMARY :
DRM_PLANE_TYPE_OVERLAY; DRM_PLANE_TYPE_OVERLAY;
ret = exynos_plane_init(drm_dev, &ctx->planes[zpos], ret = exynos_plane_init(drm_dev, &ctx->planes[zpos],
1 << ctx->pipe, type, zpos); 1 << ctx->pipe, type, decon_formats,
ARRAY_SIZE(decon_formats), zpos);
if (ret) if (ret)
return ret; return ret;
} }
...@@ -542,13 +570,21 @@ static irqreturn_t decon_lcd_sys_irq_handler(int irq, void *dev_id) ...@@ -542,13 +570,21 @@ static irqreturn_t decon_lcd_sys_irq_handler(int irq, void *dev_id)
{ {
struct decon_context *ctx = dev_id; struct decon_context *ctx = dev_id;
u32 val; u32 val;
int win;
if (!test_bit(BIT_CLKS_ENABLED, &ctx->enabled)) if (!test_bit(BIT_CLKS_ENABLED, &ctx->enabled))
goto out; goto out;
val = readl(ctx->addr + DECON_VIDINTCON1); val = readl(ctx->addr + DECON_VIDINTCON1);
if (val & VIDINTCON1_INTFRMDONEPEND) { if (val & VIDINTCON1_INTFRMDONEPEND) {
exynos_drm_crtc_finish_pageflip(ctx->crtc); for (win = 0 ; win < WINDOWS_NR ; win++) {
struct exynos_drm_plane *plane = &ctx->planes[win];
if (!plane->pending_fb)
continue;
exynos_drm_crtc_finish_update(ctx->crtc, plane);
}
/* clear */ /* clear */
writel(VIDINTCON1_INTFRMDONEPEND, writel(VIDINTCON1_INTFRMDONEPEND,
......
...@@ -70,6 +70,18 @@ static const struct of_device_id decon_driver_dt_match[] = { ...@@ -70,6 +70,18 @@ static const struct of_device_id decon_driver_dt_match[] = {
}; };
MODULE_DEVICE_TABLE(of, decon_driver_dt_match); MODULE_DEVICE_TABLE(of, decon_driver_dt_match);
static const uint32_t decon_formats[] = {
DRM_FORMAT_RGB565,
DRM_FORMAT_XRGB8888,
DRM_FORMAT_XBGR8888,
DRM_FORMAT_RGBX8888,
DRM_FORMAT_BGRX8888,
DRM_FORMAT_ARGB8888,
DRM_FORMAT_ABGR8888,
DRM_FORMAT_RGBA8888,
DRM_FORMAT_BGRA8888,
};
static void decon_wait_for_vblank(struct exynos_drm_crtc *crtc) static void decon_wait_for_vblank(struct exynos_drm_crtc *crtc)
{ {
struct decon_context *ctx = crtc->ctx; struct decon_context *ctx = crtc->ctx;
...@@ -383,6 +395,17 @@ static void decon_shadow_protect_win(struct decon_context *ctx, ...@@ -383,6 +395,17 @@ static void decon_shadow_protect_win(struct decon_context *ctx,
writel(val, ctx->regs + SHADOWCON); writel(val, ctx->regs + SHADOWCON);
} }
static void decon_atomic_begin(struct exynos_drm_crtc *crtc,
struct exynos_drm_plane *plane)
{
struct decon_context *ctx = crtc->ctx;
if (ctx->suspended)
return;
decon_shadow_protect_win(ctx, plane->zpos, true);
}
static void decon_update_plane(struct exynos_drm_crtc *crtc, static void decon_update_plane(struct exynos_drm_crtc *crtc,
struct exynos_drm_plane *plane) struct exynos_drm_plane *plane)
{ {
...@@ -410,9 +433,6 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc, ...@@ -410,9 +433,6 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc,
* is set. * is set.
*/ */
/* protect windows */
decon_shadow_protect_win(ctx, win, true);
/* buffer start address */ /* buffer start address */
val = (unsigned long)plane->dma_addr[0]; val = (unsigned long)plane->dma_addr[0];
writel(val, ctx->regs + VIDW_BUF_START(win)); writel(val, ctx->regs + VIDW_BUF_START(win));
...@@ -510,14 +530,22 @@ static void decon_disable_plane(struct exynos_drm_crtc *crtc, ...@@ -510,14 +530,22 @@ static void decon_disable_plane(struct exynos_drm_crtc *crtc,
val &= ~WINCONx_ENWIN; val &= ~WINCONx_ENWIN;
writel(val, ctx->regs + WINCON(win)); writel(val, ctx->regs + WINCON(win));
/* unprotect windows */
decon_shadow_protect_win(ctx, win, false);
val = readl(ctx->regs + DECON_UPDATE); val = readl(ctx->regs + DECON_UPDATE);
val |= DECON_UPDATE_STANDALONE_F; val |= DECON_UPDATE_STANDALONE_F;
writel(val, ctx->regs + DECON_UPDATE); writel(val, ctx->regs + DECON_UPDATE);
} }
static void decon_atomic_flush(struct exynos_drm_crtc *crtc,
struct exynos_drm_plane *plane)
{
struct decon_context *ctx = crtc->ctx;
if (ctx->suspended)
return;
decon_shadow_protect_win(ctx, plane->zpos, false);
}
static void decon_init(struct decon_context *ctx) static void decon_init(struct decon_context *ctx)
{ {
u32 val; u32 val;
...@@ -614,8 +642,10 @@ static const struct exynos_drm_crtc_ops decon_crtc_ops = { ...@@ -614,8 +642,10 @@ static const struct exynos_drm_crtc_ops decon_crtc_ops = {
.enable_vblank = decon_enable_vblank, .enable_vblank = decon_enable_vblank,
.disable_vblank = decon_disable_vblank, .disable_vblank = decon_disable_vblank,
.wait_for_vblank = decon_wait_for_vblank, .wait_for_vblank = decon_wait_for_vblank,
.atomic_begin = decon_atomic_begin,
.update_plane = decon_update_plane, .update_plane = decon_update_plane,
.disable_plane = decon_disable_plane, .disable_plane = decon_disable_plane,
.atomic_flush = decon_atomic_flush,
}; };
...@@ -623,6 +653,7 @@ static irqreturn_t decon_irq_handler(int irq, void *dev_id) ...@@ -623,6 +653,7 @@ static irqreturn_t decon_irq_handler(int irq, void *dev_id)
{ {
struct decon_context *ctx = (struct decon_context *)dev_id; struct decon_context *ctx = (struct decon_context *)dev_id;
u32 val, clear_bit; u32 val, clear_bit;
int win;
val = readl(ctx->regs + VIDINTCON1); val = readl(ctx->regs + VIDINTCON1);
...@@ -636,7 +667,14 @@ static irqreturn_t decon_irq_handler(int irq, void *dev_id) ...@@ -636,7 +667,14 @@ static irqreturn_t decon_irq_handler(int irq, void *dev_id)
if (!ctx->i80_if) { if (!ctx->i80_if) {
drm_crtc_handle_vblank(&ctx->crtc->base); drm_crtc_handle_vblank(&ctx->crtc->base);
exynos_drm_crtc_finish_pageflip(ctx->crtc); for (win = 0 ; win < WINDOWS_NR ; win++) {
struct exynos_drm_plane *plane = &ctx->planes[win];
if (!plane->pending_fb)
continue;
exynos_drm_crtc_finish_update(ctx->crtc, plane);
}
/* set wait vsync event to zero and wake up queue. */ /* set wait vsync event to zero and wake up queue. */
if (atomic_read(&ctx->wait_vsync_event)) { if (atomic_read(&ctx->wait_vsync_event)) {
...@@ -667,7 +705,8 @@ static int decon_bind(struct device *dev, struct device *master, void *data) ...@@ -667,7 +705,8 @@ static int decon_bind(struct device *dev, struct device *master, void *data)
type = (zpos == ctx->default_win) ? DRM_PLANE_TYPE_PRIMARY : type = (zpos == ctx->default_win) ? DRM_PLANE_TYPE_PRIMARY :
DRM_PLANE_TYPE_OVERLAY; DRM_PLANE_TYPE_OVERLAY;
ret = exynos_plane_init(drm_dev, &ctx->planes[zpos], ret = exynos_plane_init(drm_dev, &ctx->planes[zpos],
1 << ctx->pipe, type, zpos); 1 << ctx->pipe, type, decon_formats,
ARRAY_SIZE(decon_formats), zpos);
if (ret) if (ret)
return ret; return ret;
} }
......
...@@ -25,14 +25,9 @@ static void exynos_drm_crtc_enable(struct drm_crtc *crtc) ...@@ -25,14 +25,9 @@ static void exynos_drm_crtc_enable(struct drm_crtc *crtc)
{ {
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
if (exynos_crtc->enabled)
return;
if (exynos_crtc->ops->enable) if (exynos_crtc->ops->enable)
exynos_crtc->ops->enable(exynos_crtc); exynos_crtc->ops->enable(exynos_crtc);
exynos_crtc->enabled = true;
drm_crtc_vblank_on(crtc); drm_crtc_vblank_on(crtc);
} }
...@@ -40,20 +35,10 @@ static void exynos_drm_crtc_disable(struct drm_crtc *crtc) ...@@ -40,20 +35,10 @@ static void exynos_drm_crtc_disable(struct drm_crtc *crtc)
{ {
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
if (!exynos_crtc->enabled)
return;
/* wait for the completion of page flip. */
if (!wait_event_timeout(exynos_crtc->pending_flip_queue,
(exynos_crtc->event == NULL), HZ/20))
exynos_crtc->event = NULL;
drm_crtc_vblank_off(crtc); drm_crtc_vblank_off(crtc);
if (exynos_crtc->ops->disable) if (exynos_crtc->ops->disable)
exynos_crtc->ops->disable(exynos_crtc); exynos_crtc->ops->disable(exynos_crtc);
exynos_crtc->enabled = false;
} }
static bool static bool
...@@ -83,16 +68,32 @@ static void exynos_crtc_atomic_begin(struct drm_crtc *crtc, ...@@ -83,16 +68,32 @@ static void exynos_crtc_atomic_begin(struct drm_crtc *crtc,
struct drm_crtc_state *old_crtc_state) struct drm_crtc_state *old_crtc_state)
{ {
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
struct drm_plane *plane;
exynos_crtc->event = crtc->state->event;
if (crtc->state->event) { drm_atomic_crtc_for_each_plane(plane, crtc) {
WARN_ON(drm_crtc_vblank_get(crtc) != 0); struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane);
exynos_crtc->event = crtc->state->event;
if (exynos_crtc->ops->atomic_begin)
exynos_crtc->ops->atomic_begin(exynos_crtc,
exynos_plane);
} }
} }
static void exynos_crtc_atomic_flush(struct drm_crtc *crtc, static void exynos_crtc_atomic_flush(struct drm_crtc *crtc,
struct drm_crtc_state *old_crtc_state) struct drm_crtc_state *old_crtc_state)
{ {
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
struct drm_plane *plane;
drm_atomic_crtc_for_each_plane(plane, crtc) {
struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane);
if (exynos_crtc->ops->atomic_flush)
exynos_crtc->ops->atomic_flush(exynos_crtc,
exynos_plane);
}
} }
static struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = { static struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = {
...@@ -140,13 +141,13 @@ struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev, ...@@ -140,13 +141,13 @@ struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev,
if (!exynos_crtc) if (!exynos_crtc)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
init_waitqueue_head(&exynos_crtc->pending_flip_queue);
exynos_crtc->pipe = pipe; exynos_crtc->pipe = pipe;
exynos_crtc->type = type; exynos_crtc->type = type;
exynos_crtc->ops = ops; exynos_crtc->ops = ops;
exynos_crtc->ctx = ctx; exynos_crtc->ctx = ctx;
init_waitqueue_head(&exynos_crtc->wait_update);
crtc = &exynos_crtc->base; crtc = &exynos_crtc->base;
private->crtc[pipe] = crtc; private->crtc[pipe] = crtc;
...@@ -172,9 +173,6 @@ int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int pipe) ...@@ -172,9 +173,6 @@ int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int pipe)
struct exynos_drm_crtc *exynos_crtc = struct exynos_drm_crtc *exynos_crtc =
to_exynos_crtc(private->crtc[pipe]); to_exynos_crtc(private->crtc[pipe]);
if (!exynos_crtc->enabled)
return -EPERM;
if (exynos_crtc->ops->enable_vblank) if (exynos_crtc->ops->enable_vblank)
return exynos_crtc->ops->enable_vblank(exynos_crtc); return exynos_crtc->ops->enable_vblank(exynos_crtc);
...@@ -187,26 +185,31 @@ void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int pipe) ...@@ -187,26 +185,31 @@ void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int pipe)
struct exynos_drm_crtc *exynos_crtc = struct exynos_drm_crtc *exynos_crtc =
to_exynos_crtc(private->crtc[pipe]); to_exynos_crtc(private->crtc[pipe]);
if (!exynos_crtc->enabled)
return;
if (exynos_crtc->ops->disable_vblank) if (exynos_crtc->ops->disable_vblank)
exynos_crtc->ops->disable_vblank(exynos_crtc); exynos_crtc->ops->disable_vblank(exynos_crtc);
} }
void exynos_drm_crtc_finish_pageflip(struct exynos_drm_crtc *exynos_crtc) void exynos_drm_crtc_wait_pending_update(struct exynos_drm_crtc *exynos_crtc)
{
wait_event_timeout(exynos_crtc->wait_update,
(atomic_read(&exynos_crtc->pending_update) == 0),
msecs_to_jiffies(50));
}
void exynos_drm_crtc_finish_update(struct exynos_drm_crtc *exynos_crtc,
struct exynos_drm_plane *exynos_plane)
{ {
struct drm_crtc *crtc = &exynos_crtc->base; struct drm_crtc *crtc = &exynos_crtc->base;
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&crtc->dev->event_lock, flags); exynos_plane->pending_fb = NULL;
if (exynos_crtc->event) {
drm_crtc_send_vblank_event(crtc, exynos_crtc->event); if (atomic_dec_and_test(&exynos_crtc->pending_update))
drm_crtc_vblank_put(crtc); wake_up(&exynos_crtc->wait_update);
wake_up(&exynos_crtc->pending_flip_queue);
} spin_lock_irqsave(&crtc->dev->event_lock, flags);
if (exynos_crtc->event)
drm_crtc_send_vblank_event(crtc, exynos_crtc->event);
exynos_crtc->event = NULL; exynos_crtc->event = NULL;
spin_unlock_irqrestore(&crtc->dev->event_lock, flags); spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
......
...@@ -25,7 +25,9 @@ struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev, ...@@ -25,7 +25,9 @@ struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev,
void *context); void *context);
int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int pipe); int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int pipe);
void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int pipe); void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int pipe);
void exynos_drm_crtc_finish_pageflip(struct exynos_drm_crtc *exynos_crtc); void exynos_drm_crtc_wait_pending_update(struct exynos_drm_crtc *exynos_crtc);
void exynos_drm_crtc_finish_update(struct exynos_drm_crtc *exynos_crtc,
struct exynos_drm_plane *exynos_plane);
void exynos_drm_crtc_complete_scanout(struct drm_framebuffer *fb); void exynos_drm_crtc_complete_scanout(struct drm_framebuffer *fb);
/* This function gets pipe value to crtc device matched with out_type. */ /* This function gets pipe value to crtc device matched with out_type. */
......
...@@ -13,6 +13,8 @@ ...@@ -13,6 +13,8 @@
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <drm/drmP.h> #include <drm/drmP.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc_helper.h> #include <drm/drm_crtc_helper.h>
#include <linux/component.h> #include <linux/component.h>
...@@ -36,6 +38,98 @@ ...@@ -36,6 +38,98 @@
#define DRIVER_MAJOR 1 #define DRIVER_MAJOR 1
#define DRIVER_MINOR 0 #define DRIVER_MINOR 0
struct exynos_atomic_commit {
struct work_struct work;
struct drm_device *dev;
struct drm_atomic_state *state;
u32 crtcs;
};
static void exynos_atomic_wait_for_commit(struct drm_atomic_state *state)
{
struct drm_crtc_state *crtc_state;
struct drm_crtc *crtc;
int i, ret;
for_each_crtc_in_state(state, crtc, crtc_state, i) {
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
if (!crtc->state->enable)
continue;
ret = drm_crtc_vblank_get(crtc);
if (ret)
continue;
exynos_drm_crtc_wait_pending_update(exynos_crtc);
drm_crtc_vblank_put(crtc);
}
}
static void exynos_atomic_commit_complete(struct exynos_atomic_commit *commit)
{
struct drm_device *dev = commit->dev;
struct exynos_drm_private *priv = dev->dev_private;
struct drm_atomic_state *state = commit->state;
struct drm_plane *plane;
struct drm_crtc *crtc;
struct drm_plane_state *plane_state;
struct drm_crtc_state *crtc_state;
int i;
drm_atomic_helper_commit_modeset_disables(dev, state);
drm_atomic_helper_commit_modeset_enables(dev, state);
/*
* Exynos can't update planes with CRTCs and encoders disabled,
* its updates routines, specially for FIMD, requires the clocks
* to be enabled. So it is necessary to handle the modeset operations
* *before* the commit_planes() step, this way it will always
* have the relevant clocks enabled to perform the update.
*/
for_each_crtc_in_state(state, crtc, crtc_state, i) {
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
atomic_set(&exynos_crtc->pending_update, 0);
}
for_each_plane_in_state(state, plane, plane_state, i) {
struct exynos_drm_crtc *exynos_crtc =
to_exynos_crtc(plane->crtc);
if (!plane->crtc)
continue;
atomic_inc(&exynos_crtc->pending_update);
}
drm_atomic_helper_commit_planes(dev, state);
exynos_atomic_wait_for_commit(state);
drm_atomic_helper_cleanup_planes(dev, state);
drm_atomic_state_free(state);
spin_lock(&priv->lock);
priv->pending &= ~commit->crtcs;
spin_unlock(&priv->lock);
wake_up_all(&priv->wait);
kfree(commit);
}
static void exynos_drm_atomic_work(struct work_struct *work)
{
struct exynos_atomic_commit *commit = container_of(work,
struct exynos_atomic_commit, work);
exynos_atomic_commit_complete(commit);
}
static int exynos_drm_load(struct drm_device *dev, unsigned long flags) static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
{ {
struct exynos_drm_private *private; struct exynos_drm_private *private;
...@@ -47,6 +141,9 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags) ...@@ -47,6 +141,9 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
if (!private) if (!private)
return -ENOMEM; return -ENOMEM;
init_waitqueue_head(&private->wait);
spin_lock_init(&private->lock);
dev_set_drvdata(dev->dev, dev); dev_set_drvdata(dev->dev, dev);
dev->dev_private = (void *)private; dev->dev_private = (void *)private;
...@@ -149,6 +246,64 @@ static int exynos_drm_unload(struct drm_device *dev) ...@@ -149,6 +246,64 @@ static int exynos_drm_unload(struct drm_device *dev)
return 0; return 0;
} }
static int commit_is_pending(struct exynos_drm_private *priv, u32 crtcs)
{
bool pending;
spin_lock(&priv->lock);
pending = priv->pending & crtcs;
spin_unlock(&priv->lock);
return pending;
}
int exynos_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state,
bool async)
{
struct exynos_drm_private *priv = dev->dev_private;
struct exynos_atomic_commit *commit;
int i, ret;
commit = kzalloc(sizeof(*commit), GFP_KERNEL);
if (!commit)
return -ENOMEM;
ret = drm_atomic_helper_prepare_planes(dev, state);
if (ret) {
kfree(commit);
return ret;
}
/* This is the point of no return */
INIT_WORK(&commit->work, exynos_drm_atomic_work);
commit->dev = dev;
commit->state = state;
/* Wait until all affected CRTCs have completed previous commits and
* mark them as pending.
*/
for (i = 0; i < dev->mode_config.num_crtc; ++i) {
if (state->crtcs[i])
commit->crtcs |= 1 << drm_crtc_index(state->crtcs[i]);
}
wait_event(priv->wait, !commit_is_pending(priv, commit->crtcs));
spin_lock(&priv->lock);
priv->pending |= commit->crtcs;
spin_unlock(&priv->lock);
drm_atomic_helper_swap_state(dev, state);
if (async)
schedule_work(&commit->work);
else
exynos_atomic_commit_complete(commit);
return 0;
}
static int exynos_drm_suspend(struct drm_device *dev, pm_message_t state) static int exynos_drm_suspend(struct drm_device *dev, pm_message_t state)
{ {
struct drm_connector *connector; struct drm_connector *connector;
...@@ -248,25 +403,25 @@ static const struct vm_operations_struct exynos_drm_gem_vm_ops = { ...@@ -248,25 +403,25 @@ static const struct vm_operations_struct exynos_drm_gem_vm_ops = {
static const struct drm_ioctl_desc exynos_ioctls[] = { static const struct drm_ioctl_desc exynos_ioctls[] = {
DRM_IOCTL_DEF_DRV(EXYNOS_GEM_CREATE, exynos_drm_gem_create_ioctl, DRM_IOCTL_DEF_DRV(EXYNOS_GEM_CREATE, exynos_drm_gem_create_ioctl,
DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(EXYNOS_GEM_GET, exynos_drm_gem_get_ioctl,
DRM_UNLOCKED | DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(EXYNOS_VIDI_CONNECTION, vidi_connection_ioctl,
DRM_UNLOCKED | DRM_AUTH), DRM_UNLOCKED | DRM_AUTH),
DRM_IOCTL_DEF_DRV(EXYNOS_GEM_GET, DRM_IOCTL_DEF_DRV(EXYNOS_G2D_GET_VER, exynos_g2d_get_ver_ioctl,
exynos_drm_gem_get_ioctl, DRM_UNLOCKED), DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(EXYNOS_VIDI_CONNECTION, DRM_IOCTL_DEF_DRV(EXYNOS_G2D_SET_CMDLIST, exynos_g2d_set_cmdlist_ioctl,
vidi_connection_ioctl, DRM_UNLOCKED | DRM_AUTH), DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(EXYNOS_G2D_GET_VER, DRM_IOCTL_DEF_DRV(EXYNOS_G2D_EXEC, exynos_g2d_exec_ioctl,
exynos_g2d_get_ver_ioctl, DRM_UNLOCKED | DRM_AUTH), DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(EXYNOS_G2D_SET_CMDLIST, DRM_IOCTL_DEF_DRV(EXYNOS_IPP_GET_PROPERTY, exynos_drm_ipp_get_property,
exynos_g2d_set_cmdlist_ioctl, DRM_UNLOCKED | DRM_AUTH), DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(EXYNOS_G2D_EXEC, DRM_IOCTL_DEF_DRV(EXYNOS_IPP_SET_PROPERTY, exynos_drm_ipp_set_property,
exynos_g2d_exec_ioctl, DRM_UNLOCKED | DRM_AUTH), DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(EXYNOS_IPP_GET_PROPERTY, DRM_IOCTL_DEF_DRV(EXYNOS_IPP_QUEUE_BUF, exynos_drm_ipp_queue_buf,
exynos_drm_ipp_get_property, DRM_UNLOCKED | DRM_AUTH), DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(EXYNOS_IPP_SET_PROPERTY, DRM_IOCTL_DEF_DRV(EXYNOS_IPP_CMD_CTRL, exynos_drm_ipp_cmd_ctrl,
exynos_drm_ipp_set_property, DRM_UNLOCKED | DRM_AUTH), DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(EXYNOS_IPP_QUEUE_BUF,
exynos_drm_ipp_queue_buf, DRM_UNLOCKED | DRM_AUTH),
DRM_IOCTL_DEF_DRV(EXYNOS_IPP_CMD_CTRL,
exynos_drm_ipp_cmd_ctrl, DRM_UNLOCKED | DRM_AUTH),
}; };
static const struct file_operations exynos_drm_driver_fops = { static const struct file_operations exynos_drm_driver_fops = {
...@@ -283,11 +438,10 @@ static const struct file_operations exynos_drm_driver_fops = { ...@@ -283,11 +438,10 @@ static const struct file_operations exynos_drm_driver_fops = {
}; };
static struct drm_driver exynos_drm_driver = { static struct drm_driver exynos_drm_driver = {
.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME, .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME
| DRIVER_ATOMIC | DRIVER_RENDER,
.load = exynos_drm_load, .load = exynos_drm_load,
.unload = exynos_drm_unload, .unload = exynos_drm_unload,
.suspend = exynos_drm_suspend,
.resume = exynos_drm_resume,
.open = exynos_drm_open, .open = exynos_drm_open,
.preclose = exynos_drm_preclose, .preclose = exynos_drm_preclose,
.lastclose = exynos_drm_lastclose, .lastclose = exynos_drm_lastclose,
......
...@@ -74,6 +74,7 @@ struct exynos_drm_plane { ...@@ -74,6 +74,7 @@ struct exynos_drm_plane {
unsigned int v_ratio; unsigned int v_ratio;
dma_addr_t dma_addr[MAX_FB_BUFFER]; dma_addr_t dma_addr[MAX_FB_BUFFER];
unsigned int zpos; unsigned int zpos;
struct drm_framebuffer *pending_fb;
}; };
/* /*
...@@ -87,6 +88,8 @@ struct exynos_drm_plane { ...@@ -87,6 +88,8 @@ struct exynos_drm_plane {
* @disable_vblank: specific driver callback for disabling vblank interrupt. * @disable_vblank: specific driver callback for disabling vblank interrupt.
* @wait_for_vblank: wait for vblank interrupt to make sure that * @wait_for_vblank: wait for vblank interrupt to make sure that
* hardware overlay is updated. * hardware overlay is updated.
* @atomic_begin: prepare a window to receive a update
* @atomic_flush: mark the end of a window update
* @update_plane: apply hardware specific overlay data to registers. * @update_plane: apply hardware specific overlay data to registers.
* @disable_plane: disable hardware specific overlay. * @disable_plane: disable hardware specific overlay.
* @te_handler: trigger to transfer video image at the tearing effect * @te_handler: trigger to transfer video image at the tearing effect
...@@ -107,10 +110,14 @@ struct exynos_drm_crtc_ops { ...@@ -107,10 +110,14 @@ struct exynos_drm_crtc_ops {
int (*enable_vblank)(struct exynos_drm_crtc *crtc); int (*enable_vblank)(struct exynos_drm_crtc *crtc);
void (*disable_vblank)(struct exynos_drm_crtc *crtc); void (*disable_vblank)(struct exynos_drm_crtc *crtc);
void (*wait_for_vblank)(struct exynos_drm_crtc *crtc); void (*wait_for_vblank)(struct exynos_drm_crtc *crtc);
void (*atomic_begin)(struct exynos_drm_crtc *crtc,
struct exynos_drm_plane *plane);
void (*update_plane)(struct exynos_drm_crtc *crtc, void (*update_plane)(struct exynos_drm_crtc *crtc,
struct exynos_drm_plane *plane); struct exynos_drm_plane *plane);
void (*disable_plane)(struct exynos_drm_crtc *crtc, void (*disable_plane)(struct exynos_drm_crtc *crtc,
struct exynos_drm_plane *plane); struct exynos_drm_plane *plane);
void (*atomic_flush)(struct exynos_drm_crtc *crtc,
struct exynos_drm_plane *plane);
void (*te_handler)(struct exynos_drm_crtc *crtc); void (*te_handler)(struct exynos_drm_crtc *crtc);
void (*clock_enable)(struct exynos_drm_crtc *crtc, bool enable); void (*clock_enable)(struct exynos_drm_crtc *crtc, bool enable);
}; };
...@@ -129,6 +136,8 @@ struct exynos_drm_crtc_ops { ...@@ -129,6 +136,8 @@ struct exynos_drm_crtc_ops {
* this pipe value. * this pipe value.
* @enabled: if the crtc is enabled or not * @enabled: if the crtc is enabled or not
* @event: vblank event that is currently queued for flip * @event: vblank event that is currently queued for flip
* @wait_update: wait all pending planes updates to finish
* @pending_update: number of pending plane updates in this crtc
* @ops: pointer to callbacks for exynos drm specific functionality * @ops: pointer to callbacks for exynos drm specific functionality
* @ctx: A pointer to the crtc's implementation specific context * @ctx: A pointer to the crtc's implementation specific context
*/ */
...@@ -136,9 +145,9 @@ struct exynos_drm_crtc { ...@@ -136,9 +145,9 @@ struct exynos_drm_crtc {
struct drm_crtc base; struct drm_crtc base;
enum exynos_drm_output_type type; enum exynos_drm_output_type type;
unsigned int pipe; unsigned int pipe;
bool enabled;
wait_queue_head_t pending_flip_queue;
struct drm_pending_vblank_event *event; struct drm_pending_vblank_event *event;
wait_queue_head_t wait_update;
atomic_t pending_update;
const struct exynos_drm_crtc_ops *ops; const struct exynos_drm_crtc_ops *ops;
void *ctx; void *ctx;
}; };
...@@ -164,6 +173,9 @@ struct drm_exynos_file_private { ...@@ -164,6 +173,9 @@ struct drm_exynos_file_private {
* @da_space_size: size of device address space. * @da_space_size: size of device address space.
* if 0 then default value is used for it. * if 0 then default value is used for it.
* @pipe: the pipe number for this crtc/manager. * @pipe: the pipe number for this crtc/manager.
* @pending: the crtcs that have pending updates to finish
* @lock: protect access to @pending
* @wait: wait an atomic commit to finish
*/ */
struct exynos_drm_private { struct exynos_drm_private {
struct drm_fb_helper *fb_helper; struct drm_fb_helper *fb_helper;
...@@ -179,6 +191,11 @@ struct exynos_drm_private { ...@@ -179,6 +191,11 @@ struct exynos_drm_private {
unsigned long da_space_size; unsigned long da_space_size;
unsigned int pipe; unsigned int pipe;
/* for atomic commit */
u32 pending;
spinlock_t lock;
wait_queue_head_t wait;
}; };
/* /*
...@@ -237,6 +254,9 @@ static inline int exynos_dpi_bind(struct drm_device *dev, ...@@ -237,6 +254,9 @@ static inline int exynos_dpi_bind(struct drm_device *dev,
} }
#endif #endif
int exynos_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state,
bool async);
extern struct platform_driver fimd_driver; extern struct platform_driver fimd_driver;
extern struct platform_driver exynos5433_decon_driver; extern struct platform_driver exynos5433_decon_driver;
......
...@@ -267,41 +267,6 @@ static void exynos_drm_output_poll_changed(struct drm_device *dev) ...@@ -267,41 +267,6 @@ static void exynos_drm_output_poll_changed(struct drm_device *dev)
exynos_drm_fbdev_init(dev); exynos_drm_fbdev_init(dev);
} }
static int exynos_atomic_commit(struct drm_device *dev,
struct drm_atomic_state *state,
bool async)
{
int ret;
ret = drm_atomic_helper_prepare_planes(dev, state);
if (ret)
return ret;
/* This is the point of no return */
drm_atomic_helper_swap_state(dev, state);
drm_atomic_helper_commit_modeset_disables(dev, state);
drm_atomic_helper_commit_modeset_enables(dev, state);
/*
* Exynos can't update planes with CRTCs and encoders disabled,
* its updates routines, specially for FIMD, requires the clocks
* to be enabled. So it is necessary to handle the modeset operations
* *before* the commit_planes() step, this way it will always
* have the relevant clocks enabled to perform the update.
*/
drm_atomic_helper_commit_planes(dev, state);
drm_atomic_helper_cleanup_planes(dev, state);
drm_atomic_state_free(state);
return 0;
}
static const struct drm_mode_config_funcs exynos_drm_mode_config_funcs = { static const struct drm_mode_config_funcs exynos_drm_mode_config_funcs = {
.fb_create = exynos_user_fb_create, .fb_create = exynos_user_fb_create,
.output_poll_changed = exynos_drm_output_poll_changed, .output_poll_changed = exynos_drm_output_poll_changed,
......
...@@ -59,6 +59,7 @@ ...@@ -59,6 +59,7 @@
#define VIDWnALPHA1(win) (VIDW_ALPHA + 0x04 + (win) * 8) #define VIDWnALPHA1(win) (VIDW_ALPHA + 0x04 + (win) * 8)
#define VIDWx_BUF_START(win, buf) (VIDW_BUF_START(buf) + (win) * 8) #define VIDWx_BUF_START(win, buf) (VIDW_BUF_START(buf) + (win) * 8)
#define VIDWx_BUF_START_S(win, buf) (VIDW_BUF_START_S(buf) + (win) * 8)
#define VIDWx_BUF_END(win, buf) (VIDW_BUF_END(buf) + (win) * 8) #define VIDWx_BUF_END(win, buf) (VIDW_BUF_END(buf) + (win) * 8)
#define VIDWx_BUF_SIZE(win, buf) (VIDW_BUF_SIZE(buf) + (win) * 4) #define VIDWx_BUF_SIZE(win, buf) (VIDW_BUF_SIZE(buf) + (win) * 4)
...@@ -187,6 +188,14 @@ static const struct of_device_id fimd_driver_dt_match[] = { ...@@ -187,6 +188,14 @@ static const struct of_device_id fimd_driver_dt_match[] = {
}; };
MODULE_DEVICE_TABLE(of, fimd_driver_dt_match); MODULE_DEVICE_TABLE(of, fimd_driver_dt_match);
static const uint32_t fimd_formats[] = {
DRM_FORMAT_C8,
DRM_FORMAT_XRGB1555,
DRM_FORMAT_RGB565,
DRM_FORMAT_XRGB8888,
DRM_FORMAT_ARGB8888,
};
static inline struct fimd_driver_data *drm_fimd_get_driver_data( static inline struct fimd_driver_data *drm_fimd_get_driver_data(
struct platform_device *pdev) struct platform_device *pdev)
{ {
...@@ -591,6 +600,16 @@ static void fimd_shadow_protect_win(struct fimd_context *ctx, ...@@ -591,6 +600,16 @@ static void fimd_shadow_protect_win(struct fimd_context *ctx,
{ {
u32 reg, bits, val; u32 reg, bits, val;
/*
* SHADOWCON/PRTCON register is used for enabling timing.
*
* for example, once only width value of a register is set,
* if the dma is started then fimd hardware could malfunction so
* with protect window setting, the register fields with prefix '_F'
* wouldn't be updated at vsync also but updated once unprotect window
* is set.
*/
if (ctx->driver_data->has_shadowcon) { if (ctx->driver_data->has_shadowcon) {
reg = SHADOWCON; reg = SHADOWCON;
bits = SHADOWCON_WINx_PROTECT(win); bits = SHADOWCON_WINx_PROTECT(win);
...@@ -607,6 +626,28 @@ static void fimd_shadow_protect_win(struct fimd_context *ctx, ...@@ -607,6 +626,28 @@ static void fimd_shadow_protect_win(struct fimd_context *ctx,
writel(val, ctx->regs + reg); writel(val, ctx->regs + reg);
} }
static void fimd_atomic_begin(struct exynos_drm_crtc *crtc,
struct exynos_drm_plane *plane)
{
struct fimd_context *ctx = crtc->ctx;
if (ctx->suspended)
return;
fimd_shadow_protect_win(ctx, plane->zpos, true);
}
static void fimd_atomic_flush(struct exynos_drm_crtc *crtc,
struct exynos_drm_plane *plane)
{
struct fimd_context *ctx = crtc->ctx;
if (ctx->suspended)
return;
fimd_shadow_protect_win(ctx, plane->zpos, false);
}
static void fimd_update_plane(struct exynos_drm_crtc *crtc, static void fimd_update_plane(struct exynos_drm_crtc *crtc,
struct exynos_drm_plane *plane) struct exynos_drm_plane *plane)
{ {
...@@ -622,20 +663,6 @@ static void fimd_update_plane(struct exynos_drm_crtc *crtc, ...@@ -622,20 +663,6 @@ static void fimd_update_plane(struct exynos_drm_crtc *crtc,
if (ctx->suspended) if (ctx->suspended)
return; return;
/*
* SHADOWCON/PRTCON register is used for enabling timing.
*
* for example, once only width value of a register is set,
* if the dma is started then fimd hardware could malfunction so
* with protect window setting, the register fields with prefix '_F'
* wouldn't be updated at vsync also but updated once unprotect window
* is set.
*/
/* protect windows */
fimd_shadow_protect_win(ctx, win, true);
offset = plane->src_x * bpp; offset = plane->src_x * bpp;
offset += plane->src_y * pitch; offset += plane->src_y * pitch;
...@@ -707,9 +734,6 @@ static void fimd_update_plane(struct exynos_drm_crtc *crtc, ...@@ -707,9 +734,6 @@ static void fimd_update_plane(struct exynos_drm_crtc *crtc,
if (ctx->driver_data->has_shadowcon) if (ctx->driver_data->has_shadowcon)
fimd_enable_shadow_channel_path(ctx, win, true); fimd_enable_shadow_channel_path(ctx, win, true);
/* Enable DMA channel and unprotect windows */
fimd_shadow_protect_win(ctx, win, false);
if (ctx->i80_if) if (ctx->i80_if)
atomic_set(&ctx->win_updated, 1); atomic_set(&ctx->win_updated, 1);
} }
...@@ -723,16 +747,10 @@ static void fimd_disable_plane(struct exynos_drm_crtc *crtc, ...@@ -723,16 +747,10 @@ static void fimd_disable_plane(struct exynos_drm_crtc *crtc,
if (ctx->suspended) if (ctx->suspended)
return; return;
/* protect windows */
fimd_shadow_protect_win(ctx, win, true);
fimd_enable_video_output(ctx, win, false); fimd_enable_video_output(ctx, win, false);
if (ctx->driver_data->has_shadowcon) if (ctx->driver_data->has_shadowcon)
fimd_enable_shadow_channel_path(ctx, win, false); fimd_enable_shadow_channel_path(ctx, win, false);
/* unprotect windows */
fimd_shadow_protect_win(ctx, win, false);
} }
static void fimd_enable(struct exynos_drm_crtc *crtc) static void fimd_enable(struct exynos_drm_crtc *crtc)
...@@ -875,8 +893,10 @@ static const struct exynos_drm_crtc_ops fimd_crtc_ops = { ...@@ -875,8 +893,10 @@ static const struct exynos_drm_crtc_ops fimd_crtc_ops = {
.enable_vblank = fimd_enable_vblank, .enable_vblank = fimd_enable_vblank,
.disable_vblank = fimd_disable_vblank, .disable_vblank = fimd_disable_vblank,
.wait_for_vblank = fimd_wait_for_vblank, .wait_for_vblank = fimd_wait_for_vblank,
.atomic_begin = fimd_atomic_begin,
.update_plane = fimd_update_plane, .update_plane = fimd_update_plane,
.disable_plane = fimd_disable_plane, .disable_plane = fimd_disable_plane,
.atomic_flush = fimd_atomic_flush,
.te_handler = fimd_te_handler, .te_handler = fimd_te_handler,
.clock_enable = fimd_dp_clock_enable, .clock_enable = fimd_dp_clock_enable,
}; };
...@@ -884,7 +904,8 @@ static const struct exynos_drm_crtc_ops fimd_crtc_ops = { ...@@ -884,7 +904,8 @@ static const struct exynos_drm_crtc_ops fimd_crtc_ops = {
static irqreturn_t fimd_irq_handler(int irq, void *dev_id) static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
{ {
struct fimd_context *ctx = (struct fimd_context *)dev_id; struct fimd_context *ctx = (struct fimd_context *)dev_id;
u32 val, clear_bit; u32 val, clear_bit, start, start_s;
int win;
val = readl(ctx->regs + VIDINTCON1); val = readl(ctx->regs + VIDINTCON1);
...@@ -896,15 +917,25 @@ static irqreturn_t fimd_irq_handler(int irq, void *dev_id) ...@@ -896,15 +917,25 @@ static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
if (ctx->pipe < 0 || !ctx->drm_dev) if (ctx->pipe < 0 || !ctx->drm_dev)
goto out; goto out;
if (ctx->i80_if) { if (!ctx->i80_if)
exynos_drm_crtc_finish_pageflip(ctx->crtc); drm_crtc_handle_vblank(&ctx->crtc->base);
for (win = 0 ; win < WINDOWS_NR ; win++) {
struct exynos_drm_plane *plane = &ctx->planes[win];
if (!plane->pending_fb)
continue;
start = readl(ctx->regs + VIDWx_BUF_START(win, 0));
start_s = readl(ctx->regs + VIDWx_BUF_START_S(win, 0));
if (start == start_s)
exynos_drm_crtc_finish_update(ctx->crtc, plane);
}
if (ctx->i80_if) {
/* Exits triggering mode */ /* Exits triggering mode */
atomic_set(&ctx->triggering, 0); atomic_set(&ctx->triggering, 0);
} else { } else {
drm_crtc_handle_vblank(&ctx->crtc->base);
exynos_drm_crtc_finish_pageflip(ctx->crtc);
/* set wait vsync event to zero and wake up queue. */ /* set wait vsync event to zero and wake up queue. */
if (atomic_read(&ctx->wait_vsync_event)) { if (atomic_read(&ctx->wait_vsync_event)) {
atomic_set(&ctx->wait_vsync_event, 0); atomic_set(&ctx->wait_vsync_event, 0);
...@@ -933,7 +964,8 @@ static int fimd_bind(struct device *dev, struct device *master, void *data) ...@@ -933,7 +964,8 @@ static int fimd_bind(struct device *dev, struct device *master, void *data)
type = (zpos == ctx->default_win) ? DRM_PLANE_TYPE_PRIMARY : type = (zpos == ctx->default_win) ? DRM_PLANE_TYPE_PRIMARY :
DRM_PLANE_TYPE_OVERLAY; DRM_PLANE_TYPE_OVERLAY;
ret = exynos_plane_init(drm_dev, &ctx->planes[zpos], ret = exynos_plane_init(drm_dev, &ctx->planes[zpos],
1 << ctx->pipe, type, zpos); 1 << ctx->pipe, type, fimd_formats,
ARRAY_SIZE(fimd_formats), zpos);
if (ret) if (ret)
return ret; return ret;
} }
......
...@@ -668,7 +668,7 @@ exynos_drm_gem_prime_import_sg_table(struct drm_device *dev, ...@@ -668,7 +668,7 @@ exynos_drm_gem_prime_import_sg_table(struct drm_device *dev,
exynos_gem_obj = exynos_drm_gem_init(dev, attach->dmabuf->size); exynos_gem_obj = exynos_drm_gem_init(dev, attach->dmabuf->size);
if (IS_ERR(exynos_gem_obj)) { if (IS_ERR(exynos_gem_obj)) {
ret = PTR_ERR(exynos_gem_obj); ret = PTR_ERR(exynos_gem_obj);
goto err; return ERR_PTR(ret);
} }
exynos_gem_obj->dma_addr = sg_dma_address(sgt->sgl); exynos_gem_obj->dma_addr = sg_dma_address(sgt->sgl);
......
...@@ -20,12 +20,6 @@ ...@@ -20,12 +20,6 @@
#include "exynos_drm_gem.h" #include "exynos_drm_gem.h"
#include "exynos_drm_plane.h" #include "exynos_drm_plane.h"
static const uint32_t formats[] = {
DRM_FORMAT_XRGB8888,
DRM_FORMAT_ARGB8888,
DRM_FORMAT_NV12,
};
/* /*
* This function is to get X or Y size shown via screen. This needs length and * This function is to get X or Y size shown via screen. This needs length and
* start position of CRTC. * start position of CRTC.
...@@ -168,6 +162,8 @@ static void exynos_plane_atomic_update(struct drm_plane *plane, ...@@ -168,6 +162,8 @@ static void exynos_plane_atomic_update(struct drm_plane *plane,
state->src_x >> 16, state->src_y >> 16, state->src_x >> 16, state->src_y >> 16,
state->src_w >> 16, state->src_h >> 16); state->src_w >> 16, state->src_h >> 16);
exynos_plane->pending_fb = state->fb;
if (exynos_crtc->ops->update_plane) if (exynos_crtc->ops->update_plane)
exynos_crtc->ops->update_plane(exynos_crtc, exynos_plane); exynos_crtc->ops->update_plane(exynos_crtc, exynos_plane);
} }
...@@ -215,13 +211,14 @@ static void exynos_plane_attach_zpos_property(struct drm_plane *plane, ...@@ -215,13 +211,14 @@ static void exynos_plane_attach_zpos_property(struct drm_plane *plane,
int exynos_plane_init(struct drm_device *dev, int exynos_plane_init(struct drm_device *dev,
struct exynos_drm_plane *exynos_plane, struct exynos_drm_plane *exynos_plane,
unsigned long possible_crtcs, enum drm_plane_type type, unsigned long possible_crtcs, enum drm_plane_type type,
const uint32_t *formats, unsigned int fcount,
unsigned int zpos) unsigned int zpos)
{ {
int err; int err;
err = drm_universal_plane_init(dev, &exynos_plane->base, possible_crtcs, err = drm_universal_plane_init(dev, &exynos_plane->base, possible_crtcs,
&exynos_plane_funcs, formats, &exynos_plane_funcs, formats, fcount,
ARRAY_SIZE(formats), type); type);
if (err) { if (err) {
DRM_ERROR("failed to initialize plane\n"); DRM_ERROR("failed to initialize plane\n");
return err; return err;
......
...@@ -12,4 +12,5 @@ ...@@ -12,4 +12,5 @@
int exynos_plane_init(struct drm_device *dev, int exynos_plane_init(struct drm_device *dev,
struct exynos_drm_plane *exynos_plane, struct exynos_drm_plane *exynos_plane,
unsigned long possible_crtcs, enum drm_plane_type type, unsigned long possible_crtcs, enum drm_plane_type type,
const uint32_t *formats, unsigned int fcount,
unsigned int zpos); unsigned int zpos);
...@@ -83,6 +83,12 @@ static const char fake_edid_info[] = { ...@@ -83,6 +83,12 @@ static const char fake_edid_info[] = {
0x00, 0x00, 0x00, 0x06 0x00, 0x00, 0x00, 0x06
}; };
static const uint32_t formats[] = {
DRM_FORMAT_XRGB8888,
DRM_FORMAT_ARGB8888,
DRM_FORMAT_NV12,
};
static int vidi_enable_vblank(struct exynos_drm_crtc *crtc) static int vidi_enable_vblank(struct exynos_drm_crtc *crtc)
{ {
struct vidi_context *ctx = crtc->ctx; struct vidi_context *ctx = crtc->ctx;
...@@ -179,6 +185,7 @@ static void vidi_fake_vblank_handler(struct work_struct *work) ...@@ -179,6 +185,7 @@ static void vidi_fake_vblank_handler(struct work_struct *work)
{ {
struct vidi_context *ctx = container_of(work, struct vidi_context, struct vidi_context *ctx = container_of(work, struct vidi_context,
work); work);
int win;
if (ctx->pipe < 0) if (ctx->pipe < 0)
return; return;
...@@ -197,7 +204,14 @@ static void vidi_fake_vblank_handler(struct work_struct *work) ...@@ -197,7 +204,14 @@ static void vidi_fake_vblank_handler(struct work_struct *work)
mutex_unlock(&ctx->lock); mutex_unlock(&ctx->lock);
exynos_drm_crtc_finish_pageflip(ctx->crtc); for (win = 0 ; win < WINDOWS_NR ; win++) {
struct exynos_drm_plane *plane = &ctx->planes[win];
if (!plane->pending_fb)
continue;
exynos_drm_crtc_finish_update(ctx->crtc, plane);
}
} }
static int vidi_show_connection(struct device *dev, static int vidi_show_connection(struct device *dev,
...@@ -435,7 +449,8 @@ static int vidi_bind(struct device *dev, struct device *master, void *data) ...@@ -435,7 +449,8 @@ static int vidi_bind(struct device *dev, struct device *master, void *data)
type = (zpos == ctx->default_win) ? DRM_PLANE_TYPE_PRIMARY : type = (zpos == ctx->default_win) ? DRM_PLANE_TYPE_PRIMARY :
DRM_PLANE_TYPE_OVERLAY; DRM_PLANE_TYPE_OVERLAY;
ret = exynos_plane_init(drm_dev, &ctx->planes[zpos], ret = exynos_plane_init(drm_dev, &ctx->planes[zpos],
1 << ctx->pipe, type, zpos); 1 << ctx->pipe, type, formats,
ARRAY_SIZE(formats), zpos);
if (ret) if (ret)
return ret; return ret;
} }
......
...@@ -43,6 +43,7 @@ ...@@ -43,6 +43,7 @@
#define MIXER_WIN_NR 3 #define MIXER_WIN_NR 3
#define MIXER_DEFAULT_WIN 0 #define MIXER_DEFAULT_WIN 0
#define VP_DEFAULT_WIN 2
/* The pixelformats that are natively supported by the mixer. */ /* The pixelformats that are natively supported by the mixer. */
#define MXR_FORMAT_RGB565 4 #define MXR_FORMAT_RGB565 4
...@@ -74,6 +75,19 @@ enum mixer_flag_bits { ...@@ -74,6 +75,19 @@ enum mixer_flag_bits {
MXR_BIT_VSYNC, MXR_BIT_VSYNC,
}; };
static const uint32_t mixer_formats[] = {
DRM_FORMAT_XRGB4444,
DRM_FORMAT_XRGB1555,
DRM_FORMAT_RGB565,
DRM_FORMAT_XRGB8888,
DRM_FORMAT_ARGB8888,
};
static const uint32_t vp_formats[] = {
DRM_FORMAT_NV12,
DRM_FORMAT_NV21,
};
struct mixer_context { struct mixer_context {
struct platform_device *pdev; struct platform_device *pdev;
struct device *dev; struct device *dev;
...@@ -716,6 +730,7 @@ static irqreturn_t mixer_irq_handler(int irq, void *arg) ...@@ -716,6 +730,7 @@ static irqreturn_t mixer_irq_handler(int irq, void *arg)
struct mixer_context *ctx = arg; struct mixer_context *ctx = arg;
struct mixer_resources *res = &ctx->mixer_res; struct mixer_resources *res = &ctx->mixer_res;
u32 val, base, shadow; u32 val, base, shadow;
int win;
spin_lock(&res->reg_slock); spin_lock(&res->reg_slock);
...@@ -742,7 +757,14 @@ static irqreturn_t mixer_irq_handler(int irq, void *arg) ...@@ -742,7 +757,14 @@ static irqreturn_t mixer_irq_handler(int irq, void *arg)
} }
drm_crtc_handle_vblank(&ctx->crtc->base); drm_crtc_handle_vblank(&ctx->crtc->base);
exynos_drm_crtc_finish_pageflip(ctx->crtc); for (win = 0 ; win < MIXER_WIN_NR ; win++) {
struct exynos_drm_plane *plane = &ctx->planes[win];
if (!plane->pending_fb)
continue;
exynos_drm_crtc_finish_update(ctx->crtc, plane);
}
/* set wait vsync event to zero and wake up queue. */ /* set wait vsync event to zero and wake up queue. */
if (atomic_read(&ctx->wait_vsync_event)) { if (atomic_read(&ctx->wait_vsync_event)) {
...@@ -1163,7 +1185,6 @@ static int mixer_bind(struct device *dev, struct device *manager, void *data) ...@@ -1163,7 +1185,6 @@ static int mixer_bind(struct device *dev, struct device *manager, void *data)
struct mixer_context *ctx = dev_get_drvdata(dev); struct mixer_context *ctx = dev_get_drvdata(dev);
struct drm_device *drm_dev = data; struct drm_device *drm_dev = data;
struct exynos_drm_plane *exynos_plane; struct exynos_drm_plane *exynos_plane;
enum drm_plane_type type;
unsigned int zpos; unsigned int zpos;
int ret; int ret;
...@@ -1172,10 +1193,23 @@ static int mixer_bind(struct device *dev, struct device *manager, void *data) ...@@ -1172,10 +1193,23 @@ static int mixer_bind(struct device *dev, struct device *manager, void *data)
return ret; return ret;
for (zpos = 0; zpos < MIXER_WIN_NR; zpos++) { for (zpos = 0; zpos < MIXER_WIN_NR; zpos++) {
enum drm_plane_type type;
const uint32_t *formats;
unsigned int fcount;
type = (zpos == MIXER_DEFAULT_WIN) ? DRM_PLANE_TYPE_PRIMARY : type = (zpos == MIXER_DEFAULT_WIN) ? DRM_PLANE_TYPE_PRIMARY :
DRM_PLANE_TYPE_OVERLAY; DRM_PLANE_TYPE_OVERLAY;
if (zpos < VP_DEFAULT_WIN) {
formats = mixer_formats;
fcount = ARRAY_SIZE(mixer_formats);
} else {
formats = vp_formats;
fcount = ARRAY_SIZE(vp_formats);
}
ret = exynos_plane_init(drm_dev, &ctx->planes[zpos], ret = exynos_plane_init(drm_dev, &ctx->planes[zpos],
1 << ctx->pipe, type, zpos); 1 << ctx->pipe, type, formats, fcount,
zpos);
if (ret) if (ret)
return ret; return ret;
} }
......
...@@ -296,6 +296,7 @@ ...@@ -296,6 +296,7 @@
/* Video buffer addresses */ /* Video buffer addresses */
#define VIDW_BUF_START(_buff) (0xA0 + ((_buff) * 8)) #define VIDW_BUF_START(_buff) (0xA0 + ((_buff) * 8))
#define VIDW_BUF_START_S(_buff) (0x40A0 + ((_buff) * 8))
#define VIDW_BUF_START1(_buff) (0xA4 + ((_buff) * 8)) #define VIDW_BUF_START1(_buff) (0xA4 + ((_buff) * 8))
#define VIDW_BUF_END(_buff) (0xD0 + ((_buff) * 8)) #define VIDW_BUF_END(_buff) (0xD0 + ((_buff) * 8))
#define VIDW_BUF_END1(_buff) (0xD4 + ((_buff) * 8)) #define VIDW_BUF_END1(_buff) (0xD4 + ((_buff) * 8))
......
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