drm: fimd: support display writeback mode
authorDonghwa Lee <dh09.lee@samsung.com>
Tue, 19 Aug 2014 02:48:51 +0000 (11:48 +0900)
committerChanho Park <chanho61.park@samsung.com>
Tue, 18 Nov 2014 03:00:36 +0000 (12:00 +0900)
This patch supports exynos drm display writeback mode that clone
the screen with fimd like below.

FIMD----->FIMC H/W---->MEMORY

Change-Id: I8172ec6ee157e2e48a23b9776e11543c33716dc5
Signed-off-by: Donghwa Lee <dh09.lee@samsung.com>
drivers/gpu/drm/exynos/exynos_drm_drv.h
drivers/gpu/drm/exynos/exynos_drm_fimc.c
drivers/gpu/drm/exynos/exynos_drm_fimd.c
drivers/gpu/drm/exynos/exynos_drm_ipp.h
include/video/samsung_fimd.h

index 3e82522..c787172 100644 (file)
@@ -53,6 +53,22 @@ enum exynos_drm_output_type {
        EXYNOS_DISPLAY_TYPE_VIDI,
 };
 
+enum exynos_drm_display_mode {
+       EXYNOS_DISPLAY_OUTPUT_NONE,
+       EXYNOS_DISPLAY_OUTPUT_WB,
+};
+
+/*
+ * A structure of wb setting infomation.
+ *
+ * @enable: enable flag for wb.
+ * @refresh: HZ of the refresh rate.
+ */
+struct drm_exynos_display_set_wb {
+       __u32   enable;
+       __u32   refresh;
+};
+
 /*
  * Exynos drm common overlay structure.
  *
@@ -364,6 +380,9 @@ int exynos_platform_device_ipp_register(void);
  */
 void exynos_platform_device_ipp_unregister(void);
 
+extern int exynos_drm_ippnb_register(struct notifier_block *nb);
+extern int exynos_drm_ippnb_unregister(struct notifier_block *nb);
+
 #ifdef CONFIG_DRM_EXYNOS_DPI
 int exynos_dpi_probe(struct device *dev);
 int exynos_dpi_remove(struct device *dev);
index 3c6931c..73c5136 100644 (file)
@@ -1640,7 +1640,7 @@ static int fimc_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd)
        struct drm_exynos_ipp_property *property;
        struct drm_exynos_ipp_config *config;
        struct drm_exynos_pos   img_pos[EXYNOS_DRM_OPS_MAX];
-       struct drm_exynos_ipp_set_wb set_wb;
+       struct drm_exynos_display_set_wb set_wb;
        int ret, i;
        u32 cfg0, cfg1;
 
@@ -1697,7 +1697,7 @@ static int fimc_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd)
 
                set_wb.enable = 1;
                set_wb.refresh = property->refresh_rate;
-               exynos_drm_ippnb_send_event(IPP_SET_WRITEBACK, (void *)&set_wb);
+               exynos_drm_ippnb_send_event(EXYNOS_DISPLAY_OUTPUT_WB, (void *)&set_wb);
                break;
        case IPP_CMD_OUTPUT:
        default:
@@ -1742,7 +1742,7 @@ static int fimc_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd)
 static void fimc_ippdrv_stop(struct device *dev, enum drm_exynos_ipp_cmd cmd)
 {
        struct fimc_context *ctx = get_fimc_context(dev);
-       struct drm_exynos_ipp_set_wb set_wb = {0, 0};
+       struct drm_exynos_display_set_wb set_wb = {0, 0};
        u32 cfg;
 
        DRM_DEBUG_KMS("%s:cmd[%d]\n", __func__, cmd);
@@ -1756,7 +1756,7 @@ static void fimc_ippdrv_stop(struct device *dev, enum drm_exynos_ipp_cmd cmd)
                fimc_write(ctx, cfg, EXYNOS_MSCTRL);
                break;
        case IPP_CMD_WB:
-               exynos_drm_ippnb_send_event(IPP_SET_WRITEBACK, (void *)&set_wb);
+               exynos_drm_ippnb_send_event(EXYNOS_DISPLAY_OUTPUT_WB, (void *)&set_wb);
                break;
        case IPP_CMD_OUTPUT:
        default:
index c3d8786..6e5e1a8 100644 (file)
@@ -169,6 +169,7 @@ struct fimd_context {
 
        struct exynos_drm_panel_info panel;
        struct fimd_driver_data *driver_data;
+       struct notifier_block   nb_ctrl;
 };
 
 static const struct of_device_id fimd_driver_dt_match[] = {
@@ -1053,6 +1054,78 @@ out:
        return IRQ_HANDLED;
 }
 
+static void fimd_set_writeback(struct fimd_context *ctx, int enable,
+               unsigned int refresh)
+{
+       u32 vidcon0 = readl(ctx->regs + VIDCON0);
+       u32 vidcon2 = readl(ctx->regs + VIDCON2);
+
+       DRM_DEBUG_KMS("%s:wb[%d]refresh[%d]\n",
+               __func__, enable, refresh);
+
+       vidcon0 &= ~VIDCON0_VIDOUT_MASK;
+       vidcon2 &= ~(VIDCON2_WB_MASK |
+                       VIDCON2_WB_SKIP_MASK |
+                       VIDCON2_TVFMTSEL_SW |
+                       VIDCON2_TVFORMATSEL_MASK);
+
+       if (enable) {
+               vidcon0 |= VIDCON0_VIDOUT_WB_RGB;
+               vidcon2 |= (VIDCON2_WB_ENABLE |
+                               VIDCON2_TVFMTSEL_SW |
+                               VIDCON2_TVFMTSEL1_YUV444);
+
+               if (refresh >= 60 || refresh == 0)
+                       DRM_INFO("%s:refresh[%d],forced set to 60hz.\n",
+                               __func__, refresh);
+               else if (refresh >= 30)
+                       vidcon2 |= VIDCON2_WB_SKIP_1_2;
+               else if (refresh >= 20)
+                       vidcon2 |= VIDCON2_WB_SKIP_1_3;
+               else if (refresh >= 15)
+                       vidcon2 |= VIDCON2_WB_SKIP_1_4;
+               else
+                       vidcon2 |= VIDCON2_WB_SKIP_1_5;
+       } else {
+               if (ctx->i80_if)
+                       vidcon0 |= VIDCON0_VIDOUT_I80_LDI0;
+               else
+                       vidcon0 |= VIDCON0_VIDOUT_RGB;
+               vidcon2 |= VIDCON2_WB_DISABLE;
+       }
+
+       writel(vidcon0, ctx->regs + VIDCON0);
+       writel(vidcon2, ctx->regs + VIDCON2);
+}
+
+static int fimd_notifier_ctrl(struct notifier_block *this,
+                       unsigned long event, void *_data)
+{
+       struct fimd_context *ctx = container_of(this,
+                               struct fimd_context, nb_ctrl);
+
+       if (ctx->suspended)
+               return -EPERM;
+
+       switch (event) {
+       case EXYNOS_DISPLAY_OUTPUT_WB: {
+               struct drm_exynos_display_set_wb *set_wb =
+                       (struct drm_exynos_display_set_wb *)_data;
+               unsigned int refresh = set_wb->refresh;
+               int enable = *((int *)&set_wb->enable);
+
+               fimd_set_writeback(ctx, enable, refresh);
+       }
+               break;
+       default:
+               /* ToDo : for checking use case */
+               DRM_INFO("%s:event[0x%x]\n", __func__, (unsigned int)event);
+               break;
+       }
+
+       return NOTIFY_DONE;
+}
+
 static int fimd_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
@@ -1149,6 +1222,13 @@ static int fimd_probe(struct platform_device *pdev)
        init_waitqueue_head(&ctx->wait_vsync_queue);
        atomic_set(&ctx->wait_vsync_event, 0);
 
+       ctx->nb_ctrl.notifier_call = fimd_notifier_ctrl;
+       ret = exynos_drm_ippnb_register(&ctx->nb_ctrl);
+       if (ret) {
+               dev_err(dev, "could not register fimd notify callback\n");
+               return ret;
+       }
+
        platform_set_drvdata(pdev, &fimd_manager);
 
        fimd_manager.ctx = ctx;
@@ -1167,9 +1247,17 @@ static int fimd_probe(struct platform_device *pdev)
 static int fimd_remove(struct platform_device *pdev)
 {
        struct exynos_drm_manager *mgr = platform_get_drvdata(pdev);
+       struct fimd_context *ctx = mgr->ctx;
+       int ret;
 
        exynos_dpi_remove(&pdev->dev);
 
+       ret = exynos_drm_ippnb_unregister(&ctx->nb_ctrl);
+       if (ret) {
+               dev_err(&pdev->dev, "could not unregister fimd notify callback\n");
+               return ret;
+       }
+
        exynos_drm_manager_unregister(&fimd_manager);
 
        fimd_dpms(mgr, DRM_MODE_DPMS_OFF);
index 9af69b3..84e2b3a 100644 (file)
 #define for_each_ipp_planar(pos)       \
        for (pos = 0; pos < EXYNOS_DRM_PLANAR_MAX; pos++)
 
-#define IPP_GET_LCD_WIDTH      _IOR('F', 302, int)
-#define IPP_GET_LCD_HEIGHT     _IOR('F', 303, int)
-#define IPP_SET_WRITEBACK      _IOW('F', 304, u32)
-
 /* definition of state */
 enum drm_exynos_ipp_state {
        IPP_STATE_IDLE,
@@ -94,17 +90,6 @@ struct drm_exynos_ipp_buf_info {
 };
 
 /*
- * A structure of wb setting infomation.
- *
- * @enable: enable flag for wb.
- * @refresh: HZ of the refresh rate.
- */
-struct drm_exynos_ipp_set_wb {
-       __u32   enable;
-       __u32   refresh;
-};
-
-/*
  * A structure of event work information.
  *
  * @work: work structure.
@@ -189,8 +174,6 @@ extern int exynos_drm_ipp_queue_buf(struct drm_device *drm_dev, void *data,
                                         struct drm_file *file);
 extern int exynos_drm_ipp_cmd_ctrl(struct drm_device *drm_dev, void *data,
                                         struct drm_file *file);
-extern int exynos_drm_ippnb_register(struct notifier_block *nb);
-extern int exynos_drm_ippnb_unregister(struct notifier_block *nb);
 extern int exynos_drm_ippnb_send_event(unsigned long val, void *v);
 extern void ipp_sched_cmd(struct work_struct *work);
 extern void ipp_sched_event(struct work_struct *work);
index eaad58b..66545d2 100644 (file)
 
 #define VIDCON2                                        0x08
 #define VIDCON2_EN601                          (1 << 23)
+#define VIDCON2_WB_ENABLE                      (1 << 15)
+#define VIDCON2_WB_MASK                                (1 << 15)
+#define VIDCON2_WB_DISABLE                     (0 << 15)
 #define VIDCON2_TVFMTSEL_SW                    (1 << 14)
+#define VIDCON2_TVFMTSEL_SW_MASK               (1 << 14)
+#define VIDCON2_TVFORMATSEL_MASK               (0x3 << 12)
 
 #define VIDCON2_TVFMTSEL1_MASK                 (0x3 << 12)
 #define VIDCON2_TVFMTSEL1_SHIFT                        12
 #define VIDCON2_ORGYCbCr                       (1 << 8)
 #define VIDCON2_YUVORDCrCb                     (1 << 7)
 
+#define VIDCON2_WB_SKIP_1_2                    (0x1 << 0)
+#define VIDCON2_WB_SKIP_1_3                    (0x2 << 0)
+#define VIDCON2_WB_SKIP_1_4                    (0x3 << 0)
+#define VIDCON2_WB_SKIP_1_5                    (0x4 << 0)
+#define VIDCON2_WB_SKIP_MASK                   (0x1f << 0)
+
 /* PRTCON (S3C6410, S5PC100)
  * Might not be present in the S3C6410 documentation,
  * but tests prove it's there almost for sure; shouldn't hurt in any case.