196976c3dSAjay Kumar /* drivers/gpu/drm/exynos/exynos7_drm_decon.c 296976c3dSAjay Kumar * 396976c3dSAjay Kumar * Copyright (C) 2014 Samsung Electronics Co.Ltd 496976c3dSAjay Kumar * Authors: 596976c3dSAjay Kumar * Akshu Agarwal <akshua@gmail.com> 696976c3dSAjay Kumar * Ajay Kumar <ajaykumar.rs@samsung.com> 796976c3dSAjay Kumar * 896976c3dSAjay Kumar * This program is free software; you can redistribute it and/or modify it 996976c3dSAjay Kumar * under the terms of the GNU General Public License as published by the 1096976c3dSAjay Kumar * Free Software Foundation; either version 2 of the License, or (at your 1196976c3dSAjay Kumar * option) any later version. 1296976c3dSAjay Kumar * 1396976c3dSAjay Kumar */ 1496976c3dSAjay Kumar #include <drm/drmP.h> 1596976c3dSAjay Kumar #include <drm/exynos_drm.h> 1696976c3dSAjay Kumar 1796976c3dSAjay Kumar #include <linux/clk.h> 1896976c3dSAjay Kumar #include <linux/component.h> 1996976c3dSAjay Kumar #include <linux/kernel.h> 2096976c3dSAjay Kumar #include <linux/of.h> 2196976c3dSAjay Kumar #include <linux/of_address.h> 2296976c3dSAjay Kumar #include <linux/of_device.h> 2396976c3dSAjay Kumar #include <linux/platform_device.h> 2496976c3dSAjay Kumar #include <linux/pm_runtime.h> 2596976c3dSAjay Kumar 2696976c3dSAjay Kumar #include <video/of_display_timing.h> 2796976c3dSAjay Kumar #include <video/of_videomode.h> 2896976c3dSAjay Kumar #include <video/exynos7_decon.h> 2996976c3dSAjay Kumar 3096976c3dSAjay Kumar #include "exynos_drm_crtc.h" 317ee14cdcSGustavo Padovan #include "exynos_drm_plane.h" 3296976c3dSAjay Kumar #include "exynos_drm_drv.h" 3396976c3dSAjay Kumar #include "exynos_drm_fbdev.h" 3496976c3dSAjay Kumar #include "exynos_drm_iommu.h" 3596976c3dSAjay Kumar 3696976c3dSAjay Kumar /* 3796976c3dSAjay Kumar * DECON stands for Display and Enhancement controller. 3896976c3dSAjay Kumar */ 3996976c3dSAjay Kumar 4096976c3dSAjay Kumar #define DECON_DEFAULT_FRAMERATE 60 4196976c3dSAjay Kumar #define MIN_FB_WIDTH_FOR_16WORD_BURST 128 4296976c3dSAjay Kumar 4396976c3dSAjay Kumar #define WINDOWS_NR 2 4496976c3dSAjay Kumar 4596976c3dSAjay Kumar struct decon_context { 4696976c3dSAjay Kumar struct device *dev; 4796976c3dSAjay Kumar struct drm_device *drm_dev; 4896976c3dSAjay Kumar struct exynos_drm_crtc *crtc; 497ee14cdcSGustavo Padovan struct exynos_drm_plane planes[WINDOWS_NR]; 5096976c3dSAjay Kumar struct clk *pclk; 5196976c3dSAjay Kumar struct clk *aclk; 5296976c3dSAjay Kumar struct clk *eclk; 5396976c3dSAjay Kumar struct clk *vclk; 5496976c3dSAjay Kumar void __iomem *regs; 5596976c3dSAjay Kumar unsigned int default_win; 5696976c3dSAjay Kumar unsigned long irq_flags; 5796976c3dSAjay Kumar bool i80_if; 5896976c3dSAjay Kumar bool suspended; 5996976c3dSAjay Kumar int pipe; 6096976c3dSAjay Kumar wait_queue_head_t wait_vsync_queue; 6196976c3dSAjay Kumar atomic_t wait_vsync_event; 6296976c3dSAjay Kumar 6396976c3dSAjay Kumar struct exynos_drm_panel_info panel; 6496976c3dSAjay Kumar struct exynos_drm_display *display; 6596976c3dSAjay Kumar }; 6696976c3dSAjay Kumar 6796976c3dSAjay Kumar static const struct of_device_id decon_driver_dt_match[] = { 6896976c3dSAjay Kumar {.compatible = "samsung,exynos7-decon"}, 6996976c3dSAjay Kumar {}, 7096976c3dSAjay Kumar }; 7196976c3dSAjay Kumar MODULE_DEVICE_TABLE(of, decon_driver_dt_match); 7296976c3dSAjay Kumar 7396976c3dSAjay Kumar static void decon_wait_for_vblank(struct exynos_drm_crtc *crtc) 7496976c3dSAjay Kumar { 7596976c3dSAjay Kumar struct decon_context *ctx = crtc->ctx; 7696976c3dSAjay Kumar 7796976c3dSAjay Kumar if (ctx->suspended) 7896976c3dSAjay Kumar return; 7996976c3dSAjay Kumar 8096976c3dSAjay Kumar atomic_set(&ctx->wait_vsync_event, 1); 8196976c3dSAjay Kumar 8296976c3dSAjay Kumar /* 8396976c3dSAjay Kumar * wait for DECON to signal VSYNC interrupt or return after 8496976c3dSAjay Kumar * timeout which is set to 50ms (refresh rate of 20). 8596976c3dSAjay Kumar */ 8696976c3dSAjay Kumar if (!wait_event_timeout(ctx->wait_vsync_queue, 8796976c3dSAjay Kumar !atomic_read(&ctx->wait_vsync_event), 8896976c3dSAjay Kumar HZ/20)) 8996976c3dSAjay Kumar DRM_DEBUG_KMS("vblank wait timed out.\n"); 9096976c3dSAjay Kumar } 9196976c3dSAjay Kumar 9296976c3dSAjay Kumar static void decon_clear_channel(struct decon_context *ctx) 9396976c3dSAjay Kumar { 945b1d5bc6STobias Jakobi unsigned int win, ch_enabled = 0; 9596976c3dSAjay Kumar 9696976c3dSAjay Kumar DRM_DEBUG_KMS("%s\n", __FILE__); 9796976c3dSAjay Kumar 9896976c3dSAjay Kumar /* Check if any channel is enabled. */ 9996976c3dSAjay Kumar for (win = 0; win < WINDOWS_NR; win++) { 10096976c3dSAjay Kumar u32 val = readl(ctx->regs + WINCON(win)); 10196976c3dSAjay Kumar 10296976c3dSAjay Kumar if (val & WINCONx_ENWIN) { 10396976c3dSAjay Kumar val &= ~WINCONx_ENWIN; 10496976c3dSAjay Kumar writel(val, ctx->regs + WINCON(win)); 10596976c3dSAjay Kumar ch_enabled = 1; 10696976c3dSAjay Kumar } 10796976c3dSAjay Kumar } 10896976c3dSAjay Kumar 10996976c3dSAjay Kumar /* Wait for vsync, as disable channel takes effect at next vsync */ 11096976c3dSAjay Kumar if (ch_enabled) { 11196976c3dSAjay Kumar unsigned int state = ctx->suspended; 11296976c3dSAjay Kumar 11396976c3dSAjay Kumar ctx->suspended = 0; 11496976c3dSAjay Kumar decon_wait_for_vblank(ctx->crtc); 11596976c3dSAjay Kumar ctx->suspended = state; 11696976c3dSAjay Kumar } 11796976c3dSAjay Kumar } 11896976c3dSAjay Kumar 11996976c3dSAjay Kumar static int decon_ctx_initialize(struct decon_context *ctx, 12096976c3dSAjay Kumar struct drm_device *drm_dev) 12196976c3dSAjay Kumar { 12296976c3dSAjay Kumar struct exynos_drm_private *priv = drm_dev->dev_private; 12396976c3dSAjay Kumar 12496976c3dSAjay Kumar ctx->drm_dev = drm_dev; 12596976c3dSAjay Kumar ctx->pipe = priv->pipe++; 12696976c3dSAjay Kumar 12796976c3dSAjay Kumar /* attach this sub driver to iommu mapping if supported. */ 12896976c3dSAjay Kumar if (is_drm_iommu_supported(ctx->drm_dev)) { 12996976c3dSAjay Kumar int ret; 13096976c3dSAjay Kumar 13196976c3dSAjay Kumar /* 13296976c3dSAjay Kumar * If any channel is already active, iommu will throw 13396976c3dSAjay Kumar * a PAGE FAULT when enabled. So clear any channel if enabled. 13496976c3dSAjay Kumar */ 13596976c3dSAjay Kumar decon_clear_channel(ctx); 13696976c3dSAjay Kumar ret = drm_iommu_attach_device(ctx->drm_dev, ctx->dev); 13796976c3dSAjay Kumar if (ret) { 13896976c3dSAjay Kumar DRM_ERROR("drm_iommu_attach failed.\n"); 13996976c3dSAjay Kumar return ret; 14096976c3dSAjay Kumar } 14196976c3dSAjay Kumar } 14296976c3dSAjay Kumar 14396976c3dSAjay Kumar return 0; 14496976c3dSAjay Kumar } 14596976c3dSAjay Kumar 14696976c3dSAjay Kumar static void decon_ctx_remove(struct decon_context *ctx) 14796976c3dSAjay Kumar { 14896976c3dSAjay Kumar /* detach this sub driver from iommu mapping if supported. */ 14996976c3dSAjay Kumar if (is_drm_iommu_supported(ctx->drm_dev)) 15096976c3dSAjay Kumar drm_iommu_detach_device(ctx->drm_dev, ctx->dev); 15196976c3dSAjay Kumar } 15296976c3dSAjay Kumar 15396976c3dSAjay Kumar static u32 decon_calc_clkdiv(struct decon_context *ctx, 15496976c3dSAjay Kumar const struct drm_display_mode *mode) 15596976c3dSAjay Kumar { 15696976c3dSAjay Kumar unsigned long ideal_clk = mode->htotal * mode->vtotal * mode->vrefresh; 15796976c3dSAjay Kumar u32 clkdiv; 15896976c3dSAjay Kumar 15996976c3dSAjay Kumar /* Find the clock divider value that gets us closest to ideal_clk */ 16096976c3dSAjay Kumar clkdiv = DIV_ROUND_UP(clk_get_rate(ctx->vclk), ideal_clk); 16196976c3dSAjay Kumar 16296976c3dSAjay Kumar return (clkdiv < 0x100) ? clkdiv : 0xff; 16396976c3dSAjay Kumar } 16496976c3dSAjay Kumar 16596976c3dSAjay Kumar static bool decon_mode_fixup(struct exynos_drm_crtc *crtc, 16696976c3dSAjay Kumar const struct drm_display_mode *mode, 16796976c3dSAjay Kumar struct drm_display_mode *adjusted_mode) 16896976c3dSAjay Kumar { 16996976c3dSAjay Kumar if (adjusted_mode->vrefresh == 0) 17096976c3dSAjay Kumar adjusted_mode->vrefresh = DECON_DEFAULT_FRAMERATE; 17196976c3dSAjay Kumar 17296976c3dSAjay Kumar return true; 17396976c3dSAjay Kumar } 17496976c3dSAjay Kumar 17596976c3dSAjay Kumar static void decon_commit(struct exynos_drm_crtc *crtc) 17696976c3dSAjay Kumar { 17796976c3dSAjay Kumar struct decon_context *ctx = crtc->ctx; 178020e79deSJoonyoung Shim struct drm_display_mode *mode = &crtc->base.state->adjusted_mode; 17996976c3dSAjay Kumar u32 val, clkdiv; 18096976c3dSAjay Kumar 18196976c3dSAjay Kumar if (ctx->suspended) 18296976c3dSAjay Kumar return; 18396976c3dSAjay Kumar 18496976c3dSAjay Kumar /* nothing to do if we haven't set the mode yet */ 18596976c3dSAjay Kumar if (mode->htotal == 0 || mode->vtotal == 0) 18696976c3dSAjay Kumar return; 18796976c3dSAjay Kumar 18896976c3dSAjay Kumar if (!ctx->i80_if) { 18996976c3dSAjay Kumar int vsync_len, vbpd, vfpd, hsync_len, hbpd, hfpd; 19096976c3dSAjay Kumar /* setup vertical timing values. */ 19196976c3dSAjay Kumar vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start; 19296976c3dSAjay Kumar vbpd = mode->crtc_vtotal - mode->crtc_vsync_end; 19396976c3dSAjay Kumar vfpd = mode->crtc_vsync_start - mode->crtc_vdisplay; 19496976c3dSAjay Kumar 19596976c3dSAjay Kumar val = VIDTCON0_VBPD(vbpd - 1) | VIDTCON0_VFPD(vfpd - 1); 19696976c3dSAjay Kumar writel(val, ctx->regs + VIDTCON0); 19796976c3dSAjay Kumar 19896976c3dSAjay Kumar val = VIDTCON1_VSPW(vsync_len - 1); 19996976c3dSAjay Kumar writel(val, ctx->regs + VIDTCON1); 20096976c3dSAjay Kumar 20196976c3dSAjay Kumar /* setup horizontal timing values. */ 20296976c3dSAjay Kumar hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start; 20396976c3dSAjay Kumar hbpd = mode->crtc_htotal - mode->crtc_hsync_end; 20496976c3dSAjay Kumar hfpd = mode->crtc_hsync_start - mode->crtc_hdisplay; 20596976c3dSAjay Kumar 20696976c3dSAjay Kumar /* setup horizontal timing values. */ 20796976c3dSAjay Kumar val = VIDTCON2_HBPD(hbpd - 1) | VIDTCON2_HFPD(hfpd - 1); 20896976c3dSAjay Kumar writel(val, ctx->regs + VIDTCON2); 20996976c3dSAjay Kumar 21096976c3dSAjay Kumar val = VIDTCON3_HSPW(hsync_len - 1); 21196976c3dSAjay Kumar writel(val, ctx->regs + VIDTCON3); 21296976c3dSAjay Kumar } 21396976c3dSAjay Kumar 21496976c3dSAjay Kumar /* setup horizontal and vertical display size. */ 21596976c3dSAjay Kumar val = VIDTCON4_LINEVAL(mode->vdisplay - 1) | 21696976c3dSAjay Kumar VIDTCON4_HOZVAL(mode->hdisplay - 1); 21796976c3dSAjay Kumar writel(val, ctx->regs + VIDTCON4); 21896976c3dSAjay Kumar 21996976c3dSAjay Kumar writel(mode->vdisplay - 1, ctx->regs + LINECNT_OP_THRESHOLD); 22096976c3dSAjay Kumar 22196976c3dSAjay Kumar /* 22296976c3dSAjay Kumar * fields of register with prefix '_F' would be updated 22396976c3dSAjay Kumar * at vsync(same as dma start) 22496976c3dSAjay Kumar */ 22596976c3dSAjay Kumar val = VIDCON0_ENVID | VIDCON0_ENVID_F; 22696976c3dSAjay Kumar writel(val, ctx->regs + VIDCON0); 22796976c3dSAjay Kumar 22896976c3dSAjay Kumar clkdiv = decon_calc_clkdiv(ctx, mode); 22996976c3dSAjay Kumar if (clkdiv > 1) { 23096976c3dSAjay Kumar val = VCLKCON1_CLKVAL_NUM_VCLK(clkdiv - 1); 23196976c3dSAjay Kumar writel(val, ctx->regs + VCLKCON1); 23296976c3dSAjay Kumar writel(val, ctx->regs + VCLKCON2); 23396976c3dSAjay Kumar } 23496976c3dSAjay Kumar 23596976c3dSAjay Kumar val = readl(ctx->regs + DECON_UPDATE); 23696976c3dSAjay Kumar val |= DECON_UPDATE_STANDALONE_F; 23796976c3dSAjay Kumar writel(val, ctx->regs + DECON_UPDATE); 23896976c3dSAjay Kumar } 23996976c3dSAjay Kumar 24096976c3dSAjay Kumar static int decon_enable_vblank(struct exynos_drm_crtc *crtc) 24196976c3dSAjay Kumar { 24296976c3dSAjay Kumar struct decon_context *ctx = crtc->ctx; 24396976c3dSAjay Kumar u32 val; 24496976c3dSAjay Kumar 24596976c3dSAjay Kumar if (ctx->suspended) 24696976c3dSAjay Kumar return -EPERM; 24796976c3dSAjay Kumar 24896976c3dSAjay Kumar if (!test_and_set_bit(0, &ctx->irq_flags)) { 24996976c3dSAjay Kumar val = readl(ctx->regs + VIDINTCON0); 25096976c3dSAjay Kumar 25196976c3dSAjay Kumar val |= VIDINTCON0_INT_ENABLE; 25296976c3dSAjay Kumar 25396976c3dSAjay Kumar if (!ctx->i80_if) { 25496976c3dSAjay Kumar val |= VIDINTCON0_INT_FRAME; 25596976c3dSAjay Kumar val &= ~VIDINTCON0_FRAMESEL0_MASK; 25696976c3dSAjay Kumar val |= VIDINTCON0_FRAMESEL0_VSYNC; 25796976c3dSAjay Kumar } 25896976c3dSAjay Kumar 25996976c3dSAjay Kumar writel(val, ctx->regs + VIDINTCON0); 26096976c3dSAjay Kumar } 26196976c3dSAjay Kumar 26296976c3dSAjay Kumar return 0; 26396976c3dSAjay Kumar } 26496976c3dSAjay Kumar 26596976c3dSAjay Kumar static void decon_disable_vblank(struct exynos_drm_crtc *crtc) 26696976c3dSAjay Kumar { 26796976c3dSAjay Kumar struct decon_context *ctx = crtc->ctx; 26896976c3dSAjay Kumar u32 val; 26996976c3dSAjay Kumar 27096976c3dSAjay Kumar if (ctx->suspended) 27196976c3dSAjay Kumar return; 27296976c3dSAjay Kumar 27396976c3dSAjay Kumar if (test_and_clear_bit(0, &ctx->irq_flags)) { 27496976c3dSAjay Kumar val = readl(ctx->regs + VIDINTCON0); 27596976c3dSAjay Kumar 27696976c3dSAjay Kumar val &= ~VIDINTCON0_INT_ENABLE; 27796976c3dSAjay Kumar if (!ctx->i80_if) 27896976c3dSAjay Kumar val &= ~VIDINTCON0_INT_FRAME; 27996976c3dSAjay Kumar 28096976c3dSAjay Kumar writel(val, ctx->regs + VIDINTCON0); 28196976c3dSAjay Kumar } 28296976c3dSAjay Kumar } 28396976c3dSAjay Kumar 28496976c3dSAjay Kumar static void decon_win_set_pixfmt(struct decon_context *ctx, unsigned int win) 28596976c3dSAjay Kumar { 2867ee14cdcSGustavo Padovan struct exynos_drm_plane *plane = &ctx->planes[win]; 28796976c3dSAjay Kumar unsigned long val; 2887ee14cdcSGustavo Padovan int padding; 28996976c3dSAjay Kumar 29096976c3dSAjay Kumar val = readl(ctx->regs + WINCON(win)); 29196976c3dSAjay Kumar val &= ~WINCONx_BPPMODE_MASK; 29296976c3dSAjay Kumar 2937ee14cdcSGustavo Padovan switch (plane->pixel_format) { 29496976c3dSAjay Kumar case DRM_FORMAT_RGB565: 29596976c3dSAjay Kumar val |= WINCONx_BPPMODE_16BPP_565; 29696976c3dSAjay Kumar val |= WINCONx_BURSTLEN_16WORD; 29796976c3dSAjay Kumar break; 29896976c3dSAjay Kumar case DRM_FORMAT_XRGB8888: 29996976c3dSAjay Kumar val |= WINCONx_BPPMODE_24BPP_xRGB; 30096976c3dSAjay Kumar val |= WINCONx_BURSTLEN_16WORD; 30196976c3dSAjay Kumar break; 30296976c3dSAjay Kumar case DRM_FORMAT_XBGR8888: 30396976c3dSAjay Kumar val |= WINCONx_BPPMODE_24BPP_xBGR; 30496976c3dSAjay Kumar val |= WINCONx_BURSTLEN_16WORD; 30596976c3dSAjay Kumar break; 30696976c3dSAjay Kumar case DRM_FORMAT_RGBX8888: 30796976c3dSAjay Kumar val |= WINCONx_BPPMODE_24BPP_RGBx; 30896976c3dSAjay Kumar val |= WINCONx_BURSTLEN_16WORD; 30996976c3dSAjay Kumar break; 31096976c3dSAjay Kumar case DRM_FORMAT_BGRX8888: 31196976c3dSAjay Kumar val |= WINCONx_BPPMODE_24BPP_BGRx; 31296976c3dSAjay Kumar val |= WINCONx_BURSTLEN_16WORD; 31396976c3dSAjay Kumar break; 31496976c3dSAjay Kumar case DRM_FORMAT_ARGB8888: 31596976c3dSAjay Kumar val |= WINCONx_BPPMODE_32BPP_ARGB | WINCONx_BLD_PIX | 31696976c3dSAjay Kumar WINCONx_ALPHA_SEL; 31796976c3dSAjay Kumar val |= WINCONx_BURSTLEN_16WORD; 31896976c3dSAjay Kumar break; 31996976c3dSAjay Kumar case DRM_FORMAT_ABGR8888: 32096976c3dSAjay Kumar val |= WINCONx_BPPMODE_32BPP_ABGR | WINCONx_BLD_PIX | 32196976c3dSAjay Kumar WINCONx_ALPHA_SEL; 32296976c3dSAjay Kumar val |= WINCONx_BURSTLEN_16WORD; 32396976c3dSAjay Kumar break; 32496976c3dSAjay Kumar case DRM_FORMAT_RGBA8888: 32596976c3dSAjay Kumar val |= WINCONx_BPPMODE_32BPP_RGBA | WINCONx_BLD_PIX | 32696976c3dSAjay Kumar WINCONx_ALPHA_SEL; 32796976c3dSAjay Kumar val |= WINCONx_BURSTLEN_16WORD; 32896976c3dSAjay Kumar break; 32996976c3dSAjay Kumar case DRM_FORMAT_BGRA8888: 33096976c3dSAjay Kumar val |= WINCONx_BPPMODE_32BPP_BGRA | WINCONx_BLD_PIX | 33196976c3dSAjay Kumar WINCONx_ALPHA_SEL; 33296976c3dSAjay Kumar val |= WINCONx_BURSTLEN_16WORD; 33396976c3dSAjay Kumar break; 33496976c3dSAjay Kumar default: 33596976c3dSAjay Kumar DRM_DEBUG_KMS("invalid pixel size so using unpacked 24bpp.\n"); 33696976c3dSAjay Kumar 33796976c3dSAjay Kumar val |= WINCONx_BPPMODE_24BPP_xRGB; 33896976c3dSAjay Kumar val |= WINCONx_BURSTLEN_16WORD; 33996976c3dSAjay Kumar break; 34096976c3dSAjay Kumar } 34196976c3dSAjay Kumar 3427ee14cdcSGustavo Padovan DRM_DEBUG_KMS("bpp = %d\n", plane->bpp); 34396976c3dSAjay Kumar 34496976c3dSAjay Kumar /* 34596976c3dSAjay Kumar * In case of exynos, setting dma-burst to 16Word causes permanent 34696976c3dSAjay Kumar * tearing for very small buffers, e.g. cursor buffer. Burst Mode 34796976c3dSAjay Kumar * switching which is based on plane size is not recommended as 34896976c3dSAjay Kumar * plane size varies a lot towards the end of the screen and rapid 34996976c3dSAjay Kumar * movement causes unstable DMA which results into iommu crash/tear. 35096976c3dSAjay Kumar */ 35196976c3dSAjay Kumar 3527ee14cdcSGustavo Padovan padding = (plane->pitch / (plane->bpp >> 3)) - plane->fb_width; 3537ee14cdcSGustavo Padovan if (plane->fb_width + padding < MIN_FB_WIDTH_FOR_16WORD_BURST) { 35496976c3dSAjay Kumar val &= ~WINCONx_BURSTLEN_MASK; 35596976c3dSAjay Kumar val |= WINCONx_BURSTLEN_8WORD; 35696976c3dSAjay Kumar } 35796976c3dSAjay Kumar 35896976c3dSAjay Kumar writel(val, ctx->regs + WINCON(win)); 35996976c3dSAjay Kumar } 36096976c3dSAjay Kumar 36196976c3dSAjay Kumar static void decon_win_set_colkey(struct decon_context *ctx, unsigned int win) 36296976c3dSAjay Kumar { 36396976c3dSAjay Kumar unsigned int keycon0 = 0, keycon1 = 0; 36496976c3dSAjay Kumar 36596976c3dSAjay Kumar keycon0 = ~(WxKEYCON0_KEYBL_EN | WxKEYCON0_KEYEN_F | 36696976c3dSAjay Kumar WxKEYCON0_DIRCON) | WxKEYCON0_COMPKEY(0); 36796976c3dSAjay Kumar 36896976c3dSAjay Kumar keycon1 = WxKEYCON1_COLVAL(0xffffffff); 36996976c3dSAjay Kumar 37096976c3dSAjay Kumar writel(keycon0, ctx->regs + WKEYCON0_BASE(win)); 37196976c3dSAjay Kumar writel(keycon1, ctx->regs + WKEYCON1_BASE(win)); 37296976c3dSAjay Kumar } 37396976c3dSAjay Kumar 37496976c3dSAjay Kumar /** 37596976c3dSAjay Kumar * shadow_protect_win() - disable updating values from shadow registers at vsync 37696976c3dSAjay Kumar * 37796976c3dSAjay Kumar * @win: window to protect registers for 37896976c3dSAjay Kumar * @protect: 1 to protect (disable updates) 37996976c3dSAjay Kumar */ 38096976c3dSAjay Kumar static void decon_shadow_protect_win(struct decon_context *ctx, 3816e2a3b66SGustavo Padovan unsigned int win, bool protect) 38296976c3dSAjay Kumar { 38396976c3dSAjay Kumar u32 bits, val; 38496976c3dSAjay Kumar 38596976c3dSAjay Kumar bits = SHADOWCON_WINx_PROTECT(win); 38696976c3dSAjay Kumar 38796976c3dSAjay Kumar val = readl(ctx->regs + SHADOWCON); 38896976c3dSAjay Kumar if (protect) 38996976c3dSAjay Kumar val |= bits; 39096976c3dSAjay Kumar else 39196976c3dSAjay Kumar val &= ~bits; 39296976c3dSAjay Kumar writel(val, ctx->regs + SHADOWCON); 39396976c3dSAjay Kumar } 39496976c3dSAjay Kumar 3956e2a3b66SGustavo Padovan static void decon_win_commit(struct exynos_drm_crtc *crtc, unsigned int win) 39696976c3dSAjay Kumar { 39796976c3dSAjay Kumar struct decon_context *ctx = crtc->ctx; 398020e79deSJoonyoung Shim struct drm_display_mode *mode = &crtc->base.state->adjusted_mode; 3997ee14cdcSGustavo Padovan struct exynos_drm_plane *plane; 4006e2a3b66SGustavo Padovan int padding; 40196976c3dSAjay Kumar unsigned long val, alpha; 40296976c3dSAjay Kumar unsigned int last_x; 40396976c3dSAjay Kumar unsigned int last_y; 40496976c3dSAjay Kumar 40596976c3dSAjay Kumar if (ctx->suspended) 40696976c3dSAjay Kumar return; 40796976c3dSAjay Kumar 40896976c3dSAjay Kumar if (win < 0 || win >= WINDOWS_NR) 40996976c3dSAjay Kumar return; 41096976c3dSAjay Kumar 4117ee14cdcSGustavo Padovan plane = &ctx->planes[win]; 41296976c3dSAjay Kumar 41396976c3dSAjay Kumar /* If suspended, enable this on resume */ 41496976c3dSAjay Kumar if (ctx->suspended) { 4157ee14cdcSGustavo Padovan plane->resume = true; 41696976c3dSAjay Kumar return; 41796976c3dSAjay Kumar } 41896976c3dSAjay Kumar 41996976c3dSAjay Kumar /* 42096976c3dSAjay Kumar * SHADOWCON/PRTCON register is used for enabling timing. 42196976c3dSAjay Kumar * 42296976c3dSAjay Kumar * for example, once only width value of a register is set, 42396976c3dSAjay Kumar * if the dma is started then decon hardware could malfunction so 42496976c3dSAjay Kumar * with protect window setting, the register fields with prefix '_F' 42596976c3dSAjay Kumar * wouldn't be updated at vsync also but updated once unprotect window 42696976c3dSAjay Kumar * is set. 42796976c3dSAjay Kumar */ 42896976c3dSAjay Kumar 42996976c3dSAjay Kumar /* protect windows */ 43096976c3dSAjay Kumar decon_shadow_protect_win(ctx, win, true); 43196976c3dSAjay Kumar 43296976c3dSAjay Kumar /* buffer start address */ 4337ee14cdcSGustavo Padovan val = (unsigned long)plane->dma_addr[0]; 43496976c3dSAjay Kumar writel(val, ctx->regs + VIDW_BUF_START(win)); 43596976c3dSAjay Kumar 4367ee14cdcSGustavo Padovan padding = (plane->pitch / (plane->bpp >> 3)) - plane->fb_width; 4377ee14cdcSGustavo Padovan 43896976c3dSAjay Kumar /* buffer size */ 4397ee14cdcSGustavo Padovan writel(plane->fb_width + padding, ctx->regs + VIDW_WHOLE_X(win)); 4407ee14cdcSGustavo Padovan writel(plane->fb_height, ctx->regs + VIDW_WHOLE_Y(win)); 44196976c3dSAjay Kumar 44296976c3dSAjay Kumar /* offset from the start of the buffer to read */ 443cb8a3db2SJoonyoung Shim writel(plane->src_x, ctx->regs + VIDW_OFFSET_X(win)); 444cb8a3db2SJoonyoung Shim writel(plane->src_y, ctx->regs + VIDW_OFFSET_Y(win)); 44596976c3dSAjay Kumar 44696976c3dSAjay Kumar DRM_DEBUG_KMS("start addr = 0x%lx\n", 4477ee14cdcSGustavo Padovan (unsigned long)val); 44896976c3dSAjay Kumar DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n", 4497ee14cdcSGustavo Padovan plane->crtc_width, plane->crtc_height); 45096976c3dSAjay Kumar 45196976c3dSAjay Kumar /* 45296976c3dSAjay Kumar * OSD position. 45396976c3dSAjay Kumar * In case the window layout goes of LCD layout, DECON fails. 45496976c3dSAjay Kumar */ 4557ee14cdcSGustavo Padovan if ((plane->crtc_x + plane->crtc_width) > mode->hdisplay) 4567ee14cdcSGustavo Padovan plane->crtc_x = mode->hdisplay - plane->crtc_width; 4577ee14cdcSGustavo Padovan if ((plane->crtc_y + plane->crtc_height) > mode->vdisplay) 4587ee14cdcSGustavo Padovan plane->crtc_y = mode->vdisplay - plane->crtc_height; 45996976c3dSAjay Kumar 4607ee14cdcSGustavo Padovan val = VIDOSDxA_TOPLEFT_X(plane->crtc_x) | 4617ee14cdcSGustavo Padovan VIDOSDxA_TOPLEFT_Y(plane->crtc_y); 46296976c3dSAjay Kumar writel(val, ctx->regs + VIDOSD_A(win)); 46396976c3dSAjay Kumar 4647ee14cdcSGustavo Padovan last_x = plane->crtc_x + plane->crtc_width; 46596976c3dSAjay Kumar if (last_x) 46696976c3dSAjay Kumar last_x--; 4677ee14cdcSGustavo Padovan last_y = plane->crtc_y + plane->crtc_height; 46896976c3dSAjay Kumar if (last_y) 46996976c3dSAjay Kumar last_y--; 47096976c3dSAjay Kumar 47196976c3dSAjay Kumar val = VIDOSDxB_BOTRIGHT_X(last_x) | VIDOSDxB_BOTRIGHT_Y(last_y); 47296976c3dSAjay Kumar 47396976c3dSAjay Kumar writel(val, ctx->regs + VIDOSD_B(win)); 47496976c3dSAjay Kumar 47596976c3dSAjay Kumar DRM_DEBUG_KMS("osd pos: tx = %d, ty = %d, bx = %d, by = %d\n", 4767ee14cdcSGustavo Padovan plane->crtc_x, plane->crtc_y, last_x, last_y); 47796976c3dSAjay Kumar 47896976c3dSAjay Kumar /* OSD alpha */ 47996976c3dSAjay Kumar alpha = VIDOSDxC_ALPHA0_R_F(0x0) | 48096976c3dSAjay Kumar VIDOSDxC_ALPHA0_G_F(0x0) | 48196976c3dSAjay Kumar VIDOSDxC_ALPHA0_B_F(0x0); 48296976c3dSAjay Kumar 48396976c3dSAjay Kumar writel(alpha, ctx->regs + VIDOSD_C(win)); 48496976c3dSAjay Kumar 48596976c3dSAjay Kumar alpha = VIDOSDxD_ALPHA1_R_F(0xff) | 48696976c3dSAjay Kumar VIDOSDxD_ALPHA1_G_F(0xff) | 48796976c3dSAjay Kumar VIDOSDxD_ALPHA1_B_F(0xff); 48896976c3dSAjay Kumar 48996976c3dSAjay Kumar writel(alpha, ctx->regs + VIDOSD_D(win)); 49096976c3dSAjay Kumar 49196976c3dSAjay Kumar decon_win_set_pixfmt(ctx, win); 49296976c3dSAjay Kumar 49396976c3dSAjay Kumar /* hardware window 0 doesn't support color key. */ 49496976c3dSAjay Kumar if (win != 0) 49596976c3dSAjay Kumar decon_win_set_colkey(ctx, win); 49696976c3dSAjay Kumar 49796976c3dSAjay Kumar /* wincon */ 49896976c3dSAjay Kumar val = readl(ctx->regs + WINCON(win)); 49996976c3dSAjay Kumar val |= WINCONx_TRIPLE_BUF_MODE; 50096976c3dSAjay Kumar val |= WINCONx_ENWIN; 50196976c3dSAjay Kumar writel(val, ctx->regs + WINCON(win)); 50296976c3dSAjay Kumar 50396976c3dSAjay Kumar /* Enable DMA channel and unprotect windows */ 50496976c3dSAjay Kumar decon_shadow_protect_win(ctx, win, false); 50596976c3dSAjay Kumar 50696976c3dSAjay Kumar val = readl(ctx->regs + DECON_UPDATE); 50796976c3dSAjay Kumar val |= DECON_UPDATE_STANDALONE_F; 50896976c3dSAjay Kumar writel(val, ctx->regs + DECON_UPDATE); 50996976c3dSAjay Kumar 5107ee14cdcSGustavo Padovan plane->enabled = true; 51196976c3dSAjay Kumar } 51296976c3dSAjay Kumar 5136e2a3b66SGustavo Padovan static void decon_win_disable(struct exynos_drm_crtc *crtc, unsigned int win) 51496976c3dSAjay Kumar { 51596976c3dSAjay Kumar struct decon_context *ctx = crtc->ctx; 5167ee14cdcSGustavo Padovan struct exynos_drm_plane *plane; 51796976c3dSAjay Kumar u32 val; 51896976c3dSAjay Kumar 51996976c3dSAjay Kumar if (win < 0 || win >= WINDOWS_NR) 52096976c3dSAjay Kumar return; 52196976c3dSAjay Kumar 5227ee14cdcSGustavo Padovan plane = &ctx->planes[win]; 52396976c3dSAjay Kumar 52496976c3dSAjay Kumar if (ctx->suspended) { 52596976c3dSAjay Kumar /* do not resume this window*/ 5267ee14cdcSGustavo Padovan plane->resume = false; 52796976c3dSAjay Kumar return; 52896976c3dSAjay Kumar } 52996976c3dSAjay Kumar 53096976c3dSAjay Kumar /* protect windows */ 53196976c3dSAjay Kumar decon_shadow_protect_win(ctx, win, true); 53296976c3dSAjay Kumar 53396976c3dSAjay Kumar /* wincon */ 53496976c3dSAjay Kumar val = readl(ctx->regs + WINCON(win)); 53596976c3dSAjay Kumar val &= ~WINCONx_ENWIN; 53696976c3dSAjay Kumar writel(val, ctx->regs + WINCON(win)); 53796976c3dSAjay Kumar 53896976c3dSAjay Kumar /* unprotect windows */ 53996976c3dSAjay Kumar decon_shadow_protect_win(ctx, win, false); 54096976c3dSAjay Kumar 54196976c3dSAjay Kumar val = readl(ctx->regs + DECON_UPDATE); 54296976c3dSAjay Kumar val |= DECON_UPDATE_STANDALONE_F; 54396976c3dSAjay Kumar writel(val, ctx->regs + DECON_UPDATE); 54496976c3dSAjay Kumar 5457ee14cdcSGustavo Padovan plane->enabled = false; 54696976c3dSAjay Kumar } 54796976c3dSAjay Kumar 54896976c3dSAjay Kumar static void decon_window_suspend(struct decon_context *ctx) 54996976c3dSAjay Kumar { 5507ee14cdcSGustavo Padovan struct exynos_drm_plane *plane; 55196976c3dSAjay Kumar int i; 55296976c3dSAjay Kumar 55396976c3dSAjay Kumar for (i = 0; i < WINDOWS_NR; i++) { 5547ee14cdcSGustavo Padovan plane = &ctx->planes[i]; 5557ee14cdcSGustavo Padovan plane->resume = plane->enabled; 5567ee14cdcSGustavo Padovan if (plane->enabled) 55796976c3dSAjay Kumar decon_win_disable(ctx->crtc, i); 55896976c3dSAjay Kumar } 55996976c3dSAjay Kumar } 56096976c3dSAjay Kumar 56196976c3dSAjay Kumar static void decon_window_resume(struct decon_context *ctx) 56296976c3dSAjay Kumar { 5637ee14cdcSGustavo Padovan struct exynos_drm_plane *plane; 56496976c3dSAjay Kumar int i; 56596976c3dSAjay Kumar 56696976c3dSAjay Kumar for (i = 0; i < WINDOWS_NR; i++) { 5677ee14cdcSGustavo Padovan plane = &ctx->planes[i]; 5687ee14cdcSGustavo Padovan plane->enabled = plane->resume; 5697ee14cdcSGustavo Padovan plane->resume = false; 57096976c3dSAjay Kumar } 57196976c3dSAjay Kumar } 57296976c3dSAjay Kumar 57396976c3dSAjay Kumar static void decon_apply(struct decon_context *ctx) 57496976c3dSAjay Kumar { 5757ee14cdcSGustavo Padovan struct exynos_drm_plane *plane; 57696976c3dSAjay Kumar int i; 57796976c3dSAjay Kumar 57896976c3dSAjay Kumar for (i = 0; i < WINDOWS_NR; i++) { 5797ee14cdcSGustavo Padovan plane = &ctx->planes[i]; 5807ee14cdcSGustavo Padovan if (plane->enabled) 58196976c3dSAjay Kumar decon_win_commit(ctx->crtc, i); 58296976c3dSAjay Kumar else 58396976c3dSAjay Kumar decon_win_disable(ctx->crtc, i); 58496976c3dSAjay Kumar } 58596976c3dSAjay Kumar 58696976c3dSAjay Kumar decon_commit(ctx->crtc); 58796976c3dSAjay Kumar } 58896976c3dSAjay Kumar 58996976c3dSAjay Kumar static void decon_init(struct decon_context *ctx) 59096976c3dSAjay Kumar { 59196976c3dSAjay Kumar u32 val; 59296976c3dSAjay Kumar 59396976c3dSAjay Kumar writel(VIDCON0_SWRESET, ctx->regs + VIDCON0); 59496976c3dSAjay Kumar 59596976c3dSAjay Kumar val = VIDOUTCON0_DISP_IF_0_ON; 59696976c3dSAjay Kumar if (!ctx->i80_if) 59796976c3dSAjay Kumar val |= VIDOUTCON0_RGBIF; 59896976c3dSAjay Kumar writel(val, ctx->regs + VIDOUTCON0); 59996976c3dSAjay Kumar 60096976c3dSAjay Kumar writel(VCLKCON0_CLKVALUP | VCLKCON0_VCLKFREE, ctx->regs + VCLKCON0); 60196976c3dSAjay Kumar 60296976c3dSAjay Kumar if (!ctx->i80_if) 60396976c3dSAjay Kumar writel(VIDCON1_VCLK_HOLD, ctx->regs + VIDCON1(0)); 60496976c3dSAjay Kumar } 60596976c3dSAjay Kumar 6063cecda03SGustavo Padovan static void decon_enable(struct exynos_drm_crtc *crtc) 60796976c3dSAjay Kumar { 6083cecda03SGustavo Padovan struct decon_context *ctx = crtc->ctx; 60938000dbbSGustavo Padovan int ret; 61096976c3dSAjay Kumar 61196976c3dSAjay Kumar if (!ctx->suspended) 6123cecda03SGustavo Padovan return; 61396976c3dSAjay Kumar 61496976c3dSAjay Kumar ctx->suspended = false; 61596976c3dSAjay Kumar 61696976c3dSAjay Kumar pm_runtime_get_sync(ctx->dev); 61796976c3dSAjay Kumar 61838000dbbSGustavo Padovan ret = clk_prepare_enable(ctx->pclk); 61938000dbbSGustavo Padovan if (ret < 0) { 62038000dbbSGustavo Padovan DRM_ERROR("Failed to prepare_enable the pclk [%d]\n", ret); 62138000dbbSGustavo Padovan return; 62238000dbbSGustavo Padovan } 62338000dbbSGustavo Padovan 62438000dbbSGustavo Padovan ret = clk_prepare_enable(ctx->aclk); 62538000dbbSGustavo Padovan if (ret < 0) { 62638000dbbSGustavo Padovan DRM_ERROR("Failed to prepare_enable the aclk [%d]\n", ret); 62738000dbbSGustavo Padovan return; 62838000dbbSGustavo Padovan } 62938000dbbSGustavo Padovan 63038000dbbSGustavo Padovan ret = clk_prepare_enable(ctx->eclk); 63138000dbbSGustavo Padovan if (ret < 0) { 63238000dbbSGustavo Padovan DRM_ERROR("Failed to prepare_enable the eclk [%d]\n", ret); 63338000dbbSGustavo Padovan return; 63438000dbbSGustavo Padovan } 63538000dbbSGustavo Padovan 63638000dbbSGustavo Padovan ret = clk_prepare_enable(ctx->vclk); 63738000dbbSGustavo Padovan if (ret < 0) { 63838000dbbSGustavo Padovan DRM_ERROR("Failed to prepare_enable the vclk [%d]\n", ret); 63938000dbbSGustavo Padovan return; 64038000dbbSGustavo Padovan } 64196976c3dSAjay Kumar 64296976c3dSAjay Kumar decon_init(ctx); 64396976c3dSAjay Kumar 64496976c3dSAjay Kumar /* if vblank was enabled status, enable it again. */ 6453cecda03SGustavo Padovan if (test_and_clear_bit(0, &ctx->irq_flags)) 6463cecda03SGustavo Padovan decon_enable_vblank(ctx->crtc); 64796976c3dSAjay Kumar 64896976c3dSAjay Kumar decon_window_resume(ctx); 64996976c3dSAjay Kumar 65096976c3dSAjay Kumar decon_apply(ctx); 65196976c3dSAjay Kumar } 65296976c3dSAjay Kumar 6533cecda03SGustavo Padovan static void decon_disable(struct exynos_drm_crtc *crtc) 65496976c3dSAjay Kumar { 6553cecda03SGustavo Padovan struct decon_context *ctx = crtc->ctx; 6563cecda03SGustavo Padovan 65796976c3dSAjay Kumar if (ctx->suspended) 6583cecda03SGustavo Padovan return; 65996976c3dSAjay Kumar 66096976c3dSAjay Kumar /* 66196976c3dSAjay Kumar * We need to make sure that all windows are disabled before we 66296976c3dSAjay Kumar * suspend that connector. Otherwise we might try to scan from 66396976c3dSAjay Kumar * a destroyed buffer later. 66496976c3dSAjay Kumar */ 66596976c3dSAjay Kumar decon_window_suspend(ctx); 66696976c3dSAjay Kumar 66796976c3dSAjay Kumar clk_disable_unprepare(ctx->vclk); 66896976c3dSAjay Kumar clk_disable_unprepare(ctx->eclk); 66996976c3dSAjay Kumar clk_disable_unprepare(ctx->aclk); 67096976c3dSAjay Kumar clk_disable_unprepare(ctx->pclk); 67196976c3dSAjay Kumar 67296976c3dSAjay Kumar pm_runtime_put_sync(ctx->dev); 67396976c3dSAjay Kumar 67496976c3dSAjay Kumar ctx->suspended = true; 67596976c3dSAjay Kumar } 67696976c3dSAjay Kumar 677f3aaf762SKrzysztof Kozlowski static const struct exynos_drm_crtc_ops decon_crtc_ops = { 6783cecda03SGustavo Padovan .enable = decon_enable, 6793cecda03SGustavo Padovan .disable = decon_disable, 68096976c3dSAjay Kumar .mode_fixup = decon_mode_fixup, 68196976c3dSAjay Kumar .commit = decon_commit, 68296976c3dSAjay Kumar .enable_vblank = decon_enable_vblank, 68396976c3dSAjay Kumar .disable_vblank = decon_disable_vblank, 68496976c3dSAjay Kumar .wait_for_vblank = decon_wait_for_vblank, 68596976c3dSAjay Kumar .win_commit = decon_win_commit, 68696976c3dSAjay Kumar .win_disable = decon_win_disable, 68796976c3dSAjay Kumar }; 68896976c3dSAjay Kumar 68996976c3dSAjay Kumar 69096976c3dSAjay Kumar static irqreturn_t decon_irq_handler(int irq, void *dev_id) 69196976c3dSAjay Kumar { 69296976c3dSAjay Kumar struct decon_context *ctx = (struct decon_context *)dev_id; 69396976c3dSAjay Kumar u32 val, clear_bit; 69496976c3dSAjay Kumar 69596976c3dSAjay Kumar val = readl(ctx->regs + VIDINTCON1); 69696976c3dSAjay Kumar 69796976c3dSAjay Kumar clear_bit = ctx->i80_if ? VIDINTCON1_INT_I80 : VIDINTCON1_INT_FRAME; 69896976c3dSAjay Kumar if (val & clear_bit) 69996976c3dSAjay Kumar writel(clear_bit, ctx->regs + VIDINTCON1); 70096976c3dSAjay Kumar 70196976c3dSAjay Kumar /* check the crtc is detached already from encoder */ 70296976c3dSAjay Kumar if (ctx->pipe < 0 || !ctx->drm_dev) 70396976c3dSAjay Kumar goto out; 70496976c3dSAjay Kumar 70596976c3dSAjay Kumar if (!ctx->i80_if) { 70696976c3dSAjay Kumar drm_handle_vblank(ctx->drm_dev, ctx->pipe); 70796976c3dSAjay Kumar exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe); 70896976c3dSAjay Kumar 70996976c3dSAjay Kumar /* set wait vsync event to zero and wake up queue. */ 71096976c3dSAjay Kumar if (atomic_read(&ctx->wait_vsync_event)) { 71196976c3dSAjay Kumar atomic_set(&ctx->wait_vsync_event, 0); 71296976c3dSAjay Kumar wake_up(&ctx->wait_vsync_queue); 71396976c3dSAjay Kumar } 71496976c3dSAjay Kumar } 71596976c3dSAjay Kumar out: 71696976c3dSAjay Kumar return IRQ_HANDLED; 71796976c3dSAjay Kumar } 71896976c3dSAjay Kumar 71996976c3dSAjay Kumar static int decon_bind(struct device *dev, struct device *master, void *data) 72096976c3dSAjay Kumar { 72196976c3dSAjay Kumar struct decon_context *ctx = dev_get_drvdata(dev); 72296976c3dSAjay Kumar struct drm_device *drm_dev = data; 7237ee14cdcSGustavo Padovan struct exynos_drm_plane *exynos_plane; 7247ee14cdcSGustavo Padovan enum drm_plane_type type; 7256e2a3b66SGustavo Padovan unsigned int zpos; 7266e2a3b66SGustavo Padovan int ret; 72796976c3dSAjay Kumar 72896976c3dSAjay Kumar ret = decon_ctx_initialize(ctx, drm_dev); 72996976c3dSAjay Kumar if (ret) { 73096976c3dSAjay Kumar DRM_ERROR("decon_ctx_initialize failed.\n"); 73196976c3dSAjay Kumar return ret; 73296976c3dSAjay Kumar } 73396976c3dSAjay Kumar 7347ee14cdcSGustavo Padovan for (zpos = 0; zpos < WINDOWS_NR; zpos++) { 7357ee14cdcSGustavo Padovan type = (zpos == ctx->default_win) ? DRM_PLANE_TYPE_PRIMARY : 7367ee14cdcSGustavo Padovan DRM_PLANE_TYPE_OVERLAY; 7377ee14cdcSGustavo Padovan ret = exynos_plane_init(drm_dev, &ctx->planes[zpos], 7386e2a3b66SGustavo Padovan 1 << ctx->pipe, type, zpos); 7397ee14cdcSGustavo Padovan if (ret) 7407ee14cdcSGustavo Padovan return ret; 7417ee14cdcSGustavo Padovan } 7427ee14cdcSGustavo Padovan 7437ee14cdcSGustavo Padovan exynos_plane = &ctx->planes[ctx->default_win]; 7447ee14cdcSGustavo Padovan ctx->crtc = exynos_drm_crtc_create(drm_dev, &exynos_plane->base, 7457ee14cdcSGustavo Padovan ctx->pipe, EXYNOS_DISPLAY_TYPE_LCD, 74696976c3dSAjay Kumar &decon_crtc_ops, ctx); 74796976c3dSAjay Kumar if (IS_ERR(ctx->crtc)) { 74896976c3dSAjay Kumar decon_ctx_remove(ctx); 74996976c3dSAjay Kumar return PTR_ERR(ctx->crtc); 75096976c3dSAjay Kumar } 75196976c3dSAjay Kumar 75296976c3dSAjay Kumar if (ctx->display) 75396976c3dSAjay Kumar exynos_drm_create_enc_conn(drm_dev, ctx->display); 75496976c3dSAjay Kumar 75596976c3dSAjay Kumar return 0; 75696976c3dSAjay Kumar 75796976c3dSAjay Kumar } 75896976c3dSAjay Kumar 75996976c3dSAjay Kumar static void decon_unbind(struct device *dev, struct device *master, 76096976c3dSAjay Kumar void *data) 76196976c3dSAjay Kumar { 76296976c3dSAjay Kumar struct decon_context *ctx = dev_get_drvdata(dev); 76396976c3dSAjay Kumar 7643cecda03SGustavo Padovan decon_disable(ctx->crtc); 76596976c3dSAjay Kumar 76696976c3dSAjay Kumar if (ctx->display) 76796976c3dSAjay Kumar exynos_dpi_remove(ctx->display); 76896976c3dSAjay Kumar 76996976c3dSAjay Kumar decon_ctx_remove(ctx); 77096976c3dSAjay Kumar } 77196976c3dSAjay Kumar 77296976c3dSAjay Kumar static const struct component_ops decon_component_ops = { 77396976c3dSAjay Kumar .bind = decon_bind, 77496976c3dSAjay Kumar .unbind = decon_unbind, 77596976c3dSAjay Kumar }; 77696976c3dSAjay Kumar 77796976c3dSAjay Kumar static int decon_probe(struct platform_device *pdev) 77896976c3dSAjay Kumar { 77996976c3dSAjay Kumar struct device *dev = &pdev->dev; 78096976c3dSAjay Kumar struct decon_context *ctx; 78196976c3dSAjay Kumar struct device_node *i80_if_timings; 78296976c3dSAjay Kumar struct resource *res; 78396976c3dSAjay Kumar int ret; 78496976c3dSAjay Kumar 78596976c3dSAjay Kumar if (!dev->of_node) 78696976c3dSAjay Kumar return -ENODEV; 78796976c3dSAjay Kumar 78896976c3dSAjay Kumar ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); 78996976c3dSAjay Kumar if (!ctx) 79096976c3dSAjay Kumar return -ENOMEM; 79196976c3dSAjay Kumar 79296976c3dSAjay Kumar ctx->dev = dev; 79396976c3dSAjay Kumar ctx->suspended = true; 79496976c3dSAjay Kumar 79596976c3dSAjay Kumar i80_if_timings = of_get_child_by_name(dev->of_node, "i80-if-timings"); 79696976c3dSAjay Kumar if (i80_if_timings) 79796976c3dSAjay Kumar ctx->i80_if = true; 79896976c3dSAjay Kumar of_node_put(i80_if_timings); 79996976c3dSAjay Kumar 80096976c3dSAjay Kumar ctx->regs = of_iomap(dev->of_node, 0); 801*86650408SAndrzej Hajda if (!ctx->regs) 802*86650408SAndrzej Hajda return -ENOMEM; 80396976c3dSAjay Kumar 80496976c3dSAjay Kumar ctx->pclk = devm_clk_get(dev, "pclk_decon0"); 80596976c3dSAjay Kumar if (IS_ERR(ctx->pclk)) { 80696976c3dSAjay Kumar dev_err(dev, "failed to get bus clock pclk\n"); 80796976c3dSAjay Kumar ret = PTR_ERR(ctx->pclk); 80896976c3dSAjay Kumar goto err_iounmap; 80996976c3dSAjay Kumar } 81096976c3dSAjay Kumar 81196976c3dSAjay Kumar ctx->aclk = devm_clk_get(dev, "aclk_decon0"); 81296976c3dSAjay Kumar if (IS_ERR(ctx->aclk)) { 81396976c3dSAjay Kumar dev_err(dev, "failed to get bus clock aclk\n"); 81496976c3dSAjay Kumar ret = PTR_ERR(ctx->aclk); 81596976c3dSAjay Kumar goto err_iounmap; 81696976c3dSAjay Kumar } 81796976c3dSAjay Kumar 81896976c3dSAjay Kumar ctx->eclk = devm_clk_get(dev, "decon0_eclk"); 81996976c3dSAjay Kumar if (IS_ERR(ctx->eclk)) { 82096976c3dSAjay Kumar dev_err(dev, "failed to get eclock\n"); 82196976c3dSAjay Kumar ret = PTR_ERR(ctx->eclk); 82296976c3dSAjay Kumar goto err_iounmap; 82396976c3dSAjay Kumar } 82496976c3dSAjay Kumar 82596976c3dSAjay Kumar ctx->vclk = devm_clk_get(dev, "decon0_vclk"); 82696976c3dSAjay Kumar if (IS_ERR(ctx->vclk)) { 82796976c3dSAjay Kumar dev_err(dev, "failed to get vclock\n"); 82896976c3dSAjay Kumar ret = PTR_ERR(ctx->vclk); 82996976c3dSAjay Kumar goto err_iounmap; 83096976c3dSAjay Kumar } 83196976c3dSAjay Kumar 83296976c3dSAjay Kumar res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, 83396976c3dSAjay Kumar ctx->i80_if ? "lcd_sys" : "vsync"); 83496976c3dSAjay Kumar if (!res) { 83596976c3dSAjay Kumar dev_err(dev, "irq request failed.\n"); 83696976c3dSAjay Kumar ret = -ENXIO; 83796976c3dSAjay Kumar goto err_iounmap; 83896976c3dSAjay Kumar } 83996976c3dSAjay Kumar 84096976c3dSAjay Kumar ret = devm_request_irq(dev, res->start, decon_irq_handler, 84196976c3dSAjay Kumar 0, "drm_decon", ctx); 84296976c3dSAjay Kumar if (ret) { 84396976c3dSAjay Kumar dev_err(dev, "irq request failed.\n"); 84496976c3dSAjay Kumar goto err_iounmap; 84596976c3dSAjay Kumar } 84696976c3dSAjay Kumar 84796976c3dSAjay Kumar init_waitqueue_head(&ctx->wait_vsync_queue); 84896976c3dSAjay Kumar atomic_set(&ctx->wait_vsync_event, 0); 84996976c3dSAjay Kumar 85096976c3dSAjay Kumar platform_set_drvdata(pdev, ctx); 85196976c3dSAjay Kumar 85296976c3dSAjay Kumar ctx->display = exynos_dpi_probe(dev); 85396976c3dSAjay Kumar if (IS_ERR(ctx->display)) { 85496976c3dSAjay Kumar ret = PTR_ERR(ctx->display); 85596976c3dSAjay Kumar goto err_iounmap; 85696976c3dSAjay Kumar } 85796976c3dSAjay Kumar 85896976c3dSAjay Kumar pm_runtime_enable(dev); 85996976c3dSAjay Kumar 86096976c3dSAjay Kumar ret = component_add(dev, &decon_component_ops); 86196976c3dSAjay Kumar if (ret) 86296976c3dSAjay Kumar goto err_disable_pm_runtime; 86396976c3dSAjay Kumar 86496976c3dSAjay Kumar return ret; 86596976c3dSAjay Kumar 86696976c3dSAjay Kumar err_disable_pm_runtime: 86796976c3dSAjay Kumar pm_runtime_disable(dev); 86896976c3dSAjay Kumar 86996976c3dSAjay Kumar err_iounmap: 87096976c3dSAjay Kumar iounmap(ctx->regs); 87196976c3dSAjay Kumar 87296976c3dSAjay Kumar return ret; 87396976c3dSAjay Kumar } 87496976c3dSAjay Kumar 87596976c3dSAjay Kumar static int decon_remove(struct platform_device *pdev) 87696976c3dSAjay Kumar { 87796976c3dSAjay Kumar struct decon_context *ctx = dev_get_drvdata(&pdev->dev); 87896976c3dSAjay Kumar 87996976c3dSAjay Kumar pm_runtime_disable(&pdev->dev); 88096976c3dSAjay Kumar 88196976c3dSAjay Kumar iounmap(ctx->regs); 88296976c3dSAjay Kumar 88396976c3dSAjay Kumar component_del(&pdev->dev, &decon_component_ops); 88496976c3dSAjay Kumar 88596976c3dSAjay Kumar return 0; 88696976c3dSAjay Kumar } 88796976c3dSAjay Kumar 88896976c3dSAjay Kumar struct platform_driver decon_driver = { 88996976c3dSAjay Kumar .probe = decon_probe, 89096976c3dSAjay Kumar .remove = decon_remove, 89196976c3dSAjay Kumar .driver = { 89296976c3dSAjay Kumar .name = "exynos-decon", 89396976c3dSAjay Kumar .of_match_table = decon_driver_dt_match, 89496976c3dSAjay Kumar }, 89596976c3dSAjay Kumar }; 896