From: Sean Paul Date: Thu, 30 Jan 2014 21:19:20 +0000 (-0500) Subject: drm/exynos: Use mode_set to configure fimd X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=a968e72771ea19aaedeeaa4ac9d8339186c302e3;p=linux-beck.git drm/exynos: Use mode_set to configure fimd This patch uses the mode passed into mode_set to configure fimd instead of directly using the panel from context. This will allow us to move the exynos_drm_display implementation out of fimd, where it doesn't belong. Signed-off-by: Sean Paul Signed-off-by: Inki Dae --- diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index dc8c5e4aa235..53d92fec665b 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -112,8 +112,8 @@ struct fimd_context { struct clk *bus_clk; struct clk *lcd_clk; void __iomem *regs; + struct drm_display_mode mode; struct fimd_win_data win_data[WINDOWS_NR]; - unsigned int clkdiv; unsigned int default_win; unsigned long irq_flags; u32 vidcon0; @@ -221,38 +221,82 @@ static void fimd_mgr_remove(struct exynos_drm_manager *mgr) drm_iommu_detach_device(ctx->drm_dev, ctx->dev); } +static u32 fimd_calc_clkdiv(struct fimd_context *ctx, + const struct drm_display_mode *mode) +{ + unsigned long ideal_clk = mode->htotal * mode->vtotal * mode->vrefresh; + u32 clkdiv; + + /* Find the clock divider value that gets us closest to ideal_clk */ + clkdiv = DIV_ROUND_UP(clk_get_rate(ctx->lcd_clk), ideal_clk); + + return (clkdiv < 0x100) ? clkdiv : 0xff; +} + +static bool fimd_mode_fixup(struct exynos_drm_manager *mgr, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + if (adjusted_mode->vrefresh == 0) + adjusted_mode->vrefresh = FIMD_DEFAULT_FRAMERATE; + + return true; +} + +static void fimd_mode_set(struct exynos_drm_manager *mgr, + const struct drm_display_mode *in_mode) +{ + struct fimd_context *ctx = mgr->ctx; + + drm_mode_copy(&ctx->mode, in_mode); +} + static void fimd_commit(struct exynos_drm_manager *mgr) { struct fimd_context *ctx = mgr->ctx; - struct exynos_drm_panel_info *panel = &ctx->panel; - struct videomode *vm = &panel->vm; + struct drm_display_mode *mode = &ctx->mode; struct fimd_driver_data *driver_data; - u32 val; + u32 val, clkdiv; + int hblank, vblank, vsync_len, vbpd, vfpd, hsync_len, hbpd, hfpd; driver_data = ctx->driver_data; if (ctx->suspended) return; + /* nothing to do if we haven't set the mode yet */ + if (mode->htotal == 0 || mode->vtotal == 0) + return; + /* setup polarity values from machine code. */ writel(ctx->vidcon1, ctx->regs + driver_data->timing_base + VIDCON1); /* setup vertical timing values. */ - val = VIDTCON0_VBPD(vm->vback_porch - 1) | - VIDTCON0_VFPD(vm->vfront_porch - 1) | - VIDTCON0_VSPW(vm->vsync_len - 1); + vblank = mode->crtc_vblank_end - mode->crtc_vblank_start; + vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start; + vbpd = (vblank - vsync_len) / 2; + vfpd = vblank - vsync_len - vbpd; + + val = VIDTCON0_VBPD(vbpd - 1) | + VIDTCON0_VFPD(vfpd - 1) | + VIDTCON0_VSPW(vsync_len - 1); writel(val, ctx->regs + driver_data->timing_base + VIDTCON0); /* setup horizontal timing values. */ - val = VIDTCON1_HBPD(vm->hback_porch - 1) | - VIDTCON1_HFPD(vm->hfront_porch - 1) | - VIDTCON1_HSPW(vm->hsync_len - 1); + hblank = mode->crtc_hblank_end - mode->crtc_hblank_start; + hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start; + hbpd = (hblank - hsync_len) / 2; + hfpd = hblank - hsync_len - hbpd; + + val = VIDTCON1_HBPD(hbpd - 1) | + VIDTCON1_HFPD(hfpd - 1) | + VIDTCON1_HSPW(hsync_len - 1); writel(val, ctx->regs + driver_data->timing_base + VIDTCON1); /* setup horizontal and vertical display size. */ - val = VIDTCON2_LINEVAL(vm->vactive - 1) | - VIDTCON2_HOZVAL(vm->hactive - 1) | - VIDTCON2_LINEVAL_E(vm->vactive - 1) | - VIDTCON2_HOZVAL_E(vm->hactive - 1); + val = VIDTCON2_LINEVAL(mode->vdisplay - 1) | + VIDTCON2_HOZVAL(mode->hdisplay - 1) | + VIDTCON2_LINEVAL_E(mode->vdisplay - 1) | + VIDTCON2_HOZVAL_E(mode->hdisplay - 1); writel(val, ctx->regs + driver_data->timing_base + VIDTCON2); /* setup clock source, clock divider, enable dma. */ @@ -264,8 +308,9 @@ static void fimd_commit(struct exynos_drm_manager *mgr) val |= VIDCON0_CLKSEL_LCD; } - if (ctx->clkdiv > 1) - val |= VIDCON0_CLKVAL_F(ctx->clkdiv - 1) | VIDCON0_CLKDIR; + clkdiv = fimd_calc_clkdiv(ctx, mode); + if (clkdiv > 1) + val |= VIDCON0_CLKVAL_F(clkdiv - 1) | VIDCON0_CLKDIR; else val &= ~VIDCON0_CLKDIR; /* 1:1 clock */ @@ -683,6 +728,8 @@ static struct exynos_drm_manager_ops fimd_manager_ops = { .initialize = fimd_mgr_initialize, .remove = fimd_mgr_remove, .dpms = fimd_dpms, + .mode_fixup = fimd_mode_fixup, + .mode_set = fimd_mode_set, .commit = fimd_commit, .enable_vblank = fimd_enable_vblank, .disable_vblank = fimd_disable_vblank, @@ -724,56 +771,6 @@ out: return IRQ_HANDLED; } -static int fimd_configure_clocks(struct fimd_context *ctx, struct device *dev) -{ - struct videomode *vm = &ctx->panel.vm; - unsigned long clk; - - ctx->bus_clk = devm_clk_get(dev, "fimd"); - if (IS_ERR(ctx->bus_clk)) { - dev_err(dev, "failed to get bus clock\n"); - return PTR_ERR(ctx->bus_clk); - } - - ctx->lcd_clk = devm_clk_get(dev, "sclk_fimd"); - if (IS_ERR(ctx->lcd_clk)) { - dev_err(dev, "failed to get lcd clock\n"); - return PTR_ERR(ctx->lcd_clk); - } - - clk = clk_get_rate(ctx->lcd_clk); - if (clk == 0) { - dev_err(dev, "error getting sclk_fimd clock rate\n"); - return -EINVAL; - } - - if (vm->pixelclock == 0) { - unsigned long c; - c = vm->hactive + vm->hback_porch + vm->hfront_porch + - vm->hsync_len; - c *= vm->vactive + vm->vback_porch + vm->vfront_porch + - vm->vsync_len; - vm->pixelclock = c * FIMD_DEFAULT_FRAMERATE; - if (vm->pixelclock == 0) { - dev_err(dev, "incorrect display timings\n"); - return -EINVAL; - } - dev_warn(dev, "pixel clock recalculated to %luHz (%dHz frame rate)\n", - vm->pixelclock, FIMD_DEFAULT_FRAMERATE); - } - ctx->clkdiv = DIV_ROUND_UP(clk, vm->pixelclock); - if (ctx->clkdiv > 256) { - dev_warn(dev, "calculated pixel clock divider too high (%u), lowered to 256\n", - ctx->clkdiv); - ctx->clkdiv = 256; - } - vm->pixelclock = clk / ctx->clkdiv; - DRM_DEBUG_KMS("pixel clock = %lu, clkdiv = %d\n", vm->pixelclock, - ctx->clkdiv); - - return 0; -} - static void fimd_clear_win(struct fimd_context *ctx, int win) { writel(0, ctx->regs + WINCON(win)); @@ -926,9 +923,17 @@ static int fimd_probe(struct platform_device *pdev) if (ret) return ret; - ret = fimd_configure_clocks(ctx, dev); - if (ret) - return ret; + ctx->bus_clk = devm_clk_get(dev, "fimd"); + if (IS_ERR(ctx->bus_clk)) { + dev_err(dev, "failed to get bus clock\n"); + return PTR_ERR(ctx->bus_clk); + } + + ctx->lcd_clk = devm_clk_get(dev, "sclk_fimd"); + if (IS_ERR(ctx->lcd_clk)) { + dev_err(dev, "failed to get lcd clock\n"); + return PTR_ERR(ctx->lcd_clk); + } res = platform_get_resource(pdev, IORESOURCE_MEM, 0);