|
@@ -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),
|