162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* exynos_drm_fimd.c 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2011 Samsung Electronics Co.Ltd 562306a36Sopenharmony_ci * Authors: 662306a36Sopenharmony_ci * Joonyoung Shim <jy0922.shim@samsung.com> 762306a36Sopenharmony_ci * Inki Dae <inki.dae@samsung.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/clk.h> 1162306a36Sopenharmony_ci#include <linux/component.h> 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 1462306a36Sopenharmony_ci#include <linux/of.h> 1562306a36Sopenharmony_ci#include <linux/platform_device.h> 1662306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1762306a36Sopenharmony_ci#include <linux/regmap.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <video/of_display_timing.h> 2062306a36Sopenharmony_ci#include <video/of_videomode.h> 2162306a36Sopenharmony_ci#include <video/samsung_fimd.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include <drm/drm_blend.h> 2462306a36Sopenharmony_ci#include <drm/drm_fourcc.h> 2562306a36Sopenharmony_ci#include <drm/drm_framebuffer.h> 2662306a36Sopenharmony_ci#include <drm/drm_vblank.h> 2762306a36Sopenharmony_ci#include <drm/exynos_drm.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include "exynos_drm_crtc.h" 3062306a36Sopenharmony_ci#include "exynos_drm_drv.h" 3162306a36Sopenharmony_ci#include "exynos_drm_fb.h" 3262306a36Sopenharmony_ci#include "exynos_drm_plane.h" 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* 3562306a36Sopenharmony_ci * FIMD stands for Fully Interactive Mobile Display and 3662306a36Sopenharmony_ci * as a display controller, it transfers contents drawn on memory 3762306a36Sopenharmony_ci * to a LCD Panel through Display Interfaces such as RGB or 3862306a36Sopenharmony_ci * CPU Interface. 3962306a36Sopenharmony_ci */ 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#define MIN_FB_WIDTH_FOR_16WORD_BURST 128 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* position control register for hardware window 0, 2 ~ 4.*/ 4462306a36Sopenharmony_ci#define VIDOSD_A(win) (VIDOSD_BASE + 0x00 + (win) * 16) 4562306a36Sopenharmony_ci#define VIDOSD_B(win) (VIDOSD_BASE + 0x04 + (win) * 16) 4662306a36Sopenharmony_ci/* 4762306a36Sopenharmony_ci * size control register for hardware windows 0 and alpha control register 4862306a36Sopenharmony_ci * for hardware windows 1 ~ 4 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_ci#define VIDOSD_C(win) (VIDOSD_BASE + 0x08 + (win) * 16) 5162306a36Sopenharmony_ci/* size control register for hardware windows 1 ~ 2. */ 5262306a36Sopenharmony_ci#define VIDOSD_D(win) (VIDOSD_BASE + 0x0C + (win) * 16) 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci#define VIDWnALPHA0(win) (VIDW_ALPHA + 0x00 + (win) * 8) 5562306a36Sopenharmony_ci#define VIDWnALPHA1(win) (VIDW_ALPHA + 0x04 + (win) * 8) 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#define VIDWx_BUF_START(win, buf) (VIDW_BUF_START(buf) + (win) * 8) 5862306a36Sopenharmony_ci#define VIDWx_BUF_START_S(win, buf) (VIDW_BUF_START_S(buf) + (win) * 8) 5962306a36Sopenharmony_ci#define VIDWx_BUF_END(win, buf) (VIDW_BUF_END(buf) + (win) * 8) 6062306a36Sopenharmony_ci#define VIDWx_BUF_SIZE(win, buf) (VIDW_BUF_SIZE(buf) + (win) * 4) 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/* color key control register for hardware window 1 ~ 4. */ 6362306a36Sopenharmony_ci#define WKEYCON0_BASE(x) ((WKEYCON0 + 0x140) + ((x - 1) * 8)) 6462306a36Sopenharmony_ci/* color key value register for hardware window 1 ~ 4. */ 6562306a36Sopenharmony_ci#define WKEYCON1_BASE(x) ((WKEYCON1 + 0x140) + ((x - 1) * 8)) 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci/* I80 trigger control register */ 6862306a36Sopenharmony_ci#define TRIGCON 0x1A4 6962306a36Sopenharmony_ci#define TRGMODE_ENABLE (1 << 0) 7062306a36Sopenharmony_ci#define SWTRGCMD_ENABLE (1 << 1) 7162306a36Sopenharmony_ci/* Exynos3250, 3472, 5260 5410, 5420 and 5422 only supported. */ 7262306a36Sopenharmony_ci#define HWTRGEN_ENABLE (1 << 3) 7362306a36Sopenharmony_ci#define HWTRGMASK_ENABLE (1 << 4) 7462306a36Sopenharmony_ci/* Exynos3250, 3472, 5260, 5420 and 5422 only supported. */ 7562306a36Sopenharmony_ci#define HWTRIGEN_PER_ENABLE (1 << 31) 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci/* display mode change control register except exynos4 */ 7862306a36Sopenharmony_ci#define VIDOUT_CON 0x000 7962306a36Sopenharmony_ci#define VIDOUT_CON_F_I80_LDI0 (0x2 << 8) 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci/* I80 interface control for main LDI register */ 8262306a36Sopenharmony_ci#define I80IFCONFAx(x) (0x1B0 + (x) * 4) 8362306a36Sopenharmony_ci#define I80IFCONFBx(x) (0x1B8 + (x) * 4) 8462306a36Sopenharmony_ci#define LCD_CS_SETUP(x) ((x) << 16) 8562306a36Sopenharmony_ci#define LCD_WR_SETUP(x) ((x) << 12) 8662306a36Sopenharmony_ci#define LCD_WR_ACTIVE(x) ((x) << 8) 8762306a36Sopenharmony_ci#define LCD_WR_HOLD(x) ((x) << 4) 8862306a36Sopenharmony_ci#define I80IFEN_ENABLE (1 << 0) 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/* FIMD has totally five hardware windows. */ 9162306a36Sopenharmony_ci#define WINDOWS_NR 5 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci/* HW trigger flag on i80 panel. */ 9462306a36Sopenharmony_ci#define I80_HW_TRG (1 << 1) 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistruct fimd_driver_data { 9762306a36Sopenharmony_ci unsigned int timing_base; 9862306a36Sopenharmony_ci unsigned int lcdblk_offset; 9962306a36Sopenharmony_ci unsigned int lcdblk_vt_shift; 10062306a36Sopenharmony_ci unsigned int lcdblk_bypass_shift; 10162306a36Sopenharmony_ci unsigned int lcdblk_mic_bypass_shift; 10262306a36Sopenharmony_ci unsigned int trg_type; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci unsigned int has_shadowcon:1; 10562306a36Sopenharmony_ci unsigned int has_clksel:1; 10662306a36Sopenharmony_ci unsigned int has_limited_fmt:1; 10762306a36Sopenharmony_ci unsigned int has_vidoutcon:1; 10862306a36Sopenharmony_ci unsigned int has_vtsel:1; 10962306a36Sopenharmony_ci unsigned int has_mic_bypass:1; 11062306a36Sopenharmony_ci unsigned int has_dp_clk:1; 11162306a36Sopenharmony_ci unsigned int has_hw_trigger:1; 11262306a36Sopenharmony_ci unsigned int has_trigger_per_te:1; 11362306a36Sopenharmony_ci unsigned int has_bgr_support:1; 11462306a36Sopenharmony_ci}; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic struct fimd_driver_data s3c64xx_fimd_driver_data = { 11762306a36Sopenharmony_ci .timing_base = 0x0, 11862306a36Sopenharmony_ci .has_clksel = 1, 11962306a36Sopenharmony_ci .has_limited_fmt = 1, 12062306a36Sopenharmony_ci}; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic struct fimd_driver_data s5pv210_fimd_driver_data = { 12362306a36Sopenharmony_ci .timing_base = 0x0, 12462306a36Sopenharmony_ci .has_shadowcon = 1, 12562306a36Sopenharmony_ci .has_clksel = 1, 12662306a36Sopenharmony_ci}; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic struct fimd_driver_data exynos3_fimd_driver_data = { 12962306a36Sopenharmony_ci .timing_base = 0x20000, 13062306a36Sopenharmony_ci .lcdblk_offset = 0x210, 13162306a36Sopenharmony_ci .lcdblk_bypass_shift = 1, 13262306a36Sopenharmony_ci .has_shadowcon = 1, 13362306a36Sopenharmony_ci .has_vidoutcon = 1, 13462306a36Sopenharmony_ci}; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic struct fimd_driver_data exynos4_fimd_driver_data = { 13762306a36Sopenharmony_ci .timing_base = 0x0, 13862306a36Sopenharmony_ci .lcdblk_offset = 0x210, 13962306a36Sopenharmony_ci .lcdblk_vt_shift = 10, 14062306a36Sopenharmony_ci .lcdblk_bypass_shift = 1, 14162306a36Sopenharmony_ci .has_shadowcon = 1, 14262306a36Sopenharmony_ci .has_vtsel = 1, 14362306a36Sopenharmony_ci .has_bgr_support = 1, 14462306a36Sopenharmony_ci}; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic struct fimd_driver_data exynos5_fimd_driver_data = { 14762306a36Sopenharmony_ci .timing_base = 0x20000, 14862306a36Sopenharmony_ci .lcdblk_offset = 0x214, 14962306a36Sopenharmony_ci .lcdblk_vt_shift = 24, 15062306a36Sopenharmony_ci .lcdblk_bypass_shift = 15, 15162306a36Sopenharmony_ci .has_shadowcon = 1, 15262306a36Sopenharmony_ci .has_vidoutcon = 1, 15362306a36Sopenharmony_ci .has_vtsel = 1, 15462306a36Sopenharmony_ci .has_dp_clk = 1, 15562306a36Sopenharmony_ci .has_bgr_support = 1, 15662306a36Sopenharmony_ci}; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic struct fimd_driver_data exynos5420_fimd_driver_data = { 15962306a36Sopenharmony_ci .timing_base = 0x20000, 16062306a36Sopenharmony_ci .lcdblk_offset = 0x214, 16162306a36Sopenharmony_ci .lcdblk_vt_shift = 24, 16262306a36Sopenharmony_ci .lcdblk_bypass_shift = 15, 16362306a36Sopenharmony_ci .lcdblk_mic_bypass_shift = 11, 16462306a36Sopenharmony_ci .has_shadowcon = 1, 16562306a36Sopenharmony_ci .has_vidoutcon = 1, 16662306a36Sopenharmony_ci .has_vtsel = 1, 16762306a36Sopenharmony_ci .has_mic_bypass = 1, 16862306a36Sopenharmony_ci .has_dp_clk = 1, 16962306a36Sopenharmony_ci .has_bgr_support = 1, 17062306a36Sopenharmony_ci}; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistruct fimd_context { 17362306a36Sopenharmony_ci struct device *dev; 17462306a36Sopenharmony_ci struct drm_device *drm_dev; 17562306a36Sopenharmony_ci void *dma_priv; 17662306a36Sopenharmony_ci struct exynos_drm_crtc *crtc; 17762306a36Sopenharmony_ci struct exynos_drm_plane planes[WINDOWS_NR]; 17862306a36Sopenharmony_ci struct exynos_drm_plane_config configs[WINDOWS_NR]; 17962306a36Sopenharmony_ci struct clk *bus_clk; 18062306a36Sopenharmony_ci struct clk *lcd_clk; 18162306a36Sopenharmony_ci void __iomem *regs; 18262306a36Sopenharmony_ci struct regmap *sysreg; 18362306a36Sopenharmony_ci unsigned long irq_flags; 18462306a36Sopenharmony_ci u32 vidcon0; 18562306a36Sopenharmony_ci u32 vidcon1; 18662306a36Sopenharmony_ci u32 vidout_con; 18762306a36Sopenharmony_ci u32 i80ifcon; 18862306a36Sopenharmony_ci bool i80_if; 18962306a36Sopenharmony_ci bool suspended; 19062306a36Sopenharmony_ci wait_queue_head_t wait_vsync_queue; 19162306a36Sopenharmony_ci atomic_t wait_vsync_event; 19262306a36Sopenharmony_ci atomic_t win_updated; 19362306a36Sopenharmony_ci atomic_t triggering; 19462306a36Sopenharmony_ci u32 clkdiv; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci const struct fimd_driver_data *driver_data; 19762306a36Sopenharmony_ci struct drm_encoder *encoder; 19862306a36Sopenharmony_ci struct exynos_drm_clk dp_clk; 19962306a36Sopenharmony_ci}; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic const struct of_device_id fimd_driver_dt_match[] = { 20262306a36Sopenharmony_ci { .compatible = "samsung,s3c6400-fimd", 20362306a36Sopenharmony_ci .data = &s3c64xx_fimd_driver_data }, 20462306a36Sopenharmony_ci { .compatible = "samsung,s5pv210-fimd", 20562306a36Sopenharmony_ci .data = &s5pv210_fimd_driver_data }, 20662306a36Sopenharmony_ci { .compatible = "samsung,exynos3250-fimd", 20762306a36Sopenharmony_ci .data = &exynos3_fimd_driver_data }, 20862306a36Sopenharmony_ci { .compatible = "samsung,exynos4210-fimd", 20962306a36Sopenharmony_ci .data = &exynos4_fimd_driver_data }, 21062306a36Sopenharmony_ci { .compatible = "samsung,exynos5250-fimd", 21162306a36Sopenharmony_ci .data = &exynos5_fimd_driver_data }, 21262306a36Sopenharmony_ci { .compatible = "samsung,exynos5420-fimd", 21362306a36Sopenharmony_ci .data = &exynos5420_fimd_driver_data }, 21462306a36Sopenharmony_ci {}, 21562306a36Sopenharmony_ci}; 21662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, fimd_driver_dt_match); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic const enum drm_plane_type fimd_win_types[WINDOWS_NR] = { 21962306a36Sopenharmony_ci DRM_PLANE_TYPE_PRIMARY, 22062306a36Sopenharmony_ci DRM_PLANE_TYPE_OVERLAY, 22162306a36Sopenharmony_ci DRM_PLANE_TYPE_OVERLAY, 22262306a36Sopenharmony_ci DRM_PLANE_TYPE_OVERLAY, 22362306a36Sopenharmony_ci DRM_PLANE_TYPE_CURSOR, 22462306a36Sopenharmony_ci}; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic const uint32_t fimd_formats[] = { 22762306a36Sopenharmony_ci DRM_FORMAT_C8, 22862306a36Sopenharmony_ci DRM_FORMAT_XRGB1555, 22962306a36Sopenharmony_ci DRM_FORMAT_RGB565, 23062306a36Sopenharmony_ci DRM_FORMAT_XRGB8888, 23162306a36Sopenharmony_ci DRM_FORMAT_ARGB8888, 23262306a36Sopenharmony_ci}; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_cistatic const uint32_t fimd_extended_formats[] = { 23562306a36Sopenharmony_ci DRM_FORMAT_C8, 23662306a36Sopenharmony_ci DRM_FORMAT_XRGB1555, 23762306a36Sopenharmony_ci DRM_FORMAT_XBGR1555, 23862306a36Sopenharmony_ci DRM_FORMAT_RGB565, 23962306a36Sopenharmony_ci DRM_FORMAT_BGR565, 24062306a36Sopenharmony_ci DRM_FORMAT_XRGB8888, 24162306a36Sopenharmony_ci DRM_FORMAT_XBGR8888, 24262306a36Sopenharmony_ci DRM_FORMAT_ARGB8888, 24362306a36Sopenharmony_ci DRM_FORMAT_ABGR8888, 24462306a36Sopenharmony_ci}; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic const unsigned int capabilities[WINDOWS_NR] = { 24762306a36Sopenharmony_ci 0, 24862306a36Sopenharmony_ci EXYNOS_DRM_PLANE_CAP_WIN_BLEND | EXYNOS_DRM_PLANE_CAP_PIX_BLEND, 24962306a36Sopenharmony_ci EXYNOS_DRM_PLANE_CAP_WIN_BLEND | EXYNOS_DRM_PLANE_CAP_PIX_BLEND, 25062306a36Sopenharmony_ci EXYNOS_DRM_PLANE_CAP_WIN_BLEND | EXYNOS_DRM_PLANE_CAP_PIX_BLEND, 25162306a36Sopenharmony_ci EXYNOS_DRM_PLANE_CAP_WIN_BLEND | EXYNOS_DRM_PLANE_CAP_PIX_BLEND, 25262306a36Sopenharmony_ci}; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic inline void fimd_set_bits(struct fimd_context *ctx, u32 reg, u32 mask, 25562306a36Sopenharmony_ci u32 val) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci val = (val & mask) | (readl(ctx->regs + reg) & ~mask); 25862306a36Sopenharmony_ci writel(val, ctx->regs + reg); 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cistatic int fimd_enable_vblank(struct exynos_drm_crtc *crtc) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci struct fimd_context *ctx = crtc->ctx; 26462306a36Sopenharmony_ci u32 val; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if (ctx->suspended) 26762306a36Sopenharmony_ci return -EPERM; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci if (!test_and_set_bit(0, &ctx->irq_flags)) { 27062306a36Sopenharmony_ci val = readl(ctx->regs + VIDINTCON0); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci val |= VIDINTCON0_INT_ENABLE; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci if (ctx->i80_if) { 27562306a36Sopenharmony_ci val |= VIDINTCON0_INT_I80IFDONE; 27662306a36Sopenharmony_ci val |= VIDINTCON0_INT_SYSMAINCON; 27762306a36Sopenharmony_ci val &= ~VIDINTCON0_INT_SYSSUBCON; 27862306a36Sopenharmony_ci } else { 27962306a36Sopenharmony_ci val |= VIDINTCON0_INT_FRAME; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci val &= ~VIDINTCON0_FRAMESEL0_MASK; 28262306a36Sopenharmony_ci val |= VIDINTCON0_FRAMESEL0_FRONTPORCH; 28362306a36Sopenharmony_ci val &= ~VIDINTCON0_FRAMESEL1_MASK; 28462306a36Sopenharmony_ci val |= VIDINTCON0_FRAMESEL1_NONE; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci writel(val, ctx->regs + VIDINTCON0); 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci return 0; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic void fimd_disable_vblank(struct exynos_drm_crtc *crtc) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci struct fimd_context *ctx = crtc->ctx; 29662306a36Sopenharmony_ci u32 val; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (ctx->suspended) 29962306a36Sopenharmony_ci return; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci if (test_and_clear_bit(0, &ctx->irq_flags)) { 30262306a36Sopenharmony_ci val = readl(ctx->regs + VIDINTCON0); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci val &= ~VIDINTCON0_INT_ENABLE; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (ctx->i80_if) { 30762306a36Sopenharmony_ci val &= ~VIDINTCON0_INT_I80IFDONE; 30862306a36Sopenharmony_ci val &= ~VIDINTCON0_INT_SYSMAINCON; 30962306a36Sopenharmony_ci val &= ~VIDINTCON0_INT_SYSSUBCON; 31062306a36Sopenharmony_ci } else 31162306a36Sopenharmony_ci val &= ~VIDINTCON0_INT_FRAME; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci writel(val, ctx->regs + VIDINTCON0); 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_cistatic void fimd_wait_for_vblank(struct exynos_drm_crtc *crtc) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci struct fimd_context *ctx = crtc->ctx; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci if (ctx->suspended) 32262306a36Sopenharmony_ci return; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci atomic_set(&ctx->wait_vsync_event, 1); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci /* 32762306a36Sopenharmony_ci * wait for FIMD to signal VSYNC interrupt or return after 32862306a36Sopenharmony_ci * timeout which is set to 50ms (refresh rate of 20). 32962306a36Sopenharmony_ci */ 33062306a36Sopenharmony_ci if (!wait_event_timeout(ctx->wait_vsync_queue, 33162306a36Sopenharmony_ci !atomic_read(&ctx->wait_vsync_event), 33262306a36Sopenharmony_ci HZ/20)) 33362306a36Sopenharmony_ci DRM_DEV_DEBUG_KMS(ctx->dev, "vblank wait timed out.\n"); 33462306a36Sopenharmony_ci} 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_cistatic void fimd_enable_video_output(struct fimd_context *ctx, unsigned int win, 33762306a36Sopenharmony_ci bool enable) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci u32 val = readl(ctx->regs + WINCON(win)); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci if (enable) 34262306a36Sopenharmony_ci val |= WINCONx_ENWIN; 34362306a36Sopenharmony_ci else 34462306a36Sopenharmony_ci val &= ~WINCONx_ENWIN; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci writel(val, ctx->regs + WINCON(win)); 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cistatic void fimd_enable_shadow_channel_path(struct fimd_context *ctx, 35062306a36Sopenharmony_ci unsigned int win, 35162306a36Sopenharmony_ci bool enable) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci u32 val = readl(ctx->regs + SHADOWCON); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci if (enable) 35662306a36Sopenharmony_ci val |= SHADOWCON_CHx_ENABLE(win); 35762306a36Sopenharmony_ci else 35862306a36Sopenharmony_ci val &= ~SHADOWCON_CHx_ENABLE(win); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci writel(val, ctx->regs + SHADOWCON); 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_cistatic int fimd_clear_channels(struct exynos_drm_crtc *crtc) 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci struct fimd_context *ctx = crtc->ctx; 36662306a36Sopenharmony_ci unsigned int win, ch_enabled = 0; 36762306a36Sopenharmony_ci int ret; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci /* Hardware is in unknown state, so ensure it gets enabled properly */ 37062306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(ctx->dev); 37162306a36Sopenharmony_ci if (ret < 0) { 37262306a36Sopenharmony_ci dev_err(ctx->dev, "failed to enable FIMD device.\n"); 37362306a36Sopenharmony_ci return ret; 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci clk_prepare_enable(ctx->bus_clk); 37762306a36Sopenharmony_ci clk_prepare_enable(ctx->lcd_clk); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci /* Check if any channel is enabled. */ 38062306a36Sopenharmony_ci for (win = 0; win < WINDOWS_NR; win++) { 38162306a36Sopenharmony_ci u32 val = readl(ctx->regs + WINCON(win)); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci if (val & WINCONx_ENWIN) { 38462306a36Sopenharmony_ci fimd_enable_video_output(ctx, win, false); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci if (ctx->driver_data->has_shadowcon) 38762306a36Sopenharmony_ci fimd_enable_shadow_channel_path(ctx, win, 38862306a36Sopenharmony_ci false); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci ch_enabled = 1; 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci /* Wait for vsync, as disable channel takes effect at next vsync */ 39562306a36Sopenharmony_ci if (ch_enabled) { 39662306a36Sopenharmony_ci ctx->suspended = false; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci fimd_enable_vblank(ctx->crtc); 39962306a36Sopenharmony_ci fimd_wait_for_vblank(ctx->crtc); 40062306a36Sopenharmony_ci fimd_disable_vblank(ctx->crtc); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci ctx->suspended = true; 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci clk_disable_unprepare(ctx->lcd_clk); 40662306a36Sopenharmony_ci clk_disable_unprepare(ctx->bus_clk); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci pm_runtime_put(ctx->dev); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci return 0; 41162306a36Sopenharmony_ci} 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_cistatic int fimd_atomic_check(struct exynos_drm_crtc *crtc, 41562306a36Sopenharmony_ci struct drm_crtc_state *state) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci struct drm_display_mode *mode = &state->adjusted_mode; 41862306a36Sopenharmony_ci struct fimd_context *ctx = crtc->ctx; 41962306a36Sopenharmony_ci unsigned long ideal_clk, lcd_rate; 42062306a36Sopenharmony_ci u32 clkdiv; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci if (mode->clock == 0) { 42362306a36Sopenharmony_ci DRM_DEV_ERROR(ctx->dev, "Mode has zero clock value.\n"); 42462306a36Sopenharmony_ci return -EINVAL; 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci ideal_clk = mode->clock * 1000; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci if (ctx->i80_if) { 43062306a36Sopenharmony_ci /* 43162306a36Sopenharmony_ci * The frame done interrupt should be occurred prior to the 43262306a36Sopenharmony_ci * next TE signal. 43362306a36Sopenharmony_ci */ 43462306a36Sopenharmony_ci ideal_clk *= 2; 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci lcd_rate = clk_get_rate(ctx->lcd_clk); 43862306a36Sopenharmony_ci if (2 * lcd_rate < ideal_clk) { 43962306a36Sopenharmony_ci DRM_DEV_ERROR(ctx->dev, 44062306a36Sopenharmony_ci "sclk_fimd clock too low(%lu) for requested pixel clock(%lu)\n", 44162306a36Sopenharmony_ci lcd_rate, ideal_clk); 44262306a36Sopenharmony_ci return -EINVAL; 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci /* Find the clock divider value that gets us closest to ideal_clk */ 44662306a36Sopenharmony_ci clkdiv = DIV_ROUND_CLOSEST(lcd_rate, ideal_clk); 44762306a36Sopenharmony_ci if (clkdiv >= 0x200) { 44862306a36Sopenharmony_ci DRM_DEV_ERROR(ctx->dev, "requested pixel clock(%lu) too low\n", 44962306a36Sopenharmony_ci ideal_clk); 45062306a36Sopenharmony_ci return -EINVAL; 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci ctx->clkdiv = (clkdiv < 0x100) ? clkdiv : 0xff; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci return 0; 45662306a36Sopenharmony_ci} 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_cistatic void fimd_setup_trigger(struct fimd_context *ctx) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci void __iomem *timing_base = ctx->regs + ctx->driver_data->timing_base; 46162306a36Sopenharmony_ci u32 trg_type = ctx->driver_data->trg_type; 46262306a36Sopenharmony_ci u32 val = readl(timing_base + TRIGCON); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci val &= ~(TRGMODE_ENABLE); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci if (trg_type == I80_HW_TRG) { 46762306a36Sopenharmony_ci if (ctx->driver_data->has_hw_trigger) 46862306a36Sopenharmony_ci val |= HWTRGEN_ENABLE | HWTRGMASK_ENABLE; 46962306a36Sopenharmony_ci if (ctx->driver_data->has_trigger_per_te) 47062306a36Sopenharmony_ci val |= HWTRIGEN_PER_ENABLE; 47162306a36Sopenharmony_ci } else { 47262306a36Sopenharmony_ci val |= TRGMODE_ENABLE; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci writel(val, timing_base + TRIGCON); 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_cistatic void fimd_commit(struct exynos_drm_crtc *crtc) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci struct fimd_context *ctx = crtc->ctx; 48162306a36Sopenharmony_ci struct drm_display_mode *mode = &crtc->base.state->adjusted_mode; 48262306a36Sopenharmony_ci const struct fimd_driver_data *driver_data = ctx->driver_data; 48362306a36Sopenharmony_ci void *timing_base = ctx->regs + driver_data->timing_base; 48462306a36Sopenharmony_ci u32 val; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci if (ctx->suspended) 48762306a36Sopenharmony_ci return; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci /* nothing to do if we haven't set the mode yet */ 49062306a36Sopenharmony_ci if (mode->htotal == 0 || mode->vtotal == 0) 49162306a36Sopenharmony_ci return; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci if (ctx->i80_if) { 49462306a36Sopenharmony_ci val = ctx->i80ifcon | I80IFEN_ENABLE; 49562306a36Sopenharmony_ci writel(val, timing_base + I80IFCONFAx(0)); 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci /* disable auto frame rate */ 49862306a36Sopenharmony_ci writel(0, timing_base + I80IFCONFBx(0)); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci /* set video type selection to I80 interface */ 50162306a36Sopenharmony_ci if (driver_data->has_vtsel && ctx->sysreg && 50262306a36Sopenharmony_ci regmap_update_bits(ctx->sysreg, 50362306a36Sopenharmony_ci driver_data->lcdblk_offset, 50462306a36Sopenharmony_ci 0x3 << driver_data->lcdblk_vt_shift, 50562306a36Sopenharmony_ci 0x1 << driver_data->lcdblk_vt_shift)) { 50662306a36Sopenharmony_ci DRM_DEV_ERROR(ctx->dev, 50762306a36Sopenharmony_ci "Failed to update sysreg for I80 i/f.\n"); 50862306a36Sopenharmony_ci return; 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci } else { 51162306a36Sopenharmony_ci int vsync_len, vbpd, vfpd, hsync_len, hbpd, hfpd; 51262306a36Sopenharmony_ci u32 vidcon1; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci /* setup polarity values */ 51562306a36Sopenharmony_ci vidcon1 = ctx->vidcon1; 51662306a36Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_NVSYNC) 51762306a36Sopenharmony_ci vidcon1 |= VIDCON1_INV_VSYNC; 51862306a36Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_NHSYNC) 51962306a36Sopenharmony_ci vidcon1 |= VIDCON1_INV_HSYNC; 52062306a36Sopenharmony_ci writel(vidcon1, ctx->regs + driver_data->timing_base + VIDCON1); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci /* setup vertical timing values. */ 52362306a36Sopenharmony_ci vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start; 52462306a36Sopenharmony_ci vbpd = mode->crtc_vtotal - mode->crtc_vsync_end; 52562306a36Sopenharmony_ci vfpd = mode->crtc_vsync_start - mode->crtc_vdisplay; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci val = VIDTCON0_VBPD(vbpd - 1) | 52862306a36Sopenharmony_ci VIDTCON0_VFPD(vfpd - 1) | 52962306a36Sopenharmony_ci VIDTCON0_VSPW(vsync_len - 1); 53062306a36Sopenharmony_ci writel(val, ctx->regs + driver_data->timing_base + VIDTCON0); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci /* setup horizontal timing values. */ 53362306a36Sopenharmony_ci hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start; 53462306a36Sopenharmony_ci hbpd = mode->crtc_htotal - mode->crtc_hsync_end; 53562306a36Sopenharmony_ci hfpd = mode->crtc_hsync_start - mode->crtc_hdisplay; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci val = VIDTCON1_HBPD(hbpd - 1) | 53862306a36Sopenharmony_ci VIDTCON1_HFPD(hfpd - 1) | 53962306a36Sopenharmony_ci VIDTCON1_HSPW(hsync_len - 1); 54062306a36Sopenharmony_ci writel(val, ctx->regs + driver_data->timing_base + VIDTCON1); 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci if (driver_data->has_vidoutcon) 54462306a36Sopenharmony_ci writel(ctx->vidout_con, timing_base + VIDOUT_CON); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci /* set bypass selection */ 54762306a36Sopenharmony_ci if (ctx->sysreg && regmap_update_bits(ctx->sysreg, 54862306a36Sopenharmony_ci driver_data->lcdblk_offset, 54962306a36Sopenharmony_ci 0x1 << driver_data->lcdblk_bypass_shift, 55062306a36Sopenharmony_ci 0x1 << driver_data->lcdblk_bypass_shift)) { 55162306a36Sopenharmony_ci DRM_DEV_ERROR(ctx->dev, 55262306a36Sopenharmony_ci "Failed to update sysreg for bypass setting.\n"); 55362306a36Sopenharmony_ci return; 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci /* TODO: When MIC is enabled for display path, the lcdblk_mic_bypass 55762306a36Sopenharmony_ci * bit should be cleared. 55862306a36Sopenharmony_ci */ 55962306a36Sopenharmony_ci if (driver_data->has_mic_bypass && ctx->sysreg && 56062306a36Sopenharmony_ci regmap_update_bits(ctx->sysreg, 56162306a36Sopenharmony_ci driver_data->lcdblk_offset, 56262306a36Sopenharmony_ci 0x1 << driver_data->lcdblk_mic_bypass_shift, 56362306a36Sopenharmony_ci 0x1 << driver_data->lcdblk_mic_bypass_shift)) { 56462306a36Sopenharmony_ci DRM_DEV_ERROR(ctx->dev, 56562306a36Sopenharmony_ci "Failed to update sysreg for bypass mic.\n"); 56662306a36Sopenharmony_ci return; 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci /* setup horizontal and vertical display size. */ 57062306a36Sopenharmony_ci val = VIDTCON2_LINEVAL(mode->vdisplay - 1) | 57162306a36Sopenharmony_ci VIDTCON2_HOZVAL(mode->hdisplay - 1) | 57262306a36Sopenharmony_ci VIDTCON2_LINEVAL_E(mode->vdisplay - 1) | 57362306a36Sopenharmony_ci VIDTCON2_HOZVAL_E(mode->hdisplay - 1); 57462306a36Sopenharmony_ci writel(val, ctx->regs + driver_data->timing_base + VIDTCON2); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci fimd_setup_trigger(ctx); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci /* 57962306a36Sopenharmony_ci * fields of register with prefix '_F' would be updated 58062306a36Sopenharmony_ci * at vsync(same as dma start) 58162306a36Sopenharmony_ci */ 58262306a36Sopenharmony_ci val = ctx->vidcon0; 58362306a36Sopenharmony_ci val |= VIDCON0_ENVID | VIDCON0_ENVID_F; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci if (ctx->driver_data->has_clksel) 58662306a36Sopenharmony_ci val |= VIDCON0_CLKSEL_LCD; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci if (ctx->clkdiv > 1) 58962306a36Sopenharmony_ci val |= VIDCON0_CLKVAL_F(ctx->clkdiv - 1) | VIDCON0_CLKDIR; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci writel(val, ctx->regs + VIDCON0); 59262306a36Sopenharmony_ci} 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_cistatic void fimd_win_set_bldeq(struct fimd_context *ctx, unsigned int win, 59562306a36Sopenharmony_ci unsigned int alpha, unsigned int pixel_alpha) 59662306a36Sopenharmony_ci{ 59762306a36Sopenharmony_ci u32 mask = BLENDEQ_A_FUNC_F(0xf) | BLENDEQ_B_FUNC_F(0xf); 59862306a36Sopenharmony_ci u32 val = 0; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci switch (pixel_alpha) { 60162306a36Sopenharmony_ci case DRM_MODE_BLEND_PIXEL_NONE: 60262306a36Sopenharmony_ci case DRM_MODE_BLEND_COVERAGE: 60362306a36Sopenharmony_ci val |= BLENDEQ_A_FUNC_F(BLENDEQ_ALPHA_A); 60462306a36Sopenharmony_ci val |= BLENDEQ_B_FUNC_F(BLENDEQ_ONE_MINUS_ALPHA_A); 60562306a36Sopenharmony_ci break; 60662306a36Sopenharmony_ci case DRM_MODE_BLEND_PREMULTI: 60762306a36Sopenharmony_ci default: 60862306a36Sopenharmony_ci if (alpha != DRM_BLEND_ALPHA_OPAQUE) { 60962306a36Sopenharmony_ci val |= BLENDEQ_A_FUNC_F(BLENDEQ_ALPHA0); 61062306a36Sopenharmony_ci val |= BLENDEQ_B_FUNC_F(BLENDEQ_ONE_MINUS_ALPHA_A); 61162306a36Sopenharmony_ci } else { 61262306a36Sopenharmony_ci val |= BLENDEQ_A_FUNC_F(BLENDEQ_ONE); 61362306a36Sopenharmony_ci val |= BLENDEQ_B_FUNC_F(BLENDEQ_ONE_MINUS_ALPHA_A); 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci break; 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci fimd_set_bits(ctx, BLENDEQx(win), mask, val); 61862306a36Sopenharmony_ci} 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_cistatic void fimd_win_set_bldmod(struct fimd_context *ctx, unsigned int win, 62162306a36Sopenharmony_ci unsigned int alpha, unsigned int pixel_alpha) 62262306a36Sopenharmony_ci{ 62362306a36Sopenharmony_ci u32 win_alpha_l = (alpha >> 8) & 0xf; 62462306a36Sopenharmony_ci u32 win_alpha_h = alpha >> 12; 62562306a36Sopenharmony_ci u32 val = 0; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci switch (pixel_alpha) { 62862306a36Sopenharmony_ci case DRM_MODE_BLEND_PIXEL_NONE: 62962306a36Sopenharmony_ci break; 63062306a36Sopenharmony_ci case DRM_MODE_BLEND_COVERAGE: 63162306a36Sopenharmony_ci case DRM_MODE_BLEND_PREMULTI: 63262306a36Sopenharmony_ci default: 63362306a36Sopenharmony_ci val |= WINCON1_ALPHA_SEL; 63462306a36Sopenharmony_ci val |= WINCON1_BLD_PIX; 63562306a36Sopenharmony_ci val |= WINCON1_ALPHA_MUL; 63662306a36Sopenharmony_ci break; 63762306a36Sopenharmony_ci } 63862306a36Sopenharmony_ci fimd_set_bits(ctx, WINCON(win), WINCONx_BLEND_MODE_MASK, val); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci /* OSD alpha */ 64162306a36Sopenharmony_ci val = VIDISD14C_ALPHA0_R(win_alpha_h) | 64262306a36Sopenharmony_ci VIDISD14C_ALPHA0_G(win_alpha_h) | 64362306a36Sopenharmony_ci VIDISD14C_ALPHA0_B(win_alpha_h) | 64462306a36Sopenharmony_ci VIDISD14C_ALPHA1_R(0x0) | 64562306a36Sopenharmony_ci VIDISD14C_ALPHA1_G(0x0) | 64662306a36Sopenharmony_ci VIDISD14C_ALPHA1_B(0x0); 64762306a36Sopenharmony_ci writel(val, ctx->regs + VIDOSD_C(win)); 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci val = VIDW_ALPHA_R(win_alpha_l) | VIDW_ALPHA_G(win_alpha_l) | 65062306a36Sopenharmony_ci VIDW_ALPHA_B(win_alpha_l); 65162306a36Sopenharmony_ci writel(val, ctx->regs + VIDWnALPHA0(win)); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci val = VIDW_ALPHA_R(0x0) | VIDW_ALPHA_G(0x0) | 65462306a36Sopenharmony_ci VIDW_ALPHA_B(0x0); 65562306a36Sopenharmony_ci writel(val, ctx->regs + VIDWnALPHA1(win)); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci fimd_set_bits(ctx, BLENDCON, BLENDCON_NEW_MASK, 65862306a36Sopenharmony_ci BLENDCON_NEW_8BIT_ALPHA_VALUE); 65962306a36Sopenharmony_ci} 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_cistatic void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win, 66262306a36Sopenharmony_ci struct drm_framebuffer *fb, int width) 66362306a36Sopenharmony_ci{ 66462306a36Sopenharmony_ci struct exynos_drm_plane *plane = &ctx->planes[win]; 66562306a36Sopenharmony_ci struct exynos_drm_plane_state *state = 66662306a36Sopenharmony_ci to_exynos_plane_state(plane->base.state); 66762306a36Sopenharmony_ci uint32_t pixel_format = fb->format->format; 66862306a36Sopenharmony_ci unsigned int alpha = state->base.alpha; 66962306a36Sopenharmony_ci u32 val = WINCONx_ENWIN; 67062306a36Sopenharmony_ci unsigned int pixel_alpha; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci if (fb->format->has_alpha) 67362306a36Sopenharmony_ci pixel_alpha = state->base.pixel_blend_mode; 67462306a36Sopenharmony_ci else 67562306a36Sopenharmony_ci pixel_alpha = DRM_MODE_BLEND_PIXEL_NONE; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci /* 67862306a36Sopenharmony_ci * In case of s3c64xx, window 0 doesn't support alpha channel. 67962306a36Sopenharmony_ci * So the request format is ARGB8888 then change it to XRGB8888. 68062306a36Sopenharmony_ci */ 68162306a36Sopenharmony_ci if (ctx->driver_data->has_limited_fmt && !win) { 68262306a36Sopenharmony_ci if (pixel_format == DRM_FORMAT_ARGB8888) 68362306a36Sopenharmony_ci pixel_format = DRM_FORMAT_XRGB8888; 68462306a36Sopenharmony_ci } 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci switch (pixel_format) { 68762306a36Sopenharmony_ci case DRM_FORMAT_C8: 68862306a36Sopenharmony_ci val |= WINCON0_BPPMODE_8BPP_PALETTE; 68962306a36Sopenharmony_ci val |= WINCONx_BURSTLEN_8WORD; 69062306a36Sopenharmony_ci val |= WINCONx_BYTSWP; 69162306a36Sopenharmony_ci break; 69262306a36Sopenharmony_ci case DRM_FORMAT_XRGB1555: 69362306a36Sopenharmony_ci case DRM_FORMAT_XBGR1555: 69462306a36Sopenharmony_ci val |= WINCON0_BPPMODE_16BPP_1555; 69562306a36Sopenharmony_ci val |= WINCONx_HAWSWP; 69662306a36Sopenharmony_ci val |= WINCONx_BURSTLEN_16WORD; 69762306a36Sopenharmony_ci break; 69862306a36Sopenharmony_ci case DRM_FORMAT_RGB565: 69962306a36Sopenharmony_ci case DRM_FORMAT_BGR565: 70062306a36Sopenharmony_ci val |= WINCON0_BPPMODE_16BPP_565; 70162306a36Sopenharmony_ci val |= WINCONx_HAWSWP; 70262306a36Sopenharmony_ci val |= WINCONx_BURSTLEN_16WORD; 70362306a36Sopenharmony_ci break; 70462306a36Sopenharmony_ci case DRM_FORMAT_XRGB8888: 70562306a36Sopenharmony_ci case DRM_FORMAT_XBGR8888: 70662306a36Sopenharmony_ci val |= WINCON0_BPPMODE_24BPP_888; 70762306a36Sopenharmony_ci val |= WINCONx_WSWP; 70862306a36Sopenharmony_ci val |= WINCONx_BURSTLEN_16WORD; 70962306a36Sopenharmony_ci break; 71062306a36Sopenharmony_ci case DRM_FORMAT_ARGB8888: 71162306a36Sopenharmony_ci case DRM_FORMAT_ABGR8888: 71262306a36Sopenharmony_ci default: 71362306a36Sopenharmony_ci val |= WINCON1_BPPMODE_25BPP_A1888; 71462306a36Sopenharmony_ci val |= WINCONx_WSWP; 71562306a36Sopenharmony_ci val |= WINCONx_BURSTLEN_16WORD; 71662306a36Sopenharmony_ci break; 71762306a36Sopenharmony_ci } 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci switch (pixel_format) { 72062306a36Sopenharmony_ci case DRM_FORMAT_XBGR1555: 72162306a36Sopenharmony_ci case DRM_FORMAT_XBGR8888: 72262306a36Sopenharmony_ci case DRM_FORMAT_ABGR8888: 72362306a36Sopenharmony_ci case DRM_FORMAT_BGR565: 72462306a36Sopenharmony_ci writel(WIN_RGB_ORDER_REVERSE, ctx->regs + WIN_RGB_ORDER(win)); 72562306a36Sopenharmony_ci break; 72662306a36Sopenharmony_ci default: 72762306a36Sopenharmony_ci writel(WIN_RGB_ORDER_FORWARD, ctx->regs + WIN_RGB_ORDER(win)); 72862306a36Sopenharmony_ci break; 72962306a36Sopenharmony_ci } 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci /* 73262306a36Sopenharmony_ci * Setting dma-burst to 16Word causes permanent tearing for very small 73362306a36Sopenharmony_ci * buffers, e.g. cursor buffer. Burst Mode switching which based on 73462306a36Sopenharmony_ci * plane size is not recommended as plane size varies alot towards the 73562306a36Sopenharmony_ci * end of the screen and rapid movement causes unstable DMA, but it is 73662306a36Sopenharmony_ci * still better to change dma-burst than displaying garbage. 73762306a36Sopenharmony_ci */ 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci if (width < MIN_FB_WIDTH_FOR_16WORD_BURST) { 74062306a36Sopenharmony_ci val &= ~WINCONx_BURSTLEN_MASK; 74162306a36Sopenharmony_ci val |= WINCONx_BURSTLEN_4WORD; 74262306a36Sopenharmony_ci } 74362306a36Sopenharmony_ci fimd_set_bits(ctx, WINCON(win), ~WINCONx_BLEND_MODE_MASK, val); 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci /* hardware window 0 doesn't support alpha channel. */ 74662306a36Sopenharmony_ci if (win != 0) { 74762306a36Sopenharmony_ci fimd_win_set_bldmod(ctx, win, alpha, pixel_alpha); 74862306a36Sopenharmony_ci fimd_win_set_bldeq(ctx, win, alpha, pixel_alpha); 74962306a36Sopenharmony_ci } 75062306a36Sopenharmony_ci} 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_cistatic void fimd_win_set_colkey(struct fimd_context *ctx, unsigned int win) 75362306a36Sopenharmony_ci{ 75462306a36Sopenharmony_ci unsigned int keycon0 = 0, keycon1 = 0; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci keycon0 = ~(WxKEYCON0_KEYBL_EN | WxKEYCON0_KEYEN_F | 75762306a36Sopenharmony_ci WxKEYCON0_DIRCON) | WxKEYCON0_COMPKEY(0); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci keycon1 = WxKEYCON1_COLVAL(0xffffffff); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci writel(keycon0, ctx->regs + WKEYCON0_BASE(win)); 76262306a36Sopenharmony_ci writel(keycon1, ctx->regs + WKEYCON1_BASE(win)); 76362306a36Sopenharmony_ci} 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci/** 76662306a36Sopenharmony_ci * fimd_shadow_protect_win() - disable updating values from shadow registers at vsync 76762306a36Sopenharmony_ci * 76862306a36Sopenharmony_ci * @ctx: local driver data 76962306a36Sopenharmony_ci * @win: window to protect registers for 77062306a36Sopenharmony_ci * @protect: 1 to protect (disable updates) 77162306a36Sopenharmony_ci */ 77262306a36Sopenharmony_cistatic void fimd_shadow_protect_win(struct fimd_context *ctx, 77362306a36Sopenharmony_ci unsigned int win, bool protect) 77462306a36Sopenharmony_ci{ 77562306a36Sopenharmony_ci u32 reg, bits, val; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci /* 77862306a36Sopenharmony_ci * SHADOWCON/PRTCON register is used for enabling timing. 77962306a36Sopenharmony_ci * 78062306a36Sopenharmony_ci * for example, once only width value of a register is set, 78162306a36Sopenharmony_ci * if the dma is started then fimd hardware could malfunction so 78262306a36Sopenharmony_ci * with protect window setting, the register fields with prefix '_F' 78362306a36Sopenharmony_ci * wouldn't be updated at vsync also but updated once unprotect window 78462306a36Sopenharmony_ci * is set. 78562306a36Sopenharmony_ci */ 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci if (ctx->driver_data->has_shadowcon) { 78862306a36Sopenharmony_ci reg = SHADOWCON; 78962306a36Sopenharmony_ci bits = SHADOWCON_WINx_PROTECT(win); 79062306a36Sopenharmony_ci } else { 79162306a36Sopenharmony_ci reg = PRTCON; 79262306a36Sopenharmony_ci bits = PRTCON_PROTECT; 79362306a36Sopenharmony_ci } 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci val = readl(ctx->regs + reg); 79662306a36Sopenharmony_ci if (protect) 79762306a36Sopenharmony_ci val |= bits; 79862306a36Sopenharmony_ci else 79962306a36Sopenharmony_ci val &= ~bits; 80062306a36Sopenharmony_ci writel(val, ctx->regs + reg); 80162306a36Sopenharmony_ci} 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_cistatic void fimd_atomic_begin(struct exynos_drm_crtc *crtc) 80462306a36Sopenharmony_ci{ 80562306a36Sopenharmony_ci struct fimd_context *ctx = crtc->ctx; 80662306a36Sopenharmony_ci int i; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci if (ctx->suspended) 80962306a36Sopenharmony_ci return; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci for (i = 0; i < WINDOWS_NR; i++) 81262306a36Sopenharmony_ci fimd_shadow_protect_win(ctx, i, true); 81362306a36Sopenharmony_ci} 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_cistatic void fimd_atomic_flush(struct exynos_drm_crtc *crtc) 81662306a36Sopenharmony_ci{ 81762306a36Sopenharmony_ci struct fimd_context *ctx = crtc->ctx; 81862306a36Sopenharmony_ci int i; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci if (ctx->suspended) 82162306a36Sopenharmony_ci return; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci for (i = 0; i < WINDOWS_NR; i++) 82462306a36Sopenharmony_ci fimd_shadow_protect_win(ctx, i, false); 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci exynos_crtc_handle_event(crtc); 82762306a36Sopenharmony_ci} 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_cistatic void fimd_update_plane(struct exynos_drm_crtc *crtc, 83062306a36Sopenharmony_ci struct exynos_drm_plane *plane) 83162306a36Sopenharmony_ci{ 83262306a36Sopenharmony_ci struct exynos_drm_plane_state *state = 83362306a36Sopenharmony_ci to_exynos_plane_state(plane->base.state); 83462306a36Sopenharmony_ci struct fimd_context *ctx = crtc->ctx; 83562306a36Sopenharmony_ci struct drm_framebuffer *fb = state->base.fb; 83662306a36Sopenharmony_ci dma_addr_t dma_addr; 83762306a36Sopenharmony_ci unsigned long val, size, offset; 83862306a36Sopenharmony_ci unsigned int last_x, last_y, buf_offsize, line_size; 83962306a36Sopenharmony_ci unsigned int win = plane->index; 84062306a36Sopenharmony_ci unsigned int cpp = fb->format->cpp[0]; 84162306a36Sopenharmony_ci unsigned int pitch = fb->pitches[0]; 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci if (ctx->suspended) 84462306a36Sopenharmony_ci return; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci offset = state->src.x * cpp; 84762306a36Sopenharmony_ci offset += state->src.y * pitch; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci /* buffer start address */ 85062306a36Sopenharmony_ci dma_addr = exynos_drm_fb_dma_addr(fb, 0) + offset; 85162306a36Sopenharmony_ci val = (unsigned long)dma_addr; 85262306a36Sopenharmony_ci writel(val, ctx->regs + VIDWx_BUF_START(win, 0)); 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci /* buffer end address */ 85562306a36Sopenharmony_ci size = pitch * state->crtc.h; 85662306a36Sopenharmony_ci val = (unsigned long)(dma_addr + size); 85762306a36Sopenharmony_ci writel(val, ctx->regs + VIDWx_BUF_END(win, 0)); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci DRM_DEV_DEBUG_KMS(ctx->dev, 86062306a36Sopenharmony_ci "start addr = 0x%lx, end addr = 0x%lx, size = 0x%lx\n", 86162306a36Sopenharmony_ci (unsigned long)dma_addr, val, size); 86262306a36Sopenharmony_ci DRM_DEV_DEBUG_KMS(ctx->dev, "ovl_width = %d, ovl_height = %d\n", 86362306a36Sopenharmony_ci state->crtc.w, state->crtc.h); 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci /* buffer size */ 86662306a36Sopenharmony_ci buf_offsize = pitch - (state->crtc.w * cpp); 86762306a36Sopenharmony_ci line_size = state->crtc.w * cpp; 86862306a36Sopenharmony_ci val = VIDW_BUF_SIZE_OFFSET(buf_offsize) | 86962306a36Sopenharmony_ci VIDW_BUF_SIZE_PAGEWIDTH(line_size) | 87062306a36Sopenharmony_ci VIDW_BUF_SIZE_OFFSET_E(buf_offsize) | 87162306a36Sopenharmony_ci VIDW_BUF_SIZE_PAGEWIDTH_E(line_size); 87262306a36Sopenharmony_ci writel(val, ctx->regs + VIDWx_BUF_SIZE(win, 0)); 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci /* OSD position */ 87562306a36Sopenharmony_ci val = VIDOSDxA_TOPLEFT_X(state->crtc.x) | 87662306a36Sopenharmony_ci VIDOSDxA_TOPLEFT_Y(state->crtc.y) | 87762306a36Sopenharmony_ci VIDOSDxA_TOPLEFT_X_E(state->crtc.x) | 87862306a36Sopenharmony_ci VIDOSDxA_TOPLEFT_Y_E(state->crtc.y); 87962306a36Sopenharmony_ci writel(val, ctx->regs + VIDOSD_A(win)); 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci last_x = state->crtc.x + state->crtc.w; 88262306a36Sopenharmony_ci if (last_x) 88362306a36Sopenharmony_ci last_x--; 88462306a36Sopenharmony_ci last_y = state->crtc.y + state->crtc.h; 88562306a36Sopenharmony_ci if (last_y) 88662306a36Sopenharmony_ci last_y--; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci val = VIDOSDxB_BOTRIGHT_X(last_x) | VIDOSDxB_BOTRIGHT_Y(last_y) | 88962306a36Sopenharmony_ci VIDOSDxB_BOTRIGHT_X_E(last_x) | VIDOSDxB_BOTRIGHT_Y_E(last_y); 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci writel(val, ctx->regs + VIDOSD_B(win)); 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci DRM_DEV_DEBUG_KMS(ctx->dev, 89462306a36Sopenharmony_ci "osd pos: tx = %d, ty = %d, bx = %d, by = %d\n", 89562306a36Sopenharmony_ci state->crtc.x, state->crtc.y, last_x, last_y); 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci /* OSD size */ 89862306a36Sopenharmony_ci if (win != 3 && win != 4) { 89962306a36Sopenharmony_ci u32 offset = VIDOSD_D(win); 90062306a36Sopenharmony_ci if (win == 0) 90162306a36Sopenharmony_ci offset = VIDOSD_C(win); 90262306a36Sopenharmony_ci val = state->crtc.w * state->crtc.h; 90362306a36Sopenharmony_ci writel(val, ctx->regs + offset); 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci DRM_DEV_DEBUG_KMS(ctx->dev, "osd size = 0x%x\n", 90662306a36Sopenharmony_ci (unsigned int)val); 90762306a36Sopenharmony_ci } 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci fimd_win_set_pixfmt(ctx, win, fb, state->src.w); 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci /* hardware window 0 doesn't support color key. */ 91262306a36Sopenharmony_ci if (win != 0) 91362306a36Sopenharmony_ci fimd_win_set_colkey(ctx, win); 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci fimd_enable_video_output(ctx, win, true); 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci if (ctx->driver_data->has_shadowcon) 91862306a36Sopenharmony_ci fimd_enable_shadow_channel_path(ctx, win, true); 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci if (ctx->i80_if) 92162306a36Sopenharmony_ci atomic_set(&ctx->win_updated, 1); 92262306a36Sopenharmony_ci} 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_cistatic void fimd_disable_plane(struct exynos_drm_crtc *crtc, 92562306a36Sopenharmony_ci struct exynos_drm_plane *plane) 92662306a36Sopenharmony_ci{ 92762306a36Sopenharmony_ci struct fimd_context *ctx = crtc->ctx; 92862306a36Sopenharmony_ci unsigned int win = plane->index; 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci if (ctx->suspended) 93162306a36Sopenharmony_ci return; 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci fimd_enable_video_output(ctx, win, false); 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci if (ctx->driver_data->has_shadowcon) 93662306a36Sopenharmony_ci fimd_enable_shadow_channel_path(ctx, win, false); 93762306a36Sopenharmony_ci} 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_cistatic void fimd_atomic_enable(struct exynos_drm_crtc *crtc) 94062306a36Sopenharmony_ci{ 94162306a36Sopenharmony_ci struct fimd_context *ctx = crtc->ctx; 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci if (!ctx->suspended) 94462306a36Sopenharmony_ci return; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci ctx->suspended = false; 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci if (pm_runtime_resume_and_get(ctx->dev) < 0) { 94962306a36Sopenharmony_ci dev_warn(ctx->dev, "failed to enable FIMD device.\n"); 95062306a36Sopenharmony_ci return; 95162306a36Sopenharmony_ci } 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci /* if vblank was enabled status, enable it again. */ 95462306a36Sopenharmony_ci if (test_and_clear_bit(0, &ctx->irq_flags)) 95562306a36Sopenharmony_ci fimd_enable_vblank(ctx->crtc); 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci fimd_commit(ctx->crtc); 95862306a36Sopenharmony_ci} 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_cistatic void fimd_atomic_disable(struct exynos_drm_crtc *crtc) 96162306a36Sopenharmony_ci{ 96262306a36Sopenharmony_ci struct fimd_context *ctx = crtc->ctx; 96362306a36Sopenharmony_ci int i; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci if (ctx->suspended) 96662306a36Sopenharmony_ci return; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci /* 96962306a36Sopenharmony_ci * We need to make sure that all windows are disabled before we 97062306a36Sopenharmony_ci * suspend that connector. Otherwise we might try to scan from 97162306a36Sopenharmony_ci * a destroyed buffer later. 97262306a36Sopenharmony_ci */ 97362306a36Sopenharmony_ci for (i = 0; i < WINDOWS_NR; i++) 97462306a36Sopenharmony_ci fimd_disable_plane(crtc, &ctx->planes[i]); 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci fimd_enable_vblank(crtc); 97762306a36Sopenharmony_ci fimd_wait_for_vblank(crtc); 97862306a36Sopenharmony_ci fimd_disable_vblank(crtc); 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci writel(0, ctx->regs + VIDCON0); 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci pm_runtime_put_sync(ctx->dev); 98362306a36Sopenharmony_ci ctx->suspended = true; 98462306a36Sopenharmony_ci} 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_cistatic void fimd_trigger(struct device *dev) 98762306a36Sopenharmony_ci{ 98862306a36Sopenharmony_ci struct fimd_context *ctx = dev_get_drvdata(dev); 98962306a36Sopenharmony_ci const struct fimd_driver_data *driver_data = ctx->driver_data; 99062306a36Sopenharmony_ci void *timing_base = ctx->regs + driver_data->timing_base; 99162306a36Sopenharmony_ci u32 reg; 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci /* 99462306a36Sopenharmony_ci * Skips triggering if in triggering state, because multiple triggering 99562306a36Sopenharmony_ci * requests can cause panel reset. 99662306a36Sopenharmony_ci */ 99762306a36Sopenharmony_ci if (atomic_read(&ctx->triggering)) 99862306a36Sopenharmony_ci return; 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci /* Enters triggering mode */ 100162306a36Sopenharmony_ci atomic_set(&ctx->triggering, 1); 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci reg = readl(timing_base + TRIGCON); 100462306a36Sopenharmony_ci reg |= (TRGMODE_ENABLE | SWTRGCMD_ENABLE); 100562306a36Sopenharmony_ci writel(reg, timing_base + TRIGCON); 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci /* 100862306a36Sopenharmony_ci * Exits triggering mode if vblank is not enabled yet, because when the 100962306a36Sopenharmony_ci * VIDINTCON0 register is not set, it can not exit from triggering mode. 101062306a36Sopenharmony_ci */ 101162306a36Sopenharmony_ci if (!test_bit(0, &ctx->irq_flags)) 101262306a36Sopenharmony_ci atomic_set(&ctx->triggering, 0); 101362306a36Sopenharmony_ci} 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_cistatic void fimd_te_handler(struct exynos_drm_crtc *crtc) 101662306a36Sopenharmony_ci{ 101762306a36Sopenharmony_ci struct fimd_context *ctx = crtc->ctx; 101862306a36Sopenharmony_ci u32 trg_type = ctx->driver_data->trg_type; 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci /* Checks the crtc is detached already from encoder */ 102162306a36Sopenharmony_ci if (!ctx->drm_dev) 102262306a36Sopenharmony_ci return; 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci if (trg_type == I80_HW_TRG) 102562306a36Sopenharmony_ci goto out; 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci /* 102862306a36Sopenharmony_ci * If there is a page flip request, triggers and handles the page flip 102962306a36Sopenharmony_ci * event so that current fb can be updated into panel GRAM. 103062306a36Sopenharmony_ci */ 103162306a36Sopenharmony_ci if (atomic_add_unless(&ctx->win_updated, -1, 0)) 103262306a36Sopenharmony_ci fimd_trigger(ctx->dev); 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ciout: 103562306a36Sopenharmony_ci /* Wakes up vsync event queue */ 103662306a36Sopenharmony_ci if (atomic_read(&ctx->wait_vsync_event)) { 103762306a36Sopenharmony_ci atomic_set(&ctx->wait_vsync_event, 0); 103862306a36Sopenharmony_ci wake_up(&ctx->wait_vsync_queue); 103962306a36Sopenharmony_ci } 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci if (test_bit(0, &ctx->irq_flags)) 104262306a36Sopenharmony_ci drm_crtc_handle_vblank(&ctx->crtc->base); 104362306a36Sopenharmony_ci} 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_cistatic void fimd_dp_clock_enable(struct exynos_drm_clk *clk, bool enable) 104662306a36Sopenharmony_ci{ 104762306a36Sopenharmony_ci struct fimd_context *ctx = container_of(clk, struct fimd_context, 104862306a36Sopenharmony_ci dp_clk); 104962306a36Sopenharmony_ci u32 val = enable ? DP_MIE_CLK_DP_ENABLE : DP_MIE_CLK_DISABLE; 105062306a36Sopenharmony_ci writel(val, ctx->regs + DP_MIE_CLKCON); 105162306a36Sopenharmony_ci} 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_cistatic const struct exynos_drm_crtc_ops fimd_crtc_ops = { 105462306a36Sopenharmony_ci .atomic_enable = fimd_atomic_enable, 105562306a36Sopenharmony_ci .atomic_disable = fimd_atomic_disable, 105662306a36Sopenharmony_ci .enable_vblank = fimd_enable_vblank, 105762306a36Sopenharmony_ci .disable_vblank = fimd_disable_vblank, 105862306a36Sopenharmony_ci .atomic_begin = fimd_atomic_begin, 105962306a36Sopenharmony_ci .update_plane = fimd_update_plane, 106062306a36Sopenharmony_ci .disable_plane = fimd_disable_plane, 106162306a36Sopenharmony_ci .atomic_flush = fimd_atomic_flush, 106262306a36Sopenharmony_ci .atomic_check = fimd_atomic_check, 106362306a36Sopenharmony_ci .te_handler = fimd_te_handler, 106462306a36Sopenharmony_ci}; 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_cistatic irqreturn_t fimd_irq_handler(int irq, void *dev_id) 106762306a36Sopenharmony_ci{ 106862306a36Sopenharmony_ci struct fimd_context *ctx = (struct fimd_context *)dev_id; 106962306a36Sopenharmony_ci u32 val, clear_bit; 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci val = readl(ctx->regs + VIDINTCON1); 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci clear_bit = ctx->i80_if ? VIDINTCON1_INT_I80 : VIDINTCON1_INT_FRAME; 107462306a36Sopenharmony_ci if (val & clear_bit) 107562306a36Sopenharmony_ci writel(clear_bit, ctx->regs + VIDINTCON1); 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci /* check the crtc is detached already from encoder */ 107862306a36Sopenharmony_ci if (!ctx->drm_dev) 107962306a36Sopenharmony_ci goto out; 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci if (!ctx->i80_if) 108262306a36Sopenharmony_ci drm_crtc_handle_vblank(&ctx->crtc->base); 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci if (ctx->i80_if) { 108562306a36Sopenharmony_ci /* Exits triggering mode */ 108662306a36Sopenharmony_ci atomic_set(&ctx->triggering, 0); 108762306a36Sopenharmony_ci } else { 108862306a36Sopenharmony_ci /* set wait vsync event to zero and wake up queue. */ 108962306a36Sopenharmony_ci if (atomic_read(&ctx->wait_vsync_event)) { 109062306a36Sopenharmony_ci atomic_set(&ctx->wait_vsync_event, 0); 109162306a36Sopenharmony_ci wake_up(&ctx->wait_vsync_queue); 109262306a36Sopenharmony_ci } 109362306a36Sopenharmony_ci } 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ciout: 109662306a36Sopenharmony_ci return IRQ_HANDLED; 109762306a36Sopenharmony_ci} 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_cistatic int fimd_bind(struct device *dev, struct device *master, void *data) 110062306a36Sopenharmony_ci{ 110162306a36Sopenharmony_ci struct fimd_context *ctx = dev_get_drvdata(dev); 110262306a36Sopenharmony_ci struct drm_device *drm_dev = data; 110362306a36Sopenharmony_ci struct exynos_drm_plane *exynos_plane; 110462306a36Sopenharmony_ci unsigned int i; 110562306a36Sopenharmony_ci int ret; 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci ctx->drm_dev = drm_dev; 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci for (i = 0; i < WINDOWS_NR; i++) { 111062306a36Sopenharmony_ci if (ctx->driver_data->has_bgr_support) { 111162306a36Sopenharmony_ci ctx->configs[i].pixel_formats = fimd_extended_formats; 111262306a36Sopenharmony_ci ctx->configs[i].num_pixel_formats = ARRAY_SIZE(fimd_extended_formats); 111362306a36Sopenharmony_ci } else { 111462306a36Sopenharmony_ci ctx->configs[i].pixel_formats = fimd_formats; 111562306a36Sopenharmony_ci ctx->configs[i].num_pixel_formats = ARRAY_SIZE(fimd_formats); 111662306a36Sopenharmony_ci } 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci ctx->configs[i].zpos = i; 111962306a36Sopenharmony_ci ctx->configs[i].type = fimd_win_types[i]; 112062306a36Sopenharmony_ci ctx->configs[i].capabilities = capabilities[i]; 112162306a36Sopenharmony_ci ret = exynos_plane_init(drm_dev, &ctx->planes[i], i, 112262306a36Sopenharmony_ci &ctx->configs[i]); 112362306a36Sopenharmony_ci if (ret) 112462306a36Sopenharmony_ci return ret; 112562306a36Sopenharmony_ci } 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci exynos_plane = &ctx->planes[DEFAULT_WIN]; 112862306a36Sopenharmony_ci ctx->crtc = exynos_drm_crtc_create(drm_dev, &exynos_plane->base, 112962306a36Sopenharmony_ci EXYNOS_DISPLAY_TYPE_LCD, &fimd_crtc_ops, ctx); 113062306a36Sopenharmony_ci if (IS_ERR(ctx->crtc)) 113162306a36Sopenharmony_ci return PTR_ERR(ctx->crtc); 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci if (ctx->driver_data->has_dp_clk) { 113462306a36Sopenharmony_ci ctx->dp_clk.enable = fimd_dp_clock_enable; 113562306a36Sopenharmony_ci ctx->crtc->pipe_clk = &ctx->dp_clk; 113662306a36Sopenharmony_ci } 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci if (ctx->encoder) 113962306a36Sopenharmony_ci exynos_dpi_bind(drm_dev, ctx->encoder); 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci if (is_drm_iommu_supported(drm_dev)) { 114262306a36Sopenharmony_ci int ret; 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci ret = fimd_clear_channels(ctx->crtc); 114562306a36Sopenharmony_ci if (ret < 0) 114662306a36Sopenharmony_ci return ret; 114762306a36Sopenharmony_ci } 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci return exynos_drm_register_dma(drm_dev, dev, &ctx->dma_priv); 115062306a36Sopenharmony_ci} 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_cistatic void fimd_unbind(struct device *dev, struct device *master, 115362306a36Sopenharmony_ci void *data) 115462306a36Sopenharmony_ci{ 115562306a36Sopenharmony_ci struct fimd_context *ctx = dev_get_drvdata(dev); 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci fimd_atomic_disable(ctx->crtc); 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci exynos_drm_unregister_dma(ctx->drm_dev, ctx->dev, &ctx->dma_priv); 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci if (ctx->encoder) 116262306a36Sopenharmony_ci exynos_dpi_remove(ctx->encoder); 116362306a36Sopenharmony_ci} 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_cistatic const struct component_ops fimd_component_ops = { 116662306a36Sopenharmony_ci .bind = fimd_bind, 116762306a36Sopenharmony_ci .unbind = fimd_unbind, 116862306a36Sopenharmony_ci}; 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_cistatic int fimd_probe(struct platform_device *pdev) 117162306a36Sopenharmony_ci{ 117262306a36Sopenharmony_ci struct device *dev = &pdev->dev; 117362306a36Sopenharmony_ci struct fimd_context *ctx; 117462306a36Sopenharmony_ci struct device_node *i80_if_timings; 117562306a36Sopenharmony_ci int ret; 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci if (!dev->of_node) 117862306a36Sopenharmony_ci return -ENODEV; 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); 118162306a36Sopenharmony_ci if (!ctx) 118262306a36Sopenharmony_ci return -ENOMEM; 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci ctx->dev = dev; 118562306a36Sopenharmony_ci ctx->suspended = true; 118662306a36Sopenharmony_ci ctx->driver_data = of_device_get_match_data(dev); 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci if (of_property_read_bool(dev->of_node, "samsung,invert-vden")) 118962306a36Sopenharmony_ci ctx->vidcon1 |= VIDCON1_INV_VDEN; 119062306a36Sopenharmony_ci if (of_property_read_bool(dev->of_node, "samsung,invert-vclk")) 119162306a36Sopenharmony_ci ctx->vidcon1 |= VIDCON1_INV_VCLK; 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci i80_if_timings = of_get_child_by_name(dev->of_node, "i80-if-timings"); 119462306a36Sopenharmony_ci if (i80_if_timings) { 119562306a36Sopenharmony_ci u32 val; 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci ctx->i80_if = true; 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci if (ctx->driver_data->has_vidoutcon) 120062306a36Sopenharmony_ci ctx->vidout_con |= VIDOUT_CON_F_I80_LDI0; 120162306a36Sopenharmony_ci else 120262306a36Sopenharmony_ci ctx->vidcon0 |= VIDCON0_VIDOUT_I80_LDI0; 120362306a36Sopenharmony_ci /* 120462306a36Sopenharmony_ci * The user manual describes that this "DSI_EN" bit is required 120562306a36Sopenharmony_ci * to enable I80 24-bit data interface. 120662306a36Sopenharmony_ci */ 120762306a36Sopenharmony_ci ctx->vidcon0 |= VIDCON0_DSI_EN; 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci if (of_property_read_u32(i80_if_timings, "cs-setup", &val)) 121062306a36Sopenharmony_ci val = 0; 121162306a36Sopenharmony_ci ctx->i80ifcon = LCD_CS_SETUP(val); 121262306a36Sopenharmony_ci if (of_property_read_u32(i80_if_timings, "wr-setup", &val)) 121362306a36Sopenharmony_ci val = 0; 121462306a36Sopenharmony_ci ctx->i80ifcon |= LCD_WR_SETUP(val); 121562306a36Sopenharmony_ci if (of_property_read_u32(i80_if_timings, "wr-active", &val)) 121662306a36Sopenharmony_ci val = 1; 121762306a36Sopenharmony_ci ctx->i80ifcon |= LCD_WR_ACTIVE(val); 121862306a36Sopenharmony_ci if (of_property_read_u32(i80_if_timings, "wr-hold", &val)) 121962306a36Sopenharmony_ci val = 0; 122062306a36Sopenharmony_ci ctx->i80ifcon |= LCD_WR_HOLD(val); 122162306a36Sopenharmony_ci } 122262306a36Sopenharmony_ci of_node_put(i80_if_timings); 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci ctx->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node, 122562306a36Sopenharmony_ci "samsung,sysreg"); 122662306a36Sopenharmony_ci if (IS_ERR(ctx->sysreg)) { 122762306a36Sopenharmony_ci dev_warn(dev, "failed to get system register.\n"); 122862306a36Sopenharmony_ci ctx->sysreg = NULL; 122962306a36Sopenharmony_ci } 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci ctx->bus_clk = devm_clk_get(dev, "fimd"); 123262306a36Sopenharmony_ci if (IS_ERR(ctx->bus_clk)) { 123362306a36Sopenharmony_ci dev_err(dev, "failed to get bus clock\n"); 123462306a36Sopenharmony_ci return PTR_ERR(ctx->bus_clk); 123562306a36Sopenharmony_ci } 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci ctx->lcd_clk = devm_clk_get(dev, "sclk_fimd"); 123862306a36Sopenharmony_ci if (IS_ERR(ctx->lcd_clk)) { 123962306a36Sopenharmony_ci dev_err(dev, "failed to get lcd clock\n"); 124062306a36Sopenharmony_ci return PTR_ERR(ctx->lcd_clk); 124162306a36Sopenharmony_ci } 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci ctx->regs = devm_platform_ioremap_resource(pdev, 0); 124462306a36Sopenharmony_ci if (IS_ERR(ctx->regs)) 124562306a36Sopenharmony_ci return PTR_ERR(ctx->regs); 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci ret = platform_get_irq_byname(pdev, ctx->i80_if ? "lcd_sys" : "vsync"); 124862306a36Sopenharmony_ci if (ret < 0) 124962306a36Sopenharmony_ci return ret; 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ci ret = devm_request_irq(dev, ret, fimd_irq_handler, 0, "drm_fimd", ctx); 125262306a36Sopenharmony_ci if (ret) { 125362306a36Sopenharmony_ci dev_err(dev, "irq request failed.\n"); 125462306a36Sopenharmony_ci return ret; 125562306a36Sopenharmony_ci } 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci init_waitqueue_head(&ctx->wait_vsync_queue); 125862306a36Sopenharmony_ci atomic_set(&ctx->wait_vsync_event, 0); 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci platform_set_drvdata(pdev, ctx); 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci ctx->encoder = exynos_dpi_probe(dev); 126362306a36Sopenharmony_ci if (IS_ERR(ctx->encoder)) 126462306a36Sopenharmony_ci return PTR_ERR(ctx->encoder); 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci pm_runtime_enable(dev); 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci ret = component_add(dev, &fimd_component_ops); 126962306a36Sopenharmony_ci if (ret) 127062306a36Sopenharmony_ci goto err_disable_pm_runtime; 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci return ret; 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_cierr_disable_pm_runtime: 127562306a36Sopenharmony_ci pm_runtime_disable(dev); 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci return ret; 127862306a36Sopenharmony_ci} 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_cistatic int fimd_remove(struct platform_device *pdev) 128162306a36Sopenharmony_ci{ 128262306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci component_del(&pdev->dev, &fimd_component_ops); 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci return 0; 128762306a36Sopenharmony_ci} 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_cistatic int exynos_fimd_suspend(struct device *dev) 129062306a36Sopenharmony_ci{ 129162306a36Sopenharmony_ci struct fimd_context *ctx = dev_get_drvdata(dev); 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_ci clk_disable_unprepare(ctx->lcd_clk); 129462306a36Sopenharmony_ci clk_disable_unprepare(ctx->bus_clk); 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci return 0; 129762306a36Sopenharmony_ci} 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_cistatic int exynos_fimd_resume(struct device *dev) 130062306a36Sopenharmony_ci{ 130162306a36Sopenharmony_ci struct fimd_context *ctx = dev_get_drvdata(dev); 130262306a36Sopenharmony_ci int ret; 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci ret = clk_prepare_enable(ctx->bus_clk); 130562306a36Sopenharmony_ci if (ret < 0) { 130662306a36Sopenharmony_ci DRM_DEV_ERROR(dev, 130762306a36Sopenharmony_ci "Failed to prepare_enable the bus clk [%d]\n", 130862306a36Sopenharmony_ci ret); 130962306a36Sopenharmony_ci return ret; 131062306a36Sopenharmony_ci } 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci ret = clk_prepare_enable(ctx->lcd_clk); 131362306a36Sopenharmony_ci if (ret < 0) { 131462306a36Sopenharmony_ci DRM_DEV_ERROR(dev, 131562306a36Sopenharmony_ci "Failed to prepare_enable the lcd clk [%d]\n", 131662306a36Sopenharmony_ci ret); 131762306a36Sopenharmony_ci return ret; 131862306a36Sopenharmony_ci } 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci return 0; 132162306a36Sopenharmony_ci} 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_cistatic DEFINE_RUNTIME_DEV_PM_OPS(exynos_fimd_pm_ops, exynos_fimd_suspend, 132462306a36Sopenharmony_ci exynos_fimd_resume, NULL); 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_cistruct platform_driver fimd_driver = { 132762306a36Sopenharmony_ci .probe = fimd_probe, 132862306a36Sopenharmony_ci .remove = fimd_remove, 132962306a36Sopenharmony_ci .driver = { 133062306a36Sopenharmony_ci .name = "exynos4-fb", 133162306a36Sopenharmony_ci .owner = THIS_MODULE, 133262306a36Sopenharmony_ci .pm = pm_ptr(&exynos_fimd_pm_ops), 133362306a36Sopenharmony_ci .of_match_table = fimd_driver_dt_match, 133462306a36Sopenharmony_ci }, 133562306a36Sopenharmony_ci}; 1336