drm/exynos: fix fb offset calculation for plane
[deliverable/linux.git] / drivers / gpu / drm / exynos / exynos_drm_fimd.c
index 130a2b510d4aebe1c9852b96457aea52e9857a15..90ca4b279a0d23b2b7338f51ac3cca996f8e4f8e 100644 (file)
@@ -25,6 +25,7 @@
 #include "exynos_drm_drv.h"
 #include "exynos_drm_fbdev.h"
 #include "exynos_drm_crtc.h"
+#include "exynos_drm_iommu.h"
 
 /*
  * FIMD is stand for Fully Interactive Mobile Display and
@@ -61,11 +62,11 @@ struct fimd_driver_data {
        unsigned int timing_base;
 };
 
-struct fimd_driver_data exynos4_fimd_driver_data = {
+static struct fimd_driver_data exynos4_fimd_driver_data = {
        .timing_base = 0x0,
 };
 
-struct fimd_driver_data exynos5_fimd_driver_data = {
+static struct fimd_driver_data exynos5_fimd_driver_data = {
        .timing_base = 0x20000,
 };
 
@@ -78,10 +79,10 @@ struct fimd_win_data {
        unsigned int            fb_height;
        unsigned int            bpp;
        dma_addr_t              dma_addr;
-       void __iomem            *vaddr;
        unsigned int            buf_offsize;
        unsigned int            line_size;      /* bytes */
        bool                    enabled;
+       bool                    resume;
 };
 
 struct fimd_context {
@@ -99,6 +100,8 @@ struct fimd_context {
        u32                             vidcon1;
        bool                            suspended;
        struct mutex                    lock;
+       wait_queue_head_t               wait_vsync_queue;
+       atomic_t                        wait_vsync_event;
 
        struct exynos_drm_panel_info *panel;
 };
@@ -307,12 +310,32 @@ static void fimd_disable_vblank(struct device *dev)
        }
 }
 
+static void fimd_wait_for_vblank(struct device *dev)
+{
+       struct fimd_context *ctx = get_fimd_context(dev);
+
+       if (ctx->suspended)
+               return;
+
+       atomic_set(&ctx->wait_vsync_event, 1);
+
+       /*
+        * wait for FIMD to signal VSYNC interrupt or return after
+        * timeout which is set to 50ms (refresh rate of 20).
+        */
+       if (!wait_event_timeout(ctx->wait_vsync_queue,
+                               !atomic_read(&ctx->wait_vsync_event),
+                               DRM_HZ/20))
+               DRM_DEBUG_KMS("vblank wait timed out.\n");
+}
+
 static struct exynos_drm_manager_ops fimd_manager_ops = {
        .dpms = fimd_dpms,
        .apply = fimd_apply,
        .commit = fimd_commit,
        .enable_vblank = fimd_enable_vblank,
        .disable_vblank = fimd_disable_vblank,
+       .wait_for_vblank = fimd_wait_for_vblank,
 };
 
 static void fimd_win_mode_set(struct device *dev,
@@ -351,7 +374,6 @@ static void fimd_win_mode_set(struct device *dev,
        win_data->fb_width = overlay->fb_width;
        win_data->fb_height = overlay->fb_height;
        win_data->dma_addr = overlay->dma_addr[0] + offset;
-       win_data->vaddr = overlay->vaddr[0] + offset;
        win_data->bpp = overlay->bpp;
        win_data->buf_offsize = (overlay->fb_width - overlay->crtc_width) *
                                (overlay->bpp >> 3);
@@ -361,9 +383,7 @@ static void fimd_win_mode_set(struct device *dev,
                        win_data->offset_x, win_data->offset_y);
        DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n",
                        win_data->ovl_width, win_data->ovl_height);
-       DRM_DEBUG_KMS("paddr = 0x%lx, vaddr = 0x%lx\n",
-                       (unsigned long)win_data->dma_addr,
-                       (unsigned long)win_data->vaddr);
+       DRM_DEBUG_KMS("paddr = 0x%lx\n", (unsigned long)win_data->dma_addr);
        DRM_DEBUG_KMS("fb_width = %d, crtc_width = %d\n",
                        overlay->fb_width, overlay->crtc_width);
 }
@@ -573,6 +593,12 @@ static void fimd_win_disable(struct device *dev, int zpos)
 
        win_data = &ctx->win_data[win];
 
