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 87e2e13389SLeela Krishna Amudala struct fimd_driver_data { 88e2e13389SLeela Krishna Amudala unsigned int timing_base; 893854fab2SYoungJun Cho unsigned int lcdblk_offset; 903854fab2SYoungJun Cho unsigned int lcdblk_vt_shift; 913854fab2SYoungJun Cho unsigned int lcdblk_bypass_shift; 92de7af100STomasz Figa 93de7af100STomasz Figa unsigned int has_shadowcon:1; 94411d9ed4STomasz Figa unsigned int has_clksel:1; 955cc4621aSInki Dae unsigned int has_limited_fmt:1; 963854fab2SYoungJun Cho unsigned int has_vidoutcon:1; 973c3c9c1dSJoonyoung Shim unsigned int has_vtsel:1; 98e2e13389SLeela Krishna Amudala }; 99e2e13389SLeela Krishna Amudala 100725ddeadSTomasz Figa static struct fimd_driver_data s3c64xx_fimd_driver_data = { 101725ddeadSTomasz Figa .timing_base = 0x0, 102725ddeadSTomasz Figa .has_clksel = 1, 1035cc4621aSInki Dae .has_limited_fmt = 1, 104725ddeadSTomasz Figa }; 105725ddeadSTomasz Figa 106d6ce7b58SInki Dae static struct fimd_driver_data exynos3_fimd_driver_data = { 107d6ce7b58SInki Dae .timing_base = 0x20000, 108d6ce7b58SInki Dae .lcdblk_offset = 0x210, 109d6ce7b58SInki Dae .lcdblk_bypass_shift = 1, 110d6ce7b58SInki Dae .has_shadowcon = 1, 111d6ce7b58SInki Dae .has_vidoutcon = 1, 112d6ce7b58SInki Dae }; 113d6ce7b58SInki Dae 1146ecf18f9SSachin Kamat static struct fimd_driver_data exynos4_fimd_driver_data = { 115e2e13389SLeela Krishna Amudala .timing_base = 0x0, 1163854fab2SYoungJun Cho .lcdblk_offset = 0x210, 1173854fab2SYoungJun Cho .lcdblk_vt_shift = 10, 1183854fab2SYoungJun Cho .lcdblk_bypass_shift = 1, 119de7af100STomasz Figa .has_shadowcon = 1, 1203c3c9c1dSJoonyoung Shim .has_vtsel = 1, 121e2e13389SLeela Krishna Amudala }; 122e2e13389SLeela Krishna Amudala 123dcb622aaSYoungJun Cho static struct fimd_driver_data exynos4415_fimd_driver_data = { 124dcb622aaSYoungJun Cho .timing_base = 0x20000, 125dcb622aaSYoungJun Cho .lcdblk_offset = 0x210, 126dcb622aaSYoungJun Cho .lcdblk_vt_shift = 10, 127dcb622aaSYoungJun Cho .lcdblk_bypass_shift = 1, 128dcb622aaSYoungJun Cho .has_shadowcon = 1, 129dcb622aaSYoungJun Cho .has_vidoutcon = 1, 1303c3c9c1dSJoonyoung Shim .has_vtsel = 1, 131dcb622aaSYoungJun Cho }; 132dcb622aaSYoungJun Cho 1336ecf18f9SSachin Kamat static struct fimd_driver_data exynos5_fimd_driver_data = { 134e2e13389SLeela Krishna Amudala .timing_base = 0x20000, 1353854fab2SYoungJun Cho .lcdblk_offset = 0x214, 1363854fab2SYoungJun Cho .lcdblk_vt_shift = 24, 1373854fab2SYoungJun Cho .lcdblk_bypass_shift = 15, 138de7af100STomasz Figa .has_shadowcon = 1, 1393854fab2SYoungJun Cho .has_vidoutcon = 1, 1403c3c9c1dSJoonyoung Shim .has_vtsel = 1, 141e2e13389SLeela Krishna Amudala }; 142e2e13389SLeela Krishna Amudala 1431c248b7dSInki Dae struct fimd_win_data { 1441c248b7dSInki Dae unsigned int offset_x; 1451c248b7dSInki Dae unsigned int offset_y; 14619c8b834SInki Dae unsigned int ovl_width; 14719c8b834SInki Dae unsigned int ovl_height; 14819c8b834SInki Dae unsigned int fb_width; 14919c8b834SInki Dae unsigned int fb_height; 1501c248b7dSInki Dae unsigned int bpp; 151a4f38a80SInki Dae unsigned int pixel_format; 1522c871127SInki Dae dma_addr_t dma_addr; 1531c248b7dSInki Dae unsigned int buf_offsize; 1541c248b7dSInki Dae unsigned int line_size; /* bytes */ 155ec05da95SInki Dae bool enabled; 156db7e55aeSPrathyush K bool resume; 1571c248b7dSInki Dae }; 1581c248b7dSInki Dae 1591c248b7dSInki Dae struct fimd_context { 160e152dbd7SAndrzej Hajda struct exynos_drm_manager manager; 161bb7704d6SSean Paul struct device *dev; 16240c8ab4bSSean Paul struct drm_device *drm_dev; 1631c248b7dSInki Dae struct clk *bus_clk; 1641c248b7dSInki Dae struct clk *lcd_clk; 1651c248b7dSInki Dae void __iomem *regs; 1663854fab2SYoungJun Cho struct regmap *sysreg; 167a968e727SSean Paul struct drm_display_mode mode; 1681c248b7dSInki Dae struct fimd_win_data win_data[WINDOWS_NR]; 1691c248b7dSInki Dae unsigned int default_win; 1701c248b7dSInki Dae unsigned long irq_flags; 1713854fab2SYoungJun Cho u32 vidcon0; 1721c248b7dSInki Dae u32 vidcon1; 1733854fab2SYoungJun Cho u32 vidout_con; 1743854fab2SYoungJun Cho u32 i80ifcon; 1753854fab2SYoungJun Cho bool i80_if; 176cb91f6a0SJoonyoung Shim bool suspended; 177080be03dSSean Paul int pipe; 17801ce113cSPrathyush K wait_queue_head_t wait_vsync_queue; 17901ce113cSPrathyush K atomic_t wait_vsync_event; 1803854fab2SYoungJun Cho atomic_t win_updated; 1813854fab2SYoungJun Cho atomic_t triggering; 1821c248b7dSInki Dae 183562ad9f4SAndrzej Hajda struct exynos_drm_panel_info panel; 18418873465STomasz Figa struct fimd_driver_data *driver_data; 185000cc920SAndrzej Hajda struct exynos_drm_display *display; 1861c248b7dSInki Dae }; 1871c248b7dSInki Dae 188400c8ac8SAndrzej Hajda static inline struct fimd_context *mgr_to_fimd(struct exynos_drm_manager *mgr) 189400c8ac8SAndrzej Hajda { 190400c8ac8SAndrzej Hajda return container_of(mgr, struct fimd_context, manager); 191400c8ac8SAndrzej Hajda } 192400c8ac8SAndrzej Hajda 193d636ead8SJoonyoung Shim static const struct of_device_id fimd_driver_dt_match[] = { 194725ddeadSTomasz Figa { .compatible = "samsung,s3c6400-fimd", 195725ddeadSTomasz Figa .data = &s3c64xx_fimd_driver_data }, 196d6ce7b58SInki Dae { .compatible = "samsung,exynos3250-fimd", 197d6ce7b58SInki Dae .data = &exynos3_fimd_driver_data }, 1985830daf8SVikas Sajjan { .compatible = "samsung,exynos4210-fimd", 199d636ead8SJoonyoung Shim .data = &exynos4_fimd_driver_data }, 200dcb622aaSYoungJun Cho { .compatible = "samsung,exynos4415-fimd", 201dcb622aaSYoungJun Cho .data = &exynos4415_fimd_driver_data }, 2025830daf8SVikas Sajjan { .compatible = "samsung,exynos5250-fimd", 203d636ead8SJoonyoung Shim .data = &exynos5_fimd_driver_data }, 204d636ead8SJoonyoung Shim {}, 205d636ead8SJoonyoung Shim }; 2060262ceebSSjoerd Simons MODULE_DEVICE_TABLE(of, fimd_driver_dt_match); 207d636ead8SJoonyoung Shim 208e2e13389SLeela Krishna Amudala static inline struct fimd_driver_data *drm_fimd_get_driver_data( 209e2e13389SLeela Krishna Amudala struct platform_device *pdev) 210e2e13389SLeela Krishna Amudala { 211d636ead8SJoonyoung Shim const struct of_device_id *of_id = 212d636ead8SJoonyoung Shim of_match_device(fimd_driver_dt_match, &pdev->dev); 213d636ead8SJoonyoung Shim 214d636ead8SJoonyoung Shim return (struct fimd_driver_data *)of_id->data; 215e2e13389SLeela Krishna Amudala } 216e2e13389SLeela Krishna Amudala 217f13bdbd1SAkshu Agrawal static void fimd_wait_for_vblank(struct exynos_drm_manager *mgr) 218f13bdbd1SAkshu Agrawal { 219400c8ac8SAndrzej Hajda struct fimd_context *ctx = mgr_to_fimd(mgr); 220f13bdbd1SAkshu Agrawal 221f13bdbd1SAkshu Agrawal if (ctx->suspended) 222f13bdbd1SAkshu Agrawal return; 223f13bdbd1SAkshu Agrawal 224f13bdbd1SAkshu Agrawal atomic_set(&ctx->wait_vsync_event, 1); 225f13bdbd1SAkshu Agrawal 226f13bdbd1SAkshu Agrawal /* 227f13bdbd1SAkshu Agrawal * wait for FIMD to signal VSYNC interrupt or return after 228f13bdbd1SAkshu Agrawal * timeout which is set to 50ms (refresh rate of 20). 229f13bdbd1SAkshu Agrawal */ 230f13bdbd1SAkshu Agrawal if (!wait_event_timeout(ctx->wait_vsync_queue, 231f13bdbd1SAkshu Agrawal !atomic_read(&ctx->wait_vsync_event), 232f13bdbd1SAkshu Agrawal HZ/20)) 233f13bdbd1SAkshu Agrawal DRM_DEBUG_KMS("vblank wait timed out.\n"); 234f13bdbd1SAkshu Agrawal } 235f13bdbd1SAkshu Agrawal 236f181a543SYoungJun Cho static void fimd_enable_video_output(struct fimd_context *ctx, int win, 237f181a543SYoungJun Cho bool enable) 238f181a543SYoungJun Cho { 239f181a543SYoungJun Cho u32 val = readl(ctx->regs + WINCON(win)); 240f181a543SYoungJun Cho 241f181a543SYoungJun Cho if (enable) 242f181a543SYoungJun Cho val |= WINCONx_ENWIN; 243f181a543SYoungJun Cho else 244f181a543SYoungJun Cho val &= ~WINCONx_ENWIN; 245f181a543SYoungJun Cho 246f181a543SYoungJun Cho writel(val, ctx->regs + WINCON(win)); 247f181a543SYoungJun Cho } 248f181a543SYoungJun Cho 249999d8b31SYoungJun Cho static void fimd_enable_shadow_channel_path(struct fimd_context *ctx, int win, 250999d8b31SYoungJun Cho bool enable) 251999d8b31SYoungJun Cho { 252999d8b31SYoungJun Cho u32 val = readl(ctx->regs + SHADOWCON); 253999d8b31SYoungJun Cho 254999d8b31SYoungJun Cho if (enable) 255999d8b31SYoungJun Cho val |= SHADOWCON_CHx_ENABLE(win); 256999d8b31SYoungJun Cho else 257999d8b31SYoungJun Cho val &= ~SHADOWCON_CHx_ENABLE(win); 258999d8b31SYoungJun Cho 259999d8b31SYoungJun Cho writel(val, ctx->regs + SHADOWCON); 260999d8b31SYoungJun Cho } 261999d8b31SYoungJun Cho 262f13bdbd1SAkshu Agrawal static void fimd_clear_channel(struct exynos_drm_manager *mgr) 263f13bdbd1SAkshu Agrawal { 264400c8ac8SAndrzej Hajda struct fimd_context *ctx = mgr_to_fimd(mgr); 265f13bdbd1SAkshu Agrawal int win, ch_enabled = 0; 266f13bdbd1SAkshu Agrawal 267f13bdbd1SAkshu Agrawal DRM_DEBUG_KMS("%s\n", __FILE__); 268f13bdbd1SAkshu Agrawal 269f13bdbd1SAkshu Agrawal /* Check if any channel is enabled. */ 270f13bdbd1SAkshu Agrawal for (win = 0; win < WINDOWS_NR; win++) { 271eb8a3bf7SMarek Szyprowski u32 val = readl(ctx->regs + WINCON(win)); 272eb8a3bf7SMarek Szyprowski 273eb8a3bf7SMarek Szyprowski if (val & WINCONx_ENWIN) { 274f181a543SYoungJun Cho fimd_enable_video_output(ctx, win, false); 275eb8a3bf7SMarek Szyprowski 276999d8b31SYoungJun Cho if (ctx->driver_data->has_shadowcon) 277999d8b31SYoungJun Cho fimd_enable_shadow_channel_path(ctx, win, 278999d8b31SYoungJun Cho false); 279999d8b31SYoungJun Cho 280f13bdbd1SAkshu Agrawal ch_enabled = 1; 281f13bdbd1SAkshu Agrawal } 282f13bdbd1SAkshu Agrawal } 283f13bdbd1SAkshu Agrawal 284f13bdbd1SAkshu Agrawal /* Wait for vsync, as disable channel takes effect at next vsync */ 285eb8a3bf7SMarek Szyprowski if (ch_enabled) { 286eb8a3bf7SMarek Szyprowski unsigned int state = ctx->suspended; 287eb8a3bf7SMarek Szyprowski 288eb8a3bf7SMarek Szyprowski ctx->suspended = 0; 289f13bdbd1SAkshu Agrawal fimd_wait_for_vblank(mgr); 290eb8a3bf7SMarek Szyprowski ctx->suspended = state; 291eb8a3bf7SMarek Szyprowski } 292f13bdbd1SAkshu Agrawal } 293f13bdbd1SAkshu Agrawal 294bb7704d6SSean Paul static int fimd_mgr_initialize(struct exynos_drm_manager *mgr, 295f37cd5e8SInki Dae struct drm_device *drm_dev) 29640c8ab4bSSean Paul { 297400c8ac8SAndrzej Hajda struct fimd_context *ctx = mgr_to_fimd(mgr); 298f37cd5e8SInki Dae struct exynos_drm_private *priv; 299f37cd5e8SInki Dae priv = drm_dev->dev_private; 30040c8ab4bSSean Paul 301f37cd5e8SInki Dae mgr->drm_dev = ctx->drm_dev = drm_dev; 302f37cd5e8SInki Dae mgr->pipe = ctx->pipe = priv->pipe++; 303080be03dSSean Paul 304080be03dSSean Paul /* attach this sub driver to iommu mapping if supported. */ 305f13bdbd1SAkshu Agrawal if (is_drm_iommu_supported(ctx->drm_dev)) { 306f13bdbd1SAkshu Agrawal /* 307f13bdbd1SAkshu Agrawal * If any channel is already active, iommu will throw 308f13bdbd1SAkshu Agrawal * a PAGE FAULT when enabled. So clear any channel if enabled. 309f13bdbd1SAkshu Agrawal */ 310f13bdbd1SAkshu Agrawal fimd_clear_channel(mgr); 311080be03dSSean Paul drm_iommu_attach_device(ctx->drm_dev, ctx->dev); 312f13bdbd1SAkshu Agrawal } 31340c8ab4bSSean Paul 31440c8ab4bSSean Paul return 0; 31540c8ab4bSSean Paul } 31640c8ab4bSSean Paul 317080be03dSSean Paul static void fimd_mgr_remove(struct exynos_drm_manager *mgr) 318ec05da95SInki Dae { 319400c8ac8SAndrzej Hajda struct fimd_context *ctx = mgr_to_fimd(mgr); 320c32b06efSInki Dae 321080be03dSSean Paul /* detach this sub driver from iommu mapping if supported. */ 322080be03dSSean Paul if (is_drm_iommu_supported(ctx->drm_dev)) 323080be03dSSean Paul drm_iommu_detach_device(ctx->drm_dev, ctx->dev); 324ec05da95SInki Dae } 325ec05da95SInki Dae 326a968e727SSean Paul static u32 fimd_calc_clkdiv(struct fimd_context *ctx, 327a968e727SSean Paul const struct drm_display_mode *mode) 328a968e727SSean Paul { 329a968e727SSean Paul unsigned long ideal_clk = mode->htotal * mode->vtotal * mode->vrefresh; 330a968e727SSean Paul u32 clkdiv; 331a968e727SSean Paul 3323854fab2SYoungJun Cho if (ctx->i80_if) { 3333854fab2SYoungJun Cho /* 3343854fab2SYoungJun Cho * The frame done interrupt should be occurred prior to the 3353854fab2SYoungJun Cho * next TE signal. 3363854fab2SYoungJun Cho */ 3373854fab2SYoungJun Cho ideal_clk *= 2; 3383854fab2SYoungJun Cho } 3393854fab2SYoungJun Cho 340a968e727SSean Paul /* Find the clock divider value that gets us closest to ideal_clk */ 341a968e727SSean Paul clkdiv = DIV_ROUND_UP(clk_get_rate(ctx->lcd_clk), ideal_clk); 342a968e727SSean Paul 343a968e727SSean Paul return (clkdiv < 0x100) ? clkdiv : 0xff; 344a968e727SSean Paul } 345a968e727SSean Paul 346a968e727SSean Paul static bool fimd_mode_fixup(struct exynos_drm_manager *mgr, 347a968e727SSean Paul const struct drm_display_mode *mode, 348a968e727SSean Paul struct drm_display_mode *adjusted_mode) 349a968e727SSean Paul { 350a968e727SSean Paul if (adjusted_mode->vrefresh == 0) 351a968e727SSean Paul adjusted_mode->vrefresh = FIMD_DEFAULT_FRAMERATE; 352a968e727SSean Paul 353a968e727SSean Paul return true; 354a968e727SSean Paul } 355a968e727SSean Paul 356a968e727SSean Paul static void fimd_mode_set(struct exynos_drm_manager *mgr, 357a968e727SSean Paul const struct drm_display_mode *in_mode) 358a968e727SSean Paul { 359400c8ac8SAndrzej Hajda struct fimd_context *ctx = mgr_to_fimd(mgr); 360a968e727SSean Paul 361a968e727SSean Paul drm_mode_copy(&ctx->mode, in_mode); 362a968e727SSean Paul } 363a968e727SSean Paul 364bb7704d6SSean Paul static void fimd_commit(struct exynos_drm_manager *mgr) 3651c248b7dSInki Dae { 366400c8ac8SAndrzej Hajda struct fimd_context *ctx = mgr_to_fimd(mgr); 367a968e727SSean Paul struct drm_display_mode *mode = &ctx->mode; 3683854fab2SYoungJun Cho struct fimd_driver_data *driver_data = ctx->driver_data; 3693854fab2SYoungJun Cho void *timing_base = ctx->regs + driver_data->timing_base; 3703854fab2SYoungJun Cho u32 val, clkdiv; 3711c248b7dSInki Dae 372e30d4bcfSInki Dae if (ctx->suspended) 373e30d4bcfSInki Dae return; 374e30d4bcfSInki Dae 375a968e727SSean Paul /* nothing to do if we haven't set the mode yet */ 376a968e727SSean Paul if (mode->htotal == 0 || mode->vtotal == 0) 377a968e727SSean Paul return; 378a968e727SSean Paul 3793854fab2SYoungJun Cho if (ctx->i80_if) { 3803854fab2SYoungJun Cho val = ctx->i80ifcon | I80IFEN_ENABLE; 3813854fab2SYoungJun Cho writel(val, timing_base + I80IFCONFAx(0)); 3823854fab2SYoungJun Cho 3833854fab2SYoungJun Cho /* disable auto frame rate */ 3843854fab2SYoungJun Cho writel(0, timing_base + I80IFCONFBx(0)); 3853854fab2SYoungJun Cho 3863854fab2SYoungJun Cho /* set video type selection to I80 interface */ 3873c3c9c1dSJoonyoung Shim if (driver_data->has_vtsel && ctx->sysreg && 3883c3c9c1dSJoonyoung Shim regmap_update_bits(ctx->sysreg, 3893854fab2SYoungJun Cho driver_data->lcdblk_offset, 3903854fab2SYoungJun Cho 0x3 << driver_data->lcdblk_vt_shift, 3913854fab2SYoungJun Cho 0x1 << driver_data->lcdblk_vt_shift)) { 3923854fab2SYoungJun Cho DRM_ERROR("Failed to update sysreg for I80 i/f.\n"); 3933854fab2SYoungJun Cho return; 3943854fab2SYoungJun Cho } 3953854fab2SYoungJun Cho } else { 3963854fab2SYoungJun Cho int vsync_len, vbpd, vfpd, hsync_len, hbpd, hfpd; 3973854fab2SYoungJun Cho u32 vidcon1; 3983854fab2SYoungJun Cho 3991417f109SSean Paul /* setup polarity values */ 4001417f109SSean Paul vidcon1 = ctx->vidcon1; 4011417f109SSean Paul if (mode->flags & DRM_MODE_FLAG_NVSYNC) 4021417f109SSean Paul vidcon1 |= VIDCON1_INV_VSYNC; 4031417f109SSean Paul if (mode->flags & DRM_MODE_FLAG_NHSYNC) 4041417f109SSean Paul vidcon1 |= VIDCON1_INV_HSYNC; 4051417f109SSean Paul writel(vidcon1, ctx->regs + driver_data->timing_base + VIDCON1); 4061c248b7dSInki Dae 4071c248b7dSInki Dae /* setup vertical timing values. */ 408a968e727SSean Paul vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start; 4098b4cad23SAndrzej Hajda vbpd = mode->crtc_vtotal - mode->crtc_vsync_end; 4108b4cad23SAndrzej Hajda vfpd = mode->crtc_vsync_start - mode->crtc_vdisplay; 411a968e727SSean Paul 412a968e727SSean Paul val = VIDTCON0_VBPD(vbpd - 1) | 413a968e727SSean Paul VIDTCON0_VFPD(vfpd - 1) | 414a968e727SSean Paul VIDTCON0_VSPW(vsync_len - 1); 415e2e13389SLeela Krishna Amudala writel(val, ctx->regs + driver_data->timing_base + VIDTCON0); 4161c248b7dSInki Dae 4171c248b7dSInki Dae /* setup horizontal timing values. */ 418a968e727SSean Paul hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start; 4198b4cad23SAndrzej Hajda hbpd = mode->crtc_htotal - mode->crtc_hsync_end; 4208b4cad23SAndrzej Hajda hfpd = mode->crtc_hsync_start - mode->crtc_hdisplay; 421a968e727SSean Paul 422a968e727SSean Paul val = VIDTCON1_HBPD(hbpd - 1) | 423a968e727SSean Paul VIDTCON1_HFPD(hfpd - 1) | 424a968e727SSean Paul VIDTCON1_HSPW(hsync_len - 1); 425e2e13389SLeela Krishna Amudala writel(val, ctx->regs + driver_data->timing_base + VIDTCON1); 4263854fab2SYoungJun Cho } 4273854fab2SYoungJun Cho 4283854fab2SYoungJun Cho if (driver_data->has_vidoutcon) 4293854fab2SYoungJun Cho writel(ctx->vidout_con, timing_base + VIDOUT_CON); 4303854fab2SYoungJun Cho 4313854fab2SYoungJun Cho /* set bypass selection */ 4323854fab2SYoungJun Cho if (ctx->sysreg && regmap_update_bits(ctx->sysreg, 4333854fab2SYoungJun Cho driver_data->lcdblk_offset, 4343854fab2SYoungJun Cho 0x1 << driver_data->lcdblk_bypass_shift, 4353854fab2SYoungJun Cho 0x1 << driver_data->lcdblk_bypass_shift)) { 4363854fab2SYoungJun Cho DRM_ERROR("Failed to update sysreg for bypass setting.\n"); 4373854fab2SYoungJun Cho return; 4383854fab2SYoungJun Cho } 4391c248b7dSInki Dae 4401c248b7dSInki Dae /* setup horizontal and vertical display size. */ 441a968e727SSean Paul val = VIDTCON2_LINEVAL(mode->vdisplay - 1) | 442a968e727SSean Paul VIDTCON2_HOZVAL(mode->hdisplay - 1) | 443a968e727SSean Paul VIDTCON2_LINEVAL_E(mode->vdisplay - 1) | 444a968e727SSean Paul VIDTCON2_HOZVAL_E(mode->hdisplay - 1); 445e2e13389SLeela Krishna Amudala writel(val, ctx->regs + driver_data->timing_base + VIDTCON2); 4461c248b7dSInki Dae 4471c248b7dSInki Dae /* 4481c248b7dSInki Dae * fields of register with prefix '_F' would be updated 4491c248b7dSInki Dae * at vsync(same as dma start) 4501c248b7dSInki Dae */ 4513854fab2SYoungJun Cho val = ctx->vidcon0; 4523854fab2SYoungJun Cho val |= VIDCON0_ENVID | VIDCON0_ENVID_F; 4531d531062SAndrzej Hajda 4541d531062SAndrzej Hajda if (ctx->driver_data->has_clksel) 4551d531062SAndrzej Hajda val |= VIDCON0_CLKSEL_LCD; 4561d531062SAndrzej Hajda 4571d531062SAndrzej Hajda clkdiv = fimd_calc_clkdiv(ctx, mode); 4581d531062SAndrzej Hajda if (clkdiv > 1) 4591d531062SAndrzej Hajda val |= VIDCON0_CLKVAL_F(clkdiv - 1) | VIDCON0_CLKDIR; 4601d531062SAndrzej Hajda 4611c248b7dSInki Dae writel(val, ctx->regs + VIDCON0); 4621c248b7dSInki Dae } 4631c248b7dSInki Dae 464bb7704d6SSean Paul static int fimd_enable_vblank(struct exynos_drm_manager *mgr) 4651c248b7dSInki Dae { 466400c8ac8SAndrzej Hajda struct fimd_context *ctx = mgr_to_fimd(mgr); 4671c248b7dSInki Dae u32 val; 4681c248b7dSInki Dae 469cb91f6a0SJoonyoung Shim if (ctx->suspended) 470cb91f6a0SJoonyoung Shim return -EPERM; 471cb91f6a0SJoonyoung Shim 4721c248b7dSInki Dae if (!test_and_set_bit(0, &ctx->irq_flags)) { 4731c248b7dSInki Dae val = readl(ctx->regs + VIDINTCON0); 4741c248b7dSInki Dae 4751c248b7dSInki Dae val |= VIDINTCON0_INT_ENABLE; 4761c905d95SYoungJun Cho 4771c905d95SYoungJun Cho if (ctx->i80_if) { 4781c905d95SYoungJun Cho val |= VIDINTCON0_INT_I80IFDONE; 4791c905d95SYoungJun Cho val |= VIDINTCON0_INT_SYSMAINCON; 4801c905d95SYoungJun Cho val &= ~VIDINTCON0_INT_SYSSUBCON; 4811c905d95SYoungJun Cho } else { 4821c248b7dSInki Dae val |= VIDINTCON0_INT_FRAME; 4831c248b7dSInki Dae 4841c248b7dSInki Dae val &= ~VIDINTCON0_FRAMESEL0_MASK; 4851c248b7dSInki Dae val |= VIDINTCON0_FRAMESEL0_VSYNC; 4861c248b7dSInki Dae val &= ~VIDINTCON0_FRAMESEL1_MASK; 4871c248b7dSInki Dae val |= VIDINTCON0_FRAMESEL1_NONE; 4881c905d95SYoungJun Cho } 4891c248b7dSInki Dae 4901c248b7dSInki Dae writel(val, ctx->regs + VIDINTCON0); 4911c248b7dSInki Dae } 4921c248b7dSInki Dae 4931c248b7dSInki Dae return 0; 4941c248b7dSInki Dae } 4951c248b7dSInki Dae 496bb7704d6SSean Paul static void fimd_disable_vblank(struct exynos_drm_manager *mgr) 4971c248b7dSInki Dae { 498400c8ac8SAndrzej Hajda struct fimd_context *ctx = mgr_to_fimd(mgr); 4991c248b7dSInki Dae u32 val; 5001c248b7dSInki Dae 501cb91f6a0SJoonyoung Shim if (ctx->suspended) 502cb91f6a0SJoonyoung Shim return; 503cb91f6a0SJoonyoung Shim 5041c248b7dSInki Dae if (test_and_clear_bit(0, &ctx->irq_flags)) { 5051c248b7dSInki Dae val = readl(ctx->regs + VIDINTCON0); 5061c248b7dSInki Dae 5071c248b7dSInki Dae val &= ~VIDINTCON0_INT_ENABLE; 5081c248b7dSInki Dae 5091c905d95SYoungJun Cho if (ctx->i80_if) { 5101c905d95SYoungJun Cho val &= ~VIDINTCON0_INT_I80IFDONE; 5111c905d95SYoungJun Cho val &= ~VIDINTCON0_INT_SYSMAINCON; 5121c905d95SYoungJun Cho val &= ~VIDINTCON0_INT_SYSSUBCON; 5131c905d95SYoungJun Cho } else 5141c905d95SYoungJun Cho val &= ~VIDINTCON0_INT_FRAME; 5151c905d95SYoungJun Cho 5161c248b7dSInki Dae writel(val, ctx->regs + VIDINTCON0); 5171c248b7dSInki Dae } 5181c248b7dSInki Dae } 5191c248b7dSInki Dae 520bb7704d6SSean Paul static void fimd_win_mode_set(struct exynos_drm_manager *mgr, 5211c248b7dSInki Dae struct exynos_drm_overlay *overlay) 5221c248b7dSInki Dae { 523400c8ac8SAndrzej Hajda struct fimd_context *ctx = mgr_to_fimd(mgr); 5241c248b7dSInki Dae struct fimd_win_data *win_data; 525864ee9e6SJoonyoung Shim int win; 52619c8b834SInki Dae unsigned long offset; 5271c248b7dSInki Dae 5281c248b7dSInki Dae if (!overlay) { 529bb7704d6SSean Paul DRM_ERROR("overlay is NULL\n"); 5301c248b7dSInki Dae return; 5311c248b7dSInki Dae } 5321c248b7dSInki Dae 533864ee9e6SJoonyoung Shim win = overlay->zpos; 534864ee9e6SJoonyoung Shim if (win == DEFAULT_ZPOS) 535864ee9e6SJoonyoung Shim win = ctx->default_win; 536864ee9e6SJoonyoung Shim 53737b006e8SKrzysztof Kozlowski if (win < 0 || win >= WINDOWS_NR) 538864ee9e6SJoonyoung Shim return; 539864ee9e6SJoonyoung Shim 54019c8b834SInki Dae offset = overlay->fb_x * (overlay->bpp >> 3); 54119c8b834SInki Dae offset += overlay->fb_y * overlay->pitch; 54219c8b834SInki Dae 54319c8b834SInki Dae DRM_DEBUG_KMS("offset = 0x%lx, pitch = %x\n", offset, overlay->pitch); 54419c8b834SInki Dae 545864ee9e6SJoonyoung Shim win_data = &ctx->win_data[win]; 5461c248b7dSInki Dae 54719c8b834SInki Dae win_data->offset_x = overlay->crtc_x; 54819c8b834SInki Dae win_data->offset_y = overlay->crtc_y; 54919c8b834SInki Dae win_data->ovl_width = overlay->crtc_width; 55019c8b834SInki Dae win_data->ovl_height = overlay->crtc_height; 55119c8b834SInki Dae win_data->fb_width = overlay->fb_width; 55219c8b834SInki Dae win_data->fb_height = overlay->fb_height; 553229d3534SSeung-Woo Kim win_data->dma_addr = overlay->dma_addr[0] + offset; 5541c248b7dSInki Dae win_data->bpp = overlay->bpp; 555a4f38a80SInki Dae win_data->pixel_format = overlay->pixel_format; 55619c8b834SInki Dae win_data->buf_offsize = (overlay->fb_width - overlay->crtc_width) * 55719c8b834SInki Dae (overlay->bpp >> 3); 55819c8b834SInki Dae win_data->line_size = overlay->crtc_width * (overlay->bpp >> 3); 55919c8b834SInki Dae 56019c8b834SInki Dae DRM_DEBUG_KMS("offset_x = %d, offset_y = %d\n", 56119c8b834SInki Dae win_data->offset_x, win_data->offset_y); 56219c8b834SInki Dae DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n", 56319c8b834SInki Dae win_data->ovl_width, win_data->ovl_height); 564ddd8e959SYoungJun Cho DRM_DEBUG_KMS("paddr = 0x%lx\n", (unsigned long)win_data->dma_addr); 56519c8b834SInki Dae DRM_DEBUG_KMS("fb_width = %d, crtc_width = %d\n", 56619c8b834SInki Dae overlay->fb_width, overlay->crtc_width); 5671c248b7dSInki Dae } 5681c248b7dSInki Dae 569bb7704d6SSean Paul static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win) 5701c248b7dSInki Dae { 5711c248b7dSInki Dae struct fimd_win_data *win_data = &ctx->win_data[win]; 5721c248b7dSInki Dae unsigned long val; 5731c248b7dSInki Dae 5741c248b7dSInki Dae val = WINCONx_ENWIN; 5751c248b7dSInki Dae 5765cc4621aSInki Dae /* 5775cc4621aSInki Dae * In case of s3c64xx, window 0 doesn't support alpha channel. 5785cc4621aSInki Dae * So the request format is ARGB8888 then change it to XRGB8888. 5795cc4621aSInki Dae */ 5805cc4621aSInki Dae if (ctx->driver_data->has_limited_fmt && !win) { 5815cc4621aSInki Dae if (win_data->pixel_format == DRM_FORMAT_ARGB8888) 5825cc4621aSInki Dae win_data->pixel_format = DRM_FORMAT_XRGB8888; 5835cc4621aSInki Dae } 5845cc4621aSInki Dae 585a4f38a80SInki Dae switch (win_data->pixel_format) { 586a4f38a80SInki Dae case DRM_FORMAT_C8: 5871c248b7dSInki Dae val |= WINCON0_BPPMODE_8BPP_PALETTE; 5881c248b7dSInki Dae val |= WINCONx_BURSTLEN_8WORD; 5891c248b7dSInki Dae val |= WINCONx_BYTSWP; 5901c248b7dSInki Dae break; 591a4f38a80SInki Dae case DRM_FORMAT_XRGB1555: 592a4f38a80SInki Dae val |= WINCON0_BPPMODE_16BPP_1555; 593a4f38a80SInki Dae val |= WINCONx_HAWSWP; 594a4f38a80SInki Dae val |= WINCONx_BURSTLEN_16WORD; 595a4f38a80SInki Dae break; 596a4f38a80SInki Dae case DRM_FORMAT_RGB565: 5971c248b7dSInki Dae val |= WINCON0_BPPMODE_16BPP_565; 5981c248b7dSInki Dae val |= WINCONx_HAWSWP; 5991c248b7dSInki Dae val |= WINCONx_BURSTLEN_16WORD; 6001c248b7dSInki Dae break; 601a4f38a80SInki Dae case DRM_FORMAT_XRGB8888: 6021c248b7dSInki Dae val |= WINCON0_BPPMODE_24BPP_888; 6031c248b7dSInki Dae val |= WINCONx_WSWP; 6041c248b7dSInki Dae val |= WINCONx_BURSTLEN_16WORD; 6051c248b7dSInki Dae break; 606a4f38a80SInki Dae case DRM_FORMAT_ARGB8888: 607a4f38a80SInki Dae val |= WINCON1_BPPMODE_25BPP_A1888 6081c248b7dSInki Dae | WINCON1_BLD_PIX | WINCON1_ALPHA_SEL; 6091c248b7dSInki Dae val |= WINCONx_WSWP; 6101c248b7dSInki Dae val |= WINCONx_BURSTLEN_16WORD; 6111c248b7dSInki Dae break; 6121c248b7dSInki Dae default: 6131c248b7dSInki Dae DRM_DEBUG_KMS("invalid pixel size so using unpacked 24bpp.\n"); 6141c248b7dSInki Dae 6151c248b7dSInki Dae val |= WINCON0_BPPMODE_24BPP_888; 6161c248b7dSInki Dae val |= WINCONx_WSWP; 6171c248b7dSInki Dae val |= WINCONx_BURSTLEN_16WORD; 6181c248b7dSInki Dae break; 6191c248b7dSInki Dae } 6201c248b7dSInki Dae 6211c248b7dSInki Dae DRM_DEBUG_KMS("bpp = %d\n", win_data->bpp); 6221c248b7dSInki Dae 62366367461SRahul Sharma /* 62466367461SRahul Sharma * In case of exynos, setting dma-burst to 16Word causes permanent 62566367461SRahul Sharma * tearing for very small buffers, e.g. cursor buffer. Burst Mode 62666367461SRahul Sharma * switching which is based on overlay size is not recommended as 62766367461SRahul Sharma * overlay size varies alot towards the end of the screen and rapid 62866367461SRahul Sharma * movement causes unstable DMA which results into iommu crash/tear. 62966367461SRahul Sharma */ 63066367461SRahul Sharma 63166367461SRahul Sharma if (win_data->fb_width < MIN_FB_WIDTH_FOR_16WORD_BURST) { 63266367461SRahul Sharma val &= ~WINCONx_BURSTLEN_MASK; 63366367461SRahul Sharma val |= WINCONx_BURSTLEN_4WORD; 63466367461SRahul Sharma } 63566367461SRahul Sharma 6361c248b7dSInki Dae writel(val, ctx->regs + WINCON(win)); 6371c248b7dSInki Dae } 6381c248b7dSInki Dae 639bb7704d6SSean Paul static void fimd_win_set_colkey(struct fimd_context *ctx, unsigned int win) 6401c248b7dSInki Dae { 6411c248b7dSInki Dae unsigned int keycon0 = 0, keycon1 = 0; 6421c248b7dSInki Dae 6431c248b7dSInki Dae keycon0 = ~(WxKEYCON0_KEYBL_EN | WxKEYCON0_KEYEN_F | 6441c248b7dSInki Dae WxKEYCON0_DIRCON) | WxKEYCON0_COMPKEY(0); 6451c248b7dSInki Dae 6461c248b7dSInki Dae keycon1 = WxKEYCON1_COLVAL(0xffffffff); 6471c248b7dSInki Dae 6481c248b7dSInki Dae writel(keycon0, ctx->regs + WKEYCON0_BASE(win)); 6491c248b7dSInki Dae writel(keycon1, ctx->regs + WKEYCON1_BASE(win)); 6501c248b7dSInki Dae } 6511c248b7dSInki Dae 652de7af100STomasz Figa /** 653de7af100STomasz Figa * shadow_protect_win() - disable updating values from shadow registers at vsync 654de7af100STomasz Figa * 655de7af100STomasz Figa * @win: window to protect registers for 656de7af100STomasz Figa * @protect: 1 to protect (disable updates) 657de7af100STomasz Figa */ 658de7af100STomasz Figa static void fimd_shadow_protect_win(struct fimd_context *ctx, 659de7af100STomasz Figa int win, bool protect) 660de7af100STomasz Figa { 661de7af100STomasz Figa u32 reg, bits, val; 662de7af100STomasz Figa 663de7af100STomasz Figa if (ctx->driver_data->has_shadowcon) { 664de7af100STomasz Figa reg = SHADOWCON; 665de7af100STomasz Figa bits = SHADOWCON_WINx_PROTECT(win); 666de7af100STomasz Figa } else { 667de7af100STomasz Figa reg = PRTCON; 668de7af100STomasz Figa bits = PRTCON_PROTECT; 669de7af100STomasz Figa } 670de7af100STomasz Figa 671de7af100STomasz Figa val = readl(ctx->regs + reg); 672de7af100STomasz Figa if (protect) 673de7af100STomasz Figa val |= bits; 674de7af100STomasz Figa else 675de7af100STomasz Figa val &= ~bits; 676de7af100STomasz Figa writel(val, ctx->regs + reg); 677de7af100STomasz Figa } 678de7af100STomasz Figa 679bb7704d6SSean Paul static void fimd_win_commit(struct exynos_drm_manager *mgr, int zpos) 6801c248b7dSInki Dae { 681400c8ac8SAndrzej Hajda struct fimd_context *ctx = mgr_to_fimd(mgr); 6821c248b7dSInki Dae struct fimd_win_data *win_data; 683864ee9e6SJoonyoung Shim int win = zpos; 6841c248b7dSInki Dae unsigned long val, alpha, size; 685f56aad3aSJoonyoung Shim unsigned int last_x; 686f56aad3aSJoonyoung Shim unsigned int last_y; 6871c248b7dSInki Dae 688e30d4bcfSInki Dae if (ctx->suspended) 689e30d4bcfSInki Dae return; 690e30d4bcfSInki Dae 691864ee9e6SJoonyoung Shim if (win == DEFAULT_ZPOS) 692864ee9e6SJoonyoung Shim win = ctx->default_win; 693864ee9e6SJoonyoung Shim 69437b006e8SKrzysztof Kozlowski if (win < 0 || win >= WINDOWS_NR) 6951c248b7dSInki Dae return; 6961c248b7dSInki Dae 6971c248b7dSInki Dae win_data = &ctx->win_data[win]; 6981c248b7dSInki Dae 699a43b933bSSean Paul /* If suspended, enable this on resume */ 700a43b933bSSean Paul if (ctx->suspended) { 701a43b933bSSean Paul win_data->resume = true; 702a43b933bSSean Paul return; 703a43b933bSSean Paul } 704a43b933bSSean Paul 7051c248b7dSInki Dae /* 706de7af100STomasz Figa * SHADOWCON/PRTCON register is used for enabling timing. 7071c248b7dSInki Dae * 7081c248b7dSInki Dae * for example, once only width value of a register is set, 7091c248b7dSInki Dae * if the dma is started then fimd hardware could malfunction so 7101c248b7dSInki Dae * with protect window setting, the register fields with prefix '_F' 7111c248b7dSInki Dae * wouldn't be updated at vsync also but updated once unprotect window 7121c248b7dSInki Dae * is set. 7131c248b7dSInki Dae */ 7141c248b7dSInki Dae 7151c248b7dSInki Dae /* protect windows */ 716de7af100STomasz Figa fimd_shadow_protect_win(ctx, win, true); 7171c248b7dSInki Dae 7181c248b7dSInki Dae /* buffer start address */ 7192c871127SInki Dae val = (unsigned long)win_data->dma_addr; 7201c248b7dSInki Dae writel(val, ctx->regs + VIDWx_BUF_START(win, 0)); 7211c248b7dSInki Dae 7221c248b7dSInki Dae /* buffer end address */ 72319c8b834SInki Dae size = win_data->fb_width * win_data->ovl_height * (win_data->bpp >> 3); 7242c871127SInki Dae val = (unsigned long)(win_data->dma_addr + size); 7251c248b7dSInki Dae writel(val, ctx->regs + VIDWx_BUF_END(win, 0)); 7261c248b7dSInki Dae 7271c248b7dSInki Dae DRM_DEBUG_KMS("start addr = 0x%lx, end addr = 0x%lx, size = 0x%lx\n", 7282c871127SInki Dae (unsigned long)win_data->dma_addr, val, size); 72919c8b834SInki Dae DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n", 73019c8b834SInki Dae win_data->ovl_width, win_data->ovl_height); 7311c248b7dSInki Dae 7321c248b7dSInki Dae /* buffer size */ 7331c248b7dSInki Dae val = VIDW_BUF_SIZE_OFFSET(win_data->buf_offsize) | 734ca555e5aSJoonyoung Shim VIDW_BUF_SIZE_PAGEWIDTH(win_data->line_size) | 735ca555e5aSJoonyoung Shim VIDW_BUF_SIZE_OFFSET_E(win_data->buf_offsize) | 736ca555e5aSJoonyoung Shim VIDW_BUF_SIZE_PAGEWIDTH_E(win_data->line_size); 7371c248b7dSInki Dae writel(val, ctx->regs + VIDWx_BUF_SIZE(win, 0)); 7381c248b7dSInki Dae 7391c248b7dSInki Dae /* OSD position */ 7401c248b7dSInki Dae val = VIDOSDxA_TOPLEFT_X(win_data->offset_x) | 741ca555e5aSJoonyoung Shim VIDOSDxA_TOPLEFT_Y(win_data->offset_y) | 742ca555e5aSJoonyoung Shim VIDOSDxA_TOPLEFT_X_E(win_data->offset_x) | 743ca555e5aSJoonyoung Shim VIDOSDxA_TOPLEFT_Y_E(win_data->offset_y); 7441c248b7dSInki Dae writel(val, ctx->regs + VIDOSD_A(win)); 7451c248b7dSInki Dae 746f56aad3aSJoonyoung Shim last_x = win_data->offset_x + win_data->ovl_width; 747f56aad3aSJoonyoung Shim if (last_x) 748f56aad3aSJoonyoung Shim last_x--; 749f56aad3aSJoonyoung Shim last_y = win_data->offset_y + win_data->ovl_height; 750f56aad3aSJoonyoung Shim if (last_y) 751f56aad3aSJoonyoung Shim last_y--; 752f56aad3aSJoonyoung Shim 753ca555e5aSJoonyoung Shim val = VIDOSDxB_BOTRIGHT_X(last_x) | VIDOSDxB_BOTRIGHT_Y(last_y) | 754ca555e5aSJoonyoung Shim VIDOSDxB_BOTRIGHT_X_E(last_x) | VIDOSDxB_BOTRIGHT_Y_E(last_y); 755ca555e5aSJoonyoung Shim 7561c248b7dSInki Dae writel(val, ctx->regs + VIDOSD_B(win)); 7571c248b7dSInki Dae 75819c8b834SInki Dae DRM_DEBUG_KMS("osd pos: tx = %d, ty = %d, bx = %d, by = %d\n", 759f56aad3aSJoonyoung Shim win_data->offset_x, win_data->offset_y, last_x, last_y); 7601c248b7dSInki Dae 7611c248b7dSInki Dae /* hardware window 0 doesn't support alpha channel. */ 7621c248b7dSInki Dae if (win != 0) { 7631c248b7dSInki Dae /* OSD alpha */ 7641c248b7dSInki Dae alpha = VIDISD14C_ALPHA1_R(0xf) | 7651c248b7dSInki Dae VIDISD14C_ALPHA1_G(0xf) | 7661c248b7dSInki Dae VIDISD14C_ALPHA1_B(0xf); 7671c248b7dSInki Dae 7681c248b7dSInki Dae writel(alpha, ctx->regs + VIDOSD_C(win)); 7691c248b7dSInki Dae } 7701c248b7dSInki Dae 7711c248b7dSInki Dae /* OSD size */ 7721c248b7dSInki Dae if (win != 3 && win != 4) { 7731c248b7dSInki Dae u32 offset = VIDOSD_D(win); 7741c248b7dSInki Dae if (win == 0) 7750f10cf14SLeela Krishna Amudala offset = VIDOSD_C(win); 77619c8b834SInki Dae val = win_data->ovl_width * win_data->ovl_height; 7771c248b7dSInki Dae writel(val, ctx->regs + offset); 7781c248b7dSInki Dae 7791c248b7dSInki Dae DRM_DEBUG_KMS("osd size = 0x%x\n", (unsigned int)val); 7801c248b7dSInki Dae } 7811c248b7dSInki Dae 782bb7704d6SSean Paul fimd_win_set_pixfmt(ctx, win); 7831c248b7dSInki Dae 7841c248b7dSInki Dae /* hardware window 0 doesn't support color key. */ 7851c248b7dSInki Dae if (win != 0) 786bb7704d6SSean Paul fimd_win_set_colkey(ctx, win); 7871c248b7dSInki Dae 788f181a543SYoungJun Cho fimd_enable_video_output(ctx, win, true); 789ec05da95SInki Dae 790999d8b31SYoungJun Cho if (ctx->driver_data->has_shadowcon) 791999d8b31SYoungJun Cho fimd_enable_shadow_channel_path(ctx, win, true); 792ec05da95SInki Dae 79374944a58SYoungJun Cho /* Enable DMA channel and unprotect windows */ 79474944a58SYoungJun Cho fimd_shadow_protect_win(ctx, win, false); 79574944a58SYoungJun Cho 796ec05da95SInki Dae win_data->enabled = true; 7973854fab2SYoungJun Cho 7983854fab2SYoungJun Cho if (ctx->i80_if) 7993854fab2SYoungJun Cho atomic_set(&ctx->win_updated, 1); 8001c248b7dSInki Dae } 8011c248b7dSInki Dae 802bb7704d6SSean Paul static void fimd_win_disable(struct exynos_drm_manager *mgr, int zpos) 8031c248b7dSInki Dae { 804400c8ac8SAndrzej Hajda struct fimd_context *ctx = mgr_to_fimd(mgr); 805ec05da95SInki Dae struct fimd_win_data *win_data; 806864ee9e6SJoonyoung Shim int win = zpos; 8071c248b7dSInki Dae 808864ee9e6SJoonyoung Shim if (win == DEFAULT_ZPOS) 809864ee9e6SJoonyoung Shim win = ctx->default_win; 810864ee9e6SJoonyoung Shim 81137b006e8SKrzysztof Kozlowski if (win < 0 || win >= WINDOWS_NR) 8121c248b7dSInki Dae return; 8131c248b7dSInki Dae 814ec05da95SInki Dae win_data = &ctx->win_data[win]; 815ec05da95SInki Dae 816db7e55aeSPrathyush K if (ctx->suspended) { 817db7e55aeSPrathyush K /* do not resume this window*/ 818db7e55aeSPrathyush K win_data->resume = false; 819db7e55aeSPrathyush K return; 820db7e55aeSPrathyush K } 821db7e55aeSPrathyush K 8221c248b7dSInki Dae /* protect windows */ 823de7af100STomasz Figa fimd_shadow_protect_win(ctx, win, true); 8241c248b7dSInki Dae 825f181a543SYoungJun Cho fimd_enable_video_output(ctx, win, false); 8261c248b7dSInki Dae 827999d8b31SYoungJun Cho if (ctx->driver_data->has_shadowcon) 828999d8b31SYoungJun Cho fimd_enable_shadow_channel_path(ctx, win, false); 829de7af100STomasz Figa 830999d8b31SYoungJun Cho /* unprotect windows */ 831de7af100STomasz Figa fimd_shadow_protect_win(ctx, win, false); 832ec05da95SInki Dae 833ec05da95SInki Dae win_data->enabled = false; 8341c248b7dSInki Dae } 8351c248b7dSInki Dae 836a43b933bSSean Paul static void fimd_window_suspend(struct exynos_drm_manager *mgr) 837a43b933bSSean Paul { 838400c8ac8SAndrzej Hajda struct fimd_context *ctx = mgr_to_fimd(mgr); 839a43b933bSSean Paul struct fimd_win_data *win_data; 840a43b933bSSean Paul int i; 841a43b933bSSean Paul 842a43b933bSSean Paul for (i = 0; i < WINDOWS_NR; i++) { 843a43b933bSSean Paul win_data = &ctx->win_data[i]; 844a43b933bSSean Paul win_data->resume = win_data->enabled; 845a43b933bSSean Paul if (win_data->enabled) 846a43b933bSSean Paul fimd_win_disable(mgr, i); 847a43b933bSSean Paul } 848a43b933bSSean Paul } 849a43b933bSSean Paul 850a43b933bSSean Paul static void fimd_window_resume(struct exynos_drm_manager *mgr) 851a43b933bSSean Paul { 852400c8ac8SAndrzej Hajda struct fimd_context *ctx = mgr_to_fimd(mgr); 853a43b933bSSean Paul struct fimd_win_data *win_data; 854a43b933bSSean Paul int i; 855a43b933bSSean Paul 856a43b933bSSean Paul for (i = 0; i < WINDOWS_NR; i++) { 857a43b933bSSean Paul win_data = &ctx->win_data[i]; 858a43b933bSSean Paul win_data->enabled = win_data->resume; 859a43b933bSSean Paul win_data->resume = false; 860a43b933bSSean Paul } 861a43b933bSSean Paul } 862a43b933bSSean Paul 863a43b933bSSean Paul static void fimd_apply(struct exynos_drm_manager *mgr) 864a43b933bSSean Paul { 865400c8ac8SAndrzej Hajda struct fimd_context *ctx = mgr_to_fimd(mgr); 866a43b933bSSean Paul struct fimd_win_data *win_data; 867a43b933bSSean Paul int i; 868a43b933bSSean Paul 869a43b933bSSean Paul for (i = 0; i < WINDOWS_NR; i++) { 870a43b933bSSean Paul win_data = &ctx->win_data[i]; 871a43b933bSSean Paul if (win_data->enabled) 872a43b933bSSean Paul fimd_win_commit(mgr, i); 873d9b68d89SAndrzej Hajda else 874d9b68d89SAndrzej Hajda fimd_win_disable(mgr, i); 875a43b933bSSean Paul } 876a43b933bSSean Paul 877a43b933bSSean Paul fimd_commit(mgr); 878a43b933bSSean Paul } 879a43b933bSSean Paul 880a43b933bSSean Paul static int fimd_poweron(struct exynos_drm_manager *mgr) 881a43b933bSSean Paul { 882400c8ac8SAndrzej Hajda struct fimd_context *ctx = mgr_to_fimd(mgr); 883a43b933bSSean Paul int ret; 884a43b933bSSean Paul 885a43b933bSSean Paul if (!ctx->suspended) 886a43b933bSSean Paul return 0; 887a43b933bSSean Paul 888a43b933bSSean Paul ctx->suspended = false; 889a43b933bSSean Paul 890af65c804SSean Paul pm_runtime_get_sync(ctx->dev); 891af65c804SSean Paul 892a43b933bSSean Paul ret = clk_prepare_enable(ctx->bus_clk); 893a43b933bSSean Paul if (ret < 0) { 894a43b933bSSean Paul DRM_ERROR("Failed to prepare_enable the bus clk [%d]\n", ret); 895a43b933bSSean Paul goto bus_clk_err; 896a43b933bSSean Paul } 897a43b933bSSean Paul 898a43b933bSSean Paul ret = clk_prepare_enable(ctx->lcd_clk); 899a43b933bSSean Paul if (ret < 0) { 900a43b933bSSean Paul DRM_ERROR("Failed to prepare_enable the lcd clk [%d]\n", ret); 901a43b933bSSean Paul goto lcd_clk_err; 902a43b933bSSean Paul } 903a43b933bSSean Paul 904a43b933bSSean Paul /* if vblank was enabled status, enable it again. */ 905a43b933bSSean Paul if (test_and_clear_bit(0, &ctx->irq_flags)) { 906a43b933bSSean Paul ret = fimd_enable_vblank(mgr); 907a43b933bSSean Paul if (ret) { 908a43b933bSSean Paul DRM_ERROR("Failed to re-enable vblank [%d]\n", ret); 909a43b933bSSean Paul goto enable_vblank_err; 910a43b933bSSean Paul } 911a43b933bSSean Paul } 912a43b933bSSean Paul 913a43b933bSSean Paul fimd_window_resume(mgr); 914a43b933bSSean Paul 915a43b933bSSean Paul fimd_apply(mgr); 916a43b933bSSean Paul 917a43b933bSSean Paul return 0; 918a43b933bSSean Paul 919a43b933bSSean Paul enable_vblank_err: 920a43b933bSSean Paul clk_disable_unprepare(ctx->lcd_clk); 921a43b933bSSean Paul lcd_clk_err: 922a43b933bSSean Paul clk_disable_unprepare(ctx->bus_clk); 923a43b933bSSean Paul bus_clk_err: 924a43b933bSSean Paul ctx->suspended = true; 925a43b933bSSean Paul return ret; 926a43b933bSSean Paul } 927a43b933bSSean Paul 928a43b933bSSean Paul static int fimd_poweroff(struct exynos_drm_manager *mgr) 929a43b933bSSean Paul { 930400c8ac8SAndrzej Hajda struct fimd_context *ctx = mgr_to_fimd(mgr); 931a43b933bSSean Paul 932a43b933bSSean Paul if (ctx->suspended) 933a43b933bSSean Paul return 0; 934a43b933bSSean Paul 935a43b933bSSean Paul /* 936a43b933bSSean Paul * We need to make sure that all windows are disabled before we 937a43b933bSSean Paul * suspend that connector. Otherwise we might try to scan from 938a43b933bSSean Paul * a destroyed buffer later. 939a43b933bSSean Paul */ 940a43b933bSSean Paul fimd_window_suspend(mgr); 941a43b933bSSean Paul 942a43b933bSSean Paul clk_disable_unprepare(ctx->lcd_clk); 943a43b933bSSean Paul clk_disable_unprepare(ctx->bus_clk); 944a43b933bSSean Paul 945af65c804SSean Paul pm_runtime_put_sync(ctx->dev); 946af65c804SSean Paul 947a43b933bSSean Paul ctx->suspended = true; 948a43b933bSSean Paul return 0; 949a43b933bSSean Paul } 950a43b933bSSean Paul 951080be03dSSean Paul static void fimd_dpms(struct exynos_drm_manager *mgr, int mode) 952080be03dSSean Paul { 953af65c804SSean Paul DRM_DEBUG_KMS("%s, %d\n", __FILE__, mode); 954080be03dSSean Paul 955080be03dSSean Paul switch (mode) { 956080be03dSSean Paul case DRM_MODE_DPMS_ON: 957af65c804SSean Paul fimd_poweron(mgr); 958080be03dSSean Paul break; 959080be03dSSean Paul case DRM_MODE_DPMS_STANDBY: 960080be03dSSean Paul case DRM_MODE_DPMS_SUSPEND: 961080be03dSSean Paul case DRM_MODE_DPMS_OFF: 962af65c804SSean Paul fimd_poweroff(mgr); 963080be03dSSean Paul break; 964080be03dSSean Paul default: 965080be03dSSean Paul DRM_DEBUG_KMS("unspecified mode %d\n", mode); 966080be03dSSean Paul break; 967080be03dSSean Paul } 968080be03dSSean Paul } 969080be03dSSean Paul 9703854fab2SYoungJun Cho static void fimd_trigger(struct device *dev) 9713854fab2SYoungJun Cho { 972e152dbd7SAndrzej Hajda struct fimd_context *ctx = dev_get_drvdata(dev); 9733854fab2SYoungJun Cho struct fimd_driver_data *driver_data = ctx->driver_data; 9743854fab2SYoungJun Cho void *timing_base = ctx->regs + driver_data->timing_base; 9753854fab2SYoungJun Cho u32 reg; 9763854fab2SYoungJun Cho 9779b67eb73SJoonyoung Shim /* 9781c905d95SYoungJun Cho * Skips triggering if in triggering state, because multiple triggering 9799b67eb73SJoonyoung Shim * requests can cause panel reset. 9809b67eb73SJoonyoung Shim */ 9819b67eb73SJoonyoung Shim if (atomic_read(&ctx->triggering)) 9829b67eb73SJoonyoung Shim return; 9839b67eb73SJoonyoung Shim 9841c905d95SYoungJun Cho /* Enters triggering mode */ 9853854fab2SYoungJun Cho atomic_set(&ctx->triggering, 1); 9863854fab2SYoungJun Cho 9873854fab2SYoungJun Cho reg = readl(timing_base + TRIGCON); 9883854fab2SYoungJun Cho reg |= (TRGMODE_I80_RGB_ENABLE_I80 | SWTRGCMD_I80_RGB_ENABLE); 9893854fab2SYoungJun Cho writel(reg, timing_base + TRIGCON); 99087ab85b3SYoungJun Cho 99187ab85b3SYoungJun Cho /* 99287ab85b3SYoungJun Cho * Exits triggering mode if vblank is not enabled yet, because when the 99387ab85b3SYoungJun Cho * VIDINTCON0 register is not set, it can not exit from triggering mode. 99487ab85b3SYoungJun Cho */ 99587ab85b3SYoungJun Cho if (!test_bit(0, &ctx->irq_flags)) 99687ab85b3SYoungJun Cho atomic_set(&ctx->triggering, 0); 9973854fab2SYoungJun Cho } 9983854fab2SYoungJun Cho 9993854fab2SYoungJun Cho static void fimd_te_handler(struct exynos_drm_manager *mgr) 10003854fab2SYoungJun Cho { 1001400c8ac8SAndrzej Hajda struct fimd_context *ctx = mgr_to_fimd(mgr); 10023854fab2SYoungJun Cho 10033854fab2SYoungJun Cho /* Checks the crtc is detached already from encoder */ 10043854fab2SYoungJun Cho if (ctx->pipe < 0 || !ctx->drm_dev) 10053854fab2SYoungJun Cho return; 10063854fab2SYoungJun Cho 10073854fab2SYoungJun Cho /* 10083854fab2SYoungJun Cho * If there is a page flip request, triggers and handles the page flip 10093854fab2SYoungJun Cho * event so that current fb can be updated into panel GRAM. 10103854fab2SYoungJun Cho */ 10113854fab2SYoungJun Cho if (atomic_add_unless(&ctx->win_updated, -1, 0)) 10123854fab2SYoungJun Cho fimd_trigger(ctx->dev); 10133854fab2SYoungJun Cho 10143854fab2SYoungJun Cho /* Wakes up vsync event queue */ 10153854fab2SYoungJun Cho if (atomic_read(&ctx->wait_vsync_event)) { 10163854fab2SYoungJun Cho atomic_set(&ctx->wait_vsync_event, 0); 10173854fab2SYoungJun Cho wake_up(&ctx->wait_vsync_queue); 1018b301ae24SYoungJun Cho } 10193854fab2SYoungJun Cho 1020adf67abfSJoonyoung Shim if (test_bit(0, &ctx->irq_flags)) 10213854fab2SYoungJun Cho drm_handle_vblank(ctx->drm_dev, ctx->pipe); 10223854fab2SYoungJun Cho } 10233854fab2SYoungJun Cho 10241c6244c3SSean Paul static struct exynos_drm_manager_ops fimd_manager_ops = { 10251c6244c3SSean Paul .dpms = fimd_dpms, 1026a968e727SSean Paul .mode_fixup = fimd_mode_fixup, 1027a968e727SSean Paul .mode_set = fimd_mode_set, 10281c6244c3SSean Paul .commit = fimd_commit, 10291c6244c3SSean Paul .enable_vblank = fimd_enable_vblank, 10301c6244c3SSean Paul .disable_vblank = fimd_disable_vblank, 10311c6244c3SSean Paul .wait_for_vblank = fimd_wait_for_vblank, 10321c6244c3SSean Paul .win_mode_set = fimd_win_mode_set, 10331c6244c3SSean Paul .win_commit = fimd_win_commit, 10341c6244c3SSean Paul .win_disable = fimd_win_disable, 10353854fab2SYoungJun Cho .te_handler = fimd_te_handler, 10361c248b7dSInki Dae }; 10371c248b7dSInki Dae 10381c248b7dSInki Dae static irqreturn_t fimd_irq_handler(int irq, void *dev_id) 10391c248b7dSInki Dae { 10401c248b7dSInki Dae struct fimd_context *ctx = (struct fimd_context *)dev_id; 10413854fab2SYoungJun Cho u32 val, clear_bit; 10421c248b7dSInki Dae 10431c248b7dSInki Dae val = readl(ctx->regs + VIDINTCON1); 10441c248b7dSInki Dae 10453854fab2SYoungJun Cho clear_bit = ctx->i80_if ? VIDINTCON1_INT_I80 : VIDINTCON1_INT_FRAME; 10463854fab2SYoungJun Cho if (val & clear_bit) 10473854fab2SYoungJun Cho writel(clear_bit, ctx->regs + VIDINTCON1); 10481c248b7dSInki Dae 1049ec05da95SInki Dae /* check the crtc is detached already from encoder */ 1050080be03dSSean Paul if (ctx->pipe < 0 || !ctx->drm_dev) 1051ec05da95SInki Dae goto out; 1052483b88f8SInki Dae 1053adf67abfSJoonyoung Shim if (ctx->i80_if) { 10541c905d95SYoungJun Cho exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe); 10551c905d95SYoungJun Cho 10561c905d95SYoungJun Cho /* Exits triggering mode */ 10573854fab2SYoungJun Cho atomic_set(&ctx->triggering, 0); 10583854fab2SYoungJun Cho } else { 1059adf67abfSJoonyoung Shim drm_handle_vblank(ctx->drm_dev, ctx->pipe); 1060adf67abfSJoonyoung Shim exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe); 1061adf67abfSJoonyoung Shim 106201ce113cSPrathyush K /* set wait vsync event to zero and wake up queue. */ 106301ce113cSPrathyush K if (atomic_read(&ctx->wait_vsync_event)) { 106401ce113cSPrathyush K atomic_set(&ctx->wait_vsync_event, 0); 10658dd9ad5dSSeung-Woo Kim wake_up(&ctx->wait_vsync_queue); 106601ce113cSPrathyush K } 10673854fab2SYoungJun Cho } 10683854fab2SYoungJun Cho 1069ec05da95SInki Dae out: 10701c248b7dSInki Dae return IRQ_HANDLED; 10711c248b7dSInki Dae } 10721c248b7dSInki Dae 1073f37cd5e8SInki Dae static int fimd_bind(struct device *dev, struct device *master, void *data) 1074562ad9f4SAndrzej Hajda { 1075e152dbd7SAndrzej Hajda struct fimd_context *ctx = dev_get_drvdata(dev); 1076f37cd5e8SInki Dae struct drm_device *drm_dev = data; 1077000cc920SAndrzej Hajda 1078e152dbd7SAndrzej Hajda fimd_mgr_initialize(&ctx->manager, drm_dev); 1079e152dbd7SAndrzej Hajda exynos_drm_crtc_create(&ctx->manager); 1080000cc920SAndrzej Hajda if (ctx->display) 1081000cc920SAndrzej Hajda exynos_drm_create_enc_conn(drm_dev, ctx->display); 1082000cc920SAndrzej Hajda 1083000cc920SAndrzej Hajda return 0; 1084000cc920SAndrzej Hajda 1085000cc920SAndrzej Hajda } 1086000cc920SAndrzej Hajda 1087000cc920SAndrzej Hajda static void fimd_unbind(struct device *dev, struct device *master, 1088000cc920SAndrzej Hajda void *data) 1089000cc920SAndrzej Hajda { 1090e152dbd7SAndrzej Hajda struct fimd_context *ctx = dev_get_drvdata(dev); 1091000cc920SAndrzej Hajda 1092e152dbd7SAndrzej Hajda fimd_dpms(&ctx->manager, DRM_MODE_DPMS_OFF); 1093000cc920SAndrzej Hajda 1094000cc920SAndrzej Hajda if (ctx->display) 1095000cc920SAndrzej Hajda exynos_dpi_remove(dev); 1096000cc920SAndrzej Hajda 1097e152dbd7SAndrzej Hajda fimd_mgr_remove(&ctx->manager); 1098000cc920SAndrzej Hajda } 1099000cc920SAndrzej Hajda 1100000cc920SAndrzej Hajda static const struct component_ops fimd_component_ops = { 1101000cc920SAndrzej Hajda .bind = fimd_bind, 1102000cc920SAndrzej Hajda .unbind = fimd_unbind, 1103000cc920SAndrzej Hajda }; 1104000cc920SAndrzej Hajda 1105000cc920SAndrzej Hajda static int fimd_probe(struct platform_device *pdev) 1106000cc920SAndrzej Hajda { 1107000cc920SAndrzej Hajda struct device *dev = &pdev->dev; 1108000cc920SAndrzej Hajda struct fimd_context *ctx; 11093854fab2SYoungJun Cho struct device_node *i80_if_timings; 1110000cc920SAndrzej Hajda struct resource *res; 1111562ad9f4SAndrzej Hajda int ret = -EINVAL; 1112562ad9f4SAndrzej Hajda 1113e152dbd7SAndrzej Hajda if (!dev->of_node) 1114e152dbd7SAndrzej Hajda return -ENODEV; 11152d3f173cSSachin Kamat 1116d873ab99SSeung-Woo Kim ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); 1117e152dbd7SAndrzej Hajda if (!ctx) 1118e152dbd7SAndrzej Hajda return -ENOMEM; 1119e152dbd7SAndrzej Hajda 1120e152dbd7SAndrzej Hajda ctx->manager.type = EXYNOS_DISPLAY_TYPE_LCD; 1121e152dbd7SAndrzej Hajda ctx->manager.ops = &fimd_manager_ops; 1122e152dbd7SAndrzej Hajda 1123e152dbd7SAndrzej Hajda ret = exynos_drm_component_add(dev, EXYNOS_DEVICE_TYPE_CRTC, 1124e152dbd7SAndrzej Hajda ctx->manager.type); 1125e152dbd7SAndrzej Hajda if (ret) 1126e152dbd7SAndrzej Hajda return ret; 11271c248b7dSInki Dae 1128bb7704d6SSean Paul ctx->dev = dev; 1129a43b933bSSean Paul ctx->suspended = true; 11303854fab2SYoungJun Cho ctx->driver_data = drm_fimd_get_driver_data(pdev); 1131bb7704d6SSean Paul 11321417f109SSean Paul if (of_property_read_bool(dev->of_node, "samsung,invert-vden")) 11331417f109SSean Paul ctx->vidcon1 |= VIDCON1_INV_VDEN; 11341417f109SSean Paul if (of_property_read_bool(dev->of_node, "samsung,invert-vclk")) 11351417f109SSean Paul ctx->vidcon1 |= VIDCON1_INV_VCLK; 1136562ad9f4SAndrzej Hajda 11373854fab2SYoungJun Cho i80_if_timings = of_get_child_by_name(dev->of_node, "i80-if-timings"); 11383854fab2SYoungJun Cho if (i80_if_timings) { 11393854fab2SYoungJun Cho u32 val; 11403854fab2SYoungJun Cho 11413854fab2SYoungJun Cho ctx->i80_if = true; 11423854fab2SYoungJun Cho 11433854fab2SYoungJun Cho if (ctx->driver_data->has_vidoutcon) 11443854fab2SYoungJun Cho ctx->vidout_con |= VIDOUT_CON_F_I80_LDI0; 11453854fab2SYoungJun Cho else 11463854fab2SYoungJun Cho ctx->vidcon0 |= VIDCON0_VIDOUT_I80_LDI0; 11473854fab2SYoungJun Cho /* 11483854fab2SYoungJun Cho * The user manual describes that this "DSI_EN" bit is required 11493854fab2SYoungJun Cho * to enable I80 24-bit data interface. 11503854fab2SYoungJun Cho */ 11513854fab2SYoungJun Cho ctx->vidcon0 |= VIDCON0_DSI_EN; 11523854fab2SYoungJun Cho 11533854fab2SYoungJun Cho if (of_property_read_u32(i80_if_timings, "cs-setup", &val)) 11543854fab2SYoungJun Cho val = 0; 11553854fab2SYoungJun Cho ctx->i80ifcon = LCD_CS_SETUP(val); 11563854fab2SYoungJun Cho if (of_property_read_u32(i80_if_timings, "wr-setup", &val)) 11573854fab2SYoungJun Cho val = 0; 11583854fab2SYoungJun Cho ctx->i80ifcon |= LCD_WR_SETUP(val); 11593854fab2SYoungJun Cho if (of_property_read_u32(i80_if_timings, "wr-active", &val)) 11603854fab2SYoungJun Cho val = 1; 11613854fab2SYoungJun Cho ctx->i80ifcon |= LCD_WR_ACTIVE(val); 11623854fab2SYoungJun Cho if (of_property_read_u32(i80_if_timings, "wr-hold", &val)) 11633854fab2SYoungJun Cho val = 0; 11643854fab2SYoungJun Cho ctx->i80ifcon |= LCD_WR_HOLD(val); 11653854fab2SYoungJun Cho } 11663854fab2SYoungJun Cho of_node_put(i80_if_timings); 11673854fab2SYoungJun Cho 11683854fab2SYoungJun Cho ctx->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node, 11693854fab2SYoungJun Cho "samsung,sysreg"); 11703854fab2SYoungJun Cho if (IS_ERR(ctx->sysreg)) { 11713854fab2SYoungJun Cho dev_warn(dev, "failed to get system register.\n"); 11723854fab2SYoungJun Cho ctx->sysreg = NULL; 11733854fab2SYoungJun Cho } 11743854fab2SYoungJun Cho 1175a968e727SSean Paul ctx->bus_clk = devm_clk_get(dev, "fimd"); 1176a968e727SSean Paul if (IS_ERR(ctx->bus_clk)) { 1177a968e727SSean Paul dev_err(dev, "failed to get bus clock\n"); 1178df5225bcSInki Dae ret = PTR_ERR(ctx->bus_clk); 1179df5225bcSInki Dae goto err_del_component; 1180a968e727SSean Paul } 1181a968e727SSean Paul 1182a968e727SSean Paul ctx->lcd_clk = devm_clk_get(dev, "sclk_fimd"); 1183a968e727SSean Paul if (IS_ERR(ctx->lcd_clk)) { 1184a968e727SSean Paul dev_err(dev, "failed to get lcd clock\n"); 1185df5225bcSInki Dae ret = PTR_ERR(ctx->lcd_clk); 1186df5225bcSInki Dae goto err_del_component; 1187a968e727SSean Paul } 11881c248b7dSInki Dae 11891c248b7dSInki Dae res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 11901c248b7dSInki Dae 1191d873ab99SSeung-Woo Kim ctx->regs = devm_ioremap_resource(dev, res); 1192df5225bcSInki Dae if (IS_ERR(ctx->regs)) { 1193df5225bcSInki Dae ret = PTR_ERR(ctx->regs); 1194df5225bcSInki Dae goto err_del_component; 1195df5225bcSInki Dae } 11961c248b7dSInki Dae 11973854fab2SYoungJun Cho res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, 11983854fab2SYoungJun Cho ctx->i80_if ? "lcd_sys" : "vsync"); 11991c248b7dSInki Dae if (!res) { 12001c248b7dSInki Dae dev_err(dev, "irq request failed.\n"); 1201df5225bcSInki Dae ret = -ENXIO; 1202df5225bcSInki Dae goto err_del_component; 12031c248b7dSInki Dae } 12041c248b7dSInki Dae 1205055e0c06SSean Paul ret = devm_request_irq(dev, res->start, fimd_irq_handler, 1206edc57266SSachin Kamat 0, "drm_fimd", ctx); 1207edc57266SSachin Kamat if (ret) { 12081c248b7dSInki Dae dev_err(dev, "irq request failed.\n"); 1209df5225bcSInki Dae goto err_del_component; 12101c248b7dSInki Dae } 12111c248b7dSInki Dae 121257ed0f7bSDaniel Vetter init_waitqueue_head(&ctx->wait_vsync_queue); 121301ce113cSPrathyush K atomic_set(&ctx->wait_vsync_event, 0); 12141c248b7dSInki Dae 1215e152dbd7SAndrzej Hajda platform_set_drvdata(pdev, ctx); 1216080be03dSSean Paul 1217000cc920SAndrzej Hajda ctx->display = exynos_dpi_probe(dev); 1218000cc920SAndrzej Hajda if (IS_ERR(ctx->display)) 1219000cc920SAndrzej Hajda return PTR_ERR(ctx->display); 1220f37cd5e8SInki Dae 1221e152dbd7SAndrzej Hajda pm_runtime_enable(dev); 1222f37cd5e8SInki Dae 1223e152dbd7SAndrzej Hajda ret = component_add(dev, &fimd_component_ops); 1224df5225bcSInki Dae if (ret) 1225df5225bcSInki Dae goto err_disable_pm_runtime; 1226df5225bcSInki Dae 1227df5225bcSInki Dae return ret; 1228df5225bcSInki Dae 1229df5225bcSInki Dae err_disable_pm_runtime: 1230e152dbd7SAndrzej Hajda pm_runtime_disable(dev); 1231df5225bcSInki Dae 1232df5225bcSInki Dae err_del_component: 1233e152dbd7SAndrzej Hajda exynos_drm_component_del(dev, EXYNOS_DEVICE_TYPE_CRTC); 1234df5225bcSInki Dae return ret; 1235f37cd5e8SInki Dae } 1236f37cd5e8SInki Dae 1237f37cd5e8SInki Dae static int fimd_remove(struct platform_device *pdev) 1238f37cd5e8SInki Dae { 1239af65c804SSean Paul pm_runtime_disable(&pdev->dev); 1240cb91f6a0SJoonyoung Shim 1241df5225bcSInki Dae component_del(&pdev->dev, &fimd_component_ops); 1242df5225bcSInki Dae exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC); 1243df5225bcSInki Dae 12441c248b7dSInki Dae return 0; 12451c248b7dSInki Dae } 12461c248b7dSInki Dae 1247132a5b91SJoonyoung Shim struct platform_driver fimd_driver = { 12481c248b7dSInki Dae .probe = fimd_probe, 124956550d94SGreg Kroah-Hartman .remove = fimd_remove, 12501c248b7dSInki Dae .driver = { 12511c248b7dSInki Dae .name = "exynos4-fb", 12521c248b7dSInki Dae .owner = THIS_MODULE, 12532d3f173cSSachin Kamat .of_match_table = fimd_driver_dt_match, 12541c248b7dSInki Dae }, 12551c248b7dSInki Dae }; 1256