From 6ca16eb0cd2ee10dfd7c66e26d42aa9fc923f6b8 Mon Sep 17 00:00:00 2001 From: Shirish S Date: Mon, 17 Sep 2012 18:05:52 +0530 Subject: [PATCH] Add S2R functionality for HDMI and Mixer 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 This system behaves as expected. Change-Id: I19e1acedc7d732a9f3c6a35918bd67c2d867144a Signed-off-by: Shirish S Signed-off-by: Amit Daniel Kachhap Signed-off-by: Rahul Sharma Reviewed-on: https://gerrit.chromium.org/gerrit/32216 Reviewed-by: Sean Paul Tested-by: Shirish S Reviewed-by: Shirish S Commit-Ready: Shirish S --- .../devicetree/bindings/hdmi/samsung-hdmi.txt | 10 + drivers/gpu/drm/exynos/exynos_drm_hdmi.c | 61 +++- drivers/gpu/drm/exynos/exynos_drm_hdmi.h | 7 +- drivers/gpu/drm/exynos/exynos_hdmi.c | 267 ++++++++++---- drivers/gpu/drm/exynos/exynos_mixer.c | 338 +++++++++++------- 5 files changed, 462 insertions(+), 221 deletions(-) create mode 100644 Documentation/devicetree/bindings/hdmi/samsung-hdmi.txt diff --git a/Documentation/devicetree/bindings/hdmi/samsung-hdmi.txt b/Documentation/devicetree/bindings/hdmi/samsung-hdmi.txt new file mode 100644 index 000000000000..522a75c63b4e --- /dev/null +++ b/Documentation/devicetree/bindings/hdmi/samsung-hdmi.txt @@ -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. diff --git a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c index fe84d77ed365..c042edeeffba 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c @@ -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 = { diff --git a/drivers/gpu/drm/exynos/exynos_drm_hdmi.h b/drivers/gpu/drm/exynos/exynos_drm_hdmi.h index a065d6f62c05..0afa65410555 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_hdmi.h +++ b/drivers/gpu/drm/exynos/exynos_drm_hdmi.h @@ -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); diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 444cf09b023e..a146c6a23390 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -33,6 +33,9 @@ #include #include #include +#include +#include +#include #include @@ -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); diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c index 20b6c828539a..34354cb7573b 100644 --- a/drivers/gpu/drm/exynos/exynos_mixer.c +++ b/drivers/gpu/drm/exynos/exynos_mixer.c @@ -44,8 +44,6 @@ #include #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; } -- 2.20.1