+       if (ctx->suspended) {
+               /* do not resume this window*/
+               win_data->resume = false;
+               return;
+       }
+
        /* protect windows */
        val = readl(ctx->regs + SHADOWCON);
        val |= SHADOWCON_WINx_PROTECT(win);
@@ -592,22 +618,10 @@ static void fimd_win_disable(struct device *dev, int zpos)
        win_data->enabled = false;
 }
 
-static void fimd_wait_for_vblank(struct device *dev)
-{
-       struct fimd_context *ctx = get_fimd_context(dev);
-       int ret;
-
-       ret = wait_for((__raw_readl(ctx->regs + VIDCON1) &
-                                       VIDCON1_VSTATUS_VSYNC), 50);
-       if (ret < 0)
-               DRM_DEBUG_KMS("vblank wait timed out.\n");
-}
-
 static struct exynos_drm_overlay_ops fimd_overlay_ops = {
        .mode_set = fimd_win_mode_set,
        .commit = fimd_win_commit,
        .disable = fimd_win_disable,
-       .wait_for_vblank = fimd_wait_for_vblank,
 };
 
 static struct exynos_drm_manager fimd_manager = {
@@ -623,7 +637,6 @@ static void fimd_finish_pageflip(struct drm_device *drm_dev, int crtc)
        struct drm_pending_vblank_event *e, *t;
        struct timeval now;
        unsigned long flags;
-       bool is_checked = false;
 
        spin_lock_irqsave(&drm_dev->event_lock, flags);
 
@@ -633,8 +646,6 @@ static void fimd_finish_pageflip(struct drm_device *drm_dev, int crtc)
                if (crtc != e->pipe)
                        continue;
 
-               is_checked = true;
-
                do_gettimeofday(&now);
                e->event.sequence = 0;
                e->event.tv_sec = now.tv_sec;
@@ -642,22 +653,7 @@ static void fimd_finish_pageflip(struct drm_device *drm_dev, int crtc)
 
                list_move_tail(&e->base.link, &e->base.file_priv->event_list);
                wake_up_interruptible(&e->base.file_priv->event_wait);
-       }
-
-       if (is_checked) {
-               /*
-                * call drm_vblank_put only in case that drm_vblank_get was
-                * called.
-                */
-               if (atomic_read(&drm_dev->vblank_refcount[crtc]) > 0)
-                       drm_vblank_put(drm_dev, crtc);
-
-               /*
-                * don't off vblank if vblank_disable_allowed is 1,
-                * because vblank would be off by timer handler.
-                */
-               if (!drm_dev->vblank_disable_allowed)
-                       drm_vblank_off(drm_dev, crtc);
+               drm_vblank_put(drm_dev, crtc);
        }
 
        spin_unlock_irqrestore(&drm_dev->event_lock, flags);
@@ -684,6 +680,11 @@ static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
        drm_handle_vblank(drm_dev, manager->pipe);
        fimd_finish_pageflip(drm_dev, manager->pipe);
 
+       /* set wait vsync event to zero and wake up queue. */
+       if (atomic_read(&ctx->wait_vsync_event)) {
+               atomic_set(&ctx->wait_vsync_event, 0);
+               DRM_WAKEUP(&ctx->wait_vsync_queue);
+       }
 out:
        return IRQ_HANDLED;
 }
@@ -709,6 +710,10 @@ static int fimd_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
         */
        drm_dev->vblank_disable_allowed = 1;
 
+       /* attach this sub driver to iommu mapping if supported. */
+       if (is_drm_iommu_supported(drm_dev))
+               drm_iommu_attach_device(drm_dev, dev);
+
        return 0;
 }
 
@@ -716,7 +721,9 @@ static void fimd_subdrv_remove(struct drm_device *drm_dev, struct device *dev)
 {
        DRM_DEBUG_KMS("%s\n", __FILE__);
 
-       /* TODO. */
+       /* detach this sub driver from iommu mapping if supported. */
+       if (is_drm_iommu_supported(drm_dev))
+               drm_iommu_detach_device(drm_dev, dev);
 }
 
 static int fimd_calc_clkdiv(struct fimd_context *ctx,
@@ -805,11 +812,38 @@ static int fimd_clock(struct fimd_context *ctx, bool enable)
        return 0;
 }
 
