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> 233854fab2SYoungJun Cho #include <linux/mfd/syscon.h> 243854fab2SYoungJun Cho #include <linux/regmap.h> 251c248b7dSInki Dae 267f4596f4SVikas Sajjan #include <video/of_display_timing.h> 27111e6055SAndrzej Hajda #include <video/of_videomode.h> 285a213a55SLeela Krishna Amudala #include <video/samsung_fimd.h> 291c248b7dSInki Dae #include <drm/exynos_drm.h> 301c248b7dSInki Dae 311c248b7dSInki Dae #include "exynos_drm_drv.h" 321c248b7dSInki Dae #include "exynos_drm_fbdev.h" 331c248b7dSInki Dae #include "exynos_drm_crtc.h" 34bcc5cd1cSInki Dae #include "exynos_drm_iommu.h" 351c248b7dSInki Dae 361c248b7dSInki Dae /* 37b8654b37SSachin Kamat * FIMD stands for Fully Interactive Mobile Display and 381c248b7dSInki Dae * as a display controller, it transfers contents drawn on memory 391c248b7dSInki Dae * to a LCD Panel through Display Interfaces such as RGB or 401c248b7dSInki Dae * CPU Interface. 411c248b7dSInki Dae */ 421c248b7dSInki Dae 43111e6055SAndrzej Hajda #define FIMD_DEFAULT_FRAMERATE 60 4466367461SRahul Sharma #define MIN_FB_WIDTH_FOR_16WORD_BURST 128 45111e6055SAndrzej Hajda 461c248b7dSInki Dae /* position control register for hardware window 0, 2 ~ 4.*/ 471c248b7dSInki Dae #define VIDOSD_A(win) (VIDOSD_BASE + 0x00 + (win) * 16) 481c248b7dSInki Dae #define VIDOSD_B(win) (VIDOSD_BASE + 0x04 + (win) * 16) 490f10cf14SLeela Krishna Amudala /* 500f10cf14SLeela Krishna Amudala * size control register for hardware windows 0 and alpha control register 510f10cf14SLeela Krishna Amudala * for hardware windows 1 ~ 4 520f10cf14SLeela Krishna Amudala */ 530f10cf14SLeela Krishna Amudala #define VIDOSD_C(win) (VIDOSD_BASE + 0x08 + (win) * 16) 540f10cf14SLeela Krishna Amudala /* size control register for hardware windows 1 ~ 2. */ 551c248b7dSInki Dae #define VIDOSD_D(win) (VIDOSD_BASE + 0x0C + (win) * 16) 561c248b7dSInki Dae 571c248b7dSInki Dae #define VIDWx_BUF_START(win, buf) (VIDW_BUF_START(buf) + (win) * 8) 581c248b7dSInki Dae #define VIDWx_BUF_END(win, buf) (VIDW_BUF_END(buf) + (win) * 8) 591c248b7dSInki Dae #define VIDWx_BUF_SIZE(win, buf) (VIDW_BUF_SIZE(buf) + (win) * 4) 601c248b7dSInki Dae 611c248b7dSInki Dae /* color key control register for hardware window 1 ~ 4. */ 620f10cf14SLeela Krishna Amudala #define WKEYCON0_BASE(x) ((WKEYCON0 + 0x140) + ((x - 1) * 8)) 631c248b7dSInki Dae /* color key value register for hardware window 1 ~ 4. */ 640f10cf14SLeela Krishna Amudala #define WKEYCON1_BASE(x) ((WKEYCON1 + 0x140) + ((x - 1) * 8)) 651c248b7dSInki Dae 663854fab2SYoungJun Cho /* I80 / RGB trigger control register */ 673854fab2SYoungJun Cho #define TRIGCON 0x1A4 683854fab2SYoungJun Cho #define TRGMODE_I80_RGB_ENABLE_I80 (1 << 0) 693854fab2SYoungJun Cho #define SWTRGCMD_I80_RGB_ENABLE (1 << 1) 703854fab2SYoungJun Cho 713854fab2SYoungJun Cho /* display mode change control register except exynos4 */ 723854fab2SYoungJun Cho #define VIDOUT_CON 0x000 733854fab2SYoungJun Cho #define VIDOUT_CON_F_I80_LDI0 (0x2 << 8) 743854fab2SYoungJun Cho 753854fab2SYoungJun Cho /* I80 interface control for main LDI register */ 763854fab2SYoungJun Cho #define I80IFCONFAx(x) (0x1B0 + (x) * 4) 773854fab2SYoungJun Cho #define I80IFCONFBx(x) (0x1B8 + (x) * 4) 783854fab2SYoungJun Cho #define LCD_CS_SETUP(x) ((x) << 16) 793854fab2SYoungJun Cho #define LCD_WR_SETUP(x) ((x) << 12) 803854fab2SYoungJun Cho #define LCD_WR_ACTIVE(x) ((x) << 8) 813854fab2SYoungJun Cho #define LCD_WR_HOLD(x) ((x) << 4) 823854fab2SYoungJun Cho #define I80IFEN_ENABLE (1 << 0) 833854fab2SYoungJun Cho 841c248b7dSInki Dae /* FIMD has totally five hardware windows. */ 851c248b7dSInki Dae #define WINDOWS_NR 5 861c248b7dSInki Dae 87bb7704d6SSean Paul #define get_fimd_manager(mgr) platform_get_drvdata(to_platform_device(dev)) 881c248b7dSInki Dae 89e2e13389SLeela Krishna Amudala struct fimd_driver_data { 90e2e13389SLeela Krishna Amudala unsigned int timing_base; 913854fab2SYoungJun Cho unsigned int lcdblk_offset; 923854fab2SYoungJun Cho unsigned int lcdblk_vt_shift; 933854fab2SYoungJun Cho unsigned int lcdblk_bypass_shift; 94de7af100STomasz Figa 95de7af100STomasz Figa unsigned int has_shadowcon:1; 96411d9ed4STomasz Figa unsigned int has_clksel:1; 975cc4621aSInki Dae unsigned int has_limited_fmt:1; 983854fab2SYoungJun Cho unsigned int has_vidoutcon:1; 99e2e13389SLeela Krishna Amudala }; 100e2e13389SLeela Krishna Amudala 101725ddeadSTomasz Figa static struct fimd_driver_data s3c64xx_fimd_driver_data = { 102725ddeadSTomasz Figa .timing_base = 0x0, 103725ddeadSTomasz Figa .has_clksel = 1, 1045cc4621aSInki Dae .has_limited_fmt = 1, 105725ddeadSTomasz Figa }; 106725ddeadSTomasz Figa 1076ecf18f9SSachin Kamat static struct fimd_driver_data exynos4_fimd_driver_data = { 108e2e13389SLeela Krishna Amudala .timing_base = 0x0, 1093854fab2SYoungJun Cho .lcdblk_offset = 0x210, 1103854fab2SYoungJun Cho .lcdblk_vt_shift = 10, 1113854fab2SYoungJun Cho .lcdblk_bypass_shift = 1, 112de7af100STomasz Figa .has_shadowcon = 1, 113e2e13389SLeela Krishna Amudala }; 114e2e13389SLeela Krishna Amudala 1156ecf18f9SSachin Kamat static struct fimd_driver_data exynos5_fimd_driver_data = { 116e2e13389SLeela Krishna Amudala .timing_base = 0x20000, 1173854fab2SYoungJun Cho .lcdblk_offset = 0x214, 1183854fab2SYoungJun Cho .lcdblk_vt_shift = 24, 1193854fab2SYoungJun Cho .lcdblk_bypass_shift = 15, 120de7af100STomasz Figa .has_shadowcon = 1, 1213854fab2SYoungJun Cho .has_vidoutcon = 1, 122e2e13389SLeela Krishna Amudala }; 123e2e13389SLeela Krishna Amudala 1241c248b7dSInki Dae struct fimd_win_data { 1251c248b7dSInki Dae unsigned int offset_x; 1261c248b7dSInki Dae unsigned int offset_y; 12719c8b834SInki Dae unsigned int ovl_width; 12819c8b834SInki Dae unsigned int ovl_height; 12919c8b834SInki Dae unsigned int fb_width; 13019c8b834SInki Dae unsigned int fb_height; 1311c248b7dSInki Dae unsigned int bpp; 132a4f38a80SInki Dae unsigned int pixel_format; 1332c871127SInki Dae dma_addr_t dma_addr; 1341c248b7dSInki Dae unsigned int buf_offsize; 1351c248b7dSInki Dae unsigned int line_size; /* bytes */ 136ec05da95SInki Dae bool enabled; 137db7e55aeSPrathyush K bool resume; 1381c248b7dSInki Dae }; 1391c248b7dSInki Dae 1401c248b7dSInki Dae struct fimd_context { 141bb7704d6SSean Paul struct device *dev; 14240c8ab4bSSean Paul struct drm_device *drm_dev; 1431c248b7dSInki Dae struct clk *bus_clk; 1441c248b7dSInki Dae struct clk *lcd_clk; 1451c248b7dSInki Dae void __iomem *regs; 1463854fab2SYoungJun Cho struct regmap *sysreg; 147a968e727SSean Paul struct drm_display_mode mode; 1481c248b7dSInki Dae struct fimd_win_data win_data[WINDOWS_NR]; 1491c248b7dSInki Dae unsigned int default_win; 1501c248b7dSInki Dae unsigned long irq_flags; 1513854fab2SYoungJun Cho u32 vidcon0; 1521c248b7dSInki Dae u32 vidcon1; 1533854fab2SYoungJun Cho u32 vidout_con; 1543854fab2SYoungJun Cho u32 i80ifcon; 1553854fab2SYoungJun Cho bool i80_if; 156cb91f6a0SJoonyoung Shim bool suspended; 157080be03dSSean Paul int pipe; 15801ce113cSPrathyush K wait_queue_head_t wait_vsync_queue; 15901ce113cSPrathyush K atomic_t wait_vsync_event; 1603854fab2SYoungJun Cho atomic_t win_updated; 1613854fab2SYoungJun Cho atomic_t triggering; 1621c248b7dSInki Dae 163562ad9f4SAndrzej Hajda struct exynos_drm_panel_info panel; 16418873465STomasz Figa struct fimd_driver_data *driver_data; 165000cc920SAndrzej Hajda struct exynos_drm_display *display; 1661c248b7dSInki Dae }; 1671c248b7dSInki Dae 168d636ead8SJoonyoung Shim static const struct of_device_id fimd_driver_dt_match[] = { 169725ddeadSTomasz Figa { .compatible = "samsung,s3c6400-fimd", 170725ddeadSTomasz Figa .data = &s3c64xx_fimd_driver_data }, 1715830daf8SVikas Sajjan { .compatible = "samsung,exynos4210-fimd", 172d636ead8SJoonyoung Shim .data = &exynos4_fimd_driver_data }, 1735830daf8SVikas Sajjan { .compatible = "samsung,exynos5250-fimd", 174d636ead8SJoonyoung Shim .data = &exynos5_fimd_driver_data }, 175d636ead8SJoonyoung Shim {}, 176d636ead8SJoonyoung Shim }; 1770262ceebSSjoerd Simons MODULE_DEVICE_TABLE(of, fimd_driver_dt_match); 178d636ead8SJoonyoung Shim 179e2e13389SLeela Krishna Amudala static inline struct fimd_driver_data *drm_fimd_get_driver_data( 180e2e13389SLeela Krishna Amudala struct platform_device *pdev) 181e2e13389SLeela Krishna Amudala { 182d636ead8SJoonyoung Shim const struct of_device_id *of_id = 183d636ead8SJoonyoung Shim of_match_device(fimd_driver_dt_match, &pdev->dev); 184d636ead8SJoonyoung Shim 185d636ead8SJoonyoung Shim return (struct fimd_driver_data *)of_id->data; 186e2e13389SLeela Krishna Amudala } 187e2e13389SLeela Krishna Amudala 188f13bdbd1SAkshu Agrawal static void fimd_wait_for_vblank(struct exynos_drm_manager *mgr) 189f13bdbd1SAkshu Agrawal { 190f13bdbd1SAkshu Agrawal struct fimd_context *ctx = mgr->ctx; 191f13bdbd1SAkshu Agrawal 192f13bdbd1SAkshu Agrawal if (ctx->suspended) 193f13bdbd1SAkshu Agrawal return; 194f13bdbd1SAkshu Agrawal 195f13bdbd1SAkshu Agrawal atomic_set(&ctx->wait_vsync_event, 1); 196f13bdbd1SAkshu Agrawal 197f13bdbd1SAkshu Agrawal /* 198f13bdbd1SAkshu Agrawal * wait for FIMD to signal VSYNC interrupt or return after 199f13bdbd1SAkshu Agrawal * timeout which is set to 50ms (refresh rate of 20). 200f13bdbd1SAkshu Agrawal */ 201f13bdbd1SAkshu Agrawal if (!wait_event_timeout(ctx->wait_vsync_queue, 202f13bdbd1SAkshu Agrawal !atomic_read(&ctx->wait_vsync_event), 203f13bdbd1SAkshu Agrawal HZ/20)) 204f13bdbd1SAkshu Agrawal DRM_DEBUG_KMS("vblank wait timed out.\n"); 205f13bdbd1SAkshu Agrawal } 206f13bdbd1SAkshu Agrawal 207f13bdbd1SAkshu Agrawal 208f13bdbd1SAkshu Agrawal static void fimd_clear_channel(struct exynos_drm_manager *mgr) 209f13bdbd1SAkshu Agrawal { 210f13bdbd1SAkshu Agrawal struct fimd_context *ctx = mgr->ctx; 211f13bdbd1SAkshu Agrawal int win, ch_enabled = 0; 212f13bdbd1SAkshu Agrawal 213f13bdbd1SAkshu Agrawal DRM_DEBUG_KMS("%s\n", __FILE__); 214f13bdbd1SAkshu Agrawal 215f13bdbd1SAkshu Agrawal /* Check if any channel is enabled. */ 216f13bdbd1SAkshu Agrawal for (win = 0; win < WINDOWS_NR; win++) { 217f13bdbd1SAkshu Agrawal u32 val = readl(ctx->regs + SHADOWCON); 218f13bdbd1SAkshu Agrawal if (val & SHADOWCON_CHx_ENABLE(win)) { 219f13bdbd1SAkshu Agrawal val &= ~SHADOWCON_CHx_ENABLE(win); 220f13bdbd1SAkshu Agrawal writel(val, ctx->regs + SHADOWCON); 221f13bdbd1SAkshu Agrawal ch_enabled = 1; 222f13bdbd1SAkshu Agrawal } 223f13bdbd1SAkshu Agrawal } 224f13bdbd1SAkshu Agrawal 225f13bdbd1SAkshu Agrawal /* Wait for vsync, as disable channel takes effect at next vsync */ 226f13bdbd1SAkshu Agrawal if (ch_enabled) 227f13bdbd1SAkshu Agrawal fimd_wait_for_vblank(mgr); 228f13bdbd1SAkshu Agrawal } 229f13bdbd1SAkshu Agrawal 230bb7704d6SSean Paul static int fimd_mgr_initialize(struct exynos_drm_manager *mgr, 231f37cd5e8SInki Dae struct drm_device *drm_dev) 23240c8ab4bSSean Paul { 233bb7704d6SSean Paul struct fimd_context *ctx = mgr->ctx; 234f37cd5e8SInki Dae struct exynos_drm_private *priv; 235f37cd5e8SInki Dae priv = drm_dev->dev_private; 23640c8ab4bSSean Paul 237f37cd5e8SInki Dae mgr->drm_dev = ctx->drm_dev = drm_dev; 238f37cd5e8SInki Dae mgr->pipe = ctx->pipe = priv->pipe++; 239080be03dSSean Paul 240080be03dSSean Paul /* 241080be03dSSean Paul * enable drm irq mode. 242080be03dSSean Paul * - with irq_enabled = true, we can use the vblank feature. 243080be03dSSean Paul * 244080be03dSSean Paul * P.S. note that we wouldn't use drm irq handler but 245080be03dSSean Paul * just specific driver own one instead because 246080be03dSSean Paul * drm framework supports only one irq handler. 247080be03dSSean Paul */ 248080be03dSSean Paul drm_dev->irq_enabled = true; 249080be03dSSean Paul 250080be03dSSean Paul /* 251080be03dSSean Paul * with vblank_disable_allowed = true, vblank interrupt will be disabled 252080be03dSSean Paul * by drm timer once a current process gives up ownership of 253080be03dSSean Paul * vblank event.(after drm_vblank_put function is called) 254080be03dSSean Paul */ 255080be03dSSean Paul drm_dev->vblank_disable_allowed = true; 256080be03dSSean Paul 257080be03dSSean Paul /* attach this sub driver to iommu mapping if supported. */ 258f13bdbd1SAkshu Agrawal if (is_drm_iommu_supported(ctx->drm_dev)) { 259f13bdbd1SAkshu Agrawal /* 260f13bdbd1SAkshu Agrawal * If any channel is already active, iommu will throw 261f13bdbd1SAkshu Agrawal * a PAGE FAULT when enabled. So clear any channel if enabled. 262f13bdbd1SAkshu Agrawal */ 263f13bdbd1SAkshu Agrawal fimd_clear_channel(mgr); 264080be03dSSean Paul drm_iommu_attach_device(ctx->drm_dev, ctx->dev); 265f13bdbd1SAkshu Agrawal } 26640c8ab4bSSean Paul 26740c8ab4bSSean Paul return 0; 26840c8ab4bSSean Paul } 26940c8ab4bSSean Paul 270080be03dSSean Paul static void fimd_mgr_remove(struct exynos_drm_manager *mgr) 271ec05da95SInki Dae { 272bb7704d6SSean Paul struct fimd_context *ctx = mgr->ctx; 273c32b06efSInki Dae 274080be03dSSean Paul /* detach this sub driver from iommu mapping if supported. */ 275080be03dSSean Paul if (is_drm_iommu_supported(ctx->drm_dev)) 276080be03dSSean Paul drm_iommu_detach_device(ctx->drm_dev, ctx->dev); 277ec05da95SInki Dae } 278ec05da95SInki Dae 279a968e727SSean Paul static u32 fimd_calc_clkdiv(struct fimd_context *ctx, 280a968e727SSean Paul const struct drm_display_mode *mode) 281a968e727SSean Paul { 282a968e727SSean Paul unsigned long ideal_clk = mode->htotal * mode->vtotal * mode->vrefresh; 283a968e727SSean Paul u32 clkdiv; 284a968e727SSean Paul 2853854fab2SYoungJun Cho if (ctx->i80_if) { 2863854fab2SYoungJun Cho /* 2873854fab2SYoungJun Cho * The frame done interrupt should be occurred prior to the 2883854fab2SYoungJun Cho * next TE signal. 2893854fab2SYoungJun Cho */ 2903854fab2SYoungJun Cho ideal_clk *= 2; 2913854fab2SYoungJun Cho } 2923854fab2SYoungJun Cho 293a968e727SSean Paul /* Find the clock divider value that gets us closest to ideal_clk */ 294a968e727SSean Paul clkdiv = DIV_ROUND_UP(clk_get_rate(ctx->lcd_clk), ideal_clk); 295a968e727SSean Paul 296a968e727SSean Paul return (clkdiv < 0x100) ? clkdiv : 0xff; 297a968e727SSean Paul } 298a968e727SSean Paul 299a968e727SSean Paul static bool fimd_mode_fixup(struct exynos_drm_manager *mgr, 300a968e727SSean Paul const struct drm_display_mode *mode, 301a968e727SSean Paul struct drm_display_mode *adjusted_mode) 302a968e727SSean Paul { 303a968e727SSean Paul if (adjusted_mode->vrefresh == 0) 304a968e727SSean Paul adjusted_mode->vrefresh = FIMD_DEFAULT_FRAMERATE; 305a968e727SSean Paul 306a968e727SSean Paul return true; 307a968e727SSean Paul } 308a968e727SSean Paul 309a968e727SSean Paul static void fimd_mode_set(struct exynos_drm_manager *mgr, 310a968e727SSean Paul const struct drm_display_mode *in_mode) 311a968e727SSean Paul { 312a968e727SSean Paul struct fimd_context *ctx = mgr->ctx; 313a968e727SSean Paul 314a968e727SSean Paul drm_mode_copy(&ctx->mode, in_mode); 315a968e727SSean Paul } 316a968e727SSean Paul 317bb7704d6SSean Paul static void fimd_commit(struct exynos_drm_manager *mgr) 3181c248b7dSInki Dae { 319bb7704d6SSean Paul struct fimd_context *ctx = mgr->ctx; 320a968e727SSean Paul struct drm_display_mode *mode = &ctx->mode; 3213854fab2SYoungJun Cho struct fimd_driver_data *driver_data = ctx->driver_data; 3223854fab2SYoungJun Cho void *timing_base = ctx->regs + driver_data->timing_base; 3233854fab2SYoungJun Cho u32 val, clkdiv; 3241c248b7dSInki Dae 325e30d4bcfSInki Dae if (ctx->suspended) 326e30d4bcfSInki Dae return; 327e30d4bcfSInki Dae 328a968e727SSean Paul /* nothing to do if we haven't set the mode yet */ 329a968e727SSean Paul if (mode->htotal == 0 || mode->vtotal == 0) 330a968e727SSean Paul return; 331a968e727SSean Paul 3323854fab2SYoungJun Cho if (ctx->i80_if) { 3333854fab2SYoungJun Cho val = ctx->i80ifcon | I80IFEN_ENABLE; 3343854fab2SYoungJun Cho writel(val, timing_base + I80IFCONFAx(0)); 3353854fab2SYoungJun Cho 3363854fab2SYoungJun Cho /* disable auto frame rate */ 3373854fab2SYoungJun Cho writel(0, timing_base + I80IFCONFBx(0)); 3383854fab2SYoungJun Cho 3393854fab2SYoungJun Cho /* set video type selection to I80 interface */ 3403854fab2SYoungJun Cho if (ctx->sysreg && regmap_update_bits(ctx->sysreg, 3413854fab2SYoungJun Cho driver_data->lcdblk_offset, 3423854fab2SYoungJun Cho 0x3 << driver_data->lcdblk_vt_shift, 3433854fab2SYoungJun Cho 0x1 << driver_data->lcdblk_vt_shift)) { 3443854fab2SYoungJun Cho DRM_ERROR("Failed to update sysreg for I80 i/f.\n"); 3453854fab2SYoungJun Cho return; 3463854fab2SYoungJun Cho } 3473854fab2SYoungJun Cho } else { 3483854fab2SYoungJun Cho int vsync_len, vbpd, vfpd, hsync_len, hbpd, hfpd; 3493854fab2SYoungJun Cho u32 vidcon1; 3503854fab2SYoungJun Cho 3511417f109SSean Paul /* setup polarity values */ 3521417f109SSean Paul vidcon1 = ctx->vidcon1; 3531417f109SSean Paul if (mode->flags & DRM_MODE_FLAG_NVSYNC) 3541417f109SSean Paul vidcon1 |= VIDCON1_INV_VSYNC; 3551417f109SSean Paul if (mode->flags & DRM_MODE_FLAG_NHSYNC) 3561417f109SSean Paul vidcon1 |= VIDCON1_INV_HSYNC; 3571417f109SSean Paul writel(vidcon1, ctx->regs + driver_data->timing_base + VIDCON1); 3581c248b7dSInki Dae 3591c248b7dSInki Dae /* setup vertical timing values. */ 360a968e727SSean Paul vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start; 3618b4cad23SAndrzej Hajda vbpd = mode->crtc_vtotal - mode->crtc_vsync_end; 3628b4cad23SAndrzej Hajda vfpd = mode->crtc_vsync_start - mode->crtc_vdisplay; 363a968e727SSean Paul 364a968e727SSean Paul val = VIDTCON0_VBPD(vbpd - 1) | 365a968e727SSean Paul VIDTCON0_VFPD(vfpd - 1) | 366a968e727SSean Paul VIDTCON0_VSPW(vsync_len - 1); 367e2e13389SLeela Krishna Amudala writel(val, ctx->regs + driver_data->timing_base + VIDTCON0); 3681c248b7dSInki Dae 3691c248b7dSInki Dae /* setup horizontal timing values. */ 370a968e727SSean Paul hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start; 3718b4cad23SAndrzej Hajda hbpd = mode->crtc_htotal - mode->crtc_hsync_end; 3728b4cad23SAndrzej Hajda hfpd = mode->crtc_hsync_start - mode->crtc_hdisplay; 373a968e727SSean Paul 374a968e727SSean Paul val = VIDTCON1_HBPD(hbpd - 1) | 375a968e727SSean Paul VIDTCON1_HFPD(hfpd - 1) | 376a968e727SSean Paul VIDTCON1_HSPW(hsync_len - 1); 377e2e13389SLeela Krishna Amudala writel(val, ctx->regs + driver_data->timing_base + VIDTCON1); 3783854fab2SYoungJun Cho } 3793854fab2SYoungJun Cho 3803854fab2SYoungJun Cho if (driver_data->has_vidoutcon) 3813854fab2SYoungJun Cho writel(ctx->vidout_con, timing_base + VIDOUT_CON); 3823854fab2SYoungJun Cho 3833854fab2SYoungJun Cho /* set bypass selection */ 3843854fab2SYoungJun Cho if (ctx->sysreg && regmap_update_bits(ctx->sysreg, 3853854fab2SYoungJun Cho driver_data->lcdblk_offset, 3863854fab2SYoungJun Cho 0x1 << driver_data->lcdblk_bypass_shift, 3873854fab2SYoungJun Cho 0x1 << driver_data->lcdblk_bypass_shift)) { 3883854fab2SYoungJun Cho DRM_ERROR("Failed to update sysreg for bypass setting.\n"); 3893854fab2SYoungJun Cho return; 3903854fab2SYoungJun Cho } 3911c248b7dSInki Dae 3921c248b7dSInki Dae /* setup horizontal and vertical display size. */ 393a968e727SSean Paul val = VIDTCON2_LINEVAL(mode->vdisplay - 1) | 394a968e727SSean Paul VIDTCON2_HOZVAL(mode->hdisplay - 1) | 395a968e727SSean Paul VIDTCON2_LINEVAL_E(mode->vdisplay - 1) | 396a968e727SSean Paul VIDTCON2_HOZVAL_E(mode->hdisplay - 1); 397e2e13389SLeela Krishna Amudala writel(val, ctx->regs + driver_data->timing_base + VIDTCON2); 3981c248b7dSInki Dae 3991c248b7dSInki Dae /* 4001c248b7dSInki Dae * fields of register with prefix '_F' would be updated 4011c248b7dSInki Dae * at vsync(same as dma start) 4021c248b7dSInki Dae */ 4033854fab2SYoungJun Cho val = ctx->vidcon0; 4043854fab2SYoungJun Cho val |= VIDCON0_ENVID | VIDCON0_ENVID_F; 4051d531062SAndrzej Hajda 4061d531062SAndrzej Hajda if (ctx->driver_data->has_clksel) 4071d531062SAndrzej Hajda val |= VIDCON0_CLKSEL_LCD; 4081d531062SAndrzej Hajda 4091d531062SAndrzej Hajda clkdiv = fimd_calc_clkdiv(ctx, mode); 4101d531062SAndrzej Hajda if (clkdiv > 1) 4111d531062SAndrzej Hajda val |= VIDCON0_CLKVAL_F(clkdiv - 1) | VIDCON0_CLKDIR; 4121d531062SAndrzej Hajda 4131c248b7dSInki Dae writel(val, ctx->regs + VIDCON0); 4141c248b7dSInki Dae } 4151c248b7dSInki Dae 416bb7704d6SSean Paul static int fimd_enable_vblank(struct exynos_drm_manager *mgr) 4171c248b7dSInki Dae { 418bb7704d6SSean Paul struct fimd_context *ctx = mgr->ctx; 4191c248b7dSInki Dae u32 val; 4201c248b7dSInki Dae 421cb91f6a0SJoonyoung Shim if (ctx->suspended) 422cb91f6a0SJoonyoung Shim return -EPERM; 423cb91f6a0SJoonyoung Shim 4241c248b7dSInki Dae if (!test_and_set_bit(0, &ctx->irq_flags)) { 4251c248b7dSInki Dae val = readl(ctx->regs + VIDINTCON0); 4261c248b7dSInki Dae 4271c248b7dSInki Dae val |= VIDINTCON0_INT_ENABLE; 4281c248b7dSInki Dae val |= VIDINTCON0_INT_FRAME; 4291c248b7dSInki Dae 4301c248b7dSInki Dae val &= ~VIDINTCON0_FRAMESEL0_MASK; 4311c248b7dSInki Dae val |= VIDINTCON0_FRAMESEL0_VSYNC; 4321c248b7dSInki Dae val &= ~VIDINTCON0_FRAMESEL1_MASK; 4331c248b7dSInki Dae val |= VIDINTCON0_FRAMESEL1_NONE; 4341c248b7dSInki Dae 4351c248b7dSInki Dae writel(val, ctx->regs + VIDINTCON0); 4361c248b7dSInki Dae } 4371c248b7dSInki Dae 4381c248b7dSInki Dae return 0; 4391c248b7dSInki Dae } 4401c248b7dSInki Dae 441bb7704d6SSean Paul static void fimd_disable_vblank(struct exynos_drm_manager *mgr) 4421c248b7dSInki Dae { 443bb7704d6SSean Paul struct fimd_context *ctx = mgr->ctx; 4441c248b7dSInki Dae u32 val; 4451c248b7dSInki Dae 446cb91f6a0SJoonyoung Shim if (ctx->suspended) 447cb91f6a0SJoonyoung Shim return; 448cb91f6a0SJoonyoung Shim 4491c248b7dSInki Dae if (test_and_clear_bit(0, &ctx->irq_flags)) { 4501c248b7dSInki Dae val = readl(ctx->regs + VIDINTCON0); 4511c248b7dSInki Dae 4521c248b7dSInki Dae val &= ~VIDINTCON0_INT_FRAME; 4531c248b7dSInki Dae val &= ~VIDINTCON0_INT_ENABLE; 4541c248b7dSInki Dae 4551c248b7dSInki Dae writel(val, ctx->regs + VIDINTCON0); 4561c248b7dSInki Dae } 4571c248b7dSInki Dae } 4581c248b7dSInki Dae 459bb7704d6SSean Paul static void fimd_win_mode_set(struct exynos_drm_manager *mgr, 4601c248b7dSInki Dae struct exynos_drm_overlay *overlay) 4611c248b7dSInki Dae { 462bb7704d6SSean Paul struct fimd_context *ctx = mgr->ctx; 4631c248b7dSInki Dae struct fimd_win_data *win_data; 464864ee9e6SJoonyoung Shim int win; 46519c8b834SInki Dae unsigned long offset; 4661c248b7dSInki Dae 4671c248b7dSInki Dae if (!overlay) { 468bb7704d6SSean Paul DRM_ERROR("overlay is NULL\n"); 4691c248b7dSInki Dae return; 4701c248b7dSInki Dae } 4711c248b7dSInki Dae 472864ee9e6SJoonyoung Shim win = overlay->zpos; 473864ee9e6SJoonyoung Shim if (win == DEFAULT_ZPOS) 474864ee9e6SJoonyoung Shim win = ctx->default_win; 475864ee9e6SJoonyoung Shim 47637b006e8SKrzysztof Kozlowski if (win < 0 || win >= WINDOWS_NR) 477864ee9e6SJoonyoung Shim return; 478864ee9e6SJoonyoung Shim 47919c8b834SInki Dae offset = overlay->fb_x * (overlay->bpp >> 3); 48019c8b834SInki Dae offset += overlay->fb_y * overlay->pitch; 48119c8b834SInki Dae 48219c8b834SInki Dae DRM_DEBUG_KMS("offset = 0x%lx, pitch = %x\n", offset, overlay->pitch); 48319c8b834SInki Dae 484864ee9e6SJoonyoung Shim win_data = &ctx->win_data[win]; 4851c248b7dSInki Dae 48619c8b834SInki Dae win_data->offset_x = overlay->crtc_x; 48719c8b834SInki Dae win_data->offset_y = overlay->crtc_y; 48819c8b834SInki Dae win_data->ovl_width = overlay->crtc_width; 48919c8b834SInki Dae win_data->ovl_height = overlay->crtc_height; 49019c8b834SInki Dae win_data->fb_width = overlay->fb_width; 49119c8b834SInki Dae win_data->fb_height = overlay->fb_height; 492229d3534SSeung-Woo Kim win_data->dma_addr = overlay->dma_addr[0] + offset; 4931c248b7dSInki Dae win_data->bpp = overlay->bpp; 494a4f38a80SInki Dae win_data->pixel_format = overlay->pixel_format; 49519c8b834SInki Dae win_data->buf_offsize = (overlay->fb_width - overlay->crtc_width) * 49619c8b834SInki Dae (overlay->bpp >> 3); 49719c8b834SInki Dae win_data->line_size = overlay->crtc_width * (overlay->bpp >> 3); 49819c8b834SInki Dae 49919c8b834SInki Dae DRM_DEBUG_KMS("offset_x = %d, offset_y = %d\n", 50019c8b834SInki Dae win_data->offset_x, win_data->offset_y); 50119c8b834SInki Dae DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n", 50219c8b834SInki Dae win_data->ovl_width, win_data->ovl_height); 503ddd8e959SYoungJun Cho DRM_DEBUG_KMS("paddr = 0x%lx\n", (unsigned long)win_data->dma_addr); 50419c8b834SInki Dae DRM_DEBUG_KMS("fb_width = %d, crtc_width = %d\n", 50519c8b834SInki Dae overlay->fb_width, overlay->crtc_width); 5061c248b7dSInki Dae } 5071c248b7dSInki Dae 508bb7704d6SSean Paul static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win) 5091c248b7dSInki Dae { 5101c248b7dSInki Dae struct fimd_win_data *win_data = &ctx->win_data[win]; 5111c248b7dSInki Dae unsigned long val; 5121c248b7dSInki Dae 5131c248b7dSInki Dae val = WINCONx_ENWIN; 5141c248b7dSInki Dae 5155cc4621aSInki Dae /* 5165cc4621aSInki Dae * In case of s3c64xx, window 0 doesn't support alpha channel. 5175cc4621aSInki Dae * So the request format is ARGB8888 then change it to XRGB8888. 5185cc4621aSInki Dae */ 5195cc4621aSInki Dae if (ctx->driver_data->has_limited_fmt && !win) { 5205cc4621aSInki Dae if (win_data->pixel_format == DRM_FORMAT_ARGB8888) 5215cc4621aSInki Dae win_data->pixel_format = DRM_FORMAT_XRGB8888; 5225cc4621aSInki Dae } 5235cc4621aSInki Dae 524a4f38a80SInki Dae switch (win_data->pixel_format) { 525a4f38a80SInki Dae case DRM_FORMAT_C8: 5261c248b7dSInki Dae val |= WINCON0_BPPMODE_8BPP_PALETTE; 5271c248b7dSInki Dae val |= WINCONx_BURSTLEN_8WORD; 5281c248b7dSInki Dae val |= WINCONx_BYTSWP; 5291c248b7dSInki Dae break; 530a4f38a80SInki Dae case DRM_FORMAT_XRGB1555: 531a4f38a80SInki Dae val |= WINCON0_BPPMODE_16BPP_1555; 532a4f38a80SInki Dae val |= WINCONx_HAWSWP; 533a4f38a80SInki Dae val |= WINCONx_BURSTLEN_16WORD; 534a4f38a80SInki Dae break; 535a4f38a80SInki Dae case DRM_FORMAT_RGB565: 5361c248b7dSInki Dae val |= WINCON0_BPPMODE_16BPP_565; 5371c248b7dSInki Dae val |= WINCONx_HAWSWP; 5381c248b7dSInki Dae val |= WINCONx_BURSTLEN_16WORD; 5391c248b7dSInki Dae break; 540a4f38a80SInki Dae case DRM_FORMAT_XRGB8888: 5411c248b7dSInki Dae val |= WINCON0_BPPMODE_24BPP_888; 5421c248b7dSInki Dae val |= WINCONx_WSWP; 5431c248b7dSInki Dae val |= WINCONx_BURSTLEN_16WORD; 5441c248b7dSInki Dae break; 545a4f38a80SInki Dae case DRM_FORMAT_ARGB8888: 546a4f38a80SInki Dae val |= WINCON1_BPPMODE_25BPP_A1888 5471c248b7dSInki Dae | WINCON1_BLD_PIX | WINCON1_ALPHA_SEL; 5481c248b7dSInki Dae val |= WINCONx_WSWP; 5491c248b7dSInki Dae val |= WINCONx_BURSTLEN_16WORD; 5501c248b7dSInki Dae break; 5511c248b7dSInki Dae default: 5521c248b7dSInki Dae DRM_DEBUG_KMS("invalid pixel size so using unpacked 24bpp.\n"); 5531c248b7dSInki Dae 5541c248b7dSInki Dae val |= WINCON0_BPPMODE_24BPP_888; 5551c248b7dSInki Dae val |= WINCONx_WSWP; 5561c248b7dSInki Dae val |= WINCONx_BURSTLEN_16WORD; 5571c248b7dSInki Dae break; 5581c248b7dSInki Dae } 5591c248b7dSInki Dae 5601c248b7dSInki Dae DRM_DEBUG_KMS("bpp = %d\n", win_data->bpp); 5611c248b7dSInki Dae 56266367461SRahul Sharma /* 56366367461SRahul Sharma * In case of exynos, setting dma-burst to 16Word causes permanent 56466367461SRahul Sharma * tearing for very small buffers, e.g. cursor buffer. Burst Mode 56566367461SRahul Sharma * switching which is based on overlay size is not recommended as 56666367461SRahul Sharma * overlay size varies alot towards the end of the screen and rapid 56766367461SRahul Sharma * movement causes unstable DMA which results into iommu crash/tear. 56866367461SRahul Sharma */ 56966367461SRahul Sharma 57066367461SRahul Sharma if (win_data->fb_width < MIN_FB_WIDTH_FOR_16WORD_BURST) { 57166367461SRahul Sharma val &= ~WINCONx_BURSTLEN_MASK; 57266367461SRahul Sharma val |= WINCONx_BURSTLEN_4WORD; 57366367461SRahul Sharma } 57466367461SRahul Sharma 5751c248b7dSInki Dae writel(val, ctx->regs + WINCON(win)); 5761c248b7dSInki Dae } 5771c248b7dSInki Dae 578bb7704d6SSean Paul static void fimd_win_set_colkey(struct fimd_context *ctx, unsigned int win) 5791c248b7dSInki Dae { 5801c248b7dSInki Dae unsigned int keycon0 = 0, keycon1 = 0; 5811c248b7dSInki Dae 5821c248b7dSInki Dae keycon0 = ~(WxKEYCON0_KEYBL_EN | WxKEYCON0_KEYEN_F | 5831c248b7dSInki Dae WxKEYCON0_DIRCON) | WxKEYCON0_COMPKEY(0); 5841c248b7dSInki Dae 5851c248b7dSInki Dae keycon1 = WxKEYCON1_COLVAL(0xffffffff); 5861c248b7dSInki Dae 5871c248b7dSInki Dae writel(keycon0, ctx->regs + WKEYCON0_BASE(win)); 5881c248b7dSInki Dae writel(keycon1, ctx->regs + WKEYCON1_BASE(win)); 5891c248b7dSInki Dae } 5901c248b7dSInki Dae 591de7af100STomasz Figa /** 592de7af100STomasz Figa * shadow_protect_win() - disable updating values from shadow registers at vsync 593de7af100STomasz Figa * 594de7af100STomasz Figa * @win: window to protect registers for 595de7af100STomasz Figa * @protect: 1 to protect (disable updates) 596de7af100STomasz Figa */ 597de7af100STomasz Figa static void fimd_shadow_protect_win(struct fimd_context *ctx, 598de7af100STomasz Figa int win, bool protect) 599de7af100STomasz Figa { 600de7af100STomasz Figa u32 reg, bits, val; 601de7af100STomasz Figa 602de7af100STomasz Figa if (ctx->driver_data->has_shadowcon) { 603de7af100STomasz Figa reg = SHADOWCON; 604de7af100STomasz Figa bits = SHADOWCON_WINx_PROTECT(win); 605de7af100STomasz Figa } else { 606de7af100STomasz Figa reg = PRTCON; 607de7af100STomasz Figa bits = PRTCON_PROTECT; 608de7af100STomasz Figa } 609de7af100STomasz Figa 610de7af100STomasz Figa val = readl(ctx->regs + reg); 611de7af100STomasz Figa if (protect) 612de7af100STomasz Figa val |= bits; 613de7af100STomasz Figa else 614de7af100STomasz Figa val &= ~bits; 615de7af100STomasz Figa writel(val, ctx->regs + reg); 616de7af100STomasz Figa } 617de7af100STomasz Figa 618bb7704d6SSean Paul static void fimd_win_commit(struct exynos_drm_manager *mgr, int zpos) 6191c248b7dSInki Dae { 620bb7704d6SSean Paul struct fimd_context *ctx = mgr->ctx; 6211c248b7dSInki Dae struct fimd_win_data *win_data; 622864ee9e6SJoonyoung Shim int win = zpos; 6231c248b7dSInki Dae unsigned long val, alpha, size; 624f56aad3aSJoonyoung Shim unsigned int last_x; 625f56aad3aSJoonyoung Shim unsigned int last_y; 6261c248b7dSInki Dae 627e30d4bcfSInki Dae if (ctx->suspended) 628e30d4bcfSInki Dae return; 629e30d4bcfSInki Dae 630864ee9e6SJoonyoung Shim if (win == DEFAULT_ZPOS) 631864ee9e6SJoonyoung Shim win = ctx->default_win; 632864ee9e6SJoonyoung Shim 63337b006e8SKrzysztof Kozlowski if (win < 0 || win >= WINDOWS_NR) 6341c248b7dSInki Dae return; 6351c248b7dSInki Dae 6361c248b7dSInki Dae win_data = &ctx->win_data[win]; 6371c248b7dSInki Dae 638a43b933bSSean Paul /* If suspended, enable this on resume */ 639a43b933bSSean Paul if (ctx->suspended) { 640a43b933bSSean Paul win_data->resume = true; 641a43b933bSSean Paul return; 642a43b933bSSean Paul } 643a43b933bSSean Paul 6441c248b7dSInki Dae /* 645de7af100STomasz Figa * SHADOWCON/PRTCON register is used for enabling timing. 6461c248b7dSInki Dae * 6471c248b7dSInki Dae * for example, once only width value of a register is set, 6481c248b7dSInki Dae * if the dma is started then fimd hardware could malfunction so 6491c248b7dSInki Dae * with protect window setting, the register fields with prefix '_F' 6501c248b7dSInki Dae * wouldn't be updated at vsync also but updated once unprotect window 6511c248b7dSInki Dae * is set. 6521c248b7dSInki Dae */ 6531c248b7dSInki Dae 6541c248b7dSInki Dae /* protect windows */ 655de7af100STomasz Figa fimd_shadow_protect_win(ctx, win, true); 6561c248b7dSInki Dae 6571c248b7dSInki Dae /* buffer start address */ 6582c871127SInki Dae val = (unsigned long)win_data->dma_addr; 6591c248b7dSInki Dae writel(val, ctx->regs + VIDWx_BUF_START(win, 0)); 6601c248b7dSInki Dae 6611c248b7dSInki Dae /* buffer end address */ 66219c8b834SInki Dae size = win_data->fb_width * win_data->ovl_height * (win_data->bpp >> 3); 6632c871127SInki Dae val = (unsigned long)(win_data->dma_addr + size); 6641c248b7dSInki Dae writel(val, ctx->regs + VIDWx_BUF_END(win, 0)); 6651c248b7dSInki Dae 6661c248b7dSInki Dae DRM_DEBUG_KMS("start addr = 0x%lx, end addr = 0x%lx, size = 0x%lx\n", 6672c871127SInki Dae (unsigned long)win_data->dma_addr, val, size); 66819c8b834SInki Dae DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n", 66919c8b834SInki Dae win_data->ovl_width, win_data->ovl_height); 6701c248b7dSInki Dae 6711c248b7dSInki Dae /* buffer size */ 6721c248b7dSInki Dae val = VIDW_BUF_SIZE_OFFSET(win_data->buf_offsize) | 673ca555e5aSJoonyoung Shim VIDW_BUF_SIZE_PAGEWIDTH(win_data->line_size) | 674ca555e5aSJoonyoung Shim VIDW_BUF_SIZE_OFFSET_E(win_data->buf_offsize) | 675ca555e5aSJoonyoung Shim VIDW_BUF_SIZE_PAGEWIDTH_E(win_data->line_size); 6761c248b7dSInki Dae writel(val, ctx->regs + VIDWx_BUF_SIZE(win, 0)); 6771c248b7dSInki Dae 6781c248b7dSInki Dae /* OSD position */ 6791c248b7dSInki Dae val = VIDOSDxA_TOPLEFT_X(win_data->offset_x) | 680ca555e5aSJoonyoung Shim VIDOSDxA_TOPLEFT_Y(win_data->offset_y) | 681ca555e5aSJoonyoung Shim VIDOSDxA_TOPLEFT_X_E(win_data->offset_x) | 682ca555e5aSJoonyoung Shim VIDOSDxA_TOPLEFT_Y_E(win_data->offset_y); 6831c248b7dSInki Dae writel(val, ctx->regs + VIDOSD_A(win)); 6841c248b7dSInki Dae 685f56aad3aSJoonyoung Shim last_x = win_data->offset_x + win_data->ovl_width; 686f56aad3aSJoonyoung Shim if (last_x) 687f56aad3aSJoonyoung Shim last_x--; 688f56aad3aSJoonyoung Shim last_y = win_data->offset_y + win_data->ovl_height; 689f56aad3aSJoonyoung Shim if (last_y) 690f56aad3aSJoonyoung Shim last_y--; 691f56aad3aSJoonyoung Shim 692ca555e5aSJoonyoung Shim val = VIDOSDxB_BOTRIGHT_X(last_x) | VIDOSDxB_BOTRIGHT_Y(last_y) | 693ca555e5aSJoonyoung Shim VIDOSDxB_BOTRIGHT_X_E(last_x) | VIDOSDxB_BOTRIGHT_Y_E(last_y); 694ca555e5aSJoonyoung Shim 6951c248b7dSInki Dae writel(val, ctx->regs + VIDOSD_B(win)); 6961c248b7dSInki Dae 69719c8b834SInki Dae DRM_DEBUG_KMS("osd pos: tx = %d, ty = %d, bx = %d, by = %d\n", 698f56aad3aSJoonyoung Shim win_data->offset_x, win_data->offset_y, last_x, last_y); 6991c248b7dSInki Dae 7001c248b7dSInki Dae /* hardware window 0 doesn't support alpha channel. */ 7011c248b7dSInki Dae if (win != 0) { 7021c248b7dSInki Dae /* OSD alpha */ 7031c248b7dSInki Dae alpha = VIDISD14C_ALPHA1_R(0xf) | 7041c248b7dSInki Dae VIDISD14C_ALPHA1_G(0xf) | 7051c248b7dSInki Dae VIDISD14C_ALPHA1_B(0xf); 7061c248b7dSInki Dae 7071c248b7dSInki Dae writel(alpha, ctx->regs + VIDOSD_C(win)); 7081c248b7dSInki Dae } 7091c248b7dSInki Dae 7101c248b7dSInki Dae /* OSD size */ 7111c248b7dSInki Dae if (win != 3 && win != 4) { 7121c248b7dSInki Dae u32 offset = VIDOSD_D(win); 7131c248b7dSInki Dae if (win == 0) 7140f10cf14SLeela Krishna Amudala offset = VIDOSD_C(win); 71519c8b834SInki Dae val = win_data->ovl_width * win_data->ovl_height; 7161c248b7dSInki Dae writel(val, ctx->regs + offset); 7171c248b7dSInki Dae 7181c248b7dSInki Dae DRM_DEBUG_KMS("osd size = 0x%x\n", (unsigned int)val); 7191c248b7dSInki Dae } 7201c248b7dSInki Dae 721bb7704d6SSean Paul fimd_win_set_pixfmt(ctx, win); 7221c248b7dSInki Dae 7231c248b7dSInki Dae /* hardware window 0 doesn't support color key. */ 7241c248b7dSInki Dae if (win != 0) 725bb7704d6SSean Paul fimd_win_set_colkey(ctx, win); 7261c248b7dSInki Dae 727ec05da95SInki Dae /* wincon */ 728ec05da95SInki Dae val = readl(ctx->regs + WINCON(win)); 729ec05da95SInki Dae val |= WINCONx_ENWIN; 730ec05da95SInki Dae writel(val, ctx->regs + WINCON(win)); 731ec05da95SInki Dae 7321c248b7dSInki Dae /* Enable DMA channel and unprotect windows */ 733de7af100STomasz Figa fimd_shadow_protect_win(ctx, win, false); 734de7af100STomasz Figa 735de7af100STomasz Figa if (ctx->driver_data->has_shadowcon) { 7361c248b7dSInki Dae val = readl(ctx->regs + SHADOWCON); 7371c248b7dSInki Dae val |= SHADOWCON_CHx_ENABLE(win); 7381c248b7dSInki Dae writel(val, ctx->regs + SHADOWCON); 739de7af100STomasz Figa } 740ec05da95SInki Dae 741ec05da95SInki Dae win_data->enabled = true; 7423854fab2SYoungJun Cho 7433854fab2SYoungJun Cho if (ctx->i80_if) 7443854fab2SYoungJun Cho atomic_set(&ctx->win_updated, 1); 7451c248b7dSInki Dae } 7461c248b7dSInki Dae 747bb7704d6SSean Paul static void fimd_win_disable(struct exynos_drm_manager *mgr, int zpos) 7481c248b7dSInki Dae { 749bb7704d6SSean Paul struct fimd_context *ctx = mgr->ctx; 750ec05da95SInki Dae struct fimd_win_data *win_data; 751864ee9e6SJoonyoung Shim int win = zpos; 7521c248b7dSInki Dae u32 val; 7531c248b7dSInki Dae 754864ee9e6SJoonyoung Shim if (win == DEFAULT_ZPOS) 755864ee9e6SJoonyoung Shim win = ctx->default_win; 756864ee9e6SJoonyoung Shim 75737b006e8SKrzysztof Kozlowski if (win < 0 || win >= WINDOWS_NR) 7581c248b7dSInki Dae return; 7591c248b7dSInki Dae 760ec05da95SInki Dae win_data = &ctx->win_data[win]; 761ec05da95SInki Dae 762db7e55aeSPrathyush K if (ctx->suspended) { 763db7e55aeSPrathyush K /* do not resume this window*/ 764db7e55aeSPrathyush K win_data->resume = false; 765db7e55aeSPrathyush K return; 766db7e55aeSPrathyush K } 767db7e55aeSPrathyush K 7681c248b7dSInki Dae /* protect windows */ 769de7af100STomasz Figa fimd_shadow_protect_win(ctx, win, true); 7701c248b7dSInki Dae 7711c248b7dSInki Dae /* wincon */ 7721c248b7dSInki Dae val = readl(ctx->regs + WINCON(win)); 7731c248b7dSInki Dae val &= ~WINCONx_ENWIN; 7741c248b7dSInki Dae writel(val, ctx->regs + WINCON(win)); 7751c248b7dSInki Dae 7761c248b7dSInki Dae /* unprotect windows */ 777de7af100STomasz Figa if (ctx->driver_data->has_shadowcon) { 7781c248b7dSInki Dae val = readl(ctx->regs + SHADOWCON); 7791c248b7dSInki Dae val &= ~SHADOWCON_CHx_ENABLE(win); 7801c248b7dSInki Dae writel(val, ctx->regs + SHADOWCON); 781de7af100STomasz Figa } 782de7af100STomasz Figa 783de7af100STomasz Figa fimd_shadow_protect_win(ctx, win, false); 784ec05da95SInki Dae 785ec05da95SInki Dae win_data->enabled = false; 7861c248b7dSInki Dae } 7871c248b7dSInki Dae 788a43b933bSSean Paul static void fimd_window_suspend(struct exynos_drm_manager *mgr) 789a43b933bSSean Paul { 790a43b933bSSean Paul struct fimd_context *ctx = mgr->ctx; 791a43b933bSSean Paul struct fimd_win_data *win_data; 792a43b933bSSean Paul int i; 793a43b933bSSean Paul 794a43b933bSSean Paul for (i = 0; i < WINDOWS_NR; i++) { 795a43b933bSSean Paul win_data = &ctx->win_data[i]; 796a43b933bSSean Paul win_data->resume = win_data->enabled; 797a43b933bSSean Paul if (win_data->enabled) 798a43b933bSSean Paul fimd_win_disable(mgr, i); 799a43b933bSSean Paul } 800a43b933bSSean Paul fimd_wait_for_vblank(mgr); 801a43b933bSSean Paul } 802a43b933bSSean Paul 803a43b933bSSean Paul static void fimd_window_resume(struct exynos_drm_manager *mgr) 804a43b933bSSean Paul { 805a43b933bSSean Paul struct fimd_context *ctx = mgr->ctx; 806a43b933bSSean Paul struct fimd_win_data *win_data; 807a43b933bSSean Paul int i; 808a43b933bSSean Paul 809a43b933bSSean Paul for (i = 0; i < WINDOWS_NR; i++) { 810a43b933bSSean Paul win_data = &ctx->win_data[i]; 811a43b933bSSean Paul win_data->enabled = win_data->resume; 812a43b933bSSean Paul win_data->resume = false; 813a43b933bSSean Paul } 814a43b933bSSean Paul } 815a43b933bSSean Paul 816a43b933bSSean Paul static void fimd_apply(struct exynos_drm_manager *mgr) 817a43b933bSSean Paul { 818a43b933bSSean Paul struct fimd_context *ctx = mgr->ctx; 819a43b933bSSean Paul struct fimd_win_data *win_data; 820a43b933bSSean Paul int i; 821a43b933bSSean Paul 822a43b933bSSean Paul for (i = 0; i < WINDOWS_NR; i++) { 823a43b933bSSean Paul win_data = &ctx->win_data[i]; 824a43b933bSSean Paul if (win_data->enabled) 825a43b933bSSean Paul fimd_win_commit(mgr, i); 826d9b68d89SAndrzej Hajda else 827d9b68d89SAndrzej Hajda fimd_win_disable(mgr, i); 828a43b933bSSean Paul } 829a43b933bSSean Paul 830a43b933bSSean Paul fimd_commit(mgr); 831a43b933bSSean Paul } 832a43b933bSSean Paul 833a43b933bSSean Paul static int fimd_poweron(struct exynos_drm_manager *mgr) 834a43b933bSSean Paul { 835a43b933bSSean Paul struct fimd_context *ctx = mgr->ctx; 836a43b933bSSean Paul int ret; 837a43b933bSSean Paul 838a43b933bSSean Paul if (!ctx->suspended) 839a43b933bSSean Paul return 0; 840a43b933bSSean Paul 841a43b933bSSean Paul ctx->suspended = false; 842a43b933bSSean Paul 843af65c804SSean Paul pm_runtime_get_sync(ctx->dev); 844af65c804SSean Paul 845a43b933bSSean Paul ret = clk_prepare_enable(ctx->bus_clk); 846a43b933bSSean Paul if (ret < 0) { 847a43b933bSSean Paul DRM_ERROR("Failed to prepare_enable the bus clk [%d]\n", ret); 848a43b933bSSean Paul goto bus_clk_err; 849a43b933bSSean Paul } 850a43b933bSSean Paul 851a43b933bSSean Paul ret = clk_prepare_enable(ctx->lcd_clk); 852a43b933bSSean Paul if (ret < 0) { 853a43b933bSSean Paul DRM_ERROR("Failed to prepare_enable the lcd clk [%d]\n", ret); 854a43b933bSSean Paul goto lcd_clk_err; 855a43b933bSSean Paul } 856a43b933bSSean Paul 857a43b933bSSean Paul /* if vblank was enabled status, enable it again. */ 858a43b933bSSean Paul if (test_and_clear_bit(0, &ctx->irq_flags)) { 859a43b933bSSean Paul ret = fimd_enable_vblank(mgr); 860a43b933bSSean Paul if (ret) { 861a43b933bSSean Paul DRM_ERROR("Failed to re-enable vblank [%d]\n", ret); 862a43b933bSSean Paul goto enable_vblank_err; 863a43b933bSSean Paul } 864a43b933bSSean Paul } 865a43b933bSSean Paul 866a43b933bSSean Paul fimd_window_resume(mgr); 867a43b933bSSean Paul 868a43b933bSSean Paul fimd_apply(mgr); 869a43b933bSSean Paul 870a43b933bSSean Paul return 0; 871a43b933bSSean Paul 872a43b933bSSean Paul enable_vblank_err: 873a43b933bSSean Paul clk_disable_unprepare(ctx->lcd_clk); 874a43b933bSSean Paul lcd_clk_err: 875a43b933bSSean Paul clk_disable_unprepare(ctx->bus_clk); 876a43b933bSSean Paul bus_clk_err: 877a43b933bSSean Paul ctx->suspended = true; 878a43b933bSSean Paul return ret; 879a43b933bSSean Paul } 880a43b933bSSean Paul 881a43b933bSSean Paul static int fimd_poweroff(struct exynos_drm_manager *mgr) 882a43b933bSSean Paul { 883a43b933bSSean Paul struct fimd_context *ctx = mgr->ctx; 884a43b933bSSean Paul 885a43b933bSSean Paul if (ctx->suspended) 886a43b933bSSean Paul return 0; 887a43b933bSSean Paul 888a43b933bSSean Paul /* 889a43b933bSSean Paul * We need to make sure that all windows are disabled before we 890a43b933bSSean Paul * suspend that connector. Otherwise we might try to scan from 891a43b933bSSean Paul * a destroyed buffer later. 892a43b933bSSean Paul */ 893a43b933bSSean Paul fimd_window_suspend(mgr); 894a43b933bSSean Paul 895a43b933bSSean Paul clk_disable_unprepare(ctx->lcd_clk); 896a43b933bSSean Paul clk_disable_unprepare(ctx->bus_clk); 897a43b933bSSean Paul 898af65c804SSean Paul pm_runtime_put_sync(ctx->dev); 899af65c804SSean Paul 900a43b933bSSean Paul ctx->suspended = true; 901a43b933bSSean Paul return 0; 902a43b933bSSean Paul } 903a43b933bSSean Paul 904080be03dSSean Paul static void fimd_dpms(struct exynos_drm_manager *mgr, int mode) 905080be03dSSean Paul { 906af65c804SSean Paul DRM_DEBUG_KMS("%s, %d\n", __FILE__, mode); 907080be03dSSean Paul 908080be03dSSean Paul switch (mode) { 909080be03dSSean Paul case DRM_MODE_DPMS_ON: 910af65c804SSean Paul fimd_poweron(mgr); 911080be03dSSean Paul break; 912080be03dSSean Paul case DRM_MODE_DPMS_STANDBY: 913080be03dSSean Paul case DRM_MODE_DPMS_SUSPEND: 914080be03dSSean Paul case DRM_MODE_DPMS_OFF: 915af65c804SSean Paul fimd_poweroff(mgr); 916080be03dSSean Paul break; 917080be03dSSean Paul default: 918080be03dSSean Paul DRM_DEBUG_KMS("unspecified mode %d\n", mode); 919080be03dSSean Paul break; 920080be03dSSean Paul } 921080be03dSSean Paul } 922080be03dSSean Paul 9233854fab2SYoungJun Cho static void fimd_trigger(struct device *dev) 9243854fab2SYoungJun Cho { 9253854fab2SYoungJun Cho struct exynos_drm_manager *mgr = get_fimd_manager(dev); 9263854fab2SYoungJun Cho struct fimd_context *ctx = mgr->ctx; 9273854fab2SYoungJun Cho struct fimd_driver_data *driver_data = ctx->driver_data; 9283854fab2SYoungJun Cho void *timing_base = ctx->regs + driver_data->timing_base; 9293854fab2SYoungJun Cho u32 reg; 9303854fab2SYoungJun Cho 9313854fab2SYoungJun Cho atomic_set(&ctx->triggering, 1); 9323854fab2SYoungJun Cho 9333854fab2SYoungJun Cho reg = readl(ctx->regs + VIDINTCON0); 9343854fab2SYoungJun Cho reg |= (VIDINTCON0_INT_ENABLE | VIDINTCON0_INT_I80IFDONE | 9353854fab2SYoungJun Cho VIDINTCON0_INT_SYSMAINCON); 9363854fab2SYoungJun Cho writel(reg, ctx->regs + VIDINTCON0); 9373854fab2SYoungJun Cho 9383854fab2SYoungJun Cho reg = readl(timing_base + TRIGCON); 9393854fab2SYoungJun Cho reg |= (TRGMODE_I80_RGB_ENABLE_I80 | SWTRGCMD_I80_RGB_ENABLE); 9403854fab2SYoungJun Cho writel(reg, timing_base + TRIGCON); 9413854fab2SYoungJun Cho } 9423854fab2SYoungJun Cho 9433854fab2SYoungJun Cho static void fimd_te_handler(struct exynos_drm_manager *mgr) 9443854fab2SYoungJun Cho { 9453854fab2SYoungJun Cho struct fimd_context *ctx = mgr->ctx; 9463854fab2SYoungJun Cho 9473854fab2SYoungJun Cho /* Checks the crtc is detached already from encoder */ 9483854fab2SYoungJun Cho if (ctx->pipe < 0 || !ctx->drm_dev) 9493854fab2SYoungJun Cho return; 9503854fab2SYoungJun Cho 9513854fab2SYoungJun Cho /* 9523854fab2SYoungJun Cho * Skips to trigger if in triggering state, because multiple triggering 9533854fab2SYoungJun Cho * requests can cause panel reset. 9543854fab2SYoungJun Cho */ 9553854fab2SYoungJun Cho if (atomic_read(&ctx->triggering)) 9563854fab2SYoungJun Cho return; 9573854fab2SYoungJun Cho 9583854fab2SYoungJun Cho /* 9593854fab2SYoungJun Cho * If there is a page flip request, triggers and handles the page flip 9603854fab2SYoungJun Cho * event so that current fb can be updated into panel GRAM. 9613854fab2SYoungJun Cho */ 9623854fab2SYoungJun Cho if (atomic_add_unless(&ctx->win_updated, -1, 0)) 9633854fab2SYoungJun Cho fimd_trigger(ctx->dev); 9643854fab2SYoungJun Cho 9653854fab2SYoungJun Cho /* Wakes up vsync event queue */ 9663854fab2SYoungJun Cho if (atomic_read(&ctx->wait_vsync_event)) { 9673854fab2SYoungJun Cho atomic_set(&ctx->wait_vsync_event, 0); 9683854fab2SYoungJun Cho wake_up(&ctx->wait_vsync_queue); 9693854fab2SYoungJun Cho 9703854fab2SYoungJun Cho if (!atomic_read(&ctx->triggering)) 9713854fab2SYoungJun Cho drm_handle_vblank(ctx->drm_dev, ctx->pipe); 9723854fab2SYoungJun Cho } 9733854fab2SYoungJun Cho } 9743854fab2SYoungJun Cho 9751c6244c3SSean Paul static struct exynos_drm_manager_ops fimd_manager_ops = { 9761c6244c3SSean Paul .dpms = fimd_dpms, 977a968e727SSean Paul .mode_fixup = fimd_mode_fixup, 978a968e727SSean Paul .mode_set = fimd_mode_set, 9791c6244c3SSean Paul .commit = fimd_commit, 9801c6244c3SSean Paul .enable_vblank = fimd_enable_vblank, 9811c6244c3SSean Paul .disable_vblank = fimd_disable_vblank, 9821c6244c3SSean Paul .wait_for_vblank = fimd_wait_for_vblank, 9831c6244c3SSean Paul .win_mode_set = fimd_win_mode_set, 9841c6244c3SSean Paul .win_commit = fimd_win_commit, 9851c6244c3SSean Paul .win_disable = fimd_win_disable, 9863854fab2SYoungJun Cho .te_handler = fimd_te_handler, 9871c248b7dSInki Dae }; 9881c248b7dSInki Dae 989677e84c1SJoonyoung Shim static struct exynos_drm_manager fimd_manager = { 990080be03dSSean Paul .type = EXYNOS_DISPLAY_TYPE_LCD, 991677e84c1SJoonyoung Shim .ops = &fimd_manager_ops, 992677e84c1SJoonyoung Shim }; 993677e84c1SJoonyoung Shim 9941c248b7dSInki Dae static irqreturn_t fimd_irq_handler(int irq, void *dev_id) 9951c248b7dSInki Dae { 9961c248b7dSInki Dae struct fimd_context *ctx = (struct fimd_context *)dev_id; 9973854fab2SYoungJun Cho u32 val, clear_bit; 9981c248b7dSInki Dae 9991c248b7dSInki Dae val = readl(ctx->regs + VIDINTCON1); 10001c248b7dSInki Dae 10013854fab2SYoungJun Cho clear_bit = ctx->i80_if ? VIDINTCON1_INT_I80 : VIDINTCON1_INT_FRAME; 10023854fab2SYoungJun Cho if (val & clear_bit) 10033854fab2SYoungJun Cho writel(clear_bit, ctx->regs + VIDINTCON1); 10041c248b7dSInki Dae 1005ec05da95SInki Dae /* check the crtc is detached already from encoder */ 1006080be03dSSean Paul if (ctx->pipe < 0 || !ctx->drm_dev) 1007ec05da95SInki Dae goto out; 1008483b88f8SInki Dae 10093854fab2SYoungJun Cho if (ctx->i80_if) { 10103854fab2SYoungJun Cho /* unset I80 frame done interrupt */ 10113854fab2SYoungJun Cho val = readl(ctx->regs + VIDINTCON0); 10123854fab2SYoungJun Cho val &= ~(VIDINTCON0_INT_I80IFDONE | VIDINTCON0_INT_SYSMAINCON); 10133854fab2SYoungJun Cho writel(val, ctx->regs + VIDINTCON0); 10143854fab2SYoungJun Cho 10153854fab2SYoungJun Cho /* exit triggering mode */ 10163854fab2SYoungJun Cho atomic_set(&ctx->triggering, 0); 10173854fab2SYoungJun Cho 10183854fab2SYoungJun Cho drm_handle_vblank(ctx->drm_dev, ctx->pipe); 10193854fab2SYoungJun Cho exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe); 10203854fab2SYoungJun Cho } else { 1021080be03dSSean Paul drm_handle_vblank(ctx->drm_dev, ctx->pipe); 1022080be03dSSean Paul exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe); 10231c248b7dSInki Dae 102401ce113cSPrathyush K /* set wait vsync event to zero and wake up queue. */ 102501ce113cSPrathyush K if (atomic_read(&ctx->wait_vsync_event)) { 102601ce113cSPrathyush K atomic_set(&ctx->wait_vsync_event, 0); 10278dd9ad5dSSeung-Woo Kim wake_up(&ctx->wait_vsync_queue); 102801ce113cSPrathyush K } 10293854fab2SYoungJun Cho } 10303854fab2SYoungJun Cho 1031ec05da95SInki Dae out: 10321c248b7dSInki Dae return IRQ_HANDLED; 10331c248b7dSInki Dae } 10341c248b7dSInki Dae 1035f37cd5e8SInki Dae static int fimd_bind(struct device *dev, struct device *master, void *data) 1036562ad9f4SAndrzej Hajda { 1037000cc920SAndrzej Hajda struct fimd_context *ctx = fimd_manager.ctx; 1038f37cd5e8SInki Dae struct drm_device *drm_dev = data; 1039000cc920SAndrzej Hajda 1040000cc920SAndrzej Hajda fimd_mgr_initialize(&fimd_manager, drm_dev); 1041000cc920SAndrzej Hajda exynos_drm_crtc_create(&fimd_manager); 1042000cc920SAndrzej Hajda if (ctx->display) 1043000cc920SAndrzej Hajda exynos_drm_create_enc_conn(drm_dev, ctx->display); 1044000cc920SAndrzej Hajda 1045000cc920SAndrzej Hajda return 0; 1046000cc920SAndrzej Hajda 1047000cc920SAndrzej Hajda } 1048000cc920SAndrzej Hajda 1049000cc920SAndrzej Hajda static void fimd_unbind(struct device *dev, struct device *master, 1050000cc920SAndrzej Hajda void *data) 1051000cc920SAndrzej Hajda { 1052000cc920SAndrzej Hajda struct exynos_drm_manager *mgr = dev_get_drvdata(dev); 1053000cc920SAndrzej Hajda struct fimd_context *ctx = fimd_manager.ctx; 1054000cc920SAndrzej Hajda struct drm_crtc *crtc = mgr->crtc; 1055000cc920SAndrzej Hajda 1056000cc920SAndrzej Hajda fimd_dpms(mgr, DRM_MODE_DPMS_OFF); 1057000cc920SAndrzej Hajda 1058000cc920SAndrzej Hajda if (ctx->display) 1059000cc920SAndrzej Hajda exynos_dpi_remove(dev); 1060000cc920SAndrzej Hajda 1061000cc920SAndrzej Hajda fimd_mgr_remove(mgr); 1062000cc920SAndrzej Hajda 1063000cc920SAndrzej Hajda crtc->funcs->destroy(crtc); 1064000cc920SAndrzej Hajda } 1065000cc920SAndrzej Hajda 1066000cc920SAndrzej Hajda static const struct component_ops fimd_component_ops = { 1067000cc920SAndrzej Hajda .bind = fimd_bind, 1068000cc920SAndrzej Hajda .unbind = fimd_unbind, 1069000cc920SAndrzej Hajda }; 1070000cc920SAndrzej Hajda 1071000cc920SAndrzej Hajda static int fimd_probe(struct platform_device *pdev) 1072000cc920SAndrzej Hajda { 1073000cc920SAndrzej Hajda struct device *dev = &pdev->dev; 1074000cc920SAndrzej Hajda struct fimd_context *ctx; 10753854fab2SYoungJun Cho struct device_node *i80_if_timings; 1076000cc920SAndrzej Hajda struct resource *res; 1077562ad9f4SAndrzej Hajda int ret = -EINVAL; 1078562ad9f4SAndrzej Hajda 1079df5225bcSInki Dae ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC, 1080df5225bcSInki Dae fimd_manager.type); 1081df5225bcSInki Dae if (ret) 1082df5225bcSInki Dae return ret; 1083df5225bcSInki Dae 1084df5225bcSInki Dae if (!dev->of_node) { 1085df5225bcSInki Dae ret = -ENODEV; 1086df5225bcSInki Dae goto err_del_component; 1087df5225bcSInki Dae } 10882d3f173cSSachin Kamat 1089d873ab99SSeung-Woo Kim ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); 1090df5225bcSInki Dae if (!ctx) { 1091df5225bcSInki Dae ret = -ENOMEM; 1092df5225bcSInki Dae goto err_del_component; 1093df5225bcSInki Dae } 10941c248b7dSInki Dae 1095bb7704d6SSean Paul ctx->dev = dev; 1096a43b933bSSean Paul ctx->suspended = true; 10973854fab2SYoungJun Cho ctx->driver_data = drm_fimd_get_driver_data(pdev); 1098bb7704d6SSean Paul 10991417f109SSean Paul if (of_property_read_bool(dev->of_node, "samsung,invert-vden")) 11001417f109SSean Paul ctx->vidcon1 |= VIDCON1_INV_VDEN; 11011417f109SSean Paul if (of_property_read_bool(dev->of_node, "samsung,invert-vclk")) 11021417f109SSean Paul ctx->vidcon1 |= VIDCON1_INV_VCLK; 1103562ad9f4SAndrzej Hajda 11043854fab2SYoungJun Cho i80_if_timings = of_get_child_by_name(dev->of_node, "i80-if-timings"); 11053854fab2SYoungJun Cho if (i80_if_timings) { 11063854fab2SYoungJun Cho u32 val; 11073854fab2SYoungJun Cho 11083854fab2SYoungJun Cho ctx->i80_if = true; 11093854fab2SYoungJun Cho 11103854fab2SYoungJun Cho if (ctx->driver_data->has_vidoutcon) 11113854fab2SYoungJun Cho ctx->vidout_con |= VIDOUT_CON_F_I80_LDI0; 11123854fab2SYoungJun Cho else 11133854fab2SYoungJun Cho ctx->vidcon0 |= VIDCON0_VIDOUT_I80_LDI0; 11143854fab2SYoungJun Cho /* 11153854fab2SYoungJun Cho * The user manual describes that this "DSI_EN" bit is required 11163854fab2SYoungJun Cho * to enable I80 24-bit data interface. 11173854fab2SYoungJun Cho */ 11183854fab2SYoungJun Cho ctx->vidcon0 |= VIDCON0_DSI_EN; 11193854fab2SYoungJun Cho 11203854fab2SYoungJun Cho if (of_property_read_u32(i80_if_timings, "cs-setup", &val)) 11213854fab2SYoungJun Cho val = 0; 11223854fab2SYoungJun Cho ctx->i80ifcon = LCD_CS_SETUP(val); 11233854fab2SYoungJun Cho if (of_property_read_u32(i80_if_timings, "wr-setup", &val)) 11243854fab2SYoungJun Cho val = 0; 11253854fab2SYoungJun Cho ctx->i80ifcon |= LCD_WR_SETUP(val); 11263854fab2SYoungJun Cho if (of_property_read_u32(i80_if_timings, "wr-active", &val)) 11273854fab2SYoungJun Cho val = 1; 11283854fab2SYoungJun Cho ctx->i80ifcon |= LCD_WR_ACTIVE(val); 11293854fab2SYoungJun Cho if (of_property_read_u32(i80_if_timings, "wr-hold", &val)) 11303854fab2SYoungJun Cho val = 0; 11313854fab2SYoungJun Cho ctx->i80ifcon |= LCD_WR_HOLD(val); 11323854fab2SYoungJun Cho } 11333854fab2SYoungJun Cho of_node_put(i80_if_timings); 11343854fab2SYoungJun Cho 11353854fab2SYoungJun Cho ctx->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node, 11363854fab2SYoungJun Cho "samsung,sysreg"); 11373854fab2SYoungJun Cho if (IS_ERR(ctx->sysreg)) { 11383854fab2SYoungJun Cho dev_warn(dev, "failed to get system register.\n"); 11393854fab2SYoungJun Cho ctx->sysreg = NULL; 11403854fab2SYoungJun Cho } 11413854fab2SYoungJun Cho 1142a968e727SSean Paul ctx->bus_clk = devm_clk_get(dev, "fimd"); 1143a968e727SSean Paul if (IS_ERR(ctx->bus_clk)) { 1144a968e727SSean Paul dev_err(dev, "failed to get bus clock\n"); 1145df5225bcSInki Dae ret = PTR_ERR(ctx->bus_clk); 1146df5225bcSInki Dae goto err_del_component; 1147a968e727SSean Paul } 1148a968e727SSean Paul 1149a968e727SSean Paul ctx->lcd_clk = devm_clk_get(dev, "sclk_fimd"); 1150a968e727SSean Paul if (IS_ERR(ctx->lcd_clk)) { 1151a968e727SSean Paul dev_err(dev, "failed to get lcd clock\n"); 1152df5225bcSInki Dae ret = PTR_ERR(ctx->lcd_clk); 1153df5225bcSInki Dae goto err_del_component; 1154a968e727SSean Paul } 11551c248b7dSInki Dae 11561c248b7dSInki Dae res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 11571c248b7dSInki Dae 1158d873ab99SSeung-Woo Kim ctx->regs = devm_ioremap_resource(dev, res); 1159df5225bcSInki Dae if (IS_ERR(ctx->regs)) { 1160df5225bcSInki Dae ret = PTR_ERR(ctx->regs); 1161df5225bcSInki Dae goto err_del_component; 1162df5225bcSInki Dae } 11631c248b7dSInki Dae 11643854fab2SYoungJun Cho res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, 11653854fab2SYoungJun Cho ctx->i80_if ? "lcd_sys" : "vsync"); 11661c248b7dSInki Dae if (!res) { 11671c248b7dSInki Dae dev_err(dev, "irq request failed.\n"); 1168df5225bcSInki Dae ret = -ENXIO; 1169df5225bcSInki Dae goto err_del_component; 11701c248b7dSInki Dae } 11711c248b7dSInki Dae 1172055e0c06SSean Paul ret = devm_request_irq(dev, res->start, fimd_irq_handler, 1173edc57266SSachin Kamat 0, "drm_fimd", ctx); 1174edc57266SSachin Kamat if (ret) { 11751c248b7dSInki Dae dev_err(dev, "irq request failed.\n"); 1176df5225bcSInki Dae goto err_del_component; 11771c248b7dSInki Dae } 11781c248b7dSInki Dae 117957ed0f7bSDaniel Vetter init_waitqueue_head(&ctx->wait_vsync_queue); 118001ce113cSPrathyush K atomic_set(&ctx->wait_vsync_event, 0); 11811c248b7dSInki Dae 1182bb7704d6SSean Paul platform_set_drvdata(pdev, &fimd_manager); 1183c32b06efSInki Dae 1184080be03dSSean Paul fimd_manager.ctx = ctx; 1185080be03dSSean Paul 1186000cc920SAndrzej Hajda ctx->display = exynos_dpi_probe(dev); 1187000cc920SAndrzej Hajda if (IS_ERR(ctx->display)) 1188000cc920SAndrzej Hajda return PTR_ERR(ctx->display); 1189f37cd5e8SInki Dae 1190f37cd5e8SInki Dae pm_runtime_enable(&pdev->dev); 1191f37cd5e8SInki Dae 1192df5225bcSInki Dae ret = component_add(&pdev->dev, &fimd_component_ops); 1193df5225bcSInki Dae if (ret) 1194df5225bcSInki Dae goto err_disable_pm_runtime; 1195df5225bcSInki Dae 1196df5225bcSInki Dae return ret; 1197df5225bcSInki Dae 1198df5225bcSInki Dae err_disable_pm_runtime: 1199df5225bcSInki Dae pm_runtime_disable(&pdev->dev); 1200df5225bcSInki Dae 1201df5225bcSInki Dae err_del_component: 1202df5225bcSInki Dae exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC); 1203df5225bcSInki Dae return ret; 1204f37cd5e8SInki Dae } 1205f37cd5e8SInki Dae 1206f37cd5e8SInki Dae static int fimd_remove(struct platform_device *pdev) 1207f37cd5e8SInki Dae { 1208af65c804SSean Paul pm_runtime_disable(&pdev->dev); 1209cb91f6a0SJoonyoung Shim 1210df5225bcSInki Dae component_del(&pdev->dev, &fimd_component_ops); 1211df5225bcSInki Dae exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC); 1212df5225bcSInki Dae 12131c248b7dSInki Dae return 0; 12141c248b7dSInki Dae } 12151c248b7dSInki Dae 1216132a5b91SJoonyoung Shim struct platform_driver fimd_driver = { 12171c248b7dSInki Dae .probe = fimd_probe, 121856550d94SGreg Kroah-Hartman .remove = fimd_remove, 12191c248b7dSInki Dae .driver = { 12201c248b7dSInki Dae .name = "exynos4-fb", 12211c248b7dSInki Dae .owner = THIS_MODULE, 12222d3f173cSSachin Kamat .of_match_table = fimd_driver_dt_match, 12231c248b7dSInki Dae }, 12241c248b7dSInki Dae }; 1225