mirror of
https://github.com/torvalds/linux.git
synced 2024-12-15 23:51:46 +00:00
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: Joonyoung Shim <jy0922.shim@samsung.com> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com> Signed-off-by: Inki Dae <inki.dae@samsung.com>
This commit is contained in:
parent
66265a2e8e
commit
cf8fc4f10e
@ -37,6 +37,8 @@ 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_ops_register(struct exynos_hdmi_ops *ops)
|
||||
@ -189,23 +191,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,
|
||||
@ -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)
|
||||
{
|
||||
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 = {
|
||||
@ -335,25 +364,6 @@ static int __devinit exynos_drm_hdmi_probe(struct platform_device *pdev)
|
||||
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)
|
||||
{
|
||||
struct drm_hdmi_context *ctx = platform_get_drvdata(pdev);
|
||||
@ -372,6 +382,5 @@ struct platform_driver exynos_drm_common_hdmi_driver = {
|
||||
.driver = {
|
||||
.name = "exynos-drm-hdmi",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &hdmi_pm_ops,
|
||||
},
|
||||
};
|
||||
|
@ -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,14 @@ 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);
|
||||
|
||||
/* overlay */
|
||||
void (*win_mode_set)(void *ctx, struct exynos_drm_overlay *overlay);
|
||||
|
@ -57,13 +57,15 @@ struct hdmi_resources {
|
||||
struct hdmi_context {
|
||||
struct device *dev;
|
||||
struct drm_device *drm_dev;
|
||||
bool hpd_handle;
|
||||
bool enabled;
|
||||
bool hpd;
|
||||
bool powered;
|
||||
bool is_v13;
|
||||
struct mutex hdmi_mutex;
|
||||
|
||||
struct resource *regs_res;
|
||||
void __iomem *regs;
|
||||
unsigned int irq;
|
||||
unsigned int external_irq;
|
||||
unsigned int internal_irq;
|
||||
|
||||
struct i2c_client *ddc_port;
|
||||
struct i2c_client *hdmiphy_port;
|
||||
@ -1192,12 +1194,8 @@ static int hdmi_conf_index(struct hdmi_context *hdata,
|
||||
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;
|
||||
|
||||
return false;
|
||||
return hdata->hpd;
|
||||
}
|
||||
|
||||
static int hdmi_get_edid(void *ctx, struct drm_connector *connector,
|
||||
@ -1287,28 +1285,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;
|
||||
@ -1476,9 +1452,6 @@ static void hdmi_conf_reset(struct hdmi_context *hdata)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
/* disable hpd handle for drm */
|
||||
hdata->hpd_handle = false;
|
||||
|
||||
if (hdata->is_v13)
|
||||
reg = HDMI_V13_CORE_RSTOUT;
|
||||
else
|
||||
@ -1489,16 +1462,10 @@ static void hdmi_conf_reset(struct hdmi_context *hdata)
|
||||
mdelay(10);
|
||||
hdmi_reg_writemask(hdata, reg, ~0, HDMI_CORE_SW_RSTOUT);
|
||||
mdelay(10);
|
||||
|
||||
/* enable hpd handle for drm */
|
||||
hdata->hpd_handle = true;
|
||||
}
|
||||
|
||||
static void hdmi_conf_init(struct hdmi_context *hdata)
|
||||
{
|
||||
/* disable hpd handle for drm */
|
||||
hdata->hpd_handle = false;
|
||||
|
||||
/* enable HPD interrupts */
|
||||
hdmi_reg_writemask(hdata, HDMI_INTC_CON, 0, HDMI_INTC_EN_GLOBAL |
|
||||
HDMI_INTC_EN_HPD_PLUG | HDMI_INTC_EN_HPD_UNPLUG);
|
||||
@ -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_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)
|
||||
@ -1888,8 +1852,11 @@ static void hdmi_conf_apply(struct hdmi_context *hdata)
|
||||
hdmiphy_conf_reset(hdata);
|
||||
hdmiphy_conf_apply(hdata);
|
||||
|
||||
mutex_lock(&hdata->hdmi_mutex);
|
||||
hdmi_conf_reset(hdata);
|
||||
hdmi_conf_init(hdata);
|
||||
mutex_unlock(&hdata->hdmi_mutex);
|
||||
|
||||
hdmi_audio_init(hdata);
|
||||
|
||||
/* setting core registers */
|
||||
@ -1969,20 +1936,86 @@ static void hdmi_commit(void *ctx)
|
||||
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
|
||||
|
||||
hdmi_conf_apply(hdata);
|
||||
|
||||
hdata->enabled = true;
|
||||
}
|
||||
|
||||
static void hdmi_disable(void *ctx)
|
||||
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;
|
||||
|
||||
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_poweroff(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)
|
||||
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);
|
||||
|
||||
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__);
|
||||
|
||||
if (hdata->enabled) {
|
||||
hdmi_audio_control(hdata, false);
|
||||
hdmiphy_conf_reset(hdata);
|
||||
hdmi_conf_reset(hdata);
|
||||
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 = {
|
||||
.is_connected = hdmi_is_connected,
|
||||
.get_edid = hdmi_get_edid,
|
||||
.check_timing = hdmi_check_timing,
|
||||
.power_on = hdmi_display_power_on,
|
||||
|
||||
/* manager */
|
||||
.mode_fixup = hdmi_mode_fixup,
|
||||
.mode_set = hdmi_mode_set,
|
||||
.get_max_resol = hdmi_get_max_resol,
|
||||
.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 hdmi_context *hdata = ctx->ctx;
|
||||
@ -2010,19 +2061,28 @@ static irqreturn_t hdmi_irq_thread(int irq, void *arg)
|
||||
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);
|
||||
DRM_DEBUG_KMS("unplugged\n");
|
||||
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);
|
||||
DRM_DEBUG_KMS("plugged\n");
|
||||
hdmi_reg_writemask(hdata, HDMI_INTC_FLAG, ~0,
|
||||
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);
|
||||
|
||||
out:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
@ -2116,68 +2176,6 @@ static int hdmi_resources_cleanup(struct hdmi_context *hdata)
|
||||
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;
|
||||
|
||||
void hdmi_attach_ddc_client(struct i2c_client *ddc)
|
||||
@ -2222,6 +2220,8 @@ static int __devinit hdmi_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
mutex_init(&hdata->hdmi_mutex);
|
||||
|
||||
drm_hdmi_ctx->ctx = (void *)hdata;
|
||||
hdata->parent_ctx = (void *)drm_hdmi_ctx;
|
||||
|
||||
@ -2278,28 +2278,49 @@ static int __devinit hdmi_probe(struct platform_device *pdev)
|
||||
|
||||
hdata->hdmiphy_port = hdmi_hdmiphy;
|
||||
|
||||
hdata->irq = platform_get_irq_byname(pdev, "internal_irq");
|
||||
if (hdata->irq < 0) {
|
||||
hdata->external_irq = platform_get_irq_byname(pdev, "external_irq");
|
||||
if (hdata->external_irq < 0) {
|
||||
DRM_ERROR("failed to get platform irq\n");
|
||||
ret = hdata->irq;
|
||||
ret = hdata->external_irq;
|
||||
goto err_hdmiphy;
|
||||
}
|
||||
|
||||
/* register hpd interrupt */
|
||||
ret = request_threaded_irq(hdata->irq, NULL, hdmi_irq_thread,
|
||||
IRQF_ONESHOT, "drm_hdmi", drm_hdmi_ctx);
|
||||
if (ret) {
|
||||
DRM_ERROR("request interrupt failed.\n");
|
||||
hdata->internal_irq = platform_get_irq_byname(pdev, "internal_irq");
|
||||
if (hdata->internal_irq < 0) {
|
||||
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) {
|
||||
DRM_ERROR("failed to register hdmi internal interrupt\n");
|
||||
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. */
|
||||
exynos_hdmi_ops_register(&hdmi_ops);
|
||||
|
||||
hdmi_resource_poweron(hdata);
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
free_irq(hdata->external_irq, drm_hdmi_ctx);
|
||||
err_hdmiphy:
|
||||
i2c_del_driver(&hdmiphy_driver);
|
||||
err_ddc:
|
||||
@ -2319,15 +2340,15 @@ err_data:
|
||||
|
||||
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 hdmi_context *hdata = ctx->ctx;
|
||||
|
||||
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
|
||||
|
||||
hdmi_resource_poweroff(hdata);
|
||||
pm_runtime_disable(dev);
|
||||
|
||||
disable_irq(hdata->irq);
|
||||
free_irq(hdata->irq, hdata);
|
||||
free_irq(hdata->internal_irq, hdata);
|
||||
|
||||
hdmi_resources_cleanup(hdata);
|
||||
|
||||
@ -2352,6 +2373,5 @@ struct platform_driver hdmi_driver = {
|
||||
.driver = {
|
||||
.name = "exynos4-hdmi",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &hdmi_pm_ops,
|
||||
},
|
||||
};
|
||||
|
@ -37,9 +37,6 @@
|
||||
#include "exynos_drm_drv.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))
|
||||
|
||||
struct hdmi_win_data {
|
||||
@ -63,7 +60,6 @@ struct hdmi_win_data {
|
||||
};
|
||||
|
||||
struct mixer_resources {
|
||||
struct device *dev;
|
||||
int irq;
|
||||
void __iomem *mixer_regs;
|
||||
void __iomem *vp_regs;
|
||||
@ -76,10 +72,13 @@ struct mixer_resources {
|
||||
};
|
||||
|
||||
struct mixer_context {
|
||||
unsigned int irq;
|
||||
struct device *dev;
|
||||
int pipe;
|
||||
bool interlace;
|
||||
bool powered;
|
||||
u32 int_en;
|
||||
|
||||
struct mutex mixer_mutex;
|
||||
struct mixer_resources mixer_res;
|
||||
struct hdmi_win_data win_data[MIXER_WIN_NR];
|
||||
};
|
||||
@ -591,6 +590,116 @@ static void vp_win_reset(struct mixer_context *ctx)
|
||||
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)
|
||||
{
|
||||
struct mixer_context *mixer_ctx = ctx;
|
||||
@ -618,6 +727,27 @@ static void mixer_disable_vblank(void *ctx)
|
||||
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,
|
||||
struct exynos_drm_overlay *overlay)
|
||||
{
|
||||
@ -643,7 +773,7 @@ static void mixer_win_mode_set(void *ctx,
|
||||
win = MIXER_DEFAULT_WIN;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -672,44 +802,26 @@ static void mixer_win_mode_set(void *ctx,
|
||||
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;
|
||||
int win = zpos;
|
||||
|
||||
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)
|
||||
vp_video_buffer(mixer_ctx, win);
|
||||
else
|
||||
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_resources *res = &mixer_ctx->mixer_res;
|
||||
unsigned long flags;
|
||||
int win = zpos;
|
||||
|
||||
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);
|
||||
mixer_vsync_set_update(mixer_ctx, false);
|
||||
|
||||
@ -723,6 +835,7 @@ static struct exynos_mixer_ops mixer_ops = {
|
||||
/* manager */
|
||||
.enable_vblank = mixer_enable_vblank,
|
||||
.disable_vblank = mixer_disable_vblank,
|
||||
.dpms = mixer_dpms,
|
||||
|
||||
/* overlay */
|
||||
.win_mode_set = mixer_win_mode_set,
|
||||
@ -811,117 +924,6 @@ out:
|
||||
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,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
@ -931,7 +933,6 @@ static int __devinit mixer_resources_init(struct exynos_drm_hdmi_context *ctx,
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
mixer_res->dev = dev;
|
||||
spin_lock_init(&mixer_res->reg_slock);
|
||||
|
||||
mixer_res->mixer = clk_get(dev, "mixer");
|
||||
@ -1027,7 +1028,6 @@ fail:
|
||||
clk_put(mixer_res->vp);
|
||||
if (!IS_ERR_OR_NULL(mixer_res->mixer))
|
||||
clk_put(mixer_res->mixer);
|
||||
mixer_res->dev = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1035,7 +1035,6 @@ static void mixer_resources_cleanup(struct mixer_context *ctx)
|
||||
{
|
||||
struct mixer_resources *res = &ctx->mixer_res;
|
||||
|
||||
disable_irq(res->irq);
|
||||
free_irq(res->irq, ctx);
|
||||
|
||||
iounmap(res->vp_regs);
|
||||
@ -1064,6 +1063,9 @@ static int __devinit mixer_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
mutex_init(&ctx->mixer_mutex);
|
||||
|
||||
ctx->dev = &pdev->dev;
|
||||
drm_hdmi_ctx->ctx = (void *)ctx;
|
||||
|
||||
platform_set_drvdata(pdev, drm_hdmi_ctx);
|
||||
@ -1076,7 +1078,7 @@ 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);
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
@ -1095,7 +1097,8 @@ static int mixer_remove(struct platform_device *pdev)
|
||||
|
||||
dev_info(dev, "remove successful\n");
|
||||
|
||||
mixer_resource_poweroff(ctx);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
mixer_resources_cleanup(ctx);
|
||||
|
||||
return 0;
|
||||
@ -1105,7 +1108,6 @@ struct platform_driver mixer_driver = {
|
||||
.driver = {
|
||||
.name = "s5p-mixer",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &mixer_pm_ops,
|
||||
},
|
||||
.probe = mixer_probe,
|
||||
.remove = __devexit_p(mixer_remove),
|
||||
|
Loading…
Reference in New Issue
Block a user