Add S2R functionality for HDMI and Mixer
authorShirish S <s.shirish@samsung.com>
Mon, 17 Sep 2012 12:35:52 +0000 (18:05 +0530)
committerGerrit <chrome-bot@google.com>
Tue, 18 Sep 2012 06:21:16 +0000 (23:21 -0700)
This patch adds clock gating support for HDMI and
Mixer via runtime PM routines.

BUG=chrome-os-partner:12958
Test=On Snow,
DISPLAY=":0" xset dpms force off/on
HDMI hotplug in and out
rtcwake -m mem -s <X>
This system behaves as expected.

Change-Id: I19e1acedc7d732a9f3c6a35918bd67c2d867144a
Signed-off-by: Shirish S <s.shirish@samsung.com>
Signed-off-by: Amit Daniel Kachhap <amit.daniel@samsung.com>
Signed-off-by: Rahul Sharma <rahul.sharma@samsung.com>
Reviewed-on: https://gerrit.chromium.org/gerrit/32216
Reviewed-by: Sean Paul <seanpaul@chromium.org>
Tested-by: Shirish S <shirish@chromium.org>
Reviewed-by: Shirish S <shirish@chromium.org>
Commit-Ready: Shirish S <shirish@chromium.org>

Documentation/devicetree/bindings/hdmi/samsung-hdmi.txt [new file with mode: 0644]
drivers/gpu/drm/exynos/exynos_drm_hdmi.c
drivers/gpu/drm/exynos/exynos_drm_hdmi.h
drivers/gpu/drm/exynos/exynos_hdmi.c
drivers/gpu/drm/exynos/exynos_mixer.c

diff --git a/Documentation/devicetree/bindings/hdmi/samsung-hdmi.txt b/Documentation/devicetree/bindings/hdmi/samsung-hdmi.txt
new file mode 100644 (file)
index 0000000..522a75c
--- /dev/null
@@ -0,0 +1,10 @@
+* Samsung's HDMI Controller
+
+The Samsung's HDMI controller can drive HDMI and DVI monitors.
+
+Required properties:
+- compatible : should be "samsung,exynos5-hdmi"
+- reg : base physical address of the controller and length of memory mapped
+       region.
+- interrupts : interrupt number to the cpu.
+- hpd-gpio   : gpio pin number for hpd signals.
index fe84d77..c042ede 100644 (file)
@@ -42,6 +42,7 @@ struct drm_hdmi_context {
        struct exynos_drm_subdrv        subdrv;
        struct exynos_drm_hdmi_context  *hdmi_ctx;
        struct exynos_drm_hdmi_context  *mixer_ctx;
+       bool    enabled[MIXER_WIN_NR];
 };
 
 void exynos_hdmi_drv_attach(struct exynos_drm_hdmi_context *ctx)
@@ -116,6 +117,11 @@ static int drm_hdmi_power_on(struct device *dev, int mode)
 
        DRM_DEBUG_KMS("%s\n", __FILE__);
 
+       if (mixer_ops && mixer_ops->power_on) {
+               if (mixer_ops->power_on(ctx->mixer_ctx->ctx, mode))
+                       return -EINVAL;
+       }
+
        if (hdmi_ops && hdmi_ops->power_on)
                return hdmi_ops->power_on(ctx->hdmi_ctx->ctx, mode);
 
@@ -206,23 +212,34 @@ static void drm_hdmi_dpms(struct device *subdrv_dev, int mode)
 
        DRM_DEBUG_KMS("%s\n", __FILE__);
 
