Exynos variants support post pocessors(PP) for FIMD (MDNIE and IELCD) which work closely with fimd. These post processors have strict dependency on FIMD for poweron/poweroff. This patch adds an infrastructure in FIMD driver to accomodate such devices. Added structure definition for FIMD PP and interfaces from FIMD driver to control FIMD PP. Signed-off-by: Ajay Kumar <ajaykumar.rs@xxxxxxxxxxx> Signed-off-by: Shirish S <s.shirish@xxxxxxxxxxx> Signed-off-by: Rahul Sharma <rahul.sharma@xxxxxxxxxxx> --- drivers/gpu/drm/exynos/exynos_drm_fimd.c | 162 +++++++++++++++++++++++++++++++ drivers/gpu/drm/exynos/exynos_fimd_pp.h | 52 ++++++++++ include/video/samsung_fimd.h | 6 +- 3 files changed, 219 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/exynos/exynos_fimd_pp.h diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index a5511be..a584d8e 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -30,6 +30,7 @@ #include "exynos_drm_fbdev.h" #include "exynos_drm_crtc.h" #include "exynos_drm_iommu.h" +#include "exynos_fimd_pp.h" /* * FIMD stands for Fully Interactive Mobile Display and @@ -113,11 +114,14 @@ struct fimd_context { void __iomem *regs; struct drm_display_mode mode; struct fimd_win_data win_data[WINDOWS_NR]; + struct list_head fimd_pp_list; unsigned int default_win; unsigned long irq_flags; u32 vidcon0; u32 vidcon1; bool suspended; + bool enable_pp; + bool pp_running; int pipe; wait_queue_head_t wait_vsync_queue; atomic_t wait_vsync_event; @@ -145,6 +149,12 @@ static inline struct fimd_driver_data *drm_fimd_get_driver_data( return (struct fimd_driver_data *)of_id->data; } +void fimd_add_pp_to_list(struct fimd_context *ctx, + struct exynos_fimd_pp *pp) +{ + list_add_tail(&pp->list, &ctx->fimd_pp_list); +} + static int fimd_mgr_initialize(struct exynos_drm_manager *mgr, struct drm_device *drm_dev) { @@ -214,17 +224,49 @@ static void fimd_mode_set(struct exynos_drm_manager *mgr, const struct drm_display_mode *in_mode) { struct fimd_context *ctx = mgr->ctx; + struct exynos_fimd_pp *pp_display; drm_mode_copy(&ctx->mode, in_mode); + + /* mode_set for the post processors*/ + list_for_each_entry(pp_display, &ctx->fimd_pp_list, list) + if (pp_display->ops->mode_set) + pp_display->ops->mode_set(pp_display->ctx, in_mode); +} + +static int fimd_wait_till_per_frame_off(struct fimd_context *ctx) +{ + unsigned int val = readl(ctx->regs + VIDCON0); + int count = 10; + + val &= ~(VIDCON0_ENVID_F); + writel(val, ctx->regs + VIDCON0); + + while (count) { + val = readl(ctx->regs + VIDCON0); + if ((val & VIDCON0_ENVID_F) == 0) + break; + mdelay(17); + count--; + } + + if (count == 0) { + DRM_ERROR("FIMD per frame switch off TIMEDOUT\n"); + return -ETIMEDOUT; + } + + return 0; } static void fimd_commit(struct exynos_drm_manager *mgr) { struct fimd_context *ctx = mgr->ctx; struct drm_display_mode *mode = &ctx->mode; + struct exynos_fimd_pp *pp_display; struct fimd_driver_data *driver_data; u32 val, clkdiv, vidcon1; int hblank, vblank, vsync_len, vbpd, vfpd, hsync_len, hbpd, hfpd; + int ret = 0; driver_data = ctx->driver_data; if (ctx->suspended) @@ -234,6 +276,30 @@ static void fimd_commit(struct exynos_drm_manager *mgr) if (mode->htotal == 0 || mode->vtotal == 0) return; + /* The FIMD and the post processor(PP) poweroff calls below are needed + * here to make sure that fimd_commit doesn't switch on the PPs twice + * in a row. For proper working we need to keep fimd, and the PPs + * off, before we start again. This flag "pp_running" will also help + * in syncing any of the clock enable/disable calls in PP routines. + */ + if (ctx->enable_pp && ctx->pp_running) { + if (fimd_wait_till_per_frame_off(ctx)) + DRM_ERROR("failed to switch off fimd per frame\n"); + list_for_each_entry_reverse(pp_display, &ctx->fimd_pp_list, + list) { + if (pp_display->ops->power_off) { + ret = pp_display->ops-> + power_off(pp_display->ctx); + if (ret) { + DRM_ERROR("fimd_pp switch off fail\n"); + ctx->enable_pp = false; + } else { + ctx->pp_running = false; + } + } + } + } + /* setup polarity values */ vidcon1 = ctx->vidcon1; if (mode->flags & DRM_MODE_FLAG_NVSYNC) @@ -286,10 +352,34 @@ static void fimd_commit(struct exynos_drm_manager *mgr) else val &= ~VIDCON0_CLKDIR; /* 1:1 clock */ + writel(val, ctx->regs + VIDCON0); + + if (ctx->enable_pp && !ctx->pp_running) { + /* set VCLK Free run control */ + val = readl(ctx->regs + VIDCON0); + val |= VIDCON0_VCLKFREE; + writel(val, ctx->regs + VIDCON0); + + /* post processor setup and poweron*/ + list_for_each_entry(pp_display, &ctx->fimd_pp_list, list) { + if (pp_display->ops->power_on) { + ret = pp_display->ops-> + power_on(pp_display->ctx); + if (ret) { + DRM_ERROR("fimd_pp poweron failed\n"); + ctx->enable_pp = false; + } else { + ctx->pp_running = true; + } + } + } + } + /* * fields of register with prefix '_F' would be updated * at vsync(same as dma start) */ + val = readl(ctx->regs + VIDCON0); val |= VIDCON0_ENVID | VIDCON0_ENVID_F; writel(val, ctx->regs + VIDCON0); } @@ -749,6 +839,9 @@ static int fimd_poweron(struct exynos_drm_manager *mgr) goto lcd_clk_err; } + if (ctx->enable_pp) + writel(MDNIE_CLK_ENABLE, ctx->regs + DP_MDNIE_CLKCON); + /* if vblank was enabled status, enable it again. */ if (test_and_clear_bit(0, &ctx->irq_flags)) { ret = fimd_enable_vblank(mgr); @@ -776,6 +869,9 @@ bus_clk_err: static int fimd_poweroff(struct exynos_drm_manager *mgr) { struct fimd_context *ctx = mgr->ctx; + struct exynos_fimd_pp *pp_display; + int ret = 0; + if (ctx->suspended) return 0; @@ -787,6 +883,27 @@ static int fimd_poweroff(struct exynos_drm_manager *mgr) */ fimd_window_suspend(mgr); + if (ctx->enable_pp && ctx->pp_running) { + if (fimd_wait_till_per_frame_off(ctx)) + DRM_ERROR("failed to switch off fimd per frame\n"); + list_for_each_entry_reverse(pp_display, &ctx->fimd_pp_list, + list) { + if (pp_display->ops->power_off) { + ret = pp_display->ops-> + power_off(pp_display->ctx); + if (ret) { + DRM_ERROR("fimd_pp switch off fail\n"); + ctx->enable_pp = false; + } else { + ctx->pp_running = false; + } + } + } + } + + if (ctx->enable_pp) + writel(0, ctx->regs + DP_MDNIE_CLKCON); + clk_disable_unprepare(ctx->lcd_clk); clk_disable_unprepare(ctx->bus_clk); @@ -815,6 +932,47 @@ static void fimd_dpms(struct exynos_drm_manager *mgr, int mode) } } +static int fimd_set_property(void *in_ctx, struct drm_property *property, + uint64_t val) +{ + struct fimd_context *ctx = in_ctx; + struct exynos_fimd_pp *pp_display; + int ret; + + list_for_each_entry(pp_display, &ctx->fimd_pp_list, list) { + if (pp_display->ops->set_property) { + ret = pp_display->ops->set_property(ctx->drm_dev, + pp_display->ctx, property, val); + if (ret) { + DRM_ERROR("pp set_property failed.\n"); + return ret; + } + } + } + + return 0; +} + +static int fimd_set_gamma(void *in_ctx, u16 *r, u16 *g, u16 *b, + uint32_t start, uint32_t size) +{ + struct fimd_context *ctx = in_ctx; + struct exynos_fimd_pp *pp_display; + int ret; + + list_for_each_entry(pp_display, &ctx->fimd_pp_list, list) { + if (pp_display->ops->set_property) + ret = pp_display->ops->set_gamma(pp_display->ctx, + r, g, b, start, size); + if (ret) { + DRM_ERROR("pp set_gamma failed.\n"); + return ret; + } + } + + return 0; +} + static struct exynos_drm_manager_ops fimd_manager_ops = { .dpms = fimd_dpms, .mode_fixup = fimd_mode_fixup, @@ -826,6 +984,8 @@ static struct exynos_drm_manager_ops fimd_manager_ops = { .win_mode_set = fimd_win_mode_set, .win_commit = fimd_win_commit, .win_disable = fimd_win_disable, + .set_property = fimd_set_property, + .set_gamma = fimd_set_gamma, }; static struct exynos_drm_manager fimd_manager = { @@ -919,6 +1079,8 @@ static int fimd_bind(struct device *dev, struct device *master, void *data) init_waitqueue_head(&ctx->wait_vsync_queue); atomic_set(&ctx->wait_vsync_event, 0); + INIT_LIST_HEAD(&ctx->fimd_pp_list); + platform_set_drvdata(pdev, &fimd_manager); fimd_manager.ctx = ctx; diff --git a/drivers/gpu/drm/exynos/exynos_fimd_pp.h b/drivers/gpu/drm/exynos/exynos_fimd_pp.h new file mode 100644 index 0000000..528d3cb --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_fimd_pp.h @@ -0,0 +1,52 @@ +/* drivers/gpu/drm/exynos/exynos_fimd_pp.h + * + * Copyright (C) 2014 Samsung Electronics Co.Ltd + * + * Header file for FIMD post processors. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#ifndef __EXYNOS_FIMD_H__ +#define __EXYNOS_FIMD_H__ + +#include <drm/exynos_drm.h> + +/** + * Post Processor interfaces structure for DRM based FIMD. + * + * @power_on: setup the post processor. + * @power_off: power off the post processor. + * @mode_set: set video timings if needed by the post processor. + * @set_property: use crtc properties to set the post processor. + * @set_gamma: set gamma properties to post processor. + * + */ +struct exynos_fimd_pp_ops { + int (*power_on)(void *pp_ctx); + int (*power_off)(void *pp_ctx); + void (*mode_set)(void *pp_ctx, const struct drm_display_mode *in_mode); + int (*set_property)(struct drm_device *drm_dev, void *pp_ctx, + struct drm_property *property, uint64_t val); + int (*set_gamma)(void *pp_ctx, u16 *r, + u16 *g, u16 *b, uint32_t start, uint32_t size); +}; + +/* + * Exynos FIMD post processor structure + * @list: the list entry + * @ops: pointer to callbacks for post processor specific functionality + * @ctx: A pointer to the post processor's implementation specific context + * + */ +struct exynos_fimd_pp { + struct list_head list; + struct exynos_fimd_pp_ops *ops; + void *ctx; +}; + +#endif diff --git a/include/video/samsung_fimd.h b/include/video/samsung_fimd.h index b039320..3ff7cad 100644 --- a/include/video/samsung_fimd.h +++ b/include/video/samsung_fimd.h @@ -60,7 +60,7 @@ #define VIDCON0_CLKVAL_F_SHIFT 6 #define VIDCON0_CLKVAL_F_LIMIT 0xff #define VIDCON0_CLKVAL_F(_x) ((_x) << 6) -#define VIDCON0_VLCKFREE (1 << 5) +#define VIDCON0_VCLKFREE (1 << 5) #define VIDCON0_CLKDIR (1 << 4) #define VIDCON0_CLKSEL_MASK (0x3 << 2) @@ -435,6 +435,10 @@ #define BLENDCON_NEW_8BIT_ALPHA_VALUE (1 << 0) #define BLENDCON_NEW_4BIT_ALPHA_VALUE (0 << 0) +/* DP_MDNIE_CLKCON */ +#define DP_MDNIE_CLKCON 0x27c +#define MDNIE_CLK_ENABLE 0x3 + /* Notes on per-window bpp settings * * Value Win0 Win1 Win2 Win3 Win 4 -- 1.8.1.2 -- To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html