From 034dd340b08be1f2f0477ad16131d609f9dbd53c Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 30 Aug 2026 09:22:00 -0700 Subject: Merge tag 'trace-v7.3-2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace Pull tracing fixes from Steven Rostedt: - Fix error output of boot instance creation failure Currently if a boot instance creation fails, instead of printing out the name of the instance that failed, it prints "(null)". That is because it prints "cur_str" that had already been processed by strsep(). Print the saved name instead. While at it, print the error code of the failure. - Fix use-after-free for same named historgrams Histograms can be named so that they can be used in multiple events. But if the named histogram has a variable attached, the second event that uses the named histogram which duplicates it and needs to free the original after duplication leaves the old variable in place and still visible. If another histogram uses than variable, it will use the stale one which will try to reference the freed duplicate histogram and crash the kernel. Free the duplicate variables along with the duplicated histogram data. - Check return value of kthread_run() in event self test The events self tests uses a kthread for testing but does not check if it succeeded in creating a kthread. If the kthread creation were to fail, the code will still try to call kthread_stop() on the error returned. - Fix race between reading trace_pipe and updating subbuffer size If a user is reading the trace_pipe file at the same time they update the ring buffer sub-buffer size, can cause the trace_pipe read to read stale data. Add trace_access_lock() around updating the ring buffer sub-buffer size. - Fix eventfs_inode on failure path in creation of the events directory In the creation of the "events" directory, if after allocating the eventfs_inode a failure is detected, it calls cleanup_ei() which calls free_ei(). The free_ei() will test if eventfs_inode being freed has no children. It is a bug if it does. But on the failure case of the creation of the "events" directory, the children lists have not yet been initialized and the free will trigger a warning because list_empty() on an uninitialized list returns false. Move the initialization into init_ei() where it makes more sense and makes sure that a created eventfs_inode has its lists initialized upon creation. - Check return value of kthread_run() in ftrace direct sample code The sample code that shows how to use the ftrace direct calls does not test the return of kthread_run() to see if it succeeds. Return a failure if the kthread_run() doesn't succeed. - Clear user events state on fork in case of alloc failure On fork, the child gets a pointer to the parent's user events state. It makes a copy of it then updates the child's pointer to it. But if the allocation fails, the duplication function leaves the child with a pointer to its parent's descriptor. When the child cleans up its data, it will free the parent's descriptor while the parent is still using it. In the duplication function, set the child's user_event_mm to NULL before testing if the allocation succeeded, and when it exits it will not free the parent's descriptor. - Fix retry exhaustion in simple ring buffer reader swap simple_ring_buffer_swap_reader_page() starts with retry set to 8 and post-decrements it only after a failed link replacement. On the final attempt, a successful replacement leaves retry at zero, while a failed replacement leaves it at -1. But the check for success expects the retry value to be non-zero and exits with an error on zero. This is the opposite result. Fix it. - Fail nicely when the remote swap_reader_page() returns an error Currently, if the swap_reader_page() of a remote buffer fails, it triggers a WARN_ON_ONCE() and continues normally. Instead, have it exit with an error and a pr_warn() print instead of a full WARNING. * tag 'trace-v7.3-2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace: ring-buffer: Stop remote reader update when page swap fails tracing: Fix retry exhaustion in simple ring buffer reader swap tracing/user_events: Clear copied tracing state before fork duplication samples/ftrace: Fix kthread_stop() on ERR_PTR in ftrace-direct-multi-modify samples/ftrace: Fix kthread_stop() on ERR_PTR in ftrace-direct-modify eventfs: Initialize ei->children and ei->list in init_ei() tracing: Fix use-after-free in trace_pipe read on sub-buffer order change tracing: Fix crash passing ERR_PTR to kthread_stop() tracing: Fix use-after-free with same-name named triggers tracing: Fix logged instance name on creation failure --- drivers/gpu/drm/exynos/Kconfig | 131 ++ drivers/gpu/drm/exynos/Makefile | 27 + drivers/gpu/drm/exynos/exynos5433_drm_decon.c | 881 ++++++++++ drivers/gpu/drm/exynos/exynos7_drm_decon.c | 852 ++++++++++ drivers/gpu/drm/exynos/exynos_dp.c | 260 +++ drivers/gpu/drm/exynos/exynos_drm_crtc.c | 240 +++ drivers/gpu/drm/exynos/exynos_drm_crtc.h | 39 + drivers/gpu/drm/exynos/exynos_drm_dma.c | 140 ++ drivers/gpu/drm/exynos/exynos_drm_dpi.c | 262 +++ drivers/gpu/drm/exynos/exynos_drm_drv.c | 482 ++++++ drivers/gpu/drm/exynos/exynos_drm_drv.h | 263 +++ drivers/gpu/drm/exynos/exynos_drm_dsi.c | 211 +++ drivers/gpu/drm/exynos/exynos_drm_fb.c | 183 +++ drivers/gpu/drm/exynos/exynos_drm_fb.h | 22 + drivers/gpu/drm/exynos/exynos_drm_fbdev.c | 137 ++ drivers/gpu/drm/exynos/exynos_drm_fbdev.h | 27 + drivers/gpu/drm/exynos/exynos_drm_fimc.c | 1417 ++++++++++++++++ drivers/gpu/drm/exynos/exynos_drm_fimd.c | 1345 ++++++++++++++++ drivers/gpu/drm/exynos/exynos_drm_g2d.c | 1617 +++++++++++++++++++ drivers/gpu/drm/exynos/exynos_drm_g2d.h | 44 + drivers/gpu/drm/exynos/exynos_drm_gem.c | 454 ++++++ drivers/gpu/drm/exynos/exynos_drm_gem.h | 100 ++ drivers/gpu/drm/exynos/exynos_drm_gsc.c | 1428 ++++++++++++++++ drivers/gpu/drm/exynos/exynos_drm_ipp.c | 943 +++++++++++ drivers/gpu/drm/exynos/exynos_drm_ipp.h | 170 ++ drivers/gpu/drm/exynos/exynos_drm_mic.c | 472 ++++++ drivers/gpu/drm/exynos/exynos_drm_plane.c | 336 ++++ drivers/gpu/drm/exynos/exynos_drm_plane.h | 9 + drivers/gpu/drm/exynos/exynos_drm_rotator.c | 460 ++++++ drivers/gpu/drm/exynos/exynos_drm_scaler.c | 728 +++++++++ drivers/gpu/drm/exynos/exynos_drm_vidi.c | 535 ++++++ drivers/gpu/drm/exynos/exynos_drm_vidi.h | 18 + drivers/gpu/drm/exynos/exynos_hdmi.c | 2149 +++++++++++++++++++++++++ drivers/gpu/drm/exynos/exynos_mixer.c | 1340 +++++++++++++++ drivers/gpu/drm/exynos/regs-decon5433.h | 228 +++ drivers/gpu/drm/exynos/regs-decon7.h | 348 ++++ drivers/gpu/drm/exynos/regs-fimc.h | 665 ++++++++ drivers/gpu/drm/exynos/regs-gsc.h | 282 ++++ drivers/gpu/drm/exynos/regs-hdmi.h | 608 +++++++ drivers/gpu/drm/exynos/regs-mixer.h | 161 ++ drivers/gpu/drm/exynos/regs-rotator.h | 70 + drivers/gpu/drm/exynos/regs-scaler.h | 423 +++++ drivers/gpu/drm/exynos/regs-vp.h | 88 + 43 files changed, 20595 insertions(+) create mode 100644 drivers/gpu/drm/exynos/Kconfig create mode 100644 drivers/gpu/drm/exynos/Makefile create mode 100644 drivers/gpu/drm/exynos/exynos5433_drm_decon.c create mode 100644 drivers/gpu/drm/exynos/exynos7_drm_decon.c create mode 100644 drivers/gpu/drm/exynos/exynos_dp.c create mode 100644 drivers/gpu/drm/exynos/exynos_drm_crtc.c create mode 100644 drivers/gpu/drm/exynos/exynos_drm_crtc.h create mode 100644 drivers/gpu/drm/exynos/exynos_drm_dma.c create mode 100644 drivers/gpu/drm/exynos/exynos_drm_dpi.c create mode 100644 drivers/gpu/drm/exynos/exynos_drm_drv.c create mode 100644 drivers/gpu/drm/exynos/exynos_drm_drv.h create mode 100644 drivers/gpu/drm/exynos/exynos_drm_dsi.c create mode 100644 drivers/gpu/drm/exynos/exynos_drm_fb.c create mode 100644 drivers/gpu/drm/exynos/exynos_drm_fb.h create mode 100644 drivers/gpu/drm/exynos/exynos_drm_fbdev.c create mode 100644 drivers/gpu/drm/exynos/exynos_drm_fbdev.h create mode 100644 drivers/gpu/drm/exynos/exynos_drm_fimc.c create mode 100644 drivers/gpu/drm/exynos/exynos_drm_fimd.c create mode 100644 drivers/gpu/drm/exynos/exynos_drm_g2d.c create mode 100644 drivers/gpu/drm/exynos/exynos_drm_g2d.h create mode 100644 drivers/gpu/drm/exynos/exynos_drm_gem.c create mode 100644 drivers/gpu/drm/exynos/exynos_drm_gem.h create mode 100644 drivers/gpu/drm/exynos/exynos_drm_gsc.c create mode 100644 drivers/gpu/drm/exynos/exynos_drm_ipp.c create mode 100644 drivers/gpu/drm/exynos/exynos_drm_ipp.h create mode 100644 drivers/gpu/drm/exynos/exynos_drm_mic.c create mode 100644 drivers/gpu/drm/exynos/exynos_drm_plane.c create mode 100644 drivers/gpu/drm/exynos/exynos_drm_plane.h create mode 100644 drivers/gpu/drm/exynos/exynos_drm_rotator.c create mode 100644 drivers/gpu/drm/exynos/exynos_drm_scaler.c create mode 100644 drivers/gpu/drm/exynos/exynos_drm_vidi.c create mode 100644 drivers/gpu/drm/exynos/exynos_drm_vidi.h create mode 100644 drivers/gpu/drm/exynos/exynos_hdmi.c create mode 100644 drivers/gpu/drm/exynos/exynos_mixer.c create mode 100644 drivers/gpu/drm/exynos/regs-decon5433.h create mode 100644 drivers/gpu/drm/exynos/regs-decon7.h create mode 100644 drivers/gpu/drm/exynos/regs-fimc.h create mode 100644 drivers/gpu/drm/exynos/regs-gsc.h create mode 100644 drivers/gpu/drm/exynos/regs-hdmi.h create mode 100644 drivers/gpu/drm/exynos/regs-mixer.h create mode 100644 drivers/gpu/drm/exynos/regs-rotator.h create mode 100644 drivers/gpu/drm/exynos/regs-scaler.h create mode 100644 drivers/gpu/drm/exynos/regs-vp.h (limited to 'drivers/gpu/drm/exynos') diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig new file mode 100644 index 000000000..a59131ae1 --- /dev/null +++ b/drivers/gpu/drm/exynos/Kconfig @@ -0,0 +1,131 @@ +# SPDX-License-Identifier: GPL-2.0-only +config DRM_EXYNOS + tristate "DRM Support for Samsung SoC Exynos Series" + depends on OF && DRM && COMMON_CLK + depends on ARCH_S3C64XX || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST + depends on MMU + select DRM_CLIENT_SELECTION + select DRM_DISPLAY_HELPER if DRM_EXYNOS_DP + select DRM_KMS_HELPER + select VIDEOMODE_HELPERS + select FB_DMAMEM_HELPERS if DRM_FBDEV_EMULATION + select SND_SOC_HDMI_CODEC if SND_SOC + help + Choose this option if you have a Samsung SoC Exynos chipset. + If M is selected the module will be called exynosdrm. + +if DRM_EXYNOS + +comment "CRTCs" + +config DRM_EXYNOS_FIMD + bool "FIMD" + depends on !FB_S3C + select MFD_SYSCON + help + Choose this option if you want to use Exynos FIMD for DRM. + +config DRM_EXYNOS5433_DECON + bool "DECON on Exynos5433" + help + Choose this option if you want to use Exynos5433 DECON for DRM. + +config DRM_EXYNOS7_DECON + bool "DECON on Exynos7" + depends on !FB_S3C + help + Choose this option if you want to use Exynos DECON for DRM. + +config DRM_EXYNOS_MIXER + bool "Mixer" + help + Choose this option if you want to use Exynos Mixer for DRM. + +config DRM_EXYNOS_VIDI + bool "Virtual Display" + help + Choose this option if you want to use Exynos VIDI for DRM. + +comment "Encoders and Bridges" + +config DRM_EXYNOS_DPI + bool "Parallel output" + depends on DRM_EXYNOS_FIMD + select DRM_PANEL + default n + help + This enables support for Exynos parallel output. + +config DRM_EXYNOS_DSI + bool "MIPI-DSI host" + depends on DRM_EXYNOS_FIMD || DRM_EXYNOS5433_DECON || DRM_EXYNOS7_DECON + select DRM_MIPI_DSI + select DRM_PANEL + select DRM_SAMSUNG_DSIM + default n + help + This enables support for Exynos MIPI-DSI device. + +config DRM_EXYNOS_DP + bool "Exynos specific extensions for Analogix DP driver" + depends on DRM_EXYNOS_FIMD || DRM_EXYNOS7_DECON + depends on OF + select DRM_ANALOGIX_DP + select DRM_BRIDGE_CONNECTOR + select DRM_DISPLAY_DP_HELPER + default DRM_EXYNOS + select DRM_OF_DISPLAY_MODE_BRIDGE + select DRM_PANEL + help + This enables support for DP device. + +config DRM_EXYNOS_HDMI + bool "HDMI" + depends on DRM_EXYNOS_MIXER || DRM_EXYNOS5433_DECON + select CEC_CORE if CEC_NOTIFIER + help + Choose this option if you want to use Exynos HDMI for DRM. + +config DRM_EXYNOS_MIC + bool "Mobile Image Compressor" + depends on DRM_EXYNOS5433_DECON + help + Choose this option if you want to use Exynos MIC for DRM. + +comment "Sub-drivers" + +config DRM_EXYNOS_G2D + bool "G2D" + depends on VIDEO_SAMSUNG_S5P_G2D=n || COMPILE_TEST + help + Choose this option if you want to use Exynos G2D for DRM. + +config DRM_EXYNOS_IPP + bool + +config DRM_EXYNOS_FIMC + bool "FIMC" + select DRM_EXYNOS_IPP + help + Choose this option if you want to use Exynos FIMC for DRM. + +config DRM_EXYNOS_ROTATOR + bool "Rotator" + select DRM_EXYNOS_IPP + help + Choose this option if you want to use Exynos Rotator for DRM. + +config DRM_EXYNOS_SCALER + bool "Scaler" + select DRM_EXYNOS_IPP + help + Choose this option if you want to use Exynos Scaler for DRM. + +config DRM_EXYNOS_GSC + bool "GScaler" + depends on VIDEO_SAMSUNG_EXYNOS_GSC=n || COMPILE_TEST + select DRM_EXYNOS_IPP + help + Choose this option if you want to use Exynos GSC for DRM. + +endif diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile new file mode 100644 index 000000000..2fd2f3ee4 --- /dev/null +++ b/drivers/gpu/drm/exynos/Makefile @@ -0,0 +1,27 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the drm device driver. This driver provides support for the +# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. + +exynosdrm-y := exynos_drm_drv.o exynos_drm_crtc.o exynos_drm_fb.o \ + exynos_drm_gem.o exynos_drm_plane.o exynos_drm_dma.o + +exynosdrm-$(CONFIG_DRM_FBDEV_EMULATION) += exynos_drm_fbdev.o +exynosdrm-$(CONFIG_DRM_EXYNOS_FIMD) += exynos_drm_fimd.o +exynosdrm-$(CONFIG_DRM_EXYNOS5433_DECON) += exynos5433_drm_decon.o +exynosdrm-$(CONFIG_DRM_EXYNOS7_DECON) += exynos7_drm_decon.o +exynosdrm-$(CONFIG_DRM_EXYNOS_DPI) += exynos_drm_dpi.o +exynosdrm-$(CONFIG_DRM_EXYNOS_DSI) += exynos_drm_dsi.o +exynosdrm-$(CONFIG_DRM_EXYNOS_DP) += exynos_dp.o +exynosdrm-$(CONFIG_DRM_EXYNOS_MIXER) += exynos_mixer.o +exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI) += exynos_hdmi.o +exynosdrm-$(CONFIG_DRM_EXYNOS_VIDI) += exynos_drm_vidi.o +exynosdrm-$(CONFIG_DRM_EXYNOS_G2D) += exynos_drm_g2d.o +exynosdrm-$(CONFIG_DRM_EXYNOS_IPP) += exynos_drm_ipp.o +exynosdrm-$(CONFIG_DRM_EXYNOS_FIMC) += exynos_drm_fimc.o +exynosdrm-$(CONFIG_DRM_EXYNOS_ROTATOR) += exynos_drm_rotator.o +exynosdrm-$(CONFIG_DRM_EXYNOS_SCALER) += exynos_drm_scaler.o +exynosdrm-$(CONFIG_DRM_EXYNOS_GSC) += exynos_drm_gsc.o +exynosdrm-$(CONFIG_DRM_EXYNOS_MIC) += exynos_drm_mic.o + +obj-$(CONFIG_DRM_EXYNOS) += exynosdrm.o diff --git a/drivers/gpu/drm/exynos/exynos5433_drm_decon.c b/drivers/gpu/drm/exynos/exynos5433_drm_decon.c new file mode 100644 index 000000000..9ae0fa466 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos5433_drm_decon.c @@ -0,0 +1,881 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* drivers/gpu/drm/exynos5433_drm_decon.c + * + * Copyright (C) 2015 Samsung Electronics Co.Ltd + * Authors: + * Joonyoung Shim + * Hyungwon Hwang + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "exynos_drm_crtc.h" +#include "exynos_drm_drv.h" +#include "exynos_drm_fb.h" +#include "exynos_drm_plane.h" +#include "regs-decon5433.h" + +#define DSD_CFG_MUX 0x1004 +#define DSD_CFG_MUX_TE_UNMASK_GLOBAL BIT(13) + +#define WINDOWS_NR 5 +#define PRIMARY_WIN 2 +#define CURSON_WIN 4 + +#define MIN_FB_WIDTH_FOR_16WORD_BURST 128 + +#define I80_HW_TRG (1 << 0) +#define IFTYPE_HDMI (1 << 1) + +static const char * const decon_clks_name[] = { + "pclk", + "aclk_decon", + "aclk_smmu_decon0x", + "aclk_xiu_decon0x", + "pclk_smmu_decon0x", + "aclk_smmu_decon1x", + "aclk_xiu_decon1x", + "pclk_smmu_decon1x", + "sclk_decon_vclk", + "sclk_decon_eclk", +}; + +struct decon_context { + struct device *dev; + struct drm_device *drm_dev; + void *dma_priv; + struct exynos_drm_crtc *crtc; + struct exynos_drm_plane planes[WINDOWS_NR]; + struct exynos_drm_plane_config configs[WINDOWS_NR]; + void __iomem *addr; + struct regmap *sysreg; + struct clk *clks[ARRAY_SIZE(decon_clks_name)]; + unsigned int irq; + unsigned int irq_vsync; + unsigned int irq_lcd_sys; + unsigned int te_irq; + unsigned long out_type; + int first_win; + spinlock_t vblank_lock; + u32 frame_id; +}; + +static const uint32_t decon_formats[] = { + DRM_FORMAT_XRGB1555, + DRM_FORMAT_RGB565, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_ARGB8888, +}; + +static const enum drm_plane_type decon_win_types[WINDOWS_NR] = { + [PRIMARY_WIN] = DRM_PLANE_TYPE_PRIMARY, + [CURSON_WIN] = DRM_PLANE_TYPE_CURSOR, +}; + +static const unsigned int capabilities[WINDOWS_NR] = { + 0, + EXYNOS_DRM_PLANE_CAP_WIN_BLEND | EXYNOS_DRM_PLANE_CAP_PIX_BLEND, + EXYNOS_DRM_PLANE_CAP_WIN_BLEND | EXYNOS_DRM_PLANE_CAP_PIX_BLEND, + EXYNOS_DRM_PLANE_CAP_WIN_BLEND | EXYNOS_DRM_PLANE_CAP_PIX_BLEND, + EXYNOS_DRM_PLANE_CAP_WIN_BLEND | EXYNOS_DRM_PLANE_CAP_PIX_BLEND, +}; + +static inline void decon_set_bits(struct decon_context *ctx, u32 reg, u32 mask, + u32 val) +{ + val = (val & mask) | (readl(ctx->addr + reg) & ~mask); + writel(val, ctx->addr + reg); +} + +static int decon_enable_vblank(struct exynos_drm_crtc *crtc) +{ + struct decon_context *ctx = crtc->ctx; + u32 val; + + val = VIDINTCON0_INTEN; + if (crtc->i80_mode) + val |= VIDINTCON0_FRAMEDONE; + else + val |= VIDINTCON0_INTFRMEN | VIDINTCON0_FRAMESEL_FP; + + writel(val, ctx->addr + DECON_VIDINTCON0); + + enable_irq(ctx->irq); + if (!(ctx->out_type & I80_HW_TRG)) + enable_irq(ctx->te_irq); + + return 0; +} + +static void decon_disable_vblank(struct exynos_drm_crtc *crtc) +{ + struct decon_context *ctx = crtc->ctx; + + if (!(ctx->out_type & I80_HW_TRG)) + disable_irq_nosync(ctx->te_irq); + disable_irq_nosync(ctx->irq); + + writel(0, ctx->addr + DECON_VIDINTCON0); +} + +/* return number of starts/ends of frame transmissions since reset */ +static u32 decon_get_frame_count(struct decon_context *ctx, bool end) +{ + u32 frm, pfrm, status, cnt = 2; + + /* To get consistent result repeat read until frame id is stable. + * Usually the loop will be executed once, in rare cases when the loop + * is executed at frame change time 2nd pass will be needed. + */ + frm = readl(ctx->addr + DECON_CRFMID); + do { + status = readl(ctx->addr + DECON_VIDCON1); + pfrm = frm; + frm = readl(ctx->addr + DECON_CRFMID); + } while (frm != pfrm && --cnt); + + /* CRFMID is incremented on BPORCH in case of I80 and on VSYNC in case + * of RGB, it should be taken into account. + */ + if (!frm) + return 0; + + switch (status & (VIDCON1_VSTATUS_MASK | VIDCON1_I80_ACTIVE)) { + case VIDCON1_VSTATUS_VS: + if (!(ctx->crtc->i80_mode)) + --frm; + break; + case VIDCON1_VSTATUS_BP: + --frm; + break; + case VIDCON1_I80_ACTIVE: + case VIDCON1_VSTATUS_AC: + if (end) + --frm; + break; + default: + break; + } + + return frm; +} + +static void decon_setup_trigger(struct decon_context *ctx) +{ + if (!ctx->crtc->i80_mode && !(ctx->out_type & I80_HW_TRG)) + return; + + if (!(ctx->out_type & I80_HW_TRG)) { + writel(TRIGCON_TRIGEN_PER_F | TRIGCON_TRIGEN_F | + TRIGCON_TE_AUTO_MASK | TRIGCON_SWTRIGEN, + ctx->addr + DECON_TRIGCON); + return; + } + + writel(TRIGCON_TRIGEN_PER_F | TRIGCON_TRIGEN_F | TRIGCON_HWTRIGMASK + | TRIGCON_HWTRIGEN, ctx->addr + DECON_TRIGCON); + + if (regmap_update_bits(ctx->sysreg, DSD_CFG_MUX, + DSD_CFG_MUX_TE_UNMASK_GLOBAL, ~0)) + DRM_DEV_ERROR(ctx->dev, "Cannot update sysreg.\n"); +} + +static void decon_commit(struct exynos_drm_crtc *crtc) +{ + struct decon_context *ctx = crtc->ctx; + struct drm_display_mode *m = &crtc->base.mode; + bool interlaced = false; + u32 val; + + if (ctx->out_type & IFTYPE_HDMI) { + m->crtc_hsync_start = m->crtc_hdisplay + 10; + m->crtc_hsync_end = m->crtc_htotal - 92; + m->crtc_vsync_start = m->crtc_vdisplay + 1; + m->crtc_vsync_end = m->crtc_vsync_start + 1; + if (m->flags & DRM_MODE_FLAG_INTERLACE) + interlaced = true; + } + + decon_setup_trigger(ctx); + + /* lcd on and use command if */ + val = VIDOUT_LCD_ON; + if (interlaced) + val |= VIDOUT_INTERLACE_EN_F; + if (crtc->i80_mode) { + val |= VIDOUT_COMMAND_IF; + } else { + val |= VIDOUT_RGB_IF; + } + + writel(val, ctx->addr + DECON_VIDOUTCON0); + + if (interlaced) + val = VIDTCON2_LINEVAL(m->vdisplay / 2 - 1) | + VIDTCON2_HOZVAL(m->hdisplay - 1); + else + val = VIDTCON2_LINEVAL(m->vdisplay - 1) | + VIDTCON2_HOZVAL(m->hdisplay - 1); + writel(val, ctx->addr + DECON_VIDTCON2); + + if (!crtc->i80_mode) { + int vbp = m->crtc_vtotal - m->crtc_vsync_end; + int vfp = m->crtc_vsync_start - m->crtc_vdisplay; + + if (interlaced) + vbp = vbp / 2 - 1; + val = VIDTCON00_VBPD_F(vbp - 1) | VIDTCON00_VFPD_F(vfp - 1); + writel(val, ctx->addr + DECON_VIDTCON00); + + val = VIDTCON01_VSPW_F( + m->crtc_vsync_end - m->crtc_vsync_start - 1); + writel(val, ctx->addr + DECON_VIDTCON01); + + val = VIDTCON10_HBPD_F( + m->crtc_htotal - m->crtc_hsync_end - 1) | + VIDTCON10_HFPD_F( + m->crtc_hsync_start - m->crtc_hdisplay - 1); + writel(val, ctx->addr + DECON_VIDTCON10); + + val = VIDTCON11_HSPW_F( + m->crtc_hsync_end - m->crtc_hsync_start - 1); + writel(val, ctx->addr + DECON_VIDTCON11); + } + + /* enable output and display signal */ + decon_set_bits(ctx, DECON_VIDCON0, VIDCON0_ENVID | VIDCON0_ENVID_F, ~0); + + decon_set_bits(ctx, DECON_UPDATE, STANDALONE_UPDATE_F, ~0); +} + +static void decon_win_set_bldeq(struct decon_context *ctx, unsigned int win, + unsigned int alpha, unsigned int pixel_alpha) +{ + u32 mask = BLENDERQ_A_FUNC_F(0xf) | BLENDERQ_B_FUNC_F(0xf); + u32 val = 0; + + switch (pixel_alpha) { + case DRM_MODE_BLEND_PIXEL_NONE: + case DRM_MODE_BLEND_COVERAGE: + val |= BLENDERQ_A_FUNC_F(BLENDERQ_ALPHA_A); + val |= BLENDERQ_B_FUNC_F(BLENDERQ_ONE_MINUS_ALPHA_A); + break; + case DRM_MODE_BLEND_PREMULTI: + default: + if (alpha != DRM_BLEND_ALPHA_OPAQUE) { + val |= BLENDERQ_A_FUNC_F(BLENDERQ_ALPHA0); + val |= BLENDERQ_B_FUNC_F(BLENDERQ_ONE_MINUS_ALPHA_A); + } else { + val |= BLENDERQ_A_FUNC_F(BLENDERQ_ONE); + val |= BLENDERQ_B_FUNC_F(BLENDERQ_ONE_MINUS_ALPHA_A); + } + break; + } + decon_set_bits(ctx, DECON_BLENDERQx(win), mask, val); +} + +static void decon_win_set_bldmod(struct decon_context *ctx, unsigned int win, + unsigned int alpha, unsigned int pixel_alpha) +{ + u32 win_alpha = alpha >> 8; + u32 val = 0; + + switch (pixel_alpha) { + case DRM_MODE_BLEND_PIXEL_NONE: + break; + case DRM_MODE_BLEND_COVERAGE: + case DRM_MODE_BLEND_PREMULTI: + default: + val |= WINCONx_ALPHA_SEL_F; + val |= WINCONx_BLD_PIX_F; + val |= WINCONx_ALPHA_MUL_F; + break; + } + decon_set_bits(ctx, DECON_WINCONx(win), WINCONx_BLEND_MODE_MASK, val); + + if (alpha != DRM_BLEND_ALPHA_OPAQUE) { + val = VIDOSD_Wx_ALPHA_R_F(win_alpha) | + VIDOSD_Wx_ALPHA_G_F(win_alpha) | + VIDOSD_Wx_ALPHA_B_F(win_alpha); + decon_set_bits(ctx, DECON_VIDOSDxC(win), + VIDOSDxC_ALPHA0_RGB_MASK, val); + decon_set_bits(ctx, DECON_BLENDCON, BLEND_NEW, BLEND_NEW); + } +} + +static void decon_win_set_pixfmt(struct decon_context *ctx, unsigned int win, + struct drm_framebuffer *fb) +{ + struct exynos_drm_plane *plane = &ctx->planes[win]; + struct exynos_drm_plane_state *state = + to_exynos_plane_state(plane->base.state); + unsigned int alpha = state->base.alpha; + unsigned int pixel_alpha; + unsigned long val; + + if (fb->format->has_alpha) + pixel_alpha = state->base.pixel_blend_mode; + else + pixel_alpha = DRM_MODE_BLEND_PIXEL_NONE; + + val = readl(ctx->addr + DECON_WINCONx(win)); + val &= WINCONx_ENWIN_F; + + switch (fb->format->format) { + case DRM_FORMAT_XRGB1555: + val |= WINCONx_BPPMODE_16BPP_I1555; + val |= WINCONx_HAWSWP_F; + val |= WINCONx_BURSTLEN_16WORD; + break; + case DRM_FORMAT_RGB565: + val |= WINCONx_BPPMODE_16BPP_565; + val |= WINCONx_HAWSWP_F; + val |= WINCONx_BURSTLEN_16WORD; + break; + case DRM_FORMAT_XRGB8888: + val |= WINCONx_BPPMODE_24BPP_888; + val |= WINCONx_WSWP_F; + val |= WINCONx_BURSTLEN_16WORD; + break; + case DRM_FORMAT_ARGB8888: + default: + val |= WINCONx_BPPMODE_32BPP_A8888; + val |= WINCONx_WSWP_F; + val |= WINCONx_BURSTLEN_16WORD; + break; + } + + DRM_DEV_DEBUG_KMS(ctx->dev, "cpp = %u\n", fb->format->cpp[0]); + + /* + * In case of exynos, setting dma-burst to 16Word causes permanent + * tearing for very small buffers, e.g. cursor buffer. Burst Mode + * switching which is based on plane size is not recommended as + * plane size varies a lot towards the end of the screen and rapid + * movement causes unstable DMA which results into iommu crash/tear. + */ + + if (fb->width < MIN_FB_WIDTH_FOR_16WORD_BURST) { + val &= ~WINCONx_BURSTLEN_MASK; + val |= WINCONx_BURSTLEN_8WORD; + } + decon_set_bits(ctx, DECON_WINCONx(win), ~WINCONx_BLEND_MODE_MASK, val); + + if (win > 0) { + decon_win_set_bldmod(ctx, win, alpha, pixel_alpha); + decon_win_set_bldeq(ctx, win, alpha, pixel_alpha); + } +} + +static void decon_shadow_protect(struct decon_context *ctx, bool protect) +{ + decon_set_bits(ctx, DECON_SHADOWCON, SHADOWCON_PROTECT_MASK, + protect ? ~0 : 0); +} + +static void decon_atomic_begin(struct exynos_drm_crtc *crtc) +{ + struct decon_context *ctx = crtc->ctx; + + decon_shadow_protect(ctx, true); +} + +#define BIT_VAL(x, e, s) (((x) & ((1 << ((e) - (s) + 1)) - 1)) << (s)) +#define COORDINATE_X(x) BIT_VAL((x), 23, 12) +#define COORDINATE_Y(x) BIT_VAL((x), 11, 0) + +static void decon_update_plane(struct exynos_drm_crtc *crtc, + struct exynos_drm_plane *plane) +{ + struct exynos_drm_plane_state *state = + to_exynos_plane_state(plane->base.state); + struct decon_context *ctx = crtc->ctx; + struct drm_framebuffer *fb = state->base.fb; + unsigned int win = plane->index; + unsigned int cpp = fb->format->cpp[0]; + unsigned int pitch = fb->pitches[0]; + dma_addr_t dma_addr = exynos_drm_fb_dma_addr(fb, 0); + u32 val; + + if (crtc->base.mode.flags & DRM_MODE_FLAG_INTERLACE) { + val = COORDINATE_X(state->crtc.x) | + COORDINATE_Y(state->crtc.y / 2); + writel(val, ctx->addr + DECON_VIDOSDxA(win)); + + val = COORDINATE_X(state->crtc.x + state->crtc.w - 1) | + COORDINATE_Y((state->crtc.y + state->crtc.h) / 2 - 1); + writel(val, ctx->addr + DECON_VIDOSDxB(win)); + } else { + val = COORDINATE_X(state->crtc.x) | COORDINATE_Y(state->crtc.y); + writel(val, ctx->addr + DECON_VIDOSDxA(win)); + + val = COORDINATE_X(state->crtc.x + state->crtc.w - 1) | + COORDINATE_Y(state->crtc.y + state->crtc.h - 1); + writel(val, ctx->addr + DECON_VIDOSDxB(win)); + } + + val = VIDOSD_Wx_ALPHA_R_F(0xff) | VIDOSD_Wx_ALPHA_G_F(0xff) | + VIDOSD_Wx_ALPHA_B_F(0xff); + writel(val, ctx->addr + DECON_VIDOSDxC(win)); + + val = VIDOSD_Wx_ALPHA_R_F(0x0) | VIDOSD_Wx_ALPHA_G_F(0x0) | + VIDOSD_Wx_ALPHA_B_F(0x0); + writel(val, ctx->addr + DECON_VIDOSDxD(win)); + + writel(dma_addr, ctx->addr + DECON_VIDW0xADD0B0(win)); + + val = dma_addr + pitch * state->src.h; + writel(val, ctx->addr + DECON_VIDW0xADD1B0(win)); + + if (!(ctx->out_type & IFTYPE_HDMI)) + val = BIT_VAL(pitch - state->crtc.w * cpp, 27, 14) + | BIT_VAL(state->crtc.w * cpp, 13, 0); + else + val = BIT_VAL(pitch - state->crtc.w * cpp, 29, 15) + | BIT_VAL(state->crtc.w * cpp, 14, 0); + writel(val, ctx->addr + DECON_VIDW0xADD2(win)); + + decon_win_set_pixfmt(ctx, win, fb); + + /* window enable */ + decon_set_bits(ctx, DECON_WINCONx(win), WINCONx_ENWIN_F, ~0); +} + +static void decon_disable_plane(struct exynos_drm_crtc *crtc, + struct exynos_drm_plane *plane) +{ + struct decon_context *ctx = crtc->ctx; + unsigned int win = plane->index; + + decon_set_bits(ctx, DECON_WINCONx(win), WINCONx_ENWIN_F, 0); +} + +static void decon_atomic_flush(struct exynos_drm_crtc *crtc) +{ + struct decon_context *ctx = crtc->ctx; + unsigned long flags; + + spin_lock_irqsave(&ctx->vblank_lock, flags); + + decon_shadow_protect(ctx, false); + + decon_set_bits(ctx, DECON_UPDATE, STANDALONE_UPDATE_F, ~0); + + ctx->frame_id = decon_get_frame_count(ctx, true); + + exynos_crtc_handle_event(crtc); + + spin_unlock_irqrestore(&ctx->vblank_lock, flags); +} + +static void decon_swreset(struct decon_context *ctx) +{ + unsigned long flags; + u32 val; + int ret; + + writel(0, ctx->addr + DECON_VIDCON0); + readl_poll_timeout(ctx->addr + DECON_VIDCON0, val, + ~val & VIDCON0_STOP_STATUS, 12, 20000); + + writel(VIDCON0_SWRESET, ctx->addr + DECON_VIDCON0); + ret = readl_poll_timeout(ctx->addr + DECON_VIDCON0, val, + ~val & VIDCON0_SWRESET, 12, 20000); + + WARN(ret < 0, "failed to software reset DECON\n"); + + spin_lock_irqsave(&ctx->vblank_lock, flags); + ctx->frame_id = 0; + spin_unlock_irqrestore(&ctx->vblank_lock, flags); + + if (!(ctx->out_type & IFTYPE_HDMI)) + return; + + writel(VIDCON0_CLKVALUP | VIDCON0_VLCKFREE, ctx->addr + DECON_VIDCON0); + decon_set_bits(ctx, DECON_CMU, + CMU_CLKGAGE_MODE_SFR_F | CMU_CLKGAGE_MODE_MEM_F, ~0); + writel(VIDCON1_VCLK_RUN_VDEN_DISABLE, ctx->addr + DECON_VIDCON1); + writel(CRCCTRL_CRCEN | CRCCTRL_CRCSTART_F | CRCCTRL_CRCCLKEN, + ctx->addr + DECON_CRCCTRL); +} + +static void decon_atomic_enable(struct exynos_drm_crtc *crtc) +{ + struct decon_context *ctx = crtc->ctx; + int ret; + + ret = pm_runtime_resume_and_get(ctx->dev); + if (ret < 0) { + DRM_DEV_ERROR(ctx->dev, "failed to enable DECON device.\n"); + return; + } + + exynos_drm_pipe_clk_enable(crtc, true); + + decon_swreset(ctx); + + decon_commit(ctx->crtc); +} + +static void decon_atomic_disable(struct exynos_drm_crtc *crtc) +{ + struct decon_context *ctx = crtc->ctx; + int i; + + if (!(ctx->out_type & I80_HW_TRG)) + synchronize_irq(ctx->te_irq); + synchronize_irq(ctx->irq); + + /* + * We need to make sure that all windows are disabled before we + * suspend that connector. Otherwise we might try to scan from + * a destroyed buffer later. + */ + for (i = ctx->first_win; i < WINDOWS_NR; i++) + decon_disable_plane(crtc, &ctx->planes[i]); + + decon_swreset(ctx); + + exynos_drm_pipe_clk_enable(crtc, false); + + pm_runtime_put_sync(ctx->dev); +} + +static irqreturn_t decon_te_irq_handler(int irq, void *dev_id) +{ + struct decon_context *ctx = dev_id; + + decon_set_bits(ctx, DECON_TRIGCON, TRIGCON_SWTRIGCMD, ~0); + + return IRQ_HANDLED; +} + +static void decon_clear_channels(struct exynos_drm_crtc *crtc) +{ + struct decon_context *ctx = crtc->ctx; + int win, i, ret; + + for (i = 0; i < ARRAY_SIZE(decon_clks_name); i++) { + ret = clk_prepare_enable(ctx->clks[i]); + if (ret < 0) + goto err; + } + + decon_shadow_protect(ctx, true); + for (win = 0; win < WINDOWS_NR; win++) + decon_set_bits(ctx, DECON_WINCONx(win), WINCONx_ENWIN_F, 0); + decon_shadow_protect(ctx, false); + + decon_set_bits(ctx, DECON_UPDATE, STANDALONE_UPDATE_F, ~0); + + /* TODO: wait for possible vsync */ + msleep(50); + +err: + while (--i >= 0) + clk_disable_unprepare(ctx->clks[i]); +} + +static enum drm_mode_status decon_mode_valid(struct exynos_drm_crtc *crtc, + const struct drm_display_mode *mode) +{ + struct decon_context *ctx = crtc->ctx; + + ctx->irq = crtc->i80_mode ? ctx->irq_lcd_sys : ctx->irq_vsync; + + if (ctx->irq) + return MODE_OK; + + dev_info(ctx->dev, "Sink requires %s mode, but appropriate interrupt is not provided.\n", + crtc->i80_mode ? "command" : "video"); + + return MODE_BAD; +} + +static const struct exynos_drm_crtc_ops decon_crtc_ops = { + .atomic_enable = decon_atomic_enable, + .atomic_disable = decon_atomic_disable, + .enable_vblank = decon_enable_vblank, + .disable_vblank = decon_disable_vblank, + .atomic_begin = decon_atomic_begin, + .update_plane = decon_update_plane, + .disable_plane = decon_disable_plane, + .mode_valid = decon_mode_valid, + .atomic_flush = decon_atomic_flush, +}; + +static int decon_bind(struct device *dev, struct device *master, void *data) +{ + struct decon_context *ctx = dev_get_drvdata(dev); + struct drm_device *drm_dev = data; + struct exynos_drm_plane *exynos_plane; + enum exynos_drm_output_type out_type; + unsigned int win; + int ret; + + ctx->drm_dev = drm_dev; + + for (win = ctx->first_win; win < WINDOWS_NR; win++) { + ctx->configs[win].pixel_formats = decon_formats; + ctx->configs[win].num_pixel_formats = ARRAY_SIZE(decon_formats); + ctx->configs[win].zpos = win - ctx->first_win; + ctx->configs[win].type = decon_win_types[win]; + ctx->configs[win].capabilities = capabilities[win]; + + ret = exynos_plane_init(drm_dev, &ctx->planes[win], win, + &ctx->configs[win]); + if (ret) + return ret; + } + + exynos_plane = &ctx->planes[PRIMARY_WIN]; + out_type = (ctx->out_type & IFTYPE_HDMI) ? EXYNOS_DISPLAY_TYPE_HDMI + : EXYNOS_DISPLAY_TYPE_LCD; + ctx->crtc = exynos_drm_crtc_create(drm_dev, &exynos_plane->base, + out_type, &decon_crtc_ops, ctx); + if (IS_ERR(ctx->crtc)) + return PTR_ERR(ctx->crtc); + + decon_clear_channels(ctx->crtc); + + return exynos_drm_register_dma(drm_dev, dev, &ctx->dma_priv); +} + +static void decon_unbind(struct device *dev, struct device *master, void *data) +{ + struct decon_context *ctx = dev_get_drvdata(dev); + + decon_atomic_disable(ctx->crtc); + + /* detach this sub driver from iommu mapping if supported. */ + exynos_drm_unregister_dma(ctx->drm_dev, ctx->dev, &ctx->dma_priv); +} + +static const struct component_ops decon_component_ops = { + .bind = decon_bind, + .unbind = decon_unbind, +}; + +static void decon_handle_vblank(struct decon_context *ctx) +{ + u32 frm; + + spin_lock(&ctx->vblank_lock); + + frm = decon_get_frame_count(ctx, true); + + if (frm != ctx->frame_id) { + /* handle only if incremented, take care of wrap-around */ + if ((s32)(frm - ctx->frame_id) > 0) + drm_crtc_handle_vblank(&ctx->crtc->base); + ctx->frame_id = frm; + } + + spin_unlock(&ctx->vblank_lock); +} + +static irqreturn_t decon_irq_handler(int irq, void *dev_id) +{ + struct decon_context *ctx = dev_id; + u32 val; + + val = readl(ctx->addr + DECON_VIDINTCON1); + val &= VIDINTCON1_INTFRMDONEPEND | VIDINTCON1_INTFRMPEND; + + if (val) { + writel(val, ctx->addr + DECON_VIDINTCON1); + if (ctx->out_type & IFTYPE_HDMI) { + val = readl(ctx->addr + DECON_VIDOUTCON0); + val &= VIDOUT_INTERLACE_EN_F | VIDOUT_INTERLACE_FIELD_F; + if (val == + (VIDOUT_INTERLACE_EN_F | VIDOUT_INTERLACE_FIELD_F)) + return IRQ_HANDLED; + } + decon_handle_vblank(ctx); + } + + return IRQ_HANDLED; +} + +static int exynos5433_decon_suspend(struct device *dev) +{ + struct decon_context *ctx = dev_get_drvdata(dev); + int i = ARRAY_SIZE(decon_clks_name); + + while (--i >= 0) + clk_disable_unprepare(ctx->clks[i]); + + return 0; +} + +static int exynos5433_decon_resume(struct device *dev) +{ + struct decon_context *ctx = dev_get_drvdata(dev); + int i, ret; + + for (i = 0; i < ARRAY_SIZE(decon_clks_name); i++) { + ret = clk_prepare_enable(ctx->clks[i]); + if (ret < 0) + goto err; + } + + return 0; + +err: + while (--i >= 0) + clk_disable_unprepare(ctx->clks[i]); + + return ret; +} + +static DEFINE_RUNTIME_DEV_PM_OPS(exynos5433_decon_pm_ops, + exynos5433_decon_suspend, + exynos5433_decon_resume, NULL); + +static const struct of_device_id exynos5433_decon_driver_dt_match[] = { + { + .compatible = "samsung,exynos5433-decon", + .data = (void *)I80_HW_TRG + }, + { + .compatible = "samsung,exynos5433-decon-tv", + .data = (void *)(I80_HW_TRG | IFTYPE_HDMI) + }, + {}, +}; +MODULE_DEVICE_TABLE(of, exynos5433_decon_driver_dt_match); + +static int decon_conf_irq(struct decon_context *ctx, const char *name, + irq_handler_t handler, unsigned long int flags) +{ + struct platform_device *pdev = to_platform_device(ctx->dev); + int ret, irq = platform_get_irq_byname(pdev, name); + + if (irq < 0) { + switch (irq) { + case -EPROBE_DEFER: + return irq; + case -ENODATA: + case -ENXIO: + return 0; + default: + dev_err(ctx->dev, "IRQ %s get failed, %d\n", name, irq); + return irq; + } + } + ret = devm_request_irq(ctx->dev, irq, handler, + flags | IRQF_NO_AUTOEN, "drm_decon", ctx); + if (ret < 0) { + dev_err(ctx->dev, "IRQ %s request failed\n", name); + return ret; + } + + return irq; +} + +static int exynos5433_decon_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct decon_context *ctx; + int ret; + int i; + + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->dev = dev; + ctx->out_type = (unsigned long)of_device_get_match_data(dev); + spin_lock_init(&ctx->vblank_lock); + + if (ctx->out_type & IFTYPE_HDMI) + ctx->first_win = 1; + + for (i = 0; i < ARRAY_SIZE(decon_clks_name); i++) { + struct clk *clk; + + clk = devm_clk_get(ctx->dev, decon_clks_name[i]); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + ctx->clks[i] = clk; + } + + ctx->addr = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(ctx->addr)) + return PTR_ERR(ctx->addr); + + ret = decon_conf_irq(ctx, "vsync", decon_irq_handler, 0); + if (ret < 0) + return ret; + ctx->irq_vsync = ret; + + ret = decon_conf_irq(ctx, "lcd_sys", decon_irq_handler, 0); + if (ret < 0) + return ret; + ctx->irq_lcd_sys = ret; + + ret = decon_conf_irq(ctx, "te", decon_te_irq_handler, + IRQF_TRIGGER_RISING); + if (ret < 0) + return ret; + if (ret) { + ctx->te_irq = ret; + ctx->out_type &= ~I80_HW_TRG; + } + + if (ctx->out_type & I80_HW_TRG) { + ctx->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node, + "samsung,disp-sysreg"); + if (IS_ERR(ctx->sysreg)) { + dev_err(dev, "failed to get system register\n"); + return PTR_ERR(ctx->sysreg); + } + } + + platform_set_drvdata(pdev, ctx); + + pm_runtime_enable(dev); + + ret = component_add(dev, &decon_component_ops); + if (ret) + goto err_disable_pm_runtime; + + return 0; + +err_disable_pm_runtime: + pm_runtime_disable(dev); + + return ret; +} + +static void exynos5433_decon_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + + component_del(&pdev->dev, &decon_component_ops); +} + +struct platform_driver exynos5433_decon_driver = { + .probe = exynos5433_decon_probe, + .remove = exynos5433_decon_remove, + .driver = { + .name = "exynos5433-decon", + .pm = pm_ptr(&exynos5433_decon_pm_ops), + .of_match_table = exynos5433_decon_driver_dt_match, + }, +}; diff --git a/drivers/gpu/drm/exynos/exynos7_drm_decon.c b/drivers/gpu/drm/exynos/exynos7_drm_decon.c new file mode 100644 index 000000000..bb74b17f9 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos7_drm_decon.c @@ -0,0 +1,852 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* drivers/gpu/drm/exynos/exynos7_drm_decon.c + * + * Copyright (C) 2014 Samsung Electronics Co.Ltd + * Authors: + * Akshu Agarwal + * Ajay Kumar + */ + +#include +#include +#include +#include +#include +#include +#include + +#include