These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / gpu / drm / exynos / exynos7_drm_decon.c
index 6714e5b..ead2b16 100644 (file)
  * DECON stands for Display and Enhancement controller.
  */
 
-#define DECON_DEFAULT_FRAMERATE 60
 #define MIN_FB_WIDTH_FOR_16WORD_BURST 128
 
 #define WINDOWS_NR     2
+#define CURSOR_WIN     1
 
 struct decon_context {
        struct device                   *dev;
@@ -52,7 +52,6 @@ struct decon_context {
        struct clk                      *eclk;
        struct clk                      *vclk;
        void __iomem                    *regs;
-       unsigned int                    default_win;
        unsigned long                   irq_flags;
        bool                            i80_if;
        bool                            suspended;
@@ -61,7 +60,7 @@ struct decon_context {
        atomic_t                        wait_vsync_event;
 
        struct exynos_drm_panel_info panel;
-       struct exynos_drm_display *display;
+       struct drm_encoder *encoder;
 };
 
 static const struct of_device_id decon_driver_dt_match[] = {
@@ -70,6 +69,18 @@ static const struct of_device_id 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)
 {
        struct decon_context *ctx = crtc->ctx;
@@ -89,8 +100,9 @@ static void decon_wait_for_vblank(struct exynos_drm_crtc *crtc)
                DRM_DEBUG_KMS("vblank wait timed out.\n");
 }
 
-static void decon_clear_channel(struct decon_context *ctx)
+static void decon_clear_channels(struct exynos_drm_crtc *crtc)
 {
+       struct decon_context *ctx = crtc->ctx;
        unsigned int win, ch_enabled = 0;
 
        DRM_DEBUG_KMS("%s\n", __FILE__);
@@ -120,34 +132,24 @@ static int decon_ctx_initialize(struct decon_context *ctx,
                        struct drm_device *drm_dev)
 {
        struct exynos_drm_private *priv = drm_dev->dev_private;
+       int ret;
 
        ctx->drm_dev = drm_dev;
        ctx->pipe = priv->pipe++;
 
-       /* attach this sub driver to iommu mapping if supported. */
-       if (is_drm_iommu_supported(ctx->drm_dev)) {
-               int ret;
-
-               /*
-                * If any channel is already active, iommu will throw
-                * a PAGE FAULT when enabled. So clear any channel if enabled.
-                */
-               decon_clear_channel(ctx);
-               ret = drm_iommu_attach_device(ctx->drm_dev, ctx->dev);
-               if (ret) {
-                       DRM_ERROR("drm_iommu_attach failed.\n");
-                       return ret;
-               }
-       }
+       decon_clear_channels(ctx->crtc);
 
-       return 0;
+       ret = drm_iommu_attach_device(drm_dev, ctx->dev);
+       if (ret)
+               priv->pipe--;
+
+       return ret;
 }
 
 static void decon_ctx_remove(struct decon_context *ctx)
 {
        /* detach this sub driver from iommu mapping if supported. */
-       if (is_drm_iommu_supported(ctx->drm_dev))
-               drm_iommu_detach_device(ctx->drm_dev, ctx->dev);
+       drm_iommu_detach_device(ctx->drm_dev, ctx->dev);
 }
 
 static u32 decon_calc_clkdiv(struct decon_context *ctx,
@@ -162,20 +164,10 @@ static u32 decon_calc_clkdiv(struct decon_context *ctx,
        return (clkdiv < 0x100) ? clkdiv : 0xff;
 }
 
-static bool decon_mode_fixup(struct exynos_drm_crtc *crtc,
-               const struct drm_display_mode *mode,
-               struct drm_display_mode *adjusted_mode)
-{
-       if (adjusted_mode->vrefresh == 0)
-               adjusted_mode->vrefresh = DECON_DEFAULT_FRAMERATE;
-
-       return true;
-}
-
 static void decon_commit(struct exynos_drm_crtc *crtc)
 {
        struct decon_context *ctx = crtc->ctx;
-       struct drm_display_mode *mode = &crtc->base.mode;
+       struct drm_display_mode *mode = &crtc->base.state->adjusted_mode;
        u32 val, clkdiv;
 
        if (ctx->suspended)
@@ -281,16 +273,16 @@ static void decon_disable_vblank(struct exynos_drm_crtc *crtc)
        }
 }
 
-static void decon_win_set_pixfmt(struct decon_context *ctx, unsigned int win)
+static void decon_win_set_pixfmt(struct decon_context *ctx, unsigned int win,
+                                struct drm_framebuffer *fb)
 {
-       struct exynos_drm_plane *plane = &ctx->planes[win];
        unsigned long val;
        int padding;
 
        val = readl(ctx->regs + WINCON(win));
        val &= ~WINCONx_BPPMODE_MASK;
 
-       switch (plane->pixel_format) {
+       switch (fb->pixel_format) {
        case DRM_FORMAT_RGB565:
                val |= WINCONx_BPPMODE_16BPP_565;
                val |= WINCONx_BURSTLEN_16WORD;
@@ -339,7 +331,7 @@ static void decon_win_set_pixfmt(struct decon_context *ctx, unsigned int win)
                break;
        }
 
-       DRM_DEBUG_KMS("bpp = %d\n", plane->bpp);
+       DRM_DEBUG_KMS("bpp = %d\n", fb->bits_per_pixel);
 
        /*
         * In case of exynos, setting dma-burst to 16Word causes permanent
@@ -349,8 +341,8 @@ static void decon_win_set_pixfmt(struct decon_context *ctx, unsigned int win)
         * movement causes unstable DMA which results into iommu crash/tear.
         */
 
-       padding = (plane->pitch / (plane->bpp >> 3)) - plane->fb_width;
-       if (plane->fb_width + padding < MIN_FB_WIDTH_FOR_16WORD_BURST) {
+       padding = (fb->pitches[0] / (fb->bits_per_pixel >> 3)) - fb->width;
+       if (fb->width + padding < MIN_FB_WIDTH_FOR_16WORD_BURST) {
                val &= ~WINCONx_BURSTLEN_MASK;
                val |= WINCONx_BURSTLEN_8WORD;
        }
@@ -392,29 +384,33 @@ static void decon_shadow_protect_win(struct decon_context *ctx,
        writel(val, ctx->regs + SHADOWCON);
 }
 
-static void decon_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)
+static void decon_atomic_begin(struct exynos_drm_crtc *crtc,
+                                       struct exynos_drm_plane *plane)
 {
        struct decon_context *ctx = crtc->ctx;
-       struct drm_display_mode *mode = &crtc->base.mode;
-       struct exynos_drm_plane *plane;
-       int padding;
-       unsigned long val, alpha;
-       unsigned int last_x;
-       unsigned int last_y;
 
        if (ctx->suspended)
                return;
 
-       if (win < 0 || win >= WINDOWS_NR)
-               return;
+       decon_shadow_protect_win(ctx, plane->zpos, true);
+}
 
-       plane = &ctx->planes[win];
+static void decon_update_plane(struct exynos_drm_crtc *crtc,
+                              struct exynos_drm_plane *plane)
+{
+       struct decon_context *ctx = crtc->ctx;
+       struct drm_display_mode *mode = &crtc->base.state->adjusted_mode;
+       struct drm_plane_state *state = plane->base.state;
+       int padding;
+       unsigned long val, alpha;
+       unsigned int last_x;
+       unsigned int last_y;
+       unsigned int win = plane->zpos;
+       unsigned int bpp = state->fb->bits_per_pixel >> 3;
+       unsigned int pitch = state->fb->pitches[0];
 
-       /* If suspended, enable this on resume */
-       if (ctx->suspended) {
-               plane->resume = true;
+       if (ctx->suspended)
                return;
-       }
 
        /*
         * SHADOWCON/PRTCON register is used for enabling timing.
@@ -426,18 +422,15 @@ static void decon_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)
         * is set.
         */
 
-       /* protect windows */
-       decon_shadow_protect_win(ctx, win, true);
-
        /* buffer start address */
        val = (unsigned long)plane->dma_addr[0];
        writel(val, ctx->regs + VIDW_BUF_START(win));
 
-       padding = (plane->pitch / (plane->bpp >> 3)) - plane->fb_width;
+       padding = (pitch / bpp) - state->fb->width;
 
        /* buffer size */
-       writel(plane->fb_width + padding, ctx->regs + VIDW_WHOLE_X(win));
-       writel(plane->fb_height, ctx->regs + VIDW_WHOLE_Y(win));
+       writel(state->fb->width + padding, ctx->regs + VIDW_WHOLE_X(win));
+       writel(state->fb->height, ctx->regs + VIDW_WHOLE_Y(win));
 
        /* offset from the start of the buffer to read */
        writel(plane->src_x, ctx->regs + VIDW_OFFSET_X(win));
@@ -446,25 +439,25 @@ static void decon_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)
        DRM_DEBUG_KMS("start addr = 0x%lx\n",
                        (unsigned long)val);
        DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n",
-                       plane->crtc_width, plane->crtc_height);
+                       plane->crtc_w, plane->crtc_h);
 
        /*
         * OSD position.
         * In case the window layout goes of LCD layout, DECON fails.
         */
-       if ((plane->crtc_x + plane->crtc_width) > mode->hdisplay)
-               plane->crtc_x = mode->hdisplay - plane->crtc_width;
-       if ((plane->crtc_y + plane->crtc_height) > mode->vdisplay)
-               plane->crtc_y = mode->vdisplay - plane->crtc_height;
+       if ((plane->crtc_x + plane->crtc_w) > mode->hdisplay)
+               plane->crtc_x = mode->hdisplay - plane->crtc_w;
+       if ((plane->crtc_y + plane->crtc_h) > mode->vdisplay)
+               plane->crtc_y = mode->vdisplay - plane->crtc_h;
 
        val = VIDOSDxA_TOPLEFT_X(plane->crtc_x) |
                VIDOSDxA_TOPLEFT_Y(plane->crtc_y);
        writel(val, ctx->regs + VIDOSD_A(win));
 
-       last_x = plane->crtc_x + plane->crtc_width;
+       last_x = plane->crtc_x + plane->crtc_w;
        if (last_x)
                last_x--;
-       last_y = plane->crtc_y + plane->crtc_height;
+       last_y = plane->crtc_y + plane->crtc_h;
        if (last_y)
                last_y--;
 
@@ -488,7 +481,7 @@ static void decon_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)
 
        writel(alpha, ctx->regs + VIDOSD_D(win));
 
-       decon_win_set_pixfmt(ctx, win);
+       decon_win_set_pixfmt(ctx, win, state->fb);
 
        /* hardware window 0 doesn't support color key. */
        if (win != 0)
@@ -506,26 +499,17 @@ static void decon_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)
        val = readl(ctx->regs + DECON_UPDATE);
        val |= DECON_UPDATE_STANDALONE_F;
        writel(val, ctx->regs + DECON_UPDATE);
-
-       plane->enabled = true;
 }
 
