summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/exynos/exynos7_drm_decon.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/exynos/exynos7_drm_decon.c')
-rw-r--r--drivers/gpu/drm/exynos/exynos7_drm_decon.c163
1 files changed, 84 insertions, 79 deletions
diff --git a/drivers/gpu/drm/exynos/exynos7_drm_decon.c b/drivers/gpu/drm/exynos/exynos7_drm_decon.c
index 0d185c0564b9..bb74b17f9753 100644
--- a/drivers/gpu/drm/exynos/exynos7_drm_decon.c
+++ b/drivers/gpu/drm/exynos/exynos7_drm_decon.c
@@ -20,6 +20,7 @@
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
+#include <drm/drm_print.h>
#include <drm/drm_vblank.h>
#include <drm/exynos_drm.h>
@@ -37,6 +38,24 @@
#define WINDOWS_NR 2
+struct decon_data {
+ unsigned int vidw_buf_start_base;
+ unsigned int shadowcon_win_protect_shift;
+ unsigned int wincon_burstlen_shift;
+};
+
+static const struct decon_data exynos7_decon_data = {
+ .vidw_buf_start_base = 0x80,
+ .shadowcon_win_protect_shift = 10,
+ .wincon_burstlen_shift = 11,
+};
+
+static const struct decon_data exynos7870_decon_data = {
+ .vidw_buf_start_base = 0x880,
+ .shadowcon_win_protect_shift = 8,
+ .wincon_burstlen_shift = 10,
+};
+
struct decon_context {
struct device *dev;
struct drm_device *drm_dev;
@@ -51,15 +70,22 @@ struct decon_context {
void __iomem *regs;
unsigned long irq_flags;
bool i80_if;
- bool suspended;
wait_queue_head_t wait_vsync_queue;
atomic_t wait_vsync_event;
+ const struct decon_data *data;
struct drm_encoder *encoder;
};
static const struct of_device_id decon_driver_dt_match[] = {
- {.compatible = "samsung,exynos7-decon"},
+ {
+ .compatible = "samsung,exynos7-decon",
+ .data = &exynos7_decon_data,
+ },
+ {
+ .compatible = "samsung,exynos7870-decon",
+ .data = &exynos7870_decon_data,
+ },
{},
};
MODULE_DEVICE_TABLE(of, decon_driver_dt_match);
@@ -81,13 +107,31 @@ static const enum drm_plane_type decon_win_types[WINDOWS_NR] = {
DRM_PLANE_TYPE_CURSOR,
};
-static void decon_wait_for_vblank(struct exynos_drm_crtc *crtc)
+/**
+ * decon_shadow_protect_win() - disable updating values from shadow registers at vsync
+ *
+ * @ctx: display and enhancement controller context
+ * @win: window to protect registers for
+ * @protect: 1 to protect (disable updates)
+ */
+static void decon_shadow_protect_win(struct decon_context *ctx,
+ unsigned int win, bool protect)
{
- struct decon_context *ctx = crtc->ctx;
+ u32 bits, val;
+ unsigned int shift = ctx->data->shadowcon_win_protect_shift;
- if (ctx->suspended)
- return;
+ bits = SHADOWCON_WINx_PROTECT(shift, win);
+ val = readl(ctx->regs + SHADOWCON);
+ if (protect)
+ val |= bits;
+ else
+ val &= ~bits;
+ writel(val, ctx->regs + SHADOWCON);
+}
+
+static void decon_wait_for_vblank(struct decon_context *ctx)
+{
atomic_set(&ctx->wait_vsync_event, 1);
/*
@@ -100,25 +144,33 @@ static void decon_wait_for_vblank(struct exynos_drm_crtc *crtc)
DRM_DEV_DEBUG_KMS(ctx->dev, "vblank wait timed out.\n");
}
-static void decon_clear_channels(struct exynos_drm_crtc *crtc)
+static void decon_clear_channels(struct decon_context *ctx)
{
- struct decon_context *ctx = crtc->ctx;
unsigned int win, ch_enabled = 0;
+ u32 val;
/* Check if any channel is enabled. */
for (win = 0; win < WINDOWS_NR; win++) {
- u32 val = readl(ctx->regs + WINCON(win));
+ val = readl(ctx->regs + WINCON(win));
if (val & WINCONx_ENWIN) {
+ decon_shadow_protect_win(ctx, win, true);
+
val &= ~WINCONx_ENWIN;
writel(val, ctx->regs + WINCON(win));
ch_enabled = 1;
+
+ decon_shadow_protect_win(ctx, win, false);
}
}
+ val = readl(ctx->regs + DECON_UPDATE);
+ val |= DECON_UPDATE_STANDALONE_F;
+ writel(val, ctx->regs + DECON_UPDATE);
+
/* Wait for vsync, as disable channel takes effect at next vsync */
if (ch_enabled)
- decon_wait_for_vblank(ctx->crtc);
+ decon_wait_for_vblank(ctx);
}
static int decon_ctx_initialize(struct decon_context *ctx,
@@ -126,7 +178,7 @@ static int decon_ctx_initialize(struct decon_context *ctx,
{
ctx->drm_dev = drm_dev;
- decon_clear_channels(ctx->crtc);
+ decon_clear_channels(ctx);
return exynos_drm_register_dma(drm_dev, ctx->dev, &ctx->dma_priv);
}
@@ -140,7 +192,7 @@ static void decon_ctx_remove(struct decon_context *ctx)
static u32 decon_calc_clkdiv(struct decon_context *ctx,
const struct drm_display_mode *mode)
{
- unsigned long ideal_clk = mode->clock;
+ unsigned long ideal_clk = mode->clock * 1000;
u32 clkdiv;
/* Find the clock divider value that gets us closest to ideal_clk */
@@ -155,9 +207,6 @@ static void decon_commit(struct exynos_drm_crtc *crtc)
struct drm_display_mode *mode = &crtc->base.state->adjusted_mode;
u32 val, clkdiv;
- if (ctx->suspended)
- return;
-
/* nothing to do if we haven't set the mode yet */
if (mode->htotal == 0 || mode->vtotal == 0)
return;
@@ -219,9 +268,6 @@ static int decon_enable_vblank(struct exynos_drm_crtc *crtc)
struct decon_context *ctx = crtc->ctx;
u32 val;
- if (ctx->suspended)
- return -EPERM;
-
if (!test_and_set_bit(0, &ctx->irq_flags)) {
val = readl(ctx->regs + VIDINTCON0);
@@ -244,9 +290,6 @@ static void decon_disable_vblank(struct exynos_drm_crtc *crtc)
struct decon_context *ctx = crtc->ctx;
u32 val;
- if (ctx->suspended)
- return;
-
if (test_and_clear_bit(0, &ctx->irq_flags)) {
val = readl(ctx->regs + VIDINTCON0);
@@ -263,6 +306,7 @@ static void decon_win_set_pixfmt(struct decon_context *ctx, unsigned int win,
{
unsigned long val;
int padding;
+ unsigned int shift = ctx->data->wincon_burstlen_shift;
val = readl(ctx->regs + WINCON(win));
val &= ~WINCONx_BPPMODE_MASK;
@@ -270,44 +314,44 @@ static void decon_win_set_pixfmt(struct decon_context *ctx, unsigned int win,
switch (fb->format->format) {
case DRM_FORMAT_RGB565:
val |= WINCONx_BPPMODE_16BPP_565;
- val |= WINCONx_BURSTLEN_16WORD;
+ val |= WINCONx_BURSTLEN_16WORD(shift);
break;
case DRM_FORMAT_XRGB8888:
val |= WINCONx_BPPMODE_24BPP_xRGB;
- val |= WINCONx_BURSTLEN_16WORD;
+ val |= WINCONx_BURSTLEN_16WORD(shift);
break;
case DRM_FORMAT_XBGR8888:
val |= WINCONx_BPPMODE_24BPP_xBGR;
- val |= WINCONx_BURSTLEN_16WORD;
+ val |= WINCONx_BURSTLEN_16WORD(shift);
break;
case DRM_FORMAT_RGBX8888:
val |= WINCONx_BPPMODE_24BPP_RGBx;
- val |= WINCONx_BURSTLEN_16WORD;
+ val |= WINCONx_BURSTLEN_16WORD(shift);
break;
case DRM_FORMAT_BGRX8888:
val |= WINCONx_BPPMODE_24BPP_BGRx;
- val |= WINCONx_BURSTLEN_16WORD;
+ val |= WINCONx_BURSTLEN_16WORD(shift);
break;
case DRM_FORMAT_ARGB8888:
val |= WINCONx_BPPMODE_32BPP_ARGB | WINCONx_BLD_PIX |
WINCONx_ALPHA_SEL;
- val |= WINCONx_BURSTLEN_16WORD;
+ val |= WINCONx_BURSTLEN_16WORD(shift);
break;
case DRM_FORMAT_ABGR8888:
val |= WINCONx_BPPMODE_32BPP_ABGR | WINCONx_BLD_PIX |
WINCONx_ALPHA_SEL;
- val |= WINCONx_BURSTLEN_16WORD;
+ val |= WINCONx_BURSTLEN_16WORD(shift);
break;
case DRM_FORMAT_RGBA8888:
val |= WINCONx_BPPMODE_32BPP_RGBA | WINCONx_BLD_PIX |
WINCONx_ALPHA_SEL;
- val |= WINCONx_BURSTLEN_16WORD;
+ val |= WINCONx_BURSTLEN_16WORD(shift);
break;
case DRM_FORMAT_BGRA8888:
default:
val |= WINCONx_BPPMODE_32BPP_BGRA | WINCONx_BLD_PIX |
WINCONx_ALPHA_SEL;
- val |= WINCONx_BURSTLEN_16WORD;
+ val |= WINCONx_BURSTLEN_16WORD(shift);
break;
}
@@ -323,8 +367,8 @@ static void decon_win_set_pixfmt(struct decon_context *ctx, unsigned int win,
padding = (fb->pitches[0] / fb->format->cpp[0]) - fb->width;
if (fb->width + padding < MIN_FB_WIDTH_FOR_16WORD_BURST) {
- val &= ~WINCONx_BURSTLEN_MASK;
- val |= WINCONx_BURSTLEN_8WORD;
+ val &= ~WINCONx_BURSTLEN_MASK(shift);
+ val |= WINCONx_BURSTLEN_8WORD(shift);
}
writel(val, ctx->regs + WINCON(win));
@@ -343,36 +387,11 @@ static void decon_win_set_colkey(struct decon_context *ctx, unsigned int win)
writel(keycon1, ctx->regs + WKEYCON1_BASE(win));
}
-/**
- * decon_shadow_protect_win() - disable updating values from shadow registers at vsync
- *
- * @ctx: display and enhancement controller context
- * @win: window to protect registers for
- * @protect: 1 to protect (disable updates)
- */
-static void decon_shadow_protect_win(struct decon_context *ctx,
- unsigned int win, bool protect)
-{
- u32 bits, val;
-
- bits = SHADOWCON_WINx_PROTECT(win);
-
- val = readl(ctx->regs + SHADOWCON);
- if (protect)
- val |= bits;
- else
- val &= ~bits;
- writel(val, ctx->regs + SHADOWCON);
-}
-
static void decon_atomic_begin(struct exynos_drm_crtc *crtc)
{
struct decon_context *ctx = crtc->ctx;
int i;
- if (ctx->suspended)
- return;
-
for (i = 0; i < WINDOWS_NR; i++)
decon_shadow_protect_win(ctx, i, true);
}
@@ -391,9 +410,7 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc,
unsigned int win = plane->index;
unsigned int cpp = fb->format->cpp[0];
unsigned int pitch = fb->pitches[0];
-
- if (ctx->suspended)
- return;
+ unsigned int vidw_addr0_base = ctx->data->vidw_buf_start_base;
/*
* SHADOWCON/PRTCON register is used for enabling timing.
@@ -407,7 +424,7 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc,
/* buffer start address */
val = (unsigned long)exynos_drm_fb_dma_addr(fb, 0);
- writel(val, ctx->regs + VIDW_BUF_START(win));
+ writel(val, ctx->regs + VIDW_BUF_START(vidw_addr0_base, win));
padding = (pitch / cpp) - fb->width;
@@ -482,9 +499,6 @@ static void decon_disable_plane(struct exynos_drm_crtc *crtc,
unsigned int win = plane->index;
u32 val;
- if (ctx->suspended)
- return;
-
/* protect windows */
decon_shadow_protect_win(ctx, win, true);
@@ -503,9 +517,6 @@ static void decon_atomic_flush(struct exynos_drm_crtc *crtc)
struct decon_context *ctx = crtc->ctx;
int i;
- if (ctx->suspended)
- return;
-
for (i = 0; i < WINDOWS_NR; i++)
decon_shadow_protect_win(ctx, i, false);
exynos_crtc_handle_event(crtc);
@@ -533,9 +544,6 @@ static void decon_atomic_enable(struct exynos_drm_crtc *crtc)
struct decon_context *ctx = crtc->ctx;
int ret;
- if (!ctx->suspended)
- return;
-
ret = pm_runtime_resume_and_get(ctx->dev);
if (ret < 0) {
DRM_DEV_ERROR(ctx->dev, "failed to enable DECON device.\n");
@@ -549,8 +557,6 @@ static void decon_atomic_enable(struct exynos_drm_crtc *crtc)
decon_enable_vblank(ctx->crtc);
decon_commit(ctx->crtc);
-
- ctx->suspended = false;
}
static void decon_atomic_disable(struct exynos_drm_crtc *crtc)
@@ -558,9 +564,6 @@ static void decon_atomic_disable(struct exynos_drm_crtc *crtc)
struct decon_context *ctx = crtc->ctx;
int i;
- if (ctx->suspended)
- return;
-
/*
* We need to make sure that all windows are disabled before we
* suspend that connector. Otherwise we might try to scan from
@@ -570,8 +573,6 @@ static void decon_atomic_disable(struct exynos_drm_crtc *crtc)
decon_disable_plane(crtc, &ctx->planes[i]);
pm_runtime_put_sync(ctx->dev);
-
- ctx->suspended = true;
}
static const struct exynos_drm_crtc_ops decon_crtc_ops = {
@@ -601,6 +602,10 @@ static irqreturn_t decon_irq_handler(int irq, void *dev_id)
if (!ctx->drm_dev)
goto out;
+ /* check if crtc and vblank have been initialized properly */
+ if (!drm_dev_has_vblank(ctx->drm_dev))
+ goto out;
+
if (!ctx->i80_if) {
drm_crtc_handle_vblank(&ctx->crtc->base);
@@ -688,7 +693,7 @@ static int decon_probe(struct platform_device *pdev)
return -ENOMEM;
ctx->dev = dev;
- ctx->suspended = true;
+ ctx->data = of_device_get_match_data(dev);
i80_if_timings = of_get_child_by_name(dev->of_node, "i80-if-timings");
if (i80_if_timings)
@@ -838,7 +843,7 @@ static DEFINE_RUNTIME_DEV_PM_OPS(exynos7_decon_pm_ops, exynos7_decon_suspend,
struct platform_driver decon_driver = {
.probe = decon_probe,
- .remove_new = decon_remove,
+ .remove = decon_remove,
.driver = {
.name = "exynos-decon",
.pm = pm_ptr(&exynos7_decon_pm_ops),