diff options
Diffstat (limited to 'drivers/gpu/drm/exynos/exynos_drm_fimd.c')
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_fimd.c | 700 |
1 files changed, 286 insertions, 414 deletions
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index a20440ce32e6..40fd6ccfcd6f 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -62,7 +62,7 @@ /* FIMD has totally five hardware windows. */ #define WINDOWS_NR 5 -#define get_fimd_context(dev) platform_get_drvdata(to_platform_device(dev)) +#define get_fimd_manager(mgr) platform_get_drvdata(to_platform_device(dev)) struct fimd_driver_data { unsigned int timing_base; @@ -105,20 +105,18 @@ struct fimd_win_data { }; struct fimd_context { - struct exynos_drm_subdrv subdrv; - int irq; - struct drm_crtc *crtc; + struct device *dev; + struct drm_device *drm_dev; 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; u32 vidcon1; bool suspended; - struct mutex lock; + int pipe; wait_queue_head_t wait_vsync_queue; atomic_t wait_vsync_event; @@ -145,153 +143,147 @@ static inline struct fimd_driver_data *drm_fimd_get_driver_data( return (struct fimd_driver_data *)of_id->data; } -static bool fimd_display_is_connected(struct device *dev) +static int fimd_mgr_initialize(struct exynos_drm_manager *mgr, + struct drm_device *drm_dev, int pipe) { - /* TODO. */ + struct fimd_context *ctx = mgr->ctx; - return true; -} + ctx->drm_dev = drm_dev; + ctx->pipe = pipe; -static void *fimd_get_panel(struct device *dev) -{ - struct fimd_context *ctx = get_fimd_context(dev); + /* + * enable drm irq mode. + * - with irq_enabled = true, we can use the vblank feature. + * + * P.S. note that we wouldn't use drm irq handler but + * just specific driver own one instead because + * drm framework supports only one irq handler. + */ + drm_dev->irq_enabled = true; - return &ctx->panel; -} + /* + * with vblank_disable_allowed = true, vblank interrupt will be disabled + * by drm timer once a current process gives up ownership of + * vblank event.(after drm_vblank_put function is called) + */ + drm_dev->vblank_disable_allowed = true; -static int fimd_check_mode(struct device *dev, struct drm_display_mode *mode) -{ - /* TODO. */ + /* attach this sub driver to iommu mapping if supported. */ + if (is_drm_iommu_supported(ctx->drm_dev)) + drm_iommu_attach_device(ctx->drm_dev, ctx->dev); return 0; } -static int fimd_display_power_on(struct device *dev, int mode) +static void fimd_mgr_remove(struct exynos_drm_manager *mgr) { - /* TODO */ + struct fimd_context *ctx = mgr->ctx; - return 0; + /* detach this sub driver from iommu mapping if supported. */ + if (is_drm_iommu_supported(ctx->drm_dev)) + drm_iommu_detach_device(ctx->drm_dev, ctx->dev); } -static struct exynos_drm_display_ops fimd_display_ops = { - .type = EXYNOS_DISPLAY_TYPE_LCD, - .is_connected = fimd_display_is_connected, - .get_panel = fimd_get_panel, - .check_mode = fimd_check_mode, - .power_on = fimd_display_power_on, -}; - -static void fimd_dpms(struct device *subdrv_dev, int mode) +static u32 fimd_calc_clkdiv(struct fimd_context *ctx, + const struct drm_display_mode *mode) { - struct fimd_context *ctx = get_fimd_context(subdrv_dev); + unsigned long ideal_clk = mode->htotal * mode->vtotal * mode->vrefresh; + u32 clkdiv; - DRM_DEBUG_KMS("%d\n", mode); + /* Find the clock divider value that gets us closest to ideal_clk */ + clkdiv = DIV_ROUND_UP(clk_get_rate(ctx->lcd_clk), ideal_clk); - mutex_lock(&ctx->lock); + return (clkdiv < 0x100) ? clkdiv : 0xff; +} - switch (mode) { - case DRM_MODE_DPMS_ON: - /* - * enable fimd hardware only if suspended status. - * - * P.S. fimd_dpms function would be called at booting time so - * clk_enable could be called double time. - */ - if (ctx->suspended) - pm_runtime_get_sync(subdrv_dev); - break; - case DRM_MODE_DPMS_STANDBY: - case DRM_MODE_DPMS_SUSPEND: - case DRM_MODE_DPMS_OFF: - if (!ctx->suspended) - pm_runtime_put_sync(subdrv_dev); - break; - default: - DRM_DEBUG_KMS("unspecified mode %d\n", mode); - break; - } +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; - mutex_unlock(&ctx->lock); + return true; } -static void fimd_apply(struct device *subdrv_dev) +static void fimd_mode_set(struct exynos_drm_manager *mgr, + const struct drm_display_mode *in_mode) { - struct fimd_context *ctx = get_fimd_context(subdrv_dev); - struct exynos_drm_manager *mgr = ctx->subdrv.manager; - struct exynos_drm_manager_ops *mgr_ops = mgr->ops; - struct exynos_drm_overlay_ops *ovl_ops = mgr->overlay_ops; - struct fimd_win_data *win_data; - int i; - - for (i = 0; i < WINDOWS_NR; i++) { - win_data = &ctx->win_data[i]; - if (win_data->enabled && (ovl_ops && ovl_ops->commit)) - ovl_ops->commit(subdrv_dev, i); - } + struct fimd_context *ctx = mgr->ctx; - if (mgr_ops && mgr_ops->commit) - mgr_ops->commit(subdrv_dev); + drm_mode_copy(&ctx->mode, in_mode); } -static void fimd_commit(struct device *dev) +static void fimd_commit(struct exynos_drm_manager *mgr) { - struct fimd_context *ctx = get_fimd_context(dev); - struct exynos_drm_panel_info *panel = &ctx->panel; - struct videomode *vm = &panel->vm; + struct fimd_context *ctx = mgr->ctx; + struct drm_display_mode *mode = &ctx->mode; struct fimd_driver_data *driver_data; - u32 val; + u32 val, clkdiv, vidcon1; + int vsync_len, vbpd, vfpd, hsync_len, hbpd, hfpd; driver_data = ctx->driver_data; if (ctx->suspended) return; - /* setup polarity values from machine code. */ - writel(ctx->vidcon1, ctx->regs + driver_data->timing_base + VIDCON1); + /* nothing to do if we haven't set the mode yet */ + if (mode->htotal == 0 || mode->vtotal == 0) + return; + + /* setup polarity values */ + vidcon1 = ctx->vidcon1; + if (mode->flags & DRM_MODE_FLAG_NVSYNC) + vidcon1 |= VIDCON1_INV_VSYNC; + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + vidcon1 |= VIDCON1_INV_HSYNC; + writel(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); + vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start; + vbpd = mode->crtc_vtotal - mode->crtc_vsync_end; + vfpd = mode->crtc_vsync_start - mode->crtc_vdisplay; + + 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); + hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start; + hbpd = mode->crtc_htotal - mode->crtc_hsync_end; + hfpd = mode->crtc_hsync_start - mode->crtc_hdisplay; + + 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. */ - val = ctx->vidcon0; - val &= ~(VIDCON0_CLKVAL_F_MASK | VIDCON0_CLKDIR); - - if (ctx->driver_data->has_clksel) { - val &= ~VIDCON0_CLKSEL_MASK; - val |= VIDCON0_CLKSEL_LCD; - } - - if (ctx->clkdiv > 1) - val |= VIDCON0_CLKVAL_F(ctx->clkdiv - 1) | VIDCON0_CLKDIR; - else - val &= ~VIDCON0_CLKDIR; /* 1:1 clock */ - /* * fields of register with prefix '_F' would be updated * at vsync(same as dma start) */ - val |= VIDCON0_ENVID | VIDCON0_ENVID_F; + val = VIDCON0_ENVID | VIDCON0_ENVID_F; + + if (ctx->driver_data->has_clksel) + val |= VIDCON0_CLKSEL_LCD; + + clkdiv = fimd_calc_clkdiv(ctx, mode); + if (clkdiv > 1) + val |= VIDCON0_CLKVAL_F(clkdiv - 1) | VIDCON0_CLKDIR; + writel(val, ctx->regs + VIDCON0); } -static int fimd_enable_vblank(struct device *dev) +static int fimd_enable_vblank(struct exynos_drm_manager *mgr) { - struct fimd_context *ctx = get_fimd_context(dev); + struct fimd_context *ctx = mgr->ctx; u32 val; if (ctx->suspended) @@ -314,9 +306,9 @@ static int fimd_enable_vblank(struct device *dev) return 0; } -static void fimd_disable_vblank(struct device *dev) +static void fimd_disable_vblank(struct exynos_drm_manager *mgr) { - struct fimd_context *ctx = get_fimd_context(dev); + struct fimd_context *ctx = mgr->ctx; u32 val; if (ctx->suspended) @@ -332,9 +324,9 @@ static void fimd_disable_vblank(struct device *dev) } } -static void fimd_wait_for_vblank(struct device *dev) +static void fimd_wait_for_vblank(struct exynos_drm_manager *mgr) { - struct fimd_context *ctx = get_fimd_context(dev); + struct fimd_context *ctx = mgr->ctx; if (ctx->suspended) return; @@ -351,25 +343,16 @@ static void fimd_wait_for_vblank(struct device *dev) DRM_DEBUG_KMS("vblank wait timed out.\n"); } -static struct exynos_drm_manager_ops fimd_manager_ops = { - .dpms = fimd_dpms, - .apply = fimd_apply, - .commit = fimd_commit, - .enable_vblank = fimd_enable_vblank, - .disable_vblank = fimd_disable_vblank, - .wait_for_vblank = fimd_wait_for_vblank, -}; - -static void fimd_win_mode_set(struct device *dev, - struct exynos_drm_overlay *overlay) +static void fimd_win_mode_set(struct exynos_drm_manager *mgr, + struct exynos_drm_overlay *overlay) { - struct fimd_context *ctx = get_fimd_context(dev); + struct fimd_context *ctx = mgr->ctx; struct fimd_win_data *win_data; int win; unsigned long offset; if (!overlay) { - dev_err(dev, "overlay is NULL\n"); + DRM_ERROR("overlay is NULL\n"); return; } @@ -409,9 +392,8 @@ static void fimd_win_mode_set(struct device *dev, overlay->fb_width, overlay->crtc_width); } -static void fimd_win_set_pixfmt(struct device *dev, unsigned int win) +static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win) { - struct fimd_context *ctx = get_fimd_context(dev); struct fimd_win_data *win_data = &ctx->win_data[win]; unsigned long val; @@ -467,9 +449,8 @@ static void fimd_win_set_pixfmt(struct device *dev, unsigned int win) writel(val, ctx->regs + WINCON(win)); } -static void fimd_win_set_colkey(struct device *dev, unsigned int win) +static void fimd_win_set_colkey(struct fimd_context *ctx, unsigned int win) { - struct fimd_context *ctx = get_fimd_context(dev); unsigned int keycon0 = 0, keycon1 = 0; keycon0 = ~(WxKEYCON0_KEYBL_EN | WxKEYCON0_KEYEN_F | @@ -508,9 +489,9 @@ static void fimd_shadow_protect_win(struct fimd_context *ctx, writel(val, ctx->regs + reg); } -static void fimd_win_commit(struct device *dev, int zpos) +static void fimd_win_commit(struct exynos_drm_manager *mgr, int zpos) { - struct fimd_context *ctx = get_fimd_context(dev); + struct fimd_context *ctx = mgr->ctx; struct fimd_win_data *win_data; int win = zpos; unsigned long val, alpha, size; @@ -528,6 +509,12 @@ static void fimd_win_commit(struct device *dev, int zpos) win_data = &ctx->win_data[win]; + /* If suspended, enable this on resume */ + if (ctx->suspended) { + win_data->resume = true; + return; + } + /* * SHADOWCON/PRTCON register is used for enabling timing. * @@ -605,11 +592,11 @@ static void fimd_win_commit(struct device *dev, int zpos) DRM_DEBUG_KMS("osd size = 0x%x\n", (unsigned int)val); } - fimd_win_set_pixfmt(dev, win); + fimd_win_set_pixfmt(ctx, win); /* hardware window 0 doesn't support color key. */ if (win != 0) - fimd_win_set_colkey(dev, win); + fimd_win_set_colkey(ctx, win); /* wincon */ val = readl(ctx->regs + WINCON(win)); @@ -628,9 +615,9 @@ static void fimd_win_commit(struct device *dev, int zpos) win_data->enabled = true; } -static void fimd_win_disable(struct device *dev, int zpos) +static void fimd_win_disable(struct exynos_drm_manager *mgr, int zpos) { - struct fimd_context *ctx = get_fimd_context(dev); + struct fimd_context *ctx = mgr->ctx; struct fimd_win_data *win_data; int win = zpos; u32 val; @@ -669,132 +656,6 @@ static void fimd_win_disable(struct device *dev, int zpos) win_data->enabled = false; } -static struct exynos_drm_overlay_ops fimd_overlay_ops = { - .mode_set = fimd_win_mode_set, - .commit = fimd_win_commit, - .disable = fimd_win_disable, -}; - -static struct exynos_drm_manager fimd_manager = { - .pipe = -1, - .ops = &fimd_manager_ops, - .overlay_ops = &fimd_overlay_ops, - .display_ops = &fimd_display_ops, -}; - -static irqreturn_t fimd_irq_handler(int irq, void *dev_id) -{ - struct fimd_context *ctx = (struct fimd_context *)dev_id; - struct exynos_drm_subdrv *subdrv = &ctx->subdrv; - struct drm_device *drm_dev = subdrv->drm_dev; - struct exynos_drm_manager *manager = subdrv->manager; - u32 val; - - val = readl(ctx->regs + VIDINTCON1); - - if (val & VIDINTCON1_INT_FRAME) - /* VSYNC interrupt */ - writel(VIDINTCON1_INT_FRAME, ctx->regs + VIDINTCON1); - - /* check the crtc is detached already from encoder */ - if (manager->pipe < 0) - goto out; - - drm_handle_vblank(drm_dev, manager->pipe); - exynos_drm_crtc_finish_pageflip(drm_dev, manager->pipe); - - /* set wait vsync event to zero and wake up queue. */ - if (atomic_read(&ctx->wait_vsync_event)) { - atomic_set(&ctx->wait_vsync_event, 0); - wake_up(&ctx->wait_vsync_queue); - } -out: - return IRQ_HANDLED; -} - -static int fimd_subdrv_probe(struct drm_device *drm_dev, struct device *dev) -{ - /* - * enable drm irq mode. - * - with irq_enabled = true, we can use the vblank feature. - * - * P.S. note that we wouldn't use drm irq handler but - * just specific driver own one instead because - * drm framework supports only one irq handler. - */ - drm_dev->irq_enabled = true; - - /* - * with vblank_disable_allowed = true, vblank interrupt will be disabled - * by drm timer once a current process gives up ownership of - * vblank event.(after drm_vblank_put function is called) - */ - drm_dev->vblank_disable_allowed = true; - - /* attach this sub driver to iommu mapping if supported. */ - if (is_drm_iommu_supported(drm_dev)) - drm_iommu_attach_device(drm_dev, dev); - - return 0; -} - -static void fimd_subdrv_remove(struct drm_device *drm_dev, struct device *dev) -{ - /* detach this sub driver from iommu mapping if supported. */ - if (is_drm_iommu_supported(drm_dev)) - drm_iommu_detach_device(drm_dev, dev); -} - -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)); @@ -808,111 +669,190 @@ static void fimd_clear_win(struct fimd_context *ctx, int win) fimd_shadow_protect_win(ctx, win, false); } -static int fimd_clock(struct fimd_context *ctx, bool enable) +static void fimd_window_suspend(struct exynos_drm_manager *mgr) { - if (enable) { - int ret; - - ret = clk_prepare_enable(ctx->bus_clk); - if (ret < 0) - return ret; + struct fimd_context *ctx = mgr->ctx; + struct fimd_win_data *win_data; + int i; - ret = clk_prepare_enable(ctx->lcd_clk); - if (ret < 0) { - clk_disable_unprepare(ctx->bus_clk); - return ret; - } - } else { - clk_disable_unprepare(ctx->lcd_clk); - clk_disable_unprepare(ctx->bus_clk); + for (i = 0; i < WINDOWS_NR; i++) { + win_data = &ctx->win_data[i]; + win_data->resume = win_data->enabled; + if (win_data->enabled) + fimd_win_disable(mgr, i); } - - return 0; + fimd_wait_for_vblank(mgr); } -static void fimd_window_suspend(struct device *dev) +static void fimd_window_resume(struct exynos_drm_manager *mgr) { - struct fimd_context *ctx = get_fimd_context(dev); + struct fimd_context *ctx = mgr->ctx; struct fimd_win_data *win_data; int i; for (i = 0; i < WINDOWS_NR; i++) { win_data = &ctx->win_data[i]; - win_data->resume = win_data->enabled; - fimd_win_disable(dev, i); + win_data->enabled = win_data->resume; + win_data->resume = false; } - fimd_wait_for_vblank(dev); } -static void fimd_window_resume(struct device *dev) +static void fimd_apply(struct exynos_drm_manager *mgr) { - struct fimd_context *ctx = get_fimd_context(dev); + struct fimd_context *ctx = mgr->ctx; struct fimd_win_data *win_data; int i; for (i = 0; i < WINDOWS_NR; i++) { win_data = &ctx->win_data[i]; - win_data->enabled = win_data->resume; - win_data->resume = false; + if (win_data->enabled) + fimd_win_commit(mgr, i); } + + fimd_commit(mgr); } -static int fimd_activate(struct fimd_context *ctx, bool enable) +static int fimd_poweron(struct exynos_drm_manager *mgr) { - struct device *dev = ctx->subdrv.dev; - if (enable) { - int ret; + struct fimd_context *ctx = mgr->ctx; + int ret; - ret = fimd_clock(ctx, true); - if (ret < 0) - return ret; + if (!ctx->suspended) + return 0; - ctx->suspended = false; + ctx->suspended = false; - /* if vblank was enabled status, enable it again. */ - if (test_and_clear_bit(0, &ctx->irq_flags)) - fimd_enable_vblank(dev); + pm_runtime_get_sync(ctx->dev); - fimd_window_resume(dev); - } else { - fimd_window_suspend(dev); + ret = clk_prepare_enable(ctx->bus_clk); + if (ret < 0) { + DRM_ERROR("Failed to prepare_enable the bus clk [%d]\n", ret); + goto bus_clk_err; + } + + ret = clk_prepare_enable(ctx->lcd_clk); + if (ret < 0) { + DRM_ERROR("Failed to prepare_enable the lcd clk [%d]\n", ret); + goto lcd_clk_err; + } - fimd_clock(ctx, false); - ctx->suspended = true; + /* if vblank was enabled status, enable it again. */ + if (test_and_clear_bit(0, &ctx->irq_flags)) { + ret = fimd_enable_vblank(mgr); + if (ret) { + DRM_ERROR("Failed to re-enable vblank [%d]\n", ret); + goto enable_vblank_err; + } } + fimd_window_resume(mgr); + + fimd_apply(mgr); + return 0; + +enable_vblank_err: + clk_disable_unprepare(ctx->lcd_clk); +lcd_clk_err: + clk_disable_unprepare(ctx->bus_clk); +bus_clk_err: + ctx->suspended = true; + return ret; } -static int fimd_get_platform_data(struct fimd_context *ctx, struct device *dev) +static int fimd_poweroff(struct exynos_drm_manager *mgr) { - struct videomode *vm; - int ret; + struct fimd_context *ctx = mgr->ctx; - vm = &ctx->panel.vm; - ret = of_get_videomode(dev->of_node, vm, OF_USE_NATIVE_MODE); - if (ret) { - DRM_ERROR("failed: of_get_videomode() : %d\n", ret); - return ret; - } + if (ctx->suspended) + return 0; - if (vm->flags & DISPLAY_FLAGS_VSYNC_LOW) - ctx->vidcon1 |= VIDCON1_INV_VSYNC; - if (vm->flags & DISPLAY_FLAGS_HSYNC_LOW) - ctx->vidcon1 |= VIDCON1_INV_HSYNC; - if (vm->flags & DISPLAY_FLAGS_DE_LOW) - ctx->vidcon1 |= VIDCON1_INV_VDEN; - if (vm->flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE) - ctx->vidcon1 |= VIDCON1_INV_VCLK; + /* + * We need to make sure that all windows are disabled before we + * suspend that connector. Otherwise we might try to scan from + * a destroyed buffer later. + */ + fimd_window_suspend(mgr); + clk_disable_unprepare(ctx->lcd_clk); + clk_disable_unprepare(ctx->bus_clk); + + pm_runtime_put_sync(ctx->dev); + + ctx->suspended = true; return 0; } +static void fimd_dpms(struct exynos_drm_manager *mgr, int mode) +{ + DRM_DEBUG_KMS("%s, %d\n", __FILE__, mode); + + switch (mode) { + case DRM_MODE_DPMS_ON: + fimd_poweron(mgr); + break; + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + fimd_poweroff(mgr); + break; + default: + DRM_DEBUG_KMS("unspecified mode %d\n", mode); + break; + } +} + +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, + .wait_for_vblank = fimd_wait_for_vblank, + .win_mode_set = fimd_win_mode_set, + .win_commit = fimd_win_commit, + .win_disable = fimd_win_disable, +}; + +static struct exynos_drm_manager fimd_manager = { + .type = EXYNOS_DISPLAY_TYPE_LCD, + .ops = &fimd_manager_ops, +}; + +static irqreturn_t fimd_irq_handler(int irq, void *dev_id) +{ + struct fimd_context *ctx = (struct fimd_context *)dev_id; + u32 val; + + val = readl(ctx->regs + VIDINTCON1); + + if (val & VIDINTCON1_INT_FRAME) + /* VSYNC interrupt */ + writel(VIDINTCON1_INT_FRAME, ctx->regs + VIDINTCON1); + + /* check the crtc is detached already from encoder */ + if (ctx->pipe < 0 || !ctx->drm_dev) + goto out; + + drm_handle_vblank(ctx->drm_dev, ctx->pipe); + exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe); + + /* set wait vsync event to zero and wake up queue. */ + if (atomic_read(&ctx->wait_vsync_event)) { + atomic_set(&ctx->wait_vsync_event, 0); + wake_up(&ctx->wait_vsync_queue); + } +out: + return IRQ_HANDLED; +} + static int fimd_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct fimd_context *ctx; - struct exynos_drm_subdrv *subdrv; struct resource *res; int win; int ret = -EINVAL; @@ -924,13 +864,25 @@ static int fimd_probe(struct platform_device *pdev) if (!ctx) return -ENOMEM; - ret = fimd_get_platform_data(ctx, dev); - if (ret) - return ret; + ctx->dev = dev; + ctx->suspended = true; - ret = fimd_configure_clocks(ctx, dev); - if (ret) - return ret; + if (of_property_read_bool(dev->of_node, "samsung,invert-vden")) + ctx->vidcon1 |= VIDCON1_INV_VDEN; + if (of_property_read_bool(dev->of_node, "samsung,invert-vclk")) + ctx->vidcon1 |= VIDCON1_INV_VCLK; + + 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); @@ -944,9 +896,7 @@ static int fimd_probe(struct platform_device *pdev) return -ENXIO; } - ctx->irq = res->start; - - ret = devm_request_irq(dev, ctx->irq, fimd_irq_handler, + ret = devm_request_irq(dev, res->start, fimd_irq_handler, 0, "drm_fimd", ctx); if (ret) { dev_err(dev, "irq request failed.\n"); @@ -957,112 +907,35 @@ static int fimd_probe(struct platform_device *pdev) init_waitqueue_head(&ctx->wait_vsync_queue); atomic_set(&ctx->wait_vsync_event, 0); - subdrv = &ctx->subdrv; + platform_set_drvdata(pdev, &fimd_manager); - subdrv->dev = dev; - subdrv->manager = &fimd_manager; - subdrv->probe = fimd_subdrv_probe; - subdrv->remove = fimd_subdrv_remove; + fimd_manager.ctx = ctx; + exynos_drm_manager_register(&fimd_manager); - mutex_init(&ctx->lock); - - platform_set_drvdata(pdev, ctx); + exynos_dpi_probe(ctx->dev); pm_runtime_enable(dev); - pm_runtime_get_sync(dev); for (win = 0; win < WINDOWS_NR; win++) fimd_clear_win(ctx, win); - exynos_drm_subdrv_register(subdrv); - return 0; } static int fimd_remove(struct platform_device *pdev) { - struct device *dev = &pdev->dev; - struct fimd_context *ctx = platform_get_drvdata(pdev); - - exynos_drm_subdrv_unregister(&ctx->subdrv); - - if (ctx->suspended) - goto out; - - pm_runtime_set_suspended(dev); - pm_runtime_put_sync(dev); - -out: - pm_runtime_disable(dev); - - return 0; -} - -#ifdef CONFIG_PM_SLEEP -static int fimd_suspend(struct device *dev) -{ - struct fimd_context *ctx = get_fimd_context(dev); + struct exynos_drm_manager *mgr = platform_get_drvdata(pdev); - /* - * 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. - */ - if (!pm_runtime_suspended(dev)) - return fimd_activate(ctx, false); + exynos_dpi_remove(&pdev->dev); - return 0; -} + exynos_drm_manager_unregister(&fimd_manager); -static int fimd_resume(struct device *dev) -{ - struct fimd_context *ctx = get_fimd_context(dev); + fimd_dpms(mgr, DRM_MODE_DPMS_OFF); - /* - * if entered to sleep when lcd panel was on, the usage_count - * 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)) { - 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); - } + pm_runtime_disable(&pdev->dev); return 0; } -#endif - -#ifdef CONFIG_PM_RUNTIME -static int fimd_runtime_suspend(struct device *dev) -{ - struct fimd_context *ctx = get_fimd_context(dev); - - return fimd_activate(ctx, false); -} - -static int fimd_runtime_resume(struct device *dev) -{ - struct fimd_context *ctx = get_fimd_context(dev); - - return fimd_activate(ctx, true); -} -#endif - -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) -}; struct platform_driver fimd_driver = { .probe = fimd_probe, @@ -1070,7 +943,6 @@ struct platform_driver fimd_driver = { .driver = { .name = "exynos4-fb", .owner = THIS_MODULE, - .pm = &fimd_pm_ops, .of_match_table = fimd_driver_dt_match, }, }; |