diff options
Diffstat (limited to 'drivers/gpu/drm/exynos/exynos7_drm_decon.c')
| -rw-r--r-- | drivers/gpu/drm/exynos/exynos7_drm_decon.c | 163 |
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), |