-static void decon_win_disable(struct exynos_drm_crtc *crtc, unsigned int win)
+static void decon_disable_plane(struct exynos_drm_crtc *crtc,
+                               struct exynos_drm_plane *plane)
 {
        struct decon_context *ctx = crtc->ctx;
-       struct exynos_drm_plane *plane;
+       unsigned int win = plane->zpos;
        u32 val;
 
-       if (win < 0 || win >= WINDOWS_NR)
-               return;
-
-       plane = &ctx->planes[win];
-
-       if (ctx->suspended) {
-               /* do not resume this window*/
-               plane->resume = false;
+       if (ctx->suspended)
                return;
-       }
 
        /* protect windows */
        decon_shadow_protect_win(ctx, win, true);
@@ -535,55 +519,20 @@ static void decon_win_disable(struct exynos_drm_crtc *crtc, unsigned int win)
        val &= ~WINCONx_ENWIN;
        writel(val, ctx->regs + WINCON(win));
 
-       /* unprotect windows */
-       decon_shadow_protect_win(ctx, win, false);
-
        val = readl(ctx->regs + DECON_UPDATE);
        val |= DECON_UPDATE_STANDALONE_F;
        writel(val, ctx->regs + DECON_UPDATE);
-
-       plane->enabled = false;
-}
-
-static void decon_window_suspend(struct decon_context *ctx)
-{
-       struct exynos_drm_plane *plane;
-       int i;
-
-       for (i = 0; i < WINDOWS_NR; i++) {
-               plane = &ctx->planes[i];
-               plane->resume = plane->enabled;
-               if (plane->enabled)
-                       decon_win_disable(ctx->crtc, i);
-       }
 }
 