-       switch (mode) {
-       case DRM_MODE_DPMS_ON:
-               break;
-       case DRM_MODE_DPMS_STANDBY:
-       case DRM_MODE_DPMS_SUSPEND:
-       case DRM_MODE_DPMS_OFF:
-               if (hdmi_ops && hdmi_ops->disable)
-                       hdmi_ops->disable(ctx->hdmi_ctx->ctx);
-               break;
-       default:
-               DRM_DEBUG_KMS("unkown dps mode: %d\n", mode);
-               break;
+       if (mixer_ops && mixer_ops->dpms)
+               mixer_ops->dpms(ctx->mixer_ctx->ctx, mode);
+
+       if (hdmi_ops && hdmi_ops->dpms)
+               hdmi_ops->dpms(ctx->hdmi_ctx->ctx, mode);
+}
+
+static void drm_hdmi_apply(struct device *subdrv_dev)
+{
+       struct drm_hdmi_context *ctx = to_context(subdrv_dev);
+       int i;
+
+       DRM_DEBUG_KMS("%s\n", __FILE__);
+
+       for (i = 0; i < MIXER_WIN_NR; i++) {
+               if (!ctx->enabled[i])
+                       continue;
+               if (mixer_ops && mixer_ops->win_commit)
+                       mixer_ops->win_commit(ctx->mixer_ctx->ctx, i);
        }
+
+       if (hdmi_ops && hdmi_ops->commit)
+               hdmi_ops->commit(ctx->hdmi_ctx->ctx);
 }
 
 static struct exynos_drm_manager_ops drm_hdmi_manager_ops = {
        .dpms = drm_hdmi_dpms,
+       .apply = drm_hdmi_apply,
        .enable_vblank = drm_hdmi_enable_vblank,
        .disable_vblank = drm_hdmi_disable_vblank,
        .mode_fixup = drm_hdmi_mode_fixup,
@@ -245,21 +262,37 @@ static void drm_mixer_mode_set(struct device *subdrv_dev,
 static void drm_mixer_commit(struct device *subdrv_dev, int zpos)
 {
        struct drm_hdmi_context *ctx = to_context(subdrv_dev);
+       int win = (zpos == DEFAULT_ZPOS) ? MIXER_DEFAULT_WIN : zpos;
 
        DRM_DEBUG_KMS("%s\n", __FILE__);
 
+       if (win < 0 || win > MIXER_WIN_NR) {
+               DRM_ERROR("mixer window[%d] is wrong\n", win);
+               return;
+       }
+
        if (mixer_ops && mixer_ops->win_commit)
-               mixer_ops->win_commit(ctx->mixer_ctx->ctx, zpos);
+               mixer_ops->win_commit(ctx->mixer_ctx->ctx, win);
+
+       ctx->enabled[win] = true;
 }
 
 static void drm_mixer_disable(struct device *subdrv_dev, int zpos)
 {
        struct drm_hdmi_context *ctx = to_context(subdrv_dev);
+       int win = (zpos == DEFAULT_ZPOS) ? MIXER_DEFAULT_WIN : zpos;
 
        DRM_DEBUG_KMS("%s\n", __FILE__);
 
+       if (win < 0 || win > MIXER_WIN_NR) {
+               DRM_ERROR("mixer window[%d] is wrong\n", win);
+               return;
+       }
+
        if (mixer_ops && mixer_ops->win_disable)
-               mixer_ops->win_disable(ctx->mixer_ctx->ctx, zpos);
+               mixer_ops->win_disable(ctx->mixer_ctx->ctx, win);
+
+       ctx->enabled[win] = false;
 }
 
 static struct exynos_drm_overlay_ops drm_hdmi_overlay_ops = {
index a065d6f..0afa654 100644 (file)
@@ -26,6 +26,9 @@
 #ifndef _EXYNOS_DRM_HDMI_H_
 #define _EXYNOS_DRM_HDMI_H_
 
+#define MIXER_WIN_NR           3
+#define MIXER_DEFAULT_WIN      0
+
 /*
  * exynos hdmi common context structure.
  *
@@ -54,13 +57,15 @@ struct exynos_hdmi_ops {
        void (*get_max_resol)(void *ctx, unsigned int *width,
                                unsigned int *height);
        void (*commit)(void *ctx);
-       void (*disable)(void *ctx);
+       void (*dpms)(void *ctx, int mode);
 };
 
 struct exynos_mixer_ops {
        /* manager */
        int (*enable_vblank)(void *ctx, int pipe);
        void (*disable_vblank)(void *ctx);
+       void (*dpms)(void *ctx, int mode);
+       int (*power_on)(void *ctx, int mode);
 
        /* overlay */
        void (*win_mode_set)(void *ctx, struct exynos_drm_overlay *overlay);
index 444cf09..a146c6a 100644 (file)
@@ -33,6 +33,9 @@
 #include <linux/pm_runtime.h>
 #include <linux/clk.h>
 #include <linux/regulator/consumer.h>
+#include <linux/io.h>
+#include <linux/of_gpio.h>
+#include <plat/gpio-cfg.h>
 
 #include <drm/exynos_drm.h>
 
@@ -135,16 +138,19 @@ struct hdmi_context {
        bool                            has_hdmi_sink;
        bool                            has_hdmi_audio;
        bool                            is_soc_exynos5;
+       bool                            is_hdmi_powered_on;
 
        struct resource                 *regs_res;
        void __iomem                    *regs;
-       unsigned int                    irq;
+       unsigned int                    external_irq;
+       unsigned int                    internal_irq;
+       unsigned int                    curr_irq;
        struct workqueue_struct         *wq;
        struct work_struct              hotplug_work;
 
        struct i2c_client               *ddc_port;
        struct i2c_client               *hdmiphy_port;
-
+       int                             hpd_gpio;
        /* current hdmiphy conf index */
        int cur_conf;
 
@@ -503,6 +509,22 @@ static inline void hdmi_reg_writemask(struct hdmi_context *hdata,
        writel(value, hdata->regs + reg_id);
 }
 
+static void hdmi_cfg_hpd(struct hdmi_context *hdata, bool external)
+{
+       if (external) {
+               s3c_gpio_cfgpin(hdata->hpd_gpio, S3C_GPIO_SFN(0xf));
+               s3c_gpio_setpull(hdata->hpd_gpio, S3C_GPIO_PULL_DOWN);
+       } else {
+               s3c_gpio_cfgpin(hdata->hpd_gpio, S3C_GPIO_SFN(3));
+               s3c_gpio_setpull(hdata->hpd_gpio, S3C_GPIO_PULL_NONE);
+       }
+}
+
+static int hdmi_get_hpd(struct hdmi_context *hdata)
+{
+       return gpio_get_value(hdata->hpd_gpio);
+}
+
 static void hdmi_v13_regs_dump(struct hdmi_context *hdata, char *prefix)
 {
 #define DUMPREG(reg_id) \
@@ -784,12 +806,17 @@ static int hdmi_v13_conf_index(struct drm_display_mode *mode)
 static bool hdmi_is_connected(void *ctx)
 {
        struct hdmi_context *hdata = ctx;
-       u32 val = hdmi_reg_read(hdata, HDMI_HPD_STATUS);
-
-       if (val)
-               return true;
+       if (hdata->is_hdmi_powered_on) {
+               if (!hdmi_reg_read(hdata, HDMI_HPD_STATUS)) {
+                       DRM_DEBUG_KMS("hdmi is not connected\n");
+                       return false;
+               }
+       } else if (!hdmi_get_hpd(hdata)) {
+                       DRM_DEBUG_KMS("hdmi is not connected\n");
+                       return false;
+       }
 
-       return false;
+       return true;
 }
 
 static int hdmi_get_edid(void *ctx, struct drm_connector *connector,
@@ -962,28 +989,6 @@ static int hdmi_check_timing(void *ctx, void *timing)
                return hdmi_v14_check_timing(check_timing);
 }
 
-static int hdmi_display_power_on(void *ctx, int mode)
-{
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
-       switch (mode) {
-       case DRM_MODE_DPMS_ON:
-               DRM_DEBUG_KMS("hdmi [on]\n");
-               break;
-       case DRM_MODE_DPMS_STANDBY:
-               break;
-       case DRM_MODE_DPMS_SUSPEND:
-               break;
-       case DRM_MODE_DPMS_OFF:
-               DRM_DEBUG_KMS("hdmi [off]\n");
-               break;
-       default:
-               break;
-       }
-
-       return 0;
-}
-
 static void hdmi_set_acr(u32 freq, u8 *acr)
 {
        u32 n, cts;
@@ -1754,23 +1759,38 @@ static void hdmi_commit(void *ctx)
        struct hdmi_context *hdata = ctx;
 
        DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-       hdmi_conf_apply(hdata);
+       if (!hdata->is_hdmi_powered_on)
+               return;
 
+       hdmi_conf_apply(hdata);
        hdata->enabled = true;
 }
 
-static void hdmi_disable(void *ctx)
+static int hdmi_power_on(void *ctx, int mode)
 {
        struct hdmi_context *hdata = ctx;
 
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
-       if (hdata->enabled) {
-               if (!hdata->is_soc_exynos5)
-                       hdmi_audio_control(hdata, false);
-               hdmiphy_conf_reset(hdata);
-               hdmi_conf_reset(hdata);
+       switch (mode) {
+       case DRM_MODE_DPMS_ON:
+               if (!hdata->is_hdmi_powered_on) {
+                       pm_runtime_get_sync(hdata->dev);
+                       hdmi_commit(ctx);
+               }
+               break;
+       case DRM_MODE_DPMS_STANDBY:
+               break;
+       case DRM_MODE_DPMS_SUSPEND:
+               break;
+       case DRM_MODE_DPMS_OFF:
+               if (hdata->is_hdmi_powered_on)
+                       pm_runtime_put_sync(hdata->dev);
+               break;
+       default:
+               DRM_DEBUG_KMS("unknown dpms mode: %d\n", mode);
+               break;
        }
+
+       return 0;
 }
 
 static struct exynos_hdmi_ops hdmi_ops = {
@@ -1778,13 +1798,12 @@ static struct exynos_hdmi_ops hdmi_ops = {
        .is_connected   = hdmi_is_connected,
        .get_edid       = hdmi_get_edid,
        .check_timing   = hdmi_check_timing,
-       .power_on       = hdmi_display_power_on,
+       .power_on       = hdmi_power_on,
 
        /* manager */
        .mode_fixup     = hdmi_mode_fixup,
        .mode_set       = hdmi_mode_set,
        .commit         = hdmi_commit,
-       .disable        = hdmi_disable,
 };
 
 /*
@@ -1805,18 +1824,21 @@ static irqreturn_t hdmi_irq_handler(int irq, void *arg)
        struct exynos_drm_hdmi_context *ctx = arg;
        struct hdmi_context *hdata = ctx->ctx;
        u32 intc_flag;
-
-       intc_flag = hdmi_reg_read(hdata, HDMI_INTC_FLAG);
-       /* clearing flags for HPD plug/unplug */
-       if (intc_flag & HDMI_INTC_FLAG_HPD_UNPLUG) {
-               DRM_DEBUG_KMS("unplugged, handling:%d\n", hdata->hpd_handle);
-               hdmi_reg_writemask(hdata, HDMI_INTC_FLAG, ~0,
-                       HDMI_INTC_FLAG_HPD_UNPLUG);
-       }
-       if (intc_flag & HDMI_INTC_FLAG_HPD_PLUG) {
-               DRM_DEBUG_KMS("plugged, handling:%d\n", hdata->hpd_handle);
-               hdmi_reg_writemask(hdata, HDMI_INTC_FLAG, ~0,
-                       HDMI_INTC_FLAG_HPD_PLUG);
+       if (hdata->is_hdmi_powered_on) {
+               intc_flag = hdmi_reg_read(hdata, HDMI_INTC_FLAG);
+               /* clearing flags for HPD plug/unplug */
+               if (intc_flag & HDMI_INTC_FLAG_HPD_UNPLUG) {
+                       DRM_DEBUG_KMS("int unplugged, handling:%d\n",
+                               hdata->hpd_handle);
+                       hdmi_reg_writemask(hdata, HDMI_INTC_FLAG, ~0,
+                               HDMI_INTC_FLAG_HPD_UNPLUG);
+               }
+               if (intc_flag & HDMI_INTC_FLAG_HPD_PLUG) {
+                       DRM_DEBUG_KMS("int plugged, handling:%d\n",
+                               hdata->hpd_handle);
+                       hdmi_reg_writemask(hdata, HDMI_INTC_FLAG, ~0,
+                               HDMI_INTC_FLAG_HPD_PLUG);
+               }
        }
 
        if (ctx->drm_dev && hdata->hpd_handle)
@@ -1890,6 +1912,12 @@ static int __devinit hdmi_resources_init(struct hdmi_context *hdata)
        }
        res->regul_count = ARRAY_SIZE(supply);
 #endif
+       /* TODO:
+        * These clocks also should be added in
+        * runtime resume and runtime suspend
+        */
+       clk_enable(res->hdmi);
+       clk_enable(res->sclk_hdmi);
 
        return 0;
 fail:
@@ -1923,15 +1951,24 @@ static void hdmi_resource_poweron(struct hdmi_context *hdata)
 {
        struct hdmi_resources *res = &hdata->res;
 
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+       hdata->is_hdmi_powered_on = true;
+       hdmi_cfg_hpd(hdata, false);
+
+       /* irq change by TV power status */
+       if (hdata->curr_irq == hdata->internal_irq)
+               return;
+
+       disable_irq(hdata->curr_irq);
+
+       hdata->curr_irq = hdata->internal_irq;
+
+       enable_irq(hdata->curr_irq);
 
        /* turn HDMI power on */
        regulator_bulk_enable(res->regul_count, res->regul_bulk);
-       /* power-on hdmi physical interface */
+
+       /* power-on hdmi clocks */
        clk_enable(res->hdmiphy);
-       /* turn clocks on */
-       clk_enable(res->hdmi);
-       clk_enable(res->sclk_hdmi);
 
        hdmiphy_conf_reset(hdata);
        hdmi_conf_reset(hdata);
@@ -1944,24 +1981,68 @@ static void hdmi_resource_poweroff(struct hdmi_context *hdata)
 {
        struct hdmi_resources *res = &hdata->res;
 
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+       hdmi_cfg_hpd(hdata, true);
 
-       /* turn clocks off */
-       clk_disable(res->sclk_hdmi);
-       clk_disable(res->hdmi);
-       /* power-off hdmiphy */
+       if (hdata->curr_irq == hdata->external_irq)
+               return;
+
+       disable_irq(hdata->curr_irq);
+       hdata->curr_irq = hdata->external_irq;
+
+       enable_irq(hdata->curr_irq);
+       hdata->is_hdmi_powered_on = false;
+
+       hdmiphy_conf_reset(hdata);
+
+       /* power-off hdmi clocks */
        clk_disable(res->hdmiphy);
+
        /* turn HDMI power off */
        regulator_bulk_disable(res->regul_count, res->regul_bulk);
 }
 
-static int hdmi_runtime_suspend(struct device *dev)
+#ifdef CONFIG_PM_SLEEP
+static int hdmi_suspend(struct device *dev)
 {
        struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev);
+       struct hdmi_context *hdata = ctx->ctx;
 
-       DRM_DEBUG_KMS("%s\n", __func__);
+       DRM_DEBUG_KMS("[hdmi] sleep suspend - start\n");
+       if (pm_runtime_suspended(dev)) {
+               DRM_DEBUG_KMS("[hdmi] sleep suspend - already suspended\n");
+               return 0;
+       }
 
-       hdmi_resource_poweroff(ctx->ctx);
+       hdmi_resource_poweroff(hdata);
+       DRM_DEBUG_KMS("[hdmi] sleep suspend - end\n");
+
+       return 0;
+}
+static int hdmi_resume(struct device *dev)
+{
+       struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev);
+       struct hdmi_context *hdata = ctx->ctx;
+
+       DRM_DEBUG_KMS("[hdmi] sleep resume - start\n");
+
+       if (!pm_runtime_suspended(dev)) {
+               hdmi_resource_poweron(hdata);
+               hdmi_commit(hdata);
+               DRM_DEBUG_KMS("[hdmi] sleep resuming\n");
+       }
+       DRM_DEBUG_KMS("[hdmi] sleep resume - end\n");
+       return 0;
+}
+#endif
+#ifdef CONFIG_PM_RUNTIME
+static int hdmi_runtime_suspend(struct device *dev)
+{
+       struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev);
+       struct hdmi_context *hdata = ctx->ctx;
+
+       DRM_DEBUG_KMS("[hdmi] runtime suspend - start\n");
+       hdmi_resource_poweroff(hdata);
+       DRM_DEBUG_KMS("[hdmi] runtime suspend - end\n");
 
        return 0;
 }
@@ -1969,17 +2050,19 @@ static int hdmi_runtime_suspend(struct device *dev)
 static int hdmi_runtime_resume(struct device *dev)
 {
        struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev);
+       struct hdmi_context *hdata = ctx->ctx;
 
-       DRM_DEBUG_KMS("%s\n", __func__);
+       DRM_DEBUG_KMS("[hdmi] runtime resume - start\n");
 
-       hdmi_resource_poweron(ctx->ctx);
+       hdmi_resource_poweron(hdata);
 
+       DRM_DEBUG_KMS("[hdmi] runtime resume - end\n");
        return 0;
 }
-
+#endif
 static const struct dev_pm_ops hdmi_pm_ops = {
-       .runtime_suspend = hdmi_runtime_suspend,
-       .runtime_resume  = hdmi_runtime_resume,
+       SET_SYSTEM_SLEEP_PM_OPS(hdmi_suspend, hdmi_resume)
+       SET_RUNTIME_PM_OPS(hdmi_runtime_suspend, hdmi_runtime_resume, NULL)
 };
 
 static struct i2c_client *hdmi_ddc, *hdmi_hdmiphy;
@@ -2006,6 +2089,7 @@ static int __devinit hdmi_probe(struct platform_device *pdev)
        struct exynos_drm_hdmi_pdata *pdata;
        struct resource *res;
        int ret;
+       enum of_gpio_flags flags;
 
        DRM_DEBUG_KMS("[%d]\n", __LINE__);
 
@@ -2119,6 +2203,18 @@ static int __devinit hdmi_probe(struct platform_device *pdev)
                goto err_hdmiphy;
        }
 
+       hdata->internal_irq = res->start;
+
+       hdata->hpd_gpio = of_get_named_gpio_flags(dev->of_node,
+                               "hpd-gpio", 0, &flags);
+
+       if (!gpio_is_valid(hdata->hpd_gpio)) {
+               DRM_ERROR("failed to get hpd gpio.");
+               goto err_hdmiphy;
+       }
+
+       hdata->external_irq = gpio_to_irq(hdata->hpd_gpio);
+
        /* create workqueue and hotplug work */
        hdata->wq = alloc_workqueue("exynos-drm-hdmi",
                        WQ_UNBOUND | WQ_NON_REENTRANT, 1);
@@ -2129,23 +2225,36 @@ static int __devinit hdmi_probe(struct platform_device *pdev)
        }
        INIT_WORK(&hdata->hotplug_work, hdmi_hotplug_func);
 
-       /* register hpd interrupt */
-       ret = request_irq(res->start, hdmi_irq_handler, 0, "drm_hdmi",
-                               drm_hdmi_ctx);
+       ret = request_irq(hdata->internal_irq, hdmi_irq_handler,
+                       0, "int_hdmi", hdata->parent_ctx);
+       if (ret) {
+               DRM_ERROR("request interrupt failed.\n");
+               goto err_workqueue;
+       }
+       disable_irq(hdata->internal_irq);
+
+       ret = request_irq(hdata->external_irq, hdmi_irq_handler,
+               IRQ_TYPE_EDGE_BOTH, "ext_hdmi", hdata->parent_ctx);
        if (ret) {
                DRM_ERROR("request interrupt failed.\n");
                goto err_workqueue;
        }
-       hdata->irq = res->start;
+       disable_irq(hdata->external_irq);
 
        /* Attach HDMI Driver to common hdmi. */
        exynos_hdmi_drv_attach(drm_hdmi_ctx);
 
        /* register specific callbacks to common hdmi. */
        exynos_hdmi_ops_register(&hdmi_ops);
-
        hdmi_resource_poweron(hdata);
+       pm_runtime_enable(dev);
 
+       if (!hdmi_is_connected(hdata)) {
+               hdmi_resource_poweroff(hdata);
+               DRM_DEBUG_KMS("gpio state is low. powering off!\n");
+       } else {
+               pm_runtime_get_sync(dev);
+       }
        return 0;
 
 err_workqueue:
@@ -2171,17 +2280,21 @@ static int __devexit hdmi_remove(struct platform_device *pdev)
 {
        struct exynos_drm_hdmi_context *ctx = platform_get_drvdata(pdev);
        struct hdmi_context *hdata = ctx->ctx;
+       struct hdmi_resources *res = &hdata->res;
 
        DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
 
        hdmi_resource_poweroff(hdata);
 
-       disable_irq(hdata->irq);
-       free_irq(hdata->irq, hdata);
+       disable_irq(hdata->curr_irq);
+       free_irq(hdata->internal_irq, hdata);
+       free_irq(hdata->external_irq, hdata);
 
        cancel_work_sync(&hdata->hotplug_work);
        destroy_workqueue(hdata->wq);
 
+       clk_disable(res->hdmi);
+       clk_disable(res->sclk_hdmi);
        hdmi_resources_cleanup(hdata);
 
        iounmap(hdata->regs);
index 20b6c82..34354cb 100644 (file)
@@ -44,8 +44,6 @@
 #include <linux/of_platform.h>
 #endif
 
-#define MIXER_WIN_NR           3
-#define MIXER_DEFAULT_WIN      0
 
 #define get_mixer_context(dev) platform_get_drvdata(to_platform_device(dev))
 
@@ -86,13 +84,15 @@ struct mixer_resources {
 };
 
 struct mixer_context {
+       struct device           *dev;
        unsigned int            irq;
        int                     pipe;
        bool                    interlace;
+       bool                    is_mixer_powered_on;
 
        struct mixer_resources  mixer_res;
        struct hdmi_win_data    win_data[MIXER_WIN_NR];
-       unsigned long event_flags;
+       unsigned long           event_flags;
 };
 
 /* event flags used  */
@@ -129,7 +129,7 @@ static const u8 filter_cr_horiz_tap4[] = {
        70,     59,     48,     37,     27,     19,     11,     5,
 };
 
-static void mixer_win_reset(struct mixer_context *ctx);
+static void mixer_win_reset(struct mixer_context *mctx);
 
 static inline u32 vp_reg_read(struct mixer_resources *res, u32 reg_id)
 {
@@ -171,12 +171,12 @@ static inline void mixer_reg_writemask(struct mixer_resources *res,
        writel(val, res->mixer_regs + reg_id);
 }
 
-static void mixer_regs_dump(struct mixer_context *ctx)
+static void mixer_regs_dump(struct mixer_context *mctx)
 {
 #define DUMPREG(reg_id) \
 do { \
        DRM_DEBUG_KMS(#reg_id " = %08x\n", \
-               (u32)readl(ctx->mixer_res.mixer_regs + reg_id)); \
+               (u32)readl(mctx->mixer_res.mixer_regs + reg_id)); \
 } while (0)
 
        DUMPREG(MXR_STATUS);
@@ -203,12 +203,12 @@ do { \
 #undef DUMPREG
 }
 
-static void vp_regs_dump(struct mixer_context *ctx)
+static void vp_regs_dump(struct mixer_context *mctx)
 {
 #define DUMPREG(reg_id) \
 do { \
        DRM_DEBUG_KMS(#reg_id " = %08x\n", \
-               (u32) readl(ctx->mixer_res.vp_regs + reg_id)); \
+               (u32) readl(mctx->mixer_res.vp_regs + reg_id)); \
 } while (0)
 
        DUMPREG(VP_ENABLE);
@@ -260,9 +260,9 @@ static void vp_default_filter(struct mixer_resources *res)
                filter_cr_horiz_tap4, sizeof filter_cr_horiz_tap4);
 }
 
-static void mixer_vsync_set_update(struct mixer_context *ctx, bool enable)
+static void mixer_vsync_set_update(struct mixer_context *mctx, bool enable)
 {
-       struct mixer_resources *res = &ctx->mixer_res;
+       struct mixer_resources *res = &mctx->mixer_res;
 
        /* block update on vsync */
        mixer_reg_writemask(res, MXR_STATUS, enable ?
@@ -273,13 +273,13 @@ static void mixer_vsync_set_update(struct mixer_context *ctx, bool enable)
                                VP_SHADOW_UPDATE_ENABLE : 0);
 }
 
-static void mixer_cfg_scan(struct mixer_context *ctx, unsigned int height)
+static void mixer_cfg_scan(struct mixer_context *mctx, unsigned int height)
 {
-       struct mixer_resources *res = &ctx->mixer_res;
+       struct mixer_resources *res = &mctx->mixer_res;
        u32 val;
 
        /* choosing between interlace and progressive mode */
-       val = (ctx->interlace ? MXR_CFG_SCAN_INTERLACE :
+       val = (mctx->interlace ? MXR_CFG_SCAN_INTERLACE :
                                MXR_CFG_SCAN_PROGRASSIVE);
 
        /* choosing between porper HD and SD mode */
@@ -297,9 +297,9 @@ static void mixer_cfg_scan(struct mixer_context *ctx, unsigned int height)
        mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_SCAN_MASK);
 }
 
-static void mixer_cfg_rgb_fmt(struct mixer_context *ctx, unsigned int height)
+static void mixer_cfg_rgb_fmt(struct mixer_context *mctx, unsigned int height)
 {
-       struct mixer_resources *res = &ctx->mixer_res;
+       struct mixer_resources *res = &mctx->mixer_res;
        u32 val;
 
        if (height == 480) {
@@ -338,9 +338,9 @@ static void mixer_cfg_rgb_fmt(struct mixer_context *ctx, unsigned int height)
        mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_RGB_FMT_MASK);
 }
 
-static void mixer_cfg_layer(struct mixer_context *ctx, int win, bool enable)
+static void mixer_cfg_layer(struct mixer_context *mctx, int win, bool enable)
 {
-       struct mixer_resources *res = &ctx->mixer_res;
+       struct mixer_resources *res = &mctx->mixer_res;
        u32 val = enable ? ~0 : 0;
 
        switch (win) {
@@ -357,23 +357,23 @@ static void mixer_cfg_layer(struct mixer_context *ctx, int win, bool enable)
        }
 }
 
-static void mixer_run(struct mixer_context *ctx)
+static void mixer_run(struct mixer_context *mctx)
 {
-       struct mixer_resources *res = &ctx->mixer_res;
+       struct mixer_resources *res = &mctx->mixer_res;
 
        mixer_reg_writemask(res, MXR_STATUS, ~0, MXR_STATUS_REG_RUN);
 
-       mixer_regs_dump(ctx);
+       mixer_regs_dump(mctx);
 }
 
-static int mixer_wait_for_vsync(struct mixer_context *ctx)
+static int mixer_wait_for_vsync(struct mixer_context *mctx)
 {
        int ret;
 
-       ctx->event_flags |= MXR_EVENT_VSYNC;
+       mctx->event_flags |= MXR_EVENT_VSYNC;
 
-       ret = wait_event_timeout(ctx->mixer_res.event_queue,
-       ((ctx->event_flags & MXR_EVENT_VSYNC) == 0), msecs_to_jiffies(1000));
+       ret = wait_event_timeout(mctx->mixer_res.event_queue,
+       ((mctx->event_flags & MXR_EVENT_VSYNC) == 0), msecs_to_jiffies(1000));
        if (ret > 0)
                return 0;
 
@@ -404,9 +404,9 @@ static void mixer_layer_update(struct mixer_context *ctx)
        mixer_reg_writemask(res, MXR_CFG, ~0, MXR_CFG_LAYER_UPDATE);
 }
 
-static void vp_video_buffer(struct mixer_context *ctx, int win)
+static void vp_video_buffer(struct mixer_context *mctx, int win)
 {
-       struct mixer_resources *res = &ctx->mixer_res;
+       struct mixer_resources *res = &mctx->mixer_res;
        unsigned long flags;
        struct hdmi_win_data *win_data;
        unsigned int full_width, full_height, width, height;
@@ -419,7 +419,7 @@ static void vp_video_buffer(struct mixer_context *ctx, int win)
        bool crcb_mode = false;
        u32 val;
 
-       win_data = &ctx->win_data[win];
+       win_data = &mctx->win_data[win];
 
        switch (win_data->pixel_format) {
        case DRM_FORMAT_NV12MT:
@@ -465,7 +465,7 @@ static void vp_video_buffer(struct mixer_context *ctx, int win)
        }
 
        if (win_data->scan_flags & DRM_MODE_FLAG_INTERLACE) {
-               ctx->interlace = true;
+               mctx->interlace = true;
                if (tiled_mode) {
                        luma_addr[1] = luma_addr[0] + 0x40;
                        chroma_addr[1] = chroma_addr[0] + 0x40;
@@ -474,16 +474,16 @@ static void vp_video_buffer(struct mixer_context *ctx, int win)
                        chroma_addr[1] = chroma_addr[0] + full_width;
                }
        } else {
-               ctx->interlace = false;
+               mctx->interlace = false;
                luma_addr[1] = 0;
                chroma_addr[1] = 0;
        }
 
        spin_lock_irqsave(&res->reg_slock, flags);
-       mixer_vsync_set_update(ctx, false);
+       mixer_vsync_set_update(mctx, false);
 
        /* interlace or progressive scan mode */
-       val = (ctx->interlace ? ~0 : 0);
+       val = (mctx->interlace ? ~0 : 0);
        vp_reg_writemask(res, VP_MODE, val, VP_MODE_LINE_SKIP);
 
        /* setup format */
@@ -506,7 +506,7 @@ static void vp_video_buffer(struct mixer_context *ctx, int win)
 
        vp_reg_write(res, VP_DST_WIDTH, width);
        vp_reg_write(res, VP_DST_H_POSITION, dst_x_offset);
-       if (ctx->interlace) {
+       if (mctx->interlace) {
                vp_reg_write(res, VP_DST_HEIGHT, height / 2);
                vp_reg_write(res, VP_DST_V_POSITION, dst_y_offset / 2);
        } else {
@@ -525,20 +525,20 @@ static void vp_video_buffer(struct mixer_context *ctx, int win)
        vp_reg_write(res, VP_TOP_C_PTR, chroma_addr[0]);
        vp_reg_write(res, VP_BOT_C_PTR, chroma_addr[1]);
 
-       mixer_cfg_scan(ctx, mode_height);
-       mixer_cfg_rgb_fmt(ctx, mode_height);
-       mixer_cfg_layer(ctx, win, true);
-       mixer_run(ctx);
+       mixer_cfg_scan(mctx, mode_height);
+       mixer_cfg_rgb_fmt(mctx, mode_height);
+       mixer_cfg_layer(mctx, win, true);
+       mixer_run(mctx);
 
-       mixer_vsync_set_update(ctx, true);
+       mixer_vsync_set_update(mctx, true);
        spin_unlock_irqrestore(&res->reg_slock, flags);
 
-       vp_regs_dump(ctx);
+       vp_regs_dump(mctx);
 }
 
-static void mixer_graph_buffer(struct mixer_context *ctx, int win)
+static void mixer_graph_buffer(struct mixer_context *mctx, int win)
 {
-       struct mixer_resources *res = &ctx->mixer_res;
+       struct mixer_resources *res = &mctx->mixer_res;
        unsigned long flags;
        struct hdmi_win_data *win_data;
        unsigned int full_width, width, height;
@@ -549,7 +549,7 @@ static void mixer_graph_buffer(struct mixer_context *ctx, int win)
        unsigned int fmt;
        u32 val;
 
-       win_data = &ctx->win_data[win];
+       win_data = &mctx->win_data[win];
 
        #define RGB565 4
        #define ARGB1555 5
@@ -591,12 +591,12 @@ static void mixer_graph_buffer(struct mixer_context *ctx, int win)
        src_y_offset = 0;
 
        if (win_data->scan_flags & DRM_MODE_FLAG_INTERLACE)
-               ctx->interlace = true;
+               mctx->interlace = true;
        else
-               ctx->interlace = false;
+               mctx->interlace = false;
 
        spin_lock_irqsave(&res->reg_slock, flags);
-       mixer_vsync_set_update(ctx, false);
+       mixer_vsync_set_update(mctx, false);
 
        /* setup format */
        mixer_reg_writemask(res, MXR_GRAPHIC_CFG(win),
@@ -624,26 +624,25 @@ static void mixer_graph_buffer(struct mixer_context *ctx, int win)
        /* set buffer address to mixer */
        mixer_reg_write(res, MXR_GRAPHIC_BASE(win), dma_addr);
 
-       mixer_cfg_scan(ctx, mode_height);
-       mixer_cfg_rgb_fmt(ctx, mode_height);
-       mixer_cfg_layer(ctx, win, true);
-       mixer_cfg_layer(ctx, MIXER_DEFAULT_WIN, true);
+       mixer_cfg_scan(mctx, mode_height);
+       mixer_cfg_rgb_fmt(mctx, mode_height);
+       mixer_cfg_layer(mctx, win, true);
+       mixer_cfg_layer(mctx, MIXER_DEFAULT_WIN, true);
 
        /* Only allow one update per vsync */
        if (!win_data->updated)
-               mixer_layer_update(ctx);
+               mixer_layer_update(mctx);
 
        win_data->updated = true;
+       mixer_run(mctx);
 
-       mixer_run(ctx);
-
-       mixer_vsync_set_update(ctx, true);
+       mixer_vsync_set_update(mctx, true);
        spin_unlock_irqrestore(&res->reg_slock, flags);
 }
 
-static void vp_win_reset(struct mixer_context *ctx)
+static void vp_win_reset(struct mixer_context *mctx)
 {
-       struct mixer_resources *res = &ctx->mixer_res;
+       struct mixer_resources *res = &mctx->mixer_res;
        int tries = 100;
 
        vp_reg_write(res, VP_SRESET, VP_SRESET_PROCESSING);
@@ -658,12 +657,12 @@ static void vp_win_reset(struct mixer_context *ctx)
 
 static int mixer_enable_vblank(void *ctx, int pipe)
 {
-       struct mixer_context *mixer_ctx = ctx;
-       struct mixer_resources *res = &mixer_ctx->mixer_res;
+       struct mixer_context *mctx = ctx;
+       struct mixer_resources *res = &mctx->mixer_res;
 
        DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
 
-       mixer_ctx->pipe = pipe;
+       mctx->pipe = pipe;
 
        /* enable vsync interrupt */
        mixer_reg_writemask(res, MXR_INT_EN, MXR_INT_EN_VSYNC,
@@ -674,8 +673,8 @@ static int mixer_enable_vblank(void *ctx, int pipe)
 
 static void mixer_disable_vblank(void *ctx)
 {
-       struct mixer_context *mixer_ctx = ctx;
-       struct mixer_resources *res = &mixer_ctx->mixer_res;
+       struct mixer_context *mctx = ctx;
+       struct mixer_resources *res = &mctx->mixer_res;
 
        DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
 
@@ -686,7 +685,7 @@ static void mixer_disable_vblank(void *ctx)
 static void mixer_win_mode_set(void *ctx,
                              struct exynos_drm_overlay *overlay)
 {
-       struct mixer_context *mixer_ctx = ctx;
+       struct mixer_context *mctx = ctx;
        struct hdmi_win_data *win_data;
        int win;
 
@@ -712,7 +711,7 @@ static void mixer_win_mode_set(void *ctx,
                return;
        }
 
-       win_data = &mixer_ctx->win_data[win];
+       win_data = &mctx->win_data[win];
 
        win_data->dma_addr = overlay->dma_addr[0];
        win_data->vaddr = overlay->vaddr[0];
@@ -739,12 +738,11 @@ static void mixer_win_mode_set(void *ctx,
 
 static void mixer_win_commit(void *ctx, int zpos)
 {
-       struct mixer_context *mixer_ctx = ctx;
-       struct mixer_resources *res = &mixer_ctx->mixer_res;
+       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);
-
        if (win == DEFAULT_ZPOS)
                win = MIXER_DEFAULT_WIN;
 
@@ -753,20 +751,25 @@ static void mixer_win_commit(void *ctx, int zpos)
                return;
        }
 
+       if (!mctx->is_mixer_powered_on) {
+               DRM_DEBUG_KMS("[%d] %s not powered on\n", __LINE__, __func__);
+               return;
+       }
+
        if (!(res->is_soc_exynos5)) {
                if (win > 1)
-                       vp_video_buffer(mixer_ctx, win);
+                       vp_video_buffer(mctx, win);
                else
-                       mixer_graph_buffer(mixer_ctx, win);
+                       mixer_graph_buffer(mctx, win);
        }
        else
-               mixer_graph_buffer(mixer_ctx, win);
+               mixer_graph_buffer(mctx, win);
 }
 
 static void mixer_win_disable(void *ctx, int zpos)
 {
-       struct mixer_context *mixer_ctx = ctx;
-       struct mixer_resources *res = &mixer_ctx->mixer_res;
+       struct mixer_context *mctx = ctx;
+       struct mixer_resources *res = &mctx->mixer_res;
        unsigned long flags;
        int win = zpos;
 
@@ -780,40 +783,29 @@ static void mixer_win_disable(void *ctx, int zpos)
                return;
        }
 
-       mixer_wait_for_vsync(mixer_ctx);
+       mixer_wait_for_vsync(mctx);
 
        spin_lock_irqsave(&res->reg_slock, flags);
-       mixer_vsync_set_update(mixer_ctx, false);
+       mixer_vsync_set_update(mctx, false);
 
-       mixer_cfg_layer(mixer_ctx, win, false);
+       mixer_cfg_layer(mctx, win, false);
 
-       mixer_vsync_set_update(mixer_ctx, true);
+       mixer_vsync_set_update(mctx, true);
 
        spin_unlock_irqrestore(&res->reg_slock, flags);
 
        if (win == MIXER_DEFAULT_WIN) {
-               mixer_win_reset(ctx);
-               mixer_enable_vblank(ctx, mixer_ctx->pipe);
+               mixer_win_reset(mctx);
+               mixer_enable_vblank(mctx, mctx->pipe);
        }
 }
 
-static struct exynos_mixer_ops mixer_ops = {
-       /* manager */
-       .enable_vblank          = mixer_enable_vblank,
-       .disable_vblank         = mixer_disable_vblank,
-
-       /* overlay */
-       .win_mode_set           = mixer_win_mode_set,
-       .win_commit             = mixer_win_commit,
-       .win_disable            = mixer_win_disable,
-};
-
 /* for pageflip event */
 static irqreturn_t mixer_irq_handler(int irq, void *arg)
 {
        struct exynos_drm_hdmi_context *drm_hdmi_ctx = arg;
-       struct mixer_context *ctx = drm_hdmi_ctx->ctx;
-       struct mixer_resources *res = &ctx->mixer_res;
+       struct mixer_context *mctx = drm_hdmi_ctx->ctx;
+       struct mixer_resources *res = &mctx->mixer_res;
        u32 val, base, shadow;
        int i;
 
@@ -825,8 +817,8 @@ static irqreturn_t mixer_irq_handler(int irq, void *arg)
        /* handling VSYNC */
        if (val & MXR_INT_STATUS_VSYNC) {
 
-               if (ctx->event_flags & MXR_EVENT_VSYNC) {
-                       DRM_DEBUG_KMS("ctx->event_flags & MXR_EVENT_VSYNC");
+               if (mctx->event_flags & MXR_EVENT_VSYNC) {
+                       DRM_DEBUG_KMS("mctx->event_flags & MXR_EVENT_VSYNC");
 
 
                        mixer_reg_write(res, MXR_GRAPHIC_WH(1), 0);
@@ -834,13 +826,13 @@ static irqreturn_t mixer_irq_handler(int irq, void *arg)
                        mixer_reg_write(res, MXR_GRAPHIC_SXY(1), 0);
                        mixer_reg_write(res, MXR_GRAPHIC_DXY(1), 0);
 
-                       ctx->event_flags &= ~MXR_EVENT_VSYNC;
-                       wake_up(&ctx->mixer_res.event_queue);
+                       mctx->event_flags &= ~MXR_EVENT_VSYNC;
+                       wake_up(&mctx->mixer_res.event_queue);
                        goto out;
                }
 
                /* interlace scan need to check shadow register */
-               if (ctx->interlace) {
+               if (mctx->interlace) {
                        base = mixer_reg_read(res, MXR_GRAPHIC_BASE(0));
                        shadow = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(0));
                        if (base != shadow)
@@ -852,17 +844,16 @@ static irqreturn_t mixer_irq_handler(int irq, void *arg)
                                goto out;
                }
 
-               drm_handle_vblank(drm_hdmi_ctx->drm_dev, ctx->pipe);
+               drm_handle_vblank(drm_hdmi_ctx->drm_dev, mctx->pipe);
 
                /* Bail out if a layer update is pending */
-               if (mixer_get_layer_update_count(ctx))
+               if (mixer_get_layer_update_count(mctx))
                        goto out;
 
                for (i = 0; i < MIXER_WIN_NR; i++)
-                       ctx->win_data[i].updated = false;
-
+                       mctx->win_data[i].updated = false;
                exynos_drm_crtc_finish_pageflip(drm_hdmi_ctx->drm_dev,
-                                               ctx->pipe);
+                                               mctx->pipe);
        }
 
 out:
@@ -879,15 +870,15 @@ out:
        return IRQ_HANDLED;
 }
 
-static void mixer_win_reset(struct mixer_context *ctx)
+static void mixer_win_reset(struct mixer_context *mctx)
 {
-       struct mixer_resources *res = &ctx->mixer_res;
+       struct mixer_resources *res = &mctx->mixer_res;
        unsigned long flags;
        u32 val; /* value stored to register */
 
        spin_lock_irqsave(&res->reg_slock, flags);
        mixer_reg_writemask(res, MXR_STATUS, ~0, MXR_STATUS_SOFT_RESET);
-       mixer_vsync_set_update(ctx, false);
+       mixer_vsync_set_update(mctx, false);
 
        mixer_reg_writemask(res, MXR_CFG, MXR_CFG_DST_HDMI, MXR_CFG_DST_MASK);
 
@@ -929,7 +920,7 @@ static void mixer_win_reset(struct mixer_context *ctx)
 
        if (!(res->is_soc_exynos5)) {
                /* configuration of Video Processor for Exynos4 soc */
-               vp_win_reset(ctx);
+               vp_win_reset(mctx);
                vp_default_filter(res);
        }
 
@@ -940,15 +931,16 @@ static void mixer_win_reset(struct mixer_context *ctx)
 
        mixer_reg_writemask(res, MXR_INT_EN, ~0, MXR_INT_EN_ALL);
 
-       mixer_vsync_set_update(ctx, true);
+       mixer_vsync_set_update(mctx, true);
        spin_unlock_irqrestore(&res->reg_slock, flags);
 }
 
-static void mixer_resource_poweron(struct mixer_context *ctx)
+static void mixer_resource_poweron(struct mixer_context *mctx)
 {
-       struct mixer_resources *res = &ctx->mixer_res;
+       struct mixer_resources *res = &mctx->mixer_res;
 
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+       if (mctx->is_mixer_powered_on)
+               return;
 
        clk_enable(res->mixer);
        if (!(res->is_soc_exynos5)) {
@@ -956,29 +948,69 @@ static void mixer_resource_poweron(struct mixer_context *ctx)
                clk_enable(res->sclk_mixer);
        }
 
-       mixer_win_reset(ctx);
+       mixer_win_reset(mctx);
+       mctx->is_mixer_powered_on = true;
 }
 
-static void mixer_resource_poweroff(struct mixer_context *ctx)
+static void mixer_resource_poweroff(struct mixer_context *mctx)
 {
-       struct mixer_resources *res = &ctx->mixer_res;
+       struct mixer_resources *res = &mctx->mixer_res;
 
        DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+       if (!mctx->is_mixer_powered_on)
+               return;
 
        clk_disable(res->mixer);
        if (!(res->is_soc_exynos5)) {
                clk_disable(res->vp);
                clk_disable(res->sclk_mixer);
        }
+       mctx->is_mixer_powered_on = false;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int mixer_resume(struct device *dev)
+{
+       struct exynos_drm_hdmi_context *ctx = get_mixer_context(dev);
+       struct mixer_context *mctx = ctx->ctx;
+
+       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 exynos_drm_hdmi_context *ctx = get_mixer_context(dev);
+       struct mixer_context *mctx = ctx->ctx;
+
+       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 exynos_drm_hdmi_context *ctx = get_mixer_context(dev);
+       struct mixer_context *mctx = ctx->ctx;
 
-       DRM_DEBUG_KMS("resume - start\n");
+       DRM_DEBUG_KMS("[mixer] runtime resume - start\n");
 
-       mixer_resource_poweron(ctx->ctx);
+       mixer_resource_poweron(mctx);
+       DRM_DEBUG_KMS("[mixer] runtime resume - end\n");
 
        return 0;
 }
@@ -986,17 +1018,63 @@ static int mixer_runtime_resume(struct device *dev)
 static int mixer_runtime_suspend(struct device *dev)
 {
        struct exynos_drm_hdmi_context *ctx = get_mixer_context(dev);
+       struct mixer_context *mctx = ctx->ctx;
 
-       DRM_DEBUG_KMS("suspend - start\n");
+       DRM_DEBUG_KMS("[mixer] runtime suspend - start\n");
 
-       mixer_resource_poweroff(ctx->ctx);
+       mixer_resource_poweroff(mctx);
+       DRM_DEBUG_KMS("[mixer] runtime suspend - end\n");
+       return 0;
+}
+
+#endif
+
+static int mixer_power_on(void *ctx, int mode)
+{
+       struct mixer_context *mctx = ctx;
+
+       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+       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);
+               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);
+               break;
+       default:
+               DRM_DEBUG_KMS("unknown dpms mode: %d\n", mode);
+               break;
+       }
 
        return 0;
 }
 
+static struct exynos_mixer_ops mixer_ops = {
+       /* manager */
+       .enable_vblank          = mixer_enable_vblank,
+       .disable_vblank         = mixer_disable_vblank,
+       .power_on               = mixer_power_on,
+
+       /* overlay */
+       .win_mode_set           = mixer_win_mode_set,
+       .win_commit             = mixer_win_commit,
+       .win_disable            = mixer_win_disable,
+};
+
 static const struct dev_pm_ops mixer_pm_ops = {
-       .runtime_suspend = mixer_runtime_suspend,
-       .runtime_resume  = mixer_runtime_resume,
+       SET_SYSTEM_SLEEP_PM_OPS(mixer_suspend, mixer_resume)
+       SET_RUNTIME_PM_OPS(mixer_runtime_suspend, mixer_runtime_resume, NULL)
 };
 
 #ifdef CONFIG_EXYNOS_IOMMU
@@ -1033,9 +1111,9 @@ static int __devinit mixer_resources_init_exynos(
                        struct platform_device *pdev,
                        int is_exynos5)
 {
-       struct mixer_context *mixer_ctx = ctx->ctx;
+       struct mixer_context *mctx = ctx->ctx;
        struct device *dev = &pdev->dev;
-       struct mixer_resources *mixer_res = &mixer_ctx->mixer_res;
+       struct mixer_resources *mixer_res = &mctx->mixer_res;
        struct resource *res;
        int ret;
 
@@ -1169,12 +1247,12 @@ fail:
        return ret;
 }
 
-static void mixer_resources_cleanup(struct mixer_context *ctx)
+static void mixer_resources_cleanup(struct mixer_context *mctx)
 {
-       struct mixer_resources *res = &ctx->mixer_res;
+       struct mixer_resources *res = &mctx->mixer_res;
 
        disable_irq(res->irq);
-       free_irq(res->irq, ctx);
+       free_irq(res->irq, mctx);
 
        iounmap(res->vp_regs);
        iounmap(res->mixer_regs);
@@ -1185,7 +1263,7 @@ static int __devinit mixer_probe(struct platform_device *pdev)
        struct device *dev = &pdev->dev;
        struct exynos_drm_hdmi_context *drm_hdmi_ctx;
        struct exynos_drm_hdmi_pdata *pdata;
-       struct mixer_context *ctx;
+       struct mixer_context *mctx;
        int ret;
 
        dev_info(dev, "probe start\n");
@@ -1196,14 +1274,16 @@ static int __devinit mixer_probe(struct platform_device *pdev)
                return -ENOMEM;
        }
 
-       ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
-       if (!ctx) {
+       mctx = kzalloc(sizeof(*mctx), GFP_KERNEL);
+       if (!mctx) {
                DRM_ERROR("failed to alloc mixer context.\n");
                kfree(drm_hdmi_ctx);
                return -ENOMEM;
        }
 
-       drm_hdmi_ctx->ctx = (void *)ctx;
+
+       mctx->dev = &pdev->dev;
+       drm_hdmi_ctx->ctx = (void *)mctx;
 
        platform_set_drvdata(pdev, drm_hdmi_ctx);
 
@@ -1220,8 +1300,8 @@ static int __devinit mixer_probe(struct platform_device *pdev)
 
        /* register specific callback point to common hdmi. */
        exynos_mixer_ops_register(&mixer_ops);
-
-       mixer_resource_poweron(ctx);
+       mctx->is_mixer_powered_on = false;
+       pm_runtime_enable(dev);
 
        return 0;
 
@@ -1236,12 +1316,12 @@ static int mixer_remove(struct platform_device *pdev)
        struct device *dev = &pdev->dev;
        struct exynos_drm_hdmi_context *drm_hdmi_ctx =
                                        platform_get_drvdata(pdev);
-       struct mixer_context *ctx = drm_hdmi_ctx->ctx;
+       struct mixer_context *mctx = drm_hdmi_ctx->ctx;
 
        dev_info(dev, "remove successful\n");
 
-       mixer_resource_poweroff(ctx);
-       mixer_resources_cleanup(ctx);
+       mixer_resource_poweroff(mctx);
+       mixer_resources_cleanup(mctx);
 
        return 0;
 }