summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDonghwa Lee <dh09.lee@samsung.com>2014-08-19 11:48:51 +0900
committerDonghwa Lee <dh09.lee@samsung.com>2014-08-19 17:55:26 +0900
commit88be1ee3d5b222a30d01d7b3b7559a3820de6f9d (patch)
tree2c17dbc0bf8c9290c38a3a27327df4908236b169
parent3ce59c107fde07dcdd6f2b5395255a4aa1fca7c4 (diff)
downloadlinux-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.h19
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fimc.c8
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fimd.c88
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_ipp.h17
-rw-r--r--include/video/samsung_fimd.h11
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.