Skip to content
Snippets Groups Projects
Commit 36b8aded authored by Liu Ying's avatar Liu Ying Committed by Leonard Crestez
Browse files

MLK-21378-2 gpu: imx: Add imx8_dprc support


Fast-forward imx8_dprc driver from imx_4.14.y.

Signed-off-by: default avatarLiu Ying <victor.liu@nxp.com>
parent 62275a39
No related branches found
No related tags found
No related merge requests found
...@@ -250,6 +250,38 @@ prg@56040000 { ...@@ -250,6 +250,38 @@ prg@56040000 {
power-domains = <&pd_dc0>; 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 Parallel display support
======================== ========================
......
...@@ -3,6 +3,11 @@ config IMX8_PRG ...@@ -3,6 +3,11 @@ config IMX8_PRG
default y if IMX_DPU_CORE=y default y if IMX_DPU_CORE=y
default m if IMX_DPU_CORE=m 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/ipu-v3/Kconfig
source drivers/gpu/imx/lcdif/Kconfig source drivers/gpu/imx/lcdif/Kconfig
source drivers/gpu/imx/dcss/Kconfig source drivers/gpu/imx/dcss/Kconfig
obj-$(CONFIG_IMX8_PRG) += imx8_prg.o obj-$(CONFIG_IMX8_PRG) += imx8_prg.o
obj-$(CONFIG_IMX8_DPRC) += imx8_dprc.o
obj-$(CONFIG_IMX_IPUV3_CORE) += ipu-v3/ obj-$(CONFIG_IMX_IPUV3_CORE) += ipu-v3/
obj-$(CONFIG_IMX_LCDIF_CORE) += lcdif/ obj-$(CONFIG_IMX_LCDIF_CORE) += lcdif/
......
/*
* 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");
...@@ -47,4 +47,28 @@ void prg_set_auxiliary(struct prg *prg); ...@@ -47,4 +47,28 @@ void prg_set_auxiliary(struct prg *prg);
void prg_set_primary(struct prg *prg); void prg_set_primary(struct prg *prg);
void prg_set_blit(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 #endif
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment