summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLiu Ying <victor.liu@nxp.com>2019-04-03 14:48:39 +0800
committerDong Aisheng <aisheng.dong@nxp.com>2019-11-25 15:58:39 +0800
commit97a558deb12b1fec496d76315af9a88668c2787e (patch)
treebcd12b790e90bbc56fa7d7e7a9a011635fa60ba5
parent7724e0e0599b07daf9e54ce7de2a74d4acabc851 (diff)
MLK-21378-2 gpu: imx: Add imx8_dprc support
Fast-forward imx8_dprc driver from imx_4.14.y. Signed-off-by: Liu Ying <victor.liu@nxp.com> [ Liu Ying: scfw call updates and other small tweaks for upgrade]
-rw-r--r--drivers/gpu/imx/Kconfig5
-rw-r--r--drivers/gpu/imx/Makefile1
-rw-r--r--drivers/gpu/imx/imx8_dprc.c870
-rw-r--r--include/video/imx8-prefetch.h24
4 files changed, 900 insertions, 0 deletions
diff --git a/drivers/gpu/imx/Kconfig b/drivers/gpu/imx/Kconfig
index 2e98a7a47cf9..3d9af00e94d6 100644
--- a/drivers/gpu/imx/Kconfig
+++ b/drivers/gpu/imx/Kconfig
@@ -3,4 +3,9 @@ config IMX8_PRG
default y if IMX_DPU_CORE=y
default m if IMX_DPU_CORE=m
+config IMX8_DPRC
+ tristate
+ default y if IMX_DPU_CORE=y
+ default m if IMX_DPU_CORE=m
+
source "drivers/gpu/imx/ipu-v3/Kconfig"
diff --git a/drivers/gpu/imx/Makefile b/drivers/gpu/imx/Makefile
index 3b8484b8be3f..c09cbc95bbeb 100644
--- a/drivers/gpu/imx/Makefile
+++ b/drivers/gpu/imx/Makefile
@@ -1,3 +1,4 @@
obj-$(CONFIG_IMX8_PRG) += imx8_prg.o
+obj-$(CONFIG_IMX8_DPRC) += imx8_dprc.o
obj-$(CONFIG_IMX_IPUV3_CORE) += ipu-v3/
diff --git a/drivers/gpu/imx/imx8_dprc.c b/drivers/gpu/imx/imx8_dprc.c
new file mode 100644
index 000000000000..a7113446f56b
--- /dev/null
+++ b/drivers/gpu/imx/imx8_dprc.c
@@ -0,0 +1,870 @@
+/*
+ * Copyright 2017-2019 NXP
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+#include <drm/drm_fourcc.h>
+#include <dt-bindings/firmware/imx/rsrc.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/firmware/imx/sci.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <video/imx8-prefetch.h>
+
+#define SET 0x4
+#define CLR 0x8
+#define TOG 0xc
+
+#define SYSTEM_CTRL0 0x00
+#define BCMD2AXI_MASTR_ID_CTRL BIT(16)
+#define SW_SHADOW_LOAD_SEL BIT(4)
+#define SHADOW_LOAD_EN BIT(3)
+#define REPEAT_EN BIT(2)
+#define SOFT_RESET BIT(1)
+#define RUN_EN BIT(0) /* self-clearing */
+
+#define IRQ_MASK 0x20
+#define IRQ_MASK_STATUS 0x30
+#define IRQ_NONMASK_STATUS 0x40
+#define DPR2RTR_FIFO_LOAD_BUF_RDY_UV_ERROR BIT(7)
+#define DPR2RTR_FIFO_LOAD_BUF_RDY_YRGB_ERROR BIT(6)
+#define DPR2RTR_UV_FIFO_OVFL BIT(5)
+#define DPR2RTR_YRGB_FIFO_OVFL BIT(4)
+#define IRQ_AXI_READ_ERROR BIT(3)
+#define IRQ_DPR_SHADOW_LOADED_MASK BIT(2)
+#define IRQ_DPR_RUN BIT(1)
+#define IRQ_DPR_CRTL_DONE BIT(0)
+#define IRQ_ERROR_MASK 0xf8
+#define IRQ_CTRL_MASK 0x7
+
+#define MODE_CTRL0 0x50
+#define PIX_COMP_SEL_MASK 0x3fc00
+#define A_COMP_SEL(byte) (((byte) & 0x3) << 16)
+#define R_COMP_SEL(byte) (((byte) & 0x3) << 14)
+#define G_COMP_SEL(byte) (((byte) & 0x3) << 12)
+#define B_COMP_SEL(byte) (((byte) & 0x3) << 10)
+#define PIX_UV_SWAP BIT(9)
+#define VU BIT(9)
+#define UV 0
+#define PIXEL_LUMA_UV_SWAP BIT(8)
+#define UYVY BIT(8)
+#define YUYV 0
+#define PIX_SIZE 0xc0
+enum {
+ PIX_SIZE_8BIT = (0 << 6),
+ PIX_SIZE_16BIT = (1 << 6),
+ PIX_SIZE_32BIT = (2 << 6),
+ PIX_SIZE_RESERVED = (3 << 6),
+};
+#define COMP_2PLANE_EN BIT(5)
+#define YUV_EN BIT(4)
+#define TILE_TYPE 0xc
+enum {
+ LINEAR_TILE = (0 << 2),
+ GPU_STANDARD_TILE = (1 << 2),
+ GPU_SUPER_TILE = (2 << 2),
+ VPU_TILE = (3 << 2),
+};
+#define RTR_4LINE_BUF_EN BIT(1)
+#define LINE4 BIT(1)
+#define LINE8 0
+#define RTR_3BUF_EN BIT(0)
+#define BUF3 BIT(0)
+#define BUF2 0
+
+#define FRAME_CTRL0 0x70
+#define PITCH(n) (((n) & 0xffff) << 16)
+#define ROT_FLIP_ORDER_EN BIT(4)
+#define ROT_FIRST BIT(4)
+#define FLIP_FIRST 0
+#define ROT_ENC 0xc
+#define DEGREE(n) ((((n) / 90) & 0x3) << 2)
+#define VFLIP_EN BIT(1)
+#define HFLIP_EN BIT(0)
+
+#define FRAME_1P_CTRL0 0x90
+#define FRAME_2P_CTRL0 0xe0
+#define MAX_BYTES_PREQ 0x7
+enum {
+ BYTE_64 = 0x0,
+ BYTE_128 = 0x1,
+ BYTE_256 = 0x2,
+ BYTE_512 = 0x3,
+ BYTE_1K = 0x4,
+ BYTE_2K = 0x5,
+ BYTE_4K = 0x6,
+};
+
+#define FRAME_1P_PIX_X_CTRL 0xa0
+#define FRAME_2P_PIX_X_CTRL 0xf0
+#define NUM_X_PIX_WIDE(n) ((n) & 0xffff)
+#define FRAME_PIX_X_ULC_CTRL 0xf0
+#define CROP_ULC_X(n) ((n) & 0xffff)
+
+#define FRAME_1P_PIX_Y_CTRL 0xb0
+#define FRAME_2P_PIX_Y_CTRL 0x100
+#define NUM_Y_PIX_HIGH(n) ((n) & 0xffff)
+#define FRAME_PIX_Y_ULC_CTRL 0x100
+#define CROP_ULC_Y(n) ((n) & 0xffff)
+
+#define FRAME_1P_BASE_ADDR_CTRL0 0xc0
+#define FRAME_2P_BASE_ADDR_CTRL0 0x110
+
+#define STATUS_CTRL0 0x130
+#define STATUS_SRC_SEL 0x70000
+enum {
+ DPR_CTRL = 0x0,
+ PREFETCH_1PLANE = 0x1,
+ RESPONSE_1PLANE = 0x2,
+ PREFETCH_2PLANE = 0x3,
+ RESPONSE_2PLANE = 0x4,
+};
+#define STATUS_MUX_SEL 0x7
+
+#define STATUS_CTRL1 0x140
+
+#define RTRAM_CTRL0 0x200
+#define ABORT_SEL BIT(7)
+#define ABORT BIT(7)
+#define STALL 0
+#define THRES_LOW_MASK 0x70
+#define THRES_LOW(n) (((n) & 0x7) << 4)
+#define THRES_HIGH_MASK 0xe
+#define THRES_HIGH(n) (((n) & 0x7) << 1)
+#define NUM_ROWS_ACTIVE BIT(0)
+#define ROWS_0_6 BIT(0)
+#define ROWS_0_4 0
+
+struct dprc {
+ struct device *dev;
+ void __iomem *base;
+ struct list_head list;
+ struct clk *clk_apb;
+ struct clk *clk_b;
+ struct clk *clk_rtram;
+ spinlock_t spin_lock;
+ u32 sc_resource;
+ bool is_blit_chan;
+
+ /* The second one, if non-NULL, is auxiliary for UV buffer. */
+ struct prg *prgs[2];
+ bool has_aux_prg;
+ bool use_aux_prg;
+};
+
+struct dprc_format_info {
+ u32 format;
+ u8 depth;
+ u8 num_planes;
+ u8 cpp[3];
+ u8 hsub;
+ u8 vsub;
+};
+
+static const struct dprc_format_info formats[] = {
+ {
+ .format = DRM_FORMAT_RGB565,
+ .depth = 16, .num_planes = 1, .cpp = { 2, 0, 0 },
+ .hsub = 1, .vsub = 1,
+ }, {
+ .format = DRM_FORMAT_ARGB8888,
+ .depth = 32, .num_planes = 1, .cpp = { 4, 0, 0 },
+ .hsub = 1, .vsub = 1,
+ }, {
+ .format = DRM_FORMAT_XRGB8888,
+ .depth = 24, .num_planes = 1, .cpp = { 4, 0, 0 },
+ .hsub = 1, .vsub = 1,
+ }, {
+ .format = DRM_FORMAT_ABGR8888,
+ .depth = 32, .num_planes = 1, .cpp = { 4, 0, 0 },
+ .hsub = 1, .vsub = 1,
+ }, {
+ .format = DRM_FORMAT_XBGR8888,
+ .depth = 24, .num_planes = 1, .cpp = { 4, 0, 0 },
+ .hsub = 1, .vsub = 1,
+ }, {
+ .format = DRM_FORMAT_RGBA8888,
+ .depth = 32, .num_planes = 1, .cpp = { 4, 0, 0 },
+ .hsub = 1, .vsub = 1,
+ }, {
+ .format = DRM_FORMAT_RGBX8888,
+ .depth = 24, .num_planes = 1, .cpp = { 4, 0, 0 },
+ .hsub = 1, .vsub = 1,
+ }, {
+ .format = DRM_FORMAT_BGRA8888,
+ .depth = 32, .num_planes = 1, .cpp = { 4, 0, 0 },
+ .hsub = 1, .vsub = 1,
+ }, {
+ .format = DRM_FORMAT_BGRX8888,
+ .depth = 24, .num_planes = 1, .cpp = { 4, 0, 0 },
+ .hsub = 1, .vsub = 1,
+ }, {
+ .format = DRM_FORMAT_NV12,
+ .depth = 0, .num_planes = 2, .cpp = { 1, 2, 0 },
+ .hsub = 2, .vsub = 2,
+ }, {
+ .format = DRM_FORMAT_NV21,
+ .depth = 0, .num_planes = 2, .cpp = { 1, 2, 0 },
+ .hsub = 2, .vsub = 2,
+ }, {
+ .format = DRM_FORMAT_YUYV,
+ .depth = 0, .num_planes = 1, .cpp = { 2, 0, 0 },
+ .hsub = 2, .vsub = 1,
+ }, {
+ .format = DRM_FORMAT_UYVY,
+ .depth = 0, .num_planes = 1, .cpp = { 2, 0, 0 },
+ .hsub = 2, .vsub = 1,
+ }
+};
+
+static const struct dprc_format_info *dprc_format_info(u32 format)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(formats); ++i) {
+ if (formats[i].format == format)
+ return &formats[i];
+ }
+
+ return NULL;
+}
+
+static DEFINE_MUTEX(dprc_list_mutex);
+static LIST_HEAD(dprc_list);
+
+static inline u32 dprc_read(struct dprc *dprc, unsigned int offset)
+{
+ return readl(dprc->base + offset);
+}
+
+static inline void dprc_write(struct dprc *dprc, u32 value, unsigned int offset)
+{
+ writel(value, dprc->base + offset);
+}
+
+static void dprc_reset(struct dprc *dprc)
+{
+ dprc_write(dprc, SOFT_RESET, SYSTEM_CTRL0 + SET);
+
+ if (dprc->is_blit_chan)
+ usleep_range(10, 20);
+ else
+ usleep_range(1000, 2000);
+
+ dprc_write(dprc, SOFT_RESET, SYSTEM_CTRL0 + CLR);
+}
+
+void dprc_enable(struct dprc *dprc)
+{
+ if (WARN_ON(!dprc))
+ return;
+
+ prg_enable(dprc->prgs[0]);
+ if (dprc->use_aux_prg)
+ prg_enable(dprc->prgs[1]);
+}
+EXPORT_SYMBOL_GPL(dprc_enable);
+
+void dprc_disable(struct dprc *dprc)
+{
+ if (WARN_ON(!dprc))
+ return;
+
+ dprc_write(dprc, SHADOW_LOAD_EN | SW_SHADOW_LOAD_SEL, SYSTEM_CTRL0);
+
+ prg_disable(dprc->prgs[0]);
+ if (dprc->has_aux_prg)
+ prg_disable(dprc->prgs[1]);
+
+ prg_reg_update(dprc->prgs[0]);
+ if (dprc->has_aux_prg)
+ prg_reg_update(dprc->prgs[1]);
+}
+EXPORT_SYMBOL_GPL(dprc_disable);
+
+static void dprc_dpu_gpr_configure(struct dprc *dprc, unsigned int stream_id)
+{
+ struct imx_sc_ipc *ipc_handle;
+
+ imx_scu_get_handle(&ipc_handle);
+ imx_sc_misc_set_control(ipc_handle,
+ dprc->sc_resource, IMX_SC_C_KACHUNK_SEL, stream_id);
+}
+
+static void dprc_prg_sel_configure(struct dprc *dprc, u32 resource, bool enable)
+{
+ struct imx_sc_ipc *ipc_handle;
+
+ imx_scu_get_handle(&ipc_handle);
+ imx_sc_misc_set_control(ipc_handle, resource, IMX_SC_C_SEL0, enable);
+}
+
+void dprc_configure(struct dprc *dprc, unsigned int stream_id,
+ unsigned int width, unsigned int height,
+ unsigned int x_offset, unsigned int y_offset,
+ unsigned int stride, u32 format, u64 modifier,
+ unsigned long baddr, unsigned long uv_baddr,
+ bool start, bool aux_start, bool interlace_frame)
+{
+ const struct dprc_format_info *info = dprc_format_info(format);
+ unsigned int dprc_width = width + x_offset;
+ unsigned int dprc_height;
+ unsigned int p1_w, p1_h, p2_w, p2_h;
+ unsigned int prg_stride = width * info->cpp[0];
+ unsigned int bpp = 8 * info->cpp[0];
+ unsigned int preq;
+ unsigned int mt_w = 0, mt_h = 0; /* w/h in a micro-tile */
+ u32 val;
+
+ if (WARN_ON(!dprc))
+ return;
+
+ dprc->use_aux_prg = false;
+
+ if (start) {
+ dprc_reset(dprc);
+
+ if (!dprc->is_blit_chan)
+ dprc_dpu_gpr_configure(dprc, stream_id);
+ }
+
+ if (interlace_frame) {
+ height /= 2;
+ y_offset /= 2;
+ }
+
+ dprc_height = height + y_offset;
+
+ /* disable all control irqs and enable all error irqs */
+ dprc_write(dprc, IRQ_CTRL_MASK, IRQ_MASK);
+
+ if (info->num_planes > 1) {
+ p1_w = round_up(dprc_width, modifier ? 8 : 64);
+ p1_h = round_up(dprc_height, 8);
+
+ p2_w = p1_w;
+ if (modifier)
+ p2_h = dprc_height / info->vsub;
+ else
+ p2_h = round_up((dprc_height / info->vsub), 8);
+
+ preq = modifier ? BYTE_64 : BYTE_1K;
+
+ dprc_write(dprc, preq, FRAME_2P_CTRL0);
+ if (dprc->sc_resource == IMX_SC_R_DC_0_BLIT1 ||
+ dprc->sc_resource == IMX_SC_R_DC_1_BLIT1) {
+ dprc_prg_sel_configure(dprc,
+ dprc->sc_resource == IMX_SC_R_DC_0_BLIT1 ?
+ IMX_SC_R_DC_0_BLIT0 : IMX_SC_R_DC_1_BLIT0,
+ true);
+ prg_set_auxiliary(dprc->prgs[1]);
+ dprc->has_aux_prg = true;
+ }
+ dprc_write(dprc, uv_baddr, FRAME_2P_BASE_ADDR_CTRL0);
+ } else {
+ switch (dprc->sc_resource) {
+ case IMX_SC_R_DC_0_BLIT0:
+ case IMX_SC_R_DC_1_BLIT0:
+ dprc_prg_sel_configure(dprc, dprc->sc_resource, false);
+ prg_set_primary(dprc->prgs[0]);
+ break;
+ case IMX_SC_R_DC_0_BLIT1:
+ case IMX_SC_R_DC_1_BLIT1:
+ dprc->has_aux_prg = false;
+ break;
+ default:
+ break;
+ }
+
+ switch (modifier) {
+ case DRM_FORMAT_MOD_VIVANTE_TILED:
+ p1_w = round_up(dprc_width, info->cpp[0] == 2 ? 8 : 4);
+ break;
+ case DRM_FORMAT_MOD_VIVANTE_SUPER_TILED:
+ if (dprc->is_blit_chan)
+ p1_w = round_up(dprc_width,
+ info->cpp[0] == 2 ? 8 : 4);
+ else
+ p1_w = round_up(dprc_width, 64);
+ break;
+ default:
+ p1_w = round_up(dprc_width,
+ info->cpp[0] == 2 ? 32 : 16);
+ break;
+ }
+ p1_h = round_up(dprc_height, 4);
+ }
+
+ dprc_write(dprc, PITCH(stride), FRAME_CTRL0);
+ switch (modifier) {
+ case DRM_FORMAT_MOD_AMPHION_TILED:
+ preq = BYTE_64;
+ mt_w = 8;
+ mt_h = 8;
+ break;
+ case DRM_FORMAT_MOD_VIVANTE_TILED:
+ case DRM_FORMAT_MOD_VIVANTE_SUPER_TILED:
+ if (bpp == 16) {
+ preq = BYTE_64;
+ mt_w = 8;
+ } else {
+ preq = (x_offset % 8) ? BYTE_64 : BYTE_128;
+ mt_w = 4;
+ }
+ mt_h = 4;
+ break;
+ default:
+ preq = BYTE_1K;
+ break;
+ }
+ dprc_write(dprc, preq, FRAME_1P_CTRL0);
+ dprc_write(dprc, NUM_X_PIX_WIDE(p1_w), FRAME_1P_PIX_X_CTRL);
+ dprc_write(dprc, NUM_Y_PIX_HIGH(p1_h), FRAME_1P_PIX_Y_CTRL);
+ dprc_write(dprc, baddr, FRAME_1P_BASE_ADDR_CTRL0);
+ if (modifier) {
+ dprc_write(dprc, CROP_ULC_X(round_down(x_offset, mt_w)),
+ FRAME_PIX_X_ULC_CTRL);
+ dprc_write(dprc, CROP_ULC_Y(round_down(y_offset, mt_h)),
+ FRAME_PIX_Y_ULC_CTRL);
+ } else {
+ dprc_write(dprc, CROP_ULC_X(0), FRAME_PIX_X_ULC_CTRL);
+ dprc_write(dprc, CROP_ULC_Y(0), FRAME_PIX_Y_ULC_CTRL);
+ }
+
+ val = dprc_read(dprc, RTRAM_CTRL0);
+ val &= ~THRES_LOW_MASK;
+ val |= THRES_LOW(3);
+ val &= ~THRES_HIGH_MASK;
+ val |= THRES_HIGH(7);
+ dprc_write(dprc, val, RTRAM_CTRL0);
+
+ val = dprc_read(dprc, MODE_CTRL0);
+ val &= ~PIX_UV_SWAP;
+ val &= ~PIXEL_LUMA_UV_SWAP;
+ val &= ~COMP_2PLANE_EN;
+ val &= ~YUV_EN;
+ val &= ~TILE_TYPE;
+ switch (modifier) {
+ case DRM_FORMAT_MOD_NONE:
+ break;
+ case DRM_FORMAT_MOD_AMPHION_TILED:
+ val |= VPU_TILE;
+ break;
+ case DRM_FORMAT_MOD_VIVANTE_TILED:
+ val |= GPU_STANDARD_TILE;
+ break;
+ case DRM_FORMAT_MOD_VIVANTE_SUPER_TILED:
+ val |= GPU_SUPER_TILE;
+ break;
+ default:
+ dev_err(dprc->dev, "unsupported modifier 0x%016llx\n",
+ modifier);
+ return;
+ }
+ val &= ~RTR_4LINE_BUF_EN;
+ val |= info->num_planes > 1 ? LINE8 : LINE4;
+ val &= ~RTR_3BUF_EN;
+ val |= BUF2;
+ val &= ~(PIX_COMP_SEL_MASK | PIX_SIZE);
+ switch (format) {
+ case DRM_FORMAT_ARGB8888:
+ case DRM_FORMAT_XRGB8888:
+ case DRM_FORMAT_ABGR8888:
+ case DRM_FORMAT_XBGR8888:
+ case DRM_FORMAT_RGBA8888:
+ case DRM_FORMAT_RGBX8888:
+ case DRM_FORMAT_BGRA8888:
+ case DRM_FORMAT_BGRX8888:
+ /*
+ * It turns out pixel components are mapped directly
+ * without position change via DPR processing with
+ * the following color component configurations.
+ * Leave the pixel format to be handled by the
+ * display controllers.
+ */
+ val |= A_COMP_SEL(3) | R_COMP_SEL(2) |
+ G_COMP_SEL(1) | B_COMP_SEL(0);
+ val |= PIX_SIZE_32BIT;
+ break;
+ case DRM_FORMAT_YUYV:
+ case DRM_FORMAT_UYVY:
+ val |= YUV_EN;
+ /* fall-through */
+ case DRM_FORMAT_RGB565:
+ val |= PIX_SIZE_16BIT;
+ break;
+ case DRM_FORMAT_NV12:
+ case DRM_FORMAT_NV21:
+ dprc->use_aux_prg = true;
+
+ val |= COMP_2PLANE_EN;
+ val |= YUV_EN;
+ val |= PIX_SIZE_8BIT;
+ break;
+ default:
+ dev_err(dprc->dev, "unsupported format 0x%08x\n", format);
+ return;
+ }
+ dprc_write(dprc, val, MODE_CTRL0);
+
+ if (dprc->is_blit_chan) {
+ val = SW_SHADOW_LOAD_SEL | RUN_EN | SHADOW_LOAD_EN;
+ dprc_write(dprc, val, SYSTEM_CTRL0);
+ } else if (start) {
+ /* software shadow load for the first frame */
+ val = SW_SHADOW_LOAD_SEL | SHADOW_LOAD_EN;
+ dprc_write(dprc, val, SYSTEM_CTRL0);
+
+ /* and then, run... */
+ val |= RUN_EN | REPEAT_EN;
+ dprc_write(dprc, val, SYSTEM_CTRL0);
+ }
+
+ prg_configure(dprc->prgs[0], width, height, x_offset, y_offset,
+ prg_stride, bpp, baddr, format, modifier, start);
+ if (dprc->use_aux_prg)
+ prg_configure(dprc->prgs[1], width, height, x_offset, y_offset,
+ prg_stride, 8, uv_baddr, format, modifier, aux_start);
+
+ dev_dbg(dprc->dev, "w-%u, h-%u, s-%u, fmt-0x%08x, mod-0x%016llx\n",
+ width, height, stride, format, modifier);
+}
+EXPORT_SYMBOL_GPL(dprc_configure);
+
+void dprc_reg_update(struct dprc *dprc)
+{
+ if (WARN_ON(!dprc))
+ return;
+
+ prg_reg_update(dprc->prgs[0]);
+ if (dprc->use_aux_prg)
+ prg_reg_update(dprc->prgs[1]);
+}
+EXPORT_SYMBOL_GPL(dprc_reg_update);
+
+void dprc_first_frame_handle(struct dprc *dprc)
+{
+ if (WARN_ON(!dprc))
+ return;
+
+ if (dprc->is_blit_chan)
+ return;
+
+ dprc_write(dprc, REPEAT_EN, SYSTEM_CTRL0);
+
+ prg_shadow_enable(dprc->prgs[0]);
+ if (dprc->use_aux_prg)
+ prg_shadow_enable(dprc->prgs[1]);
+}
+EXPORT_SYMBOL_GPL(dprc_first_frame_handle);
+
+void dprc_irq_handle(struct dprc *dprc)
+{
+ u32 mask, status;
+
+ if (WARN_ON(!dprc))
+ return;
+
+ spin_lock(&dprc->spin_lock);
+
+ mask = dprc_read(dprc, IRQ_MASK);
+ mask = ~mask;
+ status = dprc_read(dprc, IRQ_MASK_STATUS);
+ status &= mask;
+
+ /* disable irqs to be handled */
+ dprc_write(dprc, status, IRQ_MASK + SET);
+
+ /* clear status */
+ dprc_write(dprc, status, IRQ_MASK_STATUS);
+
+ if (status & DPR2RTR_FIFO_LOAD_BUF_RDY_UV_ERROR)
+ dev_err(dprc->dev,
+ "DPR to RTRAM FIFO load UV buffer ready error\n");
+
+ if (status & DPR2RTR_FIFO_LOAD_BUF_RDY_YRGB_ERROR)
+ dev_err(dprc->dev,
+ "DPR to RTRAM FIFO load YRGB buffer ready error\n");
+
+ if (status & DPR2RTR_UV_FIFO_OVFL)
+ dev_err(dprc->dev, "DPR to RTRAM FIFO UV FIFO overflow\n");
+
+ if (status & DPR2RTR_YRGB_FIFO_OVFL)
+ dev_err(dprc->dev, "DPR to RTRAM FIFO YRGB FIFO overflow\n");
+
+ if (status & IRQ_AXI_READ_ERROR)
+ dev_err(dprc->dev, "AXI read error\n");
+
+ if (status & IRQ_DPR_CRTL_DONE)
+ dprc_first_frame_handle(dprc);
+
+ spin_unlock(&dprc->spin_lock);
+}
+EXPORT_SYMBOL_GPL(dprc_irq_handle);
+
+void dprc_enable_ctrl_done_irq(struct dprc *dprc)
+{
+ unsigned long lock_flags;
+
+ if (WARN_ON(!dprc))
+ return;
+
+ spin_lock_irqsave(&dprc->spin_lock, lock_flags);
+ dprc_write(dprc, IRQ_DPR_CRTL_DONE, IRQ_MASK + CLR);
+ spin_unlock_irqrestore(&dprc->spin_lock, lock_flags);
+}
+EXPORT_SYMBOL_GPL(dprc_enable_ctrl_done_irq);
+
+bool dprc_format_supported(struct dprc *dprc, u32 format, u64 modifier)
+{
+ if (WARN_ON(!dprc))
+ return false;
+
+ switch (format) {
+ case DRM_FORMAT_ARGB8888:
+ case DRM_FORMAT_XRGB8888:
+ case DRM_FORMAT_ABGR8888:
+ case DRM_FORMAT_XBGR8888:
+ case DRM_FORMAT_RGBA8888:
+ case DRM_FORMAT_RGBX8888:
+ case DRM_FORMAT_BGRA8888:
+ case DRM_FORMAT_BGRX8888:
+ case DRM_FORMAT_RGB565:
+ return (modifier == DRM_FORMAT_MOD_NONE ||
+ modifier == DRM_FORMAT_MOD_VIVANTE_TILED ||
+ modifier == DRM_FORMAT_MOD_VIVANTE_SUPER_TILED);
+ case DRM_FORMAT_YUYV:
+ case DRM_FORMAT_UYVY:
+ switch (dprc->sc_resource) {
+ case IMX_SC_R_DC_0_FRAC0:
+ case IMX_SC_R_DC_1_FRAC0:
+ case IMX_SC_R_DC_0_WARP:
+ case IMX_SC_R_DC_1_WARP:
+ return false;
+ }
+ return modifier == DRM_FORMAT_MOD_NONE;
+ case DRM_FORMAT_NV12:
+ case DRM_FORMAT_NV21:
+ switch (dprc->sc_resource) {
+ case IMX_SC_R_DC_0_FRAC0:
+ case IMX_SC_R_DC_1_FRAC0:
+ case IMX_SC_R_DC_0_WARP:
+ case IMX_SC_R_DC_1_WARP:
+ return false;
+ case IMX_SC_R_DC_0_BLIT1:
+ case IMX_SC_R_DC_1_BLIT1:
+ return (modifier == DRM_FORMAT_MOD_NONE ||
+ modifier == DRM_FORMAT_MOD_AMPHION_TILED);
+ }
+ return (dprc->has_aux_prg &&
+ (modifier == DRM_FORMAT_MOD_NONE ||
+ modifier == DRM_FORMAT_MOD_AMPHION_TILED));
+ }
+
+ return false;
+}
+EXPORT_SYMBOL_GPL(dprc_format_supported);
+
+bool dprc_stride_supported(struct dprc *dprc,
+ unsigned int stride, unsigned int uv_stride,
+ unsigned int width, u32 format)
+{
+ const struct dprc_format_info *info = dprc_format_info(format);
+ unsigned int prg_stride = width * info->cpp[0];
+
+ if (WARN_ON(!dprc))
+ return false;
+
+ if (stride > 0xffff)
+ return false;
+
+ if (info->num_planes > 1 && stride != uv_stride)
+ return false;
+
+ return prg_stride_supported(dprc->prgs[0], prg_stride);
+}
+EXPORT_SYMBOL_GPL(dprc_stride_supported);
+
+bool dprc_stride_double_check(struct dprc *dprc,
+ unsigned int width, unsigned int x_offset,
+ u32 format, u64 modifier,
+ dma_addr_t baddr, dma_addr_t uv_baddr)
+{
+ const struct dprc_format_info *info = dprc_format_info(format);
+ unsigned int bpp = 8 * info->cpp[0];
+ unsigned int prg_stride = width * info->cpp[0];
+
+ if (WARN_ON(!dprc))
+ return false;
+
+ if (!prg_stride_double_check(dprc->prgs[0], width, x_offset,
+ bpp, modifier, prg_stride, baddr))
+ return false;
+
+ if (info->num_planes > 1 &&
+ !prg_stride_double_check(dprc->prgs[1], width, x_offset,
+ bpp, modifier, prg_stride, uv_baddr))
+ return false;
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(dprc_stride_double_check);
+
+struct dprc *
+dprc_lookup_by_phandle(struct device *dev, const char *name, int index)
+{
+ struct device_node *dprc_node = of_parse_phandle(dev->of_node,
+ name, index);
+ struct dprc *dprc;
+
+ mutex_lock(&dprc_list_mutex);
+ list_for_each_entry(dprc, &dprc_list, list) {
+ if (dprc_node == dprc->dev->of_node) {
+ mutex_unlock(&dprc_list_mutex);
+ device_link_add(dev, dprc->dev,
+ DL_FLAG_AUTOREMOVE_CONSUMER);
+ return dprc;
+ }
+ }
+ mutex_unlock(&dprc_list_mutex);
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(dprc_lookup_by_phandle);
+
+static const struct of_device_id dprc_dt_ids[] = {
+ { .compatible = "fsl,imx8qm-dpr-channel", },
+ { .compatible = "fsl,imx8qxp-dpr-channel", },
+ { /* sentinel */ },
+};
+
+static int dprc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ struct dprc *dprc;
+ int ret, i;
+
+ dprc = devm_kzalloc(dev, sizeof(*dprc), GFP_KERNEL);
+ if (!dprc)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ dprc->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(dprc->base))
+ return PTR_ERR(dprc->base);
+
+ dprc->clk_apb = devm_clk_get(dev, "apb");
+ if (IS_ERR(dprc->clk_apb))
+ return PTR_ERR(dprc->clk_apb);
+ clk_prepare_enable(dprc->clk_apb);
+
+ dprc->clk_b = devm_clk_get(dev, "b");
+ if (IS_ERR(dprc->clk_b))
+ return PTR_ERR(dprc->clk_b);
+ clk_prepare_enable(dprc->clk_b);
+
+ dprc->clk_rtram = devm_clk_get(dev, "rtram");
+ if (IS_ERR(dprc->clk_rtram))
+ return PTR_ERR(dprc->clk_rtram);
+ clk_prepare_enable(dprc->clk_rtram);
+
+ ret = of_property_read_u32(pdev->dev.of_node,
+ "fsl,sc-resource", &dprc->sc_resource);
+ if (ret) {
+ dev_err(dev, "cannot get SC resource %d\n", ret);
+ return ret;
+ }
+
+ switch (dprc->sc_resource) {
+ case IMX_SC_R_DC_0_BLIT1:
+ case IMX_SC_R_DC_1_BLIT1:
+ dprc->has_aux_prg = true;
+ /* fall-through */
+ case IMX_SC_R_DC_0_BLIT0:
+ case IMX_SC_R_DC_1_BLIT0:
+ dprc->is_blit_chan = true;
+ /* fall-through */
+ case IMX_SC_R_DC_0_FRAC0:
+ case IMX_SC_R_DC_1_FRAC0:
+ break;
+ case IMX_SC_R_DC_0_VIDEO0:
+ case IMX_SC_R_DC_0_VIDEO1:
+ case IMX_SC_R_DC_1_VIDEO0:
+ case IMX_SC_R_DC_1_VIDEO1:
+ case IMX_SC_R_DC_0_WARP:
+ case IMX_SC_R_DC_1_WARP:
+ dprc->has_aux_prg = true;
+ break;
+ default:
+ dev_err(dev, "wrong SC resource %u\n", dprc->sc_resource);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < 2; i++) {
+ if (i == 1 && !dprc->has_aux_prg)
+ break;
+
+ dprc->prgs[i] = prg_lookup_by_phandle(dev, "fsl,prgs", i);
+ if (!dprc->prgs[i])
+ return -EPROBE_DEFER;
+
+ if (i == 1)
+ prg_set_auxiliary(dprc->prgs[i]);
+
+ if (dprc->is_blit_chan)
+ prg_set_blit(dprc->prgs[i]);
+ }
+
+ dprc->dev = dev;
+ spin_lock_init(&dprc->spin_lock);
+ platform_set_drvdata(pdev, dprc);
+ mutex_lock(&dprc_list_mutex);
+ list_add(&dprc->list, &dprc_list);
+ mutex_unlock(&dprc_list_mutex);
+
+ dprc_reset(dprc);
+
+ return 0;
+}
+
+static int dprc_remove(struct platform_device *pdev)
+{
+ struct dprc *dprc = platform_get_drvdata(pdev);
+
+ mutex_lock(&dprc_list_mutex);
+ list_del(&dprc->list);
+ mutex_unlock(&dprc_list_mutex);
+
+ clk_disable_unprepare(dprc->clk_rtram);
+ clk_disable_unprepare(dprc->clk_b);
+ clk_disable_unprepare(dprc->clk_apb);
+
+ return 0;
+}
+
+struct platform_driver dprc_drv = {
+ .probe = dprc_probe,
+ .remove = dprc_remove,
+ .driver = {
+ .name = "imx8-dpr-channel",
+ .of_match_table = dprc_dt_ids,
+ },
+};
+module_platform_driver(dprc_drv);
+
+MODULE_DESCRIPTION("i.MX8 DPRC driver");
+MODULE_AUTHOR("NXP Semiconductor");
+MODULE_LICENSE("GPL");
diff --git a/include/video/imx8-prefetch.h b/include/video/imx8-prefetch.h
index 7bbb57499df4..c6952369f954 100644
--- a/include/video/imx8-prefetch.h
+++ b/include/video/imx8-prefetch.h
@@ -47,4 +47,28 @@ void prg_set_auxiliary(struct prg *prg);
void prg_set_primary(struct prg *prg);
void prg_set_blit(struct prg *prg);
+struct dprc;
+struct dprc *
+dprc_lookup_by_phandle(struct device *dev, const char *name, int index);
+void dprc_enable(struct dprc *dprc);
+void dprc_disable(struct dprc *dprc);
+void dprc_configure(struct dprc *dprc, unsigned int stream_id,
+ unsigned int width, unsigned int height,
+ unsigned int x_offset, unsigned int y_offset,
+ unsigned int stride, u32 format, u64 modifier,
+ unsigned long baddr, unsigned long uv_baddr,
+ bool start, bool aux_start, bool interlace_frame);
+void dprc_reg_update(struct dprc *dprc);
+void dprc_first_frame_handle(struct dprc *dprc);
+void dprc_irq_handle(struct dprc *dprc);
+void dprc_enable_ctrl_done_irq(struct dprc *dprc);
+bool dprc_format_supported(struct dprc *dprc, u32 format, u64 modifier);
+bool dprc_stride_supported(struct dprc *dprc,
+ unsigned int stride, unsigned int uv_stride,
+ unsigned int width, u32 format);
+bool dprc_stride_double_check(struct dprc *dprc,
+ unsigned int width, unsigned int x_offset,
+ u32 format, u64 modifier,
+ dma_addr_t baddr, dma_addr_t uv_baddr);
+
#endif