Signed-off-by: Kwanghoon Son <k.son@xxxxxxxxxxx> --- 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(+) diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig index 733b109a5095..f072743c36a0 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig @@ -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 diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile index 2fd2f3ee4fcf..93394501f975 100644 --- a/drivers/gpu/drm/exynos/Makefile +++ b/drivers/gpu/drm/exynos/Makefile @@ -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 diff --git a/drivers/gpu/drm/exynos/exynos_dpp.c b/drivers/gpu/drm/exynos/exynos_dpp.c new file mode 100644 index 000000000000..183af97460c9 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_dpp.c @@ -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, + }, +}; diff --git a/drivers/gpu/drm/exynos/exynos_dpp.h b/drivers/gpu/drm/exynos/exynos_dpp.h new file mode 100644 index 000000000000..a76b3bd09670 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_dpp.h @@ -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 diff --git a/drivers/gpu/drm/exynos/exynos_dpu_dma.c b/drivers/gpu/drm/exynos/exynos_dpu_dma.c new file mode 100644 index 000000000000..57089d64dbd4 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_dpu_dma.c @@ -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), + }, +}; diff --git a/drivers/gpu/drm/exynos/exynos_dpu_dma.h b/drivers/gpu/drm/exynos/exynos_dpu_dma.h new file mode 100644 index 000000000000..3708ef55b70d --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_dpu_dma.h @@ -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 -- 2.39.2