drm/exynos: Fix HDMI for smaller resolutions
[cascardo/linux.git] / drivers / gpu / drm / exynos / exynos_mixer.c
index e15438c..5994764 100644 (file)
 #include "exynos_drm_drv.h"
 #include "exynos_drm_hdmi.h"
 
+#include <plat/map-base.h>
+#ifdef CONFIG_EXYNOS_IOMMU
+#include <mach/sysmmu.h>
+#include <linux/of_platform.h>
+#endif
+
 #define MIXER_WIN_NR           3
 #define MIXER_DEFAULT_WIN      0
 
@@ -68,11 +74,13 @@ struct mixer_resources {
        void __iomem            *mixer_regs;
        void __iomem            *vp_regs;
        spinlock_t              reg_slock;
+       wait_queue_head_t       event_queue;
        struct clk              *mixer;
        struct clk              *vp;
        struct clk              *sclk_mixer;
        struct clk              *sclk_hdmi;
        struct clk              *sclk_dac;
+       unsigned int            is_soc_exynos5;
 };
 
 struct mixer_context {
@@ -82,6 +90,12 @@ struct mixer_context {
 
        struct mixer_resources  mixer_res;
        struct hdmi_win_data    win_data[MIXER_WIN_NR];
+       unsigned long event_flags;
+};
+
+/* event flags used  */
+enum mixer_status_flags {
+       MXR_EVENT_VSYNC = 1,
 };
 
 static const u8 filter_y_horiz_tap8[] = {
@@ -113,6 +127,8 @@ static const u8 filter_cr_horiz_tap4[] = {
        70,     59,     48,     37,     27,     19,     11,     5,
 };
 
+static void mixer_win_reset(struct mixer_context *ctx);
+
 static inline u32 vp_reg_read(struct mixer_resources *res, u32 reg_id)
 {
        return readl(res->vp_regs + reg_id);
@@ -250,8 +266,9 @@ static void mixer_vsync_set_update(struct mixer_context *ctx, bool enable)
        mixer_reg_writemask(res, MXR_STATUS, enable ?
                        MXR_STATUS_SYNC_ENABLE : 0, MXR_STATUS_SYNC_ENABLE);
 
-       vp_reg_write(res, VP_SHADOW_UPDATE, enable ?
-                       VP_SHADOW_UPDATE_ENABLE : 0);
+       if (!(res->is_soc_exynos5))
+               vp_reg_write(res, VP_SHADOW_UPDATE, enable ?
+                               VP_SHADOW_UPDATE_ENABLE : 0);
 }
 
 static void mixer_cfg_scan(struct mixer_context *ctx, unsigned int height)
@@ -347,6 +364,20 @@ static void mixer_run(struct mixer_context *ctx)
        mixer_regs_dump(ctx);
 }
 
+static int mixer_wait_for_vsync(struct mixer_context *ctx)
+{
+       int ret;
+
+       ctx->event_flags |= MXR_EVENT_VSYNC;
+
+       ret = wait_event_timeout(ctx->mixer_res.event_queue,
+       ((ctx->event_flags & MXR_EVENT_VSYNC) == 0), msecs_to_jiffies(1000));
+       if (ret > 0)
+               return 0;
+
+       return -ETIME;
+}
+
 static void vp_video_buffer(struct mixer_context *ctx, int win)
 {
        struct mixer_resources *res = &ctx->mixer_res;
@@ -570,6 +601,7 @@ static void mixer_graph_buffer(struct mixer_context *ctx, int win)
        mixer_cfg_scan(ctx, mode_height);
        mixer_cfg_rgb_fmt(ctx, mode_height);
        mixer_cfg_layer(ctx, win, true);
+       mixer_cfg_layer(ctx, MIXER_DEFAULT_WIN, true);
        mixer_run(ctx);
 
        mixer_vsync_set_update(ctx, true);
@@ -598,8 +630,6 @@ static int mixer_enable_vblank(void *ctx, int pipe)
 
        DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
 
-       mixer_ctx->pipe = pipe;
-
        /* enable vsync interrupt */
        mixer_reg_writemask(res, MXR_INT_EN, MXR_INT_EN_VSYNC,
                        MXR_INT_EN_VSYNC);
@@ -663,7 +693,7 @@ static void mixer_win_mode_set(void *ctx,
 
        win_data->fb_x = overlay->fb_x;
        win_data->fb_y = overlay->fb_y;
-       win_data->fb_width = overlay->fb_width;
+       win_data->fb_width = overlay->fb_pitch / (overlay->bpp >> 3);
        win_data->fb_height = overlay->fb_height;
 
        win_data->mode_width = overlay->mode_width;
@@ -675,6 +705,7 @@ static void mixer_win_mode_set(void *ctx,
 static void mixer_win_commit(void *ctx, int zpos)
 {
        struct mixer_context *mixer_ctx = ctx;
+       struct mixer_resources *res = &mixer_ctx->mixer_res;
        int win = zpos;
 
        DRM_DEBUG_KMS("[%d] %s, win: %d\n", __LINE__, __func__, win);
@@ -687,8 +718,12 @@ static void mixer_win_commit(void *ctx, int zpos)
                return;
        }
 
-       if (win > 1)
-               vp_video_buffer(mixer_ctx, win);
+       if (!(res->is_soc_exynos5)) {
+               if (win > 1)
+                       vp_video_buffer(mixer_ctx, win);
+               else
+                       mixer_graph_buffer(mixer_ctx, win);
+       }
        else
                mixer_graph_buffer(mixer_ctx, win);
 }
@@ -710,13 +745,21 @@ static void mixer_win_disable(void *ctx, int zpos)
                return;
        }
 
+       mixer_wait_for_vsync(mixer_ctx);
+
        spin_lock_irqsave(&res->reg_slock, flags);
        mixer_vsync_set_update(mixer_ctx, false);
 
        mixer_cfg_layer(mixer_ctx, win, false);
 
        mixer_vsync_set_update(mixer_ctx, true);
+
        spin_unlock_irqrestore(&res->reg_slock, flags);
+
+       if (win == MIXER_DEFAULT_WIN) {
+               mixer_win_reset(ctx);
+               mixer_enable_vblank(ctx, 0);
+       }
 }
 
 static struct exynos_mixer_ops mixer_ops = {
@@ -782,6 +825,22 @@ static irqreturn_t mixer_irq_handler(int irq, void *arg)
 
        /* handling VSYNC */
        if (val & MXR_INT_STATUS_VSYNC) {
+
+               if (ctx->event_flags & MXR_EVENT_VSYNC) {
+                       DRM_DEBUG_KMS("ctx->event_flags & MXR_EVENT_VSYNC");
+
+
+                       mixer_reg_write(res, MXR_GRAPHIC_WH(1), 0);
+                       mixer_reg_write(res, MXR_GRAPHIC_SPAN(1), 0);
+                       mixer_reg_write(res, MXR_GRAPHIC_SXY(1), 0);
+                       mixer_reg_write(res, MXR_GRAPHIC_DXY(1), 0);
+
+                       ctx->event_flags &= ~MXR_EVENT_VSYNC;
+                       wake_up(&ctx->mixer_res.event_queue);
+
+                       goto out;
+               }
+
                /* interlace scan need to check shadow register */
                if (ctx->interlace) {
                        val_base = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(0));
@@ -795,6 +854,11 @@ static irqreturn_t mixer_irq_handler(int irq, void *arg)
 
                drm_handle_vblank(drm_hdmi_ctx->drm_dev, ctx->pipe);
                mixer_finish_pageflip(drm_hdmi_ctx->drm_dev, ctx->pipe);
+
+               /* layer update mandatory for exynos5 soc,and not present
+               * in exynos4 */
+               if (res->is_soc_exynos5)
+                       mixer_reg_writemask(res, MXR_CFG, ~0, MXR_CFG_LAYER_UPDATE);
        }
 
 out:
@@ -818,6 +882,7 @@ static void mixer_win_reset(struct mixer_context *ctx)
        u32 val; /* value stored to register */
 
        spin_lock_irqsave(&res->reg_slock, flags);
+       mixer_reg_writemask(res, MXR_STATUS, ~0, MXR_STATUS_SOFT_RESET);
        mixer_vsync_set_update(ctx, false);
 
        mixer_reg_writemask(res, MXR_CFG, MXR_CFG_DST_HDMI, MXR_CFG_DST_MASK);
@@ -858,15 +923,19 @@ static void mixer_win_reset(struct mixer_context *ctx)
        val |= MXR_GRP_CFG_PIXEL_BLEND_EN;
        mixer_reg_write(res, MXR_GRAPHIC_CFG(1), val);
 
-       /* configuration of Video Processor Registers */
-       vp_win_reset(ctx);
-       vp_default_filter(res);
+       if (!(res->is_soc_exynos5)) {
+               /* configuration of Video Processor for Exynos4 soc */
+               vp_win_reset(ctx);
+               vp_default_filter(res);
+       }
 
        /* disable all layers */
        mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP0_ENABLE);
        mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP1_ENABLE);
        mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_VP_ENABLE);
 