-static void decon_window_resume(struct decon_context *ctx)
+static void decon_atomic_flush(struct exynos_drm_crtc *crtc,
+                                       struct exynos_drm_plane *plane)
 {
-       struct exynos_drm_plane *plane;
-       int i;
-
-       for (i = 0; i < WINDOWS_NR; i++) {
-               plane = &ctx->planes[i];
-               plane->enabled = plane->resume;
-               plane->resume = false;
-       }
-}
-
-static void decon_apply(struct decon_context *ctx)
-{
-       struct exynos_drm_plane *plane;
-       int i;
+       struct decon_context *ctx = crtc->ctx;
 
-       for (i = 0; i < WINDOWS_NR; i++) {
-               plane = &ctx->planes[i];
-               if (plane->enabled)
-                       decon_win_commit(ctx->crtc, i);
-               else
-                       decon_win_disable(ctx->crtc, i);
-       }
+       if (ctx->suspended)
+               return;
 
-       decon_commit(ctx->crtc);
+       decon_shadow_protect_win(ctx, plane->zpos, false);
 }
 
 static void decon_init(struct decon_context *ctx)
@@ -603,12 +552,13 @@ static void decon_init(struct decon_context *ctx)
                writel(VIDCON1_VCLK_HOLD, ctx->regs + VIDCON1(0));
 }
 
