18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* drivers/gpu/drm/exynos5433_drm_decon.c 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2015 Samsung Electronics Co.Ltd 58c2ecf20Sopenharmony_ci * Authors: 68c2ecf20Sopenharmony_ci * Joonyoung Shim <jy0922.shim@samsung.com> 78c2ecf20Sopenharmony_ci * Hyungwon Hwang <human.hwang@samsung.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/clk.h> 118c2ecf20Sopenharmony_ci#include <linux/component.h> 128c2ecf20Sopenharmony_ci#include <linux/iopoll.h> 138c2ecf20Sopenharmony_ci#include <linux/irq.h> 148c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 158c2ecf20Sopenharmony_ci#include <linux/of_device.h> 168c2ecf20Sopenharmony_ci#include <linux/of_gpio.h> 178c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 188c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 198c2ecf20Sopenharmony_ci#include <linux/regmap.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <drm/drm_fourcc.h> 228c2ecf20Sopenharmony_ci#include <drm/drm_vblank.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include "exynos_drm_crtc.h" 258c2ecf20Sopenharmony_ci#include "exynos_drm_drv.h" 268c2ecf20Sopenharmony_ci#include "exynos_drm_fb.h" 278c2ecf20Sopenharmony_ci#include "exynos_drm_plane.h" 288c2ecf20Sopenharmony_ci#include "regs-decon5433.h" 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define DSD_CFG_MUX 0x1004 318c2ecf20Sopenharmony_ci#define DSD_CFG_MUX_TE_UNMASK_GLOBAL BIT(13) 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define WINDOWS_NR 5 348c2ecf20Sopenharmony_ci#define PRIMARY_WIN 2 358c2ecf20Sopenharmony_ci#define CURSON_WIN 4 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define MIN_FB_WIDTH_FOR_16WORD_BURST 128 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define I80_HW_TRG (1 << 0) 408c2ecf20Sopenharmony_ci#define IFTYPE_HDMI (1 << 1) 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic const char * const decon_clks_name[] = { 438c2ecf20Sopenharmony_ci "pclk", 448c2ecf20Sopenharmony_ci "aclk_decon", 458c2ecf20Sopenharmony_ci "aclk_smmu_decon0x", 468c2ecf20Sopenharmony_ci "aclk_xiu_decon0x", 478c2ecf20Sopenharmony_ci "pclk_smmu_decon0x", 488c2ecf20Sopenharmony_ci "aclk_smmu_decon1x", 498c2ecf20Sopenharmony_ci "aclk_xiu_decon1x", 508c2ecf20Sopenharmony_ci "pclk_smmu_decon1x", 518c2ecf20Sopenharmony_ci "sclk_decon_vclk", 528c2ecf20Sopenharmony_ci "sclk_decon_eclk", 538c2ecf20Sopenharmony_ci}; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistruct decon_context { 568c2ecf20Sopenharmony_ci struct device *dev; 578c2ecf20Sopenharmony_ci struct drm_device *drm_dev; 588c2ecf20Sopenharmony_ci void *dma_priv; 598c2ecf20Sopenharmony_ci struct exynos_drm_crtc *crtc; 608c2ecf20Sopenharmony_ci struct exynos_drm_plane planes[WINDOWS_NR]; 618c2ecf20Sopenharmony_ci struct exynos_drm_plane_config configs[WINDOWS_NR]; 628c2ecf20Sopenharmony_ci void __iomem *addr; 638c2ecf20Sopenharmony_ci struct regmap *sysreg; 648c2ecf20Sopenharmony_ci struct clk *clks[ARRAY_SIZE(decon_clks_name)]; 658c2ecf20Sopenharmony_ci unsigned int irq; 668c2ecf20Sopenharmony_ci unsigned int irq_vsync; 678c2ecf20Sopenharmony_ci unsigned int irq_lcd_sys; 688c2ecf20Sopenharmony_ci unsigned int te_irq; 698c2ecf20Sopenharmony_ci unsigned long out_type; 708c2ecf20Sopenharmony_ci int first_win; 718c2ecf20Sopenharmony_ci spinlock_t vblank_lock; 728c2ecf20Sopenharmony_ci u32 frame_id; 738c2ecf20Sopenharmony_ci}; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic const uint32_t decon_formats[] = { 768c2ecf20Sopenharmony_ci DRM_FORMAT_XRGB1555, 778c2ecf20Sopenharmony_ci DRM_FORMAT_RGB565, 788c2ecf20Sopenharmony_ci DRM_FORMAT_XRGB8888, 798c2ecf20Sopenharmony_ci DRM_FORMAT_ARGB8888, 808c2ecf20Sopenharmony_ci}; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic const enum drm_plane_type decon_win_types[WINDOWS_NR] = { 838c2ecf20Sopenharmony_ci [PRIMARY_WIN] = DRM_PLANE_TYPE_PRIMARY, 848c2ecf20Sopenharmony_ci [CURSON_WIN] = DRM_PLANE_TYPE_CURSOR, 858c2ecf20Sopenharmony_ci}; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic const unsigned int capabilities[WINDOWS_NR] = { 888c2ecf20Sopenharmony_ci 0, 898c2ecf20Sopenharmony_ci EXYNOS_DRM_PLANE_CAP_WIN_BLEND | EXYNOS_DRM_PLANE_CAP_PIX_BLEND, 908c2ecf20Sopenharmony_ci EXYNOS_DRM_PLANE_CAP_WIN_BLEND | EXYNOS_DRM_PLANE_CAP_PIX_BLEND, 918c2ecf20Sopenharmony_ci EXYNOS_DRM_PLANE_CAP_WIN_BLEND | EXYNOS_DRM_PLANE_CAP_PIX_BLEND, 928c2ecf20Sopenharmony_ci EXYNOS_DRM_PLANE_CAP_WIN_BLEND | EXYNOS_DRM_PLANE_CAP_PIX_BLEND, 938c2ecf20Sopenharmony_ci}; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic inline void decon_set_bits(struct decon_context *ctx, u32 reg, u32 mask, 968c2ecf20Sopenharmony_ci u32 val) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci val = (val & mask) | (readl(ctx->addr + reg) & ~mask); 998c2ecf20Sopenharmony_ci writel(val, ctx->addr + reg); 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic int decon_enable_vblank(struct exynos_drm_crtc *crtc) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci struct decon_context *ctx = crtc->ctx; 1058c2ecf20Sopenharmony_ci u32 val; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci val = VIDINTCON0_INTEN; 1088c2ecf20Sopenharmony_ci if (crtc->i80_mode) 1098c2ecf20Sopenharmony_ci val |= VIDINTCON0_FRAMEDONE; 1108c2ecf20Sopenharmony_ci else 1118c2ecf20Sopenharmony_ci val |= VIDINTCON0_INTFRMEN | VIDINTCON0_FRAMESEL_FP; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci writel(val, ctx->addr + DECON_VIDINTCON0); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci enable_irq(ctx->irq); 1168c2ecf20Sopenharmony_ci if (!(ctx->out_type & I80_HW_TRG)) 1178c2ecf20Sopenharmony_ci enable_irq(ctx->te_irq); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci return 0; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic void decon_disable_vblank(struct exynos_drm_crtc *crtc) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci struct decon_context *ctx = crtc->ctx; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (!(ctx->out_type & I80_HW_TRG)) 1278c2ecf20Sopenharmony_ci disable_irq_nosync(ctx->te_irq); 1288c2ecf20Sopenharmony_ci disable_irq_nosync(ctx->irq); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci writel(0, ctx->addr + DECON_VIDINTCON0); 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci/* return number of starts/ends of frame transmissions since reset */ 1348c2ecf20Sopenharmony_cistatic u32 decon_get_frame_count(struct decon_context *ctx, bool end) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci u32 frm, pfrm, status, cnt = 2; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci /* To get consistent result repeat read until frame id is stable. 1398c2ecf20Sopenharmony_ci * Usually the loop will be executed once, in rare cases when the loop 1408c2ecf20Sopenharmony_ci * is executed at frame change time 2nd pass will be needed. 1418c2ecf20Sopenharmony_ci */ 1428c2ecf20Sopenharmony_ci frm = readl(ctx->addr + DECON_CRFMID); 1438c2ecf20Sopenharmony_ci do { 1448c2ecf20Sopenharmony_ci status = readl(ctx->addr + DECON_VIDCON1); 1458c2ecf20Sopenharmony_ci pfrm = frm; 1468c2ecf20Sopenharmony_ci frm = readl(ctx->addr + DECON_CRFMID); 1478c2ecf20Sopenharmony_ci } while (frm != pfrm && --cnt); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci /* CRFMID is incremented on BPORCH in case of I80 and on VSYNC in case 1508c2ecf20Sopenharmony_ci * of RGB, it should be taken into account. 1518c2ecf20Sopenharmony_ci */ 1528c2ecf20Sopenharmony_ci if (!frm) 1538c2ecf20Sopenharmony_ci return 0; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci switch (status & (VIDCON1_VSTATUS_MASK | VIDCON1_I80_ACTIVE)) { 1568c2ecf20Sopenharmony_ci case VIDCON1_VSTATUS_VS: 1578c2ecf20Sopenharmony_ci if (!(ctx->crtc->i80_mode)) 1588c2ecf20Sopenharmony_ci --frm; 1598c2ecf20Sopenharmony_ci break; 1608c2ecf20Sopenharmony_ci case VIDCON1_VSTATUS_BP: 1618c2ecf20Sopenharmony_ci --frm; 1628c2ecf20Sopenharmony_ci break; 1638c2ecf20Sopenharmony_ci case VIDCON1_I80_ACTIVE: 1648c2ecf20Sopenharmony_ci case VIDCON1_VSTATUS_AC: 1658c2ecf20Sopenharmony_ci if (end) 1668c2ecf20Sopenharmony_ci --frm; 1678c2ecf20Sopenharmony_ci break; 1688c2ecf20Sopenharmony_ci default: 1698c2ecf20Sopenharmony_ci break; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci return frm; 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic void decon_setup_trigger(struct decon_context *ctx) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci if (!ctx->crtc->i80_mode && !(ctx->out_type & I80_HW_TRG)) 1788c2ecf20Sopenharmony_ci return; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci if (!(ctx->out_type & I80_HW_TRG)) { 1818c2ecf20Sopenharmony_ci writel(TRIGCON_TRIGEN_PER_F | TRIGCON_TRIGEN_F | 1828c2ecf20Sopenharmony_ci TRIGCON_TE_AUTO_MASK | TRIGCON_SWTRIGEN, 1838c2ecf20Sopenharmony_ci ctx->addr + DECON_TRIGCON); 1848c2ecf20Sopenharmony_ci return; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci writel(TRIGCON_TRIGEN_PER_F | TRIGCON_TRIGEN_F | TRIGCON_HWTRIGMASK 1888c2ecf20Sopenharmony_ci | TRIGCON_HWTRIGEN, ctx->addr + DECON_TRIGCON); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci if (regmap_update_bits(ctx->sysreg, DSD_CFG_MUX, 1918c2ecf20Sopenharmony_ci DSD_CFG_MUX_TE_UNMASK_GLOBAL, ~0)) 1928c2ecf20Sopenharmony_ci DRM_DEV_ERROR(ctx->dev, "Cannot update sysreg.\n"); 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic void decon_commit(struct exynos_drm_crtc *crtc) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci struct decon_context *ctx = crtc->ctx; 1988c2ecf20Sopenharmony_ci struct drm_display_mode *m = &crtc->base.mode; 1998c2ecf20Sopenharmony_ci bool interlaced = false; 2008c2ecf20Sopenharmony_ci u32 val; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (ctx->out_type & IFTYPE_HDMI) { 2038c2ecf20Sopenharmony_ci m->crtc_hsync_start = m->crtc_hdisplay + 10; 2048c2ecf20Sopenharmony_ci m->crtc_hsync_end = m->crtc_htotal - 92; 2058c2ecf20Sopenharmony_ci m->crtc_vsync_start = m->crtc_vdisplay + 1; 2068c2ecf20Sopenharmony_ci m->crtc_vsync_end = m->crtc_vsync_start + 1; 2078c2ecf20Sopenharmony_ci if (m->flags & DRM_MODE_FLAG_INTERLACE) 2088c2ecf20Sopenharmony_ci interlaced = true; 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci decon_setup_trigger(ctx); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci /* lcd on and use command if */ 2148c2ecf20Sopenharmony_ci val = VIDOUT_LCD_ON; 2158c2ecf20Sopenharmony_ci if (interlaced) 2168c2ecf20Sopenharmony_ci val |= VIDOUT_INTERLACE_EN_F; 2178c2ecf20Sopenharmony_ci if (crtc->i80_mode) { 2188c2ecf20Sopenharmony_ci val |= VIDOUT_COMMAND_IF; 2198c2ecf20Sopenharmony_ci } else { 2208c2ecf20Sopenharmony_ci val |= VIDOUT_RGB_IF; 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci writel(val, ctx->addr + DECON_VIDOUTCON0); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci if (interlaced) 2268c2ecf20Sopenharmony_ci val = VIDTCON2_LINEVAL(m->vdisplay / 2 - 1) | 2278c2ecf20Sopenharmony_ci VIDTCON2_HOZVAL(m->hdisplay - 1); 2288c2ecf20Sopenharmony_ci else 2298c2ecf20Sopenharmony_ci val = VIDTCON2_LINEVAL(m->vdisplay - 1) | 2308c2ecf20Sopenharmony_ci VIDTCON2_HOZVAL(m->hdisplay - 1); 2318c2ecf20Sopenharmony_ci writel(val, ctx->addr + DECON_VIDTCON2); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci if (!crtc->i80_mode) { 2348c2ecf20Sopenharmony_ci int vbp = m->crtc_vtotal - m->crtc_vsync_end; 2358c2ecf20Sopenharmony_ci int vfp = m->crtc_vsync_start - m->crtc_vdisplay; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci if (interlaced) 2388c2ecf20Sopenharmony_ci vbp = vbp / 2 - 1; 2398c2ecf20Sopenharmony_ci val = VIDTCON00_VBPD_F(vbp - 1) | VIDTCON00_VFPD_F(vfp - 1); 2408c2ecf20Sopenharmony_ci writel(val, ctx->addr + DECON_VIDTCON00); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci val = VIDTCON01_VSPW_F( 2438c2ecf20Sopenharmony_ci m->crtc_vsync_end - m->crtc_vsync_start - 1); 2448c2ecf20Sopenharmony_ci writel(val, ctx->addr + DECON_VIDTCON01); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci val = VIDTCON10_HBPD_F( 2478c2ecf20Sopenharmony_ci m->crtc_htotal - m->crtc_hsync_end - 1) | 2488c2ecf20Sopenharmony_ci VIDTCON10_HFPD_F( 2498c2ecf20Sopenharmony_ci m->crtc_hsync_start - m->crtc_hdisplay - 1); 2508c2ecf20Sopenharmony_ci writel(val, ctx->addr + DECON_VIDTCON10); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci val = VIDTCON11_HSPW_F( 2538c2ecf20Sopenharmony_ci m->crtc_hsync_end - m->crtc_hsync_start - 1); 2548c2ecf20Sopenharmony_ci writel(val, ctx->addr + DECON_VIDTCON11); 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci /* enable output and display signal */ 2588c2ecf20Sopenharmony_ci decon_set_bits(ctx, DECON_VIDCON0, VIDCON0_ENVID | VIDCON0_ENVID_F, ~0); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci decon_set_bits(ctx, DECON_UPDATE, STANDALONE_UPDATE_F, ~0); 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistatic void decon_win_set_bldeq(struct decon_context *ctx, unsigned int win, 2648c2ecf20Sopenharmony_ci unsigned int alpha, unsigned int pixel_alpha) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci u32 mask = BLENDERQ_A_FUNC_F(0xf) | BLENDERQ_B_FUNC_F(0xf); 2678c2ecf20Sopenharmony_ci u32 val = 0; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci switch (pixel_alpha) { 2708c2ecf20Sopenharmony_ci case DRM_MODE_BLEND_PIXEL_NONE: 2718c2ecf20Sopenharmony_ci case DRM_MODE_BLEND_COVERAGE: 2728c2ecf20Sopenharmony_ci val |= BLENDERQ_A_FUNC_F(BLENDERQ_ALPHA_A); 2738c2ecf20Sopenharmony_ci val |= BLENDERQ_B_FUNC_F(BLENDERQ_ONE_MINUS_ALPHA_A); 2748c2ecf20Sopenharmony_ci break; 2758c2ecf20Sopenharmony_ci case DRM_MODE_BLEND_PREMULTI: 2768c2ecf20Sopenharmony_ci default: 2778c2ecf20Sopenharmony_ci if (alpha != DRM_BLEND_ALPHA_OPAQUE) { 2788c2ecf20Sopenharmony_ci val |= BLENDERQ_A_FUNC_F(BLENDERQ_ALPHA0); 2798c2ecf20Sopenharmony_ci val |= BLENDERQ_B_FUNC_F(BLENDERQ_ONE_MINUS_ALPHA_A); 2808c2ecf20Sopenharmony_ci } else { 2818c2ecf20Sopenharmony_ci val |= BLENDERQ_A_FUNC_F(BLENDERQ_ONE); 2828c2ecf20Sopenharmony_ci val |= BLENDERQ_B_FUNC_F(BLENDERQ_ONE_MINUS_ALPHA_A); 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci break; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci decon_set_bits(ctx, DECON_BLENDERQx(win), mask, val); 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic void decon_win_set_bldmod(struct decon_context *ctx, unsigned int win, 2908c2ecf20Sopenharmony_ci unsigned int alpha, unsigned int pixel_alpha) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci u32 win_alpha = alpha >> 8; 2938c2ecf20Sopenharmony_ci u32 val = 0; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci switch (pixel_alpha) { 2968c2ecf20Sopenharmony_ci case DRM_MODE_BLEND_PIXEL_NONE: 2978c2ecf20Sopenharmony_ci break; 2988c2ecf20Sopenharmony_ci case DRM_MODE_BLEND_COVERAGE: 2998c2ecf20Sopenharmony_ci case DRM_MODE_BLEND_PREMULTI: 3008c2ecf20Sopenharmony_ci default: 3018c2ecf20Sopenharmony_ci val |= WINCONx_ALPHA_SEL_F; 3028c2ecf20Sopenharmony_ci val |= WINCONx_BLD_PIX_F; 3038c2ecf20Sopenharmony_ci val |= WINCONx_ALPHA_MUL_F; 3048c2ecf20Sopenharmony_ci break; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci decon_set_bits(ctx, DECON_WINCONx(win), WINCONx_BLEND_MODE_MASK, val); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci if (alpha != DRM_BLEND_ALPHA_OPAQUE) { 3098c2ecf20Sopenharmony_ci val = VIDOSD_Wx_ALPHA_R_F(win_alpha) | 3108c2ecf20Sopenharmony_ci VIDOSD_Wx_ALPHA_G_F(win_alpha) | 3118c2ecf20Sopenharmony_ci VIDOSD_Wx_ALPHA_B_F(win_alpha); 3128c2ecf20Sopenharmony_ci decon_set_bits(ctx, DECON_VIDOSDxC(win), 3138c2ecf20Sopenharmony_ci VIDOSDxC_ALPHA0_RGB_MASK, val); 3148c2ecf20Sopenharmony_ci decon_set_bits(ctx, DECON_BLENDCON, BLEND_NEW, BLEND_NEW); 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic void decon_win_set_pixfmt(struct decon_context *ctx, unsigned int win, 3198c2ecf20Sopenharmony_ci struct drm_framebuffer *fb) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci struct exynos_drm_plane *plane = &ctx->planes[win]; 3228c2ecf20Sopenharmony_ci struct exynos_drm_plane_state *state = 3238c2ecf20Sopenharmony_ci to_exynos_plane_state(plane->base.state); 3248c2ecf20Sopenharmony_ci unsigned int alpha = state->base.alpha; 3258c2ecf20Sopenharmony_ci unsigned int pixel_alpha; 3268c2ecf20Sopenharmony_ci unsigned long val; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci if (fb->format->has_alpha) 3298c2ecf20Sopenharmony_ci pixel_alpha = state->base.pixel_blend_mode; 3308c2ecf20Sopenharmony_ci else 3318c2ecf20Sopenharmony_ci pixel_alpha = DRM_MODE_BLEND_PIXEL_NONE; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci val = readl(ctx->addr + DECON_WINCONx(win)); 3348c2ecf20Sopenharmony_ci val &= WINCONx_ENWIN_F; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci switch (fb->format->format) { 3378c2ecf20Sopenharmony_ci case DRM_FORMAT_XRGB1555: 3388c2ecf20Sopenharmony_ci val |= WINCONx_BPPMODE_16BPP_I1555; 3398c2ecf20Sopenharmony_ci val |= WINCONx_HAWSWP_F; 3408c2ecf20Sopenharmony_ci val |= WINCONx_BURSTLEN_16WORD; 3418c2ecf20Sopenharmony_ci break; 3428c2ecf20Sopenharmony_ci case DRM_FORMAT_RGB565: 3438c2ecf20Sopenharmony_ci val |= WINCONx_BPPMODE_16BPP_565; 3448c2ecf20Sopenharmony_ci val |= WINCONx_HAWSWP_F; 3458c2ecf20Sopenharmony_ci val |= WINCONx_BURSTLEN_16WORD; 3468c2ecf20Sopenharmony_ci break; 3478c2ecf20Sopenharmony_ci case DRM_FORMAT_XRGB8888: 3488c2ecf20Sopenharmony_ci val |= WINCONx_BPPMODE_24BPP_888; 3498c2ecf20Sopenharmony_ci val |= WINCONx_WSWP_F; 3508c2ecf20Sopenharmony_ci val |= WINCONx_BURSTLEN_16WORD; 3518c2ecf20Sopenharmony_ci break; 3528c2ecf20Sopenharmony_ci case DRM_FORMAT_ARGB8888: 3538c2ecf20Sopenharmony_ci default: 3548c2ecf20Sopenharmony_ci val |= WINCONx_BPPMODE_32BPP_A8888; 3558c2ecf20Sopenharmony_ci val |= WINCONx_WSWP_F; 3568c2ecf20Sopenharmony_ci val |= WINCONx_BURSTLEN_16WORD; 3578c2ecf20Sopenharmony_ci break; 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci DRM_DEV_DEBUG_KMS(ctx->dev, "cpp = %u\n", fb->format->cpp[0]); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci /* 3638c2ecf20Sopenharmony_ci * In case of exynos, setting dma-burst to 16Word causes permanent 3648c2ecf20Sopenharmony_ci * tearing for very small buffers, e.g. cursor buffer. Burst Mode 3658c2ecf20Sopenharmony_ci * switching which is based on plane size is not recommended as 3668c2ecf20Sopenharmony_ci * plane size varies a lot towards the end of the screen and rapid 3678c2ecf20Sopenharmony_ci * movement causes unstable DMA which results into iommu crash/tear. 3688c2ecf20Sopenharmony_ci */ 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci if (fb->width < MIN_FB_WIDTH_FOR_16WORD_BURST) { 3718c2ecf20Sopenharmony_ci val &= ~WINCONx_BURSTLEN_MASK; 3728c2ecf20Sopenharmony_ci val |= WINCONx_BURSTLEN_8WORD; 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci decon_set_bits(ctx, DECON_WINCONx(win), ~WINCONx_BLEND_MODE_MASK, val); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci if (win > 0) { 3778c2ecf20Sopenharmony_ci decon_win_set_bldmod(ctx, win, alpha, pixel_alpha); 3788c2ecf20Sopenharmony_ci decon_win_set_bldeq(ctx, win, alpha, pixel_alpha); 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci} 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_cistatic void decon_shadow_protect(struct decon_context *ctx, bool protect) 3838c2ecf20Sopenharmony_ci{ 3848c2ecf20Sopenharmony_ci decon_set_bits(ctx, DECON_SHADOWCON, SHADOWCON_PROTECT_MASK, 3858c2ecf20Sopenharmony_ci protect ? ~0 : 0); 3868c2ecf20Sopenharmony_ci} 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_cistatic void decon_atomic_begin(struct exynos_drm_crtc *crtc) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci struct decon_context *ctx = crtc->ctx; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci decon_shadow_protect(ctx, true); 3938c2ecf20Sopenharmony_ci} 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci#define BIT_VAL(x, e, s) (((x) & ((1 << ((e) - (s) + 1)) - 1)) << (s)) 3968c2ecf20Sopenharmony_ci#define COORDINATE_X(x) BIT_VAL((x), 23, 12) 3978c2ecf20Sopenharmony_ci#define COORDINATE_Y(x) BIT_VAL((x), 11, 0) 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_cistatic void decon_update_plane(struct exynos_drm_crtc *crtc, 4008c2ecf20Sopenharmony_ci struct exynos_drm_plane *plane) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci struct exynos_drm_plane_state *state = 4038c2ecf20Sopenharmony_ci to_exynos_plane_state(plane->base.state); 4048c2ecf20Sopenharmony_ci struct decon_context *ctx = crtc->ctx; 4058c2ecf20Sopenharmony_ci struct drm_framebuffer *fb = state->base.fb; 4068c2ecf20Sopenharmony_ci unsigned int win = plane->index; 4078c2ecf20Sopenharmony_ci unsigned int cpp = fb->format->cpp[0]; 4088c2ecf20Sopenharmony_ci unsigned int pitch = fb->pitches[0]; 4098c2ecf20Sopenharmony_ci dma_addr_t dma_addr = exynos_drm_fb_dma_addr(fb, 0); 4108c2ecf20Sopenharmony_ci u32 val; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci if (crtc->base.mode.flags & DRM_MODE_FLAG_INTERLACE) { 4138c2ecf20Sopenharmony_ci val = COORDINATE_X(state->crtc.x) | 4148c2ecf20Sopenharmony_ci COORDINATE_Y(state->crtc.y / 2); 4158c2ecf20Sopenharmony_ci writel(val, ctx->addr + DECON_VIDOSDxA(win)); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci val = COORDINATE_X(state->crtc.x + state->crtc.w - 1) | 4188c2ecf20Sopenharmony_ci COORDINATE_Y((state->crtc.y + state->crtc.h) / 2 - 1); 4198c2ecf20Sopenharmony_ci writel(val, ctx->addr + DECON_VIDOSDxB(win)); 4208c2ecf20Sopenharmony_ci } else { 4218c2ecf20Sopenharmony_ci val = COORDINATE_X(state->crtc.x) | COORDINATE_Y(state->crtc.y); 4228c2ecf20Sopenharmony_ci writel(val, ctx->addr + DECON_VIDOSDxA(win)); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci val = COORDINATE_X(state->crtc.x + state->crtc.w - 1) | 4258c2ecf20Sopenharmony_ci COORDINATE_Y(state->crtc.y + state->crtc.h - 1); 4268c2ecf20Sopenharmony_ci writel(val, ctx->addr + DECON_VIDOSDxB(win)); 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci val = VIDOSD_Wx_ALPHA_R_F(0xff) | VIDOSD_Wx_ALPHA_G_F(0xff) | 4308c2ecf20Sopenharmony_ci VIDOSD_Wx_ALPHA_B_F(0xff); 4318c2ecf20Sopenharmony_ci writel(val, ctx->addr + DECON_VIDOSDxC(win)); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci val = VIDOSD_Wx_ALPHA_R_F(0x0) | VIDOSD_Wx_ALPHA_G_F(0x0) | 4348c2ecf20Sopenharmony_ci VIDOSD_Wx_ALPHA_B_F(0x0); 4358c2ecf20Sopenharmony_ci writel(val, ctx->addr + DECON_VIDOSDxD(win)); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci writel(dma_addr, ctx->addr + DECON_VIDW0xADD0B0(win)); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci val = dma_addr + pitch * state->src.h; 4408c2ecf20Sopenharmony_ci writel(val, ctx->addr + DECON_VIDW0xADD1B0(win)); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci if (!(ctx->out_type & IFTYPE_HDMI)) 4438c2ecf20Sopenharmony_ci val = BIT_VAL(pitch - state->crtc.w * cpp, 27, 14) 4448c2ecf20Sopenharmony_ci | BIT_VAL(state->crtc.w * cpp, 13, 0); 4458c2ecf20Sopenharmony_ci else 4468c2ecf20Sopenharmony_ci val = BIT_VAL(pitch - state->crtc.w * cpp, 29, 15) 4478c2ecf20Sopenharmony_ci | BIT_VAL(state->crtc.w * cpp, 14, 0); 4488c2ecf20Sopenharmony_ci writel(val, ctx->addr + DECON_VIDW0xADD2(win)); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci decon_win_set_pixfmt(ctx, win, fb); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci /* window enable */ 4538c2ecf20Sopenharmony_ci decon_set_bits(ctx, DECON_WINCONx(win), WINCONx_ENWIN_F, ~0); 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cistatic void decon_disable_plane(struct exynos_drm_crtc *crtc, 4578c2ecf20Sopenharmony_ci struct exynos_drm_plane *plane) 4588c2ecf20Sopenharmony_ci{ 4598c2ecf20Sopenharmony_ci struct decon_context *ctx = crtc->ctx; 4608c2ecf20Sopenharmony_ci unsigned int win = plane->index; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci decon_set_bits(ctx, DECON_WINCONx(win), WINCONx_ENWIN_F, 0); 4638c2ecf20Sopenharmony_ci} 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_cistatic void decon_atomic_flush(struct exynos_drm_crtc *crtc) 4668c2ecf20Sopenharmony_ci{ 4678c2ecf20Sopenharmony_ci struct decon_context *ctx = crtc->ctx; 4688c2ecf20Sopenharmony_ci unsigned long flags; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci spin_lock_irqsave(&ctx->vblank_lock, flags); 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci decon_shadow_protect(ctx, false); 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci decon_set_bits(ctx, DECON_UPDATE, STANDALONE_UPDATE_F, ~0); 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci ctx->frame_id = decon_get_frame_count(ctx, true); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci exynos_crtc_handle_event(crtc); 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctx->vblank_lock, flags); 4818c2ecf20Sopenharmony_ci} 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_cistatic void decon_swreset(struct decon_context *ctx) 4848c2ecf20Sopenharmony_ci{ 4858c2ecf20Sopenharmony_ci unsigned long flags; 4868c2ecf20Sopenharmony_ci u32 val; 4878c2ecf20Sopenharmony_ci int ret; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci writel(0, ctx->addr + DECON_VIDCON0); 4908c2ecf20Sopenharmony_ci readl_poll_timeout(ctx->addr + DECON_VIDCON0, val, 4918c2ecf20Sopenharmony_ci ~val & VIDCON0_STOP_STATUS, 12, 20000); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci writel(VIDCON0_SWRESET, ctx->addr + DECON_VIDCON0); 4948c2ecf20Sopenharmony_ci ret = readl_poll_timeout(ctx->addr + DECON_VIDCON0, val, 4958c2ecf20Sopenharmony_ci ~val & VIDCON0_SWRESET, 12, 20000); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci WARN(ret < 0, "failed to software reset DECON\n"); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci spin_lock_irqsave(&ctx->vblank_lock, flags); 5008c2ecf20Sopenharmony_ci ctx->frame_id = 0; 5018c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctx->vblank_lock, flags); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci if (!(ctx->out_type & IFTYPE_HDMI)) 5048c2ecf20Sopenharmony_ci return; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci writel(VIDCON0_CLKVALUP | VIDCON0_VLCKFREE, ctx->addr + DECON_VIDCON0); 5078c2ecf20Sopenharmony_ci decon_set_bits(ctx, DECON_CMU, 5088c2ecf20Sopenharmony_ci CMU_CLKGAGE_MODE_SFR_F | CMU_CLKGAGE_MODE_MEM_F, ~0); 5098c2ecf20Sopenharmony_ci writel(VIDCON1_VCLK_RUN_VDEN_DISABLE, ctx->addr + DECON_VIDCON1); 5108c2ecf20Sopenharmony_ci writel(CRCCTRL_CRCEN | CRCCTRL_CRCSTART_F | CRCCTRL_CRCCLKEN, 5118c2ecf20Sopenharmony_ci ctx->addr + DECON_CRCCTRL); 5128c2ecf20Sopenharmony_ci} 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_cistatic void decon_atomic_enable(struct exynos_drm_crtc *crtc) 5158c2ecf20Sopenharmony_ci{ 5168c2ecf20Sopenharmony_ci struct decon_context *ctx = crtc->ctx; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci pm_runtime_get_sync(ctx->dev); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci exynos_drm_pipe_clk_enable(crtc, true); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci decon_swreset(ctx); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci decon_commit(ctx->crtc); 5258c2ecf20Sopenharmony_ci} 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_cistatic void decon_atomic_disable(struct exynos_drm_crtc *crtc) 5288c2ecf20Sopenharmony_ci{ 5298c2ecf20Sopenharmony_ci struct decon_context *ctx = crtc->ctx; 5308c2ecf20Sopenharmony_ci int i; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci if (!(ctx->out_type & I80_HW_TRG)) 5338c2ecf20Sopenharmony_ci synchronize_irq(ctx->te_irq); 5348c2ecf20Sopenharmony_ci synchronize_irq(ctx->irq); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci /* 5378c2ecf20Sopenharmony_ci * We need to make sure that all windows are disabled before we 5388c2ecf20Sopenharmony_ci * suspend that connector. Otherwise we might try to scan from 5398c2ecf20Sopenharmony_ci * a destroyed buffer later. 5408c2ecf20Sopenharmony_ci */ 5418c2ecf20Sopenharmony_ci for (i = ctx->first_win; i < WINDOWS_NR; i++) 5428c2ecf20Sopenharmony_ci decon_disable_plane(crtc, &ctx->planes[i]); 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci decon_swreset(ctx); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci exynos_drm_pipe_clk_enable(crtc, false); 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci pm_runtime_put_sync(ctx->dev); 5498c2ecf20Sopenharmony_ci} 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_cistatic irqreturn_t decon_te_irq_handler(int irq, void *dev_id) 5528c2ecf20Sopenharmony_ci{ 5538c2ecf20Sopenharmony_ci struct decon_context *ctx = dev_id; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci decon_set_bits(ctx, DECON_TRIGCON, TRIGCON_SWTRIGCMD, ~0); 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci return IRQ_HANDLED; 5588c2ecf20Sopenharmony_ci} 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_cistatic void decon_clear_channels(struct exynos_drm_crtc *crtc) 5618c2ecf20Sopenharmony_ci{ 5628c2ecf20Sopenharmony_ci struct decon_context *ctx = crtc->ctx; 5638c2ecf20Sopenharmony_ci int win, i, ret; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(decon_clks_name); i++) { 5668c2ecf20Sopenharmony_ci ret = clk_prepare_enable(ctx->clks[i]); 5678c2ecf20Sopenharmony_ci if (ret < 0) 5688c2ecf20Sopenharmony_ci goto err; 5698c2ecf20Sopenharmony_ci } 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci decon_shadow_protect(ctx, true); 5728c2ecf20Sopenharmony_ci for (win = 0; win < WINDOWS_NR; win++) 5738c2ecf20Sopenharmony_ci decon_set_bits(ctx, DECON_WINCONx(win), WINCONx_ENWIN_F, 0); 5748c2ecf20Sopenharmony_ci decon_shadow_protect(ctx, false); 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci decon_set_bits(ctx, DECON_UPDATE, STANDALONE_UPDATE_F, ~0); 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci /* TODO: wait for possible vsync */ 5798c2ecf20Sopenharmony_ci msleep(50); 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_cierr: 5828c2ecf20Sopenharmony_ci while (--i >= 0) 5838c2ecf20Sopenharmony_ci clk_disable_unprepare(ctx->clks[i]); 5848c2ecf20Sopenharmony_ci} 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_cistatic enum drm_mode_status decon_mode_valid(struct exynos_drm_crtc *crtc, 5878c2ecf20Sopenharmony_ci const struct drm_display_mode *mode) 5888c2ecf20Sopenharmony_ci{ 5898c2ecf20Sopenharmony_ci struct decon_context *ctx = crtc->ctx; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci ctx->irq = crtc->i80_mode ? ctx->irq_lcd_sys : ctx->irq_vsync; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci if (ctx->irq) 5948c2ecf20Sopenharmony_ci return MODE_OK; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci dev_info(ctx->dev, "Sink requires %s mode, but appropriate interrupt is not provided.\n", 5978c2ecf20Sopenharmony_ci crtc->i80_mode ? "command" : "video"); 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci return MODE_BAD; 6008c2ecf20Sopenharmony_ci} 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_cistatic const struct exynos_drm_crtc_ops decon_crtc_ops = { 6038c2ecf20Sopenharmony_ci .atomic_enable = decon_atomic_enable, 6048c2ecf20Sopenharmony_ci .atomic_disable = decon_atomic_disable, 6058c2ecf20Sopenharmony_ci .enable_vblank = decon_enable_vblank, 6068c2ecf20Sopenharmony_ci .disable_vblank = decon_disable_vblank, 6078c2ecf20Sopenharmony_ci .atomic_begin = decon_atomic_begin, 6088c2ecf20Sopenharmony_ci .update_plane = decon_update_plane, 6098c2ecf20Sopenharmony_ci .disable_plane = decon_disable_plane, 6108c2ecf20Sopenharmony_ci .mode_valid = decon_mode_valid, 6118c2ecf20Sopenharmony_ci .atomic_flush = decon_atomic_flush, 6128c2ecf20Sopenharmony_ci}; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_cistatic int decon_bind(struct device *dev, struct device *master, void *data) 6158c2ecf20Sopenharmony_ci{ 6168c2ecf20Sopenharmony_ci struct decon_context *ctx = dev_get_drvdata(dev); 6178c2ecf20Sopenharmony_ci struct drm_device *drm_dev = data; 6188c2ecf20Sopenharmony_ci struct exynos_drm_plane *exynos_plane; 6198c2ecf20Sopenharmony_ci enum exynos_drm_output_type out_type; 6208c2ecf20Sopenharmony_ci unsigned int win; 6218c2ecf20Sopenharmony_ci int ret; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci ctx->drm_dev = drm_dev; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci for (win = ctx->first_win; win < WINDOWS_NR; win++) { 6268c2ecf20Sopenharmony_ci ctx->configs[win].pixel_formats = decon_formats; 6278c2ecf20Sopenharmony_ci ctx->configs[win].num_pixel_formats = ARRAY_SIZE(decon_formats); 6288c2ecf20Sopenharmony_ci ctx->configs[win].zpos = win - ctx->first_win; 6298c2ecf20Sopenharmony_ci ctx->configs[win].type = decon_win_types[win]; 6308c2ecf20Sopenharmony_ci ctx->configs[win].capabilities = capabilities[win]; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci ret = exynos_plane_init(drm_dev, &ctx->planes[win], win, 6338c2ecf20Sopenharmony_ci &ctx->configs[win]); 6348c2ecf20Sopenharmony_ci if (ret) 6358c2ecf20Sopenharmony_ci return ret; 6368c2ecf20Sopenharmony_ci } 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci exynos_plane = &ctx->planes[PRIMARY_WIN]; 6398c2ecf20Sopenharmony_ci out_type = (ctx->out_type & IFTYPE_HDMI) ? EXYNOS_DISPLAY_TYPE_HDMI 6408c2ecf20Sopenharmony_ci : EXYNOS_DISPLAY_TYPE_LCD; 6418c2ecf20Sopenharmony_ci ctx->crtc = exynos_drm_crtc_create(drm_dev, &exynos_plane->base, 6428c2ecf20Sopenharmony_ci out_type, &decon_crtc_ops, ctx); 6438c2ecf20Sopenharmony_ci if (IS_ERR(ctx->crtc)) 6448c2ecf20Sopenharmony_ci return PTR_ERR(ctx->crtc); 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci decon_clear_channels(ctx->crtc); 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci return exynos_drm_register_dma(drm_dev, dev, &ctx->dma_priv); 6498c2ecf20Sopenharmony_ci} 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_cistatic void decon_unbind(struct device *dev, struct device *master, void *data) 6528c2ecf20Sopenharmony_ci{ 6538c2ecf20Sopenharmony_ci struct decon_context *ctx = dev_get_drvdata(dev); 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci decon_atomic_disable(ctx->crtc); 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci /* detach this sub driver from iommu mapping if supported. */ 6588c2ecf20Sopenharmony_ci exynos_drm_unregister_dma(ctx->drm_dev, ctx->dev, &ctx->dma_priv); 6598c2ecf20Sopenharmony_ci} 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_cistatic const struct component_ops decon_component_ops = { 6628c2ecf20Sopenharmony_ci .bind = decon_bind, 6638c2ecf20Sopenharmony_ci .unbind = decon_unbind, 6648c2ecf20Sopenharmony_ci}; 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_cistatic void decon_handle_vblank(struct decon_context *ctx) 6678c2ecf20Sopenharmony_ci{ 6688c2ecf20Sopenharmony_ci u32 frm; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci spin_lock(&ctx->vblank_lock); 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci frm = decon_get_frame_count(ctx, true); 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci if (frm != ctx->frame_id) { 6758c2ecf20Sopenharmony_ci /* handle only if incremented, take care of wrap-around */ 6768c2ecf20Sopenharmony_ci if ((s32)(frm - ctx->frame_id) > 0) 6778c2ecf20Sopenharmony_ci drm_crtc_handle_vblank(&ctx->crtc->base); 6788c2ecf20Sopenharmony_ci ctx->frame_id = frm; 6798c2ecf20Sopenharmony_ci } 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci spin_unlock(&ctx->vblank_lock); 6828c2ecf20Sopenharmony_ci} 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_cistatic irqreturn_t decon_irq_handler(int irq, void *dev_id) 6858c2ecf20Sopenharmony_ci{ 6868c2ecf20Sopenharmony_ci struct decon_context *ctx = dev_id; 6878c2ecf20Sopenharmony_ci u32 val; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci val = readl(ctx->addr + DECON_VIDINTCON1); 6908c2ecf20Sopenharmony_ci val &= VIDINTCON1_INTFRMDONEPEND | VIDINTCON1_INTFRMPEND; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci if (val) { 6938c2ecf20Sopenharmony_ci writel(val, ctx->addr + DECON_VIDINTCON1); 6948c2ecf20Sopenharmony_ci if (ctx->out_type & IFTYPE_HDMI) { 6958c2ecf20Sopenharmony_ci val = readl(ctx->addr + DECON_VIDOUTCON0); 6968c2ecf20Sopenharmony_ci val &= VIDOUT_INTERLACE_EN_F | VIDOUT_INTERLACE_FIELD_F; 6978c2ecf20Sopenharmony_ci if (val == 6988c2ecf20Sopenharmony_ci (VIDOUT_INTERLACE_EN_F | VIDOUT_INTERLACE_FIELD_F)) 6998c2ecf20Sopenharmony_ci return IRQ_HANDLED; 7008c2ecf20Sopenharmony_ci } 7018c2ecf20Sopenharmony_ci decon_handle_vblank(ctx); 7028c2ecf20Sopenharmony_ci } 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci return IRQ_HANDLED; 7058c2ecf20Sopenharmony_ci} 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 7088c2ecf20Sopenharmony_cistatic int exynos5433_decon_suspend(struct device *dev) 7098c2ecf20Sopenharmony_ci{ 7108c2ecf20Sopenharmony_ci struct decon_context *ctx = dev_get_drvdata(dev); 7118c2ecf20Sopenharmony_ci int i = ARRAY_SIZE(decon_clks_name); 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci while (--i >= 0) 7148c2ecf20Sopenharmony_ci clk_disable_unprepare(ctx->clks[i]); 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci return 0; 7178c2ecf20Sopenharmony_ci} 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_cistatic int exynos5433_decon_resume(struct device *dev) 7208c2ecf20Sopenharmony_ci{ 7218c2ecf20Sopenharmony_ci struct decon_context *ctx = dev_get_drvdata(dev); 7228c2ecf20Sopenharmony_ci int i, ret; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(decon_clks_name); i++) { 7258c2ecf20Sopenharmony_ci ret = clk_prepare_enable(ctx->clks[i]); 7268c2ecf20Sopenharmony_ci if (ret < 0) 7278c2ecf20Sopenharmony_ci goto err; 7288c2ecf20Sopenharmony_ci } 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci return 0; 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_cierr: 7338c2ecf20Sopenharmony_ci while (--i >= 0) 7348c2ecf20Sopenharmony_ci clk_disable_unprepare(ctx->clks[i]); 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci return ret; 7378c2ecf20Sopenharmony_ci} 7388c2ecf20Sopenharmony_ci#endif 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_cistatic const struct dev_pm_ops exynos5433_decon_pm_ops = { 7418c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(exynos5433_decon_suspend, exynos5433_decon_resume, 7428c2ecf20Sopenharmony_ci NULL) 7438c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 7448c2ecf20Sopenharmony_ci pm_runtime_force_resume) 7458c2ecf20Sopenharmony_ci}; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_cistatic const struct of_device_id exynos5433_decon_driver_dt_match[] = { 7488c2ecf20Sopenharmony_ci { 7498c2ecf20Sopenharmony_ci .compatible = "samsung,exynos5433-decon", 7508c2ecf20Sopenharmony_ci .data = (void *)I80_HW_TRG 7518c2ecf20Sopenharmony_ci }, 7528c2ecf20Sopenharmony_ci { 7538c2ecf20Sopenharmony_ci .compatible = "samsung,exynos5433-decon-tv", 7548c2ecf20Sopenharmony_ci .data = (void *)(I80_HW_TRG | IFTYPE_HDMI) 7558c2ecf20Sopenharmony_ci }, 7568c2ecf20Sopenharmony_ci {}, 7578c2ecf20Sopenharmony_ci}; 7588c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, exynos5433_decon_driver_dt_match); 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_cistatic int decon_conf_irq(struct decon_context *ctx, const char *name, 7618c2ecf20Sopenharmony_ci irq_handler_t handler, unsigned long int flags) 7628c2ecf20Sopenharmony_ci{ 7638c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(ctx->dev); 7648c2ecf20Sopenharmony_ci int ret, irq = platform_get_irq_byname(pdev, name); 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci if (irq < 0) { 7678c2ecf20Sopenharmony_ci switch (irq) { 7688c2ecf20Sopenharmony_ci case -EPROBE_DEFER: 7698c2ecf20Sopenharmony_ci return irq; 7708c2ecf20Sopenharmony_ci case -ENODATA: 7718c2ecf20Sopenharmony_ci case -ENXIO: 7728c2ecf20Sopenharmony_ci return 0; 7738c2ecf20Sopenharmony_ci default: 7748c2ecf20Sopenharmony_ci dev_err(ctx->dev, "IRQ %s get failed, %d\n", name, irq); 7758c2ecf20Sopenharmony_ci return irq; 7768c2ecf20Sopenharmony_ci } 7778c2ecf20Sopenharmony_ci } 7788c2ecf20Sopenharmony_ci ret = devm_request_irq(ctx->dev, irq, handler, 7798c2ecf20Sopenharmony_ci flags | IRQF_NO_AUTOEN, "drm_decon", ctx); 7808c2ecf20Sopenharmony_ci if (ret < 0) { 7818c2ecf20Sopenharmony_ci dev_err(ctx->dev, "IRQ %s request failed\n", name); 7828c2ecf20Sopenharmony_ci return ret; 7838c2ecf20Sopenharmony_ci } 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci return irq; 7868c2ecf20Sopenharmony_ci} 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_cistatic int exynos5433_decon_probe(struct platform_device *pdev) 7898c2ecf20Sopenharmony_ci{ 7908c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 7918c2ecf20Sopenharmony_ci struct decon_context *ctx; 7928c2ecf20Sopenharmony_ci struct resource *res; 7938c2ecf20Sopenharmony_ci int ret; 7948c2ecf20Sopenharmony_ci int i; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); 7978c2ecf20Sopenharmony_ci if (!ctx) 7988c2ecf20Sopenharmony_ci return -ENOMEM; 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci ctx->dev = dev; 8018c2ecf20Sopenharmony_ci ctx->out_type = (unsigned long)of_device_get_match_data(dev); 8028c2ecf20Sopenharmony_ci spin_lock_init(&ctx->vblank_lock); 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci if (ctx->out_type & IFTYPE_HDMI) 8058c2ecf20Sopenharmony_ci ctx->first_win = 1; 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(decon_clks_name); i++) { 8088c2ecf20Sopenharmony_ci struct clk *clk; 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci clk = devm_clk_get(ctx->dev, decon_clks_name[i]); 8118c2ecf20Sopenharmony_ci if (IS_ERR(clk)) 8128c2ecf20Sopenharmony_ci return PTR_ERR(clk); 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci ctx->clks[i] = clk; 8158c2ecf20Sopenharmony_ci } 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 8188c2ecf20Sopenharmony_ci ctx->addr = devm_ioremap_resource(dev, res); 8198c2ecf20Sopenharmony_ci if (IS_ERR(ctx->addr)) { 8208c2ecf20Sopenharmony_ci dev_err(dev, "ioremap failed\n"); 8218c2ecf20Sopenharmony_ci return PTR_ERR(ctx->addr); 8228c2ecf20Sopenharmony_ci } 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci ret = decon_conf_irq(ctx, "vsync", decon_irq_handler, 0); 8258c2ecf20Sopenharmony_ci if (ret < 0) 8268c2ecf20Sopenharmony_ci return ret; 8278c2ecf20Sopenharmony_ci ctx->irq_vsync = ret; 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci ret = decon_conf_irq(ctx, "lcd_sys", decon_irq_handler, 0); 8308c2ecf20Sopenharmony_ci if (ret < 0) 8318c2ecf20Sopenharmony_ci return ret; 8328c2ecf20Sopenharmony_ci ctx->irq_lcd_sys = ret; 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci ret = decon_conf_irq(ctx, "te", decon_te_irq_handler, 8358c2ecf20Sopenharmony_ci IRQF_TRIGGER_RISING); 8368c2ecf20Sopenharmony_ci if (ret < 0) 8378c2ecf20Sopenharmony_ci return ret; 8388c2ecf20Sopenharmony_ci if (ret) { 8398c2ecf20Sopenharmony_ci ctx->te_irq = ret; 8408c2ecf20Sopenharmony_ci ctx->out_type &= ~I80_HW_TRG; 8418c2ecf20Sopenharmony_ci } 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci if (ctx->out_type & I80_HW_TRG) { 8448c2ecf20Sopenharmony_ci ctx->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node, 8458c2ecf20Sopenharmony_ci "samsung,disp-sysreg"); 8468c2ecf20Sopenharmony_ci if (IS_ERR(ctx->sysreg)) { 8478c2ecf20Sopenharmony_ci dev_err(dev, "failed to get system register\n"); 8488c2ecf20Sopenharmony_ci return PTR_ERR(ctx->sysreg); 8498c2ecf20Sopenharmony_ci } 8508c2ecf20Sopenharmony_ci } 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, ctx); 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci pm_runtime_enable(dev); 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci ret = component_add(dev, &decon_component_ops); 8578c2ecf20Sopenharmony_ci if (ret) 8588c2ecf20Sopenharmony_ci goto err_disable_pm_runtime; 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci return 0; 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_cierr_disable_pm_runtime: 8638c2ecf20Sopenharmony_ci pm_runtime_disable(dev); 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci return ret; 8668c2ecf20Sopenharmony_ci} 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_cistatic int exynos5433_decon_remove(struct platform_device *pdev) 8698c2ecf20Sopenharmony_ci{ 8708c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci component_del(&pdev->dev, &decon_component_ops); 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci return 0; 8758c2ecf20Sopenharmony_ci} 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_cistruct platform_driver exynos5433_decon_driver = { 8788c2ecf20Sopenharmony_ci .probe = exynos5433_decon_probe, 8798c2ecf20Sopenharmony_ci .remove = exynos5433_decon_remove, 8808c2ecf20Sopenharmony_ci .driver = { 8818c2ecf20Sopenharmony_ci .name = "exynos5433-decon", 8828c2ecf20Sopenharmony_ci .pm = &exynos5433_decon_pm_ops, 8838c2ecf20Sopenharmony_ci .of_match_table = exynos5433_decon_driver_dt_match, 8848c2ecf20Sopenharmony_ci }, 8858c2ecf20Sopenharmony_ci}; 886