+static void fimd_window_suspend(struct device *dev)
+{
+       struct fimd_context *ctx = get_fimd_context(dev);
+       struct fimd_win_data *win_data;
+       int i;
+
+       for (i = 0; i < WINDOWS_NR; i++) {
+               win_data = &ctx->win_data[i];
+               win_data->resume = win_data->enabled;
+               fimd_win_disable(dev, i);
+       }
+       fimd_wait_for_vblank(dev);
+}
+
+static void fimd_window_resume(struct device *dev)
+{
+       struct fimd_context *ctx = get_fimd_context(dev);
+       struct fimd_win_data *win_data;
+       int i;
+
+       for (i = 0; i < WINDOWS_NR; i++) {
+               win_data = &ctx->win_data[i];
+               win_data->enabled = win_data->resume;
+               win_data->resume = false;
+       }
+}
+
 static int fimd_activate(struct fimd_context *ctx, bool enable)
 {
+       struct device *dev = ctx->subdrv.dev;
        if (enable) {
                int ret;
-               struct device *dev = ctx->subdrv.dev;
 
                ret = fimd_clock(ctx, true);
                if (ret < 0)
@@ -820,7 +854,11 @@ static int fimd_activate(struct fimd_context *ctx, bool enable)
                /* if vblank was enabled status, enable it again. */
                if (test_and_clear_bit(0, &ctx->irq_flags))
                        fimd_enable_vblank(dev);
+
+               fimd_window_resume(dev);
        } else {
+               fimd_window_suspend(dev);
+
                fimd_clock(ctx, false);
                ctx->suspended = true;
        }
@@ -857,18 +895,16 @@ static int __devinit fimd_probe(struct platform_device *pdev)
        if (!ctx)
                return -ENOMEM;
 
-       ctx->bus_clk = clk_get(dev, "fimd");
+       ctx->bus_clk = devm_clk_get(dev, "fimd");
        if (IS_ERR(ctx->bus_clk)) {
                dev_err(dev, "failed to get bus clock\n");
-               ret = PTR_ERR(ctx->bus_clk);
-               goto err_clk_get;
+               return PTR_ERR(ctx->bus_clk);
        }
 
-       ctx->lcd_clk = clk_get(dev, "sclk_fimd");
+       ctx->lcd_clk = devm_clk_get(dev, "sclk_fimd");
        if (IS_ERR(ctx->lcd_clk)) {
                dev_err(dev, "failed to get lcd clock\n");
-               ret = PTR_ERR(ctx->lcd_clk);
-               goto err_bus_clk;
+               return PTR_ERR(ctx->lcd_clk);
        }
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -876,14 +912,13 @@ static int __devinit fimd_probe(struct platform_device *pdev)
        ctx->regs = devm_request_and_ioremap(&pdev->dev, res);
        if (!ctx->regs) {
                dev_err(dev, "failed to map registers\n");
-               ret = -ENXIO;
-               goto err_clk;
+               return -ENXIO;
        }
 
        res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
        if (!res) {
                dev_err(dev, "irq request failed.\n");
-               goto err_clk;
+               return -ENXIO;
        }
 
        ctx->irq = res->start;
@@ -892,13 +927,15 @@ static int __devinit fimd_probe(struct platform_device *pdev)
                                                        0, "drm_fimd", ctx);
        if (ret) {
                dev_err(dev, "irq request failed.\n");
-               goto err_clk;
+               return ret;
        }
 
        ctx->vidcon0 = pdata->vidcon0;
        ctx->vidcon1 = pdata->vidcon1;
        ctx->default_win = pdata->default_win;
        ctx->panel = panel;
+       DRM_INIT_WAITQUEUE(&ctx->wait_vsync_queue);
+       atomic_set(&ctx->wait_vsync_event, 0);
 
        subdrv = &ctx->subdrv;
 
@@ -926,17 +963,6 @@ static int __devinit fimd_probe(struct platform_device *pdev)
        exynos_drm_subdrv_register(subdrv);
 
        return 0;
-
-err_clk:
-       clk_disable(ctx->lcd_clk);
-       clk_put(ctx->lcd_clk);
-
-err_bus_clk:
-       clk_disable(ctx->bus_clk);
-       clk_put(ctx->bus_clk);
-
-err_clk_get:
-       return ret;
 }
 
 static int __devexit fimd_remove(struct platform_device *pdev)
@@ -960,9 +986,6 @@ static int __devexit fimd_remove(struct platform_device *pdev)
 out:
        pm_runtime_disable(dev);
 
-       clk_put(ctx->lcd_clk);
-       clk_put(ctx->bus_clk);
-
        return 0;
 }
 
This page took 0.108535 seconds and 5 git commands to generate.