Skip to content

Commit

Permalink
drm/exynos: clear windows in mixer dpms off
Browse files Browse the repository at this point in the history
When mixer is turned off, we disable the clocks which will stop
the dma. Now if we remove the current framebuffer, we cannot
disable the overlay but the current framebuffer will still be freed.
When mixer resumes, the dma will continue from where it left off
and will throw a PAGE FAULT since the memory was freed.

This patch fixes the above problem by disabling the mixer windows
before disabling the mixer clocks. It also keeps track of which
windows were currently active by setting the 'resume' flag. When
mixer resumes, the window with a resume flag set is enabled again.

Now if a current fb is removed when mixer is off, mixer_win_disable
will set the 'resume' flag of that window to zero and return.
So when mixer resumes, that window will not be resumed.

Signed-off-by: Prathyush K <prathyush.k@samsung.com>
Signed-off-by: Inki Dae <inki.dae@samsung.com>
  • Loading branch information
Prathyush K authored and Inki Dae committed Dec 13, 2012
1 parent 01ce113 commit db43fd1
Showing 1 changed file with 118 additions and 75 deletions.
193 changes: 118 additions & 75 deletions drivers/gpu/drm/exynos/exynos_mixer.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ struct hdmi_win_data {
unsigned int mode_width;
unsigned int mode_height;
unsigned int scan_flags;
bool enabled;
bool resume;
};

struct mixer_resources {
Expand Down Expand Up @@ -688,60 +690,6 @@ static int mixer_iommu_on(void *ctx, bool enable)
return 0;
}

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);
if (ctx->vp_enabled) {
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);
if (ctx->vp_enabled) {
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;
Expand Down Expand Up @@ -769,27 +717,6 @@ 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)
{
Expand Down Expand Up @@ -856,6 +783,8 @@ static void mixer_win_commit(void *ctx, int win)
vp_video_buffer(mixer_ctx, win);
else
mixer_graph_buffer(mixer_ctx, win);

mixer_ctx->win_data[win].enabled = true;
}

static void mixer_win_disable(void *ctx, int win)
Expand All @@ -866,13 +795,23 @@ static void mixer_win_disable(void *ctx, int win)

DRM_DEBUG_KMS("[%d] %s, win: %d\n", __LINE__, __func__, win);

mutex_lock(&mixer_ctx->mixer_mutex);
if (!mixer_ctx->powered) {
mutex_unlock(&mixer_ctx->mixer_mutex);
mixer_ctx->win_data[win].resume = false;
return;
}
mutex_unlock(&mixer_ctx->mixer_mutex);

spin_lock_irqsave(&res->reg_slock, flags);
mixer_vsync_set_update(mixer_ctx, false);

mixer_cfg_layer(mixer_ctx, win, false);

mixer_vsync_set_update(mixer_ctx, true);
spin_unlock_irqrestore(&res->reg_slock, flags);

mixer_ctx->win_data[win].enabled = false;
}

static void mixer_wait_for_vblank(void *ctx)
Expand All @@ -898,6 +837,110 @@ static void mixer_wait_for_vblank(void *ctx)
DRM_DEBUG_KMS("vblank wait timed out.\n");
}

static void mixer_window_suspend(struct mixer_context *ctx)
{
struct hdmi_win_data *win_data;
int i;

for (i = 0; i < MIXER_WIN_NR; i++) {
win_data = &ctx->win_data[i];
win_data->resume = win_data->enabled;
mixer_win_disable(ctx, i);
}
mixer_wait_for_vblank(ctx);
}

static void mixer_window_resume(struct mixer_context *ctx)
{
struct hdmi_win_data *win_data;
int i;

for (i = 0; i < MIXER_WIN_NR; i++) {
win_data = &ctx->win_data[i];
win_data->enabled = win_data->resume;
win_data->resume = false;
}
}

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);
if (ctx->vp_enabled) {
clk_enable(res->vp);
clk_enable(res->sclk_mixer);
}

mixer_reg_write(res, MXR_INT_EN, ctx->int_en);
mixer_win_reset(ctx);

mixer_window_resume(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);

mixer_window_suspend(ctx);

ctx->int_en = mixer_reg_read(res, MXR_INT_EN);

clk_disable(res->mixer);
if (ctx->vp_enabled) {
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 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 struct exynos_mixer_ops mixer_ops = {
/* manager */
.iommu_on = mixer_iommu_on,
Expand Down

0 comments on commit db43fd1

Please sign in to comment.