diff options
Diffstat (limited to 'drivers/gpu/drm/exynos/exynos_drm_fimd.c')
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_fimd.c | 117 |
1 files changed, 92 insertions, 25 deletions
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index 58d50e368a58..a32837951dd2 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -57,6 +57,18 @@ #define get_fimd_context(dev) platform_get_drvdata(to_platform_device(dev)) +struct fimd_driver_data { + unsigned int timing_base; +}; + +struct fimd_driver_data exynos4_fimd_driver_data = { + .timing_base = 0x0, +}; + +struct fimd_driver_data exynos5_fimd_driver_data = { + .timing_base = 0x20000, +}; + struct fimd_win_data { unsigned int offset_x; unsigned int offset_y; @@ -91,6 +103,13 @@ struct fimd_context { struct exynos_drm_panel_info *panel; }; +static inline struct fimd_driver_data *drm_fimd_get_driver_data( + struct platform_device *pdev) +{ + return (struct fimd_driver_data *) + platform_get_device_id(pdev)->driver_data; +} + static bool fimd_display_is_connected(struct device *dev) { DRM_DEBUG_KMS("%s\n", __FILE__); @@ -194,32 +213,35 @@ static void fimd_commit(struct device *dev) struct fimd_context *ctx = get_fimd_context(dev); struct exynos_drm_panel_info *panel = ctx->panel; struct fb_videomode *timing = &panel->timing; + struct fimd_driver_data *driver_data; + struct platform_device *pdev = to_platform_device(dev); u32 val; + driver_data = drm_fimd_get_driver_data(pdev); if (ctx->suspended) return; DRM_DEBUG_KMS("%s\n", __FILE__); /* setup polarity values from machine code. */ - writel(ctx->vidcon1, ctx->regs + VIDCON1); + writel(ctx->vidcon1, ctx->regs + driver_data->timing_base + VIDCON1); /* setup vertical timing values. */ val = VIDTCON0_VBPD(timing->upper_margin - 1) | VIDTCON0_VFPD(timing->lower_margin - 1) | VIDTCON0_VSPW(timing->vsync_len - 1); - writel(val, ctx->regs + VIDTCON0); + writel(val, ctx->regs + driver_data->timing_base + VIDTCON0); /* setup horizontal timing values. */ val = VIDTCON1_HBPD(timing->left_margin - 1) | VIDTCON1_HFPD(timing->right_margin - 1) | VIDTCON1_HSPW(timing->hsync_len - 1); - writel(val, ctx->regs + VIDTCON1); + writel(val, ctx->regs + driver_data->timing_base + VIDTCON1); /* setup horizontal and vertical display size. */ val = VIDTCON2_LINEVAL(timing->yres - 1) | VIDTCON2_HOZVAL(timing->xres - 1); - writel(val, ctx->regs + VIDTCON2); + writel(val, ctx->regs + driver_data->timing_base + VIDTCON2); /* setup clock source, clock divider, enable dma. */ val = ctx->vidcon0; @@ -570,10 +592,22 @@ static void fimd_win_disable(struct device *dev, int zpos) win_data->enabled = false; } +static void fimd_wait_for_vblank(struct device *dev) +{ + struct fimd_context *ctx = get_fimd_context(dev); + int ret; + + ret = wait_for((__raw_readl(ctx->regs + VIDCON1) & + VIDCON1_VSTATUS_VSYNC), 50); + if (ret < 0) + DRM_DEBUG_KMS("vblank wait timed out.\n"); +} + static struct exynos_drm_overlay_ops fimd_overlay_ops = { .mode_set = fimd_win_mode_set, .commit = fimd_win_commit, .disable = fimd_win_disable, + .wait_for_vblank = fimd_wait_for_vblank, }; static struct exynos_drm_manager fimd_manager = { @@ -678,7 +712,7 @@ static int fimd_subdrv_probe(struct drm_device *drm_dev, struct device *dev) return 0; } -static void fimd_subdrv_remove(struct drm_device *drm_dev) +static void fimd_subdrv_remove(struct drm_device *drm_dev, struct device *dev) { DRM_DEBUG_KMS("%s\n", __FILE__); @@ -747,16 +781,10 @@ static void fimd_clear_win(struct fimd_context *ctx, int win) writel(val, ctx->regs + SHADOWCON); } -static int fimd_power_on(struct fimd_context *ctx, bool enable) +static int fimd_clock(struct fimd_context *ctx, bool enable) { - struct exynos_drm_subdrv *subdrv = &ctx->subdrv; - struct device *dev = subdrv->dev; - DRM_DEBUG_KMS("%s\n", __FILE__); - if (enable != false && enable != true) - return -EINVAL; - if (enable) { int ret; @@ -769,18 +797,31 @@ static int fimd_power_on(struct fimd_context *ctx, bool enable) clk_disable(ctx->bus_clk); return ret; } + } else { + clk_disable(ctx->lcd_clk); + clk_disable(ctx->bus_clk); + } + + return 0; +} + +static int fimd_activate(struct fimd_context *ctx, bool enable) +{ + if (enable) { + int ret; + struct device *dev = ctx->subdrv.dev; + + ret = fimd_clock(ctx, true); + if (ret < 0) + return ret; ctx->suspended = false; /* if vblank was enabled status, enable it again. */ if (test_and_clear_bit(0, &ctx->irq_flags)) fimd_enable_vblank(dev); - - fimd_apply(dev); } else { - clk_disable(ctx->lcd_clk); - clk_disable(ctx->bus_clk); - + fimd_clock(ctx, false); ctx->suspended = true; } @@ -930,15 +971,15 @@ static int fimd_suspend(struct device *dev) { struct fimd_context *ctx = get_fimd_context(dev); - if (pm_runtime_suspended(dev)) - return 0; - /* * do not use pm_runtime_suspend(). if pm_runtime_suspend() is * called here, an error would be returned by that interface * because the usage_count of pm runtime is more than 1. */ - return fimd_power_on(ctx, false); + if (!pm_runtime_suspended(dev)) + return fimd_activate(ctx, false); + + return 0; } static int fimd_resume(struct device *dev) @@ -950,8 +991,21 @@ static int fimd_resume(struct device *dev) * of pm runtime would still be 1 so in this case, fimd driver * should be on directly not drawing on pm runtime interface. */ - if (!pm_runtime_suspended(dev)) - return fimd_power_on(ctx, true); + if (pm_runtime_suspended(dev)) { + int ret; + + ret = fimd_activate(ctx, true); + if (ret < 0) + return ret; + + /* + * in case of dpms on(standby), fimd_apply function will + * be called by encoder's dpms callback to update fimd's + * registers but in case of sleep wakeup, it's not. + * so fimd_apply function should be called at here. + */ + fimd_apply(dev); + } return 0; } @@ -964,7 +1018,7 @@ static int fimd_runtime_suspend(struct device *dev) DRM_DEBUG_KMS("%s\n", __FILE__); - return fimd_power_on(ctx, false); + return fimd_activate(ctx, false); } static int fimd_runtime_resume(struct device *dev) @@ -973,10 +1027,22 @@ static int fimd_runtime_resume(struct device *dev) DRM_DEBUG_KMS("%s\n", __FILE__); - return fimd_power_on(ctx, true); + return fimd_activate(ctx, true); } #endif +static struct platform_device_id fimd_driver_ids[] = { + { + .name = "exynos4-fb", + .driver_data = (unsigned long)&exynos4_fimd_driver_data, + }, { + .name = "exynos5-fb", + .driver_data = (unsigned long)&exynos5_fimd_driver_data, + }, + {}, +}; +MODULE_DEVICE_TABLE(platform, fimd_driver_ids); + static const struct dev_pm_ops fimd_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(fimd_suspend, fimd_resume) SET_RUNTIME_PM_OPS(fimd_runtime_suspend, fimd_runtime_resume, NULL) @@ -985,6 +1051,7 @@ static const struct dev_pm_ops fimd_pm_ops = { struct platform_driver fimd_driver = { .probe = fimd_probe, .remove = __devexit_p(fimd_remove), + .id_table = fimd_driver_ids, .driver = { .name = "exynos4-fb", .owner = THIS_MODULE, |