@@ -17,6 +17,23 @@ if DRM_EXYNOS
comment "CRTCs"
+config DRM_EXYNOS9_DECON
+ bool "DECON on Exynos9"
+ default DRM_EXYNOS
+ select EXYNOS_IOMMU
+ help
+ Choose this option if you want to use Exynos DECON for DRM.
+
+config DRM_EXYNOS9_DP
+ bool "DisplayPort TX on Exynos"
+ depends on DRM_EXYNOS9_DECON
+ select DRM_PANEL
+ select DRM_DISPLAY_HELPER
+ select DRM_DISPLAY_DP_HELPER
+ default DRM_EXYNOS
+ help
+ This enables support for Exynos DisplayPort device.
+
config DRM_EXYNOS_FIMD
bool "FIMD"
depends on !FB_S3C
@@ -6,6 +6,9 @@
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_EXYNOS9_DECON) += exynos9_decon.o exynos_dpp.o exynos_dpu_dma.o
+exynosdrm-$(CONFIG_DRM_EXYNOS9_DP) += exynos_drm_dp.o exynos_drm_dp_link_training.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
new file mode 100644
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/component.h>
+#include <linux/irq.h>
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_vblank.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/exynos_drm.h>
+#include <drm/drm_blend.h>
+#include <drm/drm_framebuffer.h>
+
+#include "exynos_dpp.h"
+#include "exynos_drm_fb.h"
+
+#define DPP_RGB_IMG_WIDTH_MIN 16
+#define DPP_RGB_IMG_WIDTH_MAX 8192
+#define DPP_RGB_IMG_WIDTH_ALIGN 1
+
+#define DPP_RGB_IMG_HEIGHT_MIN 16
+#define DPP_RGB_IMG_HEIGHT_MAX 4096
+#define DPP_RGB_IMG_HEIGHT_ALIGN 1
+
+#define DPP_YUV_IMG_WIDTH_MIN 32
+#define DPP_YUV_IMG_WIDTH_MAX 8192
+#define DPP_YUV_IMG_WIDTH_ALIGN 2
+
+#define DPP_YUV_IMG_HEIGHT_MIN 16
+#define DPP_YUV_IMG_HEIGHT_MAX 4096
+#define DPP_YUV_IMG_HEIGHT_ALIGN 2
+
+static const struct of_device_id dpp_of_match[] = {
+ { .compatible = "samsung,exynosauto-dpp" },
+ {},
+};
+
+void dpp_update(struct exynos_dpp_context *dpp, unsigned int channel,
+ const struct exynos_drm_plane_state *state)
+{
+ writel(DPP_IMG_FORMAT(0), dpp->regs + 0x1000 + DPP_IN_CON);
+ writel(DPP_IMG_HEIGHT(state->src.h) | DPP_IMG_WIDTH(state->src.w),
+ dpp->regs + 0x1000 + DPP_IMG_SIZE);
+}
+
+static int dpp_bind(struct device *dev, struct device *master, void *data)
+{
+ struct drm_device *drm_dev = data;
+ struct exynos_drm_private *devpriv = drm_dev->dev_private;
+
+ devpriv->dpp_dev = dev;
+
+ return 0;
+}
+
+static const struct component_ops dpp_component_ops = {
+ .bind = dpp_bind,
+};
+
+static int dpp_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct exynos_dpp_context *dpp;
+ struct resource *res;
+
+ dpp = devm_kzalloc(dev, sizeof(struct exynos_dpp_context), GFP_KERNEL);
+ if (!dpp)
+ return -ENOMEM;
+
+ dpp->dev = dev;
+
+ dpp->aclk = devm_clk_get_enabled(dpp->dev, "aclk");
+ if (IS_ERR(dpp->aclk))
+ pr_err("failed to get clock");
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -EINVAL;
+
+ dpp->regs = devm_ioremap_resource(dev, res);
+
+ platform_set_drvdata(pdev, dpp);
+
+ return component_add(dev, &dpp_component_ops);
+}
+
+struct platform_driver dpp_driver = {
+ .probe = dpp_probe,
+ .driver = {
+ .name = "dpp",
+ .of_match_table = dpp_of_match,
+ },
+};
new file mode 100644
@@ -0,0 +1,278 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Samsung ExynosAuto DRM DPP(Display Pre-Processor) driver Header
+ *
+ * Copyright (C) 2018 Samsung Electronics Co.Ltd
+ */
+
+#ifndef _EXYNOS_DRM_DPP_H_
+#define _EXYNOS_DRM_DPP_H_
+
+#include <linux/clk.h>
+#include "exynos_drm_drv.h"
+
+/*
+ * DPUM DPP base: 0x18C2_0000
+ * - DPP GF0 offset: 0x1000
+ * - DPP GF1 offset: 0x2000
+ * - DPP GF2 offset: 0x3000
+ * - DPP GF3 offset: 0x4000
+ * - DPP GF4 offset: 0x5000
+ * - DPP GF5 offset: 0x6000
+ * - DPP VG0 offset: 0x7000
+ * - DPP VG1 offset: 0x8000
+ */
+#define DPP_ENABLE 0x0000
+#define DPP_SRSET (1 << 24)
+#define DPP_HDR_SEL (1 << 11)
+#define DPP_SFR_CLOCK_GATE_EN (1 << 10)
+#define DPP_SRAM_CLOCK_GATE_EN (1 << 9)
+#define DPP_INT_CLOCK_GATE_EN (1 << 8)
+#define DPP_ALL_CLOCK_GATE_EN_MASK (0x7 << 8)
+#define DPP_PSLVERR_EN (1 << 5)
+#define DPP_SFR_UPDATE_FORCE (1 << 4)
+#define DPP_QCHANNEL_EN (1 << 3)
+#define DPP_OP_STATUS (1 << 2)
+#define DPP_TZPC_FLAG (1 << 0)
+
+#define DPP_IRQ 0x0004
+#define DPP_CONFIG_ERROR (1 << 21)
+#define DPP_STATUS_FRAMEDONE_IRQ (1 << 16)
+#define DPP_ALL_IRQ_CLEAR (0x21 << 16)
+#define DPP_CONFIG_ERROR_MASK (1 << 6)
+#define DPP_IRQ_FRAMEDONE_MASK (1 << 1)
+#define DPP_ALL_IRQ_MASK (0x21 << 1)
+#define DPP_IRQ_ENABLE (1 << 0)
+#define DPP_ALL_IRQ (DPP_CONFIG_ERROR | DPP_STATUS_FRAMEDONE_IRQ)
+
+#define DPP_IN_CON 0x0008
+#define DPP_CSC_TYPE(_v) ((_v) << 18)
+#define DPP_CSC_TYPE_MASK (3 << 18)
+#define DPP_CSC_RANGE(_v) ((_v) << 17)
+#define DPP_CSC_RANGE_MASK (1 << 17)
+#define DPP_CSC_MODE(_v) ((_v) << 16)
+#define DPP_CSC_MODE_MASK (1 << 16)
+#define DPP_DITH_MASK_SEL (1 << 5)
+#define DPP_DITH_MASK_SPIN (1 << 4)
+// #define DPP_ALPHA_SEL(_v) ((_v) << 3)
+// #define DPP_ALPHA_SEL_MASK (1 << 3)
+#define DPP_IMG_FORMAT(_v) ((_v) << 0)
+#define DPP_IMG_FORMAT_MASK (0x7 << 0)
+#define DPP_IMG_FORMAT_ARGB8888 (0 << 0)
+#define DPP_IMG_FORMAT_ARGB8101010 (1 << 0)
+/* VG only */
+#define DPP_IMG_FORMAT_YUV420_8P (2 << 0)
+#define DPP_IMG_FORMAT_YUV420_P010 (3 << 0)
+#define DPP_IMG_FORMAT_YUV420_8P2 (4 << 0)
+#define DPP_IMG_FORMAT_YUV422_8P (5 << 0)
+#define DPP_IMG_FORMAT_YUV422_P210 (6 << 0)
+#define DPP_IMG_FORMAT_YUV422_8P2 (7 << 0)
+
+#define DPP_IMG_SIZE 0x0018
+#define DPP_IMG_HEIGHT(_v) ((_v) << 16)
+#define DPP_IMG_HEIGHT_MASK (0x1FFF << 16)
+#define DPP_IMG_WIDTH(_v) ((_v) << 0)
+#define DPP_IMG_WIDTH_MASK (0x1FFF << 0)
+enum dpp_attr {
+ DPP_ATTR_AFBC = 0,
+ DPP_ATTR_SAJC = 0,
+ DPP_ATTR_BLOCK = 1,
+ DPP_ATTR_FLIP = 2,
+ DPP_ATTR_ROT = 3, /* [KITT2] OTF Rotation is spec-out. */
+ DPP_ATTR_CSC = 4,
+ DPP_ATTR_SCALE = 5,
+ DPP_ATTR_HDR = 6,
+ DPP_ATTR_C_HDR = 7,
+ DPP_ATTR_C_HDR10_PLUS = 8,
+ DPP_ATTR_WCG = 9,
+ DPP_ATTR_SBWC = 10,
+ DPP_ATTR_HDR10_PLUS = 11,
+
+ DPP_ATTR_IDMA = 16,
+ DPP_ATTR_ODMA = 17,
+ DPP_ATTR_DPP = 18,
+ DPP_ATTR_SRAMC = 19,
+ DPP_ATTR_HDR_COMM = 20,
+};
+
+enum dpp_comp_type {
+ COMP_TYPE_NONE = 0,
+ COMP_TYPE_AFBC,
+ COMP_TYPE_SAJC,
+ COMP_TYPE_SBWC, /* lossless */
+ COMP_TYPE_SBWCL, /* lossy */
+};
+
+enum dpp_comp_blk_size {
+ /* SAJC indepedent block size */
+ SAJC_BLK_64B = 0,
+ SAJC_BLK_128B = 1,
+ SAJC_BLK_256B = 2,
+ /* SBWC blk byte num */
+ SBWC_BLK_32x2 = 2,
+ SBWC_BLK_32x3 = 3,
+ SBWC_BLK_32x4 = 4,
+ SBWC_BLK_32x5 = 5,
+ SBWC_BLK_32x6 = 6,
+};
+
+struct dpp_regs {
+ void __iomem *dpp_base_regs;
+ void __iomem *dma_base_regs;
+ void __iomem *dma_common_base_regs;
+ void __iomem *sramc_base_regs;
+ void __iomem *votf_base_regs;
+ void __iomem *scl_coef_base_regs;
+ void __iomem *hdr_comm_base_regs;
+ void __iomem *dpp_debug_base_regs;
+};
+
+/*
+ * src_width
+ * -----------------------------------------------
+ * | src_offset_x, |
+ * | src_offset_y img_width |
+ * | 0------------------------------- |
+ * | |//blk_offset_x,///////////////| |
+ * | |//blk_offset_y////////////////| |
+ * | |////0-------------//blk///////| img | src
+ * | |////| |/height/////|height | height
+ * | |////--------------////////////| |
+ * | |///////blk_width//////////////| |
+ * | -------------------------------- |
+ * | |
+ * -----------------------------------------------
+ *
+ * ///// accessed region
+ */
+struct dpp_region {
+ unsigned int src_width, src_height;
+ unsigned int src_offset_x, src_offset_y;
+
+ unsigned int img_width, img_height;
+
+ unsigned int blk_width, blk_height;
+ unsigned int blk_offset_x, blk_offset_y;
+};
+
+struct decon_frame {
+ int x;
+ int y;
+ u32 w;
+ u32 h;
+ u32 f_w;
+ u32 f_h;
+};
+
+struct decon_win_rect {
+ int x;
+ int y;
+ u32 w;
+ u32 h;
+};
+
+enum dpp_bpc {
+ DPP_BPC_8 = 0,
+ DPP_BPC_10,
+};
+
+struct dpp_config {
+ struct dpp_region region;
+ // enum dpu_pixel_format format;
+ u32 idma_addr[4];
+
+ bool is_bist;
+ bool is_afbc;
+ bool is_scale;
+ bool is_block;
+ bool is_4p;
+ u32 y_2b_strd;
+ u32 c_2b_strd;
+
+ u32 afbc_recov_cnt;
+};
+
+#define MAX_PLANE_ADDR_CNT 4
+
+struct dpp_params_info {
+ struct decon_frame src;
+ struct decon_frame dst;
+ struct decon_win_rect block;
+ u32 rot;
+
+ u32 min_luminance; /* TODO: remove, if can */
+ u32 max_luminance; /* TODO: remove, if can */
+ u32 y_hd_y2_stride; /* Luminance header (or Y-2B) stride */
+ u32 y_pl_c2_stride; /* Luminance payload (or C-2B) stride */
+ u32 c_hd_stride; /* Chrominance header stride */
+ u32 c_pl_stride; /* Chrominance payload stride */
+
+ bool is_scale;
+ bool is_block;
+ u32 format;
+ dma_addr_t addr[MAX_PLANE_ADDR_CNT];
+ u32 dataspace;
+ int h_ratio;
+ int v_ratio;
+ u32 standard;
+ u32 transfer;
+ u32 range;
+ u32 split;
+ enum dpp_bpc in_bpc;
+
+ unsigned long rcv_num;
+ enum dpp_comp_type comp_type;
+ enum dpp_comp_blk_size blk_size;
+
+ /* votf for output */
+ bool votf_o_en;
+ u32 votf_o_idx;
+ u32 votf_o_base_addr;
+ u32 votf_o_mfc_addr;
+
+ bool votf_en;
+ u32 votf_buf_idx;
+ u32 votf_base_addr;
+
+ /* v910 backward compatibility */;
+ struct dpp_config config_v1;
+};
+
+enum dpp_irq_type {
+ I_FRAMEDONE,
+ I_DEADLOCK,
+ I_READSLAVE,
+ I_CONFIG_ERR,
+ I_FRAMESTART,
+};
+
+/* reference between dpp device and decon/wod. */
+struct dpp_dev_data {
+ const struct dpp_cal_ops *cal_ops;
+ unsigned long irqflags;
+
+ const u8 nr_idma;
+ const u8 nr_dpuf;
+ const unsigned long attr;
+ const u32 *pixel_formats;
+ const u32 num_pixel_formats;
+};
+
+enum dpp_state {
+ DPP_STATE_OFF = 0,
+ DPP_STATE_ON,
+ DPP_STATE_WAIT_OFF,
+};
+
+struct exynos_dpp_context {
+ struct device *dev;
+ void __iomem *regs;
+
+ u32 type;
+ struct clk *aclk;
+};
+
+void dpp_update(struct exynos_dpp_context *dpp, unsigned int channel,
+ const struct exynos_drm_plane_state *state);
+
+#endif
new file mode 100644
@@ -0,0 +1,330 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
+
+#include "exynos_drm_fb.h"
+#include "exynos_dpu_dma.h"
+
+#define DMA_SHD_OFFSET 0x0800
+
+#define DPU_DMA_TEST_PATTERN0_3 0x0020
+#define DPU_DMA_TEST_PATTERN0_2 0x0024
+#define DPU_DMA_TEST_PATTERN0_1 0x0028
+#define DPU_DMA_TEST_PATTERN0_0 0x002C
+#define DPU_DMA_TEST_PATTERN1_3 0x0030
+#define DPU_DMA_TEST_PATTERN1_2 0x0034
+#define DPU_DMA_TEST_PATTERN1_1 0x0038
+#define DPU_DMA_TEST_PATTERN1_0 0x003C
+#define DPU_DMA_DEBUG_DATA 0x0104
+
+/*
+ * 1.1 - IDMA Register
+ * < DMA.offset >
+ * G0 G1 G2 G3 GF0 GF1 VG0 VG1
+ * 0x1000 0x2000 0x3000 0x4000 0x5000 0x6000 0x7000 0x8000
+ */
+
+/* Channel order GF0, G0, VG0, G1, GF1, G2, VG1, G3 */
+static unsigned int channel_map[] = { 5, 1, 7, 2, 6, 3, 8, 4 };
+
+#define IDMA_ENABLE 0x0000
+#define IDMA_IRQ 0x0004
+#define IDMA_AFBC_CONFLICT_IRQ BIT(25)
+#define IDMA_VR_CONFLICT_IRQ BIT(24)
+#define IDMA_AFBC_TIMEOUT_IRQ BIT(23)
+#define IDMA_RECOVERY_START_IRQ BIT(22)
+#define IDMA_CONFIG_ERROR BIT(21)
+#define IDMA_LOCAL_HW_RESET_DONE BIT(20)
+#define IDMA_READ_SLAVE_ERROR BIT(19)
+#define IDMA_STATUS_DEADLOCK_IRQ BIT(17)
+#define IDMA_STATUS_FRAMEDONE_IRQ BIT(16)
+#define IDMA_ALL_IRQ_CLEAR (0x3FB << 16)
+
+#define IDMA_ALL_IRQ \
+ (IDMA_AFBC_CONFLICT_IRQ | IDMA_VR_CONFLICT_IRQ | \
+ IDMA_AFBC_TIMEOUT_IRQ | IDMA_RECOVERY_START_IRQ | IDMA_CONFIG_ERROR | \
+ IDMA_LOCAL_HW_RESET_DONE | IDMA_READ_SLAVE_ERROR | \
+ IDMA_STATUS_DEADLOCK_IRQ | IDMA_STATUS_FRAMEDONE_IRQ)
+
+#define IDMA_AFBC_CONFLICT_MASK BIT(10)
+#define IDMA_CONFIG_ERROR_MASK BIT(6)
+#define IDMA_READ_SLAVE_ERROR_MASK BIT(4)
+#define IDMA_IRQ_DEADLOCK_MASK BIT(2)
+#define IDMA_IRQ_FRAMEDONE_MASK BIT(1)
+#define IDMA_ALL_IRQ_MASK (0x3FB << 1)
+#define IDMA_IRQ_ENABLE BIT(0)
+
+#define IDMA_IN_CON 0x0008
+#define IDMA_IMG_FORMAT(_v) ((_v) << 11)
+#define IDMA_IMG_FORMAT_MASK (0x3f << 11)
+#define IDMA_IMG_FORMAT_ARGB8888 (0)
+
+#define IDMA_BLOCK_EN BIT(3)
+
+#define IDMA_SRC_SIZE 0x0010
+#define IDMA_SRC_HEIGHT(_v) ((_v) << 16)
+#define IDMA_SRC_HEIGHT_MASK (0x3FFF << 16)
+#define IDMA_SRC_WIDTH(_v) ((_v) << 0)
+#define IDMA_SRC_WIDTH_MASK (0xFFFF << 0)
+
+#define IDMA_SRC_OFFSET 0x0014
+#define IDMA_SRC_OFFSET_Y(_v) ((_v) << 16)
+#define IDMA_SRC_OFFSET_Y_MASK (0x1FFF << 16)
+#define IDMA_SRC_OFFSET_X(_v) ((_v) << 0)
+#define IDMA_SRC_OFFSET_X_MASK (0x1FFF << 0)
+
+#define IDMA_IMG_SIZE 0x0018
+#define IDMA_IMG_HEIGHT(_v) ((_v) << 16)
+#define IDMA_IMG_HEIGHT_MASK (0x1FFF << 16)
+#define IDMA_IMG_WIDTH(_v) ((_v) << 0)
+#define IDMA_IMG_WIDTH_MASK (0x1FFF << 0)
+
+#define IDMA_BLOCK_OFFSET 0x0024
+#define IDMA_BLK_OFFSET_Y(_v) ((_v) << 16)
+#define IDMA_BLK_OFFSET_Y_MASK (0x1FFF << 16)
+#define IDMA_BLK_OFFSET_X(_v) ((_v) << 0)
+#define IDMA_BLK_OFFSET_X_MASK (0x1FFF << 0)
+
+#define IDMA_BLOCK_SIZE 0x0028
+#define IDMA_BLK_HEIGHT(_v) ((_v) << 16)
+#define IDMA_BLK_HEIGHT_MASK (0x1FFF << 16)
+#define IDMA_BLK_WIDTH(_v) ((_v) << 0)
+#define IDMA_BLK_WIDTH_MASK (0x1FFF << 0)
+
+#define IDMA_2BIT_STRIDE 0x0030
+#define IDMA_CHROMA_2B_STRIDE(_v) ((_v) << 16)
+#define IDMA_CHROMA_2B_STRIDE_MASK (0xFFFF << 16)
+#define IDMA_LUMA_2B_STRIDE(_v) ((_v) << 0)
+#define IDMA_LUMA_2B_STRIDE_MASK (0xFFFF << 0)
+
+#define IDMA_IN_BASE_ADDR_Y 0x0040
+#define IDMA_IN_BASE_ADDR_C 0x0044
+#define IDMA_IN_BASE_ADDR_Y2 0x0048
+#define IDMA_IN_BASE_ADDR_C2 0x004C
+
+#define IDMA_IN_REQ_DEST 0x0068
+#define IDMA_IN_REG_DEST_SEL(_v) ((_v) << 0)
+#define IDMA_IN_REG_DEST_SEL_MASK (0x3 << 0)
+
+#define IDMA_S_CFG_ERR_STATE 0x0870
+#define IDMA_S_CFG_ERR_IMG_AFBC BIT(13)
+#define IDMA_S_CFG_ERR_IMG_ROTATION BIT(12)
+#define IDMA_S_CFG_ERR_IMG_FORMAT BIT(11)
+#define IDMA_S_CFG_ERR_SRC_WIDTH BIT(10)
+#define IDMA_S_CFG_ERR_CHROM_STRIDE BIT(9)
+#define IDMA_S_CFG_ERR_BASE_ADDR_Y BIT(8)
+#define IDMA_S_CFG_ERR_BASE_ADDR_C BIT(7)
+#define IDMA_S_CFG_ERR_IMG_WIDTH_AFBC BIT(6)
+#define IDMA_S_CFG_ERR_IMG_WIDTH BIT(5)
+#define IDMA_S_CFG_ERR_IMG_HEIGHT_ROTATION BIT(4)
+#define IDMA_S_CFG_ERR_IMG_HEIGHT BIT(3)
+#define IDMA_S_CFG_ERR_BLOCKING BIT(2)
+#define IDMA_S_CFG_ERR_SRC_OFFSET_X BIT(1)
+#define IDMA_S_CFG_ERR_SRC_OFFSET_Y BIT(0)
+#define IDMA_S_CFG_ERR_GET(_v) (((_v) >> 0) & 0x7FF)
+
+static inline uint32_t dma_read(struct exynos_dpu_dma_context *ctx, u32 idx,
+ u32 offset)
+{
+ return readl(ctx->regs + 0x1000 * idx + offset);
+}
+static inline uint32_t dma_read_mask(struct exynos_dpu_dma_context *ctx,
+ u32 idx, u32 offset, u32 mask)
+{
+ uint32_t val = readl(ctx->regs + 0x1000 * idx + offset);
+ val &= (mask);
+ return val;
+}
+static inline void dma_write(struct exynos_dpu_dma_context *ctx, u32 idx,
+ u32 offset, u32 val)
+{
+ writel(val, ctx->regs + 0x1000 * idx + offset);
+}
+static inline void dma_write_mask(struct exynos_dpu_dma_context *ctx, u32 idx,
+ u32 offset, u32 val, u32 mask)
+{
+ uint32_t old = dma_read(ctx, idx, offset);
+ val = (val & mask) | (old & ~mask);
+ dma_write(ctx, idx, offset, val);
+}
+
+static void dma_reg_clear_irq(struct exynos_dpu_dma_context *ctx, u32 id,
+ u32 irq)
+{
+ dma_write_mask(ctx, id, IDMA_IRQ, ~0, irq);
+}
+
+static u32 idma_reg_get_irq_and_clear(struct exynos_dpu_dma_context *ctx,
+ u32 id)
+{
+ u32 val, cfg_err;
+
+ val = dma_read_mask(ctx, id, IDMA_IRQ, IDMA_ALL_IRQ);
+
+ if (val & IDMA_AFBC_CONFLICT_IRQ) {
+ pr_err("AFBC conflict occur\n");
+ }
+
+ if (val & IDMA_VR_CONFLICT_IRQ) {
+ pr_err("VR conflict occur\n");
+ }
+
+ if (val & IDMA_RECOVERY_START_IRQ) {
+ pr_err("recovery start occur\n");
+ }
+
+ if (val & IDMA_CONFIG_ERROR) {
+ cfg_err = dma_read(ctx, id, IDMA_S_CFG_ERR_STATE);
+ pr_err("config error occur(0x%x)\n", cfg_err);
+ }
+
+ if (val & IDMA_LOCAL_HW_RESET_DONE) {
+ pr_err("local hw reset done\n");
+ }
+
+ if (val & IDMA_READ_SLAVE_ERROR) {
+ pr_err("read slave error occur\n");
+ }
+
+ if (val & IDMA_STATUS_DEADLOCK_IRQ) {
+ pr_err("status deadlock occur\n");
+ }
+
+ if (val & IDMA_STATUS_FRAMEDONE_IRQ) {
+ pr_debug("frame done occur\n");
+ }
+
+ dma_reg_clear_irq(ctx, id, val);
+
+ return val;
+}
+
+static void dma_reg_init(struct exynos_dpu_dma_context *ctx, u32 id,
+ const unsigned long attr)
+{
+ dma_write_mask(ctx, id, IDMA_IRQ, 0, IDMA_ALL_IRQ_MASK);
+ dma_write_mask(ctx, id, IDMA_IRQ, ~0, IDMA_IRQ_ENABLE);
+}
+
+static irqreturn_t dma_irq_handler(int irq, void *priv)
+{
+ u32 val_dma = 0;
+ struct exynos_dpu_dma_context *ctx = priv;
+
+ val_dma = idma_reg_get_irq_and_clear(ctx, 5);
+
+ /* This interrupt is not mine */
+ if (!val_dma)
+ return IRQ_NONE;
+
+ return IRQ_HANDLED;
+}
+
+int dpu_dma_update(struct exynos_dpu_dma_context *ctx, unsigned int channel,
+ struct exynos_drm_plane_state *state)
+{
+ struct drm_framebuffer *fb = state->base.fb;
+ unsigned int idma = channel_map[channel];
+
+ dma_reg_init(ctx, idma, 0);
+ dma_write(ctx, idma, IDMA_IN_BASE_ADDR_Y,
+ exynos_drm_fb_dma_addr(fb, 0));
+ dma_write(ctx, idma, IDMA_SRC_OFFSET,
+ IDMA_SRC_OFFSET_Y(state->src.y) |
+ IDMA_SRC_OFFSET_X(state->src.x));
+ dma_write(ctx, idma, IDMA_SRC_SIZE,
+ IDMA_SRC_HEIGHT(fb->height) |
+ IDMA_SRC_WIDTH(fb->pitches[0] / fb->format->cpp[0]));
+ dma_write(ctx, idma, IDMA_IMG_SIZE,
+ IDMA_IMG_HEIGHT(state->src.h) | IDMA_IMG_WIDTH(state->src.w));
+
+ dma_write_mask(ctx, idma, IDMA_IN_CON,
+ IDMA_IMG_FORMAT(IDMA_IMG_FORMAT_ARGB8888),
+ IDMA_IMG_FORMAT_MASK);
+
+ return 0;
+}
+
+static int dma_bind(struct device *dev, struct device *master, void *data)
+{
+ struct exynos_dpu_dma_context *ctx = dev_get_drvdata(dev);
+ struct drm_device *drm_dev = data;
+
+ return exynos_drm_register_dma(drm_dev, dev, &ctx->dma_priv);
+};
+
+static const struct component_ops dma_component_ops = {
+ .bind = dma_bind,
+};
+
+static int dpu_dma_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct exynos_dpu_dma_context *ctx;
+ struct resource *res;
+
+ int ret;
+
+ ctx = devm_kzalloc(dev, sizeof(struct exynos_dpu_dma_context),
+ GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, ctx);
+
+ ctx->aclk = devm_clk_get_enabled(dev, "aclk");
+ if (IS_ERR(ctx->aclk))
+ return dev_err_probe(dev, PTR_ERR(ctx->aclk),
+ "Cannot get aclk\n");
+
+ ctx->irq = platform_get_irq(pdev, 0);
+ if (ctx->irq < 0)
+ return ctx->irq;
+
+ ret = devm_request_irq(dev, ctx->irq, dma_irq_handler, 0, pdev->name,
+ ctx);
+ if (ret)
+ return ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -EINVAL;
+
+ ctx->regs = devm_ioremap_resource(dev, res);
+ if (!ctx->regs)
+ return -ENOMEM;
+
+ pm_runtime_enable(dev);
+ /* For turn on attached SYSMMU */
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0) {
+ pm_runtime_disable(dev);
+ return ret;
+ }
+
+ component_add(dev, &dma_component_ops);
+
+ return 0;
+}
+
+static const struct of_device_id dpu_dma_of_match[] = {
+ { .compatible = "samsung,exynosauto-dpu-dma" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, dpu_dma_of_match);
+
+struct platform_driver dpu_dma_driver = {
+ .probe = dpu_dma_probe,
+ .driver = {
+ .name = "dpu_dma_driver",
+ .of_match_table = of_match_ptr(dpu_dma_of_match),
+ },
+};
new file mode 100644
@@ -0,0 +1,16 @@
+#ifndef _EXYNOSAUTO_DPU_DMA_H_
+#define _EXYNOSAUTO_DPU_DMA_H_
+
+#include "exynos_drm_drv.h"
+
+struct exynos_dpu_dma_context {
+ struct clk *aclk;
+ int irq;
+ void __iomem *regs;
+ void *dma_priv;
+};
+
+int dpu_dma_update(struct exynos_dpu_dma_context *ctx, unsigned int channel,
+ struct exynos_drm_plane_state *state);
+
+#endif
\ No newline at end of file
Signed-off-by: Kwanghoon Son <k.son@samsung.com> --- drivers/gpu/drm/exynos/Kconfig | 17 ++ drivers/gpu/drm/exynos/Makefile | 3 + drivers/gpu/drm/exynos/exynos_dpp.c | 96 ++++++++++ drivers/gpu/drm/exynos/exynos_dpp.h | 278 +++++++++++++++++++++++++++ drivers/gpu/drm/exynos/exynos_dpu_dma.c | 330 ++++++++++++++++++++++++++++++++ drivers/gpu/drm/exynos/exynos_dpu_dma.h | 16 ++ 6 files changed, 740 insertions(+)