diff options
author | Donghwa Lee <dh09.lee@samsung.com> | 2014-08-19 11:48:51 +0900 |
---|---|---|
committer | Donghwa Lee <dh09.lee@samsung.com> | 2014-08-19 17:55:26 +0900 |
commit | 88be1ee3d5b222a30d01d7b3b7559a3820de6f9d (patch) | |
tree | 2c17dbc0bf8c9290c38a3a27327df4908236b169 | |
parent | 3ce59c107fde07dcdd6f2b5395255a4aa1fca7c4 (diff) | |
download | linux-3.10-88be1ee3d5b222a30d01d7b3b7559a3820de6f9d.tar.gz linux-3.10-88be1ee3d5b222a30d01d7b3b7559a3820de6f9d.tar.bz2 linux-3.10-88be1ee3d5b222a30d01d7b3b7559a3820de6f9d.zip |
drm: fimd: support display writeback mode
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>
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_drv.h | 19 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_fimc.c | 8 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_fimd.c | 88 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_ipp.h | 17 | ||||
-rw-r--r-- | include/video/samsung_fimd.h | 11 |
5 files changed, 122 insertions, 21 deletions
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index 3e825229bb0..c787172d3a7 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -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); diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimc.c b/drivers/gpu/drm/exynos/exynos_drm_fimc.c index 3c6931c780f..73c51363862 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimc.c @@ -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: diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index c3d8786cd94..6e5e1a84d72 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -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); diff --git a/drivers/gpu/drm/exynos/exynos_drm_ipp.h b/drivers/gpu/drm/exynos/exynos_drm_ipp.h index 9af69b3ca81..84e2b3aa634 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_ipp.h +++ b/drivers/gpu/drm/exynos/exynos_drm_ipp.h @@ -20,10 +20,6 @@ #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); diff --git a/include/video/samsung_fimd.h b/include/video/samsung_fimd.h index eaad58b5be4..66545d2298a 100644 --- a/include/video/samsung_fimd.h +++ b/include/video/samsung_fimd.h @@ -97,7 +97,12 @@ #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 @@ -108,6 +113,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. |