-static int decon_poweron(struct decon_context *ctx)
+static void decon_enable(struct exynos_drm_crtc *crtc)
 {
+       struct decon_context *ctx = crtc->ctx;
        int ret;
 
        if (!ctx->suspended)
-               return 0;
+               return;
 
        ctx->suspended = false;
 
@@ -617,68 +567,51 @@ static int decon_poweron(struct decon_context *ctx)
        ret = clk_prepare_enable(ctx->pclk);
        if (ret < 0) {
                DRM_ERROR("Failed to prepare_enable the pclk [%d]\n", ret);
-               goto pclk_err;
+               return;
        }
 
        ret = clk_prepare_enable(ctx->aclk);
        if (ret < 0) {
                DRM_ERROR("Failed to prepare_enable the aclk [%d]\n", ret);
-               goto aclk_err;
+               return;
        }
 
        ret = clk_prepare_enable(ctx->eclk);
        if  (ret < 0) {
                DRM_ERROR("Failed to prepare_enable the eclk [%d]\n", ret);
-               goto eclk_err;
+               return;
        }
 
        ret = clk_prepare_enable(ctx->vclk);
        if  (ret < 0) {
                DRM_ERROR("Failed to prepare_enable the vclk [%d]\n", ret);
-               goto vclk_err;
+               return;
        }
 
        decon_init(ctx);
 
        /* if vblank was enabled status, enable it again. */
-       if (test_and_clear_bit(0, &ctx->irq_flags)) {
-               ret = decon_enable_vblank(ctx->crtc);
-               if (ret) {
-                       DRM_ERROR("Failed to re-enable vblank [%d]\n", ret);
-                       goto err;
-               }
-       }
-
-       decon_window_resume(ctx);
-
-       decon_apply(ctx);
+       if (test_and_clear_bit(0, &ctx->irq_flags))
+               decon_enable_vblank(ctx->crtc);
 
-       return 0;
-
-err:
-       clk_disable_unprepare(ctx->vclk);
-vclk_err:
-       clk_disable_unprepare(ctx->eclk);
-eclk_err:
-       clk_disable_unprepare(ctx->aclk);
-aclk_err:
-       clk_disable_unprepare(ctx->pclk);
-pclk_err:
-       ctx->suspended = true;
-       return ret;
+       decon_commit(ctx->crtc);
 }
 
