diff --git a/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt b/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt index e269a3d352a131a4d79c911d6e53565bf8414bf6..594e931b09e2acaf8604d4d89576477d14cdfda7 100644 --- a/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt +++ b/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt @@ -250,6 +250,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 ======================== diff --git a/drivers/gpu/imx/Kconfig b/drivers/gpu/imx/Kconfig index 7a2c7e06f0526871c8959f71489b533f45976b0e..589631d4451944b0a2908713242dacb85ff1f661 100644 --- a/drivers/gpu/imx/Kconfig +++ b/drivers/gpu/imx/Kconfig @@ -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/lcdif/Kconfig source drivers/gpu/imx/dcss/Kconfig diff --git a/drivers/gpu/imx/Makefile b/drivers/gpu/imx/Makefile index a8feed067a1f81e41de6ef1c3eea14c526542d65..3c4310858e3b977900cb8e81342211f11f5938b9 100644 --- a/drivers/gpu/imx/Makefile +++ b/drivers/gpu/imx/Makefile @@ -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_LCDIF_CORE) += lcdif/ diff --git a/drivers/gpu/imx/imx8_dprc.c b/drivers/gpu/imx/imx8_dprc.c new file mode 100644 index 0000000000000000000000000000000000000000..a0a41294769f4596bfff365d2a41124a303d3251 --- /dev/null +++ b/drivers/gpu/imx/imx8_dprc.c @@ -0,0 +1,909 @@ +/* + * 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 <linux/clk.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.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_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) +{ + 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); +} + +static void dprc_prg_sel_configure(struct dprc *dprc, u32 resource, bool enable) +{ + 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, resource, SC_C_SEL0, enable); + 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, 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 == SC_R_DC_0_BLIT1 || + dprc->sc_resource == SC_R_DC_1_BLIT1) { + dprc_prg_sel_configure(dprc, + dprc->sc_resource == SC_R_DC_0_BLIT1 ? + SC_R_DC_0_BLIT0 : 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 SC_R_DC_0_BLIT0: + case SC_R_DC_1_BLIT0: + dprc_prg_sel_configure(dprc, dprc->sc_resource, false); + prg_set_primary(dprc->prgs[0]); + break; + case SC_R_DC_0_BLIT1: + case 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 SC_R_DC_0_FRAC0: + case SC_R_DC_1_FRAC0: + case SC_R_DC_0_WARP: + case 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 SC_R_DC_0_FRAC0: + case SC_R_DC_1_FRAC0: + case SC_R_DC_0_WARP: + case SC_R_DC_1_WARP: + return false; + case SC_R_DC_0_BLIT1: + case 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 SC_R_DC_0_BLIT1: + case SC_R_DC_1_BLIT1: + dprc->has_aux_prg = true; + /* fall-through */ + case SC_R_DC_0_BLIT0: + case SC_R_DC_1_BLIT0: + 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]); + + 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 7bbb57499df4cc4d09c7efd00c20189acca967ce..c6952369f954eca6c01e32ad1bb6572b8668ed9a 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