diff options
author | Sean Paul <seanpaul@chromium.org> | 2014-01-30 16:19:20 -0500 |
---|---|---|
committer | Inki Dae <daeinki@gmail.com> | 2014-03-23 11:36:34 -0400 |
commit | a968e72771ea19aaedeeaa4ac9d8339186c302e3 (patch) | |
tree | c5206e556af52a5996a3ed10542463e05d5e0eab | |
parent | 4b4052699ae4e913c4d2b965061f10eec122e558 (diff) |
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 <seanpaul@chromium.org>
Signed-off-by: Inki Dae <inki.dae@samsung.com>
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_fimd.c | 143 |
1 files changed, 74 insertions, 69 deletions
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 { | |||
112 | struct clk *bus_clk; | 112 | struct clk *bus_clk; |
113 | struct clk *lcd_clk; | 113 | struct clk *lcd_clk; |
114 | void __iomem *regs; | 114 | void __iomem *regs; |
115 | struct drm_display_mode mode; | ||
115 | struct fimd_win_data win_data[WINDOWS_NR]; | 116 | struct fimd_win_data win_data[WINDOWS_NR]; |
116 | unsigned int clkdiv; | ||
117 | unsigned int default_win; | 117 | unsigned int default_win; |
118 | unsigned long irq_flags; | 118 | unsigned long irq_flags; |
119 | u32 vidcon0; | 119 | u32 vidcon0; |
@@ -221,38 +221,82 @@ static void fimd_mgr_remove(struct exynos_drm_manager *mgr) | |||
221 | drm_iommu_detach_device(ctx->drm_dev, ctx->dev); | 221 | drm_iommu_detach_device(ctx->drm_dev, ctx->dev); |
222 | } | 222 | } |
223 | 223 | ||
224 | static u32 fimd_calc_clkdiv(struct fimd_context *ctx, | ||
225 | const struct drm_display_mode *mode) | ||
226 | { | ||
227 | unsigned long ideal_clk = mode->htotal * mode->vtotal * mode->vrefresh; | ||
228 | u32 clkdiv; | ||
229 | |||
230 | /* Find the clock divider value that gets us closest to ideal_clk */ | ||
231 | clkdiv = DIV_ROUND_UP(clk_get_rate(ctx->lcd_clk), ideal_clk); | ||
232 | |||
233 | return (clkdiv < 0x100) ? clkdiv : 0xff; | ||
234 | } | ||
235 | |||
236 | static bool fimd_mode_fixup(struct exynos_drm_manager *mgr, | ||
237 | const struct drm_display_mode *mode, | ||
238 | struct drm_display_mode *adjusted_mode) | ||
239 | { | ||
240 | if (adjusted_mode->vrefresh == 0) | ||
241 | adjusted_mode->vrefresh = FIMD_DEFAULT_FRAMERATE; | ||
242 | |||
243 | return true; | ||
244 | } | ||
245 | |||
246 | static void fimd_mode_set(struct exynos_drm_manager *mgr, | ||
247 | const struct drm_display_mode *in_mode) | ||
248 | { | ||
249 | struct fimd_context *ctx = mgr->ctx; | ||
250 | |||
251 | drm_mode_copy(&ctx->mode, in_mode); | ||
252 | } | ||
253 | |||
224 | static void fimd_commit(struct exynos_drm_manager *mgr) | 254 | static void fimd_commit(struct exynos_drm_manager *mgr) |
225 | { | 255 | { |
226 | struct fimd_context *ctx = mgr->ctx; | 256 | struct fimd_context *ctx = mgr->ctx; |
227 | struct exynos_drm_panel_info *panel = &ctx->panel; | 257 | struct drm_display_mode *mode = &ctx->mode; |
228 | struct videomode *vm = &panel->vm; | ||
229 | struct fimd_driver_data *driver_data; | 258 | struct fimd_driver_data *driver_data; |
230 | u32 val; | 259 | u32 val, clkdiv; |
260 | int hblank, vblank, vsync_len, vbpd, vfpd, hsync_len, hbpd, hfpd; | ||
231 | 261 | ||
232 | driver_data = ctx->driver_data; | 262 | driver_data = ctx->driver_data; |
233 | if (ctx->suspended) | 263 | if (ctx->suspended) |
234 | return; | 264 | return; |
235 | 265 | ||
266 | /* nothing to do if we haven't set the mode yet */ | ||
267 | if (mode->htotal == 0 || mode->vtotal == 0) | ||
268 | return; | ||
269 | |||
236 | /* setup polarity values from machine code. */ | 270 | /* setup polarity values from machine code. */ |
237 | writel(ctx->vidcon1, ctx->regs + driver_data->timing_base + VIDCON1); | 271 | writel(ctx->vidcon1, ctx->regs + driver_data->timing_base + VIDCON1); |
238 | 272 | ||
239 | /* setup vertical timing values. */ | 273 | /* setup vertical timing values. */ |
240 | val = VIDTCON0_VBPD(vm->vback_porch - 1) | | 274 | vblank = mode->crtc_vblank_end - mode->crtc_vblank_start; |
241 | VIDTCON0_VFPD(vm->vfront_porch - 1) | | 275 | vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start; |
242 | VIDTCON0_VSPW(vm->vsync_len - 1); | 276 | vbpd = (vblank - vsync_len) / 2; |
277 | vfpd = vblank - vsync_len - vbpd; | ||
278 | |||
279 | val = VIDTCON0_VBPD(vbpd - 1) | | ||
280 | VIDTCON0_VFPD(vfpd - 1) | | ||
281 | VIDTCON0_VSPW(vsync_len - 1); | ||
243 | writel(val, ctx->regs + driver_data->timing_base + VIDTCON0); | 282 | writel(val, ctx->regs + driver_data->timing_base + VIDTCON0); |
244 | 283 | ||
245 | /* setup horizontal timing values. */ | 284 | /* setup horizontal timing values. */ |
246 | val = VIDTCON1_HBPD(vm->hback_porch - 1) | | 285 | hblank = mode->crtc_hblank_end - mode->crtc_hblank_start; |
247 | VIDTCON1_HFPD(vm->hfront_porch - 1) | | 286 | hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start; |
248 | VIDTCON1_HSPW(vm->hsync_len - 1); | 287 | hbpd = (hblank - hsync_len) / 2; |
288 | hfpd = hblank - hsync_len - hbpd; | ||
289 | |||
290 | val = VIDTCON1_HBPD(hbpd - 1) | | ||
291 | VIDTCON1_HFPD(hfpd - 1) | | ||
292 | VIDTCON1_HSPW(hsync_len - 1); | ||
249 | writel(val, ctx->regs + driver_data->timing_base + VIDTCON1); | 293 | writel(val, ctx->regs + driver_data->timing_base + VIDTCON1); |
250 | 294 | ||
251 | /* setup horizontal and vertical display size. */ | 295 | /* setup horizontal and vertical display size. */ |
252 | val = VIDTCON2_LINEVAL(vm->vactive - 1) | | 296 | val = VIDTCON2_LINEVAL(mode->vdisplay - 1) | |
253 | VIDTCON2_HOZVAL(vm->hactive - 1) | | 297 | VIDTCON2_HOZVAL(mode->hdisplay - 1) | |
254 | VIDTCON2_LINEVAL_E(vm->vactive - 1) | | 298 | VIDTCON2_LINEVAL_E(mode->vdisplay - 1) | |
255 | VIDTCON2_HOZVAL_E(vm->hactive - 1); | 299 | VIDTCON2_HOZVAL_E(mode->hdisplay - 1); |
256 | writel(val, ctx->regs + driver_data->timing_base + VIDTCON2); | 300 | writel(val, ctx->regs + driver_data->timing_base + VIDTCON2); |
257 | 301 | ||
258 | /* setup clock source, clock divider, enable dma. */ | 302 | /* setup clock source, clock divider, enable dma. */ |
@@ -264,8 +308,9 @@ static void fimd_commit(struct exynos_drm_manager *mgr) | |||
264 | val |= VIDCON0_CLKSEL_LCD; | 308 | val |= VIDCON0_CLKSEL_LCD; |
265 | } | 309 | } |
266 | 310 | ||
267 | if (ctx->clkdiv > 1) | 311 | clkdiv = fimd_calc_clkdiv(ctx, mode); |
268 | val |= VIDCON0_CLKVAL_F(ctx->clkdiv - 1) | VIDCON0_CLKDIR; | 312 | if (clkdiv > 1) |
313 | val |= VIDCON0_CLKVAL_F(clkdiv - 1) | VIDCON0_CLKDIR; | ||
269 | else | 314 | else |
270 | val &= ~VIDCON0_CLKDIR; /* 1:1 clock */ | 315 | val &= ~VIDCON0_CLKDIR; /* 1:1 clock */ |
271 | 316 | ||
@@ -683,6 +728,8 @@ static struct exynos_drm_manager_ops fimd_manager_ops = { | |||
683 | .initialize = fimd_mgr_initialize, | 728 | .initialize = fimd_mgr_initialize, |
684 | .remove = fimd_mgr_remove, | 729 | .remove = fimd_mgr_remove, |
685 | .dpms = fimd_dpms, | 730 | .dpms = fimd_dpms, |
731 | .mode_fixup = fimd_mode_fixup, | ||
732 | .mode_set = fimd_mode_set, | ||
686 | .commit = fimd_commit, | 733 | .commit = fimd_commit, |
687 | .enable_vblank = fimd_enable_vblank, | 734 | .enable_vblank = fimd_enable_vblank, |
688 | .disable_vblank = fimd_disable_vblank, | 735 | .disable_vblank = fimd_disable_vblank, |
@@ -724,56 +771,6 @@ out: | |||
724 | return IRQ_HANDLED; | 771 | return IRQ_HANDLED; |
725 | } | 772 | } |
726 | 773 | ||
727 | static int fimd_configure_clocks(struct fimd_context *ctx, struct device *dev) | ||
728 | { | ||
729 | struct videomode *vm = &ctx->panel.vm; | ||
730 | unsigned long clk; | ||
731 | |||
732 | ctx->bus_clk = devm_clk_get(dev, "fimd"); | ||
733 | if (IS_ERR(ctx->bus_clk)) { | ||
734 | dev_err(dev, "failed to get bus clock\n"); | ||
735 | return PTR_ERR(ctx->bus_clk); | ||
736 | } | ||
737 | |||
738 | ctx->lcd_clk = devm_clk_get(dev, "sclk_fimd"); | ||
739 | if (IS_ERR(ctx->lcd_clk)) { | ||
740 | dev_err(dev, "failed to get lcd clock\n"); | ||
741 | return PTR_ERR(ctx->lcd_clk); | ||
742 | } | ||
743 | |||
744 | clk = clk_get_rate(ctx->lcd_clk); | ||
745 | if (clk == 0) { | ||
746 | dev_err(dev, "error getting sclk_fimd clock rate\n"); | ||
747 | return -EINVAL; | ||
748 | } | ||
749 | |||
750 | if (vm->pixelclock == 0) { | ||
751 | unsigned long c; | ||
752 | c = vm->hactive + vm->hback_porch + vm->hfront_porch + | ||
753 | vm->hsync_len; | ||
754 | c *= vm->vactive + vm->vback_porch + vm->vfront_porch + | ||
755 | vm->vsync_len; | ||
756 | vm->pixelclock = c * FIMD_DEFAULT_FRAMERATE; | ||
757 | if (vm->pixelclock == 0) { | ||
758 | dev_err(dev, "incorrect display timings\n"); | ||
759 | return -EINVAL; | ||
760 | } | ||
761 | dev_warn(dev, "pixel clock recalculated to %luHz (%dHz frame rate)\n", | ||
762 | vm->pixelclock, FIMD_DEFAULT_FRAMERATE); | ||
763 | } | ||
764 | ctx->clkdiv = DIV_ROUND_UP(clk, vm->pixelclock); | ||
765 | if (ctx->clkdiv > 256) { | ||
766 | dev_warn(dev, "calculated pixel clock divider too high (%u), lowered to 256\n", | ||
767 | ctx->clkdiv); | ||
768 | ctx->clkdiv = 256; | ||
769 | } | ||
770 | vm->pixelclock = clk / ctx->clkdiv; | ||
771 | DRM_DEBUG_KMS("pixel clock = %lu, clkdiv = %d\n", vm->pixelclock, | ||
772 | ctx->clkdiv); | ||
773 | |||
774 | return 0; | ||
775 | } | ||
776 | |||
777 | static void fimd_clear_win(struct fimd_context *ctx, int win) | 774 | static void fimd_clear_win(struct fimd_context *ctx, int win) |
778 | { | 775 | { |
779 | writel(0, ctx->regs + WINCON(win)); | 776 | writel(0, ctx->regs + WINCON(win)); |
@@ -926,9 +923,17 @@ static int fimd_probe(struct platform_device *pdev) | |||
926 | if (ret) | 923 | if (ret) |
927 | return ret; | 924 | return ret; |
928 | 925 | ||
929 | ret = fimd_configure_clocks(ctx, dev); | 926 | ctx->bus_clk = devm_clk_get(dev, "fimd"); |
930 | if (ret) | 927 | if (IS_ERR(ctx->bus_clk)) { |
931 | return ret; | 928 | dev_err(dev, "failed to get bus clock\n"); |
929 | return PTR_ERR(ctx->bus_clk); | ||
930 | } | ||
931 | |||
932 | ctx->lcd_clk = devm_clk_get(dev, "sclk_fimd"); | ||
933 | if (IS_ERR(ctx->lcd_clk)) { | ||
934 | dev_err(dev, "failed to get lcd clock\n"); | ||
935 | return PTR_ERR(ctx->lcd_clk); | ||
936 | } | ||
932 | 937 | ||
933 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 938 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
934 | 939 | ||