-static int decon_poweroff(struct decon_context *ctx)
+static void decon_disable(struct exynos_drm_crtc *crtc)
 {
+       struct decon_context *ctx = crtc->ctx;
+       int i;
+
        if (ctx->suspended)
-               return 0;
+               return;
 
        /*
         * We need to make sure that all windows are disabled before we
         * suspend that connector. Otherwise we might try to scan from
         * a destroyed buffer later.
         */
-       decon_window_suspend(ctx);
+       for (i = 0; i < WINDOWS_NR; i++)
+               decon_disable_plane(crtc, &ctx->planes[i]);
 
        clk_disable_unprepare(ctx->vclk);
        clk_disable_unprepare(ctx->eclk);
@@ -688,37 +621,19 @@ static int decon_poweroff(struct decon_context *ctx)
        pm_runtime_put_sync(ctx->dev);
 
        ctx->suspended = true;
-       return 0;
-}
-
-static void decon_dpms(struct exynos_drm_crtc *crtc, int mode)
-{
-       DRM_DEBUG_KMS("%s, %d\n", __FILE__, mode);
-
-       switch (mode) {
-       case DRM_MODE_DPMS_ON:
-               decon_poweron(crtc->ctx);
-               break;
-       case DRM_MODE_DPMS_STANDBY:
-       case DRM_MODE_DPMS_SUSPEND:
-       case DRM_MODE_DPMS_OFF:
-               decon_poweroff(crtc->ctx);
-               break;
-       default:
-               DRM_DEBUG_KMS("unspecified mode %d\n", mode);
-               break;
-       }
 }
 
 static const struct exynos_drm_crtc_ops decon_crtc_ops = {
-       .dpms = decon_dpms,
-       .mode_fixup = decon_mode_fixup,
+       .enable = decon_enable,
+       .disable = decon_disable,
        .commit = decon_commit,
        .enable_vblank = decon_enable_vblank,
        .disable_vblank = decon_disable_vblank,
        .wait_for_vblank = decon_wait_for_vblank,
-       .win_commit = decon_win_commit,
-       .win_disable = decon_win_disable,
+       .atomic_begin = decon_atomic_begin,
+       .update_plane = decon_update_plane,
+       .disable_plane = decon_disable_plane,
+       .atomic_flush = decon_atomic_flush,
 };
 
 
