11c248b7dSInki Dae /* exynos_drm_fimd.c 21c248b7dSInki Dae * 31c248b7dSInki Dae * Copyright (C) 2011 Samsung Electronics Co.Ltd 41c248b7dSInki Dae * Authors: 51c248b7dSInki Dae * Joonyoung Shim <jy0922.shim@samsung.com> 61c248b7dSInki Dae * Inki Dae <inki.dae@samsung.com> 71c248b7dSInki Dae * 81c248b7dSInki Dae * This program is free software; you can redistribute it and/or modify it 91c248b7dSInki Dae * under the terms of the GNU General Public License as published by the 101c248b7dSInki Dae * Free Software Foundation; either version 2 of the License, or (at your 111c248b7dSInki Dae * option) any later version. 121c248b7dSInki Dae * 131c248b7dSInki Dae */ 14760285e7SDavid Howells #include <drm/drmP.h> 151c248b7dSInki Dae 161c248b7dSInki Dae #include <linux/kernel.h> 171c248b7dSInki Dae #include <linux/platform_device.h> 181c248b7dSInki Dae #include <linux/clk.h> 193f1c781dSSachin Kamat #include <linux/of.h> 20d636ead8SJoonyoung Shim #include <linux/of_device.h> 21cb91f6a0SJoonyoung Shim #include <linux/pm_runtime.h> 22f37cd5e8SInki Dae #include <linux/component.h> 231c248b7dSInki Dae 247f4596f4SVikas Sajjan #include <video/of_display_timing.h> 25111e6055SAndrzej Hajda #include <video/of_videomode.h> 265a213a55SLeela Krishna Amudala #include <video/samsung_fimd.h> 271c248b7dSInki Dae #include <drm/exynos_drm.h> 281c248b7dSInki Dae 291c248b7dSInki Dae #include "exynos_drm_drv.h" 301c248b7dSInki Dae #include "exynos_drm_fbdev.h" 311c248b7dSInki Dae #include "exynos_drm_crtc.h" 32bcc5cd1cSInki Dae #include "exynos_drm_iommu.h" 331c248b7dSInki Dae 341c248b7dSInki Dae /* 35b8654b37SSachin Kamat * FIMD stands for Fully Interactive Mobile Display and 361c248b7dSInki Dae * as a display controller, it transfers contents drawn on memory 371c248b7dSInki Dae * to a LCD Panel through Display Interfaces such as RGB or 381c248b7dSInki Dae * CPU Interface. 391c248b7dSInki Dae */ 401c248b7dSInki Dae 41111e6055SAndrzej Hajda #define FIMD_DEFAULT_FRAMERATE 60 4266367461SRahul Sharma #define MIN_FB_WIDTH_FOR_16WORD_BURST 128 43111e6055SAndrzej Hajda 441c248b7dSInki Dae /* position control register for hardware window 0, 2 ~ 4.*/ 451c248b7dSInki Dae #define VIDOSD_A(win) (VIDOSD_BASE + 0x00 + (win) * 16) 461c248b7dSInki Dae #define VIDOSD_B(win) (VIDOSD_BASE + 0x04 + (win) * 16) 470f10cf14SLeela Krishna Amudala /* 480f10cf14SLeela Krishna Amudala * size control register for hardware windows 0 and alpha control register 490f10cf14SLeela Krishna Amudala * for hardware windows 1 ~ 4 500f10cf14SLeela Krishna Amudala */ 510f10cf14SLeela Krishna Amudala #define VIDOSD_C(win) (VIDOSD_BASE + 0x08 + (win) * 16) 520f10cf14SLeela Krishna Amudala /* size control register for hardware windows 1 ~ 2. */ 531c248b7dSInki Dae #define VIDOSD_D(win) (VIDOSD_BASE + 0x0C + (win) * 16) 541c248b7dSInki Dae 551c248b7dSInki Dae #define VIDWx_BUF_START(win, buf) (VIDW_BUF_START(buf) + (win) * 8) 561c248b7dSInki Dae #define VIDWx_BUF_END(win, buf) (VIDW_BUF_END(buf) + (win) * 8) 571c248b7dSInki Dae #define VIDWx_BUF_SIZE(win, buf) (VIDW_BUF_SIZE(buf) + (win) * 4) 581c248b7dSInki Dae 591c248b7dSInki Dae /* color key control register for hardware window 1 ~ 4. */ 600f10cf14SLeela Krishna Amudala #define WKEYCON0_BASE(x) ((WKEYCON0 + 0x140) + ((x - 1) * 8)) 611c248b7dSInki Dae /* color key value register for hardware window 1 ~ 4. */ 620f10cf14SLeela Krishna Amudala #define WKEYCON1_BASE(x) ((WKEYCON1 + 0x140) + ((x - 1) * 8)) 631c248b7dSInki Dae 641c248b7dSInki Dae /* FIMD has totally five hardware windows. */ 651c248b7dSInki Dae #define WINDOWS_NR 5 661c248b7dSInki Dae 67bb7704d6SSean Paul #define get_fimd_manager(mgr) platform_get_drvdata(to_platform_device(dev)) 681c248b7dSInki Dae 69e2e13389SLeela Krishna Amudala struct fimd_driver_data { 70e2e13389SLeela Krishna Amudala unsigned int timing_base; 71de7af100STomasz Figa 72de7af100STomasz Figa unsigned int has_shadowcon:1; 73411d9ed4STomasz Figa unsigned int has_clksel:1; 745cc4621aSInki Dae unsigned int has_limited_fmt:1; 75e2e13389SLeela Krishna Amudala }; 76e2e13389SLeela Krishna Amudala 77725ddeadSTomasz Figa static struct fimd_driver_data s3c64xx_fimd_driver_data = { 78725ddeadSTomasz Figa .timing_base = 0x0, 79725ddeadSTomasz Figa .has_clksel = 1, 805cc4621aSInki Dae .has_limited_fmt = 1, 81725ddeadSTomasz Figa }; 82725ddeadSTomasz Figa 836ecf18f9SSachin Kamat static struct fimd_driver_data exynos4_fimd_driver_data = { 84e2e13389SLeela Krishna Amudala .timing_base = 0x0, 85de7af100STomasz Figa .has_shadowcon = 1, 86e2e13389SLeela Krishna Amudala }; 87e2e13389SLeela Krishna Amudala 886ecf18f9SSachin Kamat static struct fimd_driver_data exynos5_fimd_driver_data = { 89e2e13389SLeela Krishna Amudala .timing_base = 0x20000, 90de7af100STomasz Figa .has_shadowcon = 1, 91e2e13389SLeela Krishna Amudala }; 92e2e13389SLeela Krishna Amudala 931c248b7dSInki Dae struct fimd_win_data { 941c248b7dSInki Dae unsigned int offset_x; 951c248b7dSInki Dae unsigned int offset_y; 9619c8b834SInki Dae unsigned int ovl_width; 9719c8b834SInki Dae unsigned int ovl_height; 9819c8b834SInki Dae unsigned int fb_width; 9919c8b834SInki Dae unsigned int fb_height; 1001c248b7dSInki Dae unsigned int bpp; 101a4f38a80SInki Dae unsigned int pixel_format; 1022c871127SInki Dae dma_addr_t dma_addr; 1031c248b7dSInki Dae unsigned int buf_offsize; 1041c248b7dSInki Dae unsigned int line_size; /* bytes */ 105ec05da95SInki Dae bool enabled; 106db7e55aeSPrathyush K bool resume; 1071c248b7dSInki Dae }; 1081c248b7dSInki Dae 1091c248b7dSInki Dae struct fimd_context { 110bb7704d6SSean Paul struct device *dev; 11140c8ab4bSSean Paul struct drm_device *drm_dev; 1121c248b7dSInki Dae struct clk *bus_clk; 1131c248b7dSInki Dae struct clk *lcd_clk; 1141c248b7dSInki Dae void __iomem *regs; 115a968e727SSean Paul struct drm_display_mode mode; 1161c248b7dSInki Dae struct fimd_win_data win_data[WINDOWS_NR]; 1171c248b7dSInki Dae unsigned int default_win; 1181c248b7dSInki Dae unsigned long irq_flags; 1191c248b7dSInki Dae u32 vidcon1; 120cb91f6a0SJoonyoung Shim bool suspended; 121080be03dSSean Paul int pipe; 12201ce113cSPrathyush K wait_queue_head_t wait_vsync_queue; 12301ce113cSPrathyush K atomic_t wait_vsync_event; 1241c248b7dSInki Dae 125562ad9f4SAndrzej Hajda struct exynos_drm_panel_info panel; 12618873465STomasz Figa struct fimd_driver_data *driver_data; 127000cc920SAndrzej Hajda struct exynos_drm_display *display; 1281c248b7dSInki Dae }; 1291c248b7dSInki Dae 130d636ead8SJoonyoung Shim static const struct of_device_id fimd_driver_dt_match[] = { 131725ddeadSTomasz Figa { .compatible = "samsung,s3c6400-fimd", 132725ddeadSTomasz Figa .data = &s3c64xx_fimd_driver_data }, 1335830daf8SVikas Sajjan { .compatible = "samsung,exynos4210-fimd", 134d636ead8SJoonyoung Shim .data = &exynos4_fimd_driver_data }, 1355830daf8SVikas Sajjan { .compatible = "samsung,exynos5250-fimd", 136d636ead8SJoonyoung Shim .data = &exynos5_fimd_driver_data }, 137d636ead8SJoonyoung Shim {}, 138d636ead8SJoonyoung Shim }; 139d636ead8SJoonyoung Shim 140e2e13389SLeela Krishna Amudala static inline struct fimd_driver_data *drm_fimd_get_driver_data( 141e2e13389SLeela Krishna Amudala struct platform_device *pdev) 142e2e13389SLeela Krishna Amudala { 143d636ead8SJoonyoung Shim const struct of_device_id *of_id = 144d636ead8SJoonyoung Shim of_match_device(fimd_driver_dt_match, &pdev->dev); 145d636ead8SJoonyoung Shim 146d636ead8SJoonyoung Shim return (struct fimd_driver_data *)of_id->data; 147e2e13389SLeela Krishna Amudala } 148e2e13389SLeela Krishna Amudala 149f13bdbd1SAkshu Agrawal static void fimd_wait_for_vblank(struct exynos_drm_manager *mgr) 150f13bdbd1SAkshu Agrawal { 151f13bdbd1SAkshu Agrawal struct fimd_context *ctx = mgr->ctx; 152f13bdbd1SAkshu Agrawal 153f13bdbd1SAkshu Agrawal if (ctx->suspended) 154f13bdbd1SAkshu Agrawal return; 155f13bdbd1SAkshu Agrawal 156f13bdbd1SAkshu Agrawal atomic_set(&ctx->wait_vsync_event, 1); 157f13bdbd1SAkshu Agrawal 158f13bdbd1SAkshu Agrawal /* 159f13bdbd1SAkshu Agrawal * wait for FIMD to signal VSYNC interrupt or return after 160f13bdbd1SAkshu Agrawal * timeout which is set to 50ms (refresh rate of 20). 161f13bdbd1SAkshu Agrawal */ 162f13bdbd1SAkshu Agrawal if (!wait_event_timeout(ctx->wait_vsync_queue, 163f13bdbd1SAkshu Agrawal !atomic_read(&ctx->wait_vsync_event), 164f13bdbd1SAkshu Agrawal HZ/20)) 165f13bdbd1SAkshu Agrawal DRM_DEBUG_KMS("vblank wait timed out.\n"); 166f13bdbd1SAkshu Agrawal } 167f13bdbd1SAkshu Agrawal 168f13bdbd1SAkshu Agrawal 169f13bdbd1SAkshu Agrawal static void fimd_clear_channel(struct exynos_drm_manager *mgr) 170f13bdbd1SAkshu Agrawal { 171f13bdbd1SAkshu Agrawal struct fimd_context *ctx = mgr->ctx; 172f13bdbd1SAkshu Agrawal int win, ch_enabled = 0; 173f13bdbd1SAkshu Agrawal 174f13bdbd1SAkshu Agrawal DRM_DEBUG_KMS("%s\n", __FILE__); 175f13bdbd1SAkshu Agrawal 176f13bdbd1SAkshu Agrawal /* Check if any channel is enabled. */ 177f13bdbd1SAkshu Agrawal for (win = 0; win < WINDOWS_NR; win++) { 178f13bdbd1SAkshu Agrawal u32 val = readl(ctx->regs + SHADOWCON); 179f13bdbd1SAkshu Agrawal if (val & SHADOWCON_CHx_ENABLE(win)) { 180f13bdbd1SAkshu Agrawal val &= ~SHADOWCON_CHx_ENABLE(win); 181f13bdbd1SAkshu Agrawal writel(val, ctx->regs + SHADOWCON); 182f13bdbd1SAkshu Agrawal ch_enabled = 1; 183f13bdbd1SAkshu Agrawal } 184f13bdbd1SAkshu Agrawal } 185f13bdbd1SAkshu Agrawal 186f13bdbd1SAkshu Agrawal /* Wait for vsync, as disable channel takes effect at next vsync */ 187f13bdbd1SAkshu Agrawal if (ch_enabled) 188f13bdbd1SAkshu Agrawal fimd_wait_for_vblank(mgr); 189f13bdbd1SAkshu Agrawal } 190f13bdbd1SAkshu Agrawal 191bb7704d6SSean Paul static int fimd_mgr_initialize(struct exynos_drm_manager *mgr, 192f37cd5e8SInki Dae struct drm_device *drm_dev) 19340c8ab4bSSean Paul { 194bb7704d6SSean Paul struct fimd_context *ctx = mgr->ctx; 195f37cd5e8SInki Dae struct exynos_drm_private *priv; 196f37cd5e8SInki Dae priv = drm_dev->dev_private; 19740c8ab4bSSean Paul 198f37cd5e8SInki Dae mgr->drm_dev = ctx->drm_dev = drm_dev; 199f37cd5e8SInki Dae mgr->pipe = ctx->pipe = priv->pipe++; 200080be03dSSean Paul 201080be03dSSean Paul /* 202080be03dSSean Paul * enable drm irq mode. 203080be03dSSean Paul * - with irq_enabled = true, we can use the vblank feature. 204080be03dSSean Paul * 205080be03dSSean Paul * P.S. note that we wouldn't use drm irq handler but 206080be03dSSean Paul * just specific driver own one instead because 207080be03dSSean Paul * drm framework supports only one irq handler. 208080be03dSSean Paul */ 209080be03dSSean Paul drm_dev->irq_enabled = true; 210080be03dSSean Paul 211080be03dSSean Paul /* 212080be03dSSean Paul * with vblank_disable_allowed = true, vblank interrupt will be disabled 213080be03dSSean Paul * by drm timer once a current process gives up ownership of 214080be03dSSean Paul * vblank event.(after drm_vblank_put function is called) 215080be03dSSean Paul */ 216080be03dSSean Paul drm_dev->vblank_disable_allowed = true; 217080be03dSSean Paul 218080be03dSSean Paul /* attach this sub driver to iommu mapping if supported. */ 219f13bdbd1SAkshu Agrawal if (is_drm_iommu_supported(ctx->drm_dev)) { 220f13bdbd1SAkshu Agrawal /* 221f13bdbd1SAkshu Agrawal * If any channel is already active, iommu will throw 222f13bdbd1SAkshu Agrawal * a PAGE FAULT when enabled. So clear any channel if enabled. 223f13bdbd1SAkshu Agrawal */ 224f13bdbd1SAkshu Agrawal fimd_clear_channel(mgr); 225080be03dSSean Paul drm_iommu_attach_device(ctx->drm_dev, ctx->dev); 226f13bdbd1SAkshu Agrawal } 22740c8ab4bSSean Paul 22840c8ab4bSSean Paul return 0; 22940c8ab4bSSean Paul } 23040c8ab4bSSean Paul 231080be03dSSean Paul static void fimd_mgr_remove(struct exynos_drm_manager *mgr) 232ec05da95SInki Dae { 233bb7704d6SSean Paul struct fimd_context *ctx = mgr->ctx; 234c32b06efSInki Dae 235080be03dSSean Paul /* detach this sub driver from iommu mapping if supported. */ 236080be03dSSean Paul if (is_drm_iommu_supported(ctx->drm_dev)) 237080be03dSSean Paul drm_iommu_detach_device(ctx->drm_dev, ctx->dev); 238ec05da95SInki Dae } 239ec05da95SInki Dae 240a968e727SSean Paul static u32 fimd_calc_clkdiv(struct fimd_context *ctx, 241a968e727SSean Paul const struct drm_display_mode *mode) 242a968e727SSean Paul { 243a968e727SSean Paul unsigned long ideal_clk = mode->htotal * mode->vtotal * mode->vrefresh; 244a968e727SSean Paul u32 clkdiv; 245a968e727SSean Paul 246a968e727SSean Paul /* Find the clock divider value that gets us closest to ideal_clk */ 247a968e727SSean Paul clkdiv = DIV_ROUND_UP(clk_get_rate(ctx->lcd_clk), ideal_clk); 248a968e727SSean Paul 249a968e727SSean Paul return (clkdiv < 0x100) ? clkdiv : 0xff; 250a968e727SSean Paul } 251a968e727SSean Paul 252a968e727SSean Paul static bool fimd_mode_fixup(struct exynos_drm_manager *mgr, 253a968e727SSean Paul const struct drm_display_mode *mode, 254a968e727SSean Paul struct drm_display_mode *adjusted_mode) 255a968e727SSean Paul { 256a968e727SSean Paul if (adjusted_mode->vrefresh == 0) 257a968e727SSean Paul adjusted_mode->vrefresh = FIMD_DEFAULT_FRAMERATE; 258a968e727SSean Paul 259a968e727SSean Paul return true; 260a968e727SSean Paul } 261a968e727SSean Paul 262a968e727SSean Paul static void fimd_mode_set(struct exynos_drm_manager *mgr, 263a968e727SSean Paul const struct drm_display_mode *in_mode) 264a968e727SSean Paul { 265a968e727SSean Paul struct fimd_context *ctx = mgr->ctx; 266a968e727SSean Paul 267a968e727SSean Paul drm_mode_copy(&ctx->mode, in_mode); 268a968e727SSean Paul } 269a968e727SSean Paul 270bb7704d6SSean Paul static void fimd_commit(struct exynos_drm_manager *mgr) 2711c248b7dSInki Dae { 272bb7704d6SSean Paul struct fimd_context *ctx = mgr->ctx; 273a968e727SSean Paul struct drm_display_mode *mode = &ctx->mode; 274e2e13389SLeela Krishna Amudala struct fimd_driver_data *driver_data; 2751417f109SSean Paul u32 val, clkdiv, vidcon1; 2768b4cad23SAndrzej Hajda int vsync_len, vbpd, vfpd, hsync_len, hbpd, hfpd; 2771c248b7dSInki Dae 27818873465STomasz Figa driver_data = ctx->driver_data; 279e30d4bcfSInki Dae if (ctx->suspended) 280e30d4bcfSInki Dae return; 281e30d4bcfSInki Dae 282a968e727SSean Paul /* nothing to do if we haven't set the mode yet */ 283a968e727SSean Paul if (mode->htotal == 0 || mode->vtotal == 0) 284a968e727SSean Paul return; 285a968e727SSean Paul 2861417f109SSean Paul /* setup polarity values */ 2871417f109SSean Paul vidcon1 = ctx->vidcon1; 2881417f109SSean Paul if (mode->flags & DRM_MODE_FLAG_NVSYNC) 2891417f109SSean Paul vidcon1 |= VIDCON1_INV_VSYNC; 2901417f109SSean Paul if (mode->flags & DRM_MODE_FLAG_NHSYNC) 2911417f109SSean Paul vidcon1 |= VIDCON1_INV_HSYNC; 2921417f109SSean Paul writel(vidcon1, ctx->regs + driver_data->timing_base + VIDCON1); 2931c248b7dSInki Dae 2941c248b7dSInki Dae /* setup vertical timing values. */ 295a968e727SSean Paul vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start; 2968b4cad23SAndrzej Hajda vbpd = mode->crtc_vtotal - mode->crtc_vsync_end; 2978b4cad23SAndrzej Hajda vfpd = mode->crtc_vsync_start - mode->crtc_vdisplay; 298a968e727SSean Paul 299a968e727SSean Paul val = VIDTCON0_VBPD(vbpd - 1) | 300a968e727SSean Paul VIDTCON0_VFPD(vfpd - 1) | 301a968e727SSean Paul VIDTCON0_VSPW(vsync_len - 1); 302e2e13389SLeela Krishna Amudala writel(val, ctx->regs + driver_data->timing_base + VIDTCON0); 3031c248b7dSInki Dae 3041c248b7dSInki Dae /* setup horizontal timing values. */ 305a968e727SSean Paul hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start; 3068b4cad23SAndrzej Hajda hbpd = mode->crtc_htotal - mode->crtc_hsync_end; 3078b4cad23SAndrzej Hajda hfpd = mode->crtc_hsync_start - mode->crtc_hdisplay; 308a968e727SSean Paul 309a968e727SSean Paul val = VIDTCON1_HBPD(hbpd - 1) | 310a968e727SSean Paul VIDTCON1_HFPD(hfpd - 1) | 311a968e727SSean Paul VIDTCON1_HSPW(hsync_len - 1); 312e2e13389SLeela Krishna Amudala writel(val, ctx->regs + driver_data->timing_base + VIDTCON1); 3131c248b7dSInki Dae 3141c248b7dSInki Dae /* setup horizontal and vertical display size. */ 315a968e727SSean Paul val = VIDTCON2_LINEVAL(mode->vdisplay - 1) | 316a968e727SSean Paul VIDTCON2_HOZVAL(mode->hdisplay - 1) | 317a968e727SSean Paul VIDTCON2_LINEVAL_E(mode->vdisplay - 1) | 318a968e727SSean Paul VIDTCON2_HOZVAL_E(mode->hdisplay - 1); 319e2e13389SLeela Krishna Amudala writel(val, ctx->regs + driver_data->timing_base + VIDTCON2); 3201c248b7dSInki Dae 3211c248b7dSInki Dae /* 3221c248b7dSInki Dae * fields of register with prefix '_F' would be updated 3231c248b7dSInki Dae * at vsync(same as dma start) 3241c248b7dSInki Dae */ 3251d531062SAndrzej Hajda val = VIDCON0_ENVID | VIDCON0_ENVID_F; 3261d531062SAndrzej Hajda 3271d531062SAndrzej Hajda if (ctx->driver_data->has_clksel) 3281d531062SAndrzej Hajda val |= VIDCON0_CLKSEL_LCD; 3291d531062SAndrzej Hajda 3301d531062SAndrzej Hajda clkdiv = fimd_calc_clkdiv(ctx, mode); 3311d531062SAndrzej Hajda if (clkdiv > 1) 3321d531062SAndrzej Hajda val |= VIDCON0_CLKVAL_F(clkdiv - 1) | VIDCON0_CLKDIR; 3331d531062SAndrzej Hajda 3341c248b7dSInki Dae writel(val, ctx->regs + VIDCON0); 3351c248b7dSInki Dae } 3361c248b7dSInki Dae 337bb7704d6SSean Paul static int fimd_enable_vblank(struct exynos_drm_manager *mgr) 3381c248b7dSInki Dae { 339bb7704d6SSean Paul struct fimd_context *ctx = mgr->ctx; 3401c248b7dSInki Dae u32 val; 3411c248b7dSInki Dae 342cb91f6a0SJoonyoung Shim if (ctx->suspended) 343cb91f6a0SJoonyoung Shim return -EPERM; 344cb91f6a0SJoonyoung Shim 3451c248b7dSInki Dae if (!test_and_set_bit(0, &ctx->irq_flags)) { 3461c248b7dSInki Dae val = readl(ctx->regs + VIDINTCON0); 3471c248b7dSInki Dae 3481c248b7dSInki Dae val |= VIDINTCON0_INT_ENABLE; 3491c248b7dSInki Dae val |= VIDINTCON0_INT_FRAME; 3501c248b7dSInki Dae 3511c248b7dSInki Dae val &= ~VIDINTCON0_FRAMESEL0_MASK; 3521c248b7dSInki Dae val |= VIDINTCON0_FRAMESEL0_VSYNC; 3531c248b7dSInki Dae val &= ~VIDINTCON0_FRAMESEL1_MASK; 3541c248b7dSInki Dae val |= VIDINTCON0_FRAMESEL1_NONE; 3551c248b7dSInki Dae 3561c248b7dSInki Dae writel(val, ctx->regs + VIDINTCON0); 3571c248b7dSInki Dae } 3581c248b7dSInki Dae 3591c248b7dSInki Dae return 0; 3601c248b7dSInki Dae } 3611c248b7dSInki Dae 362bb7704d6SSean Paul static void fimd_disable_vblank(struct exynos_drm_manager *mgr) 3631c248b7dSInki Dae { 364bb7704d6SSean Paul struct fimd_context *ctx = mgr->ctx; 3651c248b7dSInki Dae u32 val; 3661c248b7dSInki Dae 367cb91f6a0SJoonyoung Shim if (ctx->suspended) 368cb91f6a0SJoonyoung Shim return; 369cb91f6a0SJoonyoung Shim 3701c248b7dSInki Dae if (test_and_clear_bit(0, &ctx->irq_flags)) { 3711c248b7dSInki Dae val = readl(ctx->regs + VIDINTCON0); 3721c248b7dSInki Dae 3731c248b7dSInki Dae val &= ~VIDINTCON0_INT_FRAME; 3741c248b7dSInki Dae val &= ~VIDINTCON0_INT_ENABLE; 3751c248b7dSInki Dae 3761c248b7dSInki Dae writel(val, ctx->regs + VIDINTCON0); 3771c248b7dSInki Dae } 3781c248b7dSInki Dae } 3791c248b7dSInki Dae 380bb7704d6SSean Paul static void fimd_win_mode_set(struct exynos_drm_manager *mgr, 3811c248b7dSInki Dae struct exynos_drm_overlay *overlay) 3821c248b7dSInki Dae { 383bb7704d6SSean Paul struct fimd_context *ctx = mgr->ctx; 3841c248b7dSInki Dae struct fimd_win_data *win_data; 385864ee9e6SJoonyoung Shim int win; 38619c8b834SInki Dae unsigned long offset; 3871c248b7dSInki Dae 3881c248b7dSInki Dae if (!overlay) { 389bb7704d6SSean Paul DRM_ERROR("overlay is NULL\n"); 3901c248b7dSInki Dae return; 3911c248b7dSInki Dae } 3921c248b7dSInki Dae 393864ee9e6SJoonyoung Shim win = overlay->zpos; 394864ee9e6SJoonyoung Shim if (win == DEFAULT_ZPOS) 395864ee9e6SJoonyoung Shim win = ctx->default_win; 396864ee9e6SJoonyoung Shim 39737b006e8SKrzysztof Kozlowski if (win < 0 || win >= WINDOWS_NR) 398864ee9e6SJoonyoung Shim return; 399864ee9e6SJoonyoung Shim 40019c8b834SInki Dae offset = overlay->fb_x * (overlay->bpp >> 3); 40119c8b834SInki Dae offset += overlay->fb_y * overlay->pitch; 40219c8b834SInki Dae 40319c8b834SInki Dae DRM_DEBUG_KMS("offset = 0x%lx, pitch = %x\n", offset, overlay->pitch); 40419c8b834SInki Dae 405864ee9e6SJoonyoung Shim win_data = &ctx->win_data[win]; 4061c248b7dSInki Dae 40719c8b834SInki Dae win_data->offset_x = overlay->crtc_x; 40819c8b834SInki Dae win_data->offset_y = overlay->crtc_y; 40919c8b834SInki Dae win_data->ovl_width = overlay->crtc_width; 41019c8b834SInki Dae win_data->ovl_height = overlay->crtc_height; 41119c8b834SInki Dae win_data->fb_width = overlay->fb_width; 41219c8b834SInki Dae win_data->fb_height = overlay->fb_height; 413229d3534SSeung-Woo Kim win_data->dma_addr = overlay->dma_addr[0] + offset; 4141c248b7dSInki Dae win_data->bpp = overlay->bpp; 415a4f38a80SInki Dae win_data->pixel_format = overlay->pixel_format; 41619c8b834SInki Dae win_data->buf_offsize = (overlay->fb_width - overlay->crtc_width) * 41719c8b834SInki Dae (overlay->bpp >> 3); 41819c8b834SInki Dae win_data->line_size = overlay->crtc_width * (overlay->bpp >> 3); 41919c8b834SInki Dae 42019c8b834SInki Dae DRM_DEBUG_KMS("offset_x = %d, offset_y = %d\n", 42119c8b834SInki Dae win_data->offset_x, win_data->offset_y); 42219c8b834SInki Dae DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n", 42319c8b834SInki Dae win_data->ovl_width, win_data->ovl_height); 424ddd8e959SYoungJun Cho DRM_DEBUG_KMS("paddr = 0x%lx\n", (unsigned long)win_data->dma_addr); 42519c8b834SInki Dae DRM_DEBUG_KMS("fb_width = %d, crtc_width = %d\n", 42619c8b834SInki Dae overlay->fb_width, overlay->crtc_width); 4271c248b7dSInki Dae } 4281c248b7dSInki Dae 429bb7704d6SSean Paul static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win) 4301c248b7dSInki Dae { 4311c248b7dSInki Dae struct fimd_win_data *win_data = &ctx->win_data[win]; 4321c248b7dSInki Dae unsigned long val; 4331c248b7dSInki Dae 4341c248b7dSInki Dae val = WINCONx_ENWIN; 4351c248b7dSInki Dae 4365cc4621aSInki Dae /* 4375cc4621aSInki Dae * In case of s3c64xx, window 0 doesn't support alpha channel. 4385cc4621aSInki Dae * So the request format is ARGB8888 then change it to XRGB8888. 4395cc4621aSInki Dae */ 4405cc4621aSInki Dae if (ctx->driver_data->has_limited_fmt && !win) { 4415cc4621aSInki Dae if (win_data->pixel_format == DRM_FORMAT_ARGB8888) 4425cc4621aSInki Dae win_data->pixel_format = DRM_FORMAT_XRGB8888; 4435cc4621aSInki Dae } 4445cc4621aSInki Dae 445a4f38a80SInki Dae switch (win_data->pixel_format) { 446a4f38a80SInki Dae case DRM_FORMAT_C8: 4471c248b7dSInki Dae val |= WINCON0_BPPMODE_8BPP_PALETTE; 4481c248b7dSInki Dae val |= WINCONx_BURSTLEN_8WORD; 4491c248b7dSInki Dae val |= WINCONx_BYTSWP; 4501c248b7dSInki Dae break; 451a4f38a80SInki Dae case DRM_FORMAT_XRGB1555: 452a4f38a80SInki Dae val |= WINCON0_BPPMODE_16BPP_1555; 453a4f38a80SInki Dae val |= WINCONx_HAWSWP; 454a4f38a80SInki Dae val |= WINCONx_BURSTLEN_16WORD; 455a4f38a80SInki Dae break; 456a4f38a80SInki Dae case DRM_FORMAT_RGB565: 4571c248b7dSInki Dae val |= WINCON0_BPPMODE_16BPP_565; 4581c248b7dSInki Dae val |= WINCONx_HAWSWP; 4591c248b7dSInki Dae val |= WINCONx_BURSTLEN_16WORD; 4601c248b7dSInki Dae break; 461a4f38a80SInki Dae case DRM_FORMAT_XRGB8888: 4621c248b7dSInki Dae val |= WINCON0_BPPMODE_24BPP_888; 4631c248b7dSInki Dae val |= WINCONx_WSWP; 4641c248b7dSInki Dae val |= WINCONx_BURSTLEN_16WORD; 4651c248b7dSInki Dae break; 466a4f38a80SInki Dae case DRM_FORMAT_ARGB8888: 467a4f38a80SInki Dae val |= WINCON1_BPPMODE_25BPP_A1888 4681c248b7dSInki Dae | WINCON1_BLD_PIX | WINCON1_ALPHA_SEL; 4691c248b7dSInki Dae val |= WINCONx_WSWP; 4701c248b7dSInki Dae val |= WINCONx_BURSTLEN_16WORD; 4711c248b7dSInki Dae break; 4721c248b7dSInki Dae default: 4731c248b7dSInki Dae DRM_DEBUG_KMS("invalid pixel size so using unpacked 24bpp.\n"); 4741c248b7dSInki Dae 4751c248b7dSInki Dae val |= WINCON0_BPPMODE_24BPP_888; 4761c248b7dSInki Dae val |= WINCONx_WSWP; 4771c248b7dSInki Dae val |= WINCONx_BURSTLEN_16WORD; 4781c248b7dSInki Dae break; 4791c248b7dSInki Dae } 4801c248b7dSInki Dae 4811c248b7dSInki Dae DRM_DEBUG_KMS("bpp = %d\n", win_data->bpp); 4821c248b7dSInki Dae 48366367461SRahul Sharma /* 48466367461SRahul Sharma * In case of exynos, setting dma-burst to 16Word causes permanent 48566367461SRahul Sharma * tearing for very small buffers, e.g. cursor buffer. Burst Mode 48666367461SRahul Sharma * switching which is based on overlay size is not recommended as 48766367461SRahul Sharma * overlay size varies alot towards the end of the screen and rapid 48866367461SRahul Sharma * movement causes unstable DMA which results into iommu crash/tear. 48966367461SRahul Sharma */ 49066367461SRahul Sharma 49166367461SRahul Sharma if (win_data->fb_width < MIN_FB_WIDTH_FOR_16WORD_BURST) { 49266367461SRahul Sharma val &= ~WINCONx_BURSTLEN_MASK; 49366367461SRahul Sharma val |= WINCONx_BURSTLEN_4WORD; 49466367461SRahul Sharma } 49566367461SRahul Sharma 4961c248b7dSInki Dae writel(val, ctx->regs + WINCON(win)); 4971c248b7dSInki Dae } 4981c248b7dSInki Dae 499bb7704d6SSean Paul static void fimd_win_set_colkey(struct fimd_context *ctx, unsigned int win) 5001c248b7dSInki Dae { 5011c248b7dSInki Dae unsigned int keycon0 = 0, keycon1 = 0; 5021c248b7dSInki Dae 5031c248b7dSInki Dae keycon0 = ~(WxKEYCON0_KEYBL_EN | WxKEYCON0_KEYEN_F | 5041c248b7dSInki Dae WxKEYCON0_DIRCON) | WxKEYCON0_COMPKEY(0); 5051c248b7dSInki Dae 5061c248b7dSInki Dae keycon1 = WxKEYCON1_COLVAL(0xffffffff); 5071c248b7dSInki Dae 5081c248b7dSInki Dae writel(keycon0, ctx->regs + WKEYCON0_BASE(win)); 5091c248b7dSInki Dae writel(keycon1, ctx->regs + WKEYCON1_BASE(win)); 5101c248b7dSInki Dae } 5111c248b7dSInki Dae 512de7af100STomasz Figa /** 513de7af100STomasz Figa * shadow_protect_win() - disable updating values from shadow registers at vsync 514de7af100STomasz Figa * 515de7af100STomasz Figa * @win: window to protect registers for 516de7af100STomasz Figa * @protect: 1 to protect (disable updates) 517de7af100STomasz Figa */ 518de7af100STomasz Figa static void fimd_shadow_protect_win(struct fimd_context *ctx, 519de7af100STomasz Figa int win, bool protect) 520de7af100STomasz Figa { 521de7af100STomasz Figa u32 reg, bits, val; 522de7af100STomasz Figa 523de7af100STomasz Figa if (ctx->driver_data->has_shadowcon) { 524de7af100STomasz Figa reg = SHADOWCON; 525de7af100STomasz Figa bits = SHADOWCON_WINx_PROTECT(win); 526de7af100STomasz Figa } else { 527de7af100STomasz Figa reg = PRTCON; 528de7af100STomasz Figa bits = PRTCON_PROTECT; 529de7af100STomasz Figa } 530de7af100STomasz Figa 531de7af100STomasz Figa val = readl(ctx->regs + reg); 532de7af100STomasz Figa if (protect) 533de7af100STomasz Figa val |= bits; 534de7af100STomasz Figa else 535de7af100STomasz Figa val &= ~bits; 536de7af100STomasz Figa writel(val, ctx->regs + reg); 537de7af100STomasz Figa } 538de7af100STomasz Figa 539bb7704d6SSean Paul static void fimd_win_commit(struct exynos_drm_manager *mgr, int zpos) 5401c248b7dSInki Dae { 541bb7704d6SSean Paul struct fimd_context *ctx = mgr->ctx; 5421c248b7dSInki Dae struct fimd_win_data *win_data; 543864ee9e6SJoonyoung Shim int win = zpos; 5441c248b7dSInki Dae unsigned long val, alpha, size; 545f56aad3aSJoonyoung Shim unsigned int last_x; 546f56aad3aSJoonyoung Shim unsigned int last_y; 5471c248b7dSInki Dae 548e30d4bcfSInki Dae if (ctx->suspended) 549e30d4bcfSInki Dae return; 550e30d4bcfSInki Dae 551864ee9e6SJoonyoung Shim if (win == DEFAULT_ZPOS) 552864ee9e6SJoonyoung Shim win = ctx->default_win; 553864ee9e6SJoonyoung Shim 55437b006e8SKrzysztof Kozlowski if (win < 0 || win >= WINDOWS_NR) 5551c248b7dSInki Dae return; 5561c248b7dSInki Dae 5571c248b7dSInki Dae win_data = &ctx->win_data[win]; 5581c248b7dSInki Dae 559a43b933bSSean Paul /* If suspended, enable this on resume */ 560a43b933bSSean Paul if (ctx->suspended) { 561a43b933bSSean Paul win_data->resume = true; 562a43b933bSSean Paul return; 563a43b933bSSean Paul } 564a43b933bSSean Paul 5651c248b7dSInki Dae /* 566de7af100STomasz Figa * SHADOWCON/PRTCON register is used for enabling timing. 5671c248b7dSInki Dae * 5681c248b7dSInki Dae * for example, once only width value of a register is set, 5691c248b7dSInki Dae * if the dma is started then fimd hardware could malfunction so 5701c248b7dSInki Dae * with protect window setting, the register fields with prefix '_F' 5711c248b7dSInki Dae * wouldn't be updated at vsync also but updated once unprotect window 5721c248b7dSInki Dae * is set. 5731c248b7dSInki Dae */ 5741c248b7dSInki Dae 5751c248b7dSInki Dae /* protect windows */ 576de7af100STomasz Figa fimd_shadow_protect_win(ctx, win, true); 5771c248b7dSInki Dae 5781c248b7dSInki Dae /* buffer start address */ 5792c871127SInki Dae val = (unsigned long)win_data->dma_addr; 5801c248b7dSInki Dae writel(val, ctx->regs + VIDWx_BUF_START(win, 0)); 5811c248b7dSInki Dae 5821c248b7dSInki Dae /* buffer end address */ 58319c8b834SInki Dae size = win_data->fb_width * win_data->ovl_height * (win_data->bpp >> 3); 5842c871127SInki Dae val = (unsigned long)(win_data->dma_addr + size); 5851c248b7dSInki Dae writel(val, ctx->regs + VIDWx_BUF_END(win, 0)); 5861c248b7dSInki Dae 5871c248b7dSInki Dae DRM_DEBUG_KMS("start addr = 0x%lx, end addr = 0x%lx, size = 0x%lx\n", 5882c871127SInki Dae (unsigned long)win_data->dma_addr, val, size); 58919c8b834SInki Dae DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n", 59019c8b834SInki Dae win_data->ovl_width, win_data->ovl_height); 5911c248b7dSInki Dae 5921c248b7dSInki Dae /* buffer size */ 5931c248b7dSInki Dae val = VIDW_BUF_SIZE_OFFSET(win_data->buf_offsize) | 594ca555e5aSJoonyoung Shim VIDW_BUF_SIZE_PAGEWIDTH(win_data->line_size) | 595ca555e5aSJoonyoung Shim VIDW_BUF_SIZE_OFFSET_E(win_data->buf_offsize) | 596ca555e5aSJoonyoung Shim VIDW_BUF_SIZE_PAGEWIDTH_E(win_data->line_size); 5971c248b7dSInki Dae writel(val, ctx->regs + VIDWx_BUF_SIZE(win, 0)); 5981c248b7dSInki Dae 5991c248b7dSInki Dae /* OSD position */ 6001c248b7dSInki Dae val = VIDOSDxA_TOPLEFT_X(win_data->offset_x) | 601ca555e5aSJoonyoung Shim VIDOSDxA_TOPLEFT_Y(win_data->offset_y) | 602ca555e5aSJoonyoung Shim VIDOSDxA_TOPLEFT_X_E(win_data->offset_x) | 603ca555e5aSJoonyoung Shim VIDOSDxA_TOPLEFT_Y_E(win_data->offset_y); 6041c248b7dSInki Dae writel(val, ctx->regs + VIDOSD_A(win)); 6051c248b7dSInki Dae 606f56aad3aSJoonyoung Shim last_x = win_data->offset_x + win_data->ovl_width; 607f56aad3aSJoonyoung Shim if (last_x) 608f56aad3aSJoonyoung Shim last_x--; 609f56aad3aSJoonyoung Shim last_y = win_data->offset_y + win_data->ovl_height; 610f56aad3aSJoonyoung Shim if (last_y) 611f56aad3aSJoonyoung Shim last_y--; 612f56aad3aSJoonyoung Shim 613ca555e5aSJoonyoung Shim val = VIDOSDxB_BOTRIGHT_X(last_x) | VIDOSDxB_BOTRIGHT_Y(last_y) | 614ca555e5aSJoonyoung Shim VIDOSDxB_BOTRIGHT_X_E(last_x) | VIDOSDxB_BOTRIGHT_Y_E(last_y); 615ca555e5aSJoonyoung Shim 6161c248b7dSInki Dae writel(val, ctx->regs + VIDOSD_B(win)); 6171c248b7dSInki Dae 61819c8b834SInki Dae DRM_DEBUG_KMS("osd pos: tx = %d, ty = %d, bx = %d, by = %d\n", 619f56aad3aSJoonyoung Shim win_data->offset_x, win_data->offset_y, last_x, last_y); 6201c248b7dSInki Dae 6211c248b7dSInki Dae /* hardware window 0 doesn't support alpha channel. */ 6221c248b7dSInki Dae if (win != 0) { 6231c248b7dSInki Dae /* OSD alpha */ 6241c248b7dSInki Dae alpha = VIDISD14C_ALPHA1_R(0xf) | 6251c248b7dSInki Dae VIDISD14C_ALPHA1_G(0xf) | 6261c248b7dSInki Dae VIDISD14C_ALPHA1_B(0xf); 6271c248b7dSInki Dae 6281c248b7dSInki Dae writel(alpha, ctx->regs + VIDOSD_C(win)); 6291c248b7dSInki Dae } 6301c248b7dSInki Dae 6311c248b7dSInki Dae /* OSD size */ 6321c248b7dSInki Dae if (win != 3 && win != 4) { 6331c248b7dSInki Dae u32 offset = VIDOSD_D(win); 6341c248b7dSInki Dae if (win == 0) 6350f10cf14SLeela Krishna Amudala offset = VIDOSD_C(win); 63619c8b834SInki Dae val = win_data->ovl_width * win_data->ovl_height; 6371c248b7dSInki Dae writel(val, ctx->regs + offset); 6381c248b7dSInki Dae 6391c248b7dSInki Dae DRM_DEBUG_KMS("osd size = 0x%x\n", (unsigned int)val); 6401c248b7dSInki Dae } 6411c248b7dSInki Dae 642bb7704d6SSean Paul fimd_win_set_pixfmt(ctx, win); 6431c248b7dSInki Dae 6441c248b7dSInki Dae /* hardware window 0 doesn't support color key. */ 6451c248b7dSInki Dae if (win != 0) 646bb7704d6SSean Paul fimd_win_set_colkey(ctx, win); 6471c248b7dSInki Dae 648ec05da95SInki Dae /* wincon */ 649ec05da95SInki Dae val = readl(ctx->regs + WINCON(win)); 650ec05da95SInki Dae val |= WINCONx_ENWIN; 651ec05da95SInki Dae writel(val, ctx->regs + WINCON(win)); 652ec05da95SInki Dae 6531c248b7dSInki Dae /* Enable DMA channel and unprotect windows */ 654de7af100STomasz Figa fimd_shadow_protect_win(ctx, win, false); 655de7af100STomasz Figa 656de7af100STomasz Figa if (ctx->driver_data->has_shadowcon) { 6571c248b7dSInki Dae val = readl(ctx->regs + SHADOWCON); 6581c248b7dSInki Dae val |= SHADOWCON_CHx_ENABLE(win); 6591c248b7dSInki Dae writel(val, ctx->regs + SHADOWCON); 660de7af100STomasz Figa } 661ec05da95SInki Dae 662ec05da95SInki Dae win_data->enabled = true; 6631c248b7dSInki Dae } 6641c248b7dSInki Dae 665bb7704d6SSean Paul static void fimd_win_disable(struct exynos_drm_manager *mgr, int zpos) 6661c248b7dSInki Dae { 667bb7704d6SSean Paul struct fimd_context *ctx = mgr->ctx; 668ec05da95SInki Dae struct fimd_win_data *win_data; 669864ee9e6SJoonyoung Shim int win = zpos; 6701c248b7dSInki Dae u32 val; 6711c248b7dSInki Dae 672864ee9e6SJoonyoung Shim if (win == DEFAULT_ZPOS) 673864ee9e6SJoonyoung Shim win = ctx->default_win; 674864ee9e6SJoonyoung Shim 67537b006e8SKrzysztof Kozlowski if (win < 0 || win >= WINDOWS_NR) 6761c248b7dSInki Dae return; 6771c248b7dSInki Dae 678ec05da95SInki Dae win_data = &ctx->win_data[win]; 679ec05da95SInki Dae 680db7e55aeSPrathyush K if (ctx->suspended) { 681db7e55aeSPrathyush K /* do not resume this window*/ 682db7e55aeSPrathyush K win_data->resume = false; 683db7e55aeSPrathyush K return; 684db7e55aeSPrathyush K } 685db7e55aeSPrathyush K 6861c248b7dSInki Dae /* protect windows */ 687de7af100STomasz Figa fimd_shadow_protect_win(ctx, win, true); 6881c248b7dSInki Dae 6891c248b7dSInki Dae /* wincon */ 6901c248b7dSInki Dae val = readl(ctx->regs + WINCON(win)); 6911c248b7dSInki Dae val &= ~WINCONx_ENWIN; 6921c248b7dSInki Dae writel(val, ctx->regs + WINCON(win)); 6931c248b7dSInki Dae 6941c248b7dSInki Dae /* unprotect windows */ 695de7af100STomasz Figa if (ctx->driver_data->has_shadowcon) { 6961c248b7dSInki Dae val = readl(ctx->regs + SHADOWCON); 6971c248b7dSInki Dae val &= ~SHADOWCON_CHx_ENABLE(win); 6981c248b7dSInki Dae writel(val, ctx->regs + SHADOWCON); 699de7af100STomasz Figa } 700de7af100STomasz Figa 701de7af100STomasz Figa fimd_shadow_protect_win(ctx, win, false); 702ec05da95SInki Dae 703ec05da95SInki Dae win_data->enabled = false; 7041c248b7dSInki Dae } 7051c248b7dSInki Dae 706a43b933bSSean Paul static void fimd_window_suspend(struct exynos_drm_manager *mgr) 707a43b933bSSean Paul { 708a43b933bSSean Paul struct fimd_context *ctx = mgr->ctx; 709a43b933bSSean Paul struct fimd_win_data *win_data; 710a43b933bSSean Paul int i; 711a43b933bSSean Paul 712a43b933bSSean Paul for (i = 0; i < WINDOWS_NR; i++) { 713a43b933bSSean Paul win_data = &ctx->win_data[i]; 714a43b933bSSean Paul win_data->resume = win_data->enabled; 715a43b933bSSean Paul if (win_data->enabled) 716a43b933bSSean Paul fimd_win_disable(mgr, i); 717a43b933bSSean Paul } 718a43b933bSSean Paul fimd_wait_for_vblank(mgr); 719a43b933bSSean Paul } 720a43b933bSSean Paul 721a43b933bSSean Paul static void fimd_window_resume(struct exynos_drm_manager *mgr) 722a43b933bSSean Paul { 723a43b933bSSean Paul struct fimd_context *ctx = mgr->ctx; 724a43b933bSSean Paul struct fimd_win_data *win_data; 725a43b933bSSean Paul int i; 726a43b933bSSean Paul 727a43b933bSSean Paul for (i = 0; i < WINDOWS_NR; i++) { 728a43b933bSSean Paul win_data = &ctx->win_data[i]; 729a43b933bSSean Paul win_data->enabled = win_data->resume; 730a43b933bSSean Paul win_data->resume = false; 731a43b933bSSean Paul } 732a43b933bSSean Paul } 733a43b933bSSean Paul 734a43b933bSSean Paul static void fimd_apply(struct exynos_drm_manager *mgr) 735a43b933bSSean Paul { 736a43b933bSSean Paul struct fimd_context *ctx = mgr->ctx; 737a43b933bSSean Paul struct fimd_win_data *win_data; 738a43b933bSSean Paul int i; 739a43b933bSSean Paul 740a43b933bSSean Paul for (i = 0; i < WINDOWS_NR; i++) { 741a43b933bSSean Paul win_data = &ctx->win_data[i]; 742a43b933bSSean Paul if (win_data->enabled) 743a43b933bSSean Paul fimd_win_commit(mgr, i); 744d9b68d89SAndrzej Hajda else 745d9b68d89SAndrzej Hajda fimd_win_disable(mgr, i); 746a43b933bSSean Paul } 747a43b933bSSean Paul 748a43b933bSSean Paul fimd_commit(mgr); 749a43b933bSSean Paul } 750a43b933bSSean Paul 751a43b933bSSean Paul static int fimd_poweron(struct exynos_drm_manager *mgr) 752a43b933bSSean Paul { 753a43b933bSSean Paul struct fimd_context *ctx = mgr->ctx; 754a43b933bSSean Paul int ret; 755a43b933bSSean Paul 756a43b933bSSean Paul if (!ctx->suspended) 757a43b933bSSean Paul return 0; 758a43b933bSSean Paul 759a43b933bSSean Paul ctx->suspended = false; 760a43b933bSSean Paul 761af65c804SSean Paul pm_runtime_get_sync(ctx->dev); 762af65c804SSean Paul 763a43b933bSSean Paul ret = clk_prepare_enable(ctx->bus_clk); 764a43b933bSSean Paul if (ret < 0) { 765a43b933bSSean Paul DRM_ERROR("Failed to prepare_enable the bus clk [%d]\n", ret); 766a43b933bSSean Paul goto bus_clk_err; 767a43b933bSSean Paul } 768a43b933bSSean Paul 769a43b933bSSean Paul ret = clk_prepare_enable(ctx->lcd_clk); 770a43b933bSSean Paul if (ret < 0) { 771a43b933bSSean Paul DRM_ERROR("Failed to prepare_enable the lcd clk [%d]\n", ret); 772a43b933bSSean Paul goto lcd_clk_err; 773a43b933bSSean Paul } 774a43b933bSSean Paul 775a43b933bSSean Paul /* if vblank was enabled status, enable it again. */ 776a43b933bSSean Paul if (test_and_clear_bit(0, &ctx->irq_flags)) { 777a43b933bSSean Paul ret = fimd_enable_vblank(mgr); 778a43b933bSSean Paul if (ret) { 779a43b933bSSean Paul DRM_ERROR("Failed to re-enable vblank [%d]\n", ret); 780a43b933bSSean Paul goto enable_vblank_err; 781a43b933bSSean Paul } 782a43b933bSSean Paul } 783a43b933bSSean Paul 784a43b933bSSean Paul fimd_window_resume(mgr); 785a43b933bSSean Paul 786a43b933bSSean Paul fimd_apply(mgr); 787a43b933bSSean Paul 788a43b933bSSean Paul return 0; 789a43b933bSSean Paul 790a43b933bSSean Paul enable_vblank_err: 791a43b933bSSean Paul clk_disable_unprepare(ctx->lcd_clk); 792a43b933bSSean Paul lcd_clk_err: 793a43b933bSSean Paul clk_disable_unprepare(ctx->bus_clk); 794a43b933bSSean Paul bus_clk_err: 795a43b933bSSean Paul ctx->suspended = true; 796a43b933bSSean Paul return ret; 797a43b933bSSean Paul } 798a43b933bSSean Paul 799a43b933bSSean Paul static int fimd_poweroff(struct exynos_drm_manager *mgr) 800a43b933bSSean Paul { 801a43b933bSSean Paul struct fimd_context *ctx = mgr->ctx; 802a43b933bSSean Paul 803a43b933bSSean Paul if (ctx->suspended) 804a43b933bSSean Paul return 0; 805a43b933bSSean Paul 806a43b933bSSean Paul /* 807a43b933bSSean Paul * We need to make sure that all windows are disabled before we 808a43b933bSSean Paul * suspend that connector. Otherwise we might try to scan from 809a43b933bSSean Paul * a destroyed buffer later. 810a43b933bSSean Paul */ 811a43b933bSSean Paul fimd_window_suspend(mgr); 812a43b933bSSean Paul 813a43b933bSSean Paul clk_disable_unprepare(ctx->lcd_clk); 814a43b933bSSean Paul clk_disable_unprepare(ctx->bus_clk); 815a43b933bSSean Paul 816af65c804SSean Paul pm_runtime_put_sync(ctx->dev); 817af65c804SSean Paul 818a43b933bSSean Paul ctx->suspended = true; 819a43b933bSSean Paul return 0; 820a43b933bSSean Paul } 821a43b933bSSean Paul 822080be03dSSean Paul static void fimd_dpms(struct exynos_drm_manager *mgr, int mode) 823080be03dSSean Paul { 824af65c804SSean Paul DRM_DEBUG_KMS("%s, %d\n", __FILE__, mode); 825080be03dSSean Paul 826080be03dSSean Paul switch (mode) { 827080be03dSSean Paul case DRM_MODE_DPMS_ON: 828af65c804SSean Paul fimd_poweron(mgr); 829080be03dSSean Paul break; 830080be03dSSean Paul case DRM_MODE_DPMS_STANDBY: 831080be03dSSean Paul case DRM_MODE_DPMS_SUSPEND: 832080be03dSSean Paul case DRM_MODE_DPMS_OFF: 833af65c804SSean Paul fimd_poweroff(mgr); 834080be03dSSean Paul break; 835080be03dSSean Paul default: 836080be03dSSean Paul DRM_DEBUG_KMS("unspecified mode %d\n", mode); 837080be03dSSean Paul break; 838080be03dSSean Paul } 839080be03dSSean Paul } 840080be03dSSean Paul 8411c6244c3SSean Paul static struct exynos_drm_manager_ops fimd_manager_ops = { 8421c6244c3SSean Paul .dpms = fimd_dpms, 843a968e727SSean Paul .mode_fixup = fimd_mode_fixup, 844a968e727SSean Paul .mode_set = fimd_mode_set, 8451c6244c3SSean Paul .commit = fimd_commit, 8461c6244c3SSean Paul .enable_vblank = fimd_enable_vblank, 8471c6244c3SSean Paul .disable_vblank = fimd_disable_vblank, 8481c6244c3SSean Paul .wait_for_vblank = fimd_wait_for_vblank, 8491c6244c3SSean Paul .win_mode_set = fimd_win_mode_set, 8501c6244c3SSean Paul .win_commit = fimd_win_commit, 8511c6244c3SSean Paul .win_disable = fimd_win_disable, 8521c248b7dSInki Dae }; 8531c248b7dSInki Dae 854677e84c1SJoonyoung Shim static struct exynos_drm_manager fimd_manager = { 855080be03dSSean Paul .type = EXYNOS_DISPLAY_TYPE_LCD, 856677e84c1SJoonyoung Shim .ops = &fimd_manager_ops, 857677e84c1SJoonyoung Shim }; 858677e84c1SJoonyoung Shim 8591c248b7dSInki Dae static irqreturn_t fimd_irq_handler(int irq, void *dev_id) 8601c248b7dSInki Dae { 8611c248b7dSInki Dae struct fimd_context *ctx = (struct fimd_context *)dev_id; 8621c248b7dSInki Dae u32 val; 8631c248b7dSInki Dae 8641c248b7dSInki Dae val = readl(ctx->regs + VIDINTCON1); 8651c248b7dSInki Dae 8661c248b7dSInki Dae if (val & VIDINTCON1_INT_FRAME) 8671c248b7dSInki Dae /* VSYNC interrupt */ 8681c248b7dSInki Dae writel(VIDINTCON1_INT_FRAME, ctx->regs + VIDINTCON1); 8691c248b7dSInki Dae 870ec05da95SInki Dae /* check the crtc is detached already from encoder */ 871080be03dSSean Paul if (ctx->pipe < 0 || !ctx->drm_dev) 872ec05da95SInki Dae goto out; 873483b88f8SInki Dae 874080be03dSSean Paul drm_handle_vblank(ctx->drm_dev, ctx->pipe); 875080be03dSSean Paul exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe); 8761c248b7dSInki Dae 87701ce113cSPrathyush K /* set wait vsync event to zero and wake up queue. */ 87801ce113cSPrathyush K if (atomic_read(&ctx->wait_vsync_event)) { 87901ce113cSPrathyush K atomic_set(&ctx->wait_vsync_event, 0); 8808dd9ad5dSSeung-Woo Kim wake_up(&ctx->wait_vsync_queue); 88101ce113cSPrathyush K } 882ec05da95SInki Dae out: 8831c248b7dSInki Dae return IRQ_HANDLED; 8841c248b7dSInki Dae } 8851c248b7dSInki Dae 886f37cd5e8SInki Dae static int fimd_bind(struct device *dev, struct device *master, void *data) 887562ad9f4SAndrzej Hajda { 888000cc920SAndrzej Hajda struct fimd_context *ctx = fimd_manager.ctx; 889f37cd5e8SInki Dae struct drm_device *drm_dev = data; 890000cc920SAndrzej Hajda 891000cc920SAndrzej Hajda fimd_mgr_initialize(&fimd_manager, drm_dev); 892000cc920SAndrzej Hajda exynos_drm_crtc_create(&fimd_manager); 893000cc920SAndrzej Hajda if (ctx->display) 894000cc920SAndrzej Hajda exynos_drm_create_enc_conn(drm_dev, ctx->display); 895000cc920SAndrzej Hajda 896000cc920SAndrzej Hajda return 0; 897000cc920SAndrzej Hajda 898000cc920SAndrzej Hajda } 899000cc920SAndrzej Hajda 900000cc920SAndrzej Hajda static void fimd_unbind(struct device *dev, struct device *master, 901000cc920SAndrzej Hajda void *data) 902000cc920SAndrzej Hajda { 903000cc920SAndrzej Hajda struct exynos_drm_manager *mgr = dev_get_drvdata(dev); 904000cc920SAndrzej Hajda struct fimd_context *ctx = fimd_manager.ctx; 905000cc920SAndrzej Hajda struct drm_crtc *crtc = mgr->crtc; 906000cc920SAndrzej Hajda 907000cc920SAndrzej Hajda fimd_dpms(mgr, DRM_MODE_DPMS_OFF); 908000cc920SAndrzej Hajda 909000cc920SAndrzej Hajda if (ctx->display) 910000cc920SAndrzej Hajda exynos_dpi_remove(dev); 911000cc920SAndrzej Hajda 912000cc920SAndrzej Hajda fimd_mgr_remove(mgr); 913000cc920SAndrzej Hajda 914000cc920SAndrzej Hajda crtc->funcs->destroy(crtc); 915000cc920SAndrzej Hajda } 916000cc920SAndrzej Hajda 917000cc920SAndrzej Hajda static const struct component_ops fimd_component_ops = { 918000cc920SAndrzej Hajda .bind = fimd_bind, 919000cc920SAndrzej Hajda .unbind = fimd_unbind, 920000cc920SAndrzej Hajda }; 921000cc920SAndrzej Hajda 922000cc920SAndrzej Hajda static int fimd_probe(struct platform_device *pdev) 923000cc920SAndrzej Hajda { 924000cc920SAndrzej Hajda struct device *dev = &pdev->dev; 925000cc920SAndrzej Hajda struct fimd_context *ctx; 926000cc920SAndrzej Hajda struct resource *res; 927562ad9f4SAndrzej Hajda int ret = -EINVAL; 928562ad9f4SAndrzej Hajda 929df5225bcSInki Dae ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC, 930df5225bcSInki Dae fimd_manager.type); 931df5225bcSInki Dae if (ret) 932df5225bcSInki Dae return ret; 933df5225bcSInki Dae 934df5225bcSInki Dae if (!dev->of_node) { 935df5225bcSInki Dae ret = -ENODEV; 936df5225bcSInki Dae goto err_del_component; 937df5225bcSInki Dae } 9382d3f173cSSachin Kamat 939d873ab99SSeung-Woo Kim ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); 940df5225bcSInki Dae if (!ctx) { 941df5225bcSInki Dae ret = -ENOMEM; 942df5225bcSInki Dae goto err_del_component; 943df5225bcSInki Dae } 9441c248b7dSInki Dae 945bb7704d6SSean Paul ctx->dev = dev; 946a43b933bSSean Paul ctx->suspended = true; 947bb7704d6SSean Paul 9481417f109SSean Paul if (of_property_read_bool(dev->of_node, "samsung,invert-vden")) 9491417f109SSean Paul ctx->vidcon1 |= VIDCON1_INV_VDEN; 9501417f109SSean Paul if (of_property_read_bool(dev->of_node, "samsung,invert-vclk")) 9511417f109SSean Paul ctx->vidcon1 |= VIDCON1_INV_VCLK; 952562ad9f4SAndrzej Hajda 953a968e727SSean Paul ctx->bus_clk = devm_clk_get(dev, "fimd"); 954a968e727SSean Paul if (IS_ERR(ctx->bus_clk)) { 955a968e727SSean Paul dev_err(dev, "failed to get bus clock\n"); 956df5225bcSInki Dae ret = PTR_ERR(ctx->bus_clk); 957df5225bcSInki Dae goto err_del_component; 958a968e727SSean Paul } 959a968e727SSean Paul 960a968e727SSean Paul ctx->lcd_clk = devm_clk_get(dev, "sclk_fimd"); 961a968e727SSean Paul if (IS_ERR(ctx->lcd_clk)) { 962a968e727SSean Paul dev_err(dev, "failed to get lcd clock\n"); 963df5225bcSInki Dae ret = PTR_ERR(ctx->lcd_clk); 964df5225bcSInki Dae goto err_del_component; 965a968e727SSean Paul } 9661c248b7dSInki Dae 9671c248b7dSInki Dae res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 9681c248b7dSInki Dae 969d873ab99SSeung-Woo Kim ctx->regs = devm_ioremap_resource(dev, res); 970df5225bcSInki Dae if (IS_ERR(ctx->regs)) { 971df5225bcSInki Dae ret = PTR_ERR(ctx->regs); 972df5225bcSInki Dae goto err_del_component; 973df5225bcSInki Dae } 9741c248b7dSInki Dae 9751977e6d8SVikas Sajjan res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "vsync"); 9761c248b7dSInki Dae if (!res) { 9771c248b7dSInki Dae dev_err(dev, "irq request failed.\n"); 978df5225bcSInki Dae ret = -ENXIO; 979df5225bcSInki Dae goto err_del_component; 9801c248b7dSInki Dae } 9811c248b7dSInki Dae 982055e0c06SSean Paul ret = devm_request_irq(dev, res->start, fimd_irq_handler, 983edc57266SSachin Kamat 0, "drm_fimd", ctx); 984edc57266SSachin Kamat if (ret) { 9851c248b7dSInki Dae dev_err(dev, "irq request failed.\n"); 986df5225bcSInki Dae goto err_del_component; 9871c248b7dSInki Dae } 9881c248b7dSInki Dae 98918873465STomasz Figa ctx->driver_data = drm_fimd_get_driver_data(pdev); 99057ed0f7bSDaniel Vetter init_waitqueue_head(&ctx->wait_vsync_queue); 99101ce113cSPrathyush K atomic_set(&ctx->wait_vsync_event, 0); 9921c248b7dSInki Dae 993bb7704d6SSean Paul platform_set_drvdata(pdev, &fimd_manager); 994c32b06efSInki Dae 995080be03dSSean Paul fimd_manager.ctx = ctx; 996080be03dSSean Paul 997000cc920SAndrzej Hajda ctx->display = exynos_dpi_probe(dev); 998000cc920SAndrzej Hajda if (IS_ERR(ctx->display)) 999000cc920SAndrzej Hajda return PTR_ERR(ctx->display); 1000f37cd5e8SInki Dae 1001f37cd5e8SInki Dae pm_runtime_enable(&pdev->dev); 1002f37cd5e8SInki Dae 1003df5225bcSInki Dae ret = component_add(&pdev->dev, &fimd_component_ops); 1004df5225bcSInki Dae if (ret) 1005df5225bcSInki Dae goto err_disable_pm_runtime; 1006df5225bcSInki Dae 1007df5225bcSInki Dae return ret; 1008df5225bcSInki Dae 1009df5225bcSInki Dae err_disable_pm_runtime: 1010df5225bcSInki Dae pm_runtime_disable(&pdev->dev); 1011df5225bcSInki Dae 1012df5225bcSInki Dae err_del_component: 1013df5225bcSInki Dae exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC); 1014df5225bcSInki Dae return ret; 1015f37cd5e8SInki Dae } 1016f37cd5e8SInki Dae 1017f37cd5e8SInki Dae static int fimd_remove(struct platform_device *pdev) 1018f37cd5e8SInki Dae { 1019af65c804SSean Paul pm_runtime_disable(&pdev->dev); 1020cb91f6a0SJoonyoung Shim 1021df5225bcSInki Dae component_del(&pdev->dev, &fimd_component_ops); 1022df5225bcSInki Dae exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC); 1023df5225bcSInki Dae 10241c248b7dSInki Dae return 0; 10251c248b7dSInki Dae } 10261c248b7dSInki Dae 1027132a5b91SJoonyoung Shim struct platform_driver fimd_driver = { 10281c248b7dSInki Dae .probe = fimd_probe, 102956550d94SGreg Kroah-Hartman .remove = fimd_remove, 10301c248b7dSInki Dae .driver = { 10311c248b7dSInki Dae .name = "exynos4-fb", 10321c248b7dSInki Dae .owner = THIS_MODULE, 10332d3f173cSSachin Kamat .of_match_table = fimd_driver_dt_match, 10341c248b7dSInki Dae }, 10351c248b7dSInki Dae }; 1036