+       mixer_reg_writemask(res, MXR_INT_EN, ~0, MXR_INT_EN_ALL);
+
        mixer_vsync_set_update(ctx, true);
        spin_unlock_irqrestore(&res->reg_slock, flags);
 }
@@ -878,8 +947,10 @@ static void mixer_resource_poweron(struct mixer_context *ctx)
        DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
 
        clk_enable(res->mixer);
-       clk_enable(res->vp);
-       clk_enable(res->sclk_mixer);
+       if (!(res->is_soc_exynos5)) {
+               clk_enable(res->vp);
+               clk_enable(res->sclk_mixer);
+       }
 
        mixer_win_reset(ctx);
 }
@@ -891,8 +962,10 @@ static void mixer_resource_poweroff(struct mixer_context *ctx)
        DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
 
        clk_disable(res->mixer);
-       clk_disable(res->vp);
-       clk_disable(res->sclk_mixer);
+       if (!(res->is_soc_exynos5)) {
+               clk_disable(res->vp);
+               clk_disable(res->sclk_mixer);
+       }
 }
 
 static int mixer_runtime_resume(struct device *dev)
@@ -922,8 +995,33 @@ static const struct dev_pm_ops mixer_pm_ops = {
        .runtime_resume  = mixer_runtime_resume,
 };
 
