|
@@ -198,6 +198,7 @@ struct fimd_context {
|
|
atomic_t wait_vsync_event;
|
|
atomic_t wait_vsync_event;
|
|
atomic_t win_updated;
|
|
atomic_t win_updated;
|
|
atomic_t triggering;
|
|
atomic_t triggering;
|
|
|
|
+ u32 clkdiv;
|
|
|
|
|
|
const struct fimd_driver_data *driver_data;
|
|
const struct fimd_driver_data *driver_data;
|
|
struct drm_encoder *encoder;
|
|
struct drm_encoder *encoder;
|
|
@@ -389,15 +390,18 @@ static void fimd_clear_channels(struct exynos_drm_crtc *crtc)
|
|
pm_runtime_put(ctx->dev);
|
|
pm_runtime_put(ctx->dev);
|
|
}
|
|
}
|
|
|
|
|
|
-static u32 fimd_calc_clkdiv(struct fimd_context *ctx,
|
|
|
|
- const struct drm_display_mode *mode)
|
|
|
|
|
|
+
|
|
|
|
+static int fimd_atomic_check(struct exynos_drm_crtc *crtc,
|
|
|
|
+ struct drm_crtc_state *state)
|
|
{
|
|
{
|
|
- unsigned long ideal_clk;
|
|
|
|
|
|
+ struct drm_display_mode *mode = &state->adjusted_mode;
|
|
|
|
+ struct fimd_context *ctx = crtc->ctx;
|
|
|
|
+ unsigned long ideal_clk, lcd_rate;
|
|
u32 clkdiv;
|
|
u32 clkdiv;
|
|
|
|
|
|
if (mode->clock == 0) {
|
|
if (mode->clock == 0) {
|
|
- DRM_ERROR("Mode has zero clock value.\n");
|
|
|
|
- return 0xff;
|
|
|
|
|
|
+ DRM_INFO("Mode has zero clock value.\n");
|
|
|
|
+ return -EINVAL;
|
|
}
|
|
}
|
|
|
|
|
|
ideal_clk = mode->clock * 1000;
|
|
ideal_clk = mode->clock * 1000;
|
|
@@ -410,10 +414,23 @@ static u32 fimd_calc_clkdiv(struct fimd_context *ctx,
|
|
ideal_clk *= 2;
|
|
ideal_clk *= 2;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ lcd_rate = clk_get_rate(ctx->lcd_clk);
|
|
|
|
+ if (2 * lcd_rate < ideal_clk) {
|
|
|
|
+ DRM_INFO("sclk_fimd clock too low(%lu) for requested pixel clock(%lu)\n",
|
|
|
|
+ lcd_rate, ideal_clk);
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
/* Find the clock divider value that gets us closest to ideal_clk */
|
|
/* Find the clock divider value that gets us closest to ideal_clk */
|
|
- clkdiv = DIV_ROUND_CLOSEST(clk_get_rate(ctx->lcd_clk), ideal_clk);
|
|
|
|
|
|
+ clkdiv = DIV_ROUND_CLOSEST(lcd_rate, ideal_clk);
|
|
|
|
+ if (clkdiv >= 0x200) {
|
|
|
|
+ DRM_INFO("requested pixel clock(%lu) too low\n", ideal_clk);
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ctx->clkdiv = (clkdiv < 0x100) ? clkdiv : 0xff;
|
|
|
|
|
|
- return (clkdiv < 0x100) ? clkdiv : 0xff;
|
|
|
|
|
|
+ return 0;
|
|
}
|
|
}
|
|
|
|
|
|
static void fimd_setup_trigger(struct fimd_context *ctx)
|
|
static void fimd_setup_trigger(struct fimd_context *ctx)
|
|
@@ -442,7 +459,7 @@ static void fimd_commit(struct exynos_drm_crtc *crtc)
|
|
struct drm_display_mode *mode = &crtc->base.state->adjusted_mode;
|
|
struct drm_display_mode *mode = &crtc->base.state->adjusted_mode;
|
|
const struct fimd_driver_data *driver_data = ctx->driver_data;
|
|
const struct fimd_driver_data *driver_data = ctx->driver_data;
|
|
void *timing_base = ctx->regs + driver_data->timing_base;
|
|
void *timing_base = ctx->regs + driver_data->timing_base;
|
|
- u32 val, clkdiv;
|
|
|
|
|
|
+ u32 val;
|
|
|
|
|
|
if (ctx->suspended)
|
|
if (ctx->suspended)
|
|
return;
|
|
return;
|
|
@@ -543,9 +560,8 @@ static void fimd_commit(struct exynos_drm_crtc *crtc)
|
|
if (ctx->driver_data->has_clksel)
|
|
if (ctx->driver_data->has_clksel)
|
|
val |= VIDCON0_CLKSEL_LCD;
|
|
val |= VIDCON0_CLKSEL_LCD;
|
|
|
|
|
|
- clkdiv = fimd_calc_clkdiv(ctx, mode);
|
|
|
|
- if (clkdiv > 1)
|
|
|
|
- val |= VIDCON0_CLKVAL_F(clkdiv - 1) | VIDCON0_CLKDIR;
|
|
|
|
|
|
+ if (ctx->clkdiv > 1)
|
|
|
|
+ val |= VIDCON0_CLKVAL_F(ctx->clkdiv - 1) | VIDCON0_CLKDIR;
|
|
|
|
|
|
writel(val, ctx->regs + VIDCON0);
|
|
writel(val, ctx->regs + VIDCON0);
|
|
}
|
|
}
|
|
@@ -939,6 +955,7 @@ static const struct exynos_drm_crtc_ops fimd_crtc_ops = {
|
|
.update_plane = fimd_update_plane,
|
|
.update_plane = fimd_update_plane,
|
|
.disable_plane = fimd_disable_plane,
|
|
.disable_plane = fimd_disable_plane,
|
|
.atomic_flush = fimd_atomic_flush,
|
|
.atomic_flush = fimd_atomic_flush,
|
|
|
|
+ .atomic_check = fimd_atomic_check,
|
|
.te_handler = fimd_te_handler,
|
|
.te_handler = fimd_te_handler,
|
|
};
|
|
};
|
|
|
|
|