diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-01-17 13:40:25 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-01-17 13:40:25 -0800 |
commit | 984065055e6e39f8dd812529e11922374bd39352 (patch) | |
tree | a8f1bcbd81e0fadce0cef39ab5ce09ab84b261fe /drivers/gpu/drm/exynos | |
parent | 12768c1e2c83b05ea1658470045789a14b6edf4c (diff) | |
parent | 1df59b8497f47495e873c23abd6d3d290c730505 (diff) | |
download | op-kernel-dev-984065055e6e39f8dd812529e11922374bd39352.zip op-kernel-dev-984065055e6e39f8dd812529e11922374bd39352.tar.gz |
Merge branch 'drm-next' of git://people.freedesktop.org/~airlied/linux
Pull drm updates from Dave Airlie:
"This is the main drm pull request for 4.5. I don't think I've missed
anything too major, I'm mostly back at work now but I'll probably get
some sleep in 5 years time.
Summary:
New drivers:
- etnaviv:
GPU driver for the 3D core on the Vivante core used in numerous
ARM boards.
Highlights:
Core:
- Atomic suspend/resume helpers
- Move the headers to using userspace friendlier types.
- Documentation updates
- Lots of struct_mutex removal.
- Bunch of DP MST fixes from AMD.
Panel:
- More DSI helpers
- Support for some new basic panels
i915:
- Basic Kabylake support
- DP link training and detect code refactoring
- fbc/psr fixes
- FIFO underrun fixes
- SDE interrupt handling fixes
- dma-buf/fence support in pageflip path.
- GPU side for MST audio support
radeon/amdgpu:
- Drop UMS support
- GPUVM/Scheduler optimisations
- Initial Powerplay support for Tonga/Fiji/CZ/ST
- ACP audio prerequisites
nouveau:
- GK20a instmem improvements
- PCIE link speed change support
msm:
- DSI support for msm8960/apq8064
tegra:
- Host1X support for Tegra210 SoC
vc4:
- 3D acceleration support
armada:
- Get rid of struct mutex
tda998x:
- Atomic modesetting support
- TMDS clock limitations
omapdrm:
- Atomic modesetting support
- improved TILER performance
rockchip:
- RK3036 VOP support
- Atomic modesetting support
- Synopsys DW MIPI DSI support
exynos:
- Runtime PM support
- of_graph binding for DP panels
- Cleanup of IPP code
- Configurable plane support
- Kernel panic fixes at release time"
* 'drm-next' of git://people.freedesktop.org/~airlied/linux: (711 commits)
drm/fb_cma_helper: Remove implicit call to disable_unused_functions
drm/amdgpu: add missing irq.h include
drm/vmwgfx: Fix a width / pitch mismatch on framebuffer updates
drm/vmwgfx: Fix an incorrect lock check
drm: nouveau: fix nouveau_debugfs_init prototype
drm/nouveau/pci: fix check in nvkm_pcie_set_link
drm/amdgpu: validate duplicates first
drm/amdgpu: move VM page tables to the LRU end on CS v2
drm/ttm: add ttm_bo_move_to_lru_tail function v2
drm/ttm: fix adding foreign BOs to the swap LRU
drm/ttm: fix adding foreign BOs to the LRU during init v2
drm/radeon: use kobj_to_dev()
drm/amdgpu: use kobj_to_dev()
drm/amdgpu/cz: force vce clocks when sclks are forced
drm/amdgpu/cz: force uvd clocks when sclks are forced
drm/amdgpu/cz: add code to enable forcing VCE clocks
drm/amdgpu/cz: add code to enable forcing UVD clocks
drm/amdgpu: fix lost sync_to if scheduler is enabled.
drm/amd/powerplay: fix static checker warning for return meaningless value.
drm/sysfs: use kobj_to_dev()
...
Diffstat (limited to 'drivers/gpu/drm/exynos')
25 files changed, 1178 insertions, 759 deletions
diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig index 96e86cf..83efca9 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig @@ -118,7 +118,7 @@ config DRM_EXYNOS_ROTATOR config DRM_EXYNOS_GSC bool "GScaler" - depends on DRM_EXYNOS_IPP && ARCH_EXYNOS5 && !ARCH_MULTIPLATFORM + depends on DRM_EXYNOS_IPP && ARCH_EXYNOS5 && !VIDEO_SAMSUNG_EXYNOS_GSC help Choose this option if you want to use Exynos GSC for DRM. diff --git a/drivers/gpu/drm/exynos/exynos5433_drm_decon.c b/drivers/gpu/drm/exynos/exynos5433_drm_decon.c index fbe1b31..1bf6a21 100644 --- a/drivers/gpu/drm/exynos/exynos5433_drm_decon.c +++ b/drivers/gpu/drm/exynos/exynos5433_drm_decon.c @@ -21,11 +21,11 @@ #include "exynos_drm_drv.h" #include "exynos_drm_crtc.h" +#include "exynos_drm_fb.h" #include "exynos_drm_plane.h" #include "exynos_drm_iommu.h" #define WINDOWS_NR 3 -#define CURSOR_WIN 2 #define MIN_FB_WIDTH_FOR_16WORD_BURST 128 static const char * const decon_clks_name[] = { @@ -56,6 +56,7 @@ struct decon_context { struct drm_device *drm_dev; struct exynos_drm_crtc *crtc; struct exynos_drm_plane planes[WINDOWS_NR]; + struct exynos_drm_plane_config configs[WINDOWS_NR]; void __iomem *addr; struct clk *clks[ARRAY_SIZE(decon_clks_name)]; int pipe; @@ -71,6 +72,12 @@ static const uint32_t decon_formats[] = { DRM_FORMAT_ARGB8888, }; +static const enum drm_plane_type decon_win_types[WINDOWS_NR] = { + DRM_PLANE_TYPE_PRIMARY, + DRM_PLANE_TYPE_OVERLAY, + DRM_PLANE_TYPE_CURSOR, +}; + static inline void decon_set_bits(struct decon_context *ctx, u32 reg, u32 mask, u32 val) { @@ -241,15 +248,16 @@ static void decon_shadow_protect_win(struct decon_context *ctx, int win, protect ? ~0 : 0); } -static void decon_atomic_begin(struct exynos_drm_crtc *crtc, - struct exynos_drm_plane *plane) +static void decon_atomic_begin(struct exynos_drm_crtc *crtc) { struct decon_context *ctx = crtc->ctx; + int i; if (test_bit(BIT_SUSPENDED, &ctx->flags)) return; - decon_shadow_protect_win(ctx, plane->zpos, true); + for (i = ctx->first_win; i < WINDOWS_NR; i++) + decon_shadow_protect_win(ctx, i, true); } #define BIT_VAL(x, e, s) (((x) & ((1 << ((e) - (s) + 1)) - 1)) << (s)) @@ -259,21 +267,24 @@ static void decon_atomic_begin(struct exynos_drm_crtc *crtc, static void decon_update_plane(struct exynos_drm_crtc *crtc, struct exynos_drm_plane *plane) { + struct exynos_drm_plane_state *state = + to_exynos_plane_state(plane->base.state); struct decon_context *ctx = crtc->ctx; - struct drm_plane_state *state = plane->base.state; - unsigned int win = plane->zpos; - unsigned int bpp = state->fb->bits_per_pixel >> 3; - unsigned int pitch = state->fb->pitches[0]; + struct drm_framebuffer *fb = state->base.fb; + unsigned int win = plane->index; + unsigned int bpp = fb->bits_per_pixel >> 3; + unsigned int pitch = fb->pitches[0]; + dma_addr_t dma_addr = exynos_drm_fb_dma_addr(fb, 0); u32 val; if (test_bit(BIT_SUSPENDED, &ctx->flags)) return; - val = COORDINATE_X(plane->crtc_x) | COORDINATE_Y(plane->crtc_y); + val = COORDINATE_X(state->crtc.x) | COORDINATE_Y(state->crtc.y); writel(val, ctx->addr + DECON_VIDOSDxA(win)); - val = COORDINATE_X(plane->crtc_x + plane->crtc_w - 1) | - COORDINATE_Y(plane->crtc_y + plane->crtc_h - 1); + val = COORDINATE_X(state->crtc.x + state->crtc.w - 1) | + COORDINATE_Y(state->crtc.y + state->crtc.h - 1); writel(val, ctx->addr + DECON_VIDOSDxB(win)); val = VIDOSD_Wx_ALPHA_R_F(0x0) | VIDOSD_Wx_ALPHA_G_F(0x0) | @@ -284,20 +295,20 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc, VIDOSD_Wx_ALPHA_B_F(0x0); writel(val, ctx->addr + DECON_VIDOSDxD(win)); - writel(plane->dma_addr[0], ctx->addr + DECON_VIDW0xADD0B0(win)); + writel(dma_addr, ctx->addr + DECON_VIDW0xADD0B0(win)); - val = plane->dma_addr[0] + pitch * plane->crtc_h; + val = dma_addr + pitch * state->src.h; writel(val, ctx->addr + DECON_VIDW0xADD1B0(win)); if (ctx->out_type != IFTYPE_HDMI) - val = BIT_VAL(pitch - plane->crtc_w * bpp, 27, 14) - | BIT_VAL(plane->crtc_w * bpp, 13, 0); + val = BIT_VAL(pitch - state->crtc.w * bpp, 27, 14) + | BIT_VAL(state->crtc.w * bpp, 13, 0); else - val = BIT_VAL(pitch - plane->crtc_w * bpp, 29, 15) - | BIT_VAL(plane->crtc_w * bpp, 14, 0); + val = BIT_VAL(pitch - state->crtc.w * bpp, 29, 15) + | BIT_VAL(state->crtc.w * bpp, 14, 0); writel(val, ctx->addr + DECON_VIDW0xADD2(win)); - decon_win_set_pixfmt(ctx, win, state->fb); + decon_win_set_pixfmt(ctx, win, fb); /* window enable */ decon_set_bits(ctx, DECON_WINCONx(win), WINCONx_ENWIN_F, ~0); @@ -310,7 +321,7 @@ static void decon_disable_plane(struct exynos_drm_crtc *crtc, struct exynos_drm_plane *plane) { struct decon_context *ctx = crtc->ctx; - unsigned int win = plane->zpos; + unsigned int win = plane->index; if (test_bit(BIT_SUSPENDED, &ctx->flags)) return; @@ -326,15 +337,16 @@ static void decon_disable_plane(struct exynos_drm_crtc *crtc, decon_set_bits(ctx, DECON_UPDATE, STANDALONE_UPDATE_F, ~0); } -static void decon_atomic_flush(struct exynos_drm_crtc *crtc, - struct exynos_drm_plane *plane) +static void decon_atomic_flush(struct exynos_drm_crtc *crtc) { struct decon_context *ctx = crtc->ctx; + int i; if (test_bit(BIT_SUSPENDED, &ctx->flags)) return; - decon_shadow_protect_win(ctx, plane->zpos, false); + for (i = ctx->first_win; i < WINDOWS_NR; i++) + decon_shadow_protect_win(ctx, i, false); if (ctx->out_type == IFTYPE_I80) set_bit(BIT_WIN_UPDATED, &ctx->flags); @@ -377,20 +389,12 @@ static void decon_swreset(struct decon_context *ctx) static void decon_enable(struct exynos_drm_crtc *crtc) { struct decon_context *ctx = crtc->ctx; - int ret; - int i; if (!test_and_clear_bit(BIT_SUSPENDED, &ctx->flags)) return; pm_runtime_get_sync(ctx->dev); - for (i = 0; i < ARRAY_SIZE(decon_clks_name); i++) { - ret = clk_prepare_enable(ctx->clks[i]); - if (ret < 0) - goto err; - } - set_bit(BIT_CLKS_ENABLED, &ctx->flags); /* if vblank was enabled status, enable it again. */ @@ -399,11 +403,6 @@ static void decon_enable(struct exynos_drm_crtc *crtc) decon_commit(ctx->crtc); - return; -err: - while (--i >= 0) - clk_disable_unprepare(ctx->clks[i]); - set_bit(BIT_SUSPENDED, &ctx->flags); } @@ -425,9 +424,6 @@ static void decon_disable(struct exynos_drm_crtc *crtc) decon_swreset(ctx); - for (i = 0; i < ARRAY_SIZE(decon_clks_name); i++) - clk_disable_unprepare(ctx->clks[i]); - clear_bit(BIT_CLKS_ENABLED, &ctx->flags); pm_runtime_put_sync(ctx->dev); @@ -478,7 +474,6 @@ err: static struct exynos_drm_crtc_ops decon_crtc_ops = { .enable = decon_enable, .disable = decon_disable, - .commit = decon_commit, .enable_vblank = decon_enable_vblank, .disable_vblank = decon_disable_vblank, .atomic_begin = decon_atomic_begin, @@ -495,7 +490,6 @@ static int decon_bind(struct device *dev, struct device *master, void *data) struct exynos_drm_private *priv = drm_dev->dev_private; struct exynos_drm_plane *exynos_plane; enum exynos_drm_output_type out_type; - enum drm_plane_type type; unsigned int win; int ret; @@ -505,10 +499,13 @@ static int decon_bind(struct device *dev, struct device *master, void *data) for (win = ctx->first_win; win < WINDOWS_NR; win++) { int tmp = (win == ctx->first_win) ? 0 : win; - type = exynos_plane_get_type(tmp, CURSOR_WIN); - ret = exynos_plane_init(drm_dev, &ctx->planes[win], - 1 << ctx->pipe, type, decon_formats, - ARRAY_SIZE(decon_formats), win); + ctx->configs[win].pixel_formats = decon_formats; + ctx->configs[win].num_pixel_formats = ARRAY_SIZE(decon_formats); + ctx->configs[win].zpos = win; + ctx->configs[win].type = decon_win_types[tmp]; + + ret = exynos_plane_init(drm_dev, &ctx->planes[win], win, + 1 << ctx->pipe, &ctx->configs[win]); if (ret) return ret; } @@ -581,6 +578,44 @@ out: return IRQ_HANDLED; } +#ifdef CONFIG_PM +static int exynos5433_decon_suspend(struct device *dev) +{ + struct decon_context *ctx = dev_get_drvdata(dev); + int i; + + for (i = 0; i < ARRAY_SIZE(decon_clks_name); i++) + clk_disable_unprepare(ctx->clks[i]); + + return 0; +} + +static int exynos5433_decon_resume(struct device *dev) +{ + struct decon_context *ctx = dev_get_drvdata(dev); + int i, ret; + + for (i = 0; i < ARRAY_SIZE(decon_clks_name); i++) { + ret = clk_prepare_enable(ctx->clks[i]); + if (ret < 0) + goto err; + } + + return 0; + +err: + while (--i >= 0) + clk_disable_unprepare(ctx->clks[i]); + + return ret; +} +#endif + +static const struct dev_pm_ops exynos5433_decon_pm_ops = { + SET_RUNTIME_PM_OPS(exynos5433_decon_suspend, exynos5433_decon_resume, + NULL) +}; + static const struct of_device_id exynos5433_decon_driver_dt_match[] = { { .compatible = "samsung,exynos5433-decon", @@ -684,6 +719,7 @@ struct platform_driver exynos5433_decon_driver = { .remove = exynos5433_decon_remove, .driver = { .name = "exynos5433-decon", + .pm = &exynos5433_decon_pm_ops, .of_match_table = exynos5433_decon_driver_dt_match, }, }; diff --git a/drivers/gpu/drm/exynos/exynos7_drm_decon.c b/drivers/gpu/drm/exynos/exynos7_drm_decon.c index ead2b16..52bda3b 100644 --- a/drivers/gpu/drm/exynos/exynos7_drm_decon.c +++ b/drivers/gpu/drm/exynos/exynos7_drm_decon.c @@ -30,6 +30,7 @@ #include "exynos_drm_crtc.h" #include "exynos_drm_plane.h" #include "exynos_drm_drv.h" +#include "exynos_drm_fb.h" #include "exynos_drm_fbdev.h" #include "exynos_drm_iommu.h" @@ -40,13 +41,13 @@ #define MIN_FB_WIDTH_FOR_16WORD_BURST 128 #define WINDOWS_NR 2 -#define CURSOR_WIN 1 struct decon_context { struct device *dev; struct drm_device *drm_dev; struct exynos_drm_crtc *crtc; struct exynos_drm_plane planes[WINDOWS_NR]; + struct exynos_drm_plane_config configs[WINDOWS_NR]; struct clk *pclk; struct clk *aclk; struct clk *eclk; @@ -81,6 +82,11 @@ static const uint32_t decon_formats[] = { DRM_FORMAT_BGRA8888, }; +static const enum drm_plane_type decon_win_types[WINDOWS_NR] = { + DRM_PLANE_TYPE_PRIMARY, + DRM_PLANE_TYPE_CURSOR, +}; + static void decon_wait_for_vblank(struct exynos_drm_crtc *crtc) { struct decon_context *ctx = crtc->ctx; @@ -119,13 +125,8 @@ static void decon_clear_channels(struct exynos_drm_crtc *crtc) } /* Wait for vsync, as disable channel takes effect at next vsync */ - if (ch_enabled) { - unsigned int state = ctx->suspended; - - ctx->suspended = 0; + if (ch_enabled) decon_wait_for_vblank(ctx->crtc); - ctx->suspended = state; - } } static int decon_ctx_initialize(struct decon_context *ctx, @@ -384,30 +385,32 @@ static void decon_shadow_protect_win(struct decon_context *ctx, writel(val, ctx->regs + SHADOWCON); } -static void decon_atomic_begin(struct exynos_drm_crtc *crtc, - struct exynos_drm_plane *plane) +static void decon_atomic_begin(struct exynos_drm_crtc *crtc) { struct decon_context *ctx = crtc->ctx; + int i; if (ctx->suspended) return; - decon_shadow_protect_win(ctx, plane->zpos, true); + for (i = 0; i < WINDOWS_NR; i++) + decon_shadow_protect_win(ctx, i, true); } static void decon_update_plane(struct exynos_drm_crtc *crtc, struct exynos_drm_plane *plane) { + struct exynos_drm_plane_state *state = + to_exynos_plane_state(plane->base.state); struct decon_context *ctx = crtc->ctx; - struct drm_display_mode *mode = &crtc->base.state->adjusted_mode; - struct drm_plane_state *state = plane->base.state; + struct drm_framebuffer *fb = state->base.fb; int padding; unsigned long val, alpha; unsigned int last_x; unsigned int last_y; - unsigned int win = plane->zpos; - unsigned int bpp = state->fb->bits_per_pixel >> 3; - unsigned int pitch = state->fb->pitches[0]; + unsigned int win = plane->index; + unsigned int bpp = fb->bits_per_pixel >> 3; + unsigned int pitch = fb->pitches[0]; if (ctx->suspended) return; @@ -423,41 +426,32 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc, */ /* buffer start address */ - val = (unsigned long)plane->dma_addr[0]; + val = (unsigned long)exynos_drm_fb_dma_addr(fb, 0); writel(val, ctx->regs + VIDW_BUF_START(win)); - padding = (pitch / bpp) - state->fb->width; + padding = (pitch / bpp) - fb->width; /* buffer size */ - writel(state->fb->width + padding, ctx->regs + VIDW_WHOLE_X(win)); - writel(state->fb->height, ctx->regs + VIDW_WHOLE_Y(win)); + writel(fb->width + padding, ctx->regs + VIDW_WHOLE_X(win)); + writel(fb->height, ctx->regs + VIDW_WHOLE_Y(win)); /* offset from the start of the buffer to read */ - writel(plane->src_x, ctx->regs + VIDW_OFFSET_X(win)); - writel(plane->src_y, ctx->regs + VIDW_OFFSET_Y(win)); + writel(state->src.x, ctx->regs + VIDW_OFFSET_X(win)); + writel(state->src.y, ctx->regs + VIDW_OFFSET_Y(win)); DRM_DEBUG_KMS("start addr = 0x%lx\n", (unsigned long)val); DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n", - plane->crtc_w, plane->crtc_h); - - /* - * OSD position. - * In case the window layout goes of LCD layout, DECON fails. - */ - if ((plane->crtc_x + plane->crtc_w) > mode->hdisplay) - plane->crtc_x = mode->hdisplay - plane->crtc_w; - if ((plane->crtc_y + plane->crtc_h) > mode->vdisplay) - plane->crtc_y = mode->vdisplay - plane->crtc_h; + state->crtc.w, state->crtc.h); - val = VIDOSDxA_TOPLEFT_X(plane->crtc_x) | - VIDOSDxA_TOPLEFT_Y(plane->crtc_y); + val = VIDOSDxA_TOPLEFT_X(state->crtc.x) | + VIDOSDxA_TOPLEFT_Y(state->crtc.y); writel(val, ctx->regs + VIDOSD_A(win)); - last_x = plane->crtc_x + plane->crtc_w; + last_x = state->crtc.x + state->crtc.w; if (last_x) last_x--; - last_y = plane->crtc_y + plane->crtc_h; + last_y = state->crtc.y + state->crtc.h; if (last_y) last_y--; @@ -466,7 +460,7 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc, writel(val, ctx->regs + VIDOSD_B(win)); DRM_DEBUG_KMS("osd pos: tx = %d, ty = %d, bx = %d, by = %d\n", - plane->crtc_x, plane->crtc_y, last_x, last_y); + state->crtc.x, state->crtc.y, last_x, last_y); /* OSD alpha */ alpha = VIDOSDxC_ALPHA0_R_F(0x0) | @@ -481,7 +475,7 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc, writel(alpha, ctx->regs + VIDOSD_D(win)); - decon_win_set_pixfmt(ctx, win, state->fb); + decon_win_set_pixfmt(ctx, win, fb); /* hardware window 0 doesn't support color key. */ if (win != 0) @@ -505,7 +499,7 @@ static void decon_disable_plane(struct exynos_drm_crtc *crtc, struct exynos_drm_plane *plane) { struct decon_context *ctx = crtc->ctx; - unsigned int win = plane->zpos; + unsigned int win = plane->index; u32 val; if (ctx->suspended) @@ -524,15 +518,16 @@ static void decon_disable_plane(struct exynos_drm_crtc *crtc, writel(val, ctx->regs + DECON_UPDATE); } -static void decon_atomic_flush(struct exynos_drm_crtc *crtc, - struct exynos_drm_plane *plane) +static void decon_atomic_flush(struct exynos_drm_crtc *crtc) { struct decon_context *ctx = crtc->ctx; + int i; if (ctx->suspended) return; - decon_shadow_protect_win(ctx, plane->zpos, false); + for (i = 0; i < WINDOWS_NR; i++) + decon_shadow_protect_win(ctx, i, false); } static void decon_init(struct decon_context *ctx) @@ -555,39 +550,12 @@ static void decon_init(struct decon_context *ctx) static void decon_enable(struct exynos_drm_crtc *crtc) { struct decon_context *ctx = crtc->ctx; - int ret; if (!ctx->suspended) return; - ctx->suspended = false; - pm_runtime_get_sync(ctx->dev); - ret = clk_prepare_enable(ctx->pclk); - if (ret < 0) { - DRM_ERROR("Failed to prepare_enable the pclk [%d]\n", ret); - return; - } - - ret = clk_prepare_enable(ctx->aclk); - if (ret < 0) { - DRM_ERROR("Failed to prepare_enable the aclk [%d]\n", ret); - return; - } - - ret = clk_prepare_enable(ctx->eclk); - if (ret < 0) { - DRM_ERROR("Failed to prepare_enable the eclk [%d]\n", ret); - return; - } - - ret = clk_prepare_enable(ctx->vclk); - if (ret < 0) { - DRM_ERROR("Failed to prepare_enable the vclk [%d]\n", ret); - return; - } - decon_init(ctx); /* if vblank was enabled status, enable it again. */ @@ -595,6 +563,8 @@ static void decon_enable(struct exynos_drm_crtc *crtc) decon_enable_vblank(ctx->crtc); decon_commit(ctx->crtc); + + ctx->suspended = false; } static void decon_disable(struct exynos_drm_crtc *crtc) @@ -613,11 +583,6 @@ static void decon_disable(struct exynos_drm_crtc *crtc) for (i = 0; i < WINDOWS_NR; i++) decon_disable_plane(crtc, &ctx->planes[i]); - clk_disable_unprepare(ctx->vclk); - clk_disable_unprepare(ctx->eclk); - clk_disable_unprepare(ctx->aclk); - clk_disable_unprepare(ctx->pclk); - pm_runtime_put_sync(ctx->dev); ctx->suspended = true; @@ -679,8 +644,7 @@ static int decon_bind(struct device *dev, struct device *master, void *data) struct decon_context *ctx = dev_get_drvdata(dev); struct drm_device *drm_dev = data; struct exynos_drm_plane *exynos_plane; - enum drm_plane_type type; - unsigned int zpos; + unsigned int i; int ret; ret = decon_ctx_initialize(ctx, drm_dev); @@ -689,11 +653,14 @@ static int decon_bind(struct device *dev, struct device *master, void *data) return ret; } - for (zpos = 0; zpos < WINDOWS_NR; zpos++) { - type = exynos_plane_get_type(zpos, CURSOR_WIN); - ret = exynos_plane_init(drm_dev, &ctx->planes[zpos], - 1 << ctx->pipe, type, decon_formats, - ARRAY_SIZE(decon_formats), zpos); + for (i = 0; i < WINDOWS_NR; i++) { + ctx->configs[i].pixel_formats = decon_formats; + ctx->configs[i].num_pixel_formats = ARRAY_SIZE(decon_formats); + ctx->configs[i].zpos = i; + ctx->configs[i].type = decon_win_types[i]; + + ret = exynos_plane_init(drm_dev, &ctx->planes[i], i, + 1 << ctx->pipe, &ctx->configs[i]); if (ret) return ret; } @@ -843,11 +810,63 @@ static int decon_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM +static int exynos7_decon_suspend(struct device *dev) +{ + struct decon_context *ctx = dev_get_drvdata(dev); + + clk_disable_unprepare(ctx->vclk); + clk_disable_unprepare(ctx->eclk); + clk_disable_unprepare(ctx->aclk); + clk_disable_unprepare(ctx->pclk); + + return 0; +} + +static int exynos7_decon_resume(struct device *dev) +{ + struct decon_context *ctx = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(ctx->pclk); + if (ret < 0) { + DRM_ERROR("Failed to prepare_enable the pclk [%d]\n", ret); + return ret; + } + + ret = clk_prepare_enable(ctx->aclk); + if (ret < 0) { + DRM_ERROR("Failed to prepare_enable the aclk [%d]\n", ret); + return ret; + } + + ret = clk_prepare_enable(ctx->eclk); + if (ret < 0) { + DRM_ERROR("Failed to prepare_enable the eclk [%d]\n", ret); + return ret; + } + + ret = clk_prepare_enable(ctx->vclk); + if (ret < 0) { + DRM_ERROR("Failed to prepare_enable the vclk [%d]\n", ret); + return ret; + } + + return 0; +} +#endif + +static const struct dev_pm_ops exynos7_decon_pm_ops = { + SET_RUNTIME_PM_OPS(exynos7_decon_suspend, exynos7_decon_resume, + NULL) +}; + struct platform_driver decon_driver = { .probe = decon_probe, .remove = decon_remove, .driver = { .name = "exynos-decon", + .pm = &exynos7_decon_pm_ops, .of_match_table = decon_driver_dt_match, }, }; diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.c b/drivers/gpu/drm/exynos/exynos_dp_core.c index 124fb9a..b79c316 100644 --- a/drivers/gpu/drm/exynos/exynos_dp_core.c +++ b/drivers/gpu/drm/exynos/exynos_dp_core.c @@ -953,7 +953,7 @@ static void exynos_dp_connector_destroy(struct drm_connector *connector) drm_connector_cleanup(connector); } -static struct drm_connector_funcs exynos_dp_connector_funcs = { +static const struct drm_connector_funcs exynos_dp_connector_funcs = { .dpms = drm_atomic_helper_connector_dpms, .fill_modes = drm_helper_probe_single_connector_modes, .detect = exynos_dp_detect, @@ -998,7 +998,7 @@ static struct drm_encoder *exynos_dp_best_encoder( return &dp->encoder; } -static struct drm_connector_helper_funcs exynos_dp_connector_helper_funcs = { +static const struct drm_connector_helper_funcs exynos_dp_connector_helper_funcs = { .get_modes = exynos_dp_get_modes, .best_encoder = exynos_dp_best_encoder, }; @@ -1009,9 +1009,9 @@ static int exynos_drm_attach_lcd_bridge(struct exynos_dp_device *dp, { int ret; - encoder->bridge = dp->bridge; - dp->bridge->encoder = encoder; - ret = drm_bridge_attach(encoder->dev, dp->bridge); + encoder->bridge->next = dp->ptn_bridge; + dp->ptn_bridge->encoder = encoder; + ret = drm_bridge_attach(encoder->dev, dp->ptn_bridge); if (ret) { DRM_ERROR("Failed to attach bridge to drm\n"); return ret; @@ -1020,14 +1020,15 @@ static int exynos_drm_attach_lcd_bridge(struct exynos_dp_device *dp, return 0; } -static int exynos_dp_create_connector(struct drm_encoder *encoder) +static int exynos_dp_bridge_attach(struct drm_bridge *bridge) { - struct exynos_dp_device *dp = encoder_to_dp(encoder); + struct exynos_dp_device *dp = bridge->driver_private; + struct drm_encoder *encoder = &dp->encoder; struct drm_connector *connector = &dp->connector; int ret; /* Pre-empt DP connector creation if there's a bridge */ - if (dp->bridge) { + if (dp->ptn_bridge) { ret = exynos_drm_attach_lcd_bridge(dp, encoder); if (!ret) return 0; @@ -1052,27 +1053,16 @@ static int exynos_dp_create_connector(struct drm_encoder *encoder) return ret; } -static bool exynos_dp_mode_fixup(struct drm_encoder *encoder, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - return true; -} - -static void exynos_dp_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ -} - -static void exynos_dp_enable(struct drm_encoder *encoder) +static void exynos_dp_bridge_enable(struct drm_bridge *bridge) { - struct exynos_dp_device *dp = encoder_to_dp(encoder); + struct exynos_dp_device *dp = bridge->driver_private; struct exynos_drm_crtc *crtc = dp_to_crtc(dp); if (dp->dpms_mode == DRM_MODE_DPMS_ON) return; + pm_runtime_get_sync(dp->dev); + if (dp->panel) { if (drm_panel_prepare(dp->panel)) { DRM_ERROR("failed to setup the panel\n"); @@ -1083,7 +1073,6 @@ static void exynos_dp_enable(struct drm_encoder *encoder) if (crtc->ops->clock_enable) crtc->ops->clock_enable(dp_to_crtc(dp), true); - clk_prepare_enable(dp->clock); phy_power_on(dp->phy); exynos_dp_init_dp(dp); enable_irq(dp->irq); @@ -1092,9 +1081,9 @@ static void exynos_dp_enable(struct drm_encoder *encoder) dp->dpms_mode = DRM_MODE_DPMS_ON; } -static void exynos_dp_disable(struct drm_encoder *encoder) +static void exynos_dp_bridge_disable(struct drm_bridge *bridge) { - struct exynos_dp_device *dp = encoder_to_dp(encoder); + struct exynos_dp_device *dp = bridge->driver_private; struct exynos_drm_crtc *crtc = dp_to_crtc(dp); if (dp->dpms_mode != DRM_MODE_DPMS_ON) @@ -1110,7 +1099,6 @@ static void exynos_dp_disable(struct drm_encoder *encoder) disable_irq(dp->irq); flush_work(&dp->hotplug_work); phy_power_off(dp->phy); - clk_disable_unprepare(dp->clock); if (crtc->ops->clock_enable) crtc->ops->clock_enable(dp_to_crtc(dp), false); @@ -1120,17 +1108,82 @@ static void exynos_dp_disable(struct drm_encoder *encoder) DRM_ERROR("failed to turnoff the panel\n"); } + pm_runtime_put_sync(dp->dev); + dp->dpms_mode = DRM_MODE_DPMS_OFF; } -static struct drm_encoder_helper_funcs exynos_dp_encoder_helper_funcs = { +static void exynos_dp_bridge_nop(struct drm_bridge *bridge) +{ + /* do nothing */ +} + +static const struct drm_bridge_funcs exynos_dp_bridge_funcs = { + .enable = exynos_dp_bridge_enable, + .disable = exynos_dp_bridge_disable, + .pre_enable = exynos_dp_bridge_nop, + .post_disable = exynos_dp_bridge_nop, + .attach = exynos_dp_bridge_attach, +}; + +static int exynos_dp_create_connector(struct drm_encoder *encoder) +{ + struct exynos_dp_device *dp = encoder_to_dp(encoder); + struct drm_device *drm_dev = dp->drm_dev; + struct drm_bridge *bridge; + int ret; + + bridge = devm_kzalloc(drm_dev->dev, sizeof(*bridge), GFP_KERNEL); + if (!bridge) { + DRM_ERROR("failed to allocate for drm bridge\n"); + return -ENOMEM; + } + + dp->bridge = bridge; + + encoder->bridge = bridge; + bridge->driver_private = dp; + bridge->encoder = encoder; + bridge->funcs = &exynos_dp_bridge_funcs; + + ret = drm_bridge_attach(drm_dev, bridge); + if (ret) { + DRM_ERROR("failed to attach drm bridge\n"); + return -EINVAL; + } + + return 0; +} + +static bool exynos_dp_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static void exynos_dp_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ +} + +static void exynos_dp_enable(struct drm_encoder *encoder) +{ +} + +static void exynos_dp_disable(struct drm_encoder *encoder) +{ +} + +static const struct drm_encoder_helper_funcs exynos_dp_encoder_helper_funcs = { .mode_fixup = exynos_dp_mode_fixup, .mode_set = exynos_dp_mode_set, .enable = exynos_dp_enable, .disable = exynos_dp_disable, }; -static struct drm_encoder_funcs exynos_dp_encoder_funcs = { +static const struct drm_encoder_funcs exynos_dp_encoder_funcs = { .destroy = drm_encoder_cleanup, }; @@ -1238,7 +1291,7 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data) } } - if (!dp->panel && !dp->bridge) { + if (!dp->panel && !dp->ptn_bridge) { ret = exynos_dp_dt_parse_panel(dp); if (ret) return ret; @@ -1289,10 +1342,6 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data) INIT_WORK(&dp->hotplug_work, exynos_dp_hotplug); - phy_power_on(dp->phy); - - exynos_dp_init_dp(dp); - ret = devm_request_irq(&pdev->dev, dp->irq, exynos_dp_irq_handler, irq_flags, "exynos-dp", dp); if (ret) { @@ -1313,7 +1362,7 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data) DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs); drm_encoder_init(drm_dev, encoder, &exynos_dp_encoder_funcs, - DRM_MODE_ENCODER_TMDS); + DRM_MODE_ENCODER_TMDS, NULL); drm_encoder_helper_add(encoder, &exynos_dp_encoder_helper_funcs); @@ -1343,8 +1392,9 @@ static const struct component_ops exynos_dp_ops = { static int exynos_dp_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct device_node *panel_node, *bridge_node, *endpoint; + struct device_node *panel_node = NULL, *bridge_node, *endpoint = NULL; struct exynos_dp_device *dp; + int ret; dp = devm_kzalloc(&pdev->dev, sizeof(struct exynos_dp_device), GFP_KERNEL); @@ -1353,36 +1403,96 @@ static int exynos_dp_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dp); + /* This is for the backward compatibility. */ panel_node = of_parse_phandle(dev->of_node, "panel", 0); if (panel_node) { dp->panel = of_drm_find_panel(panel_node); of_node_put(panel_node); if (!dp->panel) return -EPROBE_DEFER; + } else { + endpoint = of_graph_get_next_endpoint(dev->of_node, NULL); + if (endpoint) { + panel_node = of_graph_get_remote_port_parent(endpoint); + if (panel_node) { + dp->panel = of_drm_find_panel(panel_node); + of_node_put(panel_node); + if (!dp->panel) + return -EPROBE_DEFER; + } else { + DRM_ERROR("no port node for panel device.\n"); + return -EINVAL; + } + } } + if (endpoint) + goto out; + endpoint = of_graph_get_next_endpoint(dev->of_node, NULL); if (endpoint) { bridge_node = of_graph_get_remote_port_parent(endpoint); if (bridge_node) { - dp->bridge = of_drm_find_bridge(bridge_node); + dp->ptn_bridge = of_drm_find_bridge(bridge_node); of_node_put(bridge_node); - if (!dp->bridge) + if (!dp->ptn_bridge) return -EPROBE_DEFER; } else return -EPROBE_DEFER; } - return component_add(&pdev->dev, &exynos_dp_ops); +out: + pm_runtime_enable(dev); + + ret = component_add(&pdev->dev, &exynos_dp_ops); + if (ret) + goto err_disable_pm_runtime; + + return ret; + +err_disable_pm_runtime: + pm_runtime_disable(dev); + + return ret; } static int exynos_dp_remove(struct platform_device *pdev) { + pm_runtime_disable(&pdev->dev); component_del(&pdev->dev, &exynos_dp_ops); return 0; } +#ifdef CONFIG_PM +static int exynos_dp_suspend(struct device *dev) +{ + struct exynos_dp_device *dp = dev_get_drvdata(dev); + + clk_disable_unprepare(dp->clock); + + return 0; +} + +static int exynos_dp_resume(struct device *dev) +{ + struct exynos_dp_device *dp = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(dp->clock); + if (ret < 0) { + DRM_ERROR("Failed to prepare_enable the clock clk [%d]\n", ret); + return ret; + } + + return 0; +} +#endif + +static const struct dev_pm_ops exynos_dp_pm_ops = { + SET_RUNTIME_PM_OPS(exynos_dp_suspend, exynos_dp_resume, NULL) +}; + static const struct of_device_id exynos_dp_match[] = { { .compatible = "samsung,exynos5-dp" }, {}, @@ -1395,6 +1505,7 @@ struct platform_driver dp_driver = { .driver = { .name = "exynos-dp", .owner = THIS_MODULE, + .pm = &exynos_dp_pm_ops, .of_match_table = exynos_dp_match, }, }; diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.h b/drivers/gpu/drm/exynos/exynos_dp_core.h index e413b6f..66eec4b 100644 --- a/drivers/gpu/drm/exynos/exynos_dp_core.h +++ b/drivers/gpu/drm/exynos/exynos_dp_core.h @@ -153,6 +153,7 @@ struct exynos_dp_device { struct drm_connector connector; struct drm_panel *panel; struct drm_bridge *bridge; + struct drm_bridge *ptn_bridge; struct clk *clock; unsigned int irq; void __iomem *reg_base; diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index e693571..e36579c 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -68,35 +68,23 @@ static void exynos_crtc_atomic_begin(struct drm_crtc *crtc, struct drm_crtc_state *old_crtc_state) { struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); - struct drm_plane *plane; exynos_crtc->event = crtc->state->event; - drm_atomic_crtc_for_each_plane(plane, crtc) { - struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane); - - if (exynos_crtc->ops->atomic_begin) - exynos_crtc->ops->atomic_begin(exynos_crtc, - exynos_plane); - } + if (exynos_crtc->ops->atomic_begin) + exynos_crtc->ops->atomic_begin(exynos_crtc); } static void exynos_crtc_atomic_flush(struct drm_crtc *crtc, struct drm_crtc_state *old_crtc_state) { struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); - struct drm_plane *plane; - drm_atomic_crtc_for_each_plane(plane, crtc) { - struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane); - - if (exynos_crtc->ops->atomic_flush) - exynos_crtc->ops->atomic_flush(exynos_crtc, - exynos_plane); - } + if (exynos_crtc->ops->atomic_flush) + exynos_crtc->ops->atomic_flush(exynos_crtc); } -static struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = { +static const struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = { .enable = exynos_drm_crtc_enable, .disable = exynos_drm_crtc_disable, .mode_set_nofb = exynos_drm_crtc_mode_set_nofb, @@ -116,7 +104,7 @@ static void exynos_drm_crtc_destroy(struct drm_crtc *crtc) kfree(exynos_crtc); } -static struct drm_crtc_funcs exynos_crtc_funcs = { +static const struct drm_crtc_funcs exynos_crtc_funcs = { .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, .destroy = exynos_drm_crtc_destroy, @@ -153,7 +141,7 @@ struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev, private->crtc[pipe] = crtc; ret = drm_crtc_init_with_planes(drm_dev, crtc, plane, NULL, - &exynos_crtc_funcs); + &exynos_crtc_funcs, NULL); if (ret < 0) goto err_crtc; @@ -215,29 +203,6 @@ void exynos_drm_crtc_finish_update(struct exynos_drm_crtc *exynos_crtc, spin_unlock_irqrestore(&crtc->dev->event_lock, flags); } -void exynos_drm_crtc_complete_scanout(struct drm_framebuffer *fb) -{ - struct exynos_drm_crtc *exynos_crtc; - struct drm_device *dev = fb->dev; - struct drm_crtc *crtc; - - /* - * make sure that overlay data are updated to real hardware - * for all encoders. - */ - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - exynos_crtc = to_exynos_crtc(crtc); - - /* - * wait for vblank interrupt - * - this makes sure that overlay data are updated to - * real hardware. - */ - if (exynos_crtc->ops->wait_for_vblank) - exynos_crtc->ops->wait_for_vblank(exynos_crtc); - } -} - int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev, enum exynos_drm_output_type out_type) { @@ -261,3 +226,29 @@ void exynos_drm_crtc_te_handler(struct drm_crtc *crtc) if (exynos_crtc->ops->te_handler) exynos_crtc->ops->te_handler(exynos_crtc); } + +void exynos_drm_crtc_cancel_page_flip(struct drm_crtc *crtc, + struct drm_file *file) +{ + struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); + struct drm_pending_vblank_event *e; + unsigned long flags; + + spin_lock_irqsave(&crtc->dev->event_lock, flags); + e = exynos_crtc->event; + if (e && e->base.file_priv == file) { + exynos_crtc->event = NULL; + /* + * event will be destroyed by core part + * so below line should be removed later with core changes + */ + e->base.destroy(&e->base); + /* + * event_space will be increased by core part + * so below line should be removed later with core changes. + */ + file->event_space += sizeof(e->event); + atomic_dec(&exynos_crtc->pending_update); + } + spin_unlock_irqrestore(&crtc->dev->event_lock, flags); +} diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.h b/drivers/gpu/drm/exynos/exynos_drm_crtc.h index f9f365b..cfdcf3e 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.h +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.h @@ -28,7 +28,6 @@ void exynos_drm_crtc_disable_vblank(struct drm_device *dev, unsigned int pipe); void exynos_drm_crtc_wait_pending_update(struct exynos_drm_crtc *exynos_crtc); void exynos_drm_crtc_finish_update(struct exynos_drm_crtc *exynos_crtc, struct exynos_drm_plane *exynos_plane); -void exynos_drm_crtc_complete_scanout(struct drm_framebuffer *fb); /* This function gets pipe value to crtc device matched with out_type. */ int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev, @@ -41,4 +40,8 @@ int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev, */ void exynos_drm_crtc_te_handler(struct drm_crtc *crtc); +/* This function cancels a page flip request. */ +void exynos_drm_crtc_cancel_page_flip(struct drm_crtc *crtc, + struct drm_file *file); + #endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_dpi.c b/drivers/gpu/drm/exynos/exynos_drm_dpi.c index c748b87..05350ae 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dpi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dpi.c @@ -57,7 +57,7 @@ static void exynos_dpi_connector_destroy(struct drm_connector *connector) drm_connector_cleanup(connector); } -static struct drm_connector_funcs exynos_dpi_connector_funcs = { +static const struct drm_connector_funcs exynos_dpi_connector_funcs = { .dpms = drm_atomic_helper_connector_dpms, .detect = exynos_dpi_detect, .fill_modes = drm_helper_probe_single_connector_modes, @@ -100,7 +100,7 @@ exynos_dpi_best_encoder(struct drm_connector *connector) return &ctx->encoder; } -static struct drm_connector_helper_funcs exynos_dpi_connector_helper_funcs = { +static const struct drm_connector_helper_funcs exynos_dpi_connector_helper_funcs = { .get_modes = exynos_dpi_get_modes, .best_encoder = exynos_dpi_best_encoder, }; @@ -161,14 +161,14 @@ static void exynos_dpi_disable(struct drm_encoder *encoder) } } -static struct drm_encoder_helper_funcs exynos_dpi_encoder_helper_funcs = { +static const struct drm_encoder_helper_funcs exynos_dpi_encoder_helper_funcs = { .mode_fixup = exynos_dpi_mode_fixup, .mode_set = exynos_dpi_mode_set, .enable = exynos_dpi_enable, .disable = exynos_dpi_disable, }; -static struct drm_encoder_funcs exynos_dpi_encoder_funcs = { +static const struct drm_encoder_funcs exynos_dpi_encoder_funcs = { .destroy = drm_encoder_cleanup, }; @@ -309,7 +309,7 @@ int exynos_dpi_bind(struct drm_device *dev, struct drm_encoder *encoder) DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs); drm_encoder_init(dev, encoder, &exynos_dpi_encoder_funcs, - DRM_MODE_ENCODER_TMDS); + DRM_MODE_ENCODER_TMDS, NULL); drm_encoder_helper_add(encoder, &exynos_dpi_encoder_helper_funcs); diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 2c6019d..68f0f36 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -304,45 +304,6 @@ int exynos_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state, return 0; } -#ifdef CONFIG_PM_SLEEP -static int exynos_drm_suspend(struct drm_device *dev, pm_message_t state) -{ - struct drm_connector *connector; - - drm_modeset_lock_all(dev); - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - int old_dpms = connector->dpms; - - if (connector->funcs->dpms) - connector->funcs->dpms(connector, DRM_MODE_DPMS_OFF); - - /* Set the old mode back to the connector for resume */ - connector->dpms = old_dpms; - } - drm_modeset_unlock_all(dev); - - return 0; -} - -static int exynos_drm_resume(struct drm_device *dev) -{ - struct drm_connector *connector; - - drm_modeset_lock_all(dev); - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - if (connector->funcs->dpms) { - int dpms = connector->dpms; - - connector->dpms = DRM_MODE_DPMS_OFF; - connector->funcs->dpms(connector, dpms); - } - } - drm_modeset_unlock_all(dev); - - return 0; -} -#endif - static int exynos_drm_open(struct drm_device *dev, struct drm_file *file) { struct drm_exynos_file_private *file_priv; @@ -369,7 +330,12 @@ err_file_priv_free: static void exynos_drm_preclose(struct drm_device *dev, struct drm_file *file) { + struct drm_crtc *crtc; + exynos_drm_subdrv_close(dev, file); + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) + exynos_drm_crtc_cancel_page_flip(crtc, file); } static void exynos_drm_postclose(struct drm_device *dev, struct drm_file *file) @@ -476,31 +442,54 @@ static struct drm_driver exynos_drm_driver = { }; #ifdef CONFIG_PM_SLEEP -static int exynos_drm_sys_suspend(struct device *dev) +static int exynos_drm_suspend(struct device *dev) { struct drm_device *drm_dev = dev_get_drvdata(dev); - pm_message_t message; + struct drm_connector *connector; if (pm_runtime_suspended(dev) || !drm_dev) return 0; - message.event = PM_EVENT_SUSPEND; - return exynos_drm_suspend(drm_dev, message); + drm_modeset_lock_all(drm_dev); + drm_for_each_connector(connector, drm_dev) { + int old_dpms = connector->dpms; + + if (connector->funcs->dpms) + connector->funcs->dpms(connector, DRM_MODE_DPMS_OFF); + + /* Set the old mode back to the connector for resume */ + connector->dpms = old_dpms; + } + drm_modeset_unlock_all(drm_dev); + + return 0; } -static int exynos_drm_sys_resume(struct device *dev) +static int exynos_drm_resume(struct device *dev) { struct drm_device *drm_dev = dev_get_drvdata(dev); + struct drm_connector *connector; if (pm_runtime_suspended(dev) || !drm_dev) return 0; - return exynos_drm_resume(drm_dev); + drm_modeset_lock_all(drm_dev); + drm_for_each_connector(connector, drm_dev) { + if (connector->funcs->dpms) { + int dpms = connector->dpms; + + connector->dpms = DRM_MODE_DPMS_OFF; + connector->funcs->dpms(connector, dpms); + } + } + drm_modeset_unlock_all(drm_dev); + + return 0; } #endif static const struct dev_pm_ops exynos_drm_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(exynos_drm_sys_suspend, exynos_drm_sys_resume) + SET_SYSTEM_SLEEP_PM_OPS(exynos_drm_suspend, exynos_drm_resume) }; /* forward declaration */ diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index f1eda7f..17b5ded 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -38,25 +38,46 @@ enum exynos_drm_output_type { EXYNOS_DISPLAY_TYPE_VIDI, }; +struct exynos_drm_rect { + unsigned int x, y; + unsigned int w, h; +}; + /* - * Exynos drm common overlay structure. + * Exynos drm plane state structure. * - * @base: plane object - * @src_x: offset x on a framebuffer to be displayed. - * - the unit is screen coordinates. - * @src_y: offset y on a framebuffer to be displayed. - * - the unit is screen coordinates. - * @src_w: width of a partial image to be displayed from framebuffer. - * @src_h: height of a partial image to be displayed from framebuffer. - * @crtc_x: offset x on hardware screen. - * @crtc_y: offset y on hardware screen. - * @crtc_w: window width to be displayed (hardware screen). - * @crtc_h: window height to be displayed (hardware screen). + * @base: plane_state object (contains drm_framebuffer pointer) + * @src: rectangle of the source image data to be displayed (clipped to + * visible part). + * @crtc: rectangle of the target image position on hardware screen + * (clipped to visible part). * @h_ratio: horizontal scaling ratio, 16.16 fixed point * @v_ratio: vertical scaling ratio, 16.16 fixed point - * @dma_addr: array of bus(accessed by dma) address to the memory region - * allocated for a overlay. - * @zpos: order of overlay layer(z position). + * + * this structure consists plane state data that will be applied to hardware + * specific overlay info. + */ + +struct exynos_drm_plane_state { + struct drm_plane_state base; + struct exynos_drm_rect crtc; + struct exynos_drm_rect src; + unsigned int h_ratio; + unsigned int v_ratio; + unsigned int zpos; +}; + +static inline struct exynos_drm_plane_state * +to_exynos_plane_state(struct drm_plane_state *state) +{ + return container_of(state, struct exynos_drm_plane_state, base); +} + +/* + * Exynos drm common overlay structure. + * + * @base: plane object + * @index: hardware index of the overlay layer * * this structure is common to exynos SoC and its contents would be copied * to hardware specific overlay info. @@ -64,21 +85,33 @@ enum exynos_drm_output_type { struct exynos_drm_plane { struct drm_plane base; - unsigned int src_x; - unsigned int src_y; - unsigned int src_w; - unsigned int src_h; - unsigned int crtc_x; - unsigned int crtc_y; - unsigned int crtc_w; - unsigned int crtc_h; - unsigned int h_ratio; - unsigned int v_ratio; - dma_addr_t dma_addr[MAX_FB_BUFFER]; - unsigned int zpos; + const struct exynos_drm_plane_config *config; + unsigned int index; struct drm_framebuffer *pending_fb; }; +#define EXYNOS_DRM_PLANE_CAP_DOUBLE (1 << 0) +#define EXYNOS_DRM_PLANE_CAP_SCALE (1 << 1) +#define EXYNOS_DRM_PLANE_CAP_ZPOS (1 << 2) + +/* + * Exynos DRM plane configuration structure. + * + * @zpos: initial z-position of the plane. + * @type: type of the plane (primary, cursor or overlay). + * @pixel_formats: supported pixel formats. + * @num_pixel_formats: number of elements in 'pixel_formats'. + * @capabilities: supported features (see EXYNOS_DRM_PLANE_CAP_*) + */ + +struct exynos_drm_plane_config { + unsigned int zpos; + enum drm_plane_type type; + const uint32_t *pixel_formats; + unsigned int num_pixel_formats; + unsigned int capabilities; +}; + /* * Exynos drm crtc ops * @@ -90,8 +123,8 @@ struct exynos_drm_plane { * @wait_for_vblank: wait for vblank interrupt to make sure that * hardware overlay is updated. * @atomic_check: validate state - * @atomic_begin: prepare a window to receive a update - * @atomic_flush: mark the end of a window update + * @atomic_begin: prepare device to receive an update + * @atomic_flush: mark the end of device update * @update_plane: apply hardware specific overlay data to registers. * @disable_plane: disable hardware specific overlay. * @te_handler: trigger to transfer video image at the tearing effect @@ -111,14 +144,12 @@ struct exynos_drm_crtc_ops { void (*wait_for_vblank)(struct exynos_drm_crtc *crtc); int (*atomic_check)(struct exynos_drm_crtc *crtc, struct drm_crtc_state *state); - void (*atomic_begin)(struct exynos_drm_crtc *crtc, - struct exynos_drm_plane *plane); + void (*atomic_begin)(struct exynos_drm_crtc *crtc); void (*update_plane)(struct exynos_drm_crtc *crtc, struct exynos_drm_plane *plane); void (*disable_plane)(struct exynos_drm_crtc *crtc, struct exynos_drm_plane *plane); - void (*atomic_flush)(struct exynos_drm_crtc *crtc, - struct exynos_drm_plane *plane); + void (*atomic_flush)(struct exynos_drm_crtc *crtc); void (*te_handler)(struct exynos_drm_crtc *crtc); void (*clock_enable)(struct exynos_drm_crtc *crtc, bool enable); }; diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index 12b03b3..d84a498 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -1458,66 +1458,6 @@ static const struct mipi_dsi_host_ops exynos_dsi_ops = { .transfer = exynos_dsi_host_transfer, }; -static int exynos_dsi_poweron(struct exynos_dsi *dsi) -{ - struct exynos_dsi_driver_data *driver_data = dsi->driver_data; - int ret, i; - - ret = regulator_bulk_enable(ARRAY_SIZE(dsi->supplies), dsi->supplies); - if (ret < 0) { - dev_err(dsi->dev, "cannot enable regulators %d\n", ret); - return ret; - } - - for (i = 0; i < driver_data->num_clks; i++) { - ret = clk_prepare_enable(dsi->clks[i]); - if (ret < 0) - goto err_clk; - } - - ret = phy_power_on(dsi->phy); - if (ret < 0) { - dev_err(dsi->dev, "cannot enable phy %d\n", ret); - goto err_clk; - } - - return 0; - -err_clk: - while (--i > -1) - clk_disable_unprepare(dsi->clks[i]); - regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies); - - return ret; -} - -static void exynos_dsi_poweroff(struct exynos_dsi *dsi) -{ - struct exynos_dsi_driver_data *driver_data = dsi->driver_data; - int ret, i; - - usleep_range(10000, 20000); - - if (dsi->state & DSIM_STATE_INITIALIZED) { - dsi->state &= ~DSIM_STATE_INITIALIZED; - - exynos_dsi_disable_clock(dsi); - - exynos_dsi_disable_irq(dsi); - } - - dsi->state &= ~DSIM_STATE_CMD_LPM; - - phy_power_off(dsi->phy); - - for (i = driver_data->num_clks - 1; i > -1; i--) - clk_disable_unprepare(dsi->clks[i]); - - ret = regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies); - if (ret < 0) - dev_err(dsi->dev, "cannot disable regulators %d\n", ret); -} - static void exynos_dsi_enable(struct drm_encoder *encoder) { struct exynos_dsi *dsi = encoder_to_dsi(encoder); @@ -1526,16 +1466,14 @@ static void exynos_dsi_enable(struct drm_encoder *encoder) if (dsi->state & DSIM_STATE_ENABLED) return; - ret = exynos_dsi_poweron(dsi); - if (ret < 0) - return; + pm_runtime_get_sync(dsi->dev); dsi->state |= DSIM_STATE_ENABLED; ret = drm_panel_prepare(dsi->panel); if (ret < 0) { dsi->state &= ~DSIM_STATE_ENABLED; - exynos_dsi_poweroff(dsi); + pm_runtime_put_sync(dsi->dev); return; } @@ -1547,7 +1485,7 @@ static void exynos_dsi_enable(struct drm_encoder *encoder) dsi->state &= ~DSIM_STATE_ENABLED; exynos_dsi_set_display_enable(dsi, false); drm_panel_unprepare(dsi->panel); - exynos_dsi_poweroff(dsi); + pm_runtime_put_sync(dsi->dev); return; } @@ -1569,7 +1507,7 @@ static void exynos_dsi_disable(struct drm_encoder *encoder) dsi->state &= ~DSIM_STATE_ENABLED; - exynos_dsi_poweroff(dsi); + pm_runtime_put_sync(dsi->dev); } static enum drm_connector_status @@ -1603,7 +1541,7 @@ static void exynos_dsi_connector_destroy(struct drm_connector *connector) connector->dev = NULL; } -static struct drm_connector_funcs exynos_dsi_connector_funcs = { +static const struct drm_connector_funcs exynos_dsi_connector_funcs = { .dpms = drm_atomic_helper_connector_dpms, .detect = exynos_dsi_detect, .fill_modes = drm_helper_probe_single_connector_modes, @@ -1631,7 +1569,7 @@ exynos_dsi_best_encoder(struct drm_connector *connector) return &dsi->encoder; } -static struct drm_connector_helper_funcs exynos_dsi_connector_helper_funcs = { +static const struct drm_connector_helper_funcs exynos_dsi_connector_helper_funcs = { .get_modes = exynos_dsi_get_modes, .best_encoder = exynos_dsi_best_encoder, }; @@ -1684,14 +1622,14 @@ static void exynos_dsi_mode_set(struct drm_encoder *encoder, vm->hsync_len = m->hsync_end - m->hsync_start; } -static struct drm_encoder_helper_funcs exynos_dsi_encoder_helper_funcs = { +static const struct drm_encoder_helper_funcs exynos_dsi_encoder_helper_funcs = { .mode_fixup = exynos_dsi_mode_fixup, .mode_set = exynos_dsi_mode_set, .enable = exynos_dsi_enable, .disable = exynos_dsi_disable, }; -static struct drm_encoder_funcs exynos_dsi_encoder_funcs = { +static const struct drm_encoder_funcs exynos_dsi_encoder_funcs = { .destroy = drm_encoder_cleanup, }; @@ -1797,13 +1735,13 @@ static int exynos_dsi_parse_dt(struct exynos_dsi *dsi) ep = of_graph_get_next_endpoint(node, NULL); if (!ep) { - ret = -ENXIO; + ret = -EINVAL; goto end; } dsi->bridge_node = of_graph_get_remote_port_parent(ep); if (!dsi->bridge_node) { - ret = -ENXIO; + ret = -EINVAL; goto end; } end: @@ -1831,7 +1769,7 @@ static int exynos_dsi_bind(struct device *dev, struct device *master, DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs); drm_encoder_init(drm_dev, encoder, &exynos_dsi_encoder_funcs, - DRM_MODE_ENCODER_TMDS); + DRM_MODE_ENCODER_TMDS, NULL); drm_encoder_helper_add(encoder, &exynos_dsi_encoder_helper_funcs); @@ -1954,22 +1892,99 @@ static int exynos_dsi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, &dsi->encoder); + pm_runtime_enable(dev); + return component_add(dev, &exynos_dsi_component_ops); } static int exynos_dsi_remove(struct platform_device *pdev) { + pm_runtime_disable(&pdev->dev); + component_del(&pdev->dev, &exynos_dsi_component_ops); return 0; } +#ifdef CONFIG_PM +static int exynos_dsi_suspend(struct device *dev) +{ + struct drm_encoder *encoder = dev_get_drvdata(dev); + struct exynos_dsi *dsi = encoder_to_dsi(encoder); + struct exynos_dsi_driver_data *driver_data = dsi->driver_data; + int ret, i; + + usleep_range(10000, 20000); + + if (dsi->state & DSIM_STATE_INITIALIZED) { + dsi->state &= ~DSIM_STATE_INITIALIZED; + + exynos_dsi_disable_clock(dsi); + + exynos_dsi_disable_irq(dsi); + } + + dsi->state &= ~DSIM_STATE_CMD_LPM; + + phy_power_off(dsi->phy); + + for (i = driver_data->num_clks - 1; i > -1; i--) + clk_disable_unprepare(dsi->clks[i]); + + ret = regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies); + if (ret < 0) + dev_err(dsi->dev, "cannot disable regulators %d\n", ret); + + return 0; +} + +static int exynos_dsi_resume(struct device *dev) +{ + struct drm_encoder *encoder = dev_get_drvdata(dev); + struct exynos_dsi *dsi = encoder_to_dsi(encoder); + struct exynos_dsi_driver_data *driver_data = dsi->driver_data; + int ret, i; + + ret = regulator_bulk_enable(ARRAY_SIZE(dsi->supplies), dsi->supplies); + if (ret < 0) { + dev_err(dsi->dev, "cannot enable regulators %d\n", ret); + return ret; + } + + for (i = 0; i < driver_data->num_clks; i++) { + ret = clk_prepare_enable(dsi->clks[i]); + if (ret < 0) + goto err_clk; + } + + ret = phy_power_on(dsi->phy); + if (ret < 0) { + dev_err(dsi->dev, "cannot enable phy %d\n", ret); + goto err_clk; + } + + return 0; + +err_clk: + while (--i > -1) + clk_disable_unprepare(dsi->clks[i]); + regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies); + + return ret; +} +#endif + +static const struct dev_pm_ops exynos_dsi_pm_ops = { + SET_RUNTIME_PM_OPS(exynos_dsi_suspend, exynos_dsi_resume, NULL) +}; + struct platform_driver dsi_driver = { .probe = exynos_dsi_probe, .remove = exynos_dsi_remove, .driver = { .name = "exynos-dsi", .owner = THIS_MODULE, + .pm = &exynos_dsi_pm_ops, .of_match_table = exynos_dsi_of_match, }, }; diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.c b/drivers/gpu/drm/exynos/exynos_drm_fb.c index 49b9bc3..d614194 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fb.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fb.c @@ -37,6 +37,7 @@ struct exynos_drm_fb { struct drm_framebuffer fb; struct exynos_drm_gem *exynos_gem[MAX_FB_BUFFER]; + dma_addr_t dma_addr[MAX_FB_BUFFER]; }; static int check_fb_gem_memory_type(struct drm_device *drm_dev, @@ -70,9 +71,6 @@ static void exynos_drm_fb_destroy(struct drm_framebuffer *fb) struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb); unsigned int i; - /* make sure that overlay data are updated before relesing fb. */ - exynos_drm_crtc_complete_scanout(fb); - drm_framebuffer_cleanup(fb); for (i = 0; i < ARRAY_SIZE(exynos_fb->exynos_gem); i++) { @@ -109,7 +107,7 @@ static int exynos_drm_fb_dirty(struct drm_framebuffer *fb, return 0; } -static struct drm_framebuffer_funcs exynos_drm_fb_funcs = { +static const struct drm_framebuffer_funcs exynos_drm_fb_funcs = { .destroy = exynos_drm_fb_destroy, .create_handle = exynos_drm_fb_create_handle, .dirty = exynos_drm_fb_dirty, @@ -135,6 +133,8 @@ exynos_drm_framebuffer_init(struct drm_device *dev, goto err; exynos_fb->exynos_gem[i] = exynos_gem[i]; + exynos_fb->dma_addr[i] = exynos_gem[i]->dma_addr + + mode_cmd->offsets[i]; } drm_helper_mode_fill_fb_struct(&exynos_fb->fb, mode_cmd); @@ -189,21 +189,14 @@ err: return ERR_PTR(ret); } -struct exynos_drm_gem *exynos_drm_fb_gem(struct drm_framebuffer *fb, int index) +dma_addr_t exynos_drm_fb_dma_addr(struct drm_framebuffer *fb, int index) { struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb); - struct exynos_drm_gem *exynos_gem; if (index >= MAX_FB_BUFFER) - return NULL; - - exynos_gem = exynos_fb->exynos_gem[index]; - if (!exynos_gem) - return NULL; - - DRM_DEBUG_KMS("dma_addr: 0x%lx\n", (unsigned long)exynos_gem->dma_addr); + return DMA_ERROR_CODE; - return exynos_gem; + return exynos_fb->dma_addr[index]; } static void exynos_drm_output_poll_changed(struct drm_device *dev) diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.h b/drivers/gpu/drm/exynos/exynos_drm_fb.h index 6fa0e47..3a9e75b 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fb.h +++ b/drivers/gpu/drm/exynos/exynos_drm_fb.h @@ -22,8 +22,7 @@ exynos_drm_framebuffer_init(struct drm_device *dev, struct exynos_drm_gem **exynos_gem, int count); -/* get gem object of a drm framebuffer */ -struct exynos_drm_gem *exynos_drm_fb_gem(struct drm_framebuffer *fb, int index); +dma_addr_t exynos_drm_fb_dma_addr(struct drm_framebuffer *fb, int index); void exynos_drm_mode_config_init(struct drm_device *dev); diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index bd75c15..70194d0 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -29,6 +29,7 @@ #include <drm/exynos_drm.h> #include "exynos_drm_drv.h" +#include "exynos_drm_fb.h" #include "exynos_drm_fbdev.h" #include "exynos_drm_crtc.h" #include "exynos_drm_plane.h" @@ -87,7 +88,6 @@ /* FIMD has totally five hardware windows. */ #define WINDOWS_NR 5 -#define CURSOR_WIN 4 struct fimd_driver_data { unsigned int timing_base; @@ -150,6 +150,7 @@ struct fimd_context { struct drm_device *drm_dev; struct exynos_drm_crtc *crtc; struct exynos_drm_plane planes[WINDOWS_NR]; + struct exynos_drm_plane_config configs[WINDOWS_NR]; struct clk *bus_clk; struct clk *lcd_clk; void __iomem *regs; @@ -187,6 +188,14 @@ static const struct of_device_id fimd_driver_dt_match[] = { }; MODULE_DEVICE_TABLE(of, fimd_driver_dt_match); +static const enum drm_plane_type fimd_win_types[WINDOWS_NR] = { + DRM_PLANE_TYPE_PRIMARY, + DRM_PLANE_TYPE_OVERLAY, + DRM_PLANE_TYPE_OVERLAY, + DRM_PLANE_TYPE_OVERLAY, + DRM_PLANE_TYPE_CURSOR, +}; + static const uint32_t fimd_formats[] = { DRM_FORMAT_C8, DRM_FORMAT_XRGB1555, @@ -478,7 +487,7 @@ static void fimd_commit(struct exynos_drm_crtc *crtc) static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win, - struct drm_framebuffer *fb) + uint32_t pixel_format, int width) { unsigned long val; @@ -489,11 +498,11 @@ static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win, * So the request format is ARGB8888 then change it to XRGB8888. */ if (ctx->driver_data->has_limited_fmt && !win) { - if (fb->pixel_format == DRM_FORMAT_ARGB8888) - fb->pixel_format = DRM_FORMAT_XRGB8888; + if (pixel_format == DRM_FORMAT_ARGB8888) + pixel_format = DRM_FORMAT_XRGB8888; } - switch (fb->pixel_format) { + switch (pixel_format) { case DRM_FORMAT_C8: val |= WINCON0_BPPMODE_8BPP_PALETTE; val |= WINCONx_BURSTLEN_8WORD; @@ -529,17 +538,15 @@ static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win, break; } - DRM_DEBUG_KMS("bpp = %d\n", fb->bits_per_pixel); - /* - * In case of exynos, setting dma-burst to 16Word causes permanent - * tearing for very small buffers, e.g. cursor buffer. Burst Mode - * switching which is based on plane size is not recommended as - * plane size varies alot towards the end of the screen and rapid - * movement causes unstable DMA which results into iommu crash/tear. + * Setting dma-burst to 16Word causes permanent tearing for very small + * buffers, e.g. cursor buffer. Burst Mode switching which based on + * plane size is not recommended as plane size varies alot towards the + * end of the screen and rapid movement causes unstable DMA, but it is + * still better to change dma-burst than displaying garbage. */ - if (fb->width < MIN_FB_WIDTH_FOR_16WORD_BURST) { + if (width < MIN_FB_WIDTH_FOR_16WORD_BURST) { val &= ~WINCONx_BURSTLEN_MASK; val |= WINCONx_BURSTLEN_4WORD; } @@ -615,64 +622,68 @@ static void fimd_shadow_protect_win(struct fimd_context *ctx, writel(val, ctx->regs + reg); } -static void fimd_atomic_begin(struct exynos_drm_crtc *crtc, - struct exynos_drm_plane *plane) +static void fimd_atomic_begin(struct exynos_drm_crtc *crtc) { struct fimd_context *ctx = crtc->ctx; + int i; if (ctx->suspended) return; - fimd_shadow_protect_win(ctx, plane->zpos, true); + for (i = 0; i < WINDOWS_NR; i++) + fimd_shadow_protect_win(ctx, i, true); } -static void fimd_atomic_flush(struct exynos_drm_crtc *crtc, - struct exynos_drm_plane *plane) +static void fimd_atomic_flush(struct exynos_drm_crtc *crtc) { struct fimd_context *ctx = crtc->ctx; + int i; if (ctx->suspended) return; - fimd_shadow_protect_win(ctx, plane->zpos, false); + for (i = 0; i < WINDOWS_NR; i++) + fimd_shadow_protect_win(ctx, i, false); } static void fimd_update_plane(struct exynos_drm_crtc *crtc, struct exynos_drm_plane *plane) { + struct exynos_drm_plane_state *state = + to_exynos_plane_state(plane->base.state); struct fimd_context *ctx = crtc->ctx; - struct drm_plane_state *state = plane->base.state; + struct drm_framebuffer *fb = state->base.fb; dma_addr_t dma_addr; unsigned long val, size, offset; unsigned int last_x, last_y, buf_offsize, line_size; - unsigned int win = plane->zpos; - unsigned int bpp = state->fb->bits_per_pixel >> 3; - unsigned int pitch = state->fb->pitches[0]; + unsigned int win = plane->index; + unsigned int bpp = fb->bits_per_pixel >> 3; + unsigned int pitch = fb->pitches[0]; if (ctx->suspended) return; - offset = plane->src_x * bpp; - offset += plane->src_y * pitch; + offset = state->src.x * bpp; + offset += state->src.y * pitch; /* buffer start address */ - dma_addr = plane->dma_addr[0] + offset; + dma_addr = exynos_drm_fb_dma_addr(fb, 0) + offset; val = (unsigned long)dma_addr; writel(val, ctx->regs + VIDWx_BUF_START(win, 0)); /* buffer end address */ - size = pitch * plane->crtc_h; + size = pitch * state->crtc.h; val = (unsigned long)(dma_addr + size); writel(val, ctx->regs + VIDWx_BUF_END(win, 0)); DRM_DEBUG_KMS("start addr = 0x%lx, end addr = 0x%lx, size = 0x%lx\n", (unsigned long)dma_addr, val, size); DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n", - plane->crtc_w, plane->crtc_h); + state->crtc.w, state->crtc.h); /* buffer size */ - buf_offsize = pitch - (plane->crtc_w * bpp); - line_size = plane->crtc_w * bpp; + buf_offsize = pitch - (state->crtc.w * bpp); + line_size = state->crtc.w * bpp; val = VIDW_BUF_SIZE_OFFSET(buf_offsize) | VIDW_BUF_SIZE_PAGEWIDTH(line_size) | VIDW_BUF_SIZE_OFFSET_E(buf_offsize) | @@ -680,16 +691,16 @@ static void fimd_update_plane(struct exynos_drm_crtc *crtc, writel(val, ctx->regs + VIDWx_BUF_SIZE(win, 0)); /* OSD position */ - val = VIDOSDxA_TOPLEFT_X(plane->crtc_x) | - VIDOSDxA_TOPLEFT_Y(plane->crtc_y) | - VIDOSDxA_TOPLEFT_X_E(plane->crtc_x) | - VIDOSDxA_TOPLEFT_Y_E(plane->crtc_y); + val = VIDOSDxA_TOPLEFT_X(state->crtc.x) | + VIDOSDxA_TOPLEFT_Y(state->crtc.y) | + VIDOSDxA_TOPLEFT_X_E(state->crtc.x) | + VIDOSDxA_TOPLEFT_Y_E(state->crtc.y); writel(val, ctx->regs + VIDOSD_A(win)); - last_x = plane->crtc_x + plane->crtc_w; + last_x = state->crtc.x + state->crtc.w; if (last_x) last_x--; - last_y = plane->crtc_y + plane->crtc_h; + last_y = state->crtc.y + state->crtc.h; if (last_y) last_y--; @@ -699,20 +710,20 @@ static void fimd_update_plane(struct exynos_drm_crtc *crtc, writel(val, ctx->regs + VIDOSD_B(win)); DRM_DEBUG_KMS("osd pos: tx = %d, ty = %d, bx = %d, by = %d\n", - plane->crtc_x, plane->crtc_y, last_x, last_y); + state->crtc.x, state->crtc.y, last_x, last_y); /* OSD size */ if (win != 3 && win != 4) { u32 offset = VIDOSD_D(win); if (win == 0) offset = VIDOSD_C(win); - val = plane->crtc_w * plane->crtc_h; + val = state->crtc.w * state->crtc.h; writel(val, ctx->regs + offset); DRM_DEBUG_KMS("osd size = 0x%x\n", (unsigned int)val); } - fimd_win_set_pixfmt(ctx, win, state->fb); + fimd_win_set_pixfmt(ctx, win, fb->pixel_format, state->src.w); /* hardware window 0 doesn't support color key. */ if (win != 0) @@ -731,7 +742,7 @@ static void fimd_disable_plane(struct exynos_drm_crtc *crtc, struct exynos_drm_plane *plane) { struct fimd_context *ctx = crtc->ctx; - unsigned int win = plane->zpos; + unsigned int win = plane->index; if (ctx->suspended) return; @@ -745,7 +756,6 @@ static void fimd_disable_plane(struct exynos_drm_crtc *crtc, static void fimd_enable(struct exynos_drm_crtc *crtc) { struct fimd_context *ctx = crtc->ctx; - int ret; if (!ctx->suspended) return; @@ -754,18 +764,6 @@ static void fimd_enable(struct exynos_drm_crtc *crtc) pm_runtime_get_sync(ctx->dev); - ret = clk_prepare_enable(ctx->bus_clk); - if (ret < 0) { - DRM_ERROR("Failed to prepare_enable the bus clk [%d]\n", ret); - return; - } - - ret = clk_prepare_enable(ctx->lcd_clk); - if (ret < 0) { - DRM_ERROR("Failed to prepare_enable the lcd clk [%d]\n", ret); - return; - } - /* if vblank was enabled status, enable it again. */ if (test_and_clear_bit(0, &ctx->irq_flags)) fimd_enable_vblank(ctx->crtc); @@ -795,11 +793,7 @@ static void fimd_disable(struct exynos_drm_crtc *crtc) writel(0, ctx->regs + VIDCON0); - clk_disable_unprepare(ctx->lcd_clk); - clk_disable_unprepare(ctx->bus_clk); - pm_runtime_put_sync(ctx->dev); - ctx->suspended = true; } @@ -941,18 +935,19 @@ static int fimd_bind(struct device *dev, struct device *master, void *data) struct drm_device *drm_dev = data; struct exynos_drm_private *priv = drm_dev->dev_private; struct exynos_drm_plane *exynos_plane; - enum drm_plane_type type; - unsigned int zpos; + unsigned int i; int ret; ctx->drm_dev = drm_dev; ctx->pipe = priv->pipe++; - for (zpos = 0; zpos < WINDOWS_NR; zpos++) { - type = exynos_plane_get_type(zpos, CURSOR_WIN); - ret = exynos_plane_init(drm_dev, &ctx->planes[zpos], - 1 << ctx->pipe, type, fimd_formats, - ARRAY_SIZE(fimd_formats), zpos); + for (i = 0; i < WINDOWS_NR; i++) { + ctx->configs[i].pixel_formats = fimd_formats; + ctx->configs[i].num_pixel_formats = ARRAY_SIZE(fimd_formats); + ctx->configs[i].zpos = i; + ctx->configs[i].type = fimd_win_types[i]; + ret = exynos_plane_init(drm_dev, &ctx->planes[i], i, + 1 << ctx->pipe, &ctx->configs[i]); if (ret) return ret; } @@ -1121,12 +1116,49 @@ static int fimd_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM +static int exynos_fimd_suspend(struct device *dev) +{ + struct fimd_context *ctx = dev_get_drvdata(dev); + + clk_disable_unprepare(ctx->lcd_clk); + clk_disable_unprepare(ctx->bus_clk); + + return 0; +} + +static int exynos_fimd_resume(struct device *dev) +{ + struct fimd_context *ctx = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(ctx->bus_clk); + if (ret < 0) { + DRM_ERROR("Failed to prepare_enable the bus clk [%d]\n", ret); + return ret; + } + + ret = clk_prepare_enable(ctx->lcd_clk); + if (ret < 0) { + DRM_ERROR("Failed to prepare_enable the lcd clk [%d]\n", ret); + return ret; + } + + return 0; +} +#endif + +static const struct dev_pm_ops exynos_fimd_pm_ops = { + SET_RUNTIME_PM_OPS(exynos_fimd_suspend, exynos_fimd_resume, NULL) +}; + struct platform_driver fimd_driver = { .probe = fimd_probe, .remove = fimd_remove, .driver = { .name = "exynos4-fb", .owner = THIS_MODULE, + .pm = &exynos_fimd_pm_ops, .of_match_table = fimd_driver_dt_match, }, }; diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.h b/drivers/gpu/drm/exynos/exynos_drm_gem.h index 37ab8b2..9ca5047 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.h +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.h @@ -55,8 +55,6 @@ struct exynos_drm_gem { struct sg_table *sgt; }; -struct page **exynos_gem_get_pages(struct drm_gem_object *obj, gfp_t gfpmask); - /* destroy a buffer with gem object */ void exynos_drm_gem_destroy(struct exynos_drm_gem *exynos_gem); @@ -91,10 +89,6 @@ void exynos_drm_gem_put_dma_addr(struct drm_device *dev, unsigned int gem_handle, struct drm_file *filp); -/* map user space allocated by malloc to pages. */ -int exynos_drm_gem_userptr_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); - /* get buffer information to memory region allocated by gem. */ int exynos_drm_gem_get_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); @@ -123,28 +117,6 @@ int exynos_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf); /* set vm_flags and we can change the vm attribute to other one at here. */ int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma); -static inline int vma_is_io(struct vm_area_struct *vma) -{ - return !!(vma->vm_flags & (VM_IO | VM_PFNMAP)); -} - -/* get a copy of a virtual memory region. */ -struct vm_area_struct *exynos_gem_get_vma(struct vm_area_struct *vma); - -/* release a userspace virtual memory area. */ -void exynos_gem_put_vma(struct vm_area_struct *vma); - -/* get pages from user space. */ -int exynos_gem_get_pages_from_userptr(unsigned long start, - unsigned int npages, - struct page **pages, - struct vm_area_struct *vma); - -/* drop the reference to pages. */ -void exynos_gem_put_pages_to_userptr(struct page **pages, - unsigned int npages, - struct vm_area_struct *vma); - /* map sgt with dma region. */ int exynos_gem_map_sgt_with_dma(struct drm_device *drm_dev, struct sg_table *sgt, diff --git a/drivers/gpu/drm/exynos/exynos_drm_gsc.c b/drivers/gpu/drm/exynos/exynos_drm_gsc.c index 11b87d2..7aecd23 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gsc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gsc.c @@ -15,7 +15,8 @@ #include <linux/platform_device.h> #include <linux/clk.h> #include <linux/pm_runtime.h> -#include <plat/map-base.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> #include <drm/drmP.h> #include <drm/exynos_drm.h> @@ -126,6 +127,7 @@ struct gsc_capability { * @ippdrv: prepare initialization using ippdrv. * @regs_res: register resources. * @regs: memory mapped io registers. + * @sysreg: handle to SYSREG block regmap. * @lock: locking of operations. * @gsc_clk: gsc gate clock. * @sc: scaler infomations. @@ -138,6 +140,7 @@ struct gsc_context { struct exynos_drm_ippdrv ippdrv; struct resource *regs_res; void __iomem *regs; + struct regmap *sysreg; struct mutex lock; struct clk *gsc_clk; struct gsc_scaler sc; @@ -437,9 +440,12 @@ static int gsc_sw_reset(struct gsc_context *ctx) static void gsc_set_gscblk_fimd_wb(struct gsc_context *ctx, bool enable) { - u32 gscblk_cfg; + unsigned int gscblk_cfg; - gscblk_cfg = readl(SYSREG_GSCBLK_CFG1); + if (!ctx->sysreg) + return; + + regmap_read(ctx->sysreg, SYSREG_GSCBLK_CFG1, &gscblk_cfg); if (enable) gscblk_cfg |= GSC_BLK_DISP1WB_DEST(ctx->id) | @@ -448,7 +454,7 @@ static void gsc_set_gscblk_fimd_wb(struct gsc_context *ctx, bool enable) else gscblk_cfg |= GSC_BLK_PXLASYNC_LO_MASK_WB(ctx->id); - writel(gscblk_cfg, SYSREG_GSCBLK_CFG1); + regmap_write(ctx->sysreg, SYSREG_GSCBLK_CFG1, gscblk_cfg); } static void gsc_handle_irq(struct gsc_context *ctx, bool enable, @@ -1215,10 +1221,10 @@ static int gsc_clk_ctrl(struct gsc_context *ctx, bool enable) DRM_DEBUG_KMS("enable[%d]\n", enable); if (enable) { - clk_enable(ctx->gsc_clk); + clk_prepare_enable(ctx->gsc_clk); ctx->suspended = false; } else { - clk_disable(ctx->gsc_clk); + clk_disable_unprepare(ctx->gsc_clk); ctx->suspended = true; } @@ -1663,6 +1669,15 @@ static int gsc_probe(struct platform_device *pdev) if (!ctx) return -ENOMEM; + if (dev->of_node) { + ctx->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node, + "samsung,sysreg"); + if (IS_ERR(ctx->sysreg)) { + dev_warn(dev, "failed to get system register.\n"); + ctx->sysreg = NULL; + } + } + /* clock control */ ctx->gsc_clk = devm_clk_get(dev, "gscl"); if (IS_ERR(ctx->gsc_clk)) { @@ -1713,7 +1728,6 @@ static int gsc_probe(struct platform_device *pdev) mutex_init(&ctx->lock); platform_set_drvdata(pdev, ctx); - pm_runtime_set_active(dev); pm_runtime_enable(dev); ret = exynos_drm_ippdrv_register(ippdrv); @@ -1797,6 +1811,12 @@ static const struct dev_pm_ops gsc_pm_ops = { SET_RUNTIME_PM_OPS(gsc_runtime_suspend, gsc_runtime_resume, NULL) }; +static const struct of_device_id exynos_drm_gsc_of_match[] = { + { .compatible = "samsung,exynos5-gsc" }, + { }, +}; +MODULE_DEVICE_TABLE(of, exynos_drm_gsc_of_match); + struct platform_driver gsc_driver = { .probe = gsc_probe, .remove = gsc_remove, @@ -1804,6 +1824,7 @@ struct platform_driver gsc_driver = { .name = "exynos-drm-gsc", .owner = THIS_MODULE, .pm = &gsc_pm_ops, + .of_match_table = of_match_ptr(exynos_drm_gsc_of_match), }, }; diff --git a/drivers/gpu/drm/exynos/exynos_drm_mic.c b/drivers/gpu/drm/exynos/exynos_drm_mic.c index 8994eab..4eaef36 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_mic.c +++ b/drivers/gpu/drm/exynos/exynos_drm_mic.c @@ -389,7 +389,7 @@ already_disabled: mutex_unlock(&mic_mutex); } -struct drm_bridge_funcs mic_bridge_funcs = { +static const struct drm_bridge_funcs mic_bridge_funcs = { .disable = mic_disable, .post_disable = mic_post_disable, .pre_enable = mic_pre_enable, diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c index 1793117..d862272 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_plane.c +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c @@ -56,93 +56,213 @@ static int exynos_plane_get_size(int start, unsigned length, unsigned last) return size; } -static void exynos_plane_mode_set(struct drm_plane *plane, - struct drm_crtc *crtc, - struct drm_framebuffer *fb, - int crtc_x, int crtc_y, - unsigned int crtc_w, unsigned int crtc_h, - uint32_t src_x, uint32_t src_y, - uint32_t src_w, uint32_t src_h) +static void exynos_plane_mode_set(struct exynos_drm_plane_state *exynos_state) + { - struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane); + struct drm_plane_state *state = &exynos_state->base; + struct drm_crtc *crtc = exynos_state->base.crtc; struct drm_display_mode *mode = &crtc->state->adjusted_mode; + int crtc_x, crtc_y; + unsigned int crtc_w, crtc_h; + unsigned int src_x, src_y; + unsigned int src_w, src_h; unsigned int actual_w; unsigned int actual_h; + /* + * The original src/dest coordinates are stored in exynos_state->base, + * but we want to keep another copy internal to our driver that we can + * clip/modify ourselves. + */ + + crtc_x = state->crtc_x; + crtc_y = state->crtc_y; + crtc_w = state->crtc_w; + crtc_h = state->crtc_h; + + src_x = state->src_x >> 16; + src_y = state->src_y >> 16; + src_w = state->src_w >> 16; + src_h = state->src_h >> 16; + + /* set ratio */ + exynos_state->h_ratio = (src_w << 16) / crtc_w; + exynos_state->v_ratio = (src_h << 16) / crtc_h; + + /* clip to visible area */ actual_w = exynos_plane_get_size(crtc_x, crtc_w, mode->hdisplay); actual_h = exynos_plane_get_size(crtc_y, crtc_h, mode->vdisplay); if (crtc_x < 0) { if (actual_w) - src_x -= crtc_x; + src_x += ((-crtc_x) * exynos_state->h_ratio) >> 16; crtc_x = 0; } if (crtc_y < 0) { if (actual_h) - src_y -= crtc_y; + src_y += ((-crtc_y) * exynos_state->v_ratio) >> 16; crtc_y = 0; } - /* set ratio */ - exynos_plane->h_ratio = (src_w << 16) / crtc_w; - exynos_plane->v_ratio = (src_h << 16) / crtc_h; - /* set drm framebuffer data. */ - exynos_plane->src_x = src_x; - exynos_plane->src_y = src_y; - exynos_plane->src_w = (actual_w * exynos_plane->h_ratio) >> 16; - exynos_plane->src_h = (actual_h * exynos_plane->v_ratio) >> 16; + exynos_state->src.x = src_x; + exynos_state->src.y = src_y; + exynos_state->src.w = (actual_w * exynos_state->h_ratio) >> 16; + exynos_state->src.h = (actual_h * exynos_state->v_ratio) >> 16; /* set plane range to be displayed. */ - exynos_plane->crtc_x = crtc_x; - exynos_plane->crtc_y = crtc_y; - exynos_plane->crtc_w = actual_w; - exynos_plane->crtc_h = actual_h; + exynos_state->crtc.x = crtc_x; + exynos_state->crtc.y = crtc_y; + exynos_state->crtc.w = actual_w; + exynos_state->crtc.h = actual_h; DRM_DEBUG_KMS("plane : offset_x/y(%d,%d), width/height(%d,%d)", - exynos_plane->crtc_x, exynos_plane->crtc_y, - exynos_plane->crtc_w, exynos_plane->crtc_h); + exynos_state->crtc.x, exynos_state->crtc.y, + exynos_state->crtc.w, exynos_state->crtc.h); +} + +static void exynos_drm_plane_reset(struct drm_plane *plane) +{ + struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane); + struct exynos_drm_plane_state *exynos_state; + + if (plane->state) { + exynos_state = to_exynos_plane_state(plane->state); + if (exynos_state->base.fb) + drm_framebuffer_unreference(exynos_state->base.fb); + kfree(exynos_state); + plane->state = NULL; + } + + exynos_state = kzalloc(sizeof(*exynos_state), GFP_KERNEL); + if (exynos_state) { + exynos_state->zpos = exynos_plane->config->zpos; + plane->state = &exynos_state->base; + plane->state->plane = plane; + } +} + +static struct drm_plane_state * +exynos_drm_plane_duplicate_state(struct drm_plane *plane) +{ + struct exynos_drm_plane_state *exynos_state; + struct exynos_drm_plane_state *copy; + + exynos_state = to_exynos_plane_state(plane->state); + copy = kzalloc(sizeof(*exynos_state), GFP_KERNEL); + if (!copy) + return NULL; + + __drm_atomic_helper_plane_duplicate_state(plane, ©->base); + copy->zpos = exynos_state->zpos; + return ©->base; +} + +static void exynos_drm_plane_destroy_state(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct exynos_drm_plane_state *old_exynos_state = + to_exynos_plane_state(old_state); + __drm_atomic_helper_plane_destroy_state(plane, old_state); + kfree(old_exynos_state); +} + +static int exynos_drm_plane_atomic_set_property(struct drm_plane *plane, + struct drm_plane_state *state, + struct drm_property *property, + uint64_t val) +{ + struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane); + struct exynos_drm_plane_state *exynos_state = + to_exynos_plane_state(state); + struct exynos_drm_private *dev_priv = plane->dev->dev_private; + const struct exynos_drm_plane_config *config = exynos_plane->config; - plane->crtc = crtc; + if (property == dev_priv->plane_zpos_property && + (config->capabilities & EXYNOS_DRM_PLANE_CAP_ZPOS)) + exynos_state->zpos = val; + else + return -EINVAL; + + return 0; +} + +static int exynos_drm_plane_atomic_get_property(struct drm_plane *plane, + const struct drm_plane_state *state, + struct drm_property *property, + uint64_t *val) +{ + const struct exynos_drm_plane_state *exynos_state = + container_of(state, const struct exynos_drm_plane_state, base); + struct exynos_drm_private *dev_priv = plane->dev->dev_private; + + if (property == dev_priv->plane_zpos_property) + *val = exynos_state->zpos; + else + return -EINVAL; + + return 0; } static struct drm_plane_funcs exynos_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, .destroy = drm_plane_cleanup, - .reset = drm_atomic_helper_plane_reset, - .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, - .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, + .set_property = drm_atomic_helper_plane_set_property, + .reset = exynos_drm_plane_reset, + .atomic_duplicate_state = exynos_drm_plane_duplicate_state, + .atomic_destroy_state = exynos_drm_plane_destroy_state, + .atomic_set_property = exynos_drm_plane_atomic_set_property, + .atomic_get_property = exynos_drm_plane_atomic_get_property, }; +static int +exynos_drm_plane_check_size(const struct exynos_drm_plane_config *config, + struct exynos_drm_plane_state *state) +{ + bool width_ok = false, height_ok = false; + + if (config->capabilities & EXYNOS_DRM_PLANE_CAP_SCALE) + return 0; + + if (state->src.w == state->crtc.w) + width_ok = true; + + if (state->src.h == state->crtc.h) + height_ok = true; + + if ((config->capabilities & EXYNOS_DRM_PLANE_CAP_DOUBLE) && + state->h_ratio == (1 << 15)) + width_ok = true; + + if ((config->capabilities & EXYNOS_DRM_PLANE_CAP_DOUBLE) && + state->v_ratio == (1 << 15)) + height_ok = true; + + if (width_ok & height_ok) + return 0; + + DRM_DEBUG_KMS("scaling mode is not supported"); + return -ENOTSUPP; +} + static int exynos_plane_atomic_check(struct drm_plane *plane, struct drm_plane_state *state) { struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane); - int nr; - int i; + struct exynos_drm_plane_state *exynos_state = + to_exynos_plane_state(state); + int ret = 0; - if (!state->fb) + if (!state->crtc || !state->fb) return 0; - nr = drm_format_num_planes(state->fb->pixel_format); - for (i = 0; i < nr; i++) { - struct exynos_drm_gem *exynos_gem = - exynos_drm_fb_gem(state->fb, i); - if (!exynos_gem) { - DRM_DEBUG_KMS("gem object is null\n"); - return -EFAULT; - } - - exynos_plane->dma_addr[i] = exynos_gem->dma_addr + - state->fb->offsets[i]; + /* translate state into exynos_state */ + exynos_plane_mode_set(exynos_state); - DRM_DEBUG_KMS("buffer: %d, dma_addr = 0x%lx\n", - i, (unsigned long)exynos_plane->dma_addr[i]); - } - - return 0; + ret = exynos_drm_plane_check_size(exynos_plane->config, exynos_state); + return ret; } static void exynos_plane_atomic_update(struct drm_plane *plane, @@ -155,12 +275,7 @@ static void exynos_plane_atomic_update(struct drm_plane *plane, if (!state->crtc) return; - exynos_plane_mode_set(plane, state->crtc, state->fb, - state->crtc_x, state->crtc_y, - state->crtc_w, state->crtc_h, - state->src_x >> 16, state->src_y >> 16, - state->src_w >> 16, state->src_h >> 16); - + plane->crtc = state->crtc; exynos_plane->pending_fb = state->fb; if (exynos_crtc->ops->update_plane) @@ -177,8 +292,7 @@ static void exynos_plane_atomic_disable(struct drm_plane *plane, return; if (exynos_crtc->ops->disable_plane) - exynos_crtc->ops->disable_plane(exynos_crtc, - exynos_plane); + exynos_crtc->ops->disable_plane(exynos_crtc, exynos_plane); } static const struct drm_plane_helper_funcs plane_helper_funcs = { @@ -196,8 +310,8 @@ static void exynos_plane_attach_zpos_property(struct drm_plane *plane, prop = dev_priv->plane_zpos_property; if (!prop) { - prop = drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE, - "zpos", 0, MAX_PLANE - 1); + prop = drm_property_create_range(dev, 0, "zpos", + 0, MAX_PLANE - 1); if (!prop) return; @@ -207,28 +321,19 @@ static void exynos_plane_attach_zpos_property(struct drm_plane *plane, drm_object_attach_property(&plane->base, prop, zpos); } -enum drm_plane_type exynos_plane_get_type(unsigned int zpos, - unsigned int cursor_win) -{ - if (zpos == DEFAULT_WIN) - return DRM_PLANE_TYPE_PRIMARY; - else if (zpos == cursor_win) - return DRM_PLANE_TYPE_CURSOR; - else - return DRM_PLANE_TYPE_OVERLAY; -} - int exynos_plane_init(struct drm_device *dev, struct exynos_drm_plane *exynos_plane, - unsigned long possible_crtcs, enum drm_plane_type type, - const uint32_t *formats, unsigned int fcount, - unsigned int zpos) + unsigned int index, unsigned long possible_crtcs, + const struct exynos_drm_plane_config *config) { int err; - err = drm_universal_plane_init(dev, &exynos_plane->base, possible_crtcs, - &exynos_plane_funcs, formats, fcount, - type); + err = drm_universal_plane_init(dev, &exynos_plane->base, + possible_crtcs, + &exynos_plane_funcs, + config->pixel_formats, + config->num_pixel_formats, + config->type, NULL); if (err) { DRM_ERROR("failed to initialize plane\n"); return err; @@ -236,10 +341,10 @@ int exynos_plane_init(struct drm_device *dev, drm_plane_helper_add(&exynos_plane->base, &plane_helper_funcs); - exynos_plane->zpos = zpos; + exynos_plane->index = index; + exynos_plane->config = config; - if (type == DRM_PLANE_TYPE_OVERLAY) - exynos_plane_attach_zpos_property(&exynos_plane->base, zpos); + exynos_plane_attach_zpos_property(&exynos_plane->base, config->zpos); return 0; } diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.h b/drivers/gpu/drm/exynos/exynos_drm_plane.h index abb641e..9aafad1 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_plane.h +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.h @@ -9,10 +9,7 @@ * */ -enum drm_plane_type exynos_plane_get_type(unsigned int zpos, - unsigned int cursor_win); int exynos_plane_init(struct drm_device *dev, - struct exynos_drm_plane *exynos_plane, - unsigned long possible_crtcs, enum drm_plane_type type, - const uint32_t *formats, unsigned int fcount, - unsigned int zpos); + struct exynos_drm_plane *exynos_plane, unsigned int index, + unsigned long possible_crtcs, + const struct exynos_drm_plane_config *config); diff --git a/drivers/gpu/drm/exynos/exynos_drm_rotator.c b/drivers/gpu/drm/exynos/exynos_drm_rotator.c index 2f5c118..bea0f78 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_rotator.c +++ b/drivers/gpu/drm/exynos/exynos_drm_rotator.c @@ -790,10 +790,10 @@ static int rotator_remove(struct platform_device *pdev) static int rotator_clk_crtl(struct rot_context *rot, bool enable) { if (enable) { - clk_enable(rot->clock); + clk_prepare_enable(rot->clock); rot->suspended = false; } else { - clk_disable(rot->clock); + clk_disable_unprepare(rot->clock); rot->suspended = true; } diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c index 669362c..62ac4e5 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c @@ -24,12 +24,12 @@ #include "exynos_drm_drv.h" #include "exynos_drm_crtc.h" +#include "exynos_drm_fb.h" #include "exynos_drm_plane.h" #include "exynos_drm_vidi.h" /* vidi has totally three virtual windows. */ #define WINDOWS_NR 3 -#define CURSOR_WIN 2 #define ctx_from_connector(c) container_of(c, struct vidi_context, \ connector) @@ -89,6 +89,12 @@ static const uint32_t formats[] = { DRM_FORMAT_NV12, }; +static const enum drm_plane_type vidi_win_types[WINDOWS_NR] = { + DRM_PLANE_TYPE_PRIMARY, + DRM_PLANE_TYPE_OVERLAY, + DRM_PLANE_TYPE_CURSOR, +}; + static int vidi_enable_vblank(struct exynos_drm_crtc *crtc) { struct vidi_context *ctx = crtc->ctx; @@ -125,12 +131,15 @@ static void vidi_disable_vblank(struct exynos_drm_crtc *crtc) static void vidi_update_plane(struct exynos_drm_crtc *crtc, struct exynos_drm_plane *plane) { + struct drm_plane_state *state = plane->base.state; struct vidi_context *ctx = crtc->ctx; + dma_addr_t addr; if (ctx->suspended) return; - DRM_DEBUG_KMS("dma_addr = %pad\n", plane->dma_addr); + addr = exynos_drm_fb_dma_addr(state->fb, 0); + DRM_DEBUG_KMS("dma_addr = %pad\n", &addr); if (ctx->vblank_on) schedule_work(&ctx->work); @@ -330,7 +339,7 @@ static void vidi_connector_destroy(struct drm_connector *connector) { } -static struct drm_connector_funcs vidi_connector_funcs = { +static const struct drm_connector_funcs vidi_connector_funcs = { .dpms = drm_atomic_helper_connector_dpms, .fill_modes = drm_helper_probe_single_connector_modes, .detect = vidi_detect, @@ -374,7 +383,7 @@ static struct drm_encoder *vidi_best_encoder(struct drm_connector *connector) return &ctx->encoder; } -static struct drm_connector_helper_funcs vidi_connector_helper_funcs = { +static const struct drm_connector_helper_funcs vidi_connector_helper_funcs = { .get_modes = vidi_get_modes, .best_encoder = vidi_best_encoder, }; @@ -422,14 +431,14 @@ static void exynos_vidi_disable(struct drm_encoder *encoder) { } -static struct drm_encoder_helper_funcs exynos_vidi_encoder_helper_funcs = { +static const struct drm_encoder_helper_funcs exynos_vidi_encoder_helper_funcs = { .mode_fixup = exynos_vidi_mode_fixup, .mode_set = exynos_vidi_mode_set, .enable = exynos_vidi_enable, .disable = exynos_vidi_disable, }; -static struct drm_encoder_funcs exynos_vidi_encoder_funcs = { +static const struct drm_encoder_funcs exynos_vidi_encoder_funcs = { .destroy = drm_encoder_cleanup, }; @@ -439,17 +448,21 @@ static int vidi_bind(struct device *dev, struct device *master, void *data) struct drm_device *drm_dev = data; struct drm_encoder *encoder = &ctx->encoder; struct exynos_drm_plane *exynos_plane; - enum drm_plane_type type; - unsigned int zpos; + struct exynos_drm_plane_config plane_config = { 0 }; + unsigned int i; int pipe, ret; vidi_ctx_initialize(ctx, drm_dev); - for (zpos = 0; zpos < WINDOWS_NR; zpos++) { - type = exynos_plane_get_type(zpos, CURSOR_WIN); - ret = exynos_plane_init(drm_dev, &ctx->planes[zpos], - 1 << ctx->pipe, type, formats, - ARRAY_SIZE(formats), zpos); + plane_config.pixel_formats = formats; + plane_config.num_pixel_formats = ARRAY_SIZE(formats); + + for (i = 0; i < WINDOWS_NR; i++) { + plane_config.zpos = i; + plane_config.type = vidi_win_types[i]; + + ret = exynos_plane_init(drm_dev, &ctx->planes[i], i, + 1 << ctx->pipe, &plane_config); if (ret) return ret; } @@ -473,7 +486,7 @@ static int vidi_bind(struct device *dev, struct device *master, void *data) DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs); drm_encoder_init(drm_dev, encoder, &exynos_vidi_encoder_funcs, - DRM_MODE_ENCODER_TMDS); + DRM_MODE_ENCODER_TMDS, NULL); drm_encoder_helper_add(encoder, &exynos_vidi_encoder_helper_funcs); diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 57b6755..21a29db 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -113,7 +113,7 @@ struct hdmi_context { void __iomem *regs_hdmiphy; struct i2c_client *hdmiphy_port; struct i2c_adapter *ddc_adpt; - struct gpio_desc *hpd_gpio; + struct gpio_desc *hpd_gpio; int irq; struct regmap *pmureg; struct clk *hdmi; @@ -956,7 +956,7 @@ static void hdmi_connector_destroy(struct drm_connector *connector) drm_connector_cleanup(connector); } -static struct drm_connector_funcs hdmi_connector_funcs = { +static const struct drm_connector_funcs hdmi_connector_funcs = { .dpms = drm_atomic_helper_connector_dpms, .fill_modes = drm_helper_probe_single_connector_modes, .detect = hdmi_detect, @@ -1030,7 +1030,7 @@ static struct drm_encoder *hdmi_best_encoder(struct drm_connector *connector) return &hdata->encoder; } -static struct drm_connector_helper_funcs hdmi_connector_helper_funcs = { +static const struct drm_connector_helper_funcs hdmi_connector_helper_funcs = { .get_modes = hdmi_get_modes, .mode_valid = hdmi_mode_valid, .best_encoder = hdmi_best_encoder, @@ -1588,8 +1588,6 @@ static void hdmi_enable(struct drm_encoder *encoder) if (hdata->powered) return; - hdata->powered = true; - pm_runtime_get_sync(hdata->dev); if (regulator_bulk_enable(ARRAY_SIZE(supply), hdata->regul_bulk)) @@ -1599,10 +1597,9 @@ static void hdmi_enable(struct drm_encoder *encoder) regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL, PMU_HDMI_PHY_ENABLE_BIT, 1); - clk_prepare_enable(hdata->hdmi); - clk_prepare_enable(hdata->sclk_hdmi); - hdmi_conf_apply(hdata); + + hdata->powered = true; } static void hdmi_disable(struct drm_encoder *encoder) @@ -1633,9 +1630,6 @@ static void hdmi_disable(struct drm_encoder *encoder) cancel_delayed_work(&hdata->hotplug_work); - clk_disable_unprepare(hdata->sclk_hdmi); - clk_disable_unprepare(hdata->hdmi); - /* reset pmu hdmiphy control bit to disable hdmiphy */ regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL, PMU_HDMI_PHY_ENABLE_BIT, 0); @@ -1647,14 +1641,14 @@ static void hdmi_disable(struct drm_encoder *encoder) hdata->powered = false; } -static struct drm_encoder_helper_funcs exynos_hdmi_encoder_helper_funcs = { +static const struct drm_encoder_helper_funcs exynos_hdmi_encoder_helper_funcs = { .mode_fixup = hdmi_mode_fixup, .mode_set = hdmi_mode_set, .enable = hdmi_enable, .disable = hdmi_disable, }; -static struct drm_encoder_funcs exynos_hdmi_encoder_funcs = { +static const struct drm_encoder_funcs exynos_hdmi_encoder_funcs = { .destroy = drm_encoder_cleanup, }; @@ -1793,7 +1787,7 @@ static int hdmi_bind(struct device *dev, struct device *master, void *data) DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs); drm_encoder_init(drm_dev, encoder, &exynos_hdmi_encoder_funcs, - DRM_MODE_ENCODER_TMDS); + DRM_MODE_ENCODER_TMDS, NULL); drm_encoder_helper_add(encoder, &exynos_hdmi_encoder_helper_funcs); @@ -1978,12 +1972,49 @@ static int hdmi_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM +static int exynos_hdmi_suspend(struct device *dev) +{ + struct hdmi_context *hdata = dev_get_drvdata(dev); + + clk_disable_unprepare(hdata->sclk_hdmi); + clk_disable_unprepare(hdata->hdmi); + + return 0; +} + +static int exynos_hdmi_resume(struct device *dev) +{ + struct hdmi_context *hdata = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(hdata->hdmi); + if (ret < 0) { + DRM_ERROR("Failed to prepare_enable the hdmi clk [%d]\n", ret); + return ret; + } + ret = clk_prepare_enable(hdata->sclk_hdmi); + if (ret < 0) { + DRM_ERROR("Failed to prepare_enable the sclk_mixer clk [%d]\n", + ret); + return ret; + } + + return 0; +} +#endif + +static const struct dev_pm_ops exynos_hdmi_pm_ops = { + SET_RUNTIME_PM_OPS(exynos_hdmi_suspend, exynos_hdmi_resume, NULL) +}; + struct platform_driver hdmi_driver = { .probe = hdmi_probe, .remove = hdmi_remove, .driver = { .name = "exynos-hdmi", .owner = THIS_MODULE, + .pm = &exynos_hdmi_pm_ops, .of_match_table = hdmi_match_types, }, }; diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c index d09f8f9..b5fbc1c 100644 --- a/drivers/gpu/drm/exynos/exynos_mixer.c +++ b/drivers/gpu/drm/exynos/exynos_mixer.c @@ -37,12 +37,12 @@ #include "exynos_drm_drv.h" #include "exynos_drm_crtc.h" +#include "exynos_drm_fb.h" #include "exynos_drm_plane.h" #include "exynos_drm_iommu.h" #define MIXER_WIN_NR 3 #define VP_DEFAULT_WIN 2 -#define CURSOR_WIN 1 /* The pixelformats that are natively supported by the mixer. */ #define MXR_FORMAT_RGB565 4 @@ -76,7 +76,9 @@ enum mixer_flag_bits { static const uint32_t mixer_formats[] = { DRM_FORMAT_XRGB4444, + DRM_FORMAT_ARGB4444, DRM_FORMAT_XRGB1555, + DRM_FORMAT_ARGB1555, DRM_FORMAT_RGB565, DRM_FORMAT_XRGB8888, DRM_FORMAT_ARGB8888, @@ -111,6 +113,31 @@ struct mixer_drv_data { bool has_sclk; }; +static const struct exynos_drm_plane_config plane_configs[MIXER_WIN_NR] = { + { + .zpos = 0, + .type = DRM_PLANE_TYPE_PRIMARY, + .pixel_formats = mixer_formats, + .num_pixel_formats = ARRAY_SIZE(mixer_formats), + .capabilities = EXYNOS_DRM_PLANE_CAP_DOUBLE | + EXYNOS_DRM_PLANE_CAP_ZPOS, + }, { + .zpos = 1, + .type = DRM_PLANE_TYPE_CURSOR, + .pixel_formats = mixer_formats, + .num_pixel_formats = ARRAY_SIZE(mixer_formats), + .capabilities = EXYNOS_DRM_PLANE_CAP_DOUBLE | + EXYNOS_DRM_PLANE_CAP_ZPOS, + }, { + .zpos = 2, + .type = DRM_PLANE_TYPE_OVERLAY, + .pixel_formats = vp_formats, + .num_pixel_formats = ARRAY_SIZE(vp_formats), + .capabilities = EXYNOS_DRM_PLANE_CAP_SCALE | + EXYNOS_DRM_PLANE_CAP_ZPOS, + }, +}; + static const u8 filter_y_horiz_tap8[] = { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, @@ -140,6 +167,18 @@ static const u8 filter_cr_horiz_tap4[] = { 70, 59, 48, 37, 27, 19, 11, 5, }; +static inline bool is_alpha_format(unsigned int pixel_format) +{ + switch (pixel_format) { + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_ARGB1555: + case DRM_FORMAT_ARGB4444: + return true; + default: + return false; + } +} + static inline u32 vp_reg_read(struct mixer_resources *res, u32 reg_id) { return readl(res->vp_regs + reg_id); @@ -269,6 +308,37 @@ static void vp_default_filter(struct mixer_resources *res) filter_cr_horiz_tap4, sizeof(filter_cr_horiz_tap4)); } +static void mixer_cfg_gfx_blend(struct mixer_context *ctx, unsigned int win, + bool alpha) +{ + struct mixer_resources *res = &ctx->mixer_res; + u32 val; + + val = MXR_GRP_CFG_COLOR_KEY_DISABLE; /* no blank key */ + if (alpha) { + /* blending based on pixel alpha */ + val |= MXR_GRP_CFG_BLEND_PRE_MUL; + val |= MXR_GRP_CFG_PIXEL_BLEND_EN; + } + mixer_reg_writemask(res, MXR_GRAPHIC_CFG(win), + val, MXR_GRP_CFG_MISC_MASK); +} + +static void mixer_cfg_vp_blend(struct mixer_context *ctx) +{ + struct mixer_resources *res = &ctx->mixer_res; + u32 val; + + /* + * No blending at the moment since the NV12/NV21 pixelformats don't + * have an alpha channel. However the mixer supports a global alpha + * value for a layer. Once this functionality is exposed, we can + * support blending of the video layer through this. + */ + val = 0; + mixer_reg_write(res, MXR_VIDEO_CFG, val); +} + static void mixer_vsync_set_update(struct mixer_context *ctx, bool enable) { struct mixer_resources *res = &ctx->mixer_res; @@ -350,7 +420,7 @@ static void mixer_cfg_rgb_fmt(struct mixer_context *ctx, unsigned int height) } static void mixer_cfg_layer(struct mixer_context *ctx, unsigned int win, - bool enable) + unsigned int priority, bool enable) { struct mixer_resources *res = &ctx->mixer_res; u32 val = enable ? ~0 : 0; @@ -358,20 +428,24 @@ static void mixer_cfg_layer(struct mixer_context *ctx, unsigned int win, switch (win) { case 0: mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_GRP0_ENABLE); + mixer_reg_writemask(res, MXR_LAYER_CFG, + MXR_LAYER_CFG_GRP0_VAL(priority), + MXR_LAYER_CFG_GRP0_MASK); break; case 1: mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_GRP1_ENABLE); + mixer_reg_writemask(res, MXR_LAYER_CFG, + MXR_LAYER_CFG_GRP1_VAL(priority), + MXR_LAYER_CFG_GRP1_MASK); break; - case 2: + case VP_DEFAULT_WIN: if (ctx->vp_enabled) { vp_reg_writemask(res, VP_ENABLE, val, VP_ENABLE_ON); mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_VP_ENABLE); - - /* control blending of graphic layer 0 */ - mixer_reg_writemask(res, MXR_GRAPHIC_CFG(0), val, - MXR_GRP_CFG_BLEND_PRE_MUL | - MXR_GRP_CFG_PIXEL_BLEND_EN); + mixer_reg_writemask(res, MXR_LAYER_CFG, + MXR_LAYER_CFG_VP_VAL(priority), + MXR_LAYER_CFG_VP_MASK); } break; } @@ -399,10 +473,11 @@ static void mixer_stop(struct mixer_context *ctx) static void vp_video_buffer(struct mixer_context *ctx, struct exynos_drm_plane *plane) { + struct exynos_drm_plane_state *state = + to_exynos_plane_state(plane->base.state); + struct drm_display_mode *mode = &state->base.crtc->state->adjusted_mode; struct mixer_resources *res = &ctx->mixer_res; - struct drm_plane_state *state = plane->base.state; - struct drm_framebuffer *fb = state->fb; - struct drm_display_mode *mode = &state->crtc->mode; + struct drm_framebuffer *fb = state->base.fb; unsigned long flags; dma_addr_t luma_addr[2], chroma_addr[2]; bool tiled_mode = false; @@ -422,8 +497,8 @@ static void vp_video_buffer(struct mixer_context *ctx, return; } - luma_addr[0] = plane->dma_addr[0]; - chroma_addr[0] = plane->dma_addr[1]; + luma_addr[0] = exynos_drm_fb_dma_addr(fb, 0); + chroma_addr[0] = exynos_drm_fb_dma_addr(fb, 1); if (mode->flags & DRM_MODE_FLAG_INTERLACE) { ctx->interlace = true; @@ -441,7 +516,6 @@ static void vp_video_buffer(struct mixer_context *ctx, } spin_lock_irqsave(&res->reg_slock, flags); - mixer_vsync_set_update(ctx, false); /* interlace or progressive scan mode */ val = (ctx->interlace ? ~0 : 0); @@ -459,24 +533,24 @@ static void vp_video_buffer(struct mixer_context *ctx, vp_reg_write(res, VP_IMG_SIZE_C, VP_IMG_HSIZE(fb->pitches[0]) | VP_IMG_VSIZE(fb->height / 2)); - vp_reg_write(res, VP_SRC_WIDTH, plane->src_w); - vp_reg_write(res, VP_SRC_HEIGHT, plane->src_h); + vp_reg_write(res, VP_SRC_WIDTH, state->src.w); + vp_reg_write(res, VP_SRC_HEIGHT, state->src.h); vp_reg_write(res, VP_SRC_H_POSITION, - VP_SRC_H_POSITION_VAL(plane->src_x)); - vp_reg_write(res, VP_SRC_V_POSITION, plane->src_y); + VP_SRC_H_POSITION_VAL(state->src.x)); + vp_reg_write(res, VP_SRC_V_POSITION, state->src.y); - vp_reg_write(res, VP_DST_WIDTH, plane->crtc_w); - vp_reg_write(res, VP_DST_H_POSITION, plane->crtc_x); + vp_reg_write(res, VP_DST_WIDTH, state->crtc.w); + vp_reg_write(res, VP_DST_H_POSITION, state->crtc.x); if (ctx->interlace) { - vp_reg_write(res, VP_DST_HEIGHT, plane->crtc_h / 2); - vp_reg_write(res, VP_DST_V_POSITION, plane->crtc_y / 2); + vp_reg_write(res, VP_DST_HEIGHT, state->crtc.h / 2); + vp_reg_write(res, VP_DST_V_POSITION, state->crtc.y / 2); } else { - vp_reg_write(res, VP_DST_HEIGHT, plane->crtc_h); - vp_reg_write(res, VP_DST_V_POSITION, plane->crtc_y); + vp_reg_write(res, VP_DST_HEIGHT, state->crtc.h); + vp_reg_write(res, VP_DST_V_POSITION, state->crtc.y); } - vp_reg_write(res, VP_H_RATIO, plane->h_ratio); - vp_reg_write(res, VP_V_RATIO, plane->v_ratio); + vp_reg_write(res, VP_H_RATIO, state->h_ratio); + vp_reg_write(res, VP_V_RATIO, state->v_ratio); vp_reg_write(res, VP_ENDIAN_MODE, VP_ENDIAN_MODE_LITTLE); @@ -488,10 +562,10 @@ static void vp_video_buffer(struct mixer_context *ctx, mixer_cfg_scan(ctx, mode->vdisplay); mixer_cfg_rgb_fmt(ctx, mode->vdisplay); - mixer_cfg_layer(ctx, plane->zpos, true); + mixer_cfg_layer(ctx, plane->index, state->zpos + 1, true); + mixer_cfg_vp_blend(ctx); mixer_run(ctx); - mixer_vsync_set_update(ctx, true); spin_unlock_irqrestore(&res->reg_slock, flags); mixer_regs_dump(ctx); @@ -505,39 +579,16 @@ static void mixer_layer_update(struct mixer_context *ctx) mixer_reg_writemask(res, MXR_CFG, ~0, MXR_CFG_LAYER_UPDATE); } -static int mixer_setup_scale(const struct exynos_drm_plane *plane, - unsigned int *x_ratio, unsigned int *y_ratio) -{ - if (plane->crtc_w != plane->src_w) { - if (plane->crtc_w == 2 * plane->src_w) - *x_ratio = 1; - else - goto fail; - } - - if (plane->crtc_h != plane->src_h) { - if (plane->crtc_h == 2 * plane->src_h) - *y_ratio = 1; - else - goto fail; - } - - return 0; - -fail: - DRM_DEBUG_KMS("only 2x width/height scaling of plane supported\n"); - return -ENOTSUPP; -} - static void mixer_graph_buffer(struct mixer_context *ctx, struct exynos_drm_plane *plane) { + struct exynos_drm_plane_state *state = + to_exynos_plane_state(plane->base.state); + struct drm_display_mode *mode = &state->base.crtc->state->adjusted_mode; struct mixer_resources *res = &ctx->mixer_res; - struct drm_plane_state *state = plane->base.state; - struct drm_framebuffer *fb = state->fb; - struct drm_display_mode *mode = &state->crtc->mode; + struct drm_framebuffer *fb = state->base.fb; unsigned long flags; - unsigned int win = plane->zpos; + unsigned int win = plane->index; unsigned int x_ratio = 0, y_ratio = 0; unsigned int src_x_offset, src_y_offset, dst_x_offset, dst_y_offset; dma_addr_t dma_addr; @@ -546,10 +597,12 @@ static void mixer_graph_buffer(struct mixer_context *ctx, switch (fb->pixel_format) { case DRM_FORMAT_XRGB4444: + case DRM_FORMAT_ARGB4444: fmt = MXR_FORMAT_ARGB4444; break; case DRM_FORMAT_XRGB1555: + case DRM_FORMAT_ARGB1555: fmt = MXR_FORMAT_ARGB1555; break; @@ -567,17 +620,17 @@ static void mixer_graph_buffer(struct mixer_context *ctx, return; } - /* check if mixer supports requested scaling setup */ - if (mixer_setup_scale(plane, &x_ratio, &y_ratio)) - return; + /* ratio is already checked by common plane code */ + x_ratio = state->h_ratio == (1 << 15); + y_ratio = state->v_ratio == (1 << 15); - dst_x_offset = plane->crtc_x; - dst_y_offset = plane->crtc_y; + dst_x_offset = state->crtc.x; + dst_y_offset = state->crtc.y; /* converting dma address base and source offset */ - dma_addr = plane->dma_addr[0] - + (plane->src_x * fb->bits_per_pixel >> 3) - + (plane->src_y * fb->pitches[0]); + dma_addr = exynos_drm_fb_dma_addr(fb, 0) + + (state->src.x * fb->bits_per_pixel >> 3) + + (state->src.y * fb->pitches[0]); src_x_offset = 0; src_y_offset = 0; @@ -587,7 +640,6 @@ static void mixer_graph_buffer(struct mixer_context *ctx, ctx->interlace = false; spin_lock_irqsave(&res->reg_slock, flags); - mixer_vsync_set_update(ctx, false); /* setup format */ mixer_reg_writemask(res, MXR_GRAPHIC_CFG(win), @@ -605,8 +657,8 @@ static void mixer_graph_buffer(struct mixer_context *ctx, mixer_reg_write(res, MXR_RESOLUTION, val); } - val = MXR_GRP_WH_WIDTH(plane->src_w); - val |= MXR_GRP_WH_HEIGHT(plane->src_h); + val = MXR_GRP_WH_WIDTH(state->src.w); + val |= MXR_GRP_WH_HEIGHT(state->src.h); val |= MXR_GRP_WH_H_SCALE(x_ratio); val |= MXR_GRP_WH_V_SCALE(y_ratio); mixer_reg_write(res, MXR_GRAPHIC_WH(win), val); @@ -626,7 +678,8 @@ static void mixer_graph_buffer(struct mixer_context *ctx, mixer_cfg_scan(ctx, mode->vdisplay); mixer_cfg_rgb_fmt(ctx, mode->vdisplay); - mixer_cfg_layer(ctx, win, true); + mixer_cfg_layer(ctx, win, state->zpos + 1, true); + mixer_cfg_gfx_blend(ctx, win, is_alpha_format(fb->pixel_format)); /* layer update mandatory for mixer 16.0.33.0 */ if (ctx->mxr_ver == MXR_VER_16_0_33_0 || @@ -635,7 +688,6 @@ static void mixer_graph_buffer(struct mixer_context *ctx, mixer_run(ctx); - mixer_vsync_set_update(ctx, true); spin_unlock_irqrestore(&res->reg_slock, flags); mixer_regs_dump(ctx); @@ -660,10 +712,8 @@ static void mixer_win_reset(struct mixer_context *ctx) { struct mixer_resources *res = &ctx->mixer_res; unsigned long flags; - u32 val; /* value stored to register */ spin_lock_irqsave(&res->reg_slock, flags); - mixer_vsync_set_update(ctx, false); mixer_reg_writemask(res, MXR_CFG, MXR_CFG_DST_HDMI, MXR_CFG_DST_MASK); @@ -674,40 +724,14 @@ static void mixer_win_reset(struct mixer_context *ctx) mixer_reg_writemask(res, MXR_STATUS, MXR_STATUS_16_BURST, MXR_STATUS_BURST_MASK); - /* setting default layer priority: layer1 > layer0 > video - * because typical usage scenario would be - * layer1 - OSD - * layer0 - framebuffer - * video - video overlay - */ - val = MXR_LAYER_CFG_GRP1_VAL(3); - val |= MXR_LAYER_CFG_GRP0_VAL(2); - if (ctx->vp_enabled) - val |= MXR_LAYER_CFG_VP_VAL(1); - mixer_reg_write(res, MXR_LAYER_CFG, val); + /* reset default layer priority */ + mixer_reg_write(res, MXR_LAYER_CFG, 0); /* setting background color */ mixer_reg_write(res, MXR_BG_COLOR0, 0x008080); mixer_reg_write(res, MXR_BG_COLOR1, 0x008080); mixer_reg_write(res, MXR_BG_COLOR2, 0x008080); - /* setting graphical layers */ - val = MXR_GRP_CFG_COLOR_KEY_DISABLE; /* no blank key */ - val |= MXR_GRP_CFG_WIN_BLEND_EN; - val |= MXR_GRP_CFG_ALPHA_VAL(0xff); /* non-transparent alpha */ - - /* Don't blend layer 0 onto the mixer background */ - mixer_reg_write(res, MXR_GRAPHIC_CFG(0), val); - - /* Blend layer 1 into layer 0 */ - val |= MXR_GRP_CFG_BLEND_PRE_MUL; - val |= MXR_GRP_CFG_PIXEL_BLEND_EN; - mixer_reg_write(res, MXR_GRAPHIC_CFG(1), val); - - /* setting video layers */ - val = MXR_GRP_CFG_ALPHA_VAL(0); - mixer_reg_write(res, MXR_VIDEO_CFG, val); - if (ctx->vp_enabled) { /* configuration of Video Processor Registers */ vp_win_reset(ctx); @@ -720,7 +744,6 @@ static void mixer_win_reset(struct mixer_context *ctx) if (ctx->vp_enabled) mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_VP_ENABLE); - mixer_vsync_set_update(ctx, true); spin_unlock_irqrestore(&res->reg_slock, flags); } @@ -951,17 +974,27 @@ static void mixer_disable_vblank(struct exynos_drm_crtc *crtc) mixer_reg_writemask(res, MXR_INT_EN, 0, MXR_INT_EN_VSYNC); } +static void mixer_atomic_begin(struct exynos_drm_crtc *crtc) +{ + struct mixer_context *mixer_ctx = crtc->ctx; + + if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags)) + return; + + mixer_vsync_set_update(mixer_ctx, false); +} + static void mixer_update_plane(struct exynos_drm_crtc *crtc, struct exynos_drm_plane *plane) { struct mixer_context *mixer_ctx = crtc->ctx; - DRM_DEBUG_KMS("win: %d\n", plane->zpos); + DRM_DEBUG_KMS("win: %d\n", plane->index); if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags)) return; - if (plane->zpos > 1 && mixer_ctx->vp_enabled) + if (plane->index == VP_DEFAULT_WIN) vp_video_buffer(mixer_ctx, plane); else mixer_graph_buffer(mixer_ctx, plane); @@ -974,18 +1007,24 @@ static void mixer_disable_plane(struct exynos_drm_crtc *crtc, struct mixer_resources *res = &mixer_ctx->mixer_res; unsigned long flags; - DRM_DEBUG_KMS("win: %d\n", plane->zpos); + DRM_DEBUG_KMS("win: %d\n", plane->index); if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags)) return; spin_lock_irqsave(&res->reg_slock, flags); - mixer_vsync_set_update(mixer_ctx, false); + mixer_cfg_layer(mixer_ctx, plane->index, 0, false); + spin_unlock_irqrestore(&res->reg_slock, flags); +} - mixer_cfg_layer(mixer_ctx, plane->zpos, false); +static void mixer_atomic_flush(struct exynos_drm_crtc *crtc) +{ + struct mixer_context *mixer_ctx = crtc->ctx; + + if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags)) + return; mixer_vsync_set_update(mixer_ctx, true); - spin_unlock_irqrestore(&res->reg_slock, flags); } static void mixer_wait_for_vblank(struct exynos_drm_crtc *crtc) @@ -1020,42 +1059,13 @@ static void mixer_enable(struct exynos_drm_crtc *crtc) { struct mixer_context *ctx = crtc->ctx; struct mixer_resources *res = &ctx->mixer_res; - int ret; if (test_bit(MXR_BIT_POWERED, &ctx->flags)) return; pm_runtime_get_sync(ctx->dev); - ret = clk_prepare_enable(res->mixer); - if (ret < 0) { - DRM_ERROR("Failed to prepare_enable the mixer clk [%d]\n", ret); - return; - } - ret = clk_prepare_enable(res->hdmi); - if (ret < 0) { - DRM_ERROR("Failed to prepare_enable the hdmi clk [%d]\n", ret); - return; - } - if (ctx->vp_enabled) { - ret = clk_prepare_enable(res->vp); - if (ret < 0) { - DRM_ERROR("Failed to prepare_enable the vp clk [%d]\n", - ret); - return; - } - if (ctx->has_sclk) { - ret = clk_prepare_enable(res->sclk_mixer); - if (ret < 0) { - DRM_ERROR("Failed to prepare_enable the " \ - "sclk_mixer clk [%d]\n", - ret); - return; - } - } - } - - set_bit(MXR_BIT_POWERED, &ctx->flags); + mixer_vsync_set_update(ctx, false); mixer_reg_writemask(res, MXR_STATUS, ~0, MXR_STATUS_SOFT_RESET); @@ -1064,12 +1074,15 @@ static void mixer_enable(struct exynos_drm_crtc *crtc) mixer_reg_writemask(res, MXR_INT_EN, ~0, MXR_INT_EN_VSYNC); } mixer_win_reset(ctx); + + mixer_vsync_set_update(ctx, true); + + set_bit(MXR_BIT_POWERED, &ctx->flags); } static void mixer_disable(struct exynos_drm_crtc *crtc) { struct mixer_context *ctx = crtc->ctx; - struct mixer_resources *res = &ctx->mixer_res; int i; if (!test_bit(MXR_BIT_POWERED, &ctx->flags)) @@ -1081,17 +1094,9 @@ static void mixer_disable(struct exynos_drm_crtc *crtc) for (i = 0; i < MIXER_WIN_NR; i++) mixer_disable_plane(crtc, &ctx->planes[i]); - clear_bit(MXR_BIT_POWERED, &ctx->flags); + pm_runtime_put(ctx->dev); - clk_disable_unprepare(res->hdmi); - clk_disable_unprepare(res->mixer); - if (ctx->vp_enabled) { - clk_disable_unprepare(res->vp); - if (ctx->has_sclk) - clk_disable_unprepare(res->sclk_mixer); - } - - pm_runtime_put_sync(ctx->dev); + clear_bit(MXR_BIT_POWERED, &ctx->flags); } /* Only valid for Mixer version 16.0.33.0 */ @@ -1122,8 +1127,10 @@ static const struct exynos_drm_crtc_ops mixer_crtc_ops = { .enable_vblank = mixer_enable_vblank, .disable_vblank = mixer_disable_vblank, .wait_for_vblank = mixer_wait_for_vblank, + .atomic_begin = mixer_atomic_begin, .update_plane = mixer_update_plane, .disable_plane = mixer_disable_plane, + .atomic_flush = mixer_atomic_flush, .atomic_check = mixer_atomic_check, }; @@ -1187,30 +1194,19 @@ static int mixer_bind(struct device *dev, struct device *manager, void *data) struct mixer_context *ctx = dev_get_drvdata(dev); struct drm_device *drm_dev = data; struct exynos_drm_plane *exynos_plane; - unsigned int zpos; + unsigned int i; int ret; ret = mixer_initialize(ctx, drm_dev); if (ret) return ret; - for (zpos = 0; zpos < MIXER_WIN_NR; zpos++) { - enum drm_plane_type type; - const uint32_t *formats; - unsigned int fcount; - - if (zpos < VP_DEFAULT_WIN) { - formats = mixer_formats; - fcount = ARRAY_SIZE(mixer_formats); - } else { - formats = vp_formats; - fcount = ARRAY_SIZE(vp_formats); - } + for (i = 0; i < MIXER_WIN_NR; i++) { + if (i == VP_DEFAULT_WIN && !ctx->vp_enabled) + continue; - type = exynos_plane_get_type(zpos, CURSOR_WIN); - ret = exynos_plane_init(drm_dev, &ctx->planes[zpos], - 1 << ctx->pipe, type, formats, fcount, - zpos); + ret = exynos_plane_init(drm_dev, &ctx->planes[i], i, + 1 << ctx->pipe, &plane_configs[i]); if (ret) return ret; } @@ -1293,10 +1289,70 @@ static int mixer_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int exynos_mixer_suspend(struct device *dev) +{ + struct mixer_context *ctx = dev_get_drvdata(dev); + struct mixer_resources *res = &ctx->mixer_res; + + clk_disable_unprepare(res->hdmi); + clk_disable_unprepare(res->mixer); + if (ctx->vp_enabled) { + clk_disable_unprepare(res->vp); + if (ctx->has_sclk) + clk_disable_unprepare(res->sclk_mixer); + } + + return 0; +} + +static int exynos_mixer_resume(struct device *dev) +{ + struct mixer_context *ctx = dev_get_drvdata(dev); + struct mixer_resources *res = &ctx->mixer_res; + int ret; + + ret = clk_prepare_enable(res->mixer); + if (ret < 0) { + DRM_ERROR("Failed to prepare_enable the mixer clk [%d]\n", ret); + return ret; + } + ret = clk_prepare_enable(res->hdmi); + if (ret < 0) { + DRM_ERROR("Failed to prepare_enable the hdmi clk [%d]\n", ret); + return ret; + } + if (ctx->vp_enabled) { + ret = clk_prepare_enable(res->vp); + if (ret < 0) { + DRM_ERROR("Failed to prepare_enable the vp clk [%d]\n", + ret); + return ret; + } + if (ctx->has_sclk) { + ret = clk_prepare_enable(res->sclk_mixer); + if (ret < 0) { + DRM_ERROR("Failed to prepare_enable the " \ + "sclk_mixer clk [%d]\n", + ret); + return ret; + } + } + } + + return 0; +} +#endif + +static const struct dev_pm_ops exynos_mixer_pm_ops = { + SET_RUNTIME_PM_OPS(exynos_mixer_suspend, exynos_mixer_resume, NULL) +}; + struct platform_driver mixer_driver = { .driver = { .name = "exynos-mixer", .owner = THIS_MODULE, + .pm = &exynos_mixer_pm_ops, .of_match_table = mixer_match_types, }, .probe = mixer_probe, diff --git a/drivers/gpu/drm/exynos/regs-gsc.h b/drivers/gpu/drm/exynos/regs-gsc.h index 9ad5927..4704a99 100644 --- a/drivers/gpu/drm/exynos/regs-gsc.h +++ b/drivers/gpu/drm/exynos/regs-gsc.h @@ -273,12 +273,12 @@ #define GSC_CLK_GATE_MODE_SNOOP_CNT(x) ((x) << 0) /* SYSCON. GSCBLK_CFG */ -#define SYSREG_GSCBLK_CFG1 (S3C_VA_SYS + 0x0224) +#define SYSREG_GSCBLK_CFG1 0x0224 #define GSC_BLK_DISP1WB_DEST(x) (x << 10) #define GSC_BLK_SW_RESET_WB_DEST(x) (1 << (18 + x)) #define GSC_BLK_PXLASYNC_LO_MASK_WB(x) (0 << (14 + x)) #define GSC_BLK_GSCL_WB_IN_SRC_SEL(x) (1 << (2 * x)) -#define SYSREG_GSCBLK_CFG2 (S3C_VA_SYS + 0x2000) +#define SYSREG_GSCBLK_CFG2 0x2000 #define PXLASYNC_LO_MASK_CAMIF_GSCL(x) (1 << (x)) #endif /* EXYNOS_REGS_GSC_H_ */ diff --git a/drivers/gpu/drm/exynos/regs-mixer.h b/drivers/gpu/drm/exynos/regs-mixer.h index ac60260..7f22df5 100644 --- a/drivers/gpu/drm/exynos/regs-mixer.h +++ b/drivers/gpu/drm/exynos/regs-mixer.h @@ -113,6 +113,7 @@ #define MXR_GRP_CFG_BLEND_PRE_MUL (1 << 20) #define MXR_GRP_CFG_WIN_BLEND_EN (1 << 17) #define MXR_GRP_CFG_PIXEL_BLEND_EN (1 << 16) +#define MXR_GRP_CFG_MISC_MASK ((3 << 16) | (3 << 20)) #define MXR_GRP_CFG_FORMAT_VAL(x) MXR_MASK_VAL(x, 11, 8) #define MXR_GRP_CFG_FORMAT_MASK MXR_GRP_CFG_FORMAT_VAL(~0) #define MXR_GRP_CFG_ALPHA_VAL(x) MXR_MASK_VAL(x, 7, 0) @@ -145,8 +146,11 @@ /* bit for MXR_LAYER_CFG */ #define MXR_LAYER_CFG_GRP1_VAL(x) MXR_MASK_VAL(x, 11, 8) +#define MXR_LAYER_CFG_GRP1_MASK MXR_LAYER_CFG_GRP1_VAL(~0) #define MXR_LAYER_CFG_GRP0_VAL(x) MXR_MASK_VAL(x, 7, 4) +#define MXR_LAYER_CFG_GRP0_MASK MXR_LAYER_CFG_GRP0_VAL(~0) #define MXR_LAYER_CFG_VP_VAL(x) MXR_MASK_VAL(x, 3, 0) +#define MXR_LAYER_CFG_VP_MASK MXR_LAYER_CFG_VP_VAL(~0) #endif /* SAMSUNG_REGS_MIXER_H */ |