-static int __devinit mixer_resources_init(struct exynos_drm_hdmi_context *ctx,
-                                struct platform_device *pdev)
+#ifdef CONFIG_EXYNOS_IOMMU
+static int iommu_init(struct platform_device *pdev)
+{
+       struct platform_device *pds;
+
+       pds = find_sysmmu_dt(pdev, "sysmmu");
+       if (pds == NULL) {
+               printk(KERN_ERR "No sysmmu found  :\n");
+               return -EINVAL;
+       }
+
+       platform_set_sysmmu(&pds->dev, &pdev->dev);
+       exynos_drm_common_mapping = s5p_create_iommu_mapping(&pdev->dev,
+                       0x20000000, SZ_128M, 4, exynos_drm_common_mapping);
+       if(exynos_drm_common_mapping == NULL) {
+               printk(KERN_ERR"Failed to create iommu mapping for Mixer\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+#endif
+
+static int __devinit mixer_resources_init_exynos(
+                       struct exynos_drm_hdmi_context *ctx,
+                       struct platform_device *pdev,
+                       int is_exynos5)
 {
        struct mixer_context *mixer_ctx = ctx->ctx;
        struct device *dev = &pdev->dev;
@@ -931,26 +1029,34 @@ static int __devinit mixer_resources_init(struct exynos_drm_hdmi_context *ctx,
        struct resource *res;
        int ret;
 
+       DRM_DEBUG_KMS("Mixer resources init\n");
+
+       mixer_res->is_soc_exynos5 = is_exynos5;
        mixer_res->dev = dev;
        spin_lock_init(&mixer_res->reg_slock);
 
+       if(is_exynos5)
+               init_waitqueue_head(&mixer_res->event_queue);
+
        mixer_res->mixer = clk_get(dev, "mixer");
        if (IS_ERR_OR_NULL(mixer_res->mixer)) {
                dev_err(dev, "failed to get clock 'mixer'\n");
                ret = -ENODEV;
                goto fail;
        }
-       mixer_res->vp = clk_get(dev, "vp");
-       if (IS_ERR_OR_NULL(mixer_res->vp)) {
-               dev_err(dev, "failed to get clock 'vp'\n");
-               ret = -ENODEV;
-               goto fail;
-       }
-       mixer_res->sclk_mixer = clk_get(dev, "sclk_mixer");
-       if (IS_ERR_OR_NULL(mixer_res->sclk_mixer)) {
-               dev_err(dev, "failed to get clock 'sclk_mixer'\n");
-               ret = -ENODEV;
-               goto fail;
+       if(!is_exynos5) {
+               mixer_res->vp = clk_get(dev, "vp");
+               if (IS_ERR_OR_NULL(mixer_res->vp)) {
+                       dev_err(dev, "failed to get clock 'vp'\n");
+                       ret = -ENODEV;
+                       goto fail;
+               }
+               mixer_res->sclk_mixer = clk_get(dev, "sclk_mixer");
+               if (IS_ERR_OR_NULL(mixer_res->sclk_mixer)) {
+                       dev_err(dev, "failed to get clock 'sclk_mixer'\n");
+                       ret = -ENODEV;
+                       goto fail;
+               }
        }
        mixer_res->sclk_hdmi = clk_get(dev, "sclk_hdmi");
        if (IS_ERR_OR_NULL(mixer_res->sclk_hdmi)) {
@@ -958,20 +1064,26 @@ static int __devinit mixer_resources_init(struct exynos_drm_hdmi_context *ctx,
                ret = -ENODEV;
                goto fail;
        }
-       mixer_res->sclk_dac = clk_get(dev, "sclk_dac");
-       if (IS_ERR_OR_NULL(mixer_res->sclk_dac)) {
-               dev_err(dev, "failed to get clock 'sclk_dac'\n");
-               ret = -ENODEV;
-               goto fail;
+       if(!is_exynos5) {
+               mixer_res->sclk_dac = clk_get(dev, "sclk_dac");
+               if (IS_ERR_OR_NULL(mixer_res->sclk_dac)) {
+                       dev_err(dev, "failed to get clock 'sclk_dac'\n");
+                       ret = -ENODEV;
+                       goto fail;
+               }
+               res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mxr");
        }
-       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mxr");
+       else
+               res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
        if (res == NULL) {
                dev_err(dev, "get memory resource failed.\n");
                ret = -ENXIO;
                goto fail;
        }
 
-       clk_set_parent(mixer_res->sclk_mixer, mixer_res->sclk_hdmi);
+       if(!is_exynos5)
+               clk_set_parent(mixer_res->sclk_mixer, mixer_res->sclk_hdmi);
 
        mixer_res->mixer_regs = ioremap(res->start, resource_size(res));
        if (mixer_res->mixer_regs == NULL) {
@@ -980,34 +1092,50 @@ static int __devinit mixer_resources_init(struct exynos_drm_hdmi_context *ctx,
                goto fail;
        }
 
-       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vp");
-       if (res == NULL) {
-               dev_err(dev, "get memory resource failed.\n");
-               ret = -ENXIO;
-               goto fail_mixer_regs;
-       }
+       if(!is_exynos5) {
+               res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vp");
+               if (res == NULL) {
+                       dev_err(dev, "get memory resource failed.\n");
+                       ret = -ENXIO;
+                       goto fail_vp_regs;
+               }
 
-       mixer_res->vp_regs = ioremap(res->start, resource_size(res));
-       if (mixer_res->vp_regs == NULL) {
-               dev_err(dev, "register mapping failed.\n");
-               ret = -ENXIO;
-               goto fail_mixer_regs;
-       }
+               mixer_res->vp_regs = ioremap(res->start, resource_size(res));
+               if (mixer_res->vp_regs == NULL) {
+                       dev_err(dev, "register mapping failed.\n");
+                       ret = -ENXIO;
+                       goto fail_vp_regs;
+               }
 
-       res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "irq");
-       if (res == NULL) {
-               dev_err(dev, "get interrupt resource failed.\n");
-               ret = -ENXIO;
-               goto fail_vp_regs;
+               res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "irq");
+               if (res == NULL) {
+                       dev_err(dev, "get interrupt resource failed.\n");
+                       ret = -ENXIO;
+                       goto fail_vp_regs;
+               }
+       }else {
+               res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+               if (res == NULL) {
+                       dev_err(dev, "get interrupt resource failed.\n");
+                       ret = -ENXIO;
+                       goto fail_mixer_regs;
+               }
        }
 
        ret = request_irq(res->start, mixer_irq_handler, 0, "drm_mixer", ctx);
        if (ret) {
                dev_err(dev, "request interrupt failed.\n");
-               goto fail_vp_regs;
+               goto fail_mixer_regs;
        }
        mixer_res->irq = res->start;
 
+#ifdef CONFIG_EXYNOS_IOMMU
+       ret = iommu_init(pdev);
+       if(ret) {
+               dev_err(dev, "iommu init failed.\n");
+               goto fail_mixer_regs;
+       }
+#endif
        return 0;
 
 fail_vp_regs:
@@ -1046,6 +1174,7 @@ static int __devinit mixer_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
        struct exynos_drm_hdmi_context *drm_hdmi_ctx;
+       struct exynos_drm_hdmi_pdata *pdata;
        struct mixer_context *ctx;
        int ret;
 
@@ -1068,11 +1197,17 @@ static int __devinit mixer_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, drm_hdmi_ctx);
 
+       /* Get from Platform soc deatils */
+       pdata = pdev->dev.platform_data;
+
        /* acquire resources: regs, irqs, clocks */
-       ret = mixer_resources_init(drm_hdmi_ctx, pdev);
+       ret = mixer_resources_init_exynos(drm_hdmi_ctx, pdev,pdata->is_soc_exynos5);
        if (ret)
                goto fail;
 
+       /* attach mixer driver to common hdmi. */
+       exynos_mixer_drv_attach(drm_hdmi_ctx);
+
        /* register specific callback point to common hdmi. */
        exynos_mixer_ops_register(&mixer_ops);