@@ -726,6 +641,7 @@ static irqreturn_t decon_irq_handler(int irq, void *dev_id)
 {
        struct decon_context *ctx = (struct decon_context *)dev_id;
        u32 val, clear_bit;
+       int win;
 
        val = readl(ctx->regs + VIDINTCON1);
 
@@ -738,8 +654,15 @@ static irqreturn_t decon_irq_handler(int irq, void *dev_id)
                goto out;
 
        if (!ctx->i80_if) {
-               drm_handle_vblank(ctx->drm_dev, ctx->pipe);
-               exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
+               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;
+
+                       exynos_drm_crtc_finish_update(ctx->crtc, plane);
+               }
 
                /* set wait vsync event to zero and wake up queue. */
                if (atomic_read(&ctx->wait_vsync_event)) {
@@ -767,15 +690,15 @@ static int decon_bind(struct device *dev, struct device *master, void *data)
        }
 
        for (zpos = 0; zpos < WINDOWS_NR; zpos++) {
-               type = (zpos == ctx->default_win) ? DRM_PLANE_TYPE_PRIMARY :
-                                               DRM_PLANE_TYPE_OVERLAY;
+               type = exynos_plane_get_type(zpos, CURSOR_WIN);
                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)
                        return ret;
        }
 
-       exynos_plane = &ctx->planes[ctx->default_win];
+       exynos_plane = &ctx->planes[DEFAULT_WIN];
        ctx->crtc = exynos_drm_crtc_create(drm_dev, &exynos_plane->base,
                                           ctx->pipe, EXYNOS_DISPLAY_TYPE_LCD,
                                           &decon_crtc_ops, ctx);
@@ -784,8 +707,8 @@ static int decon_bind(struct device *dev, struct device *master, void *data)
                return PTR_ERR(ctx->crtc);
        }
 
-       if (ctx->display)
-               exynos_drm_create_enc_conn(drm_dev, ctx->display);
+       if (ctx->encoder)
+               exynos_dpi_bind(drm_dev, ctx->encoder);
 
        return 0;
 
@@ -796,10 +719,10 @@ static void decon_unbind(struct device *dev, struct device *master,
 {
        struct decon_context *ctx = dev_get_drvdata(dev);
 
-       decon_dpms(ctx->crtc, DRM_MODE_DPMS_OFF);
+       decon_disable(ctx->crtc);
 
-       if (ctx->display)
-               exynos_dpi_remove(ctx->display);
+       if (ctx->encoder)
+               exynos_dpi_remove(ctx->encoder);
 
        decon_ctx_remove(ctx);
 }
@@ -824,11 +747,6 @@ static int decon_probe(struct platform_device *pdev)
        if (!ctx)
                return -ENOMEM;
 
-       ret = exynos_drm_component_add(dev, EXYNOS_DEVICE_TYPE_CRTC,
-                                       EXYNOS_DISPLAY_TYPE_LCD);
-       if (ret)
-               return ret;
-
        ctx->dev = dev;
        ctx->suspended = true;
 
@@ -838,10 +756,8 @@ static int decon_probe(struct platform_device *pdev)
        of_node_put(i80_if_timings);
 
        ctx->regs = of_iomap(dev->of_node, 0);
-       if (!ctx->regs) {
-               ret = -ENOMEM;
-               goto err_del_component;
-       }
+       if (!ctx->regs)
+               return -ENOMEM;
 
        ctx->pclk = devm_clk_get(dev, "pclk_decon0");
        if (IS_ERR(ctx->pclk)) {
@@ -891,9 +807,9 @@ static int decon_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, ctx);
 
-       ctx->display = exynos_dpi_probe(dev);
-       if (IS_ERR(ctx->display)) {
-               ret = PTR_ERR(ctx->display);
+       ctx->encoder = exynos_dpi_probe(dev);
+       if (IS_ERR(ctx->encoder)) {
+               ret = PTR_ERR(ctx->encoder);
                goto err_iounmap;
        }
 
@@ -911,8 +827,6 @@ err_disable_pm_runtime:
 err_iounmap:
        iounmap(ctx->regs);
 
-err_del_component:
-       exynos_drm_component_del(dev, EXYNOS_DEVICE_TYPE_CRTC);
        return ret;
 }
 
@@ -925,7 +839,6 @@ static int decon_remove(struct platform_device *pdev)
        iounmap(ctx->regs);
 
        component_del(&pdev->dev, &decon_component_ops);
-       exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
 
        return 0;
 }