MLK-15110-3 gpu: imx: Add i.MX8 DPR(Display Prefetch Resolve) support
authorLiu Ying <victor.liu@nxp.com>
Mon, 3 Jul 2017 05:07:23 +0000 (13:07 +0800)
committerNitin Garg <nitin.garg@nxp.com>
Tue, 20 Mar 2018 19:50:43 +0000 (14:50 -0500)
The Display Prefetch Resolve(DPR) is a processor of fetching display data
before the display pipeline which needs data to drive pixels in the active
display region.  The data is transformed, or resolved from a variety of
tiled buffer formats into linear format.  The DPR transaction sequences are
issued with a high level of DRAM efficiency.  This patch adds the base
driver support for i.MX8qm/qxp DPR.

Signed-off-by: Liu Ying <victor.liu@nxp.com>
Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt
drivers/gpu/imx/Kconfig
drivers/gpu/imx/Makefile
drivers/gpu/imx/imx8_dprc.c [new file with mode: 0644]
include/video/imx8-prefetch.h

index da728cc..9365b54 100644 (file)
@@ -214,6 +214,38 @@ prg@56040000 {
        power-domains = <&pd_dc0>;
 };
 
+Freescale i.MX8 DPRC (Display Prefetch Resolve Channel)
+=======================================================
+Required properties:
+- compatible: should be "fsl,<chip>-dpr-channel"
+- reg: should be register base and length as documented in the
+  datasheet
+- fsl,sc-resource: SCU resource number as described in
+  Documentation/devicetree/bindings/soc/fsl/imx_rsrc.txt
+- fsl,prgs: phandles to the PRG unit(s) attached to this DPRC, the first one
+  is the primary PRG and the second one(if available) is the auxiliary PRG
+  which is used to fetch luma chunk of a YUV frame with 2 planars.
+- clocks: phandles to the DPRC apb, b and rtram clocks, as described in
+  Documentation/devicetree/bindings/clock/clock-bindings.txt,
+  Documentation/devicetree/bindings/clock/imx8qm-clock.txt and
+  Documentation/devicetree/bindings/clock/imx8qxp-clock.txt
+- clock-names: should be "apb", "b" and "rtram"
+- power-domains: phandle pointing to power domain
+
+example:
+
+dpr-channel@56100000 {
+       compatible = "fsl,imx8qm-dpr-channel";
+       reg = <0x0 0x56100000 0x0 0x10000>;
+       fsl,sc-resource = <SC_R_DC_0_VIDEO0>;
+       fsl,prgs = <&prg4>, <&prg5>;
+       clocks = <&clk IMX8QM_DC0_DPR1_APB_CLK>,
+                <&clk IMX8QM_DC0_DPR1_B_CLK>,
+                <&clk IMX8QM_DC0_RTRAM1_CLK>;
+       clock-names = "apb", "b", "rtram";
+       power-domains = <&pd_dc0>;
+};
+
 Parallel display support
 ========================
 
index a941602..a97dec3 100644 (file)
@@ -3,6 +3,11 @@ 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
 source drivers/gpu/imx/dpu/Kconfig
 source drivers/gpu/imx/dpu-blit/Kconfig
index fd3cb47..182d718 100644 (file)
@@ -1,4 +1,5 @@
 obj-$(CONFIG_IMX8_PRG) += imx8_prg.o
+obj-$(CONFIG_IMX8_DPRC)        += imx8_dprc.o
 
 obj-$(CONFIG_IMX_IPUV3_CORE)   += ipu-v3/
 obj-$(CONFIG_IMX_DPU_CORE)     += dpu/
