提交 cf8fc4f1 编写于 作者: J Joonyoung Shim 提交者: Inki Dae

drm/exynos: add dpms for hdmi

The power and clocks turns on always in exynos hdmi and mixer driver,
but we should turn off the power and clocks of exynos hdmi and mixer
when the hdmi cable unplugged or when hdmi unused.

There are two interrupt to detect hotplug of hdmi cable - internal
interrupt and external interrupt. The internal interrupt can use only
when hdmi is dpms on so if hdmi is dpms off, we should use external
interrupt to detect hotplug of hdmi cable. If hdmi is dpms on, we cannot
external interrupt because the gpio pin for external interrupt is used
to hdmi HPD pin for internal interrupt.
Signed-off-by: NJoonyoung Shim <jy0922.shim@samsung.com>
Signed-off-by: NKyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: NInki Dae <inki.dae@samsung.com>
上级 66265a2e
...@@ -37,6 +37,8 @@ struct drm_hdmi_context { ...@@ -37,6 +37,8 @@ struct drm_hdmi_context {
struct exynos_drm_subdrv subdrv; struct exynos_drm_subdrv subdrv;
struct exynos_drm_hdmi_context *hdmi_ctx; struct exynos_drm_hdmi_context *hdmi_ctx;
struct exynos_drm_hdmi_context *mixer_ctx; struct exynos_drm_hdmi_context *mixer_ctx;
bool enabled[MIXER_WIN_NR];
}; };
void exynos_hdmi_ops_register(struct exynos_hdmi_ops *ops) void exynos_hdmi_ops_register(struct exynos_hdmi_ops *ops)
...@@ -189,23 +191,34 @@ static void drm_hdmi_dpms(struct device *subdrv_dev, int mode) ...@@ -189,23 +191,34 @@ static void drm_hdmi_dpms(struct device *subdrv_dev, int mode)
DRM_DEBUG_KMS("%s\n", __FILE__); DRM_DEBUG_KMS("%s\n", __FILE__);
switch (mode) { if (mixer_ops && mixer_ops->dpms)
case DRM_MODE_DPMS_ON: mixer_ops->dpms(ctx->mixer_ctx->ctx, mode);
break;
case DRM_MODE_DPMS_STANDBY: if (hdmi_ops && hdmi_ops->dpms)
case DRM_MODE_DPMS_SUSPEND: hdmi_ops->dpms(ctx->hdmi_ctx->ctx, mode);
case DRM_MODE_DPMS_OFF: }
if (hdmi_ops && hdmi_ops->disable)
hdmi_ops->disable(ctx->hdmi_ctx->ctx); static void drm_hdmi_apply(struct device *subdrv_dev)
break; {
default: struct drm_hdmi_context *ctx = to_context(subdrv_dev);
DRM_DEBUG_KMS("unkown dps mode: %d\n", mode); int i;
break;
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 = { static struct exynos_drm_manager_ops drm_hdmi_manager_ops = {
.dpms = drm_hdmi_dpms, .dpms = drm_hdmi_dpms,
.apply = drm_hdmi_apply,
.enable_vblank = drm_hdmi_enable_vblank, .enable_vblank = drm_hdmi_enable_vblank,
.disable_vblank = drm_hdmi_disable_vblank, .disable_vblank = drm_hdmi_disable_vblank,
.mode_fixup = drm_hdmi_mode_fixup, .mode_fixup = drm_hdmi_mode_fixup,
...@@ -228,21 +241,37 @@ static void drm_mixer_mode_set(struct device *subdrv_dev, ...@@ -228,21 +241,37 @@ static void drm_mixer_mode_set(struct device *subdrv_dev,
static void drm_mixer_commit(struct device *subdrv_dev, int zpos) static void drm_mixer_commit(struct device *subdrv_dev, int zpos)
{ {
struct drm_hdmi_context *ctx = to_context(subdrv_dev); struct drm_hdmi_context *ctx = to_context(subdrv_dev);
int win = (zpos == DEFAULT_ZPOS) ? MIXER_DEFAULT_WIN : zpos;
DRM_DEBUG_KMS("%s\n", __FILE__); 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) 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) static void drm_mixer_disable(struct device *subdrv_dev, int zpos)
{ {
struct drm_hdmi_context *ctx = to_context(subdrv_dev); struct drm_hdmi_context *ctx = to_context(subdrv_dev);
int win = (zpos == DEFAULT_ZPOS) ? MIXER_DEFAULT_WIN : zpos;
DRM_DEBUG_KMS("%s\n", __FILE__); 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) 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 = { static struct exynos_drm_overlay_ops drm_hdmi_overlay_ops = {
...@@ -335,25 +364,6 @@ static int __devinit exynos_drm_hdmi_probe(struct platform_device *pdev) ...@@ -335,25 +364,6 @@ static int __devinit exynos_drm_hdmi_probe(struct platform_device *pdev)
return 0; return 0;
} }
static int hdmi_runtime_suspend(struct device *dev)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
return 0;
}
static int hdmi_runtime_resume(struct device *dev)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
return 0;
}
static const struct dev_pm_ops hdmi_pm_ops = {
.runtime_suspend = hdmi_runtime_suspend,
.runtime_resume = hdmi_runtime_resume,
};
static int __devexit exynos_drm_hdmi_remove(struct platform_device *pdev) static int __devexit exynos_drm_hdmi_remove(struct platform_device *pdev)
{ {
struct drm_hdmi_context *ctx = platform_get_drvdata(pdev); struct drm_hdmi_context *ctx = platform_get_drvdata(pdev);
...@@ -372,6 +382,5 @@ struct platform_driver exynos_drm_common_hdmi_driver = { ...@@ -372,6 +382,5 @@ struct platform_driver exynos_drm_common_hdmi_driver = {
.driver = { .driver = {
.name = "exynos-drm-hdmi", .name = "exynos-drm-hdmi",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pm = &hdmi_pm_ops,
}, },
}; };
...@@ -26,6 +26,9 @@ ...@@ -26,6 +26,9 @@
#ifndef _EXYNOS_DRM_HDMI_H_ #ifndef _EXYNOS_DRM_HDMI_H_
#define _EXYNOS_DRM_HDMI_H_ #define _EXYNOS_DRM_HDMI_H_
#define MIXER_WIN_NR 3
#define MIXER_DEFAULT_WIN 0
/* /*
* exynos hdmi common context structure. * exynos hdmi common context structure.
* *
...@@ -54,13 +57,14 @@ struct exynos_hdmi_ops { ...@@ -54,13 +57,14 @@ struct exynos_hdmi_ops {
void (*get_max_resol)(void *ctx, unsigned int *width, void (*get_max_resol)(void *ctx, unsigned int *width,
unsigned int *height); unsigned int *height);
void (*commit)(void *ctx); void (*commit)(void *ctx);
void (*disable)(void *ctx); void (*dpms)(void *ctx, int mode);
}; };
struct exynos_mixer_ops { struct exynos_mixer_ops {
/* manager */ /* manager */
int (*enable_vblank)(void *ctx, int pipe); int (*enable_vblank)(void *ctx, int pipe);
void (*disable_vblank)(void *ctx); void (*disable_vblank)(void *ctx);
void (*dpms)(void *ctx, int mode);
/* overlay */ /* overlay */
void (*win_mode_set)(void *ctx, struct exynos_drm_overlay *overlay); void (*win_mode_set)(void *ctx, struct exynos_drm_overlay *overlay);
......
...@@ -57,13 +57,15 @@ struct hdmi_resources { ...@@ -57,13 +57,15 @@ struct hdmi_resources {
struct hdmi_context { struct hdmi_context {
struct device *dev; struct device *dev;
struct drm_device *drm_dev; struct drm_device *drm_dev;
bool hpd_handle; bool hpd;
bool enabled; bool powered;
bool is_v13; bool is_v13;
struct mutex hdmi_mutex;
struct resource *regs_res; struct resource *regs_res;
void __iomem *regs; void __iomem *regs;
unsigned int irq; unsigned int external_irq;
unsigned int internal_irq;
struct i2c_client *ddc_port; struct i2c_client *ddc_port;
struct i2c_client *hdmiphy_port; struct i2c_client *hdmiphy_port;
...@@ -1192,12 +1194,8 @@ static int hdmi_conf_index(struct hdmi_context *hdata, ...@@ -1192,12 +1194,8 @@ static int hdmi_conf_index(struct hdmi_context *hdata,
static bool hdmi_is_connected(void *ctx) static bool hdmi_is_connected(void *ctx)
{ {
struct hdmi_context *hdata = ctx; struct hdmi_context *hdata = ctx;
u32 val = hdmi_reg_read(hdata, HDMI_HPD_STATUS);
if (val) return hdata->hpd;
return true;
return false;
} }
static int hdmi_get_edid(void *ctx, struct drm_connector *connector, static int hdmi_get_edid(void *ctx, struct drm_connector *connector,
...@@ -1287,28 +1285,6 @@ static int hdmi_check_timing(void *ctx, void *timing) ...@@ -1287,28 +1285,6 @@ static int hdmi_check_timing(void *ctx, void *timing)
return hdmi_v14_check_timing(check_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) static void hdmi_set_acr(u32 freq, u8 *acr)
{ {
u32 n, cts; u32 n, cts;
...@@ -1476,9 +1452,6 @@ static void hdmi_conf_reset(struct hdmi_context *hdata) ...@@ -1476,9 +1452,6 @@ static void hdmi_conf_reset(struct hdmi_context *hdata)
{ {
u32 reg; u32 reg;
/* disable hpd handle for drm */
hdata->hpd_handle = false;
if (hdata->is_v13) if (hdata->is_v13)
reg = HDMI_V13_CORE_RSTOUT; reg = HDMI_V13_CORE_RSTOUT;
else else
...@@ -1489,16 +1462,10 @@ static void hdmi_conf_reset(struct hdmi_context *hdata) ...@@ -1489,16 +1462,10 @@ static void hdmi_conf_reset(struct hdmi_context *hdata)
mdelay(10); mdelay(10);
hdmi_reg_writemask(hdata, reg, ~0, HDMI_CORE_SW_RSTOUT); hdmi_reg_writemask(hdata, reg, ~0, HDMI_CORE_SW_RSTOUT);
mdelay(10); mdelay(10);
/* enable hpd handle for drm */
hdata->hpd_handle = true;
} }
static void hdmi_conf_init(struct hdmi_context *hdata) static void hdmi_conf_init(struct hdmi_context *hdata)
{ {
/* disable hpd handle for drm */
hdata->hpd_handle = false;
/* enable HPD interrupts */ /* enable HPD interrupts */
hdmi_reg_writemask(hdata, HDMI_INTC_CON, 0, HDMI_INTC_EN_GLOBAL | hdmi_reg_writemask(hdata, HDMI_INTC_CON, 0, HDMI_INTC_EN_GLOBAL |
HDMI_INTC_EN_HPD_PLUG | HDMI_INTC_EN_HPD_UNPLUG); HDMI_INTC_EN_HPD_PLUG | HDMI_INTC_EN_HPD_UNPLUG);
...@@ -1533,9 +1500,6 @@ static void hdmi_conf_init(struct hdmi_context *hdata) ...@@ -1533,9 +1500,6 @@ static void hdmi_conf_init(struct hdmi_context *hdata)
hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(1), 2 << 5); hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(1), 2 << 5);
hdmi_reg_writemask(hdata, HDMI_CON_1, 2, 3 << 5); hdmi_reg_writemask(hdata, HDMI_CON_1, 2, 3 << 5);
} }
/* enable hpd handle for drm */
hdata->hpd_handle = true;
} }
static void hdmi_v13_timing_apply(struct hdmi_context *hdata) static void hdmi_v13_timing_apply(struct hdmi_context *hdata)
...@@ -1888,8 +1852,11 @@ static void hdmi_conf_apply(struct hdmi_context *hdata) ...@@ -1888,8 +1852,11 @@ static void hdmi_conf_apply(struct hdmi_context *hdata)
hdmiphy_conf_reset(hdata); hdmiphy_conf_reset(hdata);
hdmiphy_conf_apply(hdata); hdmiphy_conf_apply(hdata);
mutex_lock(&hdata->hdmi_mutex);
hdmi_conf_reset(hdata); hdmi_conf_reset(hdata);
hdmi_conf_init(hdata); hdmi_conf_init(hdata);
mutex_unlock(&hdata->hdmi_mutex);
hdmi_audio_init(hdata); hdmi_audio_init(hdata);
/* setting core registers */ /* setting core registers */
...@@ -1969,20 +1936,86 @@ static void hdmi_commit(void *ctx) ...@@ -1969,20 +1936,86 @@ static void hdmi_commit(void *ctx)
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
hdmi_conf_apply(hdata); hdmi_conf_apply(hdata);
}
static void hdmi_poweron(struct hdmi_context *hdata)
{
struct hdmi_resources *res = &hdata->res;
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
mutex_lock(&hdata->hdmi_mutex);
if (hdata->powered) {
mutex_unlock(&hdata->hdmi_mutex);
return;
}
hdata->powered = true;
hdata->enabled = true; if (hdata->cfg_hpd)
hdata->cfg_hpd(true);
mutex_unlock(&hdata->hdmi_mutex);
pm_runtime_get_sync(hdata->dev);
regulator_bulk_enable(res->regul_count, res->regul_bulk);
clk_enable(res->hdmiphy);
clk_enable(res->hdmi);
clk_enable(res->sclk_hdmi);
} }
static void hdmi_disable(void *ctx) static void hdmi_poweroff(struct hdmi_context *hdata)
{ {
struct hdmi_context *hdata = ctx; struct hdmi_resources *res = &hdata->res;
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
if (hdata->enabled) { mutex_lock(&hdata->hdmi_mutex);
hdmi_audio_control(hdata, false); if (!hdata->powered)
goto out;
mutex_unlock(&hdata->hdmi_mutex);
/*
* The TV power domain needs any condition of hdmiphy to turn off and
* its reset state seems to meet the condition.
*/
hdmiphy_conf_reset(hdata); hdmiphy_conf_reset(hdata);
hdmi_conf_reset(hdata);
clk_disable(res->sclk_hdmi);
clk_disable(res->hdmi);
clk_disable(res->hdmiphy);
regulator_bulk_disable(res->regul_count, res->regul_bulk);
pm_runtime_put_sync(hdata->dev);
mutex_lock(&hdata->hdmi_mutex);
if (hdata->cfg_hpd)
hdata->cfg_hpd(false);
hdata->powered = false;
out:
mutex_unlock(&hdata->hdmi_mutex);
}
static void hdmi_dpms(void *ctx, int mode)
{
struct hdmi_context *hdata = ctx;
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
switch (mode) {
case DRM_MODE_DPMS_ON:
hdmi_poweron(hdata);
break;
case DRM_MODE_DPMS_STANDBY:
case DRM_MODE_DPMS_SUSPEND:
case DRM_MODE_DPMS_OFF:
hdmi_poweroff(hdata);
break;
default:
DRM_DEBUG_KMS("unknown dpms mode: %d\n", mode);
break;
} }
} }
...@@ -1991,17 +2024,35 @@ static struct exynos_hdmi_ops hdmi_ops = { ...@@ -1991,17 +2024,35 @@ static struct exynos_hdmi_ops hdmi_ops = {
.is_connected = hdmi_is_connected, .is_connected = hdmi_is_connected,
.get_edid = hdmi_get_edid, .get_edid = hdmi_get_edid,
.check_timing = hdmi_check_timing, .check_timing = hdmi_check_timing,
.power_on = hdmi_display_power_on,
/* manager */ /* manager */
.mode_fixup = hdmi_mode_fixup, .mode_fixup = hdmi_mode_fixup,
.mode_set = hdmi_mode_set, .mode_set = hdmi_mode_set,
.get_max_resol = hdmi_get_max_resol, .get_max_resol = hdmi_get_max_resol,
.commit = hdmi_commit, .commit = hdmi_commit,
.disable = hdmi_disable, .dpms = hdmi_dpms,
}; };
static irqreturn_t hdmi_irq_thread(int irq, void *arg) static irqreturn_t hdmi_external_irq_thread(int irq, void *arg)
{
struct exynos_drm_hdmi_context *ctx = arg;
struct hdmi_context *hdata = ctx->ctx;
if (!hdata->get_hpd)
goto out;
mutex_lock(&hdata->hdmi_mutex);
hdata->hpd = hdata->get_hpd();
mutex_unlock(&hdata->hdmi_mutex);
if (ctx->drm_dev)
drm_helper_hpd_irq_event(ctx->drm_dev);
out:
return IRQ_HANDLED;
}
static irqreturn_t hdmi_internal_irq_thread(int irq, void *arg)
{ {
struct exynos_drm_hdmi_context *ctx = arg; struct exynos_drm_hdmi_context *ctx = arg;
struct hdmi_context *hdata = ctx->ctx; struct hdmi_context *hdata = ctx->ctx;
...@@ -2010,19 +2061,28 @@ static irqreturn_t hdmi_irq_thread(int irq, void *arg) ...@@ -2010,19 +2061,28 @@ static irqreturn_t hdmi_irq_thread(int irq, void *arg)
intc_flag = hdmi_reg_read(hdata, HDMI_INTC_FLAG); intc_flag = hdmi_reg_read(hdata, HDMI_INTC_FLAG);
/* clearing flags for HPD plug/unplug */ /* clearing flags for HPD plug/unplug */
if (intc_flag & HDMI_INTC_FLAG_HPD_UNPLUG) { if (intc_flag & HDMI_INTC_FLAG_HPD_UNPLUG) {
DRM_DEBUG_KMS("unplugged, handling:%d\n", hdata->hpd_handle); DRM_DEBUG_KMS("unplugged\n");
hdmi_reg_writemask(hdata, HDMI_INTC_FLAG, ~0, hdmi_reg_writemask(hdata, HDMI_INTC_FLAG, ~0,
HDMI_INTC_FLAG_HPD_UNPLUG); HDMI_INTC_FLAG_HPD_UNPLUG);
} }
if (intc_flag & HDMI_INTC_FLAG_HPD_PLUG) { if (intc_flag & HDMI_INTC_FLAG_HPD_PLUG) {
DRM_DEBUG_KMS("plugged, handling:%d\n", hdata->hpd_handle); DRM_DEBUG_KMS("plugged\n");
hdmi_reg_writemask(hdata, HDMI_INTC_FLAG, ~0, hdmi_reg_writemask(hdata, HDMI_INTC_FLAG, ~0,
HDMI_INTC_FLAG_HPD_PLUG); HDMI_INTC_FLAG_HPD_PLUG);
} }
if (ctx->drm_dev && hdata->hpd_handle) mutex_lock(&hdata->hdmi_mutex);
hdata->hpd = hdmi_reg_read(hdata, HDMI_HPD_STATUS);
if (hdata->powered && hdata->hpd) {
mutex_unlock(&hdata->hdmi_mutex);
goto out;
}
mutex_unlock(&hdata->hdmi_mutex);
if (ctx->drm_dev)
drm_helper_hpd_irq_event(ctx->drm_dev); drm_helper_hpd_irq_event(ctx->drm_dev);
out:
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -2116,68 +2176,6 @@ static int hdmi_resources_cleanup(struct hdmi_context *hdata) ...@@ -2116,68 +2176,6 @@ static int hdmi_resources_cleanup(struct hdmi_context *hdata)
return 0; return 0;
} }
static void hdmi_resource_poweron(struct hdmi_context *hdata)
{
struct hdmi_resources *res = &hdata->res;
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
/* turn HDMI power on */
regulator_bulk_enable(res->regul_count, res->regul_bulk);
/* power-on hdmi physical interface */
clk_enable(res->hdmiphy);
/* turn clocks on */
clk_enable(res->hdmi);
clk_enable(res->sclk_hdmi);
hdmiphy_conf_reset(hdata);
hdmi_conf_reset(hdata);
hdmi_conf_init(hdata);
hdmi_audio_init(hdata);
}
static void hdmi_resource_poweroff(struct hdmi_context *hdata)
{
struct hdmi_resources *res = &hdata->res;
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
/* turn clocks off */
clk_disable(res->sclk_hdmi);
clk_disable(res->hdmi);
/* power-off hdmiphy */
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)
{
struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev);
DRM_DEBUG_KMS("%s\n", __func__);
hdmi_resource_poweroff(ctx->ctx);
return 0;
}
static int hdmi_runtime_resume(struct device *dev)
{
struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev);
DRM_DEBUG_KMS("%s\n", __func__);
hdmi_resource_poweron(ctx->ctx);
return 0;
}
static const struct dev_pm_ops hdmi_pm_ops = {
.runtime_suspend = hdmi_runtime_suspend,
.runtime_resume = hdmi_runtime_resume,
};
static struct i2c_client *hdmi_ddc, *hdmi_hdmiphy; static struct i2c_client *hdmi_ddc, *hdmi_hdmiphy;
void hdmi_attach_ddc_client(struct i2c_client *ddc) void hdmi_attach_ddc_client(struct i2c_client *ddc)
...@@ -2222,6 +2220,8 @@ static int __devinit hdmi_probe(struct platform_device *pdev) ...@@ -2222,6 +2220,8 @@ static int __devinit hdmi_probe(struct platform_device *pdev)
return -ENOMEM; return -ENOMEM;
} }
mutex_init(&hdata->hdmi_mutex);
drm_hdmi_ctx->ctx = (void *)hdata; drm_hdmi_ctx->ctx = (void *)hdata;
hdata->parent_ctx = (void *)drm_hdmi_ctx; hdata->parent_ctx = (void *)drm_hdmi_ctx;
...@@ -2278,28 +2278,49 @@ static int __devinit hdmi_probe(struct platform_device *pdev) ...@@ -2278,28 +2278,49 @@ static int __devinit hdmi_probe(struct platform_device *pdev)
hdata->hdmiphy_port = hdmi_hdmiphy; hdata->hdmiphy_port = hdmi_hdmiphy;
hdata->irq = platform_get_irq_byname(pdev, "internal_irq"); hdata->external_irq = platform_get_irq_byname(pdev, "external_irq");
if (hdata->irq < 0) { if (hdata->external_irq < 0) {
DRM_ERROR("failed to get platform irq\n"); DRM_ERROR("failed to get platform irq\n");
ret = hdata->irq; ret = hdata->external_irq;
goto err_hdmiphy; goto err_hdmiphy;
} }
/* register hpd interrupt */ hdata->internal_irq = platform_get_irq_byname(pdev, "internal_irq");
ret = request_threaded_irq(hdata->irq, NULL, hdmi_irq_thread, if (hdata->internal_irq < 0) {
IRQF_ONESHOT, "drm_hdmi", drm_hdmi_ctx); DRM_ERROR("failed to get platform internal irq\n");
ret = hdata->internal_irq;
goto err_hdmiphy;
}
ret = request_threaded_irq(hdata->external_irq, NULL,
hdmi_external_irq_thread, IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"hdmi_external", drm_hdmi_ctx);
if (ret) { if (ret) {
DRM_ERROR("request interrupt failed.\n"); DRM_ERROR("failed to register hdmi internal interrupt\n");
goto err_hdmiphy; goto err_hdmiphy;
} }
if (hdata->cfg_hpd)
hdata->cfg_hpd(false);
ret = request_threaded_irq(hdata->internal_irq, NULL,
hdmi_internal_irq_thread, IRQF_ONESHOT,
"hdmi_internal", drm_hdmi_ctx);
if (ret) {
DRM_ERROR("failed to register hdmi internal interrupt\n");
goto err_free_irq;
}
/* register specific callbacks to common hdmi. */ /* register specific callbacks to common hdmi. */
exynos_hdmi_ops_register(&hdmi_ops); exynos_hdmi_ops_register(&hdmi_ops);
hdmi_resource_poweron(hdata); pm_runtime_enable(dev);
return 0; return 0;
err_free_irq:
free_irq(hdata->external_irq, drm_hdmi_ctx);
err_hdmiphy: err_hdmiphy:
i2c_del_driver(&hdmiphy_driver); i2c_del_driver(&hdmiphy_driver);
err_ddc: err_ddc:
...@@ -2319,15 +2340,15 @@ static int __devinit hdmi_probe(struct platform_device *pdev) ...@@ -2319,15 +2340,15 @@ static int __devinit hdmi_probe(struct platform_device *pdev)
static int __devexit hdmi_remove(struct platform_device *pdev) static int __devexit hdmi_remove(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev;
struct exynos_drm_hdmi_context *ctx = platform_get_drvdata(pdev); struct exynos_drm_hdmi_context *ctx = platform_get_drvdata(pdev);
struct hdmi_context *hdata = ctx->ctx; struct hdmi_context *hdata = ctx->ctx;
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
hdmi_resource_poweroff(hdata); pm_runtime_disable(dev);
disable_irq(hdata->irq); free_irq(hdata->internal_irq, hdata);
free_irq(hdata->irq, hdata);
hdmi_resources_cleanup(hdata); hdmi_resources_cleanup(hdata);
...@@ -2352,6 +2373,5 @@ struct platform_driver hdmi_driver = { ...@@ -2352,6 +2373,5 @@ struct platform_driver hdmi_driver = {
.driver = { .driver = {
.name = "exynos4-hdmi", .name = "exynos4-hdmi",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pm = &hdmi_pm_ops,
}, },
}; };
...@@ -37,9 +37,6 @@ ...@@ -37,9 +37,6 @@
#include "exynos_drm_drv.h" #include "exynos_drm_drv.h"
#include "exynos_drm_hdmi.h" #include "exynos_drm_hdmi.h"
#define MIXER_WIN_NR 3
#define MIXER_DEFAULT_WIN 0
#define get_mixer_context(dev) platform_get_drvdata(to_platform_device(dev)) #define get_mixer_context(dev) platform_get_drvdata(to_platform_device(dev))
struct hdmi_win_data { struct hdmi_win_data {
...@@ -63,7 +60,6 @@ struct hdmi_win_data { ...@@ -63,7 +60,6 @@ struct hdmi_win_data {
}; };
struct mixer_resources { struct mixer_resources {
struct device *dev;
int irq; int irq;
void __iomem *mixer_regs; void __iomem *mixer_regs;
void __iomem *vp_regs; void __iomem *vp_regs;
...@@ -76,10 +72,13 @@ struct mixer_resources { ...@@ -76,10 +72,13 @@ struct mixer_resources {
}; };
struct mixer_context { struct mixer_context {
unsigned int irq; struct device *dev;
int pipe; int pipe;
bool interlace; bool interlace;
bool powered;
u32 int_en;
struct mutex mixer_mutex;
struct mixer_resources mixer_res; struct mixer_resources mixer_res;
struct hdmi_win_data win_data[MIXER_WIN_NR]; struct hdmi_win_data win_data[MIXER_WIN_NR];
}; };
...@@ -591,6 +590,116 @@ static void vp_win_reset(struct mixer_context *ctx) ...@@ -591,6 +590,116 @@ static void vp_win_reset(struct mixer_context *ctx)
WARN(tries == 0, "failed to reset Video Processor\n"); WARN(tries == 0, "failed to reset Video Processor\n");
} }
static void mixer_win_reset(struct mixer_context *ctx)
{
struct mixer_resources *res = &ctx->mixer_res;
unsigned long flags;
u32 val; /* value stored to register */
spin_lock_irqsave(&res->reg_slock, flags);
mixer_vsync_set_update(ctx, false);
mixer_reg_writemask(res, MXR_CFG, MXR_CFG_DST_HDMI, MXR_CFG_DST_MASK);
/* set output in RGB888 mode */
mixer_reg_writemask(res, MXR_CFG, MXR_CFG_OUT_RGB888, MXR_CFG_OUT_MASK);
/* 16 beat burst in DMA */
mixer_reg_writemask(res, MXR_STATUS, MXR_STATUS_16_BURST,
MXR_STATUS_BURST_MASK);
/* setting default layer priority: layer1 > layer0 > video
* because typical usage scenario would be
* layer1 - OSD
* layer0 - framebuffer
* video - video overlay
*/
val = MXR_LAYER_CFG_GRP1_VAL(3);
val |= MXR_LAYER_CFG_GRP0_VAL(2);
val |= MXR_LAYER_CFG_VP_VAL(1);
mixer_reg_write(res, MXR_LAYER_CFG, val);
/* setting background color */
mixer_reg_write(res, MXR_BG_COLOR0, 0x008080);
mixer_reg_write(res, MXR_BG_COLOR1, 0x008080);
mixer_reg_write(res, MXR_BG_COLOR2, 0x008080);
/* setting graphical layers */
val = MXR_GRP_CFG_COLOR_KEY_DISABLE; /* no blank key */
val |= MXR_GRP_CFG_WIN_BLEND_EN;
val |= MXR_GRP_CFG_ALPHA_VAL(0xff); /* non-transparent alpha */
/* the same configuration for both layers */
mixer_reg_write(res, MXR_GRAPHIC_CFG(0), val);
val |= MXR_GRP_CFG_BLEND_PRE_MUL;
val |= MXR_GRP_CFG_PIXEL_BLEND_EN;
mixer_reg_write(res, MXR_GRAPHIC_CFG(1), val);
/* configuration of Video Processor Registers */
vp_win_reset(ctx);
vp_default_filter(res);
/* disable all layers */
mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP0_ENABLE);
mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP1_ENABLE);
mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_VP_ENABLE);
mixer_vsync_set_update(ctx, true);
spin_unlock_irqrestore(&res->reg_slock, flags);
}
static void mixer_poweron(struct mixer_context *ctx)
{
struct mixer_resources *res = &ctx->mixer_res;
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
mutex_lock(&ctx->mixer_mutex);
if (ctx->powered) {
mutex_unlock(&ctx->mixer_mutex);
return;
}
ctx->powered = true;
mutex_unlock(&ctx->mixer_mutex);
pm_runtime_get_sync(ctx->dev);
clk_enable(res->mixer);
clk_enable(res->vp);
clk_enable(res->sclk_mixer);
mixer_reg_write(res, MXR_INT_EN, ctx->int_en);
mixer_win_reset(ctx);
}
static void mixer_poweroff(struct mixer_context *ctx)
{
struct mixer_resources *res = &ctx->mixer_res;
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
mutex_lock(&ctx->mixer_mutex);
if (!ctx->powered)
goto out;
mutex_unlock(&ctx->mixer_mutex);
ctx->int_en = mixer_reg_read(res, MXR_INT_EN);
clk_disable(res->mixer);
clk_disable(res->vp);
clk_disable(res->sclk_mixer);
pm_runtime_put_sync(ctx->dev);
mutex_lock(&ctx->mixer_mutex);
ctx->powered = false;
out:
mutex_unlock(&ctx->mixer_mutex);
}
static int mixer_enable_vblank(void *ctx, int pipe) static int mixer_enable_vblank(void *ctx, int pipe)
{ {
struct mixer_context *mixer_ctx = ctx; struct mixer_context *mixer_ctx = ctx;
...@@ -618,6 +727,27 @@ static void mixer_disable_vblank(void *ctx) ...@@ -618,6 +727,27 @@ static void mixer_disable_vblank(void *ctx)
mixer_reg_writemask(res, MXR_INT_EN, 0, MXR_INT_EN_VSYNC); mixer_reg_writemask(res, MXR_INT_EN, 0, MXR_INT_EN_VSYNC);
} }
static void mixer_dpms(void *ctx, int mode)
{
struct mixer_context *mixer_ctx = ctx;
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
switch (mode) {
case DRM_MODE_DPMS_ON:
mixer_poweron(mixer_ctx);
break;
case DRM_MODE_DPMS_STANDBY:
case DRM_MODE_DPMS_SUSPEND:
case DRM_MODE_DPMS_OFF:
mixer_poweroff(mixer_ctx);
break;
default:
DRM_DEBUG_KMS("unknown dpms mode: %d\n", mode);
break;
}
}
static void mixer_win_mode_set(void *ctx, static void mixer_win_mode_set(void *ctx,
struct exynos_drm_overlay *overlay) struct exynos_drm_overlay *overlay)
{ {
...@@ -643,7 +773,7 @@ static void mixer_win_mode_set(void *ctx, ...@@ -643,7 +773,7 @@ static void mixer_win_mode_set(void *ctx,
win = MIXER_DEFAULT_WIN; win = MIXER_DEFAULT_WIN;
if (win < 0 || win > MIXER_WIN_NR) { if (win < 0 || win > MIXER_WIN_NR) {
DRM_ERROR("overlay plane[%d] is wrong\n", win); DRM_ERROR("mixer window[%d] is wrong\n", win);
return; return;
} }
...@@ -672,44 +802,26 @@ static void mixer_win_mode_set(void *ctx, ...@@ -672,44 +802,26 @@ static void mixer_win_mode_set(void *ctx,
win_data->scan_flags = overlay->scan_flag; win_data->scan_flags = overlay->scan_flag;
} }
static void mixer_win_commit(void *ctx, int zpos) static void mixer_win_commit(void *ctx, int win)
{ {
struct mixer_context *mixer_ctx = ctx; struct mixer_context *mixer_ctx = ctx;
int win = zpos;
DRM_DEBUG_KMS("[%d] %s, win: %d\n", __LINE__, __func__, win); DRM_DEBUG_KMS("[%d] %s, win: %d\n", __LINE__, __func__, win);
if (win == DEFAULT_ZPOS)
win = MIXER_DEFAULT_WIN;
if (win < 0 || win > MIXER_WIN_NR) {
DRM_ERROR("overlay plane[%d] is wrong\n", win);
return;
}
if (win > 1) if (win > 1)
vp_video_buffer(mixer_ctx, win); vp_video_buffer(mixer_ctx, win);
else else
mixer_graph_buffer(mixer_ctx, win); mixer_graph_buffer(mixer_ctx, win);
} }
static void mixer_win_disable(void *ctx, int zpos) static void mixer_win_disable(void *ctx, int win)
{ {
struct mixer_context *mixer_ctx = ctx; struct mixer_context *mixer_ctx = ctx;
struct mixer_resources *res = &mixer_ctx->mixer_res; struct mixer_resources *res = &mixer_ctx->mixer_res;
unsigned long flags; unsigned long flags;
int win = zpos;
DRM_DEBUG_KMS("[%d] %s, win: %d\n", __LINE__, __func__, win); DRM_DEBUG_KMS("[%d] %s, win: %d\n", __LINE__, __func__, win);
if (win == DEFAULT_ZPOS)
win = MIXER_DEFAULT_WIN;
if (win < 0 || win > MIXER_WIN_NR) {
DRM_ERROR("overlay plane[%d] is wrong\n", win);
return;
}
spin_lock_irqsave(&res->reg_slock, flags); spin_lock_irqsave(&res->reg_slock, flags);
mixer_vsync_set_update(mixer_ctx, false); mixer_vsync_set_update(mixer_ctx, false);
...@@ -723,6 +835,7 @@ static struct exynos_mixer_ops mixer_ops = { ...@@ -723,6 +835,7 @@ static struct exynos_mixer_ops mixer_ops = {
/* manager */ /* manager */
.enable_vblank = mixer_enable_vblank, .enable_vblank = mixer_enable_vblank,
.disable_vblank = mixer_disable_vblank, .disable_vblank = mixer_disable_vblank,
.dpms = mixer_dpms,
/* overlay */ /* overlay */
.win_mode_set = mixer_win_mode_set, .win_mode_set = mixer_win_mode_set,
...@@ -811,117 +924,6 @@ static irqreturn_t mixer_irq_handler(int irq, void *arg) ...@@ -811,117 +924,6 @@ static irqreturn_t mixer_irq_handler(int irq, void *arg)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static void mixer_win_reset(struct mixer_context *ctx)
{
struct mixer_resources *res = &ctx->mixer_res;
unsigned long flags;
u32 val; /* value stored to register */
spin_lock_irqsave(&res->reg_slock, flags);
mixer_vsync_set_update(ctx, false);
mixer_reg_writemask(res, MXR_CFG, MXR_CFG_DST_HDMI, MXR_CFG_DST_MASK);
/* set output in RGB888 mode */
mixer_reg_writemask(res, MXR_CFG, MXR_CFG_OUT_RGB888, MXR_CFG_OUT_MASK);
/* 16 beat burst in DMA */
mixer_reg_writemask(res, MXR_STATUS, MXR_STATUS_16_BURST,
MXR_STATUS_BURST_MASK);
/* setting default layer priority: layer1 > layer0 > video
* because typical usage scenario would be
* layer1 - OSD
* layer0 - framebuffer
* video - video overlay
*/
val = MXR_LAYER_CFG_GRP1_VAL(3);
val |= MXR_LAYER_CFG_GRP0_VAL(2);
val |= MXR_LAYER_CFG_VP_VAL(1);
mixer_reg_write(res, MXR_LAYER_CFG, val);
/* setting background color */
mixer_reg_write(res, MXR_BG_COLOR0, 0x008080);
mixer_reg_write(res, MXR_BG_COLOR1, 0x008080);
mixer_reg_write(res, MXR_BG_COLOR2, 0x008080);
/* setting graphical layers */
val = MXR_GRP_CFG_COLOR_KEY_DISABLE; /* no blank key */
val |= MXR_GRP_CFG_WIN_BLEND_EN;
val |= MXR_GRP_CFG_ALPHA_VAL(0xff); /* non-transparent alpha */
/* the same configuration for both layers */
mixer_reg_write(res, MXR_GRAPHIC_CFG(0), val);
val |= MXR_GRP_CFG_BLEND_PRE_MUL;
val |= MXR_GRP_CFG_PIXEL_BLEND_EN;
mixer_reg_write(res, MXR_GRAPHIC_CFG(1), val);
/* configuration of Video Processor Registers */
vp_win_reset(ctx);
vp_default_filter(res);
/* disable all layers */
mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP0_ENABLE);
mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP1_ENABLE);
mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_VP_ENABLE);
mixer_vsync_set_update(ctx, true);
spin_unlock_irqrestore(&res->reg_slock, flags);
}
static void mixer_resource_poweron(struct mixer_context *ctx)
{
struct mixer_resources *res = &ctx->mixer_res;
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
clk_enable(res->mixer);
clk_enable(res->vp);
clk_enable(res->sclk_mixer);
mixer_win_reset(ctx);
}
static void mixer_resource_poweroff(struct mixer_context *ctx)
{
struct mixer_resources *res = &ctx->mixer_res;
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
clk_disable(res->mixer);
clk_disable(res->vp);
clk_disable(res->sclk_mixer);
}
static int mixer_runtime_resume(struct device *dev)
{
struct exynos_drm_hdmi_context *ctx = get_mixer_context(dev);
DRM_DEBUG_KMS("resume - start\n");
mixer_resource_poweron(ctx->ctx);
return 0;
}
static int mixer_runtime_suspend(struct device *dev)
{
struct exynos_drm_hdmi_context *ctx = get_mixer_context(dev);
DRM_DEBUG_KMS("suspend - start\n");
mixer_resource_poweroff(ctx->ctx);
return 0;
}
static const struct dev_pm_ops mixer_pm_ops = {
.runtime_suspend = mixer_runtime_suspend,
.runtime_resume = mixer_runtime_resume,
};
static int __devinit mixer_resources_init(struct exynos_drm_hdmi_context *ctx, static int __devinit mixer_resources_init(struct exynos_drm_hdmi_context *ctx,
struct platform_device *pdev) struct platform_device *pdev)
{ {
...@@ -931,7 +933,6 @@ static int __devinit mixer_resources_init(struct exynos_drm_hdmi_context *ctx, ...@@ -931,7 +933,6 @@ static int __devinit mixer_resources_init(struct exynos_drm_hdmi_context *ctx,
struct resource *res; struct resource *res;
int ret; int ret;
mixer_res->dev = dev;
spin_lock_init(&mixer_res->reg_slock); spin_lock_init(&mixer_res->reg_slock);
mixer_res->mixer = clk_get(dev, "mixer"); mixer_res->mixer = clk_get(dev, "mixer");
...@@ -1027,7 +1028,6 @@ static int __devinit mixer_resources_init(struct exynos_drm_hdmi_context *ctx, ...@@ -1027,7 +1028,6 @@ static int __devinit mixer_resources_init(struct exynos_drm_hdmi_context *ctx,
clk_put(mixer_res->vp); clk_put(mixer_res->vp);
if (!IS_ERR_OR_NULL(mixer_res->mixer)) if (!IS_ERR_OR_NULL(mixer_res->mixer))
clk_put(mixer_res->mixer); clk_put(mixer_res->mixer);
mixer_res->dev = NULL;
return ret; return ret;
} }
...@@ -1035,7 +1035,6 @@ static void mixer_resources_cleanup(struct mixer_context *ctx) ...@@ -1035,7 +1035,6 @@ static void mixer_resources_cleanup(struct mixer_context *ctx)
{ {
struct mixer_resources *res = &ctx->mixer_res; struct mixer_resources *res = &ctx->mixer_res;
disable_irq(res->irq);
free_irq(res->irq, ctx); free_irq(res->irq, ctx);
iounmap(res->vp_regs); iounmap(res->vp_regs);
...@@ -1064,6 +1063,9 @@ static int __devinit mixer_probe(struct platform_device *pdev) ...@@ -1064,6 +1063,9 @@ static int __devinit mixer_probe(struct platform_device *pdev)
return -ENOMEM; return -ENOMEM;
} }
mutex_init(&ctx->mixer_mutex);
ctx->dev = &pdev->dev;
drm_hdmi_ctx->ctx = (void *)ctx; drm_hdmi_ctx->ctx = (void *)ctx;
platform_set_drvdata(pdev, drm_hdmi_ctx); platform_set_drvdata(pdev, drm_hdmi_ctx);
...@@ -1076,7 +1078,7 @@ static int __devinit mixer_probe(struct platform_device *pdev) ...@@ -1076,7 +1078,7 @@ static int __devinit mixer_probe(struct platform_device *pdev)
/* register specific callback point to common hdmi. */ /* register specific callback point to common hdmi. */
exynos_mixer_ops_register(&mixer_ops); exynos_mixer_ops_register(&mixer_ops);
mixer_resource_poweron(ctx); pm_runtime_enable(dev);
return 0; return 0;
...@@ -1095,7 +1097,8 @@ static int mixer_remove(struct platform_device *pdev) ...@@ -1095,7 +1097,8 @@ static int mixer_remove(struct platform_device *pdev)
dev_info(dev, "remove successful\n"); dev_info(dev, "remove successful\n");
mixer_resource_poweroff(ctx); pm_runtime_disable(&pdev->dev);
mixer_resources_cleanup(ctx); mixer_resources_cleanup(ctx);
return 0; return 0;
...@@ -1105,7 +1108,6 @@ struct platform_driver mixer_driver = { ...@@ -1105,7 +1108,6 @@ struct platform_driver mixer_driver = {
.driver = { .driver = {
.name = "s5p-mixer", .name = "s5p-mixer",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pm = &mixer_pm_ops,
}, },
.probe = mixer_probe, .probe = mixer_probe,
.remove = __devexit_p(mixer_remove), .remove = __devexit_p(mixer_remove),
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册