drm/exynos: Add 1024x768/1280x800 HDMI resolutions
[cascardo/linux.git] / drivers / gpu / drm / exynos / exynos_mixer.c
index 1695670..1fd60bd 100644 (file)
@@ -54,9 +54,7 @@
 
 struct hdmi_win_data {
        dma_addr_t              dma_addr;
-       void __iomem            *vaddr;
        dma_addr_t              chroma_dma_addr;
-       void __iomem            *chroma_vaddr;
        uint32_t                pixel_format;
        unsigned int            bpp;
        unsigned int            crtc_x;
@@ -101,9 +99,18 @@ struct mixer_context {
        struct hdmi_win_data    win_data[MIXER_WIN_NR];
        unsigned long           event_flags;
        int                     previous_dxy;
-       bool                    is_800x600_initialized;
 };
 
+struct mixer_scan_range {
+       int min_res[2], max_res[2];
+       enum exynos_mixer_mode_type mode_type;
+};
+
+struct mixer_scan_adjustment {
+       int res[2], new_res[2];
+};
+
+
 /* event flags used  */
 enum mixer_status_flags {
        MXR_EVENT_VSYNC = 1,
@@ -138,6 +145,45 @@ static const u8 filter_cr_horiz_tap4[] = {
        70,     59,     48,     37,     27,     19,     11,     5,
 };
 
+struct mixer_scan_range scan_ranges[] = {
+       {
+               .min_res = { 464, 0 },
+               .max_res = { 720, 480 },
+               .mode_type = EXYNOS_MIXER_MODE_SD_NTSC,
+       },
+       {
+               .min_res = { 464, 481 },
+               .max_res = { 720, 576 },
+               .mode_type = EXYNOS_MIXER_MODE_SD_PAL,
+       },
+       {
+               .min_res = { 1024, 0 },
+               .max_res = { 1280, 720 },
+               .mode_type = EXYNOS_MIXER_MODE_HD_720,
+       },
+       {
+               .min_res = { 1664, 0 },
+               .max_res = { 1920, 1080 },
+               .mode_type = EXYNOS_MIXER_MODE_HD_1080,
+       },
+       {
+               .min_res = { 1440, 900 },
+               .max_res = { 1440, 900 },
+               .mode_type = EXYNOS_MIXER_MODE_HD_1080,
+       },
+};
+
+struct mixer_scan_adjustment scan_adjustments[] = {
+       {
+               .res = { 1024, 768 },
+               .new_res = { 1024, 720 },
+       },
+       {
+               .res = { 1280, 800 },
+               .new_res = { 1280, 720 },
+       },
+};
+
 static void mixer_win_reset(struct mixer_context *mctx);
 
 static inline u32 vp_reg_read(struct mixer_resources *res, u32 reg_id)
@@ -182,18 +228,65 @@ static inline void mixer_reg_writemask(struct mixer_resources *res,
 
 enum exynos_mixer_mode_type exynos_mixer_get_mode_type(int width, int height)
 {
-       if (width >= 464 && width <= 720 && height <= 480)
-               return EXYNOS_MIXER_MODE_SD_NTSC;
-       else if (width >= 464 && width <= 720 && height <= 576)
-               return EXYNOS_MIXER_MODE_SD_PAL;
-       else if (width >= 1024 && width <= 1280 && height <= 720)
-               return EXYNOS_MIXER_MODE_HD_720;
-       else if ((width == 1440 && height == 900) ||
-               (width == 800 && height == 600) ||
-               (width >= 1664 && width <= 1920 && height <= 1080))
-               return EXYNOS_MIXER_MODE_HD_1080;
-       else
-               return EXYNOS_MIXER_MODE_INVALID;
+       int i;
+
+       /*
+        * If the mode matches an adjustment, adjust it before finding the
+        * mode type
+        */
+       for (i = 0; i < ARRAY_SIZE(scan_adjustments); i++) {
+               struct mixer_scan_adjustment *adj = &scan_adjustments[i];
+
+               if (width == adj->res[0] && height == adj->res[1]) {
+                       width = adj->new_res[0];
+                       height = adj->new_res[1];
+               }
+       }
+
+       for (i = 0; i < ARRAY_SIZE(scan_ranges); i++) {
+               struct mixer_scan_range *range = &scan_ranges[i];
+
+               if (width >= range->min_res[0] && width <= range->max_res[0]
+                && height >= range->min_res[1] && height <= range->max_res[1])
+                       return range->mode_type;
+       }
+       return EXYNOS_MIXER_MODE_INVALID;
+}
+
+static void mixer_adjust_modes(void *ctx, struct drm_connector *connector)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(scan_adjustments); i++) {
+               struct mixer_scan_adjustment *adj = &scan_adjustments[i];
+               struct drm_display_mode *t, *mode;
+               bool native_support = false;
+
+               /*
+                * Make sure the mode resulting from the adjustment is not
+                * already natively supported. This might cause us to do
+                * something stupid like choose a chopped 1280x800 resolution
+                * over native 720p.
+                */
+               list_for_each_entry_safe(mode, t, &connector->modes, head) {
+                       if (adj->new_res[0] == mode->hdisplay &&
+                           adj->new_res[1] == mode->vdisplay) {
+                               native_support = true;
+                               break;
+                       }
+               }
+               if (native_support)
+                       continue;
+
+               list_for_each_entry_safe(mode, t, &connector->modes, head) {
+                       if (adj->res[0] == mode->hdisplay &&
+                           adj->res[1] == mode->vdisplay) {
+                               mode->hdisplay = adj->new_res[0];
+                               mode->vdisplay = adj->new_res[1];
+                               break;
+                       }
+               }
+       }
 }
 
 static void mixer_regs_dump(struct mixer_context *mctx)
@@ -345,16 +438,6 @@ static void mixer_set_layer_offset(struct mixer_context *mctx, u32 offset)
        mixer_reg_write(res, MXR_GRAPHIC0_DXY, MXR_GRP_DXY_DX(offset));
 }
 
-static void mixer_toggle_3d_path(struct mixer_context *mctx)
-{
-       struct mixer_resources *res = &mctx->mixer_res;
-
-       mixer_reg_write(res, MXR_TVOUT_CFG,
-                       mctx->is_800x600_initialized ? 0x13 : 0x17);
-
-       mctx->is_800x600_initialized = true;
-}
-
 static void mixer_cfg_rgb_fmt(struct mixer_context *mctx, unsigned int height)
 {
        struct mixer_resources *res = &mctx->mixer_res;
@@ -688,17 +771,10 @@ static void mixer_graph_buffer(struct mixer_context *mctx, int win)
 
        mixer_cfg_scan(mctx, mode_width, mode_height);
 
+       /* Workaround 4 implementation for 1440x900 resolution support */
        if (res->is_soc_exynos5) {
-               /* Workaround 4 implementation for 1440x900 resolution support */
                if (mode_width == 1440 && mode_height == 900)
                        mixer_set_layer_offset(mctx, 224);
-
-               /* Workaround 3 implementation for 800x600 resolution support */
-               if (mode_width == 800 && mode_height == 600) {
-                       mixer_set_layer_offset(mctx, 32);
-                       mixer_toggle_3d_path(mctx);
-               } else
-                       mctx->is_800x600_initialized = false;
        }
 
        mixer_cfg_rgb_fmt(mctx, mode_height);
@@ -736,7 +812,7 @@ static int mixer_enable_vblank(void *ctx, int pipe)
        struct mixer_context *mctx = ctx;
        struct mixer_resources *res = &mctx->mixer_res;
 
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+       DRM_DEBUG_KMS("pipe: %d\n", pipe);
 
        /*
         * TODO (seanpaul): Right now, this is an expected code path since we
@@ -747,6 +823,9 @@ static int mixer_enable_vblank(void *ctx, int pipe)
        if (pipe < 0)
                return -EINVAL;
 
+       if (!mctx->is_mixer_powered_on)
+               return -EPERM;
+
        mctx->pipe = pipe;
 
        /* enable vsync interrupt */
@@ -761,7 +840,10 @@ static void mixer_disable_vblank(void *ctx)
        struct mixer_context *mctx = ctx;
        struct mixer_resources *res = &mctx->mixer_res;
 
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+       DRM_DEBUG_KMS("pipe: %d\n", mctx->pipe);
+
+       if (!mctx->is_mixer_powered_on)
+               return;
 
        /* disable vsync interrupt */
        mixer_reg_writemask(res, MXR_INT_EN, 0, MXR_INT_EN_VSYNC);
@@ -774,8 +856,6 @@ static void mixer_win_mode_set(void *ctx,
        struct hdmi_win_data *win_data;
        int win;
 
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
        if (!overlay) {
                DRM_ERROR("overlay is NULL\n");
                return;
@@ -799,9 +879,7 @@ static void mixer_win_mode_set(void *ctx,
        win_data = &mctx->win_data[win];
 
        win_data->dma_addr = overlay->dma_addr[0];
-       win_data->vaddr = overlay->vaddr[0];
        win_data->chroma_dma_addr = overlay->dma_addr[1];
-       win_data->chroma_vaddr = overlay->vaddr[1];
        win_data->pixel_format = overlay->pixel_format;
        win_data->bpp = overlay->bpp;
 
@@ -821,35 +899,14 @@ static void mixer_win_mode_set(void *ctx,
        win_data->scan_flags = overlay->scan_flag;
 }
 
-static void mixer_win_page_flip(void *ctx, struct exynos_drm_overlay *overlay)
-{
-       struct mixer_context *mixer_ctx = ctx;
-       struct hdmi_win_data *win_data;
-       int win = overlay->zpos;
-
-       if (win == DEFAULT_ZPOS)
-               win = MIXER_DEFAULT_WIN;
-
-       if (win < 0 || win > MIXER_WIN_NR) {
-               DRM_ERROR("overlay plane[%d] is wrong\n", win);
-               return;
-       }
-
-       win_data = &mixer_ctx->win_data[win];
-
-       win_data->dma_addr = overlay->dma_addr[0];
-       win_data->vaddr = overlay->vaddr[0];
-       win_data->chroma_dma_addr = overlay->dma_addr[1];
-       win_data->chroma_vaddr = overlay->vaddr[1];
-}
-
 static void mixer_win_commit(void *ctx, int zpos)
 {
        struct mixer_context *mctx = ctx;
        struct mixer_resources *res = &mctx->mixer_res;
        int win = zpos;
 
-       DRM_DEBUG_KMS("[%d] %s, win: %d\n", __LINE__, __func__, win);
+       DRM_DEBUG_KMS("win: %d\n", win);
+
        if (win == DEFAULT_ZPOS)
                win = MIXER_DEFAULT_WIN;
 
@@ -859,7 +916,7 @@ static void mixer_win_commit(void *ctx, int zpos)
        }
 
        if (!mctx->is_mixer_powered_on) {
-               DRM_DEBUG_KMS("[%d] %s not powered on\n", __LINE__, __func__);
+               DRM_DEBUG_KMS("not powered on\n");
                return;
        }
 
@@ -878,6 +935,8 @@ static void mixer_apply(void *ctx)
        struct mixer_context *mctx = ctx;
        int i;
 
+       DRM_DEBUG_KMS("\n");
+
        for (i = 0; i < MIXER_WIN_NR; i++) {
                if (!mctx->enabled[i])
                        continue;
@@ -893,7 +952,7 @@ static void mixer_win_disable(void *ctx, int zpos)
        unsigned long flags;
        int win = zpos;
 
-       DRM_DEBUG_KMS("[%d] %s, win: %d\n", __LINE__, __func__, win);
+       DRM_DEBUG_KMS("win: %d\n", win);
 
        if (win == DEFAULT_ZPOS)
                win = MIXER_DEFAULT_WIN;
@@ -903,6 +962,9 @@ static void mixer_win_disable(void *ctx, int zpos)
                return;
        }
 
+       if (!mctx->is_mixer_powered_on)
+               return;
+
        mixer_wait_for_vsync(mctx);
 
        spin_lock_irqsave(&res->reg_slock, flags);
@@ -914,11 +976,6 @@ static void mixer_win_disable(void *ctx, int zpos)
        mixer_vsync_set_update(mctx, true);
 
        spin_unlock_irqrestore(&res->reg_slock, flags);
-
-       if (win == MIXER_DEFAULT_WIN) {
-               mixer_win_reset(mctx);
-               mixer_enable_vblank(mctx, mctx->pipe);
-       }
 }
 
 /* for pageflip event */
@@ -927,10 +984,13 @@ static irqreturn_t mixer_irq_handler(int irq, void *arg)
        struct mixer_context *mctx = arg;
        struct mixer_resources *res = &mctx->mixer_res;
        u32 val, base, shadow;
+       bool flip_complete = false;
        int i;
 
        spin_lock(&res->reg_slock);
 
+       WARN_ON(!mctx->is_mixer_powered_on);
+
        /* read interrupt status for handling and clearing flags for VSYNC */
        val = mixer_reg_read(res, MXR_INT_STATUS);
 
@@ -958,7 +1018,7 @@ static irqreturn_t mixer_irq_handler(int irq, void *arg)
                for (i = 0; i < MIXER_WIN_NR; i++)
                        mctx->win_data[i].updated = false;
 
-               exynos_drm_crtc_finish_pageflip(mctx->drm_dev, mctx->pipe);
+               flip_complete = true;
 
                if (mctx->event_flags & MXR_EVENT_VSYNC) {
                        DRM_DEBUG_KMS("mctx->event_flags & MXR_EVENT_VSYNC");
@@ -979,6 +1039,9 @@ out:
 
        spin_unlock(&res->reg_slock);
 
+       if (flip_complete)
+               exynos_drm_crtc_finish_pageflip(mctx->drm_dev, mctx->pipe);
+
        return IRQ_HANDLED;
 }
 
@@ -988,6 +1051,8 @@ static void mixer_win_reset(struct mixer_context *mctx)
        unsigned long flags;
        u32 val; /* value stored to register */
 
+       DRM_DEBUG_KMS("\n");
+
        spin_lock_irqsave(&res->reg_slock, flags);
        mixer_reg_writemask(res, MXR_STATUS, ~0, MXR_STATUS_SOFT_RESET);
        mixer_vsync_set_update(mctx, false);
@@ -1049,6 +1114,8 @@ static void mixer_resource_poweron(struct mixer_context *mctx)
 {
        struct mixer_resources *res = &mctx->mixer_res;
 
+       DRM_DEBUG_KMS("is_mixer_powered_on: %d\n", mctx->is_mixer_powered_on);
+
        if (mctx->is_mixer_powered_on)
                return;
 
@@ -1058,17 +1125,19 @@ static void mixer_resource_poweron(struct mixer_context *mctx)
                clk_enable(res->sclk_mixer);
        }
 
+       mctx->is_mixer_powered_on = true;
+
        mixer_win_reset(mctx);
        mixer_enable_vblank(mctx, mctx->pipe);
-
-       mctx->is_mixer_powered_on = true;
+       mixer_apply(mctx);
 }
 
 static void mixer_resource_poweroff(struct mixer_context *mctx)
 {
        struct mixer_resources *res = &mctx->mixer_res;
 
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+       DRM_DEBUG_KMS("is_mixer_powered_on: %d\n", mctx->is_mixer_powered_on);
+
        if (!mctx->is_mixer_powered_on)
                return;
 
@@ -1077,88 +1146,24 @@ static void mixer_resource_poweroff(struct mixer_context *mctx)
                clk_disable(res->vp);
                clk_disable(res->sclk_mixer);
        }
+       mixer_win_reset(mctx);
        mctx->is_mixer_powered_on = false;
 }
 
-#ifdef CONFIG_PM_SLEEP
-static int mixer_resume(struct device *dev)
-{
-       struct mixer_context *mctx = get_mixer_context(dev);
-
-       DRM_DEBUG_KMS("[mixer] sleep resume - start\n");
-
-       if (!pm_runtime_suspended(dev)) {
-               DRM_DEBUG_KMS("[mixer] sleep resume - end\n");
-               mixer_resource_poweron(mctx);
-               mixer_win_commit(mctx, 0);
-       }
-       DRM_DEBUG_KMS("[mixer] sleep resume - not done\n");
-
-       return 0;
-}
-static int mixer_suspend(struct device *dev)
-{
-       struct mixer_context *mctx = get_mixer_context(dev);
-
-       DRM_DEBUG_KMS("[mixer] suspend - start\n");
-       if (pm_runtime_suspended(dev)) {
-               DRM_DEBUG_KMS("[mixer] suspend - already suspended\n");
-               return 0;
-       }
-
-       mixer_resource_poweroff(mctx);
-       DRM_DEBUG_KMS("[mixer] suspend - end\n");
-       return 0;
-}
-#endif
-#ifdef CONFIG_PM_RUNTIME
-static int mixer_runtime_resume(struct device *dev)
-{
-       struct mixer_context *mctx = get_mixer_context(dev);
-
-       DRM_DEBUG_KMS("[mixer] runtime resume - start\n");
-
-       mixer_resource_poweron(mctx);
-       DRM_DEBUG_KMS("[mixer] runtime resume - end\n");
-
-       return 0;
-}
-
-static int mixer_runtime_suspend(struct device *dev)
-{
-       struct mixer_context *mctx = get_mixer_context(dev);
-
-       DRM_DEBUG_KMS("[mixer] runtime suspend - start\n");
-
-       mixer_resource_poweroff(mctx);
-       DRM_DEBUG_KMS("[mixer] runtime suspend - end\n");
-       return 0;
-}
-
-#endif
-
-static int mixer_power(void *ctx, int mode)
+static int mixer_dpms(void *ctx, int mode)
 {
        struct mixer_context *mctx = ctx;
 
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+       DRM_DEBUG_KMS("[DPMS:%s]\n", drm_get_dpms_name(mode));
 
        switch (mode) {
        case DRM_MODE_DPMS_ON:
-               if (mctx->is_mixer_powered_on) {
-                       DRM_DEBUG_KMS("[%d] %s returning\n", __LINE__, __func__);
-                       break;
-               }
-               pm_runtime_get_sync(mctx->dev);
+               mixer_resource_poweron(mctx);
                break;
        case DRM_MODE_DPMS_STANDBY:
        case DRM_MODE_DPMS_SUSPEND:
        case DRM_MODE_DPMS_OFF:
-               if (!mctx->is_mixer_powered_on) {
-                       DRM_DEBUG_KMS("[%d] %s returning\n", __LINE__, __func__);
-                       break;
-               }
-               pm_runtime_put_sync(mctx->dev);
+               mixer_resource_poweroff(mctx);
                break;
        default:
                DRM_DEBUG_KMS("unknown dpms mode: %d\n", mode);
@@ -1172,6 +1177,8 @@ static int mixer_subdrv_probe(void *ctx, struct drm_device *drm_dev)
 {
        struct mixer_context *mctx = ctx;
 
+       DRM_DEBUG("[DEV:%s]\n", drm_dev->devname);
+
        mctx->drm_dev = drm_dev;
 
        return 0;
@@ -1179,29 +1186,25 @@ static int mixer_subdrv_probe(void *ctx, struct drm_device *drm_dev)
 
 static struct exynos_controller_ops mixer_ops = {
        /* manager */
+       .adjust_modes           = mixer_adjust_modes,
        .subdrv_probe           = mixer_subdrv_probe,
        .enable_vblank          = mixer_enable_vblank,
        .disable_vblank         = mixer_disable_vblank,
-       .power                  = mixer_power,
+       .dpms                   = mixer_dpms,
 
        /* overlay */
        .mode_set               = mixer_win_mode_set,
-       .page_flip              = mixer_win_page_flip,
-       .apply                  = mixer_apply,
        .win_commit             = mixer_win_commit,
        .win_disable            = mixer_win_disable,
 };
 
-static const struct dev_pm_ops mixer_pm_ops = {
-       SET_SYSTEM_SLEEP_PM_OPS(mixer_suspend, mixer_resume)
-       SET_RUNTIME_PM_OPS(mixer_runtime_suspend, mixer_runtime_resume, NULL)
-};
-
 #ifdef CONFIG_EXYNOS_IOMMU
 static int iommu_init(struct platform_device *pdev)
 {
        struct platform_device *pds;
 
+       DRM_DEBUG("[PDEV:%s]\n", pdev->name);
+
        pds = find_sysmmu_dt(pdev, "sysmmu");
        if (pds == NULL) {
                printk(KERN_ERR "No sysmmu found  :\n");
@@ -1224,6 +1227,12 @@ static int iommu_init(struct platform_device *pdev)
 
        return 0;
 }
+
+static void iommu_deinit(struct platform_device *pdev)
+{
+       s5p_destroy_iommu_mapping(&pdev->dev);
+       DRM_DEBUG("released the IOMMU mapping\n");
+}
 #endif
 
 static int __devinit mixer_resources_init_exynos(
@@ -1236,7 +1245,7 @@ static int __devinit mixer_resources_init_exynos(
        struct resource *res;
        int ret;
 
-       DRM_DEBUG_KMS("Mixer resources init\n");
+       DRM_DEBUG("[PDEV:%s] is_exynos5: %d\n", pdev->name, is_exynos5);
 
        mixer_res->is_soc_exynos5 = is_exynos5;
        mixer_res->dev = dev;
@@ -1371,6 +1380,8 @@ static void mixer_resources_cleanup(struct device *dev,
 {
        struct mixer_resources *res = &mctx->mixer_res;
 
+       DRM_DEBUG("\n");
+
        disable_irq(res->irq);
        free_irq(res->irq, dev);
 
@@ -1385,6 +1396,8 @@ static int __devinit mixer_probe(struct platform_device *pdev)
        struct mixer_context *mctx;
        int ret;
 
+       DRM_DEBUG("[PDEV:%s]\n", pdev->name);
+
        dev_info(dev, "probe start\n");
 
        mctx = kzalloc(sizeof(*mctx), GFP_KERNEL);
@@ -1425,11 +1438,17 @@ static int mixer_remove(struct platform_device *pdev)
        struct device *dev = &pdev->dev;
        struct mixer_context *mctx = platform_get_drvdata(pdev);
 
+       DRM_DEBUG("[PDEV:%s]\n", pdev->name);
+
        dev_info(dev, "remove successful\n");
 
        mixer_resource_poweroff(mctx);
        mixer_resources_cleanup(dev, mctx);
 
+#ifdef CONFIG_EXYNOS_IOMMU
+       iommu_deinit(pdev);
+#endif
+
        kfree(mctx);
 
        return 0;
@@ -1439,7 +1458,6 @@ struct platform_driver mixer_driver = {
        .driver = {
                .name = "s5p-mixer",
                .owner = THIS_MODULE,
-               .pm = &mixer_pm_ops,
        },
        .probe = mixer_probe,
        .remove = __devexit_p(mixer_remove),