diff --git a/drivers/gpu/imx/imx8_dprc.c b/drivers/gpu/imx/imx8_dprc.c
new file mode 100644 (file)
index 0000000..53d48ec
--- /dev/null
@@ -0,0 +1,785 @@
+/*
+ * Copyright 2017 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 <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <soc/imx8/sc/sci.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_1P_PIX_Y_CTRL                    0xb0
+#define FRAME_2P_PIX_Y_CTRL                    0x100
+#define NUM_Y_PIX_HIGH(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_XRGB8888,
+         .depth = 24, .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_RGBX8888,
+         .depth = 24, .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);
+       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)
+{
+       sc_err_t sciErr;
+       sc_ipc_t ipcHndl = 0;
+       u32 mu_id;
+
+       if (WARN_ON(!dprc))
+               return;
+
+       sciErr = sc_ipc_getMuID(&mu_id);
+       if (sciErr != SC_ERR_NONE) {
+               dev_err(dprc->dev, "cannot obtain MU ID %d\n", sciErr);
+               return;
+       }
+
+       sciErr = sc_ipc_open(&ipcHndl, mu_id);
+       if (sciErr != SC_ERR_NONE) {
+               dev_err(dprc->dev, "sc_ipc_open failed %d\n", sciErr);
+               return;
+       }
+
+       sciErr = sc_misc_set_control(ipcHndl, dprc->sc_resource,
+                                       SC_C_KACHUNK_SEL, stream_id);
+       if (sciErr != SC_ERR_NONE)
+               dev_err(dprc->dev, "sc_misc_set_control failed %d\n", sciErr);
+
+       sc_ipc_close(mu_id);
+}
+
+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)
+{
+       const struct dprc_format_info *info = dprc_format_info(format);
+       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;
+       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);
+       }
+
+       /* 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(width, modifier ? 8 : 64);
+               p1_h = modifier ? height : round_up(height, 8);
+
+               p2_w = p1_w;
+               if (modifier)
+                       p2_h = height / info->vsub;
+               else
+                       p2_h = round_up((height / info->vsub), 8);
+
+               preq = modifier ? BYTE_64 : BYTE_1K;
+
+               dprc_write(dprc, preq, FRAME_2P_CTRL0);
+               dprc_write(dprc, NUM_X_PIX_WIDE(p2_w), FRAME_2P_PIX_X_CTRL);
+               dprc_write(dprc, NUM_Y_PIX_HIGH(p2_h), FRAME_2P_PIX_Y_CTRL);
+               dprc_write(dprc, uv_baddr, FRAME_2P_BASE_ADDR_CTRL0);
+       } else {
+               switch (modifier) {
+               case DRM_FORMAT_MOD_VIVANTE_TILED:
+                       p1_w = round_up(width, info->cpp[0] == 2 ? 8 : 4);
+                       break;
+               case DRM_FORMAT_MOD_VIVANTE_SUPER_TILED:
+                       p1_w = round_up(width, 64);
+                       break;
+               default:
+                       p1_w = round_up(width, info->cpp[0] == 2 ? 32 : 16);
+                       break;
+               }
+               p1_h = round_up(height, modifier ? 64 : 4);
+       }
+
+       dprc_write(dprc, PITCH(stride), FRAME_CTRL0);
+       switch (modifier) {
+       case DRM_FORMAT_MOD_AMPHION_TILED:
+               preq = BYTE_64;
+               break;
+       case DRM_FORMAT_MOD_VIVANTE_TILED:
+       case DRM_FORMAT_MOD_VIVANTE_SUPER_TILED:
+               preq = (bpp == 16) ? BYTE_64 : BYTE_128;
+               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);
+
+       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_XRGB8888:
+               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_XBGR8888:
+               val |= A_COMP_SEL(3) | R_COMP_SEL(0) |
+                      G_COMP_SEL(1) | B_COMP_SEL(2);
+               val |= PIX_SIZE_32BIT;
+               break;
+       case DRM_FORMAT_RGBX8888:
+               val |= A_COMP_SEL(0) | R_COMP_SEL(3) |
+                      G_COMP_SEL(2) | B_COMP_SEL(1);
+               val |= PIX_SIZE_32BIT;
+               break;
+       case DRM_FORMAT_BGRX8888:
+               val |= A_COMP_SEL(0) | R_COMP_SEL(1) |
+                      G_COMP_SEL(2) | B_COMP_SEL(3);
+               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 (start) {
+               val = RUN_EN | REPEAT_EN | SHADOW_LOAD_EN;
+               /* software shadow load for the first frame */
+               val |= SW_SHADOW_LOAD_SEL;
+               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);
+
+static void dprc_first_frame_handle(struct dprc *dprc)
+{
+       if (WARN_ON(!dprc))
+               return;
+
+       dprc_write(dprc, SW_SHADOW_LOAD_SEL, SYSTEM_CTRL0 + CLR);
+
+       prg_shadow_enable(dprc->prgs[0]);
+       if (dprc->use_aux_prg)
+               prg_shadow_enable(dprc->prgs[1]);
+}
+
+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_XRGB8888:
+       case DRM_FORMAT_XBGR8888:
+       case DRM_FORMAT_RGBX8888:
+       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:
+               return modifier == DRM_FORMAT_MOD_NONE;
+       case DRM_FORMAT_NV12:
+       case DRM_FORMAT_NV21:
+               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_crop_supported(struct dprc *dprc, u64 modifier, u32 y_offset)
+{
+       if (WARN_ON(!dprc))
+               return false;
+
+       switch (modifier) {
+       case DRM_FORMAT_MOD_AMPHION_TILED:
+               if ((y_offset % AMPHION_Y_STRIPE_HEIGHT) >
+                                       (PRG_HANDSHAKE_8LINES - 1))
+                       return false;
+               break;
+       case DRM_FORMAT_MOD_VIVANTE_SUPER_TILED:
+               if ((y_offset % VIVANTE_SUPER_TILE_HEIGHT) >
+                                       (PRG_HANDSHAKE_4LINES - 1))
+                       return false;
+               break;
+       default:
+               break;
+       }
+
+       return true;
+}
+EXPORT_SYMBOL_GPL(dprc_crop_supported);
+
+bool dprc_stride_double_check(struct dprc *dprc,
+                             unsigned int stride, unsigned int uv_stride,
+                             unsigned int width, u32 format,
+                             dma_addr_t baddr, dma_addr_t uv_baddr)
+{
+       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 (!prg_stride_double_check(dprc->prgs[0], prg_stride, baddr))
+               return false;
+
+       if (info->num_planes > 1 &&
+           !prg_stride_double_check(dprc->prgs[1], 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);
+                       return dprc;
+               }
+       }
+       mutex_unlock(&dprc_list_mutex);
+
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(dprc_lookup_by_phandle);
+
+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 SC_R_DC_0_BLIT0:
+       case SC_R_DC_0_BLIT1:
+       case SC_R_DC_1_BLIT0:
+       case SC_R_DC_1_BLIT1:
+               dprc->is_blit_chan = true;
+               /* fall-through */
+       case SC_R_DC_0_FRAC0:
+       case SC_R_DC_1_FRAC0:
+               break;
+       case SC_R_DC_0_VIDEO0:
+       case SC_R_DC_0_VIDEO1:
+       case SC_R_DC_1_VIDEO0:
+       case SC_R_DC_1_VIDEO1:
+       case SC_R_DC_0_WARP:
+       case 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]);
+       }
+
+       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;
+}
+
+static const struct of_device_id dprc_dt_ids[] = {
+       { .compatible = "fsl,imx8qm-dpr-channel", },
+       { .compatible = "fsl,imx8qxp-dpr-channel", },
+       { /* sentinel */ },
+};
+
+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");
index 52ab319..946c491 100644 (file)
@@ -43,4 +43,28 @@ bool prg_stride_double_check(struct prg *prg,
                             unsigned int stride, dma_addr_t baddr);
 void prg_set_auxiliary(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);
+void dprc_reg_update(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_crop_supported(struct dprc *dprc, u64 modifier, u32 y_offset);
+bool dprc_stride_double_check(struct dprc *dprc,
+                             unsigned int stride, unsigned int uv_stride,
+                             unsigned int width, u32 format,
+                             dma_addr_t baddr, dma_addr_t uv_baddr);
+
 #endif