Message ID | 1515117959-18068-8-git-send-email-hyun.kwon@xilinx.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Thu, Jan 04, 2018 at 06:05:56PM -0800, Hyun Kwon wrote: > Xilinx ZynqMP has a hardened display pipeline. The pipeline can > be logically partitioned into 2 parts: display and DisplayPort. > This driver handles the display part of the pipeline that handles > buffer management and blending. > > Signed-off-by: Hyun Kwon <hyun.kwon@xilinx.com> > --- > drivers/gpu/drm/xlnx/zynqmp_disp.c | 2935 ++++++++++++++++++++++++++++++++++++ > drivers/gpu/drm/xlnx/zynqmp_disp.h | 28 + > 2 files changed, 2963 insertions(+) > create mode 100644 drivers/gpu/drm/xlnx/zynqmp_disp.c > create mode 100644 drivers/gpu/drm/xlnx/zynqmp_disp.h > > diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c > new file mode 100644 > index 0000000..68f829c > --- /dev/null > +++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c > @@ -0,0 +1,2935 @@ > +/* > + * ZynqMP Display Controller Driver > + * > + * Copyright (C) 2017 - 2018 Xilinx, Inc. > + * > + * Author: Hyun Woo Kwon <hyun.kwon@xilinx.com> > + * > + * SPDX-License-Identifier: GPL-2.0 > + */ > + > +#include <drm/drmP.h> > +#include <drm/drm_atomic.h> > +#include <drm/drm_atomic_helper.h> > +#include <drm/drm_crtc.h> > +#include <drm/drm_crtc_helper.h> > +#include <drm/drm_fourcc.h> > +#include <drm/drm_plane_helper.h> > + > +#include <linux/clk.h> > +#include <linux/device.h> > +#include <linux/dmaengine.h> > +#include <linux/interrupt.h> > +#include <linux/irqreturn.h> > +#include <linux/list.h> > +#include <linux/module.h> > +#include <linux/mutex.h> > +#include <linux/of.h> > +#include <linux/of_dma.h> > +#include <linux/platform_device.h> > +#include <linux/pm_runtime.h> > +#include <linux/spinlock.h> > +#include <linux/uaccess.h> > + > +#include "xlnx_crtc.h" > +#include "xlnx_fb.h" > +#include "zynqmp_disp.h" > +#include "zynqmp_dp.h" > +#include "zynqmp_dpsub.h" > + > +/* > + * Overview > + * -------- > + * > + * The display part of ZynqMP DP subsystem. Internally, the device > + * is partitioned into 3 blocks: AV buffer manager, Blender, Audio. > + * The driver creates the DRM crtc and plane objectes and maps the DRM > + * interface into those 3 blocks. In high level, the driver is layered > + * in the following way: > + * > + * zynqmp_disp_crtc & zynqmp_disp_plane > + * |->zynqmp_disp > + * |->zynqmp_disp_aud > + * |->zynqmp_disp_blend > + * |->zynqmp_disp_av_buf > + * > + * The driver APIs are used externally by > + * - zynqmp_dpsub: Top level ZynqMP DP subsystem driver > + * - zynqmp_dp: ZynqMP DP driver > + * - xlnx_crtc: Xilinx DRM specific crtc functions > + */ > + > +/* Blender registers */ > +#define ZYNQMP_DISP_V_BLEND_BG_CLR_0 0x0 > +#define ZYNQMP_DISP_V_BLEND_BG_CLR_1 0x4 > +#define ZYNQMP_DISP_V_BLEND_BG_CLR_2 0x8 > +#define ZYNQMP_DISP_V_BLEND_BG_MAX 0xfff > +#define ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA 0xc > +#define ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA_MASK 0x1fe > +#define ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA_MAX 0xff > +#define ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT 0x14 > +#define ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_RGB 0x0 > +#define ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_YCBCR444 0x1 > +#define ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_YCBCR422 0x2 > +#define ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_YONLY 0x3 > +#define ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_XVYCC 0x4 > +#define ZYNQMP_DISP_V_BLEND_OUTPUT_EN_DOWNSAMPLE BIT(4) > +#define ZYNQMP_DISP_V_BLEND_LAYER_CONTROL 0x18 > +#define ZYNQMP_DISP_V_BLEND_LAYER_CONTROL_EN_US BIT(0) > +#define ZYNQMP_DISP_V_BLEND_LAYER_CONTROL_RGB BIT(1) > +#define ZYNQMP_DISP_V_BLEND_LAYER_CONTROL_BYPASS BIT(8) > +#define ZYNQMP_DISP_V_BLEND_NUM_COEFF 9 > +#define ZYNQMP_DISP_V_BLEND_RGB2YCBCR_COEFF0 0x20 > +#define ZYNQMP_DISP_V_BLEND_RGB2YCBCR_COEFF1 0x24 > +#define ZYNQMP_DISP_V_BLEND_RGB2YCBCR_COEFF2 0x28 > +#define ZYNQMP_DISP_V_BLEND_RGB2YCBCR_COEFF3 0x2c > +#define ZYNQMP_DISP_V_BLEND_RGB2YCBCR_COEFF4 0x30 > +#define ZYNQMP_DISP_V_BLEND_RGB2YCBCR_COEFF5 0x34 > +#define ZYNQMP_DISP_V_BLEND_RGB2YCBCR_COEFF6 0x38 > +#define ZYNQMP_DISP_V_BLEND_RGB2YCBCR_COEFF7 0x3c > +#define ZYNQMP_DISP_V_BLEND_RGB2YCBCR_COEFF8 0x40 > +#define ZYNQMP_DISP_V_BLEND_IN1CSC_COEFF0 0x44 > +#define ZYNQMP_DISP_V_BLEND_IN1CSC_COEFF1 0x48 > +#define ZYNQMP_DISP_V_BLEND_IN1CSC_COEFF2 0x4c > +#define ZYNQMP_DISP_V_BLEND_IN1CSC_COEFF3 0x50 > +#define ZYNQMP_DISP_V_BLEND_IN1CSC_COEFF4 0x54 > +#define ZYNQMP_DISP_V_BLEND_IN1CSC_COEFF5 0x58 > +#define ZYNQMP_DISP_V_BLEND_IN1CSC_COEFF6 0x5c > +#define ZYNQMP_DISP_V_BLEND_IN1CSC_COEFF7 0x60 > +#define ZYNQMP_DISP_V_BLEND_IN1CSC_COEFF8 0x64 > +#define ZYNQMP_DISP_V_BLEND_NUM_OFFSET 3 > +#define ZYNQMP_DISP_V_BLEND_LUMA_IN1CSC_OFFSET 0x68 > +#define ZYNQMP_DISP_V_BLEND_CR_IN1CSC_OFFSET 0x6c > +#define ZYNQMP_DISP_V_BLEND_CB_IN1CSC_OFFSET 0x70 > +#define ZYNQMP_DISP_V_BLEND_LUMA_OUTCSC_OFFSET 0x74 > +#define ZYNQMP_DISP_V_BLEND_CR_OUTCSC_OFFSET 0x78 > +#define ZYNQMP_DISP_V_BLEND_CB_OUTCSC_OFFSET 0x7c > +#define ZYNQMP_DISP_V_BLEND_IN2CSC_COEFF0 0x80 > +#define ZYNQMP_DISP_V_BLEND_IN2CSC_COEFF1 0x84 > +#define ZYNQMP_DISP_V_BLEND_IN2CSC_COEFF2 0x88 > +#define ZYNQMP_DISP_V_BLEND_IN2CSC_COEFF3 0x8c > +#define ZYNQMP_DISP_V_BLEND_IN2CSC_COEFF4 0x90 > +#define ZYNQMP_DISP_V_BLEND_IN2CSC_COEFF5 0x94 > +#define ZYNQMP_DISP_V_BLEND_IN2CSC_COEFF6 0x98 > +#define ZYNQMP_DISP_V_BLEND_IN2CSC_COEFF7 0x9c > +#define ZYNQMP_DISP_V_BLEND_IN2CSC_COEFF8 0xa0 > +#define ZYNQMP_DISP_V_BLEND_LUMA_IN2CSC_OFFSET 0xa4 > +#define ZYNQMP_DISP_V_BLEND_CR_IN2CSC_OFFSET 0xa8 > +#define ZYNQMP_DISP_V_BLEND_CB_IN2CSC_OFFSET 0xac > +#define ZYNQMP_DISP_V_BLEND_CHROMA_KEY_ENABLE 0x1d0 > +#define ZYNQMP_DISP_V_BLEND_CHROMA_KEY_COMP1 0x1d4 > +#define ZYNQMP_DISP_V_BLEND_CHROMA_KEY_COMP2 0x1d8 > +#define ZYNQMP_DISP_V_BLEND_CHROMA_KEY_COMP3 0x1dc > + > +/* AV buffer manager registers */ > +#define ZYNQMP_DISP_AV_BUF_FMT 0x0 > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_SHIFT 0 > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_MASK (0x1f << 0) > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_UYVY (0 << 0) > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_VYUY (1 << 0) > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YVYU (2 << 0) > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YUYV (3 << 0) > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16 (4 << 0) > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV24 (5 << 0) > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI (6 << 0) > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_MONO (7 << 0) > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI2 (8 << 0) > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YUV444 (9 << 0) > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_RGB888 (10 << 0) > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_RGBA8880 (11 << 0) > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_RGB888_10 (12 << 0) > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YUV444_10 (13 << 0) > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI2_10 (14 << 0) > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI_10 (15 << 0) > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16_10 (16 << 0) > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV24_10 (17 << 0) > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YONLY_10 (18 << 0) > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16_420 (19 << 0) > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI_420 (20 << 0) > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI2_420 (21 << 0) > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16_420_10 (22 << 0) > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI_420_10 (23 << 0) > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI2_420_10 (24 << 0) > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_SHIFT 8 > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_MASK (0xf << 8) > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGBA8888 (0 << 8) > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_ABGR8888 (1 << 8) > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGB888 (2 << 8) > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_BGR888 (3 << 8) > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGBA5551 (4 << 8) > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGBA4444 (5 << 8) > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGB565 (6 << 8) > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_8BPP (7 << 8) > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_4BPP (8 << 8) > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_2BPP (9 << 8) > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_1BPP (10 << 8) > +#define ZYNQMP_DISP_AV_BUF_NON_LIVE_LATENCY 0x8 > +#define ZYNQMP_DISP_AV_BUF_CHBUF 0x10 > +#define ZYNQMP_DISP_AV_BUF_CHBUF_EN BIT(0) > +#define ZYNQMP_DISP_AV_BUF_CHBUF_FLUSH BIT(1) > +#define ZYNQMP_DISP_AV_BUF_CHBUF_BURST_LEN_SHIFT 2 > +#define ZYNQMP_DISP_AV_BUF_CHBUF_BURST_LEN_MASK (0xf << 2) > +#define ZYNQMP_DISP_AV_BUF_CHBUF_BURST_LEN_MAX 0xf > +#define ZYNQMP_DISP_AV_BUF_CHBUF_BURST_LEN_AUD_MAX 0x3 > +#define ZYNQMP_DISP_AV_BUF_STATUS 0x28 > +#define ZYNQMP_DISP_AV_BUF_STC_CTRL 0x2c > +#define ZYNQMP_DISP_AV_BUF_STC_CTRL_EN BIT(0) > +#define ZYNQMP_DISP_AV_BUF_STC_CTRL_EVENT_SHIFT 1 > +#define ZYNQMP_DISP_AV_BUF_STC_CTRL_EVENT_EX_VSYNC 0 > +#define ZYNQMP_DISP_AV_BUF_STC_CTRL_EVENT_EX_VID 1 > +#define ZYNQMP_DISP_AV_BUF_STC_CTRL_EVENT_EX_AUD 2 > +#define ZYNQMP_DISP_AV_BUF_STC_CTRL_EVENT_INT_VSYNC 3 > +#define ZYNQMP_DISP_AV_BUF_STC_INIT_VALUE0 0x30 > +#define ZYNQMP_DISP_AV_BUF_STC_INIT_VALUE1 0x34 > +#define ZYNQMP_DISP_AV_BUF_STC_ADJ 0x38 > +#define ZYNQMP_DISP_AV_BUF_STC_VID_VSYNC_TS0 0x3c > +#define ZYNQMP_DISP_AV_BUF_STC_VID_VSYNC_TS1 0x40 > +#define ZYNQMP_DISP_AV_BUF_STC_EXT_VSYNC_TS0 0x44 > +#define ZYNQMP_DISP_AV_BUF_STC_EXT_VSYNC_TS1 0x48 > +#define ZYNQMP_DISP_AV_BUF_STC_CUSTOM_EVENT_TS0 0x4c > +#define ZYNQMP_DISP_AV_BUF_STC_CUSTOM_EVENT_TS1 0x50 > +#define ZYNQMP_DISP_AV_BUF_STC_CUSTOM_EVENT2_TS0 0x54 > +#define ZYNQMP_DISP_AV_BUF_STC_CUSTOM_EVENT2_TS1 0x58 > +#define ZYNQMP_DISP_AV_BUF_STC_SNAPSHOT0 0x60 > +#define ZYNQMP_DISP_AV_BUF_STC_SNAPSHOT1 0x64 > +#define ZYNQMP_DISP_AV_BUF_OUTPUT 0x70 > +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_SHIFT 0 > +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_MASK (0x3 << 0) > +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_LIVE (0 << 0) > +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_MEM (1 << 0) > +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_PATTERN (2 << 0) > +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_NONE (3 << 0) > +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_SHIFT 2 > +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_MASK (0x3 << 2) > +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_DISABLE (0 << 2) > +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_MEM (1 << 2) > +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_LIVE (2 << 2) > +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_NONE (3 << 2) > +#define ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_SHIFT 4 > +#define ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_MASK (0x3 << 4) > +#define ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_PL (0 << 4) > +#define ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_MEM (1 << 4) > +#define ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_PATTERN (2 << 4) > +#define ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_DISABLE (3 << 4) > +#define ZYNQMP_DISP_AV_BUF_OUTPUT_AUD2_EN BIT(6) > +#define ZYNQMP_DISP_AV_BUF_HCOUNT_VCOUNT_INT0 0x74 > +#define ZYNQMP_DISP_AV_BUF_HCOUNT_VCOUNT_INT1 0x78 > +#define ZYNQMP_DISP_AV_BUF_PATTERN_GEN_SELECT 0x100 > +#define ZYNQMP_DISP_AV_BUF_CLK_SRC 0x120 > +#define ZYNQMP_DISP_AV_BUF_CLK_SRC_VID_FROM_PS BIT(0) > +#define ZYNQMP_DISP_AV_BUF_CLK_SRC_AUD_FROM_PS BIT(1) > +#define ZYNQMP_DISP_AV_BUF_CLK_SRC_VID_INTERNAL_TIMING BIT(2) > +#define ZYNQMP_DISP_AV_BUF_SRST_REG 0x124 > +#define ZYNQMP_DISP_AV_BUF_SRST_REG_VID_RST BIT(1) > +#define ZYNQMP_DISP_AV_BUF_AUDIO_CH_CONFIG 0x12c > +#define ZYNQMP_DISP_AV_BUF_GFX_COMP0_SF 0x200 > +#define ZYNQMP_DISP_AV_BUF_GFX_COMP1_SF 0x204 > +#define ZYNQMP_DISP_AV_BUF_GFX_COMP2_SF 0x208 > +#define ZYNQMP_DISP_AV_BUF_VID_COMP0_SF 0x20c > +#define ZYNQMP_DISP_AV_BUF_VID_COMP1_SF 0x210 > +#define ZYNQMP_DISP_AV_BUF_VID_COMP2_SF 0x214 > +#define ZYNQMP_DISP_AV_BUF_LIVE_VID_COMP0_SF 0x218 > +#define ZYNQMP_DISP_AV_BUF_LIVE_VID_COMP1_SF 0x21c > +#define ZYNQMP_DISP_AV_BUF_LIVE_VID_COMP2_SF 0x220 > +#define ZYNQMP_DISP_AV_BUF_LIVE_VID_CONFIG 0x224 > +#define ZYNQMP_DISP_AV_BUF_LIVE_GFX_COMP0_SF 0x228 > +#define ZYNQMP_DISP_AV_BUF_LIVE_GFX_COMP1_SF 0x22c > +#define ZYNQMP_DISP_AV_BUF_LIVE_GFX_COMP2_SF 0x230 > +#define ZYNQMP_DISP_AV_BUF_LIVE_GFX_CONFIG 0x234 > +#define ZYNQMP_DISP_AV_BUF_4BIT_SF 0x11111 > +#define ZYNQMP_DISP_AV_BUF_5BIT_SF 0x10842 > +#define ZYNQMP_DISP_AV_BUF_6BIT_SF 0x10410 > +#define ZYNQMP_DISP_AV_BUF_8BIT_SF 0x10101 > +#define ZYNQMP_DISP_AV_BUF_10BIT_SF 0x10040 > +#define ZYNQMP_DISP_AV_BUF_NULL_SF 0 > +#define ZYNQMP_DISP_AV_BUF_NUM_SF 3 > +#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_6 0x0 > +#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_8 0x1 > +#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_10 0x2 > +#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_12 0x3 > +#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_MASK GENMASK(2, 0) > +#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_RGB 0x0 > +#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_YUV444 0x1 > +#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_YUV422 0x2 > +#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_YONLY 0x3 > +#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_MASK GENMASK(5, 4) > +#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_CB_FIRST BIT(8) > +#define ZYNQMP_DISP_AV_BUF_PALETTE_MEMORY 0x400 > + > +/* Audio registers */ > +#define ZYNQMP_DISP_AUD_MIXER_VOLUME 0x0 > +#define ZYNQMP_DISP_AUD_MIXER_VOLUME_NO_SCALE 0x20002000 > +#define ZYNQMP_DISP_AUD_MIXER_META_DATA 0x4 > +#define ZYNQMP_DISP_AUD_CH_STATUS0 0x8 > +#define ZYNQMP_DISP_AUD_CH_STATUS1 0xc > +#define ZYNQMP_DISP_AUD_CH_STATUS2 0x10 > +#define ZYNQMP_DISP_AUD_CH_STATUS3 0x14 > +#define ZYNQMP_DISP_AUD_CH_STATUS4 0x18 > +#define ZYNQMP_DISP_AUD_CH_STATUS5 0x1c > +#define ZYNQMP_DISP_AUD_CH_A_DATA0 0x20 > +#define ZYNQMP_DISP_AUD_CH_A_DATA1 0x24 > +#define ZYNQMP_DISP_AUD_CH_A_DATA2 0x28 > +#define ZYNQMP_DISP_AUD_CH_A_DATA3 0x2c > +#define ZYNQMP_DISP_AUD_CH_A_DATA4 0x30 > +#define ZYNQMP_DISP_AUD_CH_A_DATA5 0x34 > +#define ZYNQMP_DISP_AUD_CH_B_DATA0 0x38 > +#define ZYNQMP_DISP_AUD_CH_B_DATA1 0x3c > +#define ZYNQMP_DISP_AUD_CH_B_DATA2 0x40 > +#define ZYNQMP_DISP_AUD_CH_B_DATA3 0x44 > +#define ZYNQMP_DISP_AUD_CH_B_DATA4 0x48 > +#define ZYNQMP_DISP_AUD_CH_B_DATA5 0x4c > +#define ZYNQMP_DISP_AUD_SOFT_RESET 0xc00 > +#define ZYNQMP_DISP_AUD_SOFT_RESET_AUD_SRST BIT(0) > + > +#define ZYNQMP_DISP_AV_BUF_NUM_VID_GFX_BUFFERS 4 > +#define ZYNQMP_DISP_AV_BUF_NUM_BUFFERS 6 > + > +#define ZYNQMP_DISP_NUM_LAYERS 2 > +#define ZYNQMP_DISP_MAX_NUM_SUB_PLANES 3 > +/* > + * 3840x2160 is advertised max resolution, but almost any resolutions under > + * 300Mhz pixel rate would work. Thus put 4096 as maximum width and height. > + */ > +#define ZYNQMP_DISP_MAX_WIDTH 4096 > +#define ZYNQMP_DISP_MAX_HEIGHT 4096 > +/* 44 bit addressing. This is acutally DPDMA limitation */ > +#define ZYNQMP_DISP_MAX_DMA_BIT 44 > + > +/** > + * enum zynqmp_disp_layer_type - Layer type (can be used for hw ID) > + * @ZYNQMP_DISP_LAYER_VID: Video layer > + * @ZYNQMP_DISP_LAYER_GFX: Graphics layer > + */ > +enum zynqmp_disp_layer_type { > + ZYNQMP_DISP_LAYER_VID, > + ZYNQMP_DISP_LAYER_GFX > +}; > + > +/** > + * enum zynqmp_disp_layer_mode - Layer mode > + * @ZYNQMP_DISP_LAYER_NONLIVE: non-live (memory) mode > + * @ZYNQMP_DISP_LAYER_LIVE: live (stream) mode > + */ > +enum zynqmp_disp_layer_mode { > + ZYNQMP_DISP_LAYER_NONLIVE, > + ZYNQMP_DISP_LAYER_LIVE > +}; > + > +/** > + * struct zynqmp_disp_layer_dma - struct for DMA engine > + * @chan: DMA channel > + * @is_active: flag if the DMA is active > + * @xt: Interleaved desc config container > + * @sgl: Data chunk for dma_interleaved_template > + */ > +struct zynqmp_disp_layer_dma { > + struct dma_chan *chan; > + bool is_active; > + struct dma_interleaved_template xt; > + struct data_chunk sgl[1]; > +}; > + > +/** > + * struct zynqmp_disp_layer - Display subsystem layer > + * @plane: DRM plane > + * @of_node: device node > + * @dma: struct for DMA engine > + * @num_chan: Number of DMA channel > + * @id: Layer ID > + * @offset: Layer offset in the register space > + * @enabled: flag if enabled > + * @fmt: Current format descriptor > + * @drm_fmts: Array of supported DRM formats > + * @num_fmts: Number of supported DRM formats > + * @bus_fmts: Array of supported bus formats > + * @num_bus_fmts: Number of supported bus formats > + * @w: Width > + * @h: Height > + * @mode: the operation mode > + * @other: other layer > + * @disp: back pointer to struct zynqmp_disp > + */ > +struct zynqmp_disp_layer { > + struct drm_plane plane; > + struct device_node *of_node; > + struct zynqmp_disp_layer_dma dma[ZYNQMP_DISP_MAX_NUM_SUB_PLANES]; > + unsigned int num_chan; > + enum zynqmp_disp_layer_type id; > + u32 offset; > + u8 enabled; > + const struct zynqmp_disp_fmt *fmt; > + u32 *drm_fmts; > + unsigned int num_fmts; > + u32 *bus_fmts; > + unsigned int num_bus_fmts; > + u32 w; > + u32 h; > + enum zynqmp_disp_layer_mode mode; > + struct zynqmp_disp_layer *other; > + struct zynqmp_disp *disp; > +}; > + > +/** > + * struct zynqmp_disp_blend - Blender > + * @base: Base address offset > + */ > +struct zynqmp_disp_blend { > + void __iomem *base; > +}; > + > +/** > + * struct zynqmp_disp_av_buf - AV buffer manager > + * @base: Base address offset > + */ > +struct zynqmp_disp_av_buf { > + void __iomem *base; > +}; > + > +/** > + * struct zynqmp_disp_aud - Audio > + * @base: Base address offset > + */ > +struct zynqmp_disp_aud { > + void __iomem *base; > +}; > + > +/** > + * struct zynqmp_disp - Display subsystem > + * @xlnx_crtc: Xilinx DRM crtc > + * @dev: device structure > + * @dpsub: Display subsystem > + * @drm: DRM core > + * @enabled: flag if enabled > + * @blend: Blender block > + * @av_buf: AV buffer manager block > + * @aud:Audio block > + * @layers: layers > + * @g_alpha_prop: global alpha property > + * @alpha: current global alpha value > + * @g_alpha_en_prop: the global alpha enable property > + * @alpha_en: flag if the global alpha is enabled > + * @color_prop: output color format property > + * @color: current output color value > + * @bg_c0_prop: 1st component of background color property > + * @bg_c0: current value of 1st background color component > + * @bg_c1_prop: 2nd component of background color property > + * @bg_c1: current value of 2nd background color component > + * @bg_c2_prop: 3rd component of background color property > + * @bg_c2: current value of 3rd background color component > + * @tpg_prop: Test Pattern Generation mode property > + * @tpg_on: current TPG mode state > + * @event: pending vblank event request > + * @_ps_pclk: Pixel clock from PS > + * @_pl_pclk: Pixel clock from PL > + * @pclk: Pixel clock > + * @pclk_en: Flag if the pixel clock is enabled > + * @_ps_audclk: Audio clock from PS > + * @_pl_audclk: Audio clock from PL > + * @audclk: Audio clock > + * @audclk_en: Flag if the audio clock is enabled > + * @aclk: APB clock > + * @aclk_en: Flag if the APB clock is enabled > + */ > +struct zynqmp_disp { > + struct xlnx_crtc xlnx_crtc; > + struct device *dev; > + struct zynqmp_dpsub *dpsub; > + struct drm_device *drm; > + bool enabled; > + struct zynqmp_disp_blend blend; > + struct zynqmp_disp_av_buf av_buf; > + struct zynqmp_disp_aud aud; > + struct zynqmp_disp_layer layers[ZYNQMP_DISP_NUM_LAYERS]; > + struct drm_property *g_alpha_prop; > + u32 alpha; > + struct drm_property *g_alpha_en_prop; > + bool alpha_en; > + struct drm_property *color_prop; > + unsigned int color; > + struct drm_property *bg_c0_prop; > + u32 bg_c0; > + struct drm_property *bg_c1_prop; > + u32 bg_c1; > + struct drm_property *bg_c2_prop; > + u32 bg_c2; > + struct drm_property *tpg_prop; > + bool tpg_on; > + struct drm_pending_vblank_event *event; > + /* Don't operate directly on _ps_ */ > + struct clk *_ps_pclk; > + struct clk *_pl_pclk; > + struct clk *pclk; > + bool pclk_en; > + struct clk *_ps_audclk; > + struct clk *_pl_audclk; > + struct clk *audclk; > + bool audclk_en; > + struct clk *aclk; > + bool aclk_en; > +}; > + > +/** > + * struct zynqmp_disp_fmt - Display subsystem format mapping > + * @drm_fmt: drm format > + * @disp_fmt: Display subsystem format > + * @bus_fmt: Bus formats (live formats) > + * @rgb: flag for RGB formats > + * @swap: flag to swap r & b for rgb formats, and u & v for yuv formats > + * @chroma_sub: flag for chroma subsampled formats > + * @sf: scaling factors for upto 3 color components > + */ > +struct zynqmp_disp_fmt { > + u32 drm_fmt; > + u32 disp_fmt; > + u32 bus_fmt; > + bool rgb; > + bool swap; > + bool chroma_sub; > + u32 sf[3]; > +}; > + > +static void zynqmp_disp_write(void __iomem *base, int offset, u32 val) > +{ > + writel(val, base + offset); > +} > + > +static u32 zynqmp_disp_read(void __iomem *base, int offset) > +{ > + return readl(base + offset); > +} > + > +static void zynqmp_disp_clr(void __iomem *base, int offset, u32 clr) > +{ > + zynqmp_disp_write(base, offset, zynqmp_disp_read(base, offset) & ~clr); > +} > + > +static void zynqmp_disp_set(void __iomem *base, int offset, u32 set) > +{ > + zynqmp_disp_write(base, offset, zynqmp_disp_read(base, offset) | set); > +} > + > +/* > + * Clock functions > + */ > + > +/** > + * zynqmp_disp_clk_enable - Enable the clock if needed > + * @clk: clk device > + * @flag: flag if the clock is enabled > + * > + * Enable the clock only if it's not enabled @flag. > + * > + * Return: value from clk_prepare_enable(). > + */ > +static int zynqmp_disp_clk_enable(struct clk *clk, bool *flag) > +{ > + int ret = 0; > + > + if (!*flag) { > + ret = clk_prepare_enable(clk); > + if (!ret) > + *flag = true; > + } > + > + return ret; > +} > + > +/** > + * zynqmp_disp_clk_enable - Enable the clock if needed > + * @clk: clk device > + * @flag: flag if the clock is enabled > + * > + * Disable the clock only if it's enabled @flag. > + */ > +static void zynqmp_disp_clk_disable(struct clk *clk, bool *flag) > +{ > + if (*flag) { > + clk_disable_unprepare(clk); > + *flag = false; > + } > +} > + > +/** > + * zynqmp_disp_clk_enable - Enable and disable the clock > + * @clk: clk device > + * @flag: flag if the clock is enabled > + * > + * This is to ensure the clock is disabled. The initial hardware state is > + * unknown, and this makes sure that the clock is disabled. > + * > + * Return: value from clk_prepare_enable(). > + */ > +static int zynqmp_disp_clk_enable_disable(struct clk *clk, bool *flag) > +{ > + int ret = 0; > + > + if (!*flag) { > + ret = clk_prepare_enable(clk); > + clk_disable_unprepare(clk); > + } > + > + return ret; > +} > + > +/* > + * Blender functions > + */ > + > +/** > + * zynqmp_disp_blend_set_output_fmt - Set the output format of the blend > + * @blend: blend object > + * @fmt: output format > + * > + * Set the output format to @fmt. > + */ > +static void > +zynqmp_disp_blend_set_output_fmt(struct zynqmp_disp_blend *blend, u32 fmt) > +{ > + u16 reset_coeffs[] = { 0x1000, 0x0, 0x0, > + 0x0, 0x1000, 0x0, > + 0x0, 0x0, 0x1000 }; > + u32 reset_offsets[] = { 0x0, 0x0, 0x0 }; > + u16 sdtv_coeffs[] = { 0x4c9, 0x864, 0x1d3, > + 0x7d4d, 0x7ab3, 0x800, > + 0x800, 0x794d, 0x7eb3 }; > + u32 full_range_offsets[] = { 0x0, 0x8000000, 0x8000000 }; > + u16 *coeffs; > + u32 *offsets; > + u32 offset, i; > + > + zynqmp_disp_write(blend->base, ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT, fmt); > + if (fmt == ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_RGB) { > + coeffs = reset_coeffs; > + offsets = reset_offsets; > + } else { > + /* Hardcode Full-range SDTV values. Can be runtime config */ > + coeffs = sdtv_coeffs; > + offsets = full_range_offsets; > + } > + > + offset = ZYNQMP_DISP_V_BLEND_RGB2YCBCR_COEFF0; > + for (i = 0; i < ZYNQMP_DISP_V_BLEND_NUM_COEFF; i++) > + zynqmp_disp_write(blend->base, offset + i * 4, coeffs[i]); > + > + offset = ZYNQMP_DISP_V_BLEND_LUMA_OUTCSC_OFFSET; > + for (i = 0; i < ZYNQMP_DISP_V_BLEND_NUM_OFFSET; i++) > + zynqmp_disp_write(blend->base, offset + i * 4, offsets[i]); > +} > + > +/** > + * zynqmp_disp_blend_layer_enable - Enable a layer > + * @blend: blend object > + * @layer: layer to enable > + * > + * Enable a layer @layer. > + */ > +static void zynqmp_disp_blend_layer_enable(struct zynqmp_disp_blend *blend, > + struct zynqmp_disp_layer *layer) > +{ > + u32 reg, offset, i, s0, s1; > + u16 sdtv_coeffs[] = { 0x1000, 0x166f, 0x0, > + 0x1000, 0x7483, 0x7a7f, > + 0x1000, 0x0, 0x1c5a }; > + u16 swap_coeffs[] = { 0x1000, 0x0, 0x0, > + 0x0, 0x1000, 0x0, > + 0x0, 0x0, 0x1000 }; > + u16 *coeffs; > + u32 offsets[] = { 0x0, 0x1800, 0x1800 }; > + > + reg = layer->fmt->rgb ? ZYNQMP_DISP_V_BLEND_LAYER_CONTROL_RGB : 0; > + reg |= layer->fmt->chroma_sub ? > + ZYNQMP_DISP_V_BLEND_LAYER_CONTROL_EN_US : 0; > + > + zynqmp_disp_write(blend->base, > + ZYNQMP_DISP_V_BLEND_LAYER_CONTROL + layer->offset, > + reg); > + > + if (layer->id == ZYNQMP_DISP_LAYER_VID) > + offset = ZYNQMP_DISP_V_BLEND_IN1CSC_COEFF0; > + else > + offset = ZYNQMP_DISP_V_BLEND_IN2CSC_COEFF0; > + > + if (!layer->fmt->rgb) { > + coeffs = sdtv_coeffs; > + s0 = 1; > + s1 = 2; > + } else { > + coeffs = swap_coeffs; > + s0 = 0; > + s1 = 2; > + > + /* No offset for RGB formats */ > + for (i = 0; i < ZYNQMP_DISP_V_BLEND_NUM_OFFSET; i++) > + offsets[i] = 0; > + } > + > + if (layer->fmt->swap) { > + for (i = 0; i < 3; i++) { > + coeffs[i * 3 + s0] ^= coeffs[i * 3 + s1]; > + coeffs[i * 3 + s1] ^= coeffs[i * 3 + s0]; > + coeffs[i * 3 + s0] ^= coeffs[i * 3 + s1]; > + } > + } > + > + /* Program coefficients. Can be runtime configurable */ > + for (i = 0; i < ZYNQMP_DISP_V_BLEND_NUM_COEFF; i++) > + zynqmp_disp_write(blend->base, offset + i * 4, coeffs[i]); > + > + if (layer->id == ZYNQMP_DISP_LAYER_VID) > + offset = ZYNQMP_DISP_V_BLEND_LUMA_IN1CSC_OFFSET; > + else > + offset = ZYNQMP_DISP_V_BLEND_LUMA_IN2CSC_OFFSET; > + > + /* Program offsets. Can be runtime configurable */ > + for (i = 0; i < ZYNQMP_DISP_V_BLEND_NUM_OFFSET; i++) > + zynqmp_disp_write(blend->base, offset + i * 4, offsets[i]); > +} > + > +/** > + * zynqmp_disp_blend_layer_disable - Disable a layer > + * @blend: blend object > + * @layer: layer to disable > + * > + * Disable a layer @layer. > + */ > +static void zynqmp_disp_blend_layer_disable(struct zynqmp_disp_blend *blend, > + struct zynqmp_disp_layer *layer) > +{ > + u32 offset; > + unsigned int i; > + > + zynqmp_disp_write(blend->base, > + ZYNQMP_DISP_V_BLEND_LAYER_CONTROL + layer->offset, 0); > + > + if (layer->id == ZYNQMP_DISP_LAYER_VID) > + offset = ZYNQMP_DISP_V_BLEND_IN1CSC_COEFF0; > + else > + offset = ZYNQMP_DISP_V_BLEND_IN2CSC_COEFF0; > + for (i = 0; i < ZYNQMP_DISP_V_BLEND_NUM_COEFF; i++) > + zynqmp_disp_write(blend->base, offset + i * 4, 0); > + > + if (layer->id == ZYNQMP_DISP_LAYER_VID) > + offset = ZYNQMP_DISP_V_BLEND_LUMA_IN1CSC_OFFSET; > + else > + offset = ZYNQMP_DISP_V_BLEND_LUMA_IN2CSC_OFFSET; > + > + for (i = 0; i < ZYNQMP_DISP_V_BLEND_NUM_OFFSET; i++) > + zynqmp_disp_write(blend->base, offset + i * 4, 0); > +} > + > +/** > + * zynqmp_disp_blend_set_bg_color - Set the background color > + * @blend: blend object > + * @c0: color component 0 > + * @c1: color component 1 > + * @c2: color component 2 > + * > + * Set the background color. > + */ > +static void zynqmp_disp_blend_set_bg_color(struct zynqmp_disp_blend *blend, > + u32 c0, u32 c1, u32 c2) > +{ > + zynqmp_disp_write(blend->base, ZYNQMP_DISP_V_BLEND_BG_CLR_0, c0); > + zynqmp_disp_write(blend->base, ZYNQMP_DISP_V_BLEND_BG_CLR_1, c1); > + zynqmp_disp_write(blend->base, ZYNQMP_DISP_V_BLEND_BG_CLR_2, c2); > +} > + > +/** > + * zynqmp_disp_blend_set_alpha - Set the alpha for blending > + * @blend: blend object > + * @alpha: alpha value to be used > + * > + * Set the alpha for blending. > + */ > +static void > +zynqmp_disp_blend_set_alpha(struct zynqmp_disp_blend *blend, u32 alpha) > +{ > + u32 reg; > + > + reg = zynqmp_disp_read(blend->base, > + ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA); > + reg &= ~ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA_MASK; > + reg |= alpha << 1; > + zynqmp_disp_write(blend->base, ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA, > + reg); > +} > + > +/** > + * zynqmp_disp_blend_enable_alpha - Enable/disable the global alpha > + * @blend: blend object > + * @enable: flag to enable or disable alpha blending > + * > + * Enable/disable the global alpha blending based on @enable. > + */ > +static void > +zynqmp_disp_blend_enable_alpha(struct zynqmp_disp_blend *blend, bool enable) > +{ > + if (enable) > + zynqmp_disp_set(blend->base, > + ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA, BIT(0)); > + else > + zynqmp_disp_clr(blend->base, > + ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA, BIT(0)); > +} > + > +/* List of blend output formats */ > +/* The id / order should be aligned with zynqmp_disp_color_enum */ > +static const struct zynqmp_disp_fmt blend_output_fmts[] = { > + { > + .disp_fmt = ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_RGB, > + }, { > + .disp_fmt = ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_YCBCR444, > + }, { > + .disp_fmt = ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_YCBCR422, > + }, { > + .disp_fmt = ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_YONLY, > + } > +}; > + > +/* > + * AV buffer manager functions > + */ > + > +/* List of video layer formats */ > +#define ZYNQMP_DISP_AV_BUF_VID_FMT_YUYV 2 > +static const struct zynqmp_disp_fmt av_buf_vid_fmts[] = { > + { > + .drm_fmt = DRM_FORMAT_VYUY, > + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_VYUY, > + .rgb = false, > + .swap = true, > + .chroma_sub = true, > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + }, { > + .drm_fmt = DRM_FORMAT_UYVY, > + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_VYUY, > + .rgb = false, > + .swap = false, > + .chroma_sub = true, > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + }, { > + .drm_fmt = DRM_FORMAT_YUYV, > + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YUYV, > + .rgb = false, > + .swap = false, > + .chroma_sub = true, > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + }, { > + .drm_fmt = DRM_FORMAT_YVYU, > + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YUYV, > + .rgb = false, > + .swap = true, > + .chroma_sub = true, > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + }, { > + .drm_fmt = DRM_FORMAT_YUV422, > + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16, > + .rgb = false, > + .swap = false, > + .chroma_sub = true, > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + }, { > + .drm_fmt = DRM_FORMAT_YVU422, > + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16, > + .rgb = false, > + .swap = true, > + .chroma_sub = true, > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + }, { > + .drm_fmt = DRM_FORMAT_YUV444, > + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV24, > + .rgb = false, > + .swap = false, > + .chroma_sub = false, > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + }, { > + .drm_fmt = DRM_FORMAT_YVU444, > + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV24, > + .rgb = false, > + .swap = true, > + .chroma_sub = false, > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + }, { > + .drm_fmt = DRM_FORMAT_NV16, > + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI, > + .rgb = false, > + .swap = false, > + .chroma_sub = true, > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + }, { > + .drm_fmt = DRM_FORMAT_NV61, > + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI, > + .rgb = false, > + .swap = true, > + .chroma_sub = true, > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + }, { > + .drm_fmt = DRM_FORMAT_BGR888, > + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_RGB888, > + .rgb = true, > + .swap = false, > + .chroma_sub = false, > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + }, { > + .drm_fmt = DRM_FORMAT_RGB888, > + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_RGB888, > + .rgb = true, > + .swap = true, > + .chroma_sub = false, > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + }, { > + .drm_fmt = DRM_FORMAT_XBGR8888, > + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_RGBA8880, > + .rgb = true, > + .swap = false, > + .chroma_sub = false, > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + }, { > + .drm_fmt = DRM_FORMAT_XRGB8888, > + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_RGBA8880, > + .rgb = true, > + .swap = true, > + .chroma_sub = false, > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + }, { > + .drm_fmt = DRM_FORMAT_XBGR2101010, > + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_RGB888_10, > + .rgb = true, > + .swap = false, > + .chroma_sub = false, > + .sf[0] = ZYNQMP_DISP_AV_BUF_10BIT_SF, > + .sf[1] = ZYNQMP_DISP_AV_BUF_10BIT_SF, > + .sf[2] = ZYNQMP_DISP_AV_BUF_10BIT_SF, > + }, { > + .drm_fmt = DRM_FORMAT_XRGB2101010, > + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_RGB888_10, > + .rgb = true, > + .swap = true, > + .chroma_sub = false, > + .sf[0] = ZYNQMP_DISP_AV_BUF_10BIT_SF, > + .sf[1] = ZYNQMP_DISP_AV_BUF_10BIT_SF, > + .sf[2] = ZYNQMP_DISP_AV_BUF_10BIT_SF, > + }, { > + .drm_fmt = DRM_FORMAT_YUV420, > + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16_420, > + .rgb = false, > + .swap = false, > + .chroma_sub = true, > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + }, { > + .drm_fmt = DRM_FORMAT_YVU420, > + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16_420, > + .rgb = false, > + .swap = true, > + .chroma_sub = true, > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + }, { > + .drm_fmt = DRM_FORMAT_NV12, > + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI_420, > + .rgb = false, > + .swap = false, > + .chroma_sub = true, > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + }, { > + .drm_fmt = DRM_FORMAT_NV21, > + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI_420, > + .rgb = false, > + .swap = true, > + .chroma_sub = true, > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + } > +}; > + > +/* List of graphics layer formats */ > +#define ZYNQMP_DISP_AV_BUF_GFX_FMT_RGB565 10 > +static const struct zynqmp_disp_fmt av_buf_gfx_fmts[] = { > + { > + .drm_fmt = DRM_FORMAT_ABGR8888, > + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGBA8888, > + .rgb = true, > + .swap = false, > + .chroma_sub = false, > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + }, { > + .drm_fmt = DRM_FORMAT_ARGB8888, > + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGBA8888, > + .rgb = true, > + .swap = true, > + .chroma_sub = false, > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + }, { > + .drm_fmt = DRM_FORMAT_RGBA8888, > + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_ABGR8888, > + .rgb = true, > + .swap = false, > + .chroma_sub = false, > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + }, { > + .drm_fmt = DRM_FORMAT_BGRA8888, > + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_ABGR8888, > + .rgb = true, > + .swap = true, > + .chroma_sub = false, > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + }, { > + .drm_fmt = DRM_FORMAT_BGR888, > + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGB888, > + .rgb = true, > + .swap = false, > + .chroma_sub = false, > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + }, { > + .drm_fmt = DRM_FORMAT_RGB888, > + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_BGR888, > + .rgb = true, > + .swap = false, > + .chroma_sub = false, > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + }, { > + .drm_fmt = DRM_FORMAT_RGBA5551, > + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGBA5551, > + .rgb = true, > + .swap = false, > + .chroma_sub = false, > + .sf[0] = ZYNQMP_DISP_AV_BUF_5BIT_SF, > + .sf[1] = ZYNQMP_DISP_AV_BUF_5BIT_SF, > + .sf[2] = ZYNQMP_DISP_AV_BUF_5BIT_SF, > + }, { > + .drm_fmt = DRM_FORMAT_BGRA5551, > + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGBA5551, > + .rgb = true, > + .swap = true, > + .chroma_sub = false, > + .sf[0] = ZYNQMP_DISP_AV_BUF_5BIT_SF, > + .sf[1] = ZYNQMP_DISP_AV_BUF_5BIT_SF, > + .sf[2] = ZYNQMP_DISP_AV_BUF_5BIT_SF, > + }, { > + .drm_fmt = DRM_FORMAT_RGBA4444, > + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGBA4444, > + .rgb = true, > + .swap = false, > + .chroma_sub = false, > + .sf[0] = ZYNQMP_DISP_AV_BUF_4BIT_SF, > + .sf[1] = ZYNQMP_DISP_AV_BUF_4BIT_SF, > + .sf[2] = ZYNQMP_DISP_AV_BUF_4BIT_SF, > + }, { > + .drm_fmt = DRM_FORMAT_BGRA4444, > + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGBA4444, > + .rgb = true, > + .swap = true, > + .chroma_sub = false, > + .sf[0] = ZYNQMP_DISP_AV_BUF_4BIT_SF, > + .sf[1] = ZYNQMP_DISP_AV_BUF_4BIT_SF, > + .sf[2] = ZYNQMP_DISP_AV_BUF_4BIT_SF, > + }, { > + .drm_fmt = DRM_FORMAT_RGB565, > + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGB565, > + .rgb = true, > + .swap = false, > + .chroma_sub = false, > + .sf[0] = ZYNQMP_DISP_AV_BUF_5BIT_SF, > + .sf[1] = ZYNQMP_DISP_AV_BUF_6BIT_SF, > + .sf[2] = ZYNQMP_DISP_AV_BUF_5BIT_SF, > + }, { > + .drm_fmt = DRM_FORMAT_BGR565, > + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGB565, > + .rgb = true, > + .swap = true, > + .chroma_sub = false, > + .sf[0] = ZYNQMP_DISP_AV_BUF_5BIT_SF, > + .sf[1] = ZYNQMP_DISP_AV_BUF_6BIT_SF, > + .sf[2] = ZYNQMP_DISP_AV_BUF_5BIT_SF, > + } > +}; > + > +/* List of live formats */ > +/* Format can be combination of color, bpc, and cb-cr order. > + * - Color: RGB / YUV444 / YUV422 / Y only > + * - BPC: 6, 8, 10, 12 > + * - Swap: Cb and Cr swap > + * which can be 32 bus formats. Only list the subset of those for now. > + */ > +static const struct zynqmp_disp_fmt av_buf_live_fmts[] = { > + { > + .bus_fmt = MEDIA_BUS_FMT_RGB666_1X18, > + .disp_fmt = ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_6 || > + ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_RGB, > + .rgb = true, > + .swap = false, > + .chroma_sub = false, > + .sf[0] = ZYNQMP_DISP_AV_BUF_6BIT_SF, > + .sf[1] = ZYNQMP_DISP_AV_BUF_6BIT_SF, > + .sf[2] = ZYNQMP_DISP_AV_BUF_6BIT_SF, > + }, { > + .bus_fmt = MEDIA_BUS_FMT_RBG888_1X24, > + .disp_fmt = ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_8 || > + ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_RGB, > + .rgb = true, > + .swap = false, > + .chroma_sub = false, > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + }, { > + .bus_fmt = MEDIA_BUS_FMT_UYVY8_1X16, > + .disp_fmt = ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_8 || > + ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_YUV422, > + .rgb = false, > + .swap = false, > + .chroma_sub = true, > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + }, { > + .bus_fmt = MEDIA_BUS_FMT_VUY8_1X24, > + .disp_fmt = ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_8 || > + ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_YUV444, > + .rgb = false, > + .swap = false, > + .chroma_sub = false, > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > + }, { > + .bus_fmt = MEDIA_BUS_FMT_UYVY10_1X20, > + .disp_fmt = ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_10 || > + ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_YUV422, > + .rgb = false, > + .swap = false, > + .chroma_sub = true, > + .sf[0] = ZYNQMP_DISP_AV_BUF_10BIT_SF, > + .sf[1] = ZYNQMP_DISP_AV_BUF_10BIT_SF, > + .sf[2] = ZYNQMP_DISP_AV_BUF_10BIT_SF, > + } > +}; > + > +/** > + * zynqmp_disp_av_buf_set_fmt - Set the input formats > + * @av_buf: av buffer manager > + * @fmt: formats > + * > + * Set the av buffer manager format to @fmt. @fmt should have valid values > + * for both video and graphics layer. > + */ > +static void > +zynqmp_disp_av_buf_set_fmt(struct zynqmp_disp_av_buf *av_buf, u32 fmt) > +{ > + zynqmp_disp_write(av_buf->base, ZYNQMP_DISP_AV_BUF_FMT, fmt); > +} > + > +/** > + * zynqmp_disp_av_buf_get_fmt - Get the input formats > + * @av_buf: av buffer manager > + * > + * Get the input formats (which include video and graphics) of > + * av buffer manager. > + * > + * Return: value of ZYNQMP_DISP_AV_BUF_FMT register. > + */ > +static u32 > +zynqmp_disp_av_buf_get_fmt(struct zynqmp_disp_av_buf *av_buf) > +{ > + return zynqmp_disp_read(av_buf->base, ZYNQMP_DISP_AV_BUF_FMT); > +} > + > +/** > + * zynqmp_disp_av_buf_set_vid_clock_src - Set the video clock source > + * @av_buf: av buffer manager > + * @from_ps: flag if the video clock is from ps > + * > + * Set the video clock source based on @from_ps. It can come from either PS or > + * PL. > + */ > +static void > +zynqmp_disp_av_buf_set_vid_clock_src(struct zynqmp_disp_av_buf *av_buf, > + bool from_ps) > +{ > + u32 reg = zynqmp_disp_read(av_buf->base, ZYNQMP_DISP_AV_BUF_CLK_SRC); > + > + if (from_ps) > + reg |= ZYNQMP_DISP_AV_BUF_CLK_SRC_VID_FROM_PS; > + else > + reg &= ~ZYNQMP_DISP_AV_BUF_CLK_SRC_VID_FROM_PS; > + zynqmp_disp_write(av_buf->base, ZYNQMP_DISP_AV_BUF_CLK_SRC, reg); > +} > + > +/** > + * zynqmp_disp_av_buf_set_vid_timing_src - Set the video timing source > + * @av_buf: av buffer manager > + * @internal: flag if the video timing is generated internally > + * > + * Set the video timing source based on @internal. It can come externally or > + * be generated internally. > + */ > +static void > +zynqmp_disp_av_buf_set_vid_timing_src(struct zynqmp_disp_av_buf *av_buf, > + bool internal) > +{ > + u32 reg = zynqmp_disp_read(av_buf->base, ZYNQMP_DISP_AV_BUF_CLK_SRC); > + > + if (internal) > + reg |= ZYNQMP_DISP_AV_BUF_CLK_SRC_VID_INTERNAL_TIMING; > + else > + reg &= ~ZYNQMP_DISP_AV_BUF_CLK_SRC_VID_INTERNAL_TIMING; > + zynqmp_disp_write(av_buf->base, ZYNQMP_DISP_AV_BUF_CLK_SRC, reg); > +} > + > +/** > + * zynqmp_disp_av_buf_set_aud_clock_src - Set the audio clock source > + * @av_buf: av buffer manager > + * @from_ps: flag if the video clock is from ps > + * > + * Set the audio clock source based on @from_ps. It can come from either PS or > + * PL. > + */ > +static void > +zynqmp_disp_av_buf_set_aud_clock_src(struct zynqmp_disp_av_buf *av_buf, > + bool from_ps) > +{ > + u32 reg = zynqmp_disp_read(av_buf->base, ZYNQMP_DISP_AV_BUF_CLK_SRC); > + > + if (from_ps) > + reg |= ZYNQMP_DISP_AV_BUF_CLK_SRC_AUD_FROM_PS; > + else > + reg &= ~ZYNQMP_DISP_AV_BUF_CLK_SRC_AUD_FROM_PS; > + zynqmp_disp_write(av_buf->base, ZYNQMP_DISP_AV_BUF_CLK_SRC, reg); > +} > + > +/** > + * zynqmp_disp_av_buf_enable_buf - Enable buffers > + * @av_buf: av buffer manager > + * > + * Enable all (video and audio) buffers. > + */ > +static void > +zynqmp_disp_av_buf_enable_buf(struct zynqmp_disp_av_buf *av_buf) > +{ > + u32 reg, i; > + > + reg = ZYNQMP_DISP_AV_BUF_CHBUF_EN; > + reg |= ZYNQMP_DISP_AV_BUF_CHBUF_BURST_LEN_MAX << > + ZYNQMP_DISP_AV_BUF_CHBUF_BURST_LEN_SHIFT; > + > + for (i = 0; i < ZYNQMP_DISP_AV_BUF_NUM_VID_GFX_BUFFERS; i++) > + zynqmp_disp_write(av_buf->base, > + ZYNQMP_DISP_AV_BUF_CHBUF + i * 4, reg); > + > + reg = ZYNQMP_DISP_AV_BUF_CHBUF_EN; > + reg |= ZYNQMP_DISP_AV_BUF_CHBUF_BURST_LEN_AUD_MAX << > + ZYNQMP_DISP_AV_BUF_CHBUF_BURST_LEN_SHIFT; > + > + for (; i < ZYNQMP_DISP_AV_BUF_NUM_BUFFERS; i++) > + zynqmp_disp_write(av_buf->base, > + ZYNQMP_DISP_AV_BUF_CHBUF + i * 4, reg); > +} > + > +/** > + * zynqmp_disp_av_buf_disable_buf - Disable buffers > + * @av_buf: av buffer manager > + * > + * Disable all (video and audio) buffers. > + */ > +static void > +zynqmp_disp_av_buf_disable_buf(struct zynqmp_disp_av_buf *av_buf) > +{ > + u32 reg, i; > + > + reg = ZYNQMP_DISP_AV_BUF_CHBUF_FLUSH & ~ZYNQMP_DISP_AV_BUF_CHBUF_EN; > + for (i = 0; i < ZYNQMP_DISP_AV_BUF_NUM_BUFFERS; i++) > + zynqmp_disp_write(av_buf->base, > + ZYNQMP_DISP_AV_BUF_CHBUF + i * 4, reg); > +} > + > +/** > + * zynqmp_disp_av_buf_enable_aud - Enable audio > + * @av_buf: av buffer manager > + * > + * Enable all audio buffers. > + */ > +static void > +zynqmp_disp_av_buf_enable_aud(struct zynqmp_disp_av_buf *av_buf) > +{ > + u32 reg; > + > + reg = zynqmp_disp_read(av_buf->base, ZYNQMP_DISP_AV_BUF_OUTPUT); > + reg &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_MASK; > + reg |= ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_MEM; > + reg |= ZYNQMP_DISP_AV_BUF_OUTPUT_AUD2_EN; > + zynqmp_disp_write(av_buf->base, ZYNQMP_DISP_AV_BUF_OUTPUT, reg); > +} > + > +/** > + * zynqmp_disp_av_buf_enable - Enable the video pipe > + * @av_buf: av buffer manager > + * > + * De-assert the video pipe reset > + */ > +static void > +zynqmp_disp_av_buf_enable(struct zynqmp_disp_av_buf *av_buf) > +{ > + zynqmp_disp_write(av_buf->base, ZYNQMP_DISP_AV_BUF_SRST_REG, 0); > +} > + > +/** > + * zynqmp_disp_av_buf_disable - Disable the video pipe > + * @av_buf: av buffer manager > + * > + * Assert the video pipe reset > + */ > +static void > +zynqmp_disp_av_buf_disable(struct zynqmp_disp_av_buf *av_buf) > +{ > + zynqmp_disp_write(av_buf->base, ZYNQMP_DISP_AV_BUF_SRST_REG, > + ZYNQMP_DISP_AV_BUF_SRST_REG_VID_RST); > +} > + > +/** > + * zynqmp_disp_av_buf_disable_aud - Disable audio > + * @av_buf: av buffer manager > + * > + * Disable all audio buffers. > + */ > +static void > +zynqmp_disp_av_buf_disable_aud(struct zynqmp_disp_av_buf *av_buf) > +{ > + u32 reg; > + > + reg = zynqmp_disp_read(av_buf->base, ZYNQMP_DISP_AV_BUF_OUTPUT); > + reg &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_MASK; > + reg |= ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_DISABLE; > + reg &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_AUD2_EN; > + zynqmp_disp_write(av_buf->base, ZYNQMP_DISP_AV_BUF_OUTPUT, reg); > +} > + > +/** > + * zynqmp_disp_av_buf_set_tpg - Set TPG mode > + * @av_buf: av buffer manager > + * @tpg_on: if TPG should be on > + * > + * Set the TPG mode based on @tpg_on. > + */ > +static void zynqmp_disp_av_buf_set_tpg(struct zynqmp_disp_av_buf *av_buf, > + bool tpg_on) > +{ > + u32 reg; > + > + reg = zynqmp_disp_read(av_buf->base, ZYNQMP_DISP_AV_BUF_OUTPUT); > + reg &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_MASK; > + if (tpg_on) > + reg |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_PATTERN; > + else > + reg &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_PATTERN; > + zynqmp_disp_write(av_buf->base, ZYNQMP_DISP_AV_BUF_OUTPUT, reg); > +} > + > +/** > + * zynqmp_disp_av_buf_enable_vid - Enable the video layer buffer > + * @av_buf: av buffer manager > + * @layer: layer to enable > + * @mode: operation mode of layer > + * > + * Enable the video/graphics buffer for @layer. > + */ > +static void zynqmp_disp_av_buf_enable_vid(struct zynqmp_disp_av_buf *av_buf, > + struct zynqmp_disp_layer *layer, > + enum zynqmp_disp_layer_mode mode) > +{ > + u32 reg; > + > + reg = zynqmp_disp_read(av_buf->base, ZYNQMP_DISP_AV_BUF_OUTPUT); > + if (layer->id == ZYNQMP_DISP_LAYER_VID) { > + reg &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_MASK; > + if (mode == ZYNQMP_DISP_LAYER_NONLIVE) > + reg |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_MEM; > + else > + reg |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_LIVE; > + } else { > + reg &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_MASK; > + reg |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_MEM; > + if (mode == ZYNQMP_DISP_LAYER_NONLIVE) > + reg |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_MEM; > + else > + reg |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_LIVE; > + } > + zynqmp_disp_write(av_buf->base, ZYNQMP_DISP_AV_BUF_OUTPUT, reg); > +} > + > +/** > + * zynqmp_disp_av_buf_disable_vid - Disable the video layer buffer > + * @av_buf: av buffer manager > + * @layer: layer to disable > + * > + * Disable the video/graphics buffer for @layer. > + */ > +static void > +zynqmp_disp_av_buf_disable_vid(struct zynqmp_disp_av_buf *av_buf, > + struct zynqmp_disp_layer *layer) > +{ > + u32 reg; > + > + reg = zynqmp_disp_read(av_buf->base, ZYNQMP_DISP_AV_BUF_OUTPUT); > + if (layer->id == ZYNQMP_DISP_LAYER_VID) { > + reg &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_MASK; > + reg |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_NONE; > + } else { > + reg &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_MASK; > + reg |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_DISABLE; > + } > + zynqmp_disp_write(av_buf->base, ZYNQMP_DISP_AV_BUF_OUTPUT, reg); > +} > + > +/** > + * zynqmp_disp_av_buf_init_sf - Initialize scaling factors > + * @av_buf: av buffer manager > + * @vid_fmt: video format descriptor > + * @gfx_fmt: graphics format descriptor > + * > + * Initialize scaling factors for both video and graphics layers. > + * If the format descriptor is NULL, the function skips the programming. > + */ > +static void zynqmp_disp_av_buf_init_sf(struct zynqmp_disp_av_buf *av_buf, > + const struct zynqmp_disp_fmt *vid_fmt, > + const struct zynqmp_disp_fmt *gfx_fmt) > +{ > + unsigned int i; > + u32 offset; > + > + if (gfx_fmt) { > + offset = ZYNQMP_DISP_AV_BUF_GFX_COMP0_SF; > + for (i = 0; i < ZYNQMP_DISP_AV_BUF_NUM_SF; i++) > + zynqmp_disp_write(av_buf->base, offset + i * 4, > + gfx_fmt->sf[i]); > + } > + > + if (vid_fmt) { > + offset = ZYNQMP_DISP_AV_BUF_VID_COMP0_SF; > + for (i = 0; i < ZYNQMP_DISP_AV_BUF_NUM_SF; i++) > + zynqmp_disp_write(av_buf->base, offset + i * 4, > + vid_fmt->sf[i]); > + } > +} > + > +/* > + * Audio functions > + */ > + > +/** > + * zynqmp_disp_aud_init - Initialize the audio > + * @aud: audio > + * > + * Initialize the audio with default mixer volume. The de-assertion will > + * initialize the audio states. > + */ > +static void zynqmp_disp_aud_init(struct zynqmp_disp_aud *aud) > +{ > + /* Clear the audio soft reset register as it's an non-reset flop */ > + zynqmp_disp_write(aud->base, ZYNQMP_DISP_AUD_SOFT_RESET, 0); > + zynqmp_disp_write(aud->base, ZYNQMP_DISP_AUD_MIXER_VOLUME, > + ZYNQMP_DISP_AUD_MIXER_VOLUME_NO_SCALE); > +} > + > +/** > + * zynqmp_disp_aud_deinit - De-initialize the audio > + * @aud: audio > + * > + * Put the audio in reset. > + */ > +static void zynqmp_disp_aud_deinit(struct zynqmp_disp_aud *aud) > +{ > + zynqmp_disp_set(aud->base, ZYNQMP_DISP_AUD_SOFT_RESET, > + ZYNQMP_DISP_AUD_SOFT_RESET_AUD_SRST); > +} > + > +/* > + * ZynqMP Display layer functions > + */ > + > +/** > + * zynqmp_disp_layer_check_size - Verify width and height for the layer > + * @disp: Display subsystem > + * @layer: layer > + * @width: width > + * @height: height > + * > + * The Display subsystem has the limitation that both layers should have > + * identical size. This function stores width and height of @layer, and verifies > + * if the size (width and height) is valid. > + * > + * Return: 0 on success, or -EINVAL if width or/and height is invalid. > + */ > +static int zynqmp_disp_layer_check_size(struct zynqmp_disp *disp, > + struct zynqmp_disp_layer *layer, > + u32 width, u32 height) > +{ > + struct zynqmp_disp_layer *other = layer->other; > + > + if (other->enabled && (other->w != width || other->h != height)) { > + dev_err(disp->dev, "Layer width:height must be %d:%d\n", > + other->w, other->h); > + return -EINVAL; > + } > + > + layer->w = width; > + layer->h = height; > + > + return 0; > +} > + > +/** > + * zynqmp_disp_map_fmt - Find the Display subsystem format for given drm format > + * @fmts: format table to look up > + * @size: size of the table @fmts > + * @drm_fmt: DRM format to search > + * > + * Search a Display subsystem format corresponding to the given DRM format > + * @drm_fmt, and return the format descriptor which contains the Display > + * subsystem format value. > + * > + * Return: a Display subsystem format descriptor on success, or NULL. > + */ > +static const struct zynqmp_disp_fmt * > +zynqmp_disp_map_fmt(const struct zynqmp_disp_fmt fmts[], > + unsigned int size, uint32_t drm_fmt) > +{ > + unsigned int i; > + > + for (i = 0; i < size; i++) > + if (fmts[i].drm_fmt == drm_fmt) > + return &fmts[i]; > + > + return NULL; > +} > + > +/** > + * zynqmp_disp_set_fmt - Set the format of the layer > + * @disp: Display subsystem > + * @layer: layer to set the format > + * @drm_fmt: DRM format to set > + * > + * Set the format of the given layer to @drm_fmt. > + * > + * Return: 0 on success. -EINVAL if @drm_fmt is not supported by the layer. > + */ > +static int zynqmp_disp_layer_set_fmt(struct zynqmp_disp *disp, > + struct zynqmp_disp_layer *layer, > + uint32_t drm_fmt) > +{ > + const struct zynqmp_disp_fmt *fmt; > + const struct zynqmp_disp_fmt *vid_fmt = NULL, *gfx_fmt = NULL; > + u32 size, fmts, mask; > + > + if (layer->id == ZYNQMP_DISP_LAYER_VID) { > + size = ARRAY_SIZE(av_buf_vid_fmts); > + mask = ~ZYNQMP_DISP_AV_BUF_FMT_NL_VID_MASK; > + fmt = zynqmp_disp_map_fmt(av_buf_vid_fmts, size, drm_fmt); > + vid_fmt = fmt; > + } else { > + size = ARRAY_SIZE(av_buf_gfx_fmts); > + mask = ~ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_MASK; > + fmt = zynqmp_disp_map_fmt(av_buf_gfx_fmts, size, drm_fmt); > + gfx_fmt = fmt; > + } > + > + if (!fmt) > + return -EINVAL; > + > + fmts = zynqmp_disp_av_buf_get_fmt(&disp->av_buf); > + fmts &= mask; > + fmts |= fmt->disp_fmt; > + zynqmp_disp_av_buf_set_fmt(&disp->av_buf, fmts); > + zynqmp_disp_av_buf_init_sf(&disp->av_buf, vid_fmt, gfx_fmt); > + layer->fmt = fmt; > + > + return 0; > +} > + > +/** > + * zynqmp_disp_set_tpg - Enable or disable TPG > + * @disp: Display subsystem > + * @layer: Video layer > + * @tpg_on: flag if TPG needs to be enabled or disabled > + * > + * Enable / disable the TPG mode on the video layer @layer depending on > + * @tpg_on. The video layer should be disabled prior to enable request. > + * > + * Return: 0 on success. -ENODEV if it's not video layer. -EIO if > + * the video layer is enabled. > + */ > +static int zynqmp_disp_layer_set_tpg(struct zynqmp_disp *disp, > + struct zynqmp_disp_layer *layer, > + bool tpg_on) > +{ > + if (layer->id != ZYNQMP_DISP_LAYER_VID) { > + dev_err(disp->dev, > + "only the video layer has the tpg mode\n"); > + return -ENODEV; > + } > + > + if (layer->enabled) { > + dev_err(disp->dev, > + "the video layer should be disabled for tpg mode\n"); > + return -EIO; > + } > + > + zynqmp_disp_av_buf_set_tpg(&disp->av_buf, tpg_on); > + disp->tpg_on = tpg_on; > + > + return 0; > +} > + > +/** > + * zynqmp_disp_get_tpg - Get the TPG mode status > + * @disp: Display subsystem > + * @layer: Video layer > + * > + * Return if the TPG is enabled or not. > + * > + * Return: true if TPG is on, otherwise false > + */ > +static bool zynqmp_disp_layer_get_tpg(struct zynqmp_disp *disp, > + struct zynqmp_disp_layer *layer) > +{ > + return disp->tpg_on; > +} > + > +/** > + * zynqmp_disp_get_fmt - Get the supported DRM formats of the layer > + * @disp: Display subsystem > + * @layer: layer to get the formats > + * @drm_fmts: pointer to array of DRM format strings > + * @num_fmts: pointer to number of returned DRM formats > + * > + * Get the supported DRM formats of the given layer. > + */ > +static void zynqmp_disp_layer_get_fmts(struct zynqmp_disp *disp, > + struct zynqmp_disp_layer *layer, > + u32 **drm_fmts, unsigned int *num_fmts) > +{ > + *drm_fmts = layer->drm_fmts; > + *num_fmts = layer->num_fmts; > +} > + > +/** > + * zynqmp_disp_layer_enable - Enable the layer > + * @disp: Display subsystem > + * @layer: layer to esable > + * @mode: operation mode > + * > + * Enable the layer @layer. > + * > + * Return: 0 on success, otherwise error code. > + */ > +static int zynqmp_disp_layer_enable(struct zynqmp_disp *disp, > + struct zynqmp_disp_layer *layer, > + enum zynqmp_disp_layer_mode mode) > +{ > + struct device *dev = disp->dev; > + struct dma_async_tx_descriptor *desc; > + enum dma_ctrl_flags flags; > + unsigned int i; > + > + if (layer->enabled && layer->mode != mode) { > + dev_err(dev, "layer is already enabled in different mode\n"); > + return -EBUSY; > + } > + > + zynqmp_disp_av_buf_enable_vid(&disp->av_buf, layer, mode); > + zynqmp_disp_blend_layer_enable(&disp->blend, layer); > + > + layer->enabled = true; > + layer->mode = mode; > + > + if (mode == ZYNQMP_DISP_LAYER_LIVE) > + return 0; > + > + for (i = 0; i < ZYNQMP_DISP_MAX_NUM_SUB_PLANES; i++) { > + struct zynqmp_disp_layer_dma *dma = &layer->dma[i]; > + > + if (dma->chan && dma->is_active) { > + flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; > + desc = dmaengine_prep_interleaved_dma(dma->chan, > + &dma->xt, flags); > + if (!desc) { > + dev_err(dev, "failed to prep DMA descriptor\n"); > + return -ENOMEM; > + } > + > + dmaengine_submit(desc); > + dma_async_issue_pending(dma->chan); > + } > + } > + > + return 0; > +} > + > +/** > + * zynqmp_disp_layer_disable - Disable the layer > + * @disp: Display subsystem > + * @layer: layer to disable > + * @mode: operation mode > + * > + * Disable the layer @layer. > + * > + * Return: 0 on success, or -EBUSY if the layer is in different mode. > + */ > +static int zynqmp_disp_layer_disable(struct zynqmp_disp *disp, > + struct zynqmp_disp_layer *layer, > + enum zynqmp_disp_layer_mode mode) > +{ > + struct device *dev = disp->dev; > + unsigned int i; > + > + if (layer->mode != mode) { > + dev_err(dev, "the layer is operating in different mode\n"); > + return -EBUSY; > + } > + > + for (i = 0; i < ZYNQMP_DISP_MAX_NUM_SUB_PLANES; i++) > + if (layer->dma[i].chan && layer->dma[i].is_active) > + dmaengine_terminate_sync(layer->dma[i].chan); > + > + zynqmp_disp_av_buf_disable_vid(&disp->av_buf, layer); > + zynqmp_disp_blend_layer_disable(&disp->blend, layer); > + layer->enabled = false; > + > + return 0; > +} > + > +/** > + * zynqmp_disp_layer_request_dma - Request DMA channels for a layer > + * @disp: Display subsystem > + * @layer: layer to request DMA channels > + * @name: identifier string for layer type > + * > + * Request DMA engine channels for corresponding layer. > + * > + * Return: 0 on success, or err value from of_dma_request_slave_channel(). > + */ > +static int > +zynqmp_disp_layer_request_dma(struct zynqmp_disp *disp, > + struct zynqmp_disp_layer *layer, const char *name) > +{ > + struct zynqmp_disp_layer_dma *dma; > + unsigned int i; > + int ret; > + > + for (i = 0; i < layer->num_chan; i++) { > + char temp[16]; > + > + dma = &layer->dma[i]; > + snprintf(temp, sizeof(temp), "%s%d", name, i); > + dma->chan = of_dma_request_slave_channel(layer->of_node, > + temp); > + if (IS_ERR(dma->chan)) { > + dev_err(disp->dev, "failed to request dma channel\n"); > + ret = PTR_ERR(dma->chan); > + dma->chan = NULL; > + return ret; > + } > + } > + > + return 0; > +} > + > +/** > + * zynqmp_disp_layer_release_dma - Release DMA channels for a layer > + * @disp: Display subsystem > + * @layer: layer to release DMA channels > + * > + * Release the dma channels associated with @layer. > + */ > +static void zynqmp_disp_layer_release_dma(struct zynqmp_disp *disp, > + struct zynqmp_disp_layer *layer) > +{ > + unsigned int i; > + > + for (i = 0; i < layer->num_chan; i++) { > + if (layer->dma[i].chan) { > + /* Make sure the channel is terminated before release */ > + dmaengine_terminate_all(layer->dma[i].chan); > + dma_release_channel(layer->dma[i].chan); > + } > + } > +} > + > +/** > + * zynqmp_disp_layer_is_live - if any layer is live > + * @disp: Display subsystem > + * > + * Return: true if any layer is live > + */ > +static bool zynqmp_disp_layer_is_live(struct zynqmp_disp *disp) > +{ > + unsigned int i; > + > + for (i = 0; i < ZYNQMP_DISP_NUM_LAYERS; i++) { > + if (disp->layers[i].enabled && > + disp->layers[i].mode == ZYNQMP_DISP_LAYER_LIVE) > + return true; > + } > + > + return false; > +} > + > +/** > + * zynqmp_disp_layer_is_enabled - if any layer is enabled > + * @disp: Display subsystem > + * > + * Return: true if any layer is enabled > + */ > +static bool zynqmp_disp_layer_is_enabled(struct zynqmp_disp *disp) > +{ > + unsigned int i; > + > + for (i = 0; i < ZYNQMP_DISP_NUM_LAYERS; i++) > + if (disp->layers[i].enabled) > + return true; > + > + return false; > +} > + > +/** > + * zynqmp_disp_layer_destroy - Destroy all layers > + * @disp: Display subsystem > + * > + * Destroy all layers. > + */ > +static void zynqmp_disp_layer_destroy(struct zynqmp_disp *disp) > +{ > + unsigned int i; > + > + for (i = 0; i < ZYNQMP_DISP_NUM_LAYERS; i++) { > + zynqmp_disp_layer_release_dma(disp, &disp->layers[i]); > + if (disp->layers[i].of_node) > + of_node_put(disp->layers[i].of_node); > + } > +} > + > +/** > + * zynqmp_disp_layer_create - Create all layers > + * @disp: Display subsystem > + * > + * Create all layers. > + * > + * Return: 0 on success, otherwise error code from failed function > + */ > +static int zynqmp_disp_layer_create(struct zynqmp_disp *disp) > +{ > + struct zynqmp_disp_layer *layer; > + unsigned int i; > + int num_chans[ZYNQMP_DISP_NUM_LAYERS] = { 3, 1 }; > + const char * const dma_name[] = { "vid", "gfx" }; > + int ret; > + > + for (i = 0; i < ZYNQMP_DISP_NUM_LAYERS; i++) { > + char temp[16]; > + > + layer = &disp->layers[i]; > + layer->id = i; > + layer->offset = i * 4; > + layer->other = &disp->layers[!i]; > + layer->num_chan = num_chans[i]; > + snprintf(temp, sizeof(temp), "%s-layer", dma_name[i]); > + layer->of_node = of_get_child_by_name(disp->dev->of_node, temp); > + if (!layer->of_node) > + goto err; > + ret = zynqmp_disp_layer_request_dma(disp, layer, dma_name[i]); > + if (ret) > + goto err; > + layer->disp = disp; > + } > + > + return 0; > + > +err: > + zynqmp_disp_layer_destroy(disp); > + return ret; > +} > + > +/* > + * ZynqMP Display internal functions > + */ > + > +/* > + * Output format enumeration for DRM property. > + * The ID should be aligned with blend_output_fmts. > + * The string should be aligned with how zynqmp_dp_set_color() decodes. > + */ > +static struct drm_prop_enum_list zynqmp_disp_color_enum[] = { > + { 0, "rgb" }, > + { 1, "ycrcb444" }, > + { 2, "ycrcb422" }, > + { 3, "yonly" }, > +}; > + > +/** > + * zynqmp_disp_set_output_fmt - Set the output format > + * @disp: Display subsystem > + * @id: the format ID. Refer to zynqmp_disp_color_enum[]. > + * > + * This function sets the output format of the display / blender as well as > + * the format of DP controller. The @id should be aligned with > + * zynqmp_disp_color_enum, thus function needs to be used for DRM property. > + */ > +static void > +zynqmp_disp_set_output_fmt(struct zynqmp_disp *disp, unsigned int id) > +{ > + const struct zynqmp_disp_fmt *fmt = &blend_output_fmts[id]; > + > + zynqmp_dp_set_color(disp->dpsub->dp, zynqmp_disp_color_enum[id].name); > + zynqmp_disp_blend_set_output_fmt(&disp->blend, fmt->disp_fmt); > +} > + > +/** > + * zynqmp_disp_set_bg_color - Set the background color > + * @disp: Display subsystem > + * @c0: color component 0 > + * @c1: color component 1 > + * @c2: color component 2 > + * > + * Set the background color with given color components (@c0, @c1, @c2). > + */ > +static void zynqmp_disp_set_bg_color(struct zynqmp_disp *disp, > + u32 c0, u32 c1, u32 c2) > +{ > + zynqmp_disp_blend_set_bg_color(&disp->blend, c0, c1, c2); > +} > + > +/** > + * zynqmp_disp_set_alpha - Set the alpha value > + * @disp: Display subsystem > + * @alpha: alpha value to set > + * > + * Set the alpha value for blending. > + */ > +static void zynqmp_disp_set_alpha(struct zynqmp_disp *disp, u32 alpha) > +{ > + disp->alpha = alpha; > + zynqmp_disp_blend_set_alpha(&disp->blend, alpha); > +} > + > +/** > + * zynqmp_disp_get_alpha - Get the alpha value > + * @disp: Display subsystem > + * > + * Get the alpha value for blending. > + * > + * Return: current alpha value. > + */ > +static u32 zynqmp_disp_get_alpha(struct zynqmp_disp *disp) > +{ > + return disp->alpha; > +} > + > +/** > + * zynqmp_disp_set_g_alpha - Enable/disable the global alpha blending > + * @disp: Display subsystem > + * @enable: flag to enable or disable alpha blending > + * > + * Set the alpha value for blending. > + */ > +static void zynqmp_disp_set_g_alpha(struct zynqmp_disp *disp, bool enable) > +{ > + disp->alpha_en = enable; > + zynqmp_disp_blend_enable_alpha(&disp->blend, enable); > +} > + > +/** > + * zynqmp_disp_get_g_alpha - Get the global alpha status > + * @disp: Display subsystem > + * > + * Get the global alpha statue. > + * > + * Return: true if global alpha is enabled, or false. > + */ > +static bool zynqmp_disp_get_g_alpha(struct zynqmp_disp *disp) > +{ > + return disp->alpha_en; > +} > + > +/** > + * zynqmp_disp_enable - Enable the Display subsystem > + * @disp: Display subsystem > + * > + * Enable the Display subsystem. > + */ > +static void zynqmp_disp_enable(struct zynqmp_disp *disp) > +{ > + bool live; > + > + if (disp->enabled) > + return; > + > + zynqmp_disp_av_buf_enable(&disp->av_buf); > + /* Choose clock source based on the DT clock handle */ > + zynqmp_disp_av_buf_set_vid_clock_src(&disp->av_buf, !!disp->_ps_pclk); > + zynqmp_disp_av_buf_set_aud_clock_src(&disp->av_buf, !!disp->_ps_audclk); > + live = zynqmp_disp_layer_is_live(disp); > + zynqmp_disp_av_buf_set_vid_timing_src(&disp->av_buf, !live); > + zynqmp_disp_av_buf_enable_buf(&disp->av_buf); > + zynqmp_disp_av_buf_enable_aud(&disp->av_buf); > + zynqmp_disp_aud_init(&disp->aud); > + disp->enabled = true; > +} > + > +/** > + * zynqmp_disp_disable - Disable the Display subsystem > + * @disp: Display subsystem > + * @force: flag to disable forcefully > + * > + * Disable the Display subsystem. > + */ > +static void zynqmp_disp_disable(struct zynqmp_disp *disp, bool force) > +{ > + struct drm_crtc *crtc = &disp->xlnx_crtc.crtc; > + > + if (!force && (!disp->enabled || zynqmp_disp_layer_is_enabled(disp))) > + return; > + > + zynqmp_disp_aud_deinit(&disp->aud); > + zynqmp_disp_av_buf_disable_aud(&disp->av_buf); > + zynqmp_disp_av_buf_disable_buf(&disp->av_buf); > + zynqmp_disp_av_buf_disable(&disp->av_buf); > + > + /* Mark the flip is done as crtc is disabled anyway */ > + if (crtc->state->event) { > + complete_all(crtc->state->event->base.completion); > + crtc->state->event = NULL; > + } > + > + disp->enabled = false; > +} > + > +/* > + * ZynqMP Display external functions for zynqmp_dp > + */ > + > +/** > + * zynqmp_disp_handle_vblank - Handle the vblank event > + * @disp: Display subsystem > + * > + * This function handles the vblank interrupt, and sends an event to > + * CRTC object. This will be called by the DP vblank interrupt handler. > + */ > +void zynqmp_disp_handle_vblank(struct zynqmp_disp *disp) > +{ > + struct drm_crtc *crtc = &disp->xlnx_crtc.crtc; > + struct drm_device *drm = crtc->dev; > + struct drm_pending_vblank_event *event; > + unsigned long flags; > + > + drm_crtc_handle_vblank(crtc); > + > + /* Finish page flip */ > + spin_lock_irqsave(&drm->event_lock, flags); > + event = disp->event; > + disp->event = NULL; > + if (event) { > + drm_crtc_send_vblank_event(crtc, event); > + drm_crtc_vblank_put(crtc); > + } > + spin_unlock_irqrestore(&drm->event_lock, flags); Please take a look at drm_crtc_arm_vblank - that implements the exact logic you're open-coding here. > +} > + > +/** > + * zynqmp_disp_get_apb_clk_rate - Get the current APB clock rate > + * @disp: Display subsystem > + * > + * Return: the current APB clock rate. > + */ > +unsigned int zynqmp_disp_get_apb_clk_rate(struct zynqmp_disp *disp) > +{ > + return clk_get_rate(disp->aclk); > +} > + > +/** > + * zynqmp_disp_aud_enabled - If the audio is enabled > + * @disp: Display subsystem > + * > + * Return if the audio is enabled depending on the audio clock. > + * > + * Return: true if audio is enabled, or false. > + */ > +bool zynqmp_disp_aud_enabled(struct zynqmp_disp *disp) > +{ > + return !!disp->audclk; > +} > + > +/** > + * zynqmp_disp_get_aud_clk_rate - Get the current audio clock rate > + * @disp: Display subsystem > + * > + * Return: the current audio clock rate. > + */ > +unsigned int zynqmp_disp_get_aud_clk_rate(struct zynqmp_disp *disp) > +{ > + if (zynqmp_disp_aud_enabled(disp)) > + return 0; > + return clk_get_rate(disp->aclk); > +} > + > +/** > + * zynqmp_disp_get_crtc_mask - Return the CRTC bit mask > + * @disp: Display subsystem > + * > + * Return: the crtc mask of the zyqnmp_disp CRTC. > + */ > +uint32_t zynqmp_disp_get_crtc_mask(struct zynqmp_disp *disp) > +{ > + return drm_crtc_mask(&disp->xlnx_crtc.crtc); > +} > + > +/* > + * DRM property functions > + */ > + > +static void zynqmp_disp_attach_vid_plane_property(struct zynqmp_disp *disp, > + struct drm_mode_object *obj) > +{ > + drm_object_attach_property(obj, disp->tpg_prop, false); > +} You have a lot of wrappers for core functions (clk_get_rate above is similar). This makes your code neater for you, but it makes it harder for linux-people to read since they always need to jump around. Please just directly call core functions like these instead of wrapping them. > + > +static void zynqmp_disp_attach_gfx_plane_property(struct zynqmp_disp *disp, > + struct drm_mode_object *obj) > +{ > + drm_object_attach_property(obj, disp->g_alpha_prop, > + ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA_MAX); > + disp->alpha = ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA_MAX; > + /* Enable the global alpha as default */ > + drm_object_attach_property(obj, disp->g_alpha_en_prop, true); > + disp->alpha_en = true; > +} > + > +static void zynqmp_disp_attach_crtc_property(struct zynqmp_disp *disp, > + struct drm_mode_object *obj) > +{ > + drm_object_attach_property(obj, disp->color_prop, 0); > + zynqmp_dp_set_color(disp->dpsub->dp, zynqmp_disp_color_enum[0].name); > + drm_object_attach_property(obj, disp->bg_c0_prop, 0); > + drm_object_attach_property(obj, disp->bg_c1_prop, 0); > + drm_object_attach_property(obj, disp->bg_c2_prop, 0); > +} > + > +static void zynqmp_disp_create_property(struct zynqmp_disp *disp) > +{ > + int num; > + u64 max; > + > + max = ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA_MAX; > + disp->g_alpha_prop = drm_property_create_range(disp->drm, 0, "alpha", 0, > + max); > + disp->g_alpha_en_prop = drm_property_create_bool(disp->drm, 0, > + "g_alpha_en"); > + num = ARRAY_SIZE(zynqmp_disp_color_enum); > + disp->color_prop = drm_property_create_enum(disp->drm, 0, > + "output_color", > + zynqmp_disp_color_enum, > + num); > + max = ZYNQMP_DISP_V_BLEND_BG_MAX; > + disp->bg_c0_prop = drm_property_create_range(disp->drm, 0, "bg_c0", 0, > + max); > + disp->bg_c1_prop = drm_property_create_range(disp->drm, 0, "bg_c1", 0, > + max); > + disp->bg_c2_prop = drm_property_create_range(disp->drm, 0, "bg_c2", 0, > + max); > + disp->tpg_prop = drm_property_create_bool(disp->drm, 0, "tpg"); > +} > + > +static void zynqmp_disp_destroy_property(struct zynqmp_disp *disp) > +{ > + drm_property_destroy(disp->drm, disp->bg_c2_prop); > + drm_property_destroy(disp->drm, disp->bg_c1_prop); > + drm_property_destroy(disp->drm, disp->bg_c0_prop); > + drm_property_destroy(disp->drm, disp->color_prop); > + drm_property_destroy(disp->drm, disp->g_alpha_en_prop); > + drm_property_destroy(disp->drm, disp->g_alpha_prop); > +} For all the property functions: Please split this out into a separate patch (probably even want a separate follow-up patch series). This is new uapi, and we have very strict requirements for that: https://dri.freedesktop.org/docs/drm/gpu/drm-uapi.html#open-source-userspace-requirements Especially around plane blending properties we already have a huge mess, adding more driver-private and nonstandard properties isn't a good idea. You can leave all your driver backend implementation in place, just don't register the properties and decode them in atomic_get/set_property. > + > +/* > + * DRM plane functions > + */ > + > +static inline struct zynqmp_disp_layer *plane_to_layer(struct drm_plane *plane) > +{ > + return container_of(plane, struct zynqmp_disp_layer, plane); > +} > + > +static int zynqmp_disp_plane_enable(struct drm_plane *plane) > +{ > + struct zynqmp_disp_layer *layer = plane_to_layer(plane); > + struct zynqmp_disp *disp = layer->disp; > + int ret; > + > + zynqmp_disp_set_g_alpha(disp, disp->alpha_en); > + zynqmp_disp_set_alpha(disp, disp->alpha); > + ret = zynqmp_disp_layer_enable(layer->disp, layer, > + ZYNQMP_DISP_LAYER_NONLIVE); > + if (ret) > + return ret; > + > + if (layer->id == ZYNQMP_DISP_LAYER_GFX && disp->tpg_on) { > + layer = &disp->layers[ZYNQMP_DISP_LAYER_VID]; > + zynqmp_disp_layer_set_tpg(disp, layer, disp->tpg_on); > + } > + > + return 0; > +} > + > +static int zynqmp_disp_plane_disable(struct drm_plane *plane) > +{ > + struct zynqmp_disp_layer *layer = plane_to_layer(plane); > + struct zynqmp_disp *disp = layer->disp; > + > + zynqmp_disp_layer_disable(disp, layer, ZYNQMP_DISP_LAYER_NONLIVE); > + if (layer->id == ZYNQMP_DISP_LAYER_VID && disp->tpg_on) > + zynqmp_disp_layer_set_tpg(disp, layer, disp->tpg_on); > + > + return 0; > +} > + > +static int zynqmp_disp_plane_mode_set(struct drm_plane *plane, > + struct drm_framebuffer *fb, > + int crtc_x, int crtc_y, > + unsigned int crtc_w, unsigned int crtc_h, > + u32 src_x, u32 src_y, > + u32 src_w, u32 src_h) > +{ > + struct zynqmp_disp_layer *layer = plane_to_layer(plane); > + const struct drm_format_info *info = fb->format; > + struct drm_format_name_buf format_name; > + struct device *dev = layer->disp->dev; > + dma_addr_t paddr; > + size_t offset; > + unsigned int i; > + int ret; > + > + if (!info) { > + dev_err(dev, "unsupported framebuffer format %s\n", > + drm_get_format_name(info->format, &format_name)); > + return -EINVAL; > + } > + > + ret = zynqmp_disp_layer_check_size(layer->disp, layer, src_w, src_h); > + if (ret) > + return ret; > + > + for (i = 0; i < info->num_planes; i++) { > + unsigned int width = src_w / (i ? info->hsub : 1); > + unsigned int height = src_h / (i ? info->vsub : 1); > + > + paddr = xlnx_fb_get_paddr(fb, i); > + if (!paddr) { > + dev_err(dev, "failed to get a paddr\n"); > + return -EINVAL; > + } > + > + layer->dma[i].xt.numf = height; > + layer->dma[i].sgl[0].size = width * info->cpp[i]; > + layer->dma[i].sgl[0].icg = fb->pitches[i] - > + layer->dma[i].sgl[0].size; > + offset = src_x * info->cpp[i] + src_y * fb->pitches[i]; > + offset += fb->offsets[i]; > + layer->dma[i].xt.src_start = paddr + offset; > + layer->dma[i].xt.frame_size = 1; > + layer->dma[i].xt.dir = DMA_MEM_TO_DEV; > + layer->dma[i].xt.src_sgl = true; > + layer->dma[i].xt.dst_sgl = false; > + layer->dma[i].is_active = true; > + } > + > + for (; i < ZYNQMP_DISP_MAX_NUM_SUB_PLANES; i++) > + layer->dma[i].is_active = false; > + > + ret = zynqmp_disp_layer_set_fmt(layer->disp, layer, info->format); > + if (ret) > + dev_err(dev, "failed to set dp_sub layer fmt\n"); > + > + return ret; > +} > + > +static void zynqmp_disp_plane_destroy(struct drm_plane *plane) > +{ > + drm_plane_cleanup(plane); > +} > + > +static int > +zynqmp_disp_plane_atomic_set_property(struct drm_plane *plane, > + struct drm_plane_state *state, > + struct drm_property *property, u64 val) > +{ > + struct zynqmp_disp_layer *layer = plane_to_layer(plane); > + struct zynqmp_disp *disp = layer->disp; > + int ret = 0; > + > + if (property == disp->g_alpha_prop) > + zynqmp_disp_set_alpha(disp, val); > + else if (property == disp->g_alpha_en_prop) > + zynqmp_disp_set_g_alpha(disp, val); > + else if (property == disp->tpg_prop) > + ret = zynqmp_disp_layer_set_tpg(disp, layer, val); > + else > + return -EINVAL; > + > + return ret; > +} > + > +static int > +zynqmp_disp_plane_atomic_get_property(struct drm_plane *plane, > + const struct drm_plane_state *state, > + struct drm_property *property, > + uint64_t *val) > +{ > + struct zynqmp_disp_layer *layer = plane_to_layer(plane); > + struct zynqmp_disp *disp = layer->disp; > + int ret = 0; > + > + if (property == disp->g_alpha_prop) > + *val = zynqmp_disp_get_alpha(disp); > + else if (property == disp->g_alpha_en_prop) > + *val = zynqmp_disp_get_g_alpha(disp); > + else if (property == disp->tpg_prop) > + *val = zynqmp_disp_layer_get_tpg(disp, layer); > + else > + return -EINVAL; > + > + return ret; > +} Please also move the above 2 functions into the separate property enabling patch series. > + > +static struct drm_plane_funcs zynqmp_disp_plane_funcs = { > + .update_plane = drm_atomic_helper_update_plane, > + .disable_plane = drm_atomic_helper_disable_plane, > + .atomic_set_property = zynqmp_disp_plane_atomic_set_property, > + .atomic_get_property = zynqmp_disp_plane_atomic_get_property, > + .destroy = zynqmp_disp_plane_destroy, > + .reset = drm_atomic_helper_plane_reset, > + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, > + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, > +}; > + > +static int zynqmp_disp_plane_prepare_fb(struct drm_plane *plane, > + struct drm_plane_state *new_state) > +{ > + return 0; > +} > + > +static void zynqmp_disp_plane_cleanup_fb(struct drm_plane *plane, > + struct drm_plane_state *old_state) > +{ > +} > + > +static int zynqmp_disp_plane_atomic_check(struct drm_plane *plane, > + struct drm_plane_state *state) > +{ > + return 0; > +} Please remove the above dummy functions, the helpers should all have fallbacks if a callback isn't set. This is general advise (in case I've missed a few of them). If you find a place where a callback is mandatory, we need to fix the core code. > + > +static void > +zynqmp_disp_plane_atomic_update(struct drm_plane *plane, > + struct drm_plane_state *old_state) > +{ > + int ret; > + > + if (!plane->state->crtc || !plane->state->fb) > + return; > + > + ret = zynqmp_disp_plane_mode_set(plane, plane->state->fb, > + plane->state->crtc_x, > + plane->state->crtc_y, > + plane->state->crtc_w, > + plane->state->crtc_h, > + plane->state->src_x >> 16, > + plane->state->src_y >> 16, > + plane->state->src_w >> 16, > + plane->state->src_h >> 16); > + if (ret) > + return; > + > + zynqmp_disp_plane_enable(plane); > +} > + > +static void > +zynqmp_disp_plane_atomic_disable(struct drm_plane *plane, > + struct drm_plane_state *old_state) > +{ > + zynqmp_disp_plane_disable(plane); > +} > + > +static const struct drm_plane_helper_funcs zynqmp_disp_plane_helper_funcs = { > + .prepare_fb = zynqmp_disp_plane_prepare_fb, > + .cleanup_fb = zynqmp_disp_plane_cleanup_fb, > + .atomic_check = zynqmp_disp_plane_atomic_check, > + .atomic_update = zynqmp_disp_plane_atomic_update, > + .atomic_disable = zynqmp_disp_plane_atomic_disable, > +}; > + > +static int zynqmp_disp_create_plane(struct zynqmp_disp *disp) > +{ > + struct zynqmp_disp_layer *layer; > + unsigned int i; > + u32 *fmts = NULL; > + unsigned int num_fmts = 0; > + enum drm_plane_type type; > + int ret; > + > + /* graphics layer is primary, and video layer is overaly */ > + type = DRM_PLANE_TYPE_OVERLAY; > + for (i = 0; i < ZYNQMP_DISP_NUM_LAYERS; i++) { > + layer = &disp->layers[i]; > + zynqmp_disp_layer_get_fmts(disp, layer, &fmts, &num_fmts); > + ret = drm_universal_plane_init(disp->drm, &layer->plane, 0, > + &zynqmp_disp_plane_funcs, fmts, > + num_fmts, NULL, type, NULL); > + if (ret) > + goto err_plane; > + drm_plane_helper_add(&layer->plane, > + &zynqmp_disp_plane_helper_funcs); > + type = DRM_PLANE_TYPE_PRIMARY; > + } > + > + /* Attach properties to each layers */ > + zynqmp_disp_attach_gfx_plane_property(disp, &layer->plane.base); > + layer = &disp->layers[ZYNQMP_DISP_LAYER_VID]; > + zynqmp_disp_attach_vid_plane_property(disp, &layer->plane.base); > + > + return ret; > + > +err_plane: > + if (i) > + drm_plane_cleanup(&disp->layers[0].plane); > + return ret; > +} > + > +static void zynqmp_disp_destroy_plane(struct zynqmp_disp *disp) > +{ > + unsigned int i; > + > + for (i = 0; i < ZYNQMP_DISP_NUM_LAYERS; i++) > + zynqmp_disp_plane_destroy(&disp->layers[i].plane); > +} > + > +/* > + * Xlnx crtc functions > + */ > + > +static inline struct zynqmp_disp *xlnx_crtc_to_disp(struct xlnx_crtc *xlnx_crtc) > +{ > + return container_of(xlnx_crtc, struct zynqmp_disp, xlnx_crtc); > +} > + > +static int zynqmp_disp_enable_vblank(struct xlnx_crtc *xlnx_crtc) > +{ > + struct zynqmp_disp *disp = xlnx_crtc_to_disp(xlnx_crtc); > + > + zynqmp_dp_enable_vblank(disp->dpsub->dp); > + return 0; > +} > + > +static void zynqmp_disp_disable_vblank(struct xlnx_crtc *xlnx_crtc) > +{ > + struct zynqmp_disp *disp = xlnx_crtc_to_disp(xlnx_crtc); > + > + zynqmp_dp_disable_vblank(disp->dpsub->dp); > +} > + > +static int zynqmp_disp_get_max_width(struct xlnx_crtc *xlnx_crtc) > +{ > + return ZYNQMP_DISP_MAX_WIDTH; > +} > + > +static int zynqmp_disp_get_max_height(struct xlnx_crtc *xlnx_crtc) > +{ > + return ZYNQMP_DISP_MAX_HEIGHT; > +} Bikeshed, feel free to ignore: This is a bit much indirection for my taste. > + > +static uint32_t zynqmp_disp_get_format(struct xlnx_crtc *xlnx_crtc) > +{ > + struct zynqmp_disp *disp = xlnx_crtc_to_disp(xlnx_crtc); > + > + return disp->layers[ZYNQMP_DISP_LAYER_GFX].fmt->drm_fmt; > +} > + > +static unsigned int zynqmp_disp_get_align(struct xlnx_crtc *xlnx_crtc) > +{ > + struct zynqmp_disp *disp = xlnx_crtc_to_disp(xlnx_crtc); > + struct zynqmp_disp_layer *layer = &disp->layers[ZYNQMP_DISP_LAYER_VID]; > + > + return 1 << layer->dma->chan->device->copy_align; > +} > + > +static u64 zynqmp_disp_get_dma_mask(struct xlnx_crtc *xlnx_crtc) > +{ > + return DMA_BIT_MASK(ZYNQMP_DISP_MAX_DMA_BIT); > +} > + > +/* > + * DRM crtc functions > + */ > + > +static inline struct zynqmp_disp *crtc_to_disp(struct drm_crtc *crtc) > +{ > + struct xlnx_crtc *xlnx_crtc = to_xlnx_crtc(crtc); > + > + return xlnx_crtc_to_disp(xlnx_crtc); > +} > + > +static int zynqmp_disp_crtc_mode_set(struct drm_crtc *crtc, > + struct drm_display_mode *mode, > + struct drm_display_mode *adjusted_mode, > + int x, int y, > + struct drm_framebuffer *old_fb) > +{ > + struct zynqmp_disp *disp = crtc_to_disp(crtc); > + unsigned long rate; > + long diff; > + int ret; > + > + zynqmp_disp_clk_disable(disp->pclk, &disp->pclk_en); > + ret = clk_set_rate(disp->pclk, adjusted_mode->clock * 1000); > + if (ret) { > + dev_err(disp->dev, "failed to set a pixel clock\n"); > + return ret; > + } > + > + rate = clk_get_rate(disp->pclk); > + diff = rate - adjusted_mode->clock * 1000; > + if (abs(diff) > (adjusted_mode->clock * 1000) / 20) { > + dev_info(disp->dev, "request pixel rate: %d actual rate: %lu\n", > + adjusted_mode->clock, rate); > + } else { > + dev_dbg(disp->dev, "request pixel rate: %d actual rate: %lu\n", > + adjusted_mode->clock, rate); > + } > + > + /* The timing register should be programmed always */ > + zynqmp_dp_encoder_mode_set_stream(disp->dpsub->dp, adjusted_mode); > + > + return 0; > +} > + > +static void > +zynqmp_disp_crtc_atomic_enable(struct drm_crtc *crtc, > + struct drm_crtc_state *old_crtc_state) > +{ > + struct zynqmp_disp *disp = crtc_to_disp(crtc); > + struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode; > + int ret, vrefresh; > + > + zynqmp_disp_crtc_mode_set(crtc, &crtc->state->mode, > + adjusted_mode, crtc->x, crtc->y, NULL); > + > + pm_runtime_get_sync(disp->dev); > + ret = zynqmp_disp_clk_enable(disp->pclk, &disp->pclk_en); > + if (ret) { > + dev_err(disp->dev, "failed to enable a pixel clock\n"); > + return; > + } > + zynqmp_disp_set_output_fmt(disp, disp->color); > + zynqmp_disp_set_bg_color(disp, disp->bg_c0, disp->bg_c1, disp->bg_c2); > + zynqmp_disp_enable(disp); > + /* Delay of 3 vblank intervals for timing gen to be stable */ > + vrefresh = (adjusted_mode->clock * 1000) / > + (adjusted_mode->vtotal * adjusted_mode->htotal); > + msleep(3 * 1000 / vrefresh); > +} > + > +static void > +zynqmp_disp_crtc_atomic_disable(struct drm_crtc *crtc, > + struct drm_crtc_state *old_crtc_state) > +{ > + struct zynqmp_disp *disp = crtc_to_disp(crtc); > + > + zynqmp_disp_clk_disable(disp->pclk, &disp->pclk_en); > + zynqmp_disp_plane_disable(crtc->primary); > + zynqmp_disp_disable(disp, true); > + pm_runtime_put_sync(disp->dev); > +} > + > +static void zynqmp_disp_crtc_mode_set_nofb(struct drm_crtc *crtc) > +{ > +} Again please remove the unecessary dumy functions. > + > +static int zynqmp_disp_crtc_atomic_check(struct drm_crtc *crtc, > + struct drm_crtc_state *state) > +{ > + return drm_atomic_add_affected_planes(state->state, crtc); > +} > + > +static void > +zynqmp_disp_crtc_atomic_begin(struct drm_crtc *crtc, > + struct drm_crtc_state *old_crtc_state) > +{ > + /* Don't rely on vblank when disabling crtc */ > + if (crtc->primary->state->fb && crtc->state->event) { > + struct zynqmp_disp *disp = crtc_to_disp(crtc); > + > + /* Consume the flip_done event from atomic helper */ > + crtc->state->event->pipe = drm_crtc_index(crtc); > + WARN_ON(drm_crtc_vblank_get(crtc) != 0); > + disp->event = crtc->state->event; > + crtc->state->event = NULL; > + } > +} > + > +static struct drm_crtc_helper_funcs zynqmp_disp_crtc_helper_funcs = { > + .atomic_enable = zynqmp_disp_crtc_atomic_enable, > + .atomic_disable = zynqmp_disp_crtc_atomic_disable, > + .mode_set_nofb = zynqmp_disp_crtc_mode_set_nofb, > + .atomic_check = zynqmp_disp_crtc_atomic_check, > + .atomic_begin = zynqmp_disp_crtc_atomic_begin, > +}; > + > +static void zynqmp_disp_crtc_destroy(struct drm_crtc *crtc) > +{ > + zynqmp_disp_crtc_atomic_disable(crtc, NULL); > + drm_crtc_cleanup(crtc); > +} > + > +static int > +zynqmp_disp_crtc_atomic_set_property(struct drm_crtc *crtc, > + struct drm_crtc_state *state, > + struct drm_property *property, > + uint64_t val) > +{ > + struct zynqmp_disp *disp = crtc_to_disp(crtc); > + > + /* > + * CRTC prop values are just stored here and applied when CRTC gets > + * enabled > + */ > + if (property == disp->color_prop) > + disp->color = val; > + else if (property == disp->bg_c0_prop) > + disp->bg_c0 = val; > + else if (property == disp->bg_c1_prop) > + disp->bg_c1 = val; > + else if (property == disp->bg_c2_prop) > + disp->bg_c2 = val; > + else > + return -EINVAL; > + > + return 0; > +} > + > +static int > +zynqmp_disp_crtc_atomic_get_property(struct drm_crtc *crtc, > + const struct drm_crtc_state *state, > + struct drm_property *property, > + uint64_t *val) > +{ > + struct zynqmp_disp *disp = crtc_to_disp(crtc); > + > + if (property == disp->color_prop) > + *val = disp->color; > + else if (property == disp->bg_c0_prop) > + *val = disp->bg_c0; > + else if (property == disp->bg_c1_prop) > + *val = disp->bg_c1; > + else if (property == disp->bg_c2_prop) > + *val = disp->bg_c2; > + else > + return -EINVAL; > + > + return 0; > +} Again property enabling into a separate patch series pls. > + > +static struct drm_crtc_funcs zynqmp_disp_crtc_funcs = { > + .destroy = zynqmp_disp_crtc_destroy, > + .set_config = drm_atomic_helper_set_config, > + .page_flip = drm_atomic_helper_page_flip, > + .atomic_set_property = zynqmp_disp_crtc_atomic_set_property, > + .atomic_get_property = zynqmp_disp_crtc_atomic_get_property, > + .reset = drm_atomic_helper_crtc_reset, > + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, > + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, > +}; > + > +static void zynqmp_disp_create_crtc(struct zynqmp_disp *disp) > +{ > + struct drm_plane *plane = &disp->layers[ZYNQMP_DISP_LAYER_GFX].plane; > + int ret; > + > + ret = drm_crtc_init_with_planes(disp->drm, &disp->xlnx_crtc.crtc, plane, > + NULL, &zynqmp_disp_crtc_funcs, NULL); > + drm_crtc_helper_add(&disp->xlnx_crtc.crtc, > + &zynqmp_disp_crtc_helper_funcs); > + zynqmp_disp_attach_crtc_property(disp, &disp->xlnx_crtc.crtc.base); > + > + disp->xlnx_crtc.enable_vblank = &zynqmp_disp_enable_vblank; > + disp->xlnx_crtc.disable_vblank = &zynqmp_disp_disable_vblank; > + disp->xlnx_crtc.get_max_width = &zynqmp_disp_get_max_width; > + disp->xlnx_crtc.get_max_height = &zynqmp_disp_get_max_height; > + disp->xlnx_crtc.get_format = &zynqmp_disp_get_format; > + disp->xlnx_crtc.get_align = &zynqmp_disp_get_align; > + disp->xlnx_crtc.get_dma_mask = &zynqmp_disp_get_dma_mask; > + xlnx_crtc_register(disp->drm, &disp->xlnx_crtc); > +} > + > +static void zynqmp_disp_destroy_crtc(struct zynqmp_disp *disp) > +{ > + xlnx_crtc_unregister(disp->drm, &disp->xlnx_crtc); > + zynqmp_disp_crtc_destroy(&disp->xlnx_crtc.crtc); > +} > + > +static void zynqmp_disp_map_crtc_to_plane(struct zynqmp_disp *disp) > +{ > + u32 possible_crtcs = drm_crtc_mask(&disp->xlnx_crtc.crtc); > + unsigned int i; > + > + for (i = 0; i < ZYNQMP_DISP_NUM_LAYERS; i++) > + disp->layers[i].plane.possible_crtcs = possible_crtcs; > +} > + > +/* > + * Component functions > + */ > + > +int zynqmp_disp_bind(struct device *dev, struct device *master, void *data) > +{ > + struct zynqmp_dpsub *dpsub = dev_get_drvdata(dev); > + struct zynqmp_disp *disp = dpsub->disp; > + struct drm_device *drm = data; > + int ret; > + > + disp->drm = drm; > + zynqmp_disp_create_property(disp); > + ret = zynqmp_disp_create_plane(disp); > + if (ret) > + return ret; > + zynqmp_disp_create_crtc(disp); > + zynqmp_disp_map_crtc_to_plane(disp); > + > + return 0; > +} > + > +void zynqmp_disp_unbind(struct device *dev, struct device *master, void *data) > +{ > + struct zynqmp_dpsub *dpsub = dev_get_drvdata(dev); > + struct zynqmp_disp *disp = dpsub->disp; > + > + zynqmp_disp_destroy_crtc(disp); > + zynqmp_disp_destroy_plane(disp); > + zynqmp_disp_destroy_property(disp); > +} > + > +/* > + * Platform initialization functions > + */ > + > +static int zynqmp_disp_enumerate_fmts(struct zynqmp_disp *disp) > +{ > + struct zynqmp_disp_layer *layer; > + u32 *bus_fmts; > + u32 i, size, num_bus_fmts; > + > + num_bus_fmts = ARRAY_SIZE(av_buf_live_fmts); > + bus_fmts = devm_kzalloc(disp->dev, sizeof(*bus_fmts) * num_bus_fmts, > + GFP_KERNEL); > + if (!bus_fmts) > + return -ENOMEM; > + for (i = 0; i < num_bus_fmts; i++) > + bus_fmts[i] = av_buf_live_fmts[i].bus_fmt; > + > + layer = &disp->layers[ZYNQMP_DISP_LAYER_VID]; > + layer->num_bus_fmts = num_bus_fmts; > + layer->bus_fmts = bus_fmts; > + size = ARRAY_SIZE(av_buf_vid_fmts); > + layer->num_fmts = size; > + layer->drm_fmts = devm_kzalloc(disp->dev, > + sizeof(*layer->drm_fmts) * size, > + GFP_KERNEL); > + if (!layer->drm_fmts) > + return -ENOMEM; > + for (i = 0; i < layer->num_fmts; i++) > + layer->drm_fmts[i] = av_buf_vid_fmts[i].drm_fmt; > + layer->fmt = &av_buf_vid_fmts[ZYNQMP_DISP_AV_BUF_VID_FMT_YUYV]; > + > + layer = &disp->layers[ZYNQMP_DISP_LAYER_GFX]; > + layer->num_bus_fmts = num_bus_fmts; > + layer->bus_fmts = bus_fmts; > + size = ARRAY_SIZE(av_buf_gfx_fmts); > + layer->num_fmts = size; > + layer->drm_fmts = devm_kzalloc(disp->dev, > + sizeof(*layer->drm_fmts) * size, > + GFP_KERNEL); > + if (!layer->drm_fmts) > + return -ENOMEM; > + > + for (i = 0; i < layer->num_fmts; i++) > + layer->drm_fmts[i] = av_buf_gfx_fmts[i].drm_fmt; > + layer->fmt = &av_buf_gfx_fmts[ZYNQMP_DISP_AV_BUF_GFX_FMT_RGB565]; > + > + return 0; > +} > + > +int zynqmp_disp_probe(struct platform_device *pdev) > +{ > + struct zynqmp_dpsub *dpsub; > + struct zynqmp_disp *disp; > + struct resource *res; > + int ret; > + > + disp = devm_kzalloc(&pdev->dev, sizeof(*disp), GFP_KERNEL); > + if (!disp) > + return -ENOMEM; > + disp->dev = &pdev->dev; > + > + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "blend"); > + disp->blend.base = devm_ioremap_resource(&pdev->dev, res); > + if (IS_ERR(disp->blend.base)) > + return PTR_ERR(disp->blend.base); > + > + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "av_buf"); > + disp->av_buf.base = devm_ioremap_resource(&pdev->dev, res); > + if (IS_ERR(disp->av_buf.base)) > + return PTR_ERR(disp->av_buf.base); > + > + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "aud"); > + disp->aud.base = devm_ioremap_resource(&pdev->dev, res); > + if (IS_ERR(disp->aud.base)) > + return PTR_ERR(disp->aud.base); > + > + dpsub = platform_get_drvdata(pdev); > + dpsub->disp = disp; > + disp->dpsub = dpsub; > + > + ret = zynqmp_disp_enumerate_fmts(disp); > + if (ret) > + return ret; > + > + /* Try the live PL video clock */ > + disp->_pl_pclk = devm_clk_get(disp->dev, "dp_live_video_in_clk"); > + if (!IS_ERR(disp->_pl_pclk)) { > + disp->pclk = disp->_pl_pclk; > + ret = zynqmp_disp_clk_enable_disable(disp->pclk, > + &disp->pclk_en); > + if (ret) > + disp->pclk = NULL; > + } else if (PTR_ERR(disp->_pl_pclk) == -EPROBE_DEFER) { > + return PTR_ERR(disp->_pl_pclk); > + } > + > + /* If the live PL video clock is not valid, fall back to PS clock */ > + if (!disp->pclk) { > + disp->_ps_pclk = devm_clk_get(disp->dev, "dp_vtc_pixel_clk_in"); > + if (IS_ERR(disp->_ps_pclk)) { > + dev_err(disp->dev, "failed to init any video clock\n"); > + return PTR_ERR(disp->_ps_pclk); > + } > + disp->pclk = disp->_ps_pclk; > + ret = zynqmp_disp_clk_enable_disable(disp->pclk, > + &disp->pclk_en); > + if (ret) { > + dev_err(disp->dev, "failed to init any video clock\n"); > + return ret; > + } > + } > + > + disp->aclk = devm_clk_get(disp->dev, "dp_apb_clk"); > + if (IS_ERR(disp->aclk)) > + return PTR_ERR(disp->aclk); > + ret = zynqmp_disp_clk_enable(disp->aclk, &disp->aclk_en); > + if (ret) { > + dev_err(disp->dev, "failed to enable the APB clk\n"); > + return ret; > + } > + > + /* Try the live PL audio clock */ > + disp->_pl_audclk = devm_clk_get(disp->dev, "dp_live_audio_aclk"); > + if (!IS_ERR(disp->_pl_audclk)) { > + disp->audclk = disp->_pl_audclk; > + ret = zynqmp_disp_clk_enable_disable(disp->audclk, > + &disp->audclk_en); > + if (ret) > + disp->audclk = NULL; > + } > + > + /* If the live PL audio clock is not valid, fall back to PS clock */ > + if (!disp->audclk) { > + disp->_ps_audclk = devm_clk_get(disp->dev, "dp_aud_clk"); > + if (!IS_ERR(disp->_ps_audclk)) { > + disp->audclk = disp->_ps_audclk; > + ret = zynqmp_disp_clk_enable_disable(disp->audclk, > + &disp->audclk_en); > + if (ret) > + disp->audclk = NULL; > + } > + > + if (!disp->audclk) { > + dev_err(disp->dev, > + "audio is disabled due to clock failure\n"); > + } > + } > + > + ret = zynqmp_disp_layer_create(disp); > + if (ret) > + goto error_aclk; > + > + return 0; > + > +error_aclk: > + zynqmp_disp_clk_disable(disp->aclk, &disp->aclk_en); > + return ret; > +} > + > +int zynqmp_disp_remove(struct platform_device *pdev) > +{ > + struct zynqmp_dpsub *dpsub = platform_get_drvdata(pdev); > + struct zynqmp_disp *disp = dpsub->disp; > + > + zynqmp_disp_layer_destroy(disp); > + if (disp->audclk) > + zynqmp_disp_clk_disable(disp->audclk, &disp->audclk_en); > + zynqmp_disp_clk_disable(disp->aclk, &disp->aclk_en); > + zynqmp_disp_clk_disable(disp->pclk, &disp->pclk_en); > + dpsub->disp = NULL; > + > + return 0; > +} > diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.h b/drivers/gpu/drm/xlnx/zynqmp_disp.h > new file mode 100644 > index 0000000..0291fc2 > --- /dev/null > +++ b/drivers/gpu/drm/xlnx/zynqmp_disp.h > @@ -0,0 +1,28 @@ > +/* > + * ZynqMP Display Driver > + * > + * Copyright (C) 2017 - 2018 Xilinx, Inc. > + * > + * Author: Hyun Woo Kwon <hyun.kwon@xilinx.com> > + * > + * SPDX-License-Identifier: GPL-2.0 > + */ > + > +#ifndef _ZYNQMP_DISP_H_ > +#define _ZYNQMP_DISP_H_ > + > +struct zynqmp_disp; > + > +void zynqmp_disp_handle_vblank(struct zynqmp_disp *disp); > +unsigned int zynqmp_disp_get_apb_clk_rate(struct zynqmp_disp *disp); > +bool zynqmp_disp_aud_enabled(struct zynqmp_disp *disp); > +unsigned int zynqmp_disp_get_aud_clk_rate(struct zynqmp_disp *disp); > +uint32_t zynqmp_disp_get_crtc_mask(struct zynqmp_disp *disp); > + > +int zynqmp_disp_bind(struct device *dev, struct device *master, void *data); > +void zynqmp_disp_unbind(struct device *dev, struct device *master, void *data); > + > +int zynqmp_disp_probe(struct platform_device *pdev); > +int zynqmp_disp_remove(struct platform_device *pdev); > + > +#endif /* _ZYNQMP_DISP_H_ */ > -- > 2.7.4 > > _______________________________________________ > dri-devel mailing list > dri-devel@lists.freedesktop.org > https://lists.freedesktop.org/mailman/listinfo/dri-devel
Hi Daniel, > -----Original Message----- > From: Daniel Vetter [mailto:daniel.vetter@ffwll.ch] On Behalf Of Daniel > Vetter > Sent: Tuesday, January 09, 2018 1:47 AM > To: Hyun Kwon <hyunk@xilinx.com> > Cc: dri-devel@lists.freedesktop.org; devicetree@vger.kernel.org; Michal > Simek <michal.simek@xilinx.com> > Subject: Re: [PATCH 07/10] drm: xlnx: DRM KMS driver for Xilinx ZynqMP > DP subsystem display > > On Thu, Jan 04, 2018 at 06:05:56PM -0800, Hyun Kwon wrote: > > Xilinx ZynqMP has a hardened display pipeline. The pipeline can > > be logically partitioned into 2 parts: display and DisplayPort. > > This driver handles the display part of the pipeline that handles > > buffer management and blending. > > > > Signed-off-by: Hyun Kwon <hyun.kwon@xilinx.com> > > --- > > drivers/gpu/drm/xlnx/zynqmp_disp.c | 2935 > ++++++++++++++++++++++++++++++++++++ > > drivers/gpu/drm/xlnx/zynqmp_disp.h | 28 + > > 2 files changed, 2963 insertions(+) > > create mode 100644 drivers/gpu/drm/xlnx/zynqmp_disp.c > > create mode 100644 drivers/gpu/drm/xlnx/zynqmp_disp.h > > > > diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c > b/drivers/gpu/drm/xlnx/zynqmp_disp.c > > new file mode 100644 > > index 0000000..68f829c > > --- /dev/null > > +++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c > > @@ -0,0 +1,2935 @@ > > +/* > > + * ZynqMP Display Controller Driver > > + * > > + * Copyright (C) 2017 - 2018 Xilinx, Inc. > > + * > > + * Author: Hyun Woo Kwon <hyun.kwon@xilinx.com> > > + * > > + * SPDX-License-Identifier: GPL-2.0 > > + */ > > + > > +#include <drm/drmP.h> > > +#include <drm/drm_atomic.h> > > +#include <drm/drm_atomic_helper.h> > > +#include <drm/drm_crtc.h> > > +#include <drm/drm_crtc_helper.h> > > +#include <drm/drm_fourcc.h> > > +#include <drm/drm_plane_helper.h> > > + > > +#include <linux/clk.h> > > +#include <linux/device.h> > > +#include <linux/dmaengine.h> > > +#include <linux/interrupt.h> > > +#include <linux/irqreturn.h> > > +#include <linux/list.h> > > +#include <linux/module.h> > > +#include <linux/mutex.h> > > +#include <linux/of.h> > > +#include <linux/of_dma.h> > > +#include <linux/platform_device.h> > > +#include <linux/pm_runtime.h> > > +#include <linux/spinlock.h> > > +#include <linux/uaccess.h> > > + > > +#include "xlnx_crtc.h" > > +#include "xlnx_fb.h" > > +#include "zynqmp_disp.h" > > +#include "zynqmp_dp.h" > > +#include "zynqmp_dpsub.h" > > + > > +/* > > + * Overview > > + * -------- > > + * > > + * The display part of ZynqMP DP subsystem. Internally, the device > > + * is partitioned into 3 blocks: AV buffer manager, Blender, Audio. > > + * The driver creates the DRM crtc and plane objectes and maps the DRM > > + * interface into those 3 blocks. In high level, the driver is layered > > + * in the following way: > > + * > > + * zynqmp_disp_crtc & zynqmp_disp_plane > > + * |->zynqmp_disp > > + * |->zynqmp_disp_aud > > + * |->zynqmp_disp_blend > > + * |->zynqmp_disp_av_buf > > + * > > + * The driver APIs are used externally by > > + * - zynqmp_dpsub: Top level ZynqMP DP subsystem driver > > + * - zynqmp_dp: ZynqMP DP driver > > + * - xlnx_crtc: Xilinx DRM specific crtc functions > > + */ > > + > > +/* Blender registers */ > > +#define ZYNQMP_DISP_V_BLEND_BG_CLR_0 0x0 > > +#define ZYNQMP_DISP_V_BLEND_BG_CLR_1 0x4 > > +#define ZYNQMP_DISP_V_BLEND_BG_CLR_2 0x8 > > +#define ZYNQMP_DISP_V_BLEND_BG_MAX 0xfff > > +#define ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA 0xc > > +#define ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA_MASK 0x1fe > > +#define ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA_MAX 0xff > > +#define ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT 0x14 > > +#define ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_RGB > 0x0 > > +#define ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_YCBCR444 0x1 > > +#define ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_YCBCR422 0x2 > > +#define ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_YONLY 0x3 > > +#define ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_XVYCC 0x4 > > +#define ZYNQMP_DISP_V_BLEND_OUTPUT_EN_DOWNSAMPLE BIT(4) > > +#define ZYNQMP_DISP_V_BLEND_LAYER_CONTROL 0x18 > > +#define ZYNQMP_DISP_V_BLEND_LAYER_CONTROL_EN_US > BIT(0) > > +#define ZYNQMP_DISP_V_BLEND_LAYER_CONTROL_RGB BIT(1) > > +#define ZYNQMP_DISP_V_BLEND_LAYER_CONTROL_BYPASS BIT(8) > > +#define ZYNQMP_DISP_V_BLEND_NUM_COEFF 9 > > +#define ZYNQMP_DISP_V_BLEND_RGB2YCBCR_COEFF0 0x20 > > +#define ZYNQMP_DISP_V_BLEND_RGB2YCBCR_COEFF1 0x24 > > +#define ZYNQMP_DISP_V_BLEND_RGB2YCBCR_COEFF2 0x28 > > +#define ZYNQMP_DISP_V_BLEND_RGB2YCBCR_COEFF3 0x2c > > +#define ZYNQMP_DISP_V_BLEND_RGB2YCBCR_COEFF4 0x30 > > +#define ZYNQMP_DISP_V_BLEND_RGB2YCBCR_COEFF5 0x34 > > +#define ZYNQMP_DISP_V_BLEND_RGB2YCBCR_COEFF6 0x38 > > +#define ZYNQMP_DISP_V_BLEND_RGB2YCBCR_COEFF7 0x3c > > +#define ZYNQMP_DISP_V_BLEND_RGB2YCBCR_COEFF8 0x40 > > +#define ZYNQMP_DISP_V_BLEND_IN1CSC_COEFF0 0x44 > > +#define ZYNQMP_DISP_V_BLEND_IN1CSC_COEFF1 0x48 > > +#define ZYNQMP_DISP_V_BLEND_IN1CSC_COEFF2 0x4c > > +#define ZYNQMP_DISP_V_BLEND_IN1CSC_COEFF3 0x50 > > +#define ZYNQMP_DISP_V_BLEND_IN1CSC_COEFF4 0x54 > > +#define ZYNQMP_DISP_V_BLEND_IN1CSC_COEFF5 0x58 > > +#define ZYNQMP_DISP_V_BLEND_IN1CSC_COEFF6 0x5c > > +#define ZYNQMP_DISP_V_BLEND_IN1CSC_COEFF7 0x60 > > +#define ZYNQMP_DISP_V_BLEND_IN1CSC_COEFF8 0x64 > > +#define ZYNQMP_DISP_V_BLEND_NUM_OFFSET 3 > > +#define ZYNQMP_DISP_V_BLEND_LUMA_IN1CSC_OFFSET 0x68 > > +#define ZYNQMP_DISP_V_BLEND_CR_IN1CSC_OFFSET 0x6c > > +#define ZYNQMP_DISP_V_BLEND_CB_IN1CSC_OFFSET 0x70 > > +#define ZYNQMP_DISP_V_BLEND_LUMA_OUTCSC_OFFSET 0x74 > > +#define ZYNQMP_DISP_V_BLEND_CR_OUTCSC_OFFSET 0x78 > > +#define ZYNQMP_DISP_V_BLEND_CB_OUTCSC_OFFSET 0x7c > > +#define ZYNQMP_DISP_V_BLEND_IN2CSC_COEFF0 0x80 > > +#define ZYNQMP_DISP_V_BLEND_IN2CSC_COEFF1 0x84 > > +#define ZYNQMP_DISP_V_BLEND_IN2CSC_COEFF2 0x88 > > +#define ZYNQMP_DISP_V_BLEND_IN2CSC_COEFF3 0x8c > > +#define ZYNQMP_DISP_V_BLEND_IN2CSC_COEFF4 0x90 > > +#define ZYNQMP_DISP_V_BLEND_IN2CSC_COEFF5 0x94 > > +#define ZYNQMP_DISP_V_BLEND_IN2CSC_COEFF6 0x98 > > +#define ZYNQMP_DISP_V_BLEND_IN2CSC_COEFF7 0x9c > > +#define ZYNQMP_DISP_V_BLEND_IN2CSC_COEFF8 0xa0 > > +#define ZYNQMP_DISP_V_BLEND_LUMA_IN2CSC_OFFSET 0xa4 > > +#define ZYNQMP_DISP_V_BLEND_CR_IN2CSC_OFFSET 0xa8 > > +#define ZYNQMP_DISP_V_BLEND_CB_IN2CSC_OFFSET 0xac > > +#define ZYNQMP_DISP_V_BLEND_CHROMA_KEY_ENABLE 0x1d0 > > +#define ZYNQMP_DISP_V_BLEND_CHROMA_KEY_COMP1 0x1d4 > > +#define ZYNQMP_DISP_V_BLEND_CHROMA_KEY_COMP2 0x1d8 > > +#define ZYNQMP_DISP_V_BLEND_CHROMA_KEY_COMP3 0x1dc > > + > > +/* AV buffer manager registers */ > > +#define ZYNQMP_DISP_AV_BUF_FMT 0x0 > > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_SHIFT 0 > > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_MASK (0x1f > << 0) > > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_UYVY (0 << > 0) > > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_VYUY (1 << > 0) > > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YVYU (2 << > 0) > > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YUYV (3 << > 0) > > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16 (4 << > 0) > > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV24 (5 << > 0) > > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI (6 << > 0) > > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_MONO (7 << > 0) > > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI2 (8 << > 0) > > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YUV444 (9 << > 0) > > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_RGB888 (10 > << 0) > > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_RGBA8880 (11 > << 0) > > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_RGB888_10 > (12 << 0) > > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YUV444_10 > (13 << 0) > > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI2_10 (14 > << 0) > > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI_10 > (15 << 0) > > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16_10 (16 > << 0) > > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV24_10 (17 > << 0) > > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YONLY_10 (18 > << 0) > > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16_420 (19 > << 0) > > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI_420 (20 > << 0) > > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI2_420 (21 > << 0) > > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16_420_10 (22 > << 0) > > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI_420_10 (23 > << 0) > > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI2_420_10 (24 > << 0) > > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_SHIFT 8 > > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_MASK (0xf > << 8) > > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGBA8888 > (0 << 8) > > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_ABGR8888 > (1 << 8) > > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGB888 (2 << > 8) > > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_BGR888 (3 << > 8) > > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGBA5551 > (4 << 8) > > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGBA4444 > (5 << 8) > > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGB565 (6 << > 8) > > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_8BPP (7 << > 8) > > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_4BPP (8 << > 8) > > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_2BPP (9 << > 8) > > +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_1BPP (10 > << 8) > > +#define ZYNQMP_DISP_AV_BUF_NON_LIVE_LATENCY 0x8 > > +#define ZYNQMP_DISP_AV_BUF_CHBUF 0x10 > > +#define ZYNQMP_DISP_AV_BUF_CHBUF_EN BIT(0) > > +#define ZYNQMP_DISP_AV_BUF_CHBUF_FLUSH BIT(1) > > +#define ZYNQMP_DISP_AV_BUF_CHBUF_BURST_LEN_SHIFT 2 > > +#define ZYNQMP_DISP_AV_BUF_CHBUF_BURST_LEN_MASK > (0xf << 2) > > +#define ZYNQMP_DISP_AV_BUF_CHBUF_BURST_LEN_MAX > 0xf > > +#define ZYNQMP_DISP_AV_BUF_CHBUF_BURST_LEN_AUD_MAX 0x3 > > +#define ZYNQMP_DISP_AV_BUF_STATUS 0x28 > > +#define ZYNQMP_DISP_AV_BUF_STC_CTRL 0x2c > > +#define ZYNQMP_DISP_AV_BUF_STC_CTRL_EN BIT(0) > > +#define ZYNQMP_DISP_AV_BUF_STC_CTRL_EVENT_SHIFT 1 > > +#define ZYNQMP_DISP_AV_BUF_STC_CTRL_EVENT_EX_VSYNC 0 > > +#define ZYNQMP_DISP_AV_BUF_STC_CTRL_EVENT_EX_VID 1 > > +#define ZYNQMP_DISP_AV_BUF_STC_CTRL_EVENT_EX_AUD 2 > > +#define ZYNQMP_DISP_AV_BUF_STC_CTRL_EVENT_INT_VSYNC 3 > > +#define ZYNQMP_DISP_AV_BUF_STC_INIT_VALUE0 0x30 > > +#define ZYNQMP_DISP_AV_BUF_STC_INIT_VALUE1 0x34 > > +#define ZYNQMP_DISP_AV_BUF_STC_ADJ 0x38 > > +#define ZYNQMP_DISP_AV_BUF_STC_VID_VSYNC_TS0 0x3c > > +#define ZYNQMP_DISP_AV_BUF_STC_VID_VSYNC_TS1 0x40 > > +#define ZYNQMP_DISP_AV_BUF_STC_EXT_VSYNC_TS0 0x44 > > +#define ZYNQMP_DISP_AV_BUF_STC_EXT_VSYNC_TS1 0x48 > > +#define ZYNQMP_DISP_AV_BUF_STC_CUSTOM_EVENT_TS0 > 0x4c > > +#define ZYNQMP_DISP_AV_BUF_STC_CUSTOM_EVENT_TS1 > 0x50 > > +#define ZYNQMP_DISP_AV_BUF_STC_CUSTOM_EVENT2_TS0 0x54 > > +#define ZYNQMP_DISP_AV_BUF_STC_CUSTOM_EVENT2_TS1 0x58 > > +#define ZYNQMP_DISP_AV_BUF_STC_SNAPSHOT0 0x60 > > +#define ZYNQMP_DISP_AV_BUF_STC_SNAPSHOT1 0x64 > > +#define ZYNQMP_DISP_AV_BUF_OUTPUT 0x70 > > +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_SHIFT 0 > > +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_MASK (0x3 > << 0) > > +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_LIVE (0 << > 0) > > +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_MEM (1 << > 0) > > +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_PATTERN (2 << > 0) > > +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_NONE (3 << > 0) > > +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_SHIFT 2 > > +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_MASK (0x3 > << 2) > > +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_DISABLE (0 << > 2) > > +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_MEM (1 << > 2) > > +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_LIVE (2 << > 2) > > +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_NONE (3 << > 2) > > +#define ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_SHIFT 4 > > +#define ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_MASK (0x3 > << 4) > > +#define ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_PL (0 << > 4) > > +#define ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_MEM (1 << > 4) > > +#define ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_PATTERN > (2 << 4) > > +#define ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_DISABLE (3 << > 4) > > +#define ZYNQMP_DISP_AV_BUF_OUTPUT_AUD2_EN BIT(6) > > +#define ZYNQMP_DISP_AV_BUF_HCOUNT_VCOUNT_INT0 0x74 > > +#define ZYNQMP_DISP_AV_BUF_HCOUNT_VCOUNT_INT1 0x78 > > +#define ZYNQMP_DISP_AV_BUF_PATTERN_GEN_SELECT 0x100 > > +#define ZYNQMP_DISP_AV_BUF_CLK_SRC 0x120 > > +#define ZYNQMP_DISP_AV_BUF_CLK_SRC_VID_FROM_PS BIT(0) > > +#define ZYNQMP_DISP_AV_BUF_CLK_SRC_AUD_FROM_PS > BIT(1) > > +#define ZYNQMP_DISP_AV_BUF_CLK_SRC_VID_INTERNAL_TIMING > BIT(2) > > +#define ZYNQMP_DISP_AV_BUF_SRST_REG 0x124 > > +#define ZYNQMP_DISP_AV_BUF_SRST_REG_VID_RST BIT(1) > > +#define ZYNQMP_DISP_AV_BUF_AUDIO_CH_CONFIG 0x12c > > +#define ZYNQMP_DISP_AV_BUF_GFX_COMP0_SF 0x200 > > +#define ZYNQMP_DISP_AV_BUF_GFX_COMP1_SF 0x204 > > +#define ZYNQMP_DISP_AV_BUF_GFX_COMP2_SF 0x208 > > +#define ZYNQMP_DISP_AV_BUF_VID_COMP0_SF 0x20c > > +#define ZYNQMP_DISP_AV_BUF_VID_COMP1_SF 0x210 > > +#define ZYNQMP_DISP_AV_BUF_VID_COMP2_SF 0x214 > > +#define ZYNQMP_DISP_AV_BUF_LIVE_VID_COMP0_SF 0x218 > > +#define ZYNQMP_DISP_AV_BUF_LIVE_VID_COMP1_SF 0x21c > > +#define ZYNQMP_DISP_AV_BUF_LIVE_VID_COMP2_SF 0x220 > > +#define ZYNQMP_DISP_AV_BUF_LIVE_VID_CONFIG 0x224 > > +#define ZYNQMP_DISP_AV_BUF_LIVE_GFX_COMP0_SF 0x228 > > +#define ZYNQMP_DISP_AV_BUF_LIVE_GFX_COMP1_SF 0x22c > > +#define ZYNQMP_DISP_AV_BUF_LIVE_GFX_COMP2_SF 0x230 > > +#define ZYNQMP_DISP_AV_BUF_LIVE_GFX_CONFIG 0x234 > > +#define ZYNQMP_DISP_AV_BUF_4BIT_SF 0x11111 > > +#define ZYNQMP_DISP_AV_BUF_5BIT_SF 0x10842 > > +#define ZYNQMP_DISP_AV_BUF_6BIT_SF 0x10410 > > +#define ZYNQMP_DISP_AV_BUF_8BIT_SF 0x10101 > > +#define ZYNQMP_DISP_AV_BUF_10BIT_SF 0x10040 > > +#define ZYNQMP_DISP_AV_BUF_NULL_SF 0 > > +#define ZYNQMP_DISP_AV_BUF_NUM_SF 3 > > +#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_6 0x0 > > +#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_8 0x1 > > +#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_10 0x2 > > +#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_12 0x3 > > +#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_MASK > GENMASK(2, 0) > > +#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_RGB 0x0 > > +#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_YUV444 0x1 > > +#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_YUV422 0x2 > > +#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_YONLY 0x3 > > +#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_MASK > GENMASK(5, 4) > > +#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_CB_FIRST BIT(8) > > +#define ZYNQMP_DISP_AV_BUF_PALETTE_MEMORY 0x400 > > + > > +/* Audio registers */ > > +#define ZYNQMP_DISP_AUD_MIXER_VOLUME 0x0 > > +#define ZYNQMP_DISP_AUD_MIXER_VOLUME_NO_SCALE > 0x20002000 > > +#define ZYNQMP_DISP_AUD_MIXER_META_DATA 0x4 > > +#define ZYNQMP_DISP_AUD_CH_STATUS0 0x8 > > +#define ZYNQMP_DISP_AUD_CH_STATUS1 0xc > > +#define ZYNQMP_DISP_AUD_CH_STATUS2 0x10 > > +#define ZYNQMP_DISP_AUD_CH_STATUS3 0x14 > > +#define ZYNQMP_DISP_AUD_CH_STATUS4 0x18 > > +#define ZYNQMP_DISP_AUD_CH_STATUS5 0x1c > > +#define ZYNQMP_DISP_AUD_CH_A_DATA0 0x20 > > +#define ZYNQMP_DISP_AUD_CH_A_DATA1 0x24 > > +#define ZYNQMP_DISP_AUD_CH_A_DATA2 0x28 > > +#define ZYNQMP_DISP_AUD_CH_A_DATA3 0x2c > > +#define ZYNQMP_DISP_AUD_CH_A_DATA4 0x30 > > +#define ZYNQMP_DISP_AUD_CH_A_DATA5 0x34 > > +#define ZYNQMP_DISP_AUD_CH_B_DATA0 0x38 > > +#define ZYNQMP_DISP_AUD_CH_B_DATA1 0x3c > > +#define ZYNQMP_DISP_AUD_CH_B_DATA2 0x40 > > +#define ZYNQMP_DISP_AUD_CH_B_DATA3 0x44 > > +#define ZYNQMP_DISP_AUD_CH_B_DATA4 0x48 > > +#define ZYNQMP_DISP_AUD_CH_B_DATA5 0x4c > > +#define ZYNQMP_DISP_AUD_SOFT_RESET 0xc00 > > +#define ZYNQMP_DISP_AUD_SOFT_RESET_AUD_SRST BIT(0) > > + > > +#define ZYNQMP_DISP_AV_BUF_NUM_VID_GFX_BUFFERS > 4 > > +#define ZYNQMP_DISP_AV_BUF_NUM_BUFFERS 6 > > + > > +#define ZYNQMP_DISP_NUM_LAYERS 2 > > +#define ZYNQMP_DISP_MAX_NUM_SUB_PLANES 3 > > +/* > > + * 3840x2160 is advertised max resolution, but almost any resolutions > under > > + * 300Mhz pixel rate would work. Thus put 4096 as maximum width and > height. > > + */ > > +#define ZYNQMP_DISP_MAX_WIDTH 4096 > > +#define ZYNQMP_DISP_MAX_HEIGHT 4096 > > +/* 44 bit addressing. This is acutally DPDMA limitation */ > > +#define ZYNQMP_DISP_MAX_DMA_BIT 44 > > + > > +/** > > + * enum zynqmp_disp_layer_type - Layer type (can be used for hw ID) > > + * @ZYNQMP_DISP_LAYER_VID: Video layer > > + * @ZYNQMP_DISP_LAYER_GFX: Graphics layer > > + */ > > +enum zynqmp_disp_layer_type { > > + ZYNQMP_DISP_LAYER_VID, > > + ZYNQMP_DISP_LAYER_GFX > > +}; > > + > > +/** > > + * enum zynqmp_disp_layer_mode - Layer mode > > + * @ZYNQMP_DISP_LAYER_NONLIVE: non-live (memory) mode > > + * @ZYNQMP_DISP_LAYER_LIVE: live (stream) mode > > + */ > > +enum zynqmp_disp_layer_mode { > > + ZYNQMP_DISP_LAYER_NONLIVE, > > + ZYNQMP_DISP_LAYER_LIVE > > +}; > > + > > +/** > > + * struct zynqmp_disp_layer_dma - struct for DMA engine > > + * @chan: DMA channel > > + * @is_active: flag if the DMA is active > > + * @xt: Interleaved desc config container > > + * @sgl: Data chunk for dma_interleaved_template > > + */ > > +struct zynqmp_disp_layer_dma { > > + struct dma_chan *chan; > > + bool is_active; > > + struct dma_interleaved_template xt; > > + struct data_chunk sgl[1]; > > +}; > > + > > +/** > > + * struct zynqmp_disp_layer - Display subsystem layer > > + * @plane: DRM plane > > + * @of_node: device node > > + * @dma: struct for DMA engine > > + * @num_chan: Number of DMA channel > > + * @id: Layer ID > > + * @offset: Layer offset in the register space > > + * @enabled: flag if enabled > > + * @fmt: Current format descriptor > > + * @drm_fmts: Array of supported DRM formats > > + * @num_fmts: Number of supported DRM formats > > + * @bus_fmts: Array of supported bus formats > > + * @num_bus_fmts: Number of supported bus formats > > + * @w: Width > > + * @h: Height > > + * @mode: the operation mode > > + * @other: other layer > > + * @disp: back pointer to struct zynqmp_disp > > + */ > > +struct zynqmp_disp_layer { > > + struct drm_plane plane; > > + struct device_node *of_node; > > + struct zynqmp_disp_layer_dma > dma[ZYNQMP_DISP_MAX_NUM_SUB_PLANES]; > > + unsigned int num_chan; > > + enum zynqmp_disp_layer_type id; > > + u32 offset; > > + u8 enabled; > > + const struct zynqmp_disp_fmt *fmt; > > + u32 *drm_fmts; > > + unsigned int num_fmts; > > + u32 *bus_fmts; > > + unsigned int num_bus_fmts; > > + u32 w; > > + u32 h; > > + enum zynqmp_disp_layer_mode mode; > > + struct zynqmp_disp_layer *other; > > + struct zynqmp_disp *disp; > > +}; > > + > > +/** > > + * struct zynqmp_disp_blend - Blender > > + * @base: Base address offset > > + */ > > +struct zynqmp_disp_blend { > > + void __iomem *base; > > +}; > > + > > +/** > > + * struct zynqmp_disp_av_buf - AV buffer manager > > + * @base: Base address offset > > + */ > > +struct zynqmp_disp_av_buf { > > + void __iomem *base; > > +}; > > + > > +/** > > + * struct zynqmp_disp_aud - Audio > > + * @base: Base address offset > > + */ > > +struct zynqmp_disp_aud { > > + void __iomem *base; > > +}; > > + > > +/** > > + * struct zynqmp_disp - Display subsystem > > + * @xlnx_crtc: Xilinx DRM crtc > > + * @dev: device structure > > + * @dpsub: Display subsystem > > + * @drm: DRM core > > + * @enabled: flag if enabled > > + * @blend: Blender block > > + * @av_buf: AV buffer manager block > > + * @aud:Audio block > > + * @layers: layers > > + * @g_alpha_prop: global alpha property > > + * @alpha: current global alpha value > > + * @g_alpha_en_prop: the global alpha enable property > > + * @alpha_en: flag if the global alpha is enabled > > + * @color_prop: output color format property > > + * @color: current output color value > > + * @bg_c0_prop: 1st component of background color property > > + * @bg_c0: current value of 1st background color component > > + * @bg_c1_prop: 2nd component of background color property > > + * @bg_c1: current value of 2nd background color component > > + * @bg_c2_prop: 3rd component of background color property > > + * @bg_c2: current value of 3rd background color component > > + * @tpg_prop: Test Pattern Generation mode property > > + * @tpg_on: current TPG mode state > > + * @event: pending vblank event request > > + * @_ps_pclk: Pixel clock from PS > > + * @_pl_pclk: Pixel clock from PL > > + * @pclk: Pixel clock > > + * @pclk_en: Flag if the pixel clock is enabled > > + * @_ps_audclk: Audio clock from PS > > + * @_pl_audclk: Audio clock from PL > > + * @audclk: Audio clock > > + * @audclk_en: Flag if the audio clock is enabled > > + * @aclk: APB clock > > + * @aclk_en: Flag if the APB clock is enabled > > + */ > > +struct zynqmp_disp { > > + struct xlnx_crtc xlnx_crtc; > > + struct device *dev; > > + struct zynqmp_dpsub *dpsub; > > + struct drm_device *drm; > > + bool enabled; > > + struct zynqmp_disp_blend blend; > > + struct zynqmp_disp_av_buf av_buf; > > + struct zynqmp_disp_aud aud; > > + struct zynqmp_disp_layer layers[ZYNQMP_DISP_NUM_LAYERS]; > > + struct drm_property *g_alpha_prop; > > + u32 alpha; > > + struct drm_property *g_alpha_en_prop; > > + bool alpha_en; > > + struct drm_property *color_prop; > > + unsigned int color; > > + struct drm_property *bg_c0_prop; > > + u32 bg_c0; > > + struct drm_property *bg_c1_prop; > > + u32 bg_c1; > > + struct drm_property *bg_c2_prop; > > + u32 bg_c2; > > + struct drm_property *tpg_prop; > > + bool tpg_on; > > + struct drm_pending_vblank_event *event; > > + /* Don't operate directly on _ps_ */ > > + struct clk *_ps_pclk; > > + struct clk *_pl_pclk; > > + struct clk *pclk; > > + bool pclk_en; > > + struct clk *_ps_audclk; > > + struct clk *_pl_audclk; > > + struct clk *audclk; > > + bool audclk_en; > > + struct clk *aclk; > > + bool aclk_en; > > +}; > > + > > +/** > > + * struct zynqmp_disp_fmt - Display subsystem format mapping > > + * @drm_fmt: drm format > > + * @disp_fmt: Display subsystem format > > + * @bus_fmt: Bus formats (live formats) > > + * @rgb: flag for RGB formats > > + * @swap: flag to swap r & b for rgb formats, and u & v for yuv formats > > + * @chroma_sub: flag for chroma subsampled formats > > + * @sf: scaling factors for upto 3 color components > > + */ > > +struct zynqmp_disp_fmt { > > + u32 drm_fmt; > > + u32 disp_fmt; > > + u32 bus_fmt; > > + bool rgb; > > + bool swap; > > + bool chroma_sub; > > + u32 sf[3]; > > +}; > > + > > +static void zynqmp_disp_write(void __iomem *base, int offset, u32 val) > > +{ > > + writel(val, base + offset); > > +} > > + > > +static u32 zynqmp_disp_read(void __iomem *base, int offset) > > +{ > > + return readl(base + offset); > > +} > > + > > +static void zynqmp_disp_clr(void __iomem *base, int offset, u32 clr) > > +{ > > + zynqmp_disp_write(base, offset, zynqmp_disp_read(base, offset) & > ~clr); > > +} > > + > > +static void zynqmp_disp_set(void __iomem *base, int offset, u32 set) > > +{ > > + zynqmp_disp_write(base, offset, zynqmp_disp_read(base, offset) | > set); > > +} > > + > > +/* > > + * Clock functions > > + */ > > + > > +/** > > + * zynqmp_disp_clk_enable - Enable the clock if needed > > + * @clk: clk device > > + * @flag: flag if the clock is enabled > > + * > > + * Enable the clock only if it's not enabled @flag. > > + * > > + * Return: value from clk_prepare_enable(). > > + */ > > +static int zynqmp_disp_clk_enable(struct clk *clk, bool *flag) > > +{ > > + int ret = 0; > > + > > + if (!*flag) { > > + ret = clk_prepare_enable(clk); > > + if (!ret) > > + *flag = true; > > + } > > + > > + return ret; > > +} > > + > > +/** > > + * zynqmp_disp_clk_enable - Enable the clock if needed > > + * @clk: clk device > > + * @flag: flag if the clock is enabled > > + * > > + * Disable the clock only if it's enabled @flag. > > + */ > > +static void zynqmp_disp_clk_disable(struct clk *clk, bool *flag) > > +{ > > + if (*flag) { > > + clk_disable_unprepare(clk); > > + *flag = false; > > + } > > +} > > + > > +/** > > + * zynqmp_disp_clk_enable - Enable and disable the clock > > + * @clk: clk device > > + * @flag: flag if the clock is enabled > > + * > > + * This is to ensure the clock is disabled. The initial hardware state is > > + * unknown, and this makes sure that the clock is disabled. > > + * > > + * Return: value from clk_prepare_enable(). > > + */ > > +static int zynqmp_disp_clk_enable_disable(struct clk *clk, bool *flag) > > +{ > > + int ret = 0; > > + > > + if (!*flag) { > > + ret = clk_prepare_enable(clk); > > + clk_disable_unprepare(clk); > > + } > > + > > + return ret; > > +} > > + > > +/* > > + * Blender functions > > + */ > > + > > +/** > > + * zynqmp_disp_blend_set_output_fmt - Set the output format of the > blend > > + * @blend: blend object > > + * @fmt: output format > > + * > > + * Set the output format to @fmt. > > + */ > > +static void > > +zynqmp_disp_blend_set_output_fmt(struct zynqmp_disp_blend *blend, > u32 fmt) > > +{ > > + u16 reset_coeffs[] = { 0x1000, 0x0, 0x0, > > + 0x0, 0x1000, 0x0, > > + 0x0, 0x0, 0x1000 }; > > + u32 reset_offsets[] = { 0x0, 0x0, 0x0 }; > > + u16 sdtv_coeffs[] = { 0x4c9, 0x864, 0x1d3, > > + 0x7d4d, 0x7ab3, 0x800, > > + 0x800, 0x794d, 0x7eb3 }; > > + u32 full_range_offsets[] = { 0x0, 0x8000000, 0x8000000 }; > > + u16 *coeffs; > > + u32 *offsets; > > + u32 offset, i; > > + > > + zynqmp_disp_write(blend->base, > ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT, fmt); > > + if (fmt == ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_RGB) { > > + coeffs = reset_coeffs; > > + offsets = reset_offsets; > > + } else { > > + /* Hardcode Full-range SDTV values. Can be runtime config > */ > > + coeffs = sdtv_coeffs; > > + offsets = full_range_offsets; > > + } > > + > > + offset = ZYNQMP_DISP_V_BLEND_RGB2YCBCR_COEFF0; > > + for (i = 0; i < ZYNQMP_DISP_V_BLEND_NUM_COEFF; i++) > > + zynqmp_disp_write(blend->base, offset + i * 4, coeffs[i]); > > + > > + offset = ZYNQMP_DISP_V_BLEND_LUMA_OUTCSC_OFFSET; > > + for (i = 0; i < ZYNQMP_DISP_V_BLEND_NUM_OFFSET; i++) > > + zynqmp_disp_write(blend->base, offset + i * 4, offsets[i]); > > +} > > + > > +/** > > + * zynqmp_disp_blend_layer_enable - Enable a layer > > + * @blend: blend object > > + * @layer: layer to enable > > + * > > + * Enable a layer @layer. > > + */ > > +static void zynqmp_disp_blend_layer_enable(struct > zynqmp_disp_blend *blend, > > + struct zynqmp_disp_layer *layer) > > +{ > > + u32 reg, offset, i, s0, s1; > > + u16 sdtv_coeffs[] = { 0x1000, 0x166f, 0x0, > > + 0x1000, 0x7483, 0x7a7f, > > + 0x1000, 0x0, 0x1c5a }; > > + u16 swap_coeffs[] = { 0x1000, 0x0, 0x0, > > + 0x0, 0x1000, 0x0, > > + 0x0, 0x0, 0x1000 }; > > + u16 *coeffs; > > + u32 offsets[] = { 0x0, 0x1800, 0x1800 }; > > + > > + reg = layer->fmt->rgb ? > ZYNQMP_DISP_V_BLEND_LAYER_CONTROL_RGB : 0; > > + reg |= layer->fmt->chroma_sub ? > > + ZYNQMP_DISP_V_BLEND_LAYER_CONTROL_EN_US : 0; > > + > > + zynqmp_disp_write(blend->base, > > + ZYNQMP_DISP_V_BLEND_LAYER_CONTROL + layer- > >offset, > > + reg); > > + > > + if (layer->id == ZYNQMP_DISP_LAYER_VID) > > + offset = ZYNQMP_DISP_V_BLEND_IN1CSC_COEFF0; > > + else > > + offset = ZYNQMP_DISP_V_BLEND_IN2CSC_COEFF0; > > + > > + if (!layer->fmt->rgb) { > > + coeffs = sdtv_coeffs; > > + s0 = 1; > > + s1 = 2; > > + } else { > > + coeffs = swap_coeffs; > > + s0 = 0; > > + s1 = 2; > > + > > + /* No offset for RGB formats */ > > + for (i = 0; i < ZYNQMP_DISP_V_BLEND_NUM_OFFSET; i++) > > + offsets[i] = 0; > > + } > > + > > + if (layer->fmt->swap) { > > + for (i = 0; i < 3; i++) { > > + coeffs[i * 3 + s0] ^= coeffs[i * 3 + s1]; > > + coeffs[i * 3 + s1] ^= coeffs[i * 3 + s0]; > > + coeffs[i * 3 + s0] ^= coeffs[i * 3 + s1]; > > + } > > + } > > + > > + /* Program coefficients. Can be runtime configurable */ > > + for (i = 0; i < ZYNQMP_DISP_V_BLEND_NUM_COEFF; i++) > > + zynqmp_disp_write(blend->base, offset + i * 4, coeffs[i]); > > + > > + if (layer->id == ZYNQMP_DISP_LAYER_VID) > > + offset = ZYNQMP_DISP_V_BLEND_LUMA_IN1CSC_OFFSET; > > + else > > + offset = ZYNQMP_DISP_V_BLEND_LUMA_IN2CSC_OFFSET; > > + > > + /* Program offsets. Can be runtime configurable */ > > + for (i = 0; i < ZYNQMP_DISP_V_BLEND_NUM_OFFSET; i++) > > + zynqmp_disp_write(blend->base, offset + i * 4, offsets[i]); > > +} > > + > > +/** > > + * zynqmp_disp_blend_layer_disable - Disable a layer > > + * @blend: blend object > > + * @layer: layer to disable > > + * > > + * Disable a layer @layer. > > + */ > > +static void zynqmp_disp_blend_layer_disable(struct > zynqmp_disp_blend *blend, > > + struct zynqmp_disp_layer *layer) > > +{ > > + u32 offset; > > + unsigned int i; > > + > > + zynqmp_disp_write(blend->base, > > + ZYNQMP_DISP_V_BLEND_LAYER_CONTROL + layer- > >offset, 0); > > + > > + if (layer->id == ZYNQMP_DISP_LAYER_VID) > > + offset = ZYNQMP_DISP_V_BLEND_IN1CSC_COEFF0; > > + else > > + offset = ZYNQMP_DISP_V_BLEND_IN2CSC_COEFF0; > > + for (i = 0; i < ZYNQMP_DISP_V_BLEND_NUM_COEFF; i++) > > + zynqmp_disp_write(blend->base, offset + i * 4, 0); > > + > > + if (layer->id == ZYNQMP_DISP_LAYER_VID) > > + offset = ZYNQMP_DISP_V_BLEND_LUMA_IN1CSC_OFFSET; > > + else > > + offset = ZYNQMP_DISP_V_BLEND_LUMA_IN2CSC_OFFSET; > > + > > + for (i = 0; i < ZYNQMP_DISP_V_BLEND_NUM_OFFSET; i++) > > + zynqmp_disp_write(blend->base, offset + i * 4, 0); > > +} > > + > > +/** > > + * zynqmp_disp_blend_set_bg_color - Set the background color > > + * @blend: blend object > > + * @c0: color component 0 > > + * @c1: color component 1 > > + * @c2: color component 2 > > + * > > + * Set the background color. > > + */ > > +static void zynqmp_disp_blend_set_bg_color(struct > zynqmp_disp_blend *blend, > > + u32 c0, u32 c1, u32 c2) > > +{ > > + zynqmp_disp_write(blend->base, > ZYNQMP_DISP_V_BLEND_BG_CLR_0, c0); > > + zynqmp_disp_write(blend->base, > ZYNQMP_DISP_V_BLEND_BG_CLR_1, c1); > > + zynqmp_disp_write(blend->base, > ZYNQMP_DISP_V_BLEND_BG_CLR_2, c2); > > +} > > + > > +/** > > + * zynqmp_disp_blend_set_alpha - Set the alpha for blending > > + * @blend: blend object > > + * @alpha: alpha value to be used > > + * > > + * Set the alpha for blending. > > + */ > > +static void > > +zynqmp_disp_blend_set_alpha(struct zynqmp_disp_blend *blend, u32 > alpha) > > +{ > > + u32 reg; > > + > > + reg = zynqmp_disp_read(blend->base, > > + ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA); > > + reg &= ~ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA_MASK; > > + reg |= alpha << 1; > > + zynqmp_disp_write(blend->base, > ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA, > > + reg); > > +} > > + > > +/** > > + * zynqmp_disp_blend_enable_alpha - Enable/disable the global alpha > > + * @blend: blend object > > + * @enable: flag to enable or disable alpha blending > > + * > > + * Enable/disable the global alpha blending based on @enable. > > + */ > > +static void > > +zynqmp_disp_blend_enable_alpha(struct zynqmp_disp_blend *blend, > bool enable) > > +{ > > + if (enable) > > + zynqmp_disp_set(blend->base, > > + > ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA, BIT(0)); > > + else > > + zynqmp_disp_clr(blend->base, > > + > ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA, BIT(0)); > > +} > > + > > +/* List of blend output formats */ > > +/* The id / order should be aligned with zynqmp_disp_color_enum */ > > +static const struct zynqmp_disp_fmt blend_output_fmts[] = { > > + { > > + .disp_fmt = > ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_RGB, > > + }, { > > + .disp_fmt = > ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_YCBCR444, > > + }, { > > + .disp_fmt = > ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_YCBCR422, > > + }, { > > + .disp_fmt = > ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_YONLY, > > + } > > +}; > > + > > +/* > > + * AV buffer manager functions > > + */ > > + > > +/* List of video layer formats */ > > +#define ZYNQMP_DISP_AV_BUF_VID_FMT_YUYV 2 > > +static const struct zynqmp_disp_fmt av_buf_vid_fmts[] = { > > + { > > + .drm_fmt = DRM_FORMAT_VYUY, > > + .disp_fmt = > ZYNQMP_DISP_AV_BUF_FMT_NL_VID_VYUY, > > + .rgb = false, > > + .swap = true, > > + .chroma_sub = true, > > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + }, { > > + .drm_fmt = DRM_FORMAT_UYVY, > > + .disp_fmt = > ZYNQMP_DISP_AV_BUF_FMT_NL_VID_VYUY, > > + .rgb = false, > > + .swap = false, > > + .chroma_sub = true, > > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + }, { > > + .drm_fmt = DRM_FORMAT_YUYV, > > + .disp_fmt = > ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YUYV, > > + .rgb = false, > > + .swap = false, > > + .chroma_sub = true, > > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + }, { > > + .drm_fmt = DRM_FORMAT_YVYU, > > + .disp_fmt = > ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YUYV, > > + .rgb = false, > > + .swap = true, > > + .chroma_sub = true, > > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + }, { > > + .drm_fmt = DRM_FORMAT_YUV422, > > + .disp_fmt = > ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16, > > + .rgb = false, > > + .swap = false, > > + .chroma_sub = true, > > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + }, { > > + .drm_fmt = DRM_FORMAT_YVU422, > > + .disp_fmt = > ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16, > > + .rgb = false, > > + .swap = true, > > + .chroma_sub = true, > > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + }, { > > + .drm_fmt = DRM_FORMAT_YUV444, > > + .disp_fmt = > ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV24, > > + .rgb = false, > > + .swap = false, > > + .chroma_sub = false, > > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + }, { > > + .drm_fmt = DRM_FORMAT_YVU444, > > + .disp_fmt = > ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV24, > > + .rgb = false, > > + .swap = true, > > + .chroma_sub = false, > > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + }, { > > + .drm_fmt = DRM_FORMAT_NV16, > > + .disp_fmt = > ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI, > > + .rgb = false, > > + .swap = false, > > + .chroma_sub = true, > > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + }, { > > + .drm_fmt = DRM_FORMAT_NV61, > > + .disp_fmt = > ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI, > > + .rgb = false, > > + .swap = true, > > + .chroma_sub = true, > > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + }, { > > + .drm_fmt = DRM_FORMAT_BGR888, > > + .disp_fmt = > ZYNQMP_DISP_AV_BUF_FMT_NL_VID_RGB888, > > + .rgb = true, > > + .swap = false, > > + .chroma_sub = false, > > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + }, { > > + .drm_fmt = DRM_FORMAT_RGB888, > > + .disp_fmt = > ZYNQMP_DISP_AV_BUF_FMT_NL_VID_RGB888, > > + .rgb = true, > > + .swap = true, > > + .chroma_sub = false, > > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + }, { > > + .drm_fmt = DRM_FORMAT_XBGR8888, > > + .disp_fmt = > ZYNQMP_DISP_AV_BUF_FMT_NL_VID_RGBA8880, > > + .rgb = true, > > + .swap = false, > > + .chroma_sub = false, > > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + }, { > > + .drm_fmt = DRM_FORMAT_XRGB8888, > > + .disp_fmt = > ZYNQMP_DISP_AV_BUF_FMT_NL_VID_RGBA8880, > > + .rgb = true, > > + .swap = true, > > + .chroma_sub = false, > > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + }, { > > + .drm_fmt = DRM_FORMAT_XBGR2101010, > > + .disp_fmt = > ZYNQMP_DISP_AV_BUF_FMT_NL_VID_RGB888_10, > > + .rgb = true, > > + .swap = false, > > + .chroma_sub = false, > > + .sf[0] = ZYNQMP_DISP_AV_BUF_10BIT_SF, > > + .sf[1] = ZYNQMP_DISP_AV_BUF_10BIT_SF, > > + .sf[2] = ZYNQMP_DISP_AV_BUF_10BIT_SF, > > + }, { > > + .drm_fmt = DRM_FORMAT_XRGB2101010, > > + .disp_fmt = > ZYNQMP_DISP_AV_BUF_FMT_NL_VID_RGB888_10, > > + .rgb = true, > > + .swap = true, > > + .chroma_sub = false, > > + .sf[0] = ZYNQMP_DISP_AV_BUF_10BIT_SF, > > + .sf[1] = ZYNQMP_DISP_AV_BUF_10BIT_SF, > > + .sf[2] = ZYNQMP_DISP_AV_BUF_10BIT_SF, > > + }, { > > + .drm_fmt = DRM_FORMAT_YUV420, > > + .disp_fmt = > ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16_420, > > + .rgb = false, > > + .swap = false, > > + .chroma_sub = true, > > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + }, { > > + .drm_fmt = DRM_FORMAT_YVU420, > > + .disp_fmt = > ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16_420, > > + .rgb = false, > > + .swap = true, > > + .chroma_sub = true, > > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + }, { > > + .drm_fmt = DRM_FORMAT_NV12, > > + .disp_fmt = > ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI_420, > > + .rgb = false, > > + .swap = false, > > + .chroma_sub = true, > > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + }, { > > + .drm_fmt = DRM_FORMAT_NV21, > > + .disp_fmt = > ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI_420, > > + .rgb = false, > > + .swap = true, > > + .chroma_sub = true, > > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + } > > +}; > > + > > +/* List of graphics layer formats */ > > +#define ZYNQMP_DISP_AV_BUF_GFX_FMT_RGB565 10 > > +static const struct zynqmp_disp_fmt av_buf_gfx_fmts[] = { > > + { > > + .drm_fmt = DRM_FORMAT_ABGR8888, > > + .disp_fmt = > ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGBA8888, > > + .rgb = true, > > + .swap = false, > > + .chroma_sub = false, > > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + }, { > > + .drm_fmt = DRM_FORMAT_ARGB8888, > > + .disp_fmt = > ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGBA8888, > > + .rgb = true, > > + .swap = true, > > + .chroma_sub = false, > > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + }, { > > + .drm_fmt = DRM_FORMAT_RGBA8888, > > + .disp_fmt = > ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_ABGR8888, > > + .rgb = true, > > + .swap = false, > > + .chroma_sub = false, > > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + }, { > > + .drm_fmt = DRM_FORMAT_BGRA8888, > > + .disp_fmt = > ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_ABGR8888, > > + .rgb = true, > > + .swap = true, > > + .chroma_sub = false, > > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + }, { > > + .drm_fmt = DRM_FORMAT_BGR888, > > + .disp_fmt = > ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGB888, > > + .rgb = true, > > + .swap = false, > > + .chroma_sub = false, > > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + }, { > > + .drm_fmt = DRM_FORMAT_RGB888, > > + .disp_fmt = > ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_BGR888, > > + .rgb = true, > > + .swap = false, > > + .chroma_sub = false, > > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + }, { > > + .drm_fmt = DRM_FORMAT_RGBA5551, > > + .disp_fmt = > ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGBA5551, > > + .rgb = true, > > + .swap = false, > > + .chroma_sub = false, > > + .sf[0] = ZYNQMP_DISP_AV_BUF_5BIT_SF, > > + .sf[1] = ZYNQMP_DISP_AV_BUF_5BIT_SF, > > + .sf[2] = ZYNQMP_DISP_AV_BUF_5BIT_SF, > > + }, { > > + .drm_fmt = DRM_FORMAT_BGRA5551, > > + .disp_fmt = > ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGBA5551, > > + .rgb = true, > > + .swap = true, > > + .chroma_sub = false, > > + .sf[0] = ZYNQMP_DISP_AV_BUF_5BIT_SF, > > + .sf[1] = ZYNQMP_DISP_AV_BUF_5BIT_SF, > > + .sf[2] = ZYNQMP_DISP_AV_BUF_5BIT_SF, > > + }, { > > + .drm_fmt = DRM_FORMAT_RGBA4444, > > + .disp_fmt = > ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGBA4444, > > + .rgb = true, > > + .swap = false, > > + .chroma_sub = false, > > + .sf[0] = ZYNQMP_DISP_AV_BUF_4BIT_SF, > > + .sf[1] = ZYNQMP_DISP_AV_BUF_4BIT_SF, > > + .sf[2] = ZYNQMP_DISP_AV_BUF_4BIT_SF, > > + }, { > > + .drm_fmt = DRM_FORMAT_BGRA4444, > > + .disp_fmt = > ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGBA4444, > > + .rgb = true, > > + .swap = true, > > + .chroma_sub = false, > > + .sf[0] = ZYNQMP_DISP_AV_BUF_4BIT_SF, > > + .sf[1] = ZYNQMP_DISP_AV_BUF_4BIT_SF, > > + .sf[2] = ZYNQMP_DISP_AV_BUF_4BIT_SF, > > + }, { > > + .drm_fmt = DRM_FORMAT_RGB565, > > + .disp_fmt = > ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGB565, > > + .rgb = true, > > + .swap = false, > > + .chroma_sub = false, > > + .sf[0] = ZYNQMP_DISP_AV_BUF_5BIT_SF, > > + .sf[1] = ZYNQMP_DISP_AV_BUF_6BIT_SF, > > + .sf[2] = ZYNQMP_DISP_AV_BUF_5BIT_SF, > > + }, { > > + .drm_fmt = DRM_FORMAT_BGR565, > > + .disp_fmt = > ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGB565, > > + .rgb = true, > > + .swap = true, > > + .chroma_sub = false, > > + .sf[0] = ZYNQMP_DISP_AV_BUF_5BIT_SF, > > + .sf[1] = ZYNQMP_DISP_AV_BUF_6BIT_SF, > > + .sf[2] = ZYNQMP_DISP_AV_BUF_5BIT_SF, > > + } > > +}; > > + > > +/* List of live formats */ > > +/* Format can be combination of color, bpc, and cb-cr order. > > + * - Color: RGB / YUV444 / YUV422 / Y only > > + * - BPC: 6, 8, 10, 12 > > + * - Swap: Cb and Cr swap > > + * which can be 32 bus formats. Only list the subset of those for now. > > + */ > > +static const struct zynqmp_disp_fmt av_buf_live_fmts[] = { > > + { > > + .bus_fmt = MEDIA_BUS_FMT_RGB666_1X18, > > + .disp_fmt = > ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_6 || > > + > ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_RGB, > > + .rgb = true, > > + .swap = false, > > + .chroma_sub = false, > > + .sf[0] = ZYNQMP_DISP_AV_BUF_6BIT_SF, > > + .sf[1] = ZYNQMP_DISP_AV_BUF_6BIT_SF, > > + .sf[2] = ZYNQMP_DISP_AV_BUF_6BIT_SF, > > + }, { > > + .bus_fmt = MEDIA_BUS_FMT_RBG888_1X24, > > + .disp_fmt = > ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_8 || > > + > ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_RGB, > > + .rgb = true, > > + .swap = false, > > + .chroma_sub = false, > > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + }, { > > + .bus_fmt = MEDIA_BUS_FMT_UYVY8_1X16, > > + .disp_fmt = > ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_8 || > > + > ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_YUV422, > > + .rgb = false, > > + .swap = false, > > + .chroma_sub = true, > > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + }, { > > + .bus_fmt = MEDIA_BUS_FMT_VUY8_1X24, > > + .disp_fmt = > ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_8 || > > + > ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_YUV444, > > + .rgb = false, > > + .swap = false, > > + .chroma_sub = false, > > + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, > > + }, { > > + .bus_fmt = MEDIA_BUS_FMT_UYVY10_1X20, > > + .disp_fmt = > ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_10 || > > + > ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_YUV422, > > + .rgb = false, > > + .swap = false, > > + .chroma_sub = true, > > + .sf[0] = ZYNQMP_DISP_AV_BUF_10BIT_SF, > > + .sf[1] = ZYNQMP_DISP_AV_BUF_10BIT_SF, > > + .sf[2] = ZYNQMP_DISP_AV_BUF_10BIT_SF, > > + } > > +}; > > + > > +/** > > + * zynqmp_disp_av_buf_set_fmt - Set the input formats > > + * @av_buf: av buffer manager > > + * @fmt: formats > > + * > > + * Set the av buffer manager format to @fmt. @fmt should have valid > values > > + * for both video and graphics layer. > > + */ > > +static void > > +zynqmp_disp_av_buf_set_fmt(struct zynqmp_disp_av_buf *av_buf, > u32 fmt) > > +{ > > + zynqmp_disp_write(av_buf->base, ZYNQMP_DISP_AV_BUF_FMT, > fmt); > > +} > > + > > +/** > > + * zynqmp_disp_av_buf_get_fmt - Get the input formats > > + * @av_buf: av buffer manager > > + * > > + * Get the input formats (which include video and graphics) of > > + * av buffer manager. > > + * > > + * Return: value of ZYNQMP_DISP_AV_BUF_FMT register. > > + */ > > +static u32 > > +zynqmp_disp_av_buf_get_fmt(struct zynqmp_disp_av_buf *av_buf) > > +{ > > + return zynqmp_disp_read(av_buf->base, > ZYNQMP_DISP_AV_BUF_FMT); > > +} > > + > > +/** > > + * zynqmp_disp_av_buf_set_vid_clock_src - Set the video clock source > > + * @av_buf: av buffer manager > > + * @from_ps: flag if the video clock is from ps > > + * > > + * Set the video clock source based on @from_ps. It can come from > either PS or > > + * PL. > > + */ > > +static void > > +zynqmp_disp_av_buf_set_vid_clock_src(struct zynqmp_disp_av_buf > *av_buf, > > + bool from_ps) > > +{ > > + u32 reg = zynqmp_disp_read(av_buf->base, > ZYNQMP_DISP_AV_BUF_CLK_SRC); > > + > > + if (from_ps) > > + reg |= ZYNQMP_DISP_AV_BUF_CLK_SRC_VID_FROM_PS; > > + else > > + reg &= ~ZYNQMP_DISP_AV_BUF_CLK_SRC_VID_FROM_PS; > > + zynqmp_disp_write(av_buf->base, > ZYNQMP_DISP_AV_BUF_CLK_SRC, reg); > > +} > > + > > +/** > > + * zynqmp_disp_av_buf_set_vid_timing_src - Set the video timing > source > > + * @av_buf: av buffer manager > > + * @internal: flag if the video timing is generated internally > > + * > > + * Set the video timing source based on @internal. It can come externally > or > > + * be generated internally. > > + */ > > +static void > > +zynqmp_disp_av_buf_set_vid_timing_src(struct zynqmp_disp_av_buf > *av_buf, > > + bool internal) > > +{ > > + u32 reg = zynqmp_disp_read(av_buf->base, > ZYNQMP_DISP_AV_BUF_CLK_SRC); > > + > > + if (internal) > > + reg |= > ZYNQMP_DISP_AV_BUF_CLK_SRC_VID_INTERNAL_TIMING; > > + else > > + reg &= > ~ZYNQMP_DISP_AV_BUF_CLK_SRC_VID_INTERNAL_TIMING; > > + zynqmp_disp_write(av_buf->base, > ZYNQMP_DISP_AV_BUF_CLK_SRC, reg); > > +} > > + > > +/** > > + * zynqmp_disp_av_buf_set_aud_clock_src - Set the audio clock source > > + * @av_buf: av buffer manager > > + * @from_ps: flag if the video clock is from ps > > + * > > + * Set the audio clock source based on @from_ps. It can come from > either PS or > > + * PL. > > + */ > > +static void > > +zynqmp_disp_av_buf_set_aud_clock_src(struct zynqmp_disp_av_buf > *av_buf, > > + bool from_ps) > > +{ > > + u32 reg = zynqmp_disp_read(av_buf->base, > ZYNQMP_DISP_AV_BUF_CLK_SRC); > > + > > + if (from_ps) > > + reg |= ZYNQMP_DISP_AV_BUF_CLK_SRC_AUD_FROM_PS; > > + else > > + reg &= ~ZYNQMP_DISP_AV_BUF_CLK_SRC_AUD_FROM_PS; > > + zynqmp_disp_write(av_buf->base, > ZYNQMP_DISP_AV_BUF_CLK_SRC, reg); > > +} > > + > > +/** > > + * zynqmp_disp_av_buf_enable_buf - Enable buffers > > + * @av_buf: av buffer manager > > + * > > + * Enable all (video and audio) buffers. > > + */ > > +static void > > +zynqmp_disp_av_buf_enable_buf(struct zynqmp_disp_av_buf *av_buf) > > +{ > > + u32 reg, i; > > + > > + reg = ZYNQMP_DISP_AV_BUF_CHBUF_EN; > > + reg |= ZYNQMP_DISP_AV_BUF_CHBUF_BURST_LEN_MAX << > > + ZYNQMP_DISP_AV_BUF_CHBUF_BURST_LEN_SHIFT; > > + > > + for (i = 0; i < ZYNQMP_DISP_AV_BUF_NUM_VID_GFX_BUFFERS; i++) > > + zynqmp_disp_write(av_buf->base, > > + ZYNQMP_DISP_AV_BUF_CHBUF + i * 4, > reg); > > + > > + reg = ZYNQMP_DISP_AV_BUF_CHBUF_EN; > > + reg |= ZYNQMP_DISP_AV_BUF_CHBUF_BURST_LEN_AUD_MAX << > > + ZYNQMP_DISP_AV_BUF_CHBUF_BURST_LEN_SHIFT; > > + > > + for (; i < ZYNQMP_DISP_AV_BUF_NUM_BUFFERS; i++) > > + zynqmp_disp_write(av_buf->base, > > + ZYNQMP_DISP_AV_BUF_CHBUF + i * 4, > reg); > > +} > > + > > +/** > > + * zynqmp_disp_av_buf_disable_buf - Disable buffers > > + * @av_buf: av buffer manager > > + * > > + * Disable all (video and audio) buffers. > > + */ > > +static void > > +zynqmp_disp_av_buf_disable_buf(struct zynqmp_disp_av_buf *av_buf) > > +{ > > + u32 reg, i; > > + > > + reg = ZYNQMP_DISP_AV_BUF_CHBUF_FLUSH & > ~ZYNQMP_DISP_AV_BUF_CHBUF_EN; > > + for (i = 0; i < ZYNQMP_DISP_AV_BUF_NUM_BUFFERS; i++) > > + zynqmp_disp_write(av_buf->base, > > + ZYNQMP_DISP_AV_BUF_CHBUF + i * 4, > reg); > > +} > > + > > +/** > > + * zynqmp_disp_av_buf_enable_aud - Enable audio > > + * @av_buf: av buffer manager > > + * > > + * Enable all audio buffers. > > + */ > > +static void > > +zynqmp_disp_av_buf_enable_aud(struct zynqmp_disp_av_buf *av_buf) > > +{ > > + u32 reg; > > + > > + reg = zynqmp_disp_read(av_buf->base, > ZYNQMP_DISP_AV_BUF_OUTPUT); > > + reg &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_MASK; > > + reg |= ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_MEM; > > + reg |= ZYNQMP_DISP_AV_BUF_OUTPUT_AUD2_EN; > > + zynqmp_disp_write(av_buf->base, > ZYNQMP_DISP_AV_BUF_OUTPUT, reg); > > +} > > + > > +/** > > + * zynqmp_disp_av_buf_enable - Enable the video pipe > > + * @av_buf: av buffer manager > > + * > > + * De-assert the video pipe reset > > + */ > > +static void > > +zynqmp_disp_av_buf_enable(struct zynqmp_disp_av_buf *av_buf) > > +{ > > + zynqmp_disp_write(av_buf->base, > ZYNQMP_DISP_AV_BUF_SRST_REG, 0); > > +} > > + > > +/** > > + * zynqmp_disp_av_buf_disable - Disable the video pipe > > + * @av_buf: av buffer manager > > + * > > + * Assert the video pipe reset > > + */ > > +static void > > +zynqmp_disp_av_buf_disable(struct zynqmp_disp_av_buf *av_buf) > > +{ > > + zynqmp_disp_write(av_buf->base, > ZYNQMP_DISP_AV_BUF_SRST_REG, > > + ZYNQMP_DISP_AV_BUF_SRST_REG_VID_RST); > > +} > > + > > +/** > > + * zynqmp_disp_av_buf_disable_aud - Disable audio > > + * @av_buf: av buffer manager > > + * > > + * Disable all audio buffers. > > + */ > > +static void > > +zynqmp_disp_av_buf_disable_aud(struct zynqmp_disp_av_buf *av_buf) > > +{ > > + u32 reg; > > + > > + reg = zynqmp_disp_read(av_buf->base, > ZYNQMP_DISP_AV_BUF_OUTPUT); > > + reg &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_MASK; > > + reg |= ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_DISABLE; > > + reg &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_AUD2_EN; > > + zynqmp_disp_write(av_buf->base, > ZYNQMP_DISP_AV_BUF_OUTPUT, reg); > > +} > > + > > +/** > > + * zynqmp_disp_av_buf_set_tpg - Set TPG mode > > + * @av_buf: av buffer manager > > + * @tpg_on: if TPG should be on > > + * > > + * Set the TPG mode based on @tpg_on. > > + */ > > +static void zynqmp_disp_av_buf_set_tpg(struct zynqmp_disp_av_buf > *av_buf, > > + bool tpg_on) > > +{ > > + u32 reg; > > + > > + reg = zynqmp_disp_read(av_buf->base, > ZYNQMP_DISP_AV_BUF_OUTPUT); > > + reg &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_MASK; > > + if (tpg_on) > > + reg |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_PATTERN; > > + else > > + reg &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_PATTERN; > > + zynqmp_disp_write(av_buf->base, > ZYNQMP_DISP_AV_BUF_OUTPUT, reg); > > +} > > + > > +/** > > + * zynqmp_disp_av_buf_enable_vid - Enable the video layer buffer > > + * @av_buf: av buffer manager > > + * @layer: layer to enable > > + * @mode: operation mode of layer > > + * > > + * Enable the video/graphics buffer for @layer. > > + */ > > +static void zynqmp_disp_av_buf_enable_vid(struct > zynqmp_disp_av_buf *av_buf, > > + struct zynqmp_disp_layer *layer, > > + enum zynqmp_disp_layer_mode > mode) > > +{ > > + u32 reg; > > + > > + reg = zynqmp_disp_read(av_buf->base, > ZYNQMP_DISP_AV_BUF_OUTPUT); > > + if (layer->id == ZYNQMP_DISP_LAYER_VID) { > > + reg &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_MASK; > > + if (mode == ZYNQMP_DISP_LAYER_NONLIVE) > > + reg |= > ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_MEM; > > + else > > + reg |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_LIVE; > > + } else { > > + reg &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_MASK; > > + reg |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_MEM; > > + if (mode == ZYNQMP_DISP_LAYER_NONLIVE) > > + reg |= > ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_MEM; > > + else > > + reg |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_LIVE; > > + } > > + zynqmp_disp_write(av_buf->base, > ZYNQMP_DISP_AV_BUF_OUTPUT, reg); > > +} > > + > > +/** > > + * zynqmp_disp_av_buf_disable_vid - Disable the video layer buffer > > + * @av_buf: av buffer manager > > + * @layer: layer to disable > > + * > > + * Disable the video/graphics buffer for @layer. > > + */ > > +static void > > +zynqmp_disp_av_buf_disable_vid(struct zynqmp_disp_av_buf *av_buf, > > + struct zynqmp_disp_layer *layer) > > +{ > > + u32 reg; > > + > > + reg = zynqmp_disp_read(av_buf->base, > ZYNQMP_DISP_AV_BUF_OUTPUT); > > + if (layer->id == ZYNQMP_DISP_LAYER_VID) { > > + reg &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_MASK; > > + reg |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_NONE; > > + } else { > > + reg &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_MASK; > > + reg |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_DISABLE; > > + } > > + zynqmp_disp_write(av_buf->base, > ZYNQMP_DISP_AV_BUF_OUTPUT, reg); > > +} > > + > > +/** > > + * zynqmp_disp_av_buf_init_sf - Initialize scaling factors > > + * @av_buf: av buffer manager > > + * @vid_fmt: video format descriptor > > + * @gfx_fmt: graphics format descriptor > > + * > > + * Initialize scaling factors for both video and graphics layers. > > + * If the format descriptor is NULL, the function skips the programming. > > + */ > > +static void zynqmp_disp_av_buf_init_sf(struct zynqmp_disp_av_buf > *av_buf, > > + const struct zynqmp_disp_fmt *vid_fmt, > > + const struct zynqmp_disp_fmt *gfx_fmt) > > +{ > > + unsigned int i; > > + u32 offset; > > + > > + if (gfx_fmt) { > > + offset = ZYNQMP_DISP_AV_BUF_GFX_COMP0_SF; > > + for (i = 0; i < ZYNQMP_DISP_AV_BUF_NUM_SF; i++) > > + zynqmp_disp_write(av_buf->base, offset + i * 4, > > + gfx_fmt->sf[i]); > > + } > > + > > + if (vid_fmt) { > > + offset = ZYNQMP_DISP_AV_BUF_VID_COMP0_SF; > > + for (i = 0; i < ZYNQMP_DISP_AV_BUF_NUM_SF; i++) > > + zynqmp_disp_write(av_buf->base, offset + i * 4, > > + vid_fmt->sf[i]); > > + } > > +} > > + > > +/* > > + * Audio functions > > + */ > > + > > +/** > > + * zynqmp_disp_aud_init - Initialize the audio > > + * @aud: audio > > + * > > + * Initialize the audio with default mixer volume. The de-assertion will > > + * initialize the audio states. > > + */ > > +static void zynqmp_disp_aud_init(struct zynqmp_disp_aud *aud) > > +{ > > + /* Clear the audio soft reset register as it's an non-reset flop */ > > + zynqmp_disp_write(aud->base, ZYNQMP_DISP_AUD_SOFT_RESET, > 0); > > + zynqmp_disp_write(aud->base, > ZYNQMP_DISP_AUD_MIXER_VOLUME, > > + ZYNQMP_DISP_AUD_MIXER_VOLUME_NO_SCALE); > > +} > > + > > +/** > > + * zynqmp_disp_aud_deinit - De-initialize the audio > > + * @aud: audio > > + * > > + * Put the audio in reset. > > + */ > > +static void zynqmp_disp_aud_deinit(struct zynqmp_disp_aud *aud) > > +{ > > + zynqmp_disp_set(aud->base, ZYNQMP_DISP_AUD_SOFT_RESET, > > + ZYNQMP_DISP_AUD_SOFT_RESET_AUD_SRST); > > +} > > + > > +/* > > + * ZynqMP Display layer functions > > + */ > > + > > +/** > > + * zynqmp_disp_layer_check_size - Verify width and height for the layer > > + * @disp: Display subsystem > > + * @layer: layer > > + * @width: width > > + * @height: height > > + * > > + * The Display subsystem has the limitation that both layers should have > > + * identical size. This function stores width and height of @layer, and > verifies > > + * if the size (width and height) is valid. > > + * > > + * Return: 0 on success, or -EINVAL if width or/and height is invalid. > > + */ > > +static int zynqmp_disp_layer_check_size(struct zynqmp_disp *disp, > > + struct zynqmp_disp_layer *layer, > > + u32 width, u32 height) > > +{ > > + struct zynqmp_disp_layer *other = layer->other; > > + > > + if (other->enabled && (other->w != width || other->h != height)) { > > + dev_err(disp->dev, "Layer width:height must be %d:%d\n", > > + other->w, other->h); > > + return -EINVAL; > > + } > > + > > + layer->w = width; > > + layer->h = height; > > + > > + return 0; > > +} > > + > > +/** > > + * zynqmp_disp_map_fmt - Find the Display subsystem format for given > drm format > > + * @fmts: format table to look up > > + * @size: size of the table @fmts > > + * @drm_fmt: DRM format to search > > + * > > + * Search a Display subsystem format corresponding to the given DRM > format > > + * @drm_fmt, and return the format descriptor which contains the > Display > > + * subsystem format value. > > + * > > + * Return: a Display subsystem format descriptor on success, or NULL. > > + */ > > +static const struct zynqmp_disp_fmt * > > +zynqmp_disp_map_fmt(const struct zynqmp_disp_fmt fmts[], > > + unsigned int size, uint32_t drm_fmt) > > +{ > > + unsigned int i; > > + > > + for (i = 0; i < size; i++) > > + if (fmts[i].drm_fmt == drm_fmt) > > + return &fmts[i]; > > + > > + return NULL; > > +} > > + > > +/** > > + * zynqmp_disp_set_fmt - Set the format of the layer > > + * @disp: Display subsystem > > + * @layer: layer to set the format > > + * @drm_fmt: DRM format to set > > + * > > + * Set the format of the given layer to @drm_fmt. > > + * > > + * Return: 0 on success. -EINVAL if @drm_fmt is not supported by the > layer. > > + */ > > +static int zynqmp_disp_layer_set_fmt(struct zynqmp_disp *disp, > > + struct zynqmp_disp_layer *layer, > > + uint32_t drm_fmt) > > +{ > > + const struct zynqmp_disp_fmt *fmt; > > + const struct zynqmp_disp_fmt *vid_fmt = NULL, *gfx_fmt = NULL; > > + u32 size, fmts, mask; > > + > > + if (layer->id == ZYNQMP_DISP_LAYER_VID) { > > + size = ARRAY_SIZE(av_buf_vid_fmts); > > + mask = ~ZYNQMP_DISP_AV_BUF_FMT_NL_VID_MASK; > > + fmt = zynqmp_disp_map_fmt(av_buf_vid_fmts, size, > drm_fmt); > > + vid_fmt = fmt; > > + } else { > > + size = ARRAY_SIZE(av_buf_gfx_fmts); > > + mask = ~ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_MASK; > > + fmt = zynqmp_disp_map_fmt(av_buf_gfx_fmts, size, > drm_fmt); > > + gfx_fmt = fmt; > > + } > > + > > + if (!fmt) > > + return -EINVAL; > > + > > + fmts = zynqmp_disp_av_buf_get_fmt(&disp->av_buf); > > + fmts &= mask; > > + fmts |= fmt->disp_fmt; > > + zynqmp_disp_av_buf_set_fmt(&disp->av_buf, fmts); > > + zynqmp_disp_av_buf_init_sf(&disp->av_buf, vid_fmt, gfx_fmt); > > + layer->fmt = fmt; > > + > > + return 0; > > +} > > + > > +/** > > + * zynqmp_disp_set_tpg - Enable or disable TPG > > + * @disp: Display subsystem > > + * @layer: Video layer > > + * @tpg_on: flag if TPG needs to be enabled or disabled > > + * > > + * Enable / disable the TPG mode on the video layer @layer depending > on > > + * @tpg_on. The video layer should be disabled prior to enable request. > > + * > > + * Return: 0 on success. -ENODEV if it's not video layer. -EIO if > > + * the video layer is enabled. > > + */ > > +static int zynqmp_disp_layer_set_tpg(struct zynqmp_disp *disp, > > + struct zynqmp_disp_layer *layer, > > + bool tpg_on) > > +{ > > + if (layer->id != ZYNQMP_DISP_LAYER_VID) { > > + dev_err(disp->dev, > > + "only the video layer has the tpg mode\n"); > > + return -ENODEV; > > + } > > + > > + if (layer->enabled) { > > + dev_err(disp->dev, > > + "the video layer should be disabled for tpg mode\n"); > > + return -EIO; > > + } > > + > > + zynqmp_disp_av_buf_set_tpg(&disp->av_buf, tpg_on); > > + disp->tpg_on = tpg_on; > > + > > + return 0; > > +} > > + > > +/** > > + * zynqmp_disp_get_tpg - Get the TPG mode status > > + * @disp: Display subsystem > > + * @layer: Video layer > > + * > > + * Return if the TPG is enabled or not. > > + * > > + * Return: true if TPG is on, otherwise false > > + */ > > +static bool zynqmp_disp_layer_get_tpg(struct zynqmp_disp *disp, > > + struct zynqmp_disp_layer *layer) > > +{ > > + return disp->tpg_on; > > +} > > + > > +/** > > + * zynqmp_disp_get_fmt - Get the supported DRM formats of the layer > > + * @disp: Display subsystem > > + * @layer: layer to get the formats > > + * @drm_fmts: pointer to array of DRM format strings > > + * @num_fmts: pointer to number of returned DRM formats > > + * > > + * Get the supported DRM formats of the given layer. > > + */ > > +static void zynqmp_disp_layer_get_fmts(struct zynqmp_disp *disp, > > + struct zynqmp_disp_layer *layer, > > + u32 **drm_fmts, unsigned int > *num_fmts) > > +{ > > + *drm_fmts = layer->drm_fmts; > > + *num_fmts = layer->num_fmts; > > +} > > + > > +/** > > + * zynqmp_disp_layer_enable - Enable the layer > > + * @disp: Display subsystem > > + * @layer: layer to esable > > + * @mode: operation mode > > + * > > + * Enable the layer @layer. > > + * > > + * Return: 0 on success, otherwise error code. > > + */ > > +static int zynqmp_disp_layer_enable(struct zynqmp_disp *disp, > > + struct zynqmp_disp_layer *layer, > > + enum zynqmp_disp_layer_mode mode) > > +{ > > + struct device *dev = disp->dev; > > + struct dma_async_tx_descriptor *desc; > > + enum dma_ctrl_flags flags; > > + unsigned int i; > > + > > + if (layer->enabled && layer->mode != mode) { > > + dev_err(dev, "layer is already enabled in different mode\n"); > > + return -EBUSY; > > + } > > + > > + zynqmp_disp_av_buf_enable_vid(&disp->av_buf, layer, mode); > > + zynqmp_disp_blend_layer_enable(&disp->blend, layer); > > + > > + layer->enabled = true; > > + layer->mode = mode; > > + > > + if (mode == ZYNQMP_DISP_LAYER_LIVE) > > + return 0; > > + > > + for (i = 0; i < ZYNQMP_DISP_MAX_NUM_SUB_PLANES; i++) { > > + struct zynqmp_disp_layer_dma *dma = &layer->dma[i]; > > + > > + if (dma->chan && dma->is_active) { > > + flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; > > + desc = dmaengine_prep_interleaved_dma(dma- > >chan, > > + &dma->xt, flags); > > + if (!desc) { > > + dev_err(dev, "failed to prep DMA > descriptor\n"); > > + return -ENOMEM; > > + } > > + > > + dmaengine_submit(desc); > > + dma_async_issue_pending(dma->chan); > > + } > > + } > > + > > + return 0; > > +} > > + > > +/** > > + * zynqmp_disp_layer_disable - Disable the layer > > + * @disp: Display subsystem > > + * @layer: layer to disable > > + * @mode: operation mode > > + * > > + * Disable the layer @layer. > > + * > > + * Return: 0 on success, or -EBUSY if the layer is in different mode. > > + */ > > +static int zynqmp_disp_layer_disable(struct zynqmp_disp *disp, > > + struct zynqmp_disp_layer *layer, > > + enum zynqmp_disp_layer_mode mode) > > +{ > > + struct device *dev = disp->dev; > > + unsigned int i; > > + > > + if (layer->mode != mode) { > > + dev_err(dev, "the layer is operating in different mode\n"); > > + return -EBUSY; > > + } > > + > > + for (i = 0; i < ZYNQMP_DISP_MAX_NUM_SUB_PLANES; i++) > > + if (layer->dma[i].chan && layer->dma[i].is_active) > > + dmaengine_terminate_sync(layer->dma[i].chan); > > + > > + zynqmp_disp_av_buf_disable_vid(&disp->av_buf, layer); > > + zynqmp_disp_blend_layer_disable(&disp->blend, layer); > > + layer->enabled = false; > > + > > + return 0; > > +} > > + > > +/** > > + * zynqmp_disp_layer_request_dma - Request DMA channels for a layer > > + * @disp: Display subsystem > > + * @layer: layer to request DMA channels > > + * @name: identifier string for layer type > > + * > > + * Request DMA engine channels for corresponding layer. > > + * > > + * Return: 0 on success, or err value from > of_dma_request_slave_channel(). > > + */ > > +static int > > +zynqmp_disp_layer_request_dma(struct zynqmp_disp *disp, > > + struct zynqmp_disp_layer *layer, const char > *name) > > +{ > > + struct zynqmp_disp_layer_dma *dma; > > + unsigned int i; > > + int ret; > > + > > + for (i = 0; i < layer->num_chan; i++) { > > + char temp[16]; > > + > > + dma = &layer->dma[i]; > > + snprintf(temp, sizeof(temp), "%s%d", name, i); > > + dma->chan = of_dma_request_slave_channel(layer- > >of_node, > > + temp); > > + if (IS_ERR(dma->chan)) { > > + dev_err(disp->dev, "failed to request dma > channel\n"); > > + ret = PTR_ERR(dma->chan); > > + dma->chan = NULL; > > + return ret; > > + } > > + } > > + > > + return 0; > > +} > > + > > +/** > > + * zynqmp_disp_layer_release_dma - Release DMA channels for a layer > > + * @disp: Display subsystem > > + * @layer: layer to release DMA channels > > + * > > + * Release the dma channels associated with @layer. > > + */ > > +static void zynqmp_disp_layer_release_dma(struct zynqmp_disp *disp, > > + struct zynqmp_disp_layer *layer) > > +{ > > + unsigned int i; > > + > > + for (i = 0; i < layer->num_chan; i++) { > > + if (layer->dma[i].chan) { > > + /* Make sure the channel is terminated before > release */ > > + dmaengine_terminate_all(layer->dma[i].chan); > > + dma_release_channel(layer->dma[i].chan); > > + } > > + } > > +} > > + > > +/** > > + * zynqmp_disp_layer_is_live - if any layer is live > > + * @disp: Display subsystem > > + * > > + * Return: true if any layer is live > > + */ > > +static bool zynqmp_disp_layer_is_live(struct zynqmp_disp *disp) > > +{ > > + unsigned int i; > > + > > + for (i = 0; i < ZYNQMP_DISP_NUM_LAYERS; i++) { > > + if (disp->layers[i].enabled && > > + disp->layers[i].mode == ZYNQMP_DISP_LAYER_LIVE) > > + return true; > > + } > > + > > + return false; > > +} > > + > > +/** > > + * zynqmp_disp_layer_is_enabled - if any layer is enabled > > + * @disp: Display subsystem > > + * > > + * Return: true if any layer is enabled > > + */ > > +static bool zynqmp_disp_layer_is_enabled(struct zynqmp_disp *disp) > > +{ > > + unsigned int i; > > + > > + for (i = 0; i < ZYNQMP_DISP_NUM_LAYERS; i++) > > + if (disp->layers[i].enabled) > > + return true; > > + > > + return false; > > +} > > + > > +/** > > + * zynqmp_disp_layer_destroy - Destroy all layers > > + * @disp: Display subsystem > > + * > > + * Destroy all layers. > > + */ > > +static void zynqmp_disp_layer_destroy(struct zynqmp_disp *disp) > > +{ > > + unsigned int i; > > + > > + for (i = 0; i < ZYNQMP_DISP_NUM_LAYERS; i++) { > > + zynqmp_disp_layer_release_dma(disp, &disp->layers[i]); > > + if (disp->layers[i].of_node) > > + of_node_put(disp->layers[i].of_node); > > + } > > +} > > + > > +/** > > + * zynqmp_disp_layer_create - Create all layers > > + * @disp: Display subsystem > > + * > > + * Create all layers. > > + * > > + * Return: 0 on success, otherwise error code from failed function > > + */ > > +static int zynqmp_disp_layer_create(struct zynqmp_disp *disp) > > +{ > > + struct zynqmp_disp_layer *layer; > > + unsigned int i; > > + int num_chans[ZYNQMP_DISP_NUM_LAYERS] = { 3, 1 }; > > + const char * const dma_name[] = { "vid", "gfx" }; > > + int ret; > > + > > + for (i = 0; i < ZYNQMP_DISP_NUM_LAYERS; i++) { > > + char temp[16]; > > + > > + layer = &disp->layers[i]; > > + layer->id = i; > > + layer->offset = i * 4; > > + layer->other = &disp->layers[!i]; > > + layer->num_chan = num_chans[i]; > > + snprintf(temp, sizeof(temp), "%s-layer", dma_name[i]); > > + layer->of_node = of_get_child_by_name(disp->dev- > >of_node, temp); > > + if (!layer->of_node) > > + goto err; > > + ret = zynqmp_disp_layer_request_dma(disp, layer, > dma_name[i]); > > + if (ret) > > + goto err; > > + layer->disp = disp; > > + } > > + > > + return 0; > > + > > +err: > > + zynqmp_disp_layer_destroy(disp); > > + return ret; > > +} > > + > > +/* > > + * ZynqMP Display internal functions > > + */ > > + > > +/* > > + * Output format enumeration for DRM property. > > + * The ID should be aligned with blend_output_fmts. > > + * The string should be aligned with how zynqmp_dp_set_color() > decodes. > > + */ > > +static struct drm_prop_enum_list zynqmp_disp_color_enum[] = { > > + { 0, "rgb" }, > > + { 1, "ycrcb444" }, > > + { 2, "ycrcb422" }, > > + { 3, "yonly" }, > > +}; > > + > > +/** > > + * zynqmp_disp_set_output_fmt - Set the output format > > + * @disp: Display subsystem > > + * @id: the format ID. Refer to zynqmp_disp_color_enum[]. > > + * > > + * This function sets the output format of the display / blender as well as > > + * the format of DP controller. The @id should be aligned with > > + * zynqmp_disp_color_enum, thus function needs to be used for DRM > property. > > + */ > > +static void > > +zynqmp_disp_set_output_fmt(struct zynqmp_disp *disp, unsigned int > id) > > +{ > > + const struct zynqmp_disp_fmt *fmt = &blend_output_fmts[id]; > > + > > + zynqmp_dp_set_color(disp->dpsub->dp, > zynqmp_disp_color_enum[id].name); > > + zynqmp_disp_blend_set_output_fmt(&disp->blend, fmt- > >disp_fmt); > > +} > > + > > +/** > > + * zynqmp_disp_set_bg_color - Set the background color > > + * @disp: Display subsystem > > + * @c0: color component 0 > > + * @c1: color component 1 > > + * @c2: color component 2 > > + * > > + * Set the background color with given color components (@c0, @c1, > @c2). > > + */ > > +static void zynqmp_disp_set_bg_color(struct zynqmp_disp *disp, > > + u32 c0, u32 c1, u32 c2) > > +{ > > + zynqmp_disp_blend_set_bg_color(&disp->blend, c0, c1, c2); > > +} > > + > > +/** > > + * zynqmp_disp_set_alpha - Set the alpha value > > + * @disp: Display subsystem > > + * @alpha: alpha value to set > > + * > > + * Set the alpha value for blending. > > + */ > > +static void zynqmp_disp_set_alpha(struct zynqmp_disp *disp, u32 > alpha) > > +{ > > + disp->alpha = alpha; > > + zynqmp_disp_blend_set_alpha(&disp->blend, alpha); > > +} > > + > > +/** > > + * zynqmp_disp_get_alpha - Get the alpha value > > + * @disp: Display subsystem > > + * > > + * Get the alpha value for blending. > > + * > > + * Return: current alpha value. > > + */ > > +static u32 zynqmp_disp_get_alpha(struct zynqmp_disp *disp) > > +{ > > + return disp->alpha; > > +} > > + > > +/** > > + * zynqmp_disp_set_g_alpha - Enable/disable the global alpha blending > > + * @disp: Display subsystem > > + * @enable: flag to enable or disable alpha blending > > + * > > + * Set the alpha value for blending. > > + */ > > +static void zynqmp_disp_set_g_alpha(struct zynqmp_disp *disp, bool > enable) > > +{ > > + disp->alpha_en = enable; > > + zynqmp_disp_blend_enable_alpha(&disp->blend, enable); > > +} > > + > > +/** > > + * zynqmp_disp_get_g_alpha - Get the global alpha status > > + * @disp: Display subsystem > > + * > > + * Get the global alpha statue. > > + * > > + * Return: true if global alpha is enabled, or false. > > + */ > > +static bool zynqmp_disp_get_g_alpha(struct zynqmp_disp *disp) > > +{ > > + return disp->alpha_en; > > +} > > + > > +/** > > + * zynqmp_disp_enable - Enable the Display subsystem > > + * @disp: Display subsystem > > + * > > + * Enable the Display subsystem. > > + */ > > +static void zynqmp_disp_enable(struct zynqmp_disp *disp) > > +{ > > + bool live; > > + > > + if (disp->enabled) > > + return; > > + > > + zynqmp_disp_av_buf_enable(&disp->av_buf); > > + /* Choose clock source based on the DT clock handle */ > > + zynqmp_disp_av_buf_set_vid_clock_src(&disp->av_buf, !!disp- > >_ps_pclk); > > + zynqmp_disp_av_buf_set_aud_clock_src(&disp->av_buf, !!disp- > >_ps_audclk); > > + live = zynqmp_disp_layer_is_live(disp); > > + zynqmp_disp_av_buf_set_vid_timing_src(&disp->av_buf, !live); > > + zynqmp_disp_av_buf_enable_buf(&disp->av_buf); > > + zynqmp_disp_av_buf_enable_aud(&disp->av_buf); > > + zynqmp_disp_aud_init(&disp->aud); > > + disp->enabled = true; > > +} > > + > > +/** > > + * zynqmp_disp_disable - Disable the Display subsystem > > + * @disp: Display subsystem > > + * @force: flag to disable forcefully > > + * > > + * Disable the Display subsystem. > > + */ > > +static void zynqmp_disp_disable(struct zynqmp_disp *disp, bool force) > > +{ > > + struct drm_crtc *crtc = &disp->xlnx_crtc.crtc; > > + > > + if (!force && (!disp->enabled || > zynqmp_disp_layer_is_enabled(disp))) > > + return; > > + > > + zynqmp_disp_aud_deinit(&disp->aud); > > + zynqmp_disp_av_buf_disable_aud(&disp->av_buf); > > + zynqmp_disp_av_buf_disable_buf(&disp->av_buf); > > + zynqmp_disp_av_buf_disable(&disp->av_buf); > > + > > + /* Mark the flip is done as crtc is disabled anyway */ > > + if (crtc->state->event) { > > + complete_all(crtc->state->event->base.completion); > > + crtc->state->event = NULL; > > + } > > + > > + disp->enabled = false; > > +} > > + > > +/* > > + * ZynqMP Display external functions for zynqmp_dp > > + */ > > + > > +/** > > + * zynqmp_disp_handle_vblank - Handle the vblank event > > + * @disp: Display subsystem > > + * > > + * This function handles the vblank interrupt, and sends an event to > > + * CRTC object. This will be called by the DP vblank interrupt handler. > > + */ > > +void zynqmp_disp_handle_vblank(struct zynqmp_disp *disp) > > +{ > > + struct drm_crtc *crtc = &disp->xlnx_crtc.crtc; > > + struct drm_device *drm = crtc->dev; > > + struct drm_pending_vblank_event *event; > > + unsigned long flags; > > + > > + drm_crtc_handle_vblank(crtc); > > + > > + /* Finish page flip */ > > + spin_lock_irqsave(&drm->event_lock, flags); > > + event = disp->event; > > + disp->event = NULL; > > + if (event) { > > + drm_crtc_send_vblank_event(crtc, event); > > + drm_crtc_vblank_put(crtc); > > + } > > + spin_unlock_irqrestore(&drm->event_lock, flags); > > Please take a look at drm_crtc_arm_vblank - that implements the exact > logic you're open-coding here. > Sure. Will fix it. > > +} > > + > > +/** > > + * zynqmp_disp_get_apb_clk_rate - Get the current APB clock rate > > + * @disp: Display subsystem > > + * > > + * Return: the current APB clock rate. > > + */ > > +unsigned int zynqmp_disp_get_apb_clk_rate(struct zynqmp_disp *disp) > > +{ > > + return clk_get_rate(disp->aclk); > > +} > > + > > +/** > > + * zynqmp_disp_aud_enabled - If the audio is enabled > > + * @disp: Display subsystem > > + * > > + * Return if the audio is enabled depending on the audio clock. > > + * > > + * Return: true if audio is enabled, or false. > > + */ > > +bool zynqmp_disp_aud_enabled(struct zynqmp_disp *disp) > > +{ > > + return !!disp->audclk; > > +} > > + > > +/** > > + * zynqmp_disp_get_aud_clk_rate - Get the current audio clock rate > > + * @disp: Display subsystem > > + * > > + * Return: the current audio clock rate. > > + */ > > +unsigned int zynqmp_disp_get_aud_clk_rate(struct zynqmp_disp *disp) > > +{ > > + if (zynqmp_disp_aud_enabled(disp)) > > + return 0; > > + return clk_get_rate(disp->aclk); > > +} > > + > > +/** > > + * zynqmp_disp_get_crtc_mask - Return the CRTC bit mask > > + * @disp: Display subsystem > > + * > > + * Return: the crtc mask of the zyqnmp_disp CRTC. > > + */ > > +uint32_t zynqmp_disp_get_crtc_mask(struct zynqmp_disp *disp) > > +{ > > + return drm_crtc_mask(&disp->xlnx_crtc.crtc); > > +} > > + > > +/* > > + * DRM property functions > > + */ > > + > > +static void zynqmp_disp_attach_vid_plane_property(struct > zynqmp_disp *disp, > > + struct drm_mode_object > *obj) > > +{ > > + drm_object_attach_property(obj, disp->tpg_prop, false); > > +} > > You have a lot of wrappers for core functions (clk_get_rate above is > similar). This makes your code neater for you, but it makes it harder for > linux-people to read since they always need to jump around. Please just > directly call core functions like these instead of wrapping them. Will remove the unnecessary ones, such as wrappers around drm properties. Some of them are for inter-module calls, so I'll leave those only. > > > + > > +static void zynqmp_disp_attach_gfx_plane_property(struct > zynqmp_disp *disp, > > + struct drm_mode_object > *obj) > > +{ > > + drm_object_attach_property(obj, disp->g_alpha_prop, > > + > ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA_MAX); > > + disp->alpha = ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA_MAX; > > + /* Enable the global alpha as default */ > > + drm_object_attach_property(obj, disp->g_alpha_en_prop, true); > > + disp->alpha_en = true; > > +} > > + > > +static void zynqmp_disp_attach_crtc_property(struct zynqmp_disp > *disp, > > + struct drm_mode_object *obj) > > +{ > > + drm_object_attach_property(obj, disp->color_prop, 0); > > + zynqmp_dp_set_color(disp->dpsub->dp, > zynqmp_disp_color_enum[0].name); > > + drm_object_attach_property(obj, disp->bg_c0_prop, 0); > > + drm_object_attach_property(obj, disp->bg_c1_prop, 0); > > + drm_object_attach_property(obj, disp->bg_c2_prop, 0); > > +} > > + > > +static void zynqmp_disp_create_property(struct zynqmp_disp *disp) > > +{ > > + int num; > > + u64 max; > > + > > + max = ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA_MAX; > > + disp->g_alpha_prop = drm_property_create_range(disp->drm, 0, > "alpha", 0, > > + max); > > + disp->g_alpha_en_prop = drm_property_create_bool(disp->drm, 0, > > + "g_alpha_en"); > > + num = ARRAY_SIZE(zynqmp_disp_color_enum); > > + disp->color_prop = drm_property_create_enum(disp->drm, 0, > > + "output_color", > > + zynqmp_disp_color_enum, > > + num); > > + max = ZYNQMP_DISP_V_BLEND_BG_MAX; > > + disp->bg_c0_prop = drm_property_create_range(disp->drm, 0, > "bg_c0", 0, > > + max); > > + disp->bg_c1_prop = drm_property_create_range(disp->drm, 0, > "bg_c1", 0, > > + max); > > + disp->bg_c2_prop = drm_property_create_range(disp->drm, 0, > "bg_c2", 0, > > + max); > > + disp->tpg_prop = drm_property_create_bool(disp->drm, 0, "tpg"); > > +} > > + > > +static void zynqmp_disp_destroy_property(struct zynqmp_disp *disp) > > +{ > > + drm_property_destroy(disp->drm, disp->bg_c2_prop); > > + drm_property_destroy(disp->drm, disp->bg_c1_prop); > > + drm_property_destroy(disp->drm, disp->bg_c0_prop); > > + drm_property_destroy(disp->drm, disp->color_prop); > > + drm_property_destroy(disp->drm, disp->g_alpha_en_prop); > > + drm_property_destroy(disp->drm, disp->g_alpha_prop); > > +} > > For all the property functions: Please split this out into a separate > patch (probably even want a separate follow-up patch series). This is new > uapi, and we have very strict requirements for that: > > https://dri.freedesktop.org/docs/drm/gpu/drm-uapi.html#open-source- > userspace-requirements > > Especially around plane blending properties we already have a huge mess, > adding more driver-private and nonstandard properties isn't a good idea. > > You can leave all your driver backend implementation in place, just don't > register the properties and decode them in atomic_get/set_property. Ok. Will split the property stuff into a separate patch. > > > + > > +/* > > + * DRM plane functions > > + */ > > + > > +static inline struct zynqmp_disp_layer *plane_to_layer(struct > drm_plane *plane) > > +{ > > + return container_of(plane, struct zynqmp_disp_layer, plane); > > +} > > + > > +static int zynqmp_disp_plane_enable(struct drm_plane *plane) > > +{ > > + struct zynqmp_disp_layer *layer = plane_to_layer(plane); > > + struct zynqmp_disp *disp = layer->disp; > > + int ret; > > + > > + zynqmp_disp_set_g_alpha(disp, disp->alpha_en); > > + zynqmp_disp_set_alpha(disp, disp->alpha); > > + ret = zynqmp_disp_layer_enable(layer->disp, layer, > > + ZYNQMP_DISP_LAYER_NONLIVE); > > + if (ret) > > + return ret; > > + > > + if (layer->id == ZYNQMP_DISP_LAYER_GFX && disp->tpg_on) { > > + layer = &disp->layers[ZYNQMP_DISP_LAYER_VID]; > > + zynqmp_disp_layer_set_tpg(disp, layer, disp->tpg_on); > > + } > > + > > + return 0; > > +} > > + > > +static int zynqmp_disp_plane_disable(struct drm_plane *plane) > > +{ > > + struct zynqmp_disp_layer *layer = plane_to_layer(plane); > > + struct zynqmp_disp *disp = layer->disp; > > + > > + zynqmp_disp_layer_disable(disp, layer, > ZYNQMP_DISP_LAYER_NONLIVE); > > + if (layer->id == ZYNQMP_DISP_LAYER_VID && disp->tpg_on) > > + zynqmp_disp_layer_set_tpg(disp, layer, disp->tpg_on); > > + > > + return 0; > > +} > > + > > +static int zynqmp_disp_plane_mode_set(struct drm_plane *plane, > > + struct drm_framebuffer *fb, > > + int crtc_x, int crtc_y, > > + unsigned int crtc_w, unsigned int crtc_h, > > + u32 src_x, u32 src_y, > > + u32 src_w, u32 src_h) > > +{ > > + struct zynqmp_disp_layer *layer = plane_to_layer(plane); > > + const struct drm_format_info *info = fb->format; > > + struct drm_format_name_buf format_name; > > + struct device *dev = layer->disp->dev; > > + dma_addr_t paddr; > > + size_t offset; > > + unsigned int i; > > + int ret; > > + > > + if (!info) { > > + dev_err(dev, "unsupported framebuffer format %s\n", > > + drm_get_format_name(info->format, > &format_name)); > > + return -EINVAL; > > + } > > + > > + ret = zynqmp_disp_layer_check_size(layer->disp, layer, src_w, > src_h); > > + if (ret) > > + return ret; > > + > > + for (i = 0; i < info->num_planes; i++) { > > + unsigned int width = src_w / (i ? info->hsub : 1); > > + unsigned int height = src_h / (i ? info->vsub : 1); > > + > > + paddr = xlnx_fb_get_paddr(fb, i); > > + if (!paddr) { > > + dev_err(dev, "failed to get a paddr\n"); > > + return -EINVAL; > > + } > > + > > + layer->dma[i].xt.numf = height; > > + layer->dma[i].sgl[0].size = width * info->cpp[i]; > > + layer->dma[i].sgl[0].icg = fb->pitches[i] - > > + layer->dma[i].sgl[0].size; > > + offset = src_x * info->cpp[i] + src_y * fb->pitches[i]; > > + offset += fb->offsets[i]; > > + layer->dma[i].xt.src_start = paddr + offset; > > + layer->dma[i].xt.frame_size = 1; > > + layer->dma[i].xt.dir = DMA_MEM_TO_DEV; > > + layer->dma[i].xt.src_sgl = true; > > + layer->dma[i].xt.dst_sgl = false; > > + layer->dma[i].is_active = true; > > + } > > + > > + for (; i < ZYNQMP_DISP_MAX_NUM_SUB_PLANES; i++) > > + layer->dma[i].is_active = false; > > + > > + ret = zynqmp_disp_layer_set_fmt(layer->disp, layer, info->format); > > + if (ret) > > + dev_err(dev, "failed to set dp_sub layer fmt\n"); > > + > > + return ret; > > +} > > + > > +static void zynqmp_disp_plane_destroy(struct drm_plane *plane) > > +{ > > + drm_plane_cleanup(plane); > > +} > > + > > +static int > > +zynqmp_disp_plane_atomic_set_property(struct drm_plane *plane, > > + struct drm_plane_state *state, > > + struct drm_property *property, u64 val) > > +{ > > + struct zynqmp_disp_layer *layer = plane_to_layer(plane); > > + struct zynqmp_disp *disp = layer->disp; > > + int ret = 0; > > + > > + if (property == disp->g_alpha_prop) > > + zynqmp_disp_set_alpha(disp, val); > > + else if (property == disp->g_alpha_en_prop) > > + zynqmp_disp_set_g_alpha(disp, val); > > + else if (property == disp->tpg_prop) > > + ret = zynqmp_disp_layer_set_tpg(disp, layer, val); > > + else > > + return -EINVAL; > > + > > + return ret; > > +} > > + > > +static int > > +zynqmp_disp_plane_atomic_get_property(struct drm_plane *plane, > > + const struct drm_plane_state *state, > > + struct drm_property *property, > > + uint64_t *val) > > +{ > > + struct zynqmp_disp_layer *layer = plane_to_layer(plane); > > + struct zynqmp_disp *disp = layer->disp; > > + int ret = 0; > > + > > + if (property == disp->g_alpha_prop) > > + *val = zynqmp_disp_get_alpha(disp); > > + else if (property == disp->g_alpha_en_prop) > > + *val = zynqmp_disp_get_g_alpha(disp); > > + else if (property == disp->tpg_prop) > > + *val = zynqmp_disp_layer_get_tpg(disp, layer); > > + else > > + return -EINVAL; > > + > > + return ret; > > +} > > Please also move the above 2 functions into the separate property enabling > patch series. Will do. > > > + > > +static struct drm_plane_funcs zynqmp_disp_plane_funcs = { > > + .update_plane = drm_atomic_helper_update_plane, > > + .disable_plane = drm_atomic_helper_disable_plane, > > + .atomic_set_property = zynqmp_disp_plane_atomic_set_property, > > + .atomic_get_property = zynqmp_disp_plane_atomic_get_property, > > + .destroy = zynqmp_disp_plane_destroy, > > + .reset = drm_atomic_helper_plane_reset, > > + .atomic_duplicate_state = > drm_atomic_helper_plane_duplicate_state, > > + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, > > +}; > > + > > +static int zynqmp_disp_plane_prepare_fb(struct drm_plane *plane, > > + struct drm_plane_state *new_state) > > +{ > > + return 0; > > +} > > + > > +static void zynqmp_disp_plane_cleanup_fb(struct drm_plane *plane, > > + struct drm_plane_state *old_state) > > +{ > > +} > > + > > +static int zynqmp_disp_plane_atomic_check(struct drm_plane *plane, > > + struct drm_plane_state *state) > > +{ > > + return 0; > > +} > > Please remove the above dummy functions, the helpers should all have > fallbacks if a callback isn't set. This is general advise (in case I've > missed a few of them). If you find a place where a callback is mandatory, > we need to fix the core code. Sure. Will remove. > > > + > > +static void > > +zynqmp_disp_plane_atomic_update(struct drm_plane *plane, > > + struct drm_plane_state *old_state) > > +{ > > + int ret; > > + > > + if (!plane->state->crtc || !plane->state->fb) > > + return; > > + > > + ret = zynqmp_disp_plane_mode_set(plane, plane->state->fb, > > + plane->state->crtc_x, > > + plane->state->crtc_y, > > + plane->state->crtc_w, > > + plane->state->crtc_h, > > + plane->state->src_x >> 16, > > + plane->state->src_y >> 16, > > + plane->state->src_w >> 16, > > + plane->state->src_h >> 16); > > + if (ret) > > + return; > > + > > + zynqmp_disp_plane_enable(plane); > > +} > > + > > +static void > > +zynqmp_disp_plane_atomic_disable(struct drm_plane *plane, > > + struct drm_plane_state *old_state) > > +{ > > + zynqmp_disp_plane_disable(plane); > > +} > > + > > +static const struct drm_plane_helper_funcs > zynqmp_disp_plane_helper_funcs = { > > + .prepare_fb = zynqmp_disp_plane_prepare_fb, > > + .cleanup_fb = zynqmp_disp_plane_cleanup_fb, > > + .atomic_check = zynqmp_disp_plane_atomic_check, > > + .atomic_update = zynqmp_disp_plane_atomic_update, > > + .atomic_disable = zynqmp_disp_plane_atomic_disable, > > +}; > > + > > +static int zynqmp_disp_create_plane(struct zynqmp_disp *disp) > > +{ > > + struct zynqmp_disp_layer *layer; > > + unsigned int i; > > + u32 *fmts = NULL; > > + unsigned int num_fmts = 0; > > + enum drm_plane_type type; > > + int ret; > > + > > + /* graphics layer is primary, and video layer is overaly */ > > + type = DRM_PLANE_TYPE_OVERLAY; > > + for (i = 0; i < ZYNQMP_DISP_NUM_LAYERS; i++) { > > + layer = &disp->layers[i]; > > + zynqmp_disp_layer_get_fmts(disp, layer, &fmts, > &num_fmts); > > + ret = drm_universal_plane_init(disp->drm, &layer->plane, 0, > > + &zynqmp_disp_plane_funcs, > fmts, > > + num_fmts, NULL, type, NULL); > > + if (ret) > > + goto err_plane; > > + drm_plane_helper_add(&layer->plane, > > + &zynqmp_disp_plane_helper_funcs); > > + type = DRM_PLANE_TYPE_PRIMARY; > > + } > > + > > + /* Attach properties to each layers */ > > + zynqmp_disp_attach_gfx_plane_property(disp, &layer->plane.base); > > + layer = &disp->layers[ZYNQMP_DISP_LAYER_VID]; > > + zynqmp_disp_attach_vid_plane_property(disp, &layer->plane.base); > > + > > + return ret; > > + > > +err_plane: > > + if (i) > > + drm_plane_cleanup(&disp->layers[0].plane); > > + return ret; > > +} > > + > > +static void zynqmp_disp_destroy_plane(struct zynqmp_disp *disp) > > +{ > > + unsigned int i; > > + > > + for (i = 0; i < ZYNQMP_DISP_NUM_LAYERS; i++) > > + zynqmp_disp_plane_destroy(&disp->layers[i].plane); > > +} > > + > > +/* > > + * Xlnx crtc functions > > + */ > > + > > +static inline struct zynqmp_disp *xlnx_crtc_to_disp(struct xlnx_crtc > *xlnx_crtc) > > +{ > > + return container_of(xlnx_crtc, struct zynqmp_disp, xlnx_crtc); > > +} > > + > > +static int zynqmp_disp_enable_vblank(struct xlnx_crtc *xlnx_crtc) > > +{ > > + struct zynqmp_disp *disp = xlnx_crtc_to_disp(xlnx_crtc); > > + > > + zynqmp_dp_enable_vblank(disp->dpsub->dp); > > + return 0; > > +} > > + > > +static void zynqmp_disp_disable_vblank(struct xlnx_crtc *xlnx_crtc) > > +{ > > + struct zynqmp_disp *disp = xlnx_crtc_to_disp(xlnx_crtc); > > + > > + zynqmp_dp_disable_vblank(disp->dpsub->dp); > > +} > > + > > +static int zynqmp_disp_get_max_width(struct xlnx_crtc *xlnx_crtc) > > +{ > > + return ZYNQMP_DISP_MAX_WIDTH; > > +} > > + > > +static int zynqmp_disp_get_max_height(struct xlnx_crtc *xlnx_crtc) > > +{ > > + return ZYNQMP_DISP_MAX_HEIGHT; > > +} > > Bikeshed, feel free to ignore: This is a bit much indirection for my > taste. > > > + > > +static uint32_t zynqmp_disp_get_format(struct xlnx_crtc *xlnx_crtc) > > +{ > > + struct zynqmp_disp *disp = xlnx_crtc_to_disp(xlnx_crtc); > > + > > + return disp->layers[ZYNQMP_DISP_LAYER_GFX].fmt->drm_fmt; > > +} > > + > > +static unsigned int zynqmp_disp_get_align(struct xlnx_crtc *xlnx_crtc) > > +{ > > + struct zynqmp_disp *disp = xlnx_crtc_to_disp(xlnx_crtc); > > + struct zynqmp_disp_layer *layer = &disp- > >layers[ZYNQMP_DISP_LAYER_VID]; > > + > > + return 1 << layer->dma->chan->device->copy_align; > > +} > > + > > +static u64 zynqmp_disp_get_dma_mask(struct xlnx_crtc *xlnx_crtc) > > +{ > > + return DMA_BIT_MASK(ZYNQMP_DISP_MAX_DMA_BIT); > > +} > > + > > +/* > > + * DRM crtc functions > > + */ > > + > > +static inline struct zynqmp_disp *crtc_to_disp(struct drm_crtc *crtc) > > +{ > > + struct xlnx_crtc *xlnx_crtc = to_xlnx_crtc(crtc); > > + > > + return xlnx_crtc_to_disp(xlnx_crtc); > > +} > > + > > +static int zynqmp_disp_crtc_mode_set(struct drm_crtc *crtc, > > + struct drm_display_mode *mode, > > + struct drm_display_mode > *adjusted_mode, > > + int x, int y, > > + struct drm_framebuffer *old_fb) > > +{ > > + struct zynqmp_disp *disp = crtc_to_disp(crtc); > > + unsigned long rate; > > + long diff; > > + int ret; > > + > > + zynqmp_disp_clk_disable(disp->pclk, &disp->pclk_en); > > + ret = clk_set_rate(disp->pclk, adjusted_mode->clock * 1000); > > + if (ret) { > > + dev_err(disp->dev, "failed to set a pixel clock\n"); > > + return ret; > > + } > > + > > + rate = clk_get_rate(disp->pclk); > > + diff = rate - adjusted_mode->clock * 1000; > > + if (abs(diff) > (adjusted_mode->clock * 1000) / 20) { > > + dev_info(disp->dev, "request pixel rate: %d actual > rate: %lu\n", > > + adjusted_mode->clock, rate); > > + } else { > > + dev_dbg(disp->dev, "request pixel rate: %d actual > rate: %lu\n", > > + adjusted_mode->clock, rate); > > + } > > + > > + /* The timing register should be programmed always */ > > + zynqmp_dp_encoder_mode_set_stream(disp->dpsub->dp, > adjusted_mode); > > + > > + return 0; > > +} > > + > > +static void > > +zynqmp_disp_crtc_atomic_enable(struct drm_crtc *crtc, > > + struct drm_crtc_state *old_crtc_state) > > +{ > > + struct zynqmp_disp *disp = crtc_to_disp(crtc); > > + struct drm_display_mode *adjusted_mode = &crtc->state- > >adjusted_mode; > > + int ret, vrefresh; > > + > > + zynqmp_disp_crtc_mode_set(crtc, &crtc->state->mode, > > + adjusted_mode, crtc->x, crtc->y, NULL); > > + > > + pm_runtime_get_sync(disp->dev); > > + ret = zynqmp_disp_clk_enable(disp->pclk, &disp->pclk_en); > > + if (ret) { > > + dev_err(disp->dev, "failed to enable a pixel clock\n"); > > + return; > > + } > > + zynqmp_disp_set_output_fmt(disp, disp->color); > > + zynqmp_disp_set_bg_color(disp, disp->bg_c0, disp->bg_c1, disp- > >bg_c2); > > + zynqmp_disp_enable(disp); > > + /* Delay of 3 vblank intervals for timing gen to be stable */ > > + vrefresh = (adjusted_mode->clock * 1000) / > > + (adjusted_mode->vtotal * adjusted_mode->htotal); > > + msleep(3 * 1000 / vrefresh); > > +} > > + > > +static void > > +zynqmp_disp_crtc_atomic_disable(struct drm_crtc *crtc, > > + struct drm_crtc_state *old_crtc_state) > > +{ > > + struct zynqmp_disp *disp = crtc_to_disp(crtc); > > + > > + zynqmp_disp_clk_disable(disp->pclk, &disp->pclk_en); > > + zynqmp_disp_plane_disable(crtc->primary); > > + zynqmp_disp_disable(disp, true); > > + pm_runtime_put_sync(disp->dev); > > +} > > + > > +static void zynqmp_disp_crtc_mode_set_nofb(struct drm_crtc *crtc) > > +{ > > +} > > Again please remove the unecessary dumy functions. > Will do. > > + > > +static int zynqmp_disp_crtc_atomic_check(struct drm_crtc *crtc, > > + struct drm_crtc_state *state) > > +{ > > + return drm_atomic_add_affected_planes(state->state, crtc); > > +} > > + > > +static void > > +zynqmp_disp_crtc_atomic_begin(struct drm_crtc *crtc, > > + struct drm_crtc_state *old_crtc_state) > > +{ > > + /* Don't rely on vblank when disabling crtc */ > > + if (crtc->primary->state->fb && crtc->state->event) { > > + struct zynqmp_disp *disp = crtc_to_disp(crtc); > > + > > + /* Consume the flip_done event from atomic helper */ > > + crtc->state->event->pipe = drm_crtc_index(crtc); > > + WARN_ON(drm_crtc_vblank_get(crtc) != 0); > > + disp->event = crtc->state->event; > > + crtc->state->event = NULL; > > + } > > +} > > + > > +static struct drm_crtc_helper_funcs zynqmp_disp_crtc_helper_funcs = { > > + .atomic_enable = zynqmp_disp_crtc_atomic_enable, > > + .atomic_disable = zynqmp_disp_crtc_atomic_disable, > > + .mode_set_nofb = zynqmp_disp_crtc_mode_set_nofb, > > + .atomic_check = zynqmp_disp_crtc_atomic_check, > > + .atomic_begin = zynqmp_disp_crtc_atomic_begin, > > +}; > > + > > +static void zynqmp_disp_crtc_destroy(struct drm_crtc *crtc) > > +{ > > + zynqmp_disp_crtc_atomic_disable(crtc, NULL); > > + drm_crtc_cleanup(crtc); > > +} > > + > > +static int > > +zynqmp_disp_crtc_atomic_set_property(struct drm_crtc *crtc, > > + struct drm_crtc_state *state, > > + struct drm_property *property, > > + uint64_t val) > > +{ > > + struct zynqmp_disp *disp = crtc_to_disp(crtc); > > + > > + /* > > + * CRTC prop values are just stored here and applied when CRTC > gets > > + * enabled > > + */ > > + if (property == disp->color_prop) > > + disp->color = val; > > + else if (property == disp->bg_c0_prop) > > + disp->bg_c0 = val; > > + else if (property == disp->bg_c1_prop) > > + disp->bg_c1 = val; > > + else if (property == disp->bg_c2_prop) > > + disp->bg_c2 = val; > > + else > > + return -EINVAL; > > + > > + return 0; > > +} > > + > > +static int > > +zynqmp_disp_crtc_atomic_get_property(struct drm_crtc *crtc, > > + const struct drm_crtc_state *state, > > + struct drm_property *property, > > + uint64_t *val) > > +{ > > + struct zynqmp_disp *disp = crtc_to_disp(crtc); > > + > > + if (property == disp->color_prop) > > + *val = disp->color; > > + else if (property == disp->bg_c0_prop) > > + *val = disp->bg_c0; > > + else if (property == disp->bg_c1_prop) > > + *val = disp->bg_c1; > > + else if (property == disp->bg_c2_prop) > > + *val = disp->bg_c2; > > + else > > + return -EINVAL; > > + > > + return 0; > > +} > > Again property enabling into a separate patch series pls. > Sure. Thanks, -hyun > > + > > +static struct drm_crtc_funcs zynqmp_disp_crtc_funcs = { > > + .destroy = zynqmp_disp_crtc_destroy, > > + .set_config = drm_atomic_helper_set_config, > > + .page_flip = drm_atomic_helper_page_flip, > > + .atomic_set_property = zynqmp_disp_crtc_atomic_set_property, > > + .atomic_get_property = zynqmp_disp_crtc_atomic_get_property, > > + .reset = drm_atomic_helper_crtc_reset, > > + .atomic_duplicate_state = > drm_atomic_helper_crtc_duplicate_state, > > + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, > > +}; > > + > > +static void zynqmp_disp_create_crtc(struct zynqmp_disp *disp) > > +{ > > + struct drm_plane *plane = &disp- > >layers[ZYNQMP_DISP_LAYER_GFX].plane; > > + int ret; > > + > > + ret = drm_crtc_init_with_planes(disp->drm, &disp->xlnx_crtc.crtc, > plane, > > + NULL, &zynqmp_disp_crtc_funcs, > NULL); > > + drm_crtc_helper_add(&disp->xlnx_crtc.crtc, > > + &zynqmp_disp_crtc_helper_funcs); > > + zynqmp_disp_attach_crtc_property(disp, &disp- > >xlnx_crtc.crtc.base); > > + > > + disp->xlnx_crtc.enable_vblank = &zynqmp_disp_enable_vblank; > > + disp->xlnx_crtc.disable_vblank = &zynqmp_disp_disable_vblank; > > + disp->xlnx_crtc.get_max_width = &zynqmp_disp_get_max_width; > > + disp->xlnx_crtc.get_max_height = &zynqmp_disp_get_max_height; > > + disp->xlnx_crtc.get_format = &zynqmp_disp_get_format; > > + disp->xlnx_crtc.get_align = &zynqmp_disp_get_align; > > + disp->xlnx_crtc.get_dma_mask = &zynqmp_disp_get_dma_mask; > > + xlnx_crtc_register(disp->drm, &disp->xlnx_crtc); > > +} > > + > > +static void zynqmp_disp_destroy_crtc(struct zynqmp_disp *disp) > > +{ > > + xlnx_crtc_unregister(disp->drm, &disp->xlnx_crtc); > > + zynqmp_disp_crtc_destroy(&disp->xlnx_crtc.crtc); > > +} > > + > > +static void zynqmp_disp_map_crtc_to_plane(struct zynqmp_disp *disp) > > +{ > > + u32 possible_crtcs = drm_crtc_mask(&disp->xlnx_crtc.crtc); > > + unsigned int i; > > + > > + for (i = 0; i < ZYNQMP_DISP_NUM_LAYERS; i++) > > + disp->layers[i].plane.possible_crtcs = possible_crtcs; > > +} > > + > > +/* > > + * Component functions > > + */ > > + > > +int zynqmp_disp_bind(struct device *dev, struct device *master, void > *data) > > +{ > > + struct zynqmp_dpsub *dpsub = dev_get_drvdata(dev); > > + struct zynqmp_disp *disp = dpsub->disp; > > + struct drm_device *drm = data; > > + int ret; > > + > > + disp->drm = drm; > > + zynqmp_disp_create_property(disp); > > + ret = zynqmp_disp_create_plane(disp); > > + if (ret) > > + return ret; > > + zynqmp_disp_create_crtc(disp); > > + zynqmp_disp_map_crtc_to_plane(disp); > > + > > + return 0; > > +} > > + > > +void zynqmp_disp_unbind(struct device *dev, struct device *master, > void *data) > > +{ > > + struct zynqmp_dpsub *dpsub = dev_get_drvdata(dev); > > + struct zynqmp_disp *disp = dpsub->disp; > > + > > + zynqmp_disp_destroy_crtc(disp); > > + zynqmp_disp_destroy_plane(disp); > > + zynqmp_disp_destroy_property(disp); > > +} > > + > > +/* > > + * Platform initialization functions > > + */ > > + > > +static int zynqmp_disp_enumerate_fmts(struct zynqmp_disp *disp) > > +{ > > + struct zynqmp_disp_layer *layer; > > + u32 *bus_fmts; > > + u32 i, size, num_bus_fmts; > > + > > + num_bus_fmts = ARRAY_SIZE(av_buf_live_fmts); > > + bus_fmts = devm_kzalloc(disp->dev, sizeof(*bus_fmts) * > num_bus_fmts, > > + GFP_KERNEL); > > + if (!bus_fmts) > > + return -ENOMEM; > > + for (i = 0; i < num_bus_fmts; i++) > > + bus_fmts[i] = av_buf_live_fmts[i].bus_fmt; > > + > > + layer = &disp->layers[ZYNQMP_DISP_LAYER_VID]; > > + layer->num_bus_fmts = num_bus_fmts; > > + layer->bus_fmts = bus_fmts; > > + size = ARRAY_SIZE(av_buf_vid_fmts); > > + layer->num_fmts = size; > > + layer->drm_fmts = devm_kzalloc(disp->dev, > > + sizeof(*layer->drm_fmts) * size, > > + GFP_KERNEL); > > + if (!layer->drm_fmts) > > + return -ENOMEM; > > + for (i = 0; i < layer->num_fmts; i++) > > + layer->drm_fmts[i] = av_buf_vid_fmts[i].drm_fmt; > > + layer->fmt = > &av_buf_vid_fmts[ZYNQMP_DISP_AV_BUF_VID_FMT_YUYV]; > > + > > + layer = &disp->layers[ZYNQMP_DISP_LAYER_GFX]; > > + layer->num_bus_fmts = num_bus_fmts; > > + layer->bus_fmts = bus_fmts; > > + size = ARRAY_SIZE(av_buf_gfx_fmts); > > + layer->num_fmts = size; > > + layer->drm_fmts = devm_kzalloc(disp->dev, > > + sizeof(*layer->drm_fmts) * size, > > + GFP_KERNEL); > > + if (!layer->drm_fmts) > > + return -ENOMEM; > > + > > + for (i = 0; i < layer->num_fmts; i++) > > + layer->drm_fmts[i] = av_buf_gfx_fmts[i].drm_fmt; > > + layer->fmt = > &av_buf_gfx_fmts[ZYNQMP_DISP_AV_BUF_GFX_FMT_RGB565]; > > + > > + return 0; > > +} > > + > > +int zynqmp_disp_probe(struct platform_device *pdev) > > +{ > > + struct zynqmp_dpsub *dpsub; > > + struct zynqmp_disp *disp; > > + struct resource *res; > > + int ret; > > + > > + disp = devm_kzalloc(&pdev->dev, sizeof(*disp), GFP_KERNEL); > > + if (!disp) > > + return -ENOMEM; > > + disp->dev = &pdev->dev; > > + > > + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, > "blend"); > > + disp->blend.base = devm_ioremap_resource(&pdev->dev, res); > > + if (IS_ERR(disp->blend.base)) > > + return PTR_ERR(disp->blend.base); > > + > > + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, > "av_buf"); > > + disp->av_buf.base = devm_ioremap_resource(&pdev->dev, res); > > + if (IS_ERR(disp->av_buf.base)) > > + return PTR_ERR(disp->av_buf.base); > > + > > + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, > "aud"); > > + disp->aud.base = devm_ioremap_resource(&pdev->dev, res); > > + if (IS_ERR(disp->aud.base)) > > + return PTR_ERR(disp->aud.base); > > + > > + dpsub = platform_get_drvdata(pdev); > > + dpsub->disp = disp; > > + disp->dpsub = dpsub; > > + > > + ret = zynqmp_disp_enumerate_fmts(disp); > > + if (ret) > > + return ret; > > + > > + /* Try the live PL video clock */ > > + disp->_pl_pclk = devm_clk_get(disp->dev, "dp_live_video_in_clk"); > > + if (!IS_ERR(disp->_pl_pclk)) { > > + disp->pclk = disp->_pl_pclk; > > + ret = zynqmp_disp_clk_enable_disable(disp->pclk, > > + &disp->pclk_en); > > + if (ret) > > + disp->pclk = NULL; > > + } else if (PTR_ERR(disp->_pl_pclk) == -EPROBE_DEFER) { > > + return PTR_ERR(disp->_pl_pclk); > > + } > > + > > + /* If the live PL video clock is not valid, fall back to PS clock */ > > + if (!disp->pclk) { > > + disp->_ps_pclk = devm_clk_get(disp->dev, > "dp_vtc_pixel_clk_in"); > > + if (IS_ERR(disp->_ps_pclk)) { > > + dev_err(disp->dev, "failed to init any video clock\n"); > > + return PTR_ERR(disp->_ps_pclk); > > + } > > + disp->pclk = disp->_ps_pclk; > > + ret = zynqmp_disp_clk_enable_disable(disp->pclk, > > + &disp->pclk_en); > > + if (ret) { > > + dev_err(disp->dev, "failed to init any video clock\n"); > > + return ret; > > + } > > + } > > + > > + disp->aclk = devm_clk_get(disp->dev, "dp_apb_clk"); > > + if (IS_ERR(disp->aclk)) > > + return PTR_ERR(disp->aclk); > > + ret = zynqmp_disp_clk_enable(disp->aclk, &disp->aclk_en); > > + if (ret) { > > + dev_err(disp->dev, "failed to enable the APB clk\n"); > > + return ret; > > + } > > + > > + /* Try the live PL audio clock */ > > + disp->_pl_audclk = devm_clk_get(disp->dev, "dp_live_audio_aclk"); > > + if (!IS_ERR(disp->_pl_audclk)) { > > + disp->audclk = disp->_pl_audclk; > > + ret = zynqmp_disp_clk_enable_disable(disp->audclk, > > + &disp->audclk_en); > > + if (ret) > > + disp->audclk = NULL; > > + } > > + > > + /* If the live PL audio clock is not valid, fall back to PS clock */ > > + if (!disp->audclk) { > > + disp->_ps_audclk = devm_clk_get(disp->dev, "dp_aud_clk"); > > + if (!IS_ERR(disp->_ps_audclk)) { > > + disp->audclk = disp->_ps_audclk; > > + ret = zynqmp_disp_clk_enable_disable(disp->audclk, > > + &disp->audclk_en); > > + if (ret) > > + disp->audclk = NULL; > > + } > > + > > + if (!disp->audclk) { > > + dev_err(disp->dev, > > + "audio is disabled due to clock failure\n"); > > + } > > + } > > + > > + ret = zynqmp_disp_layer_create(disp); > > + if (ret) > > + goto error_aclk; > > + > > + return 0; > > + > > +error_aclk: > > + zynqmp_disp_clk_disable(disp->aclk, &disp->aclk_en); > > + return ret; > > +} > > + > > +int zynqmp_disp_remove(struct platform_device *pdev) > > +{ > > + struct zynqmp_dpsub *dpsub = platform_get_drvdata(pdev); > > + struct zynqmp_disp *disp = dpsub->disp; > > + > > + zynqmp_disp_layer_destroy(disp); > > + if (disp->audclk) > > + zynqmp_disp_clk_disable(disp->audclk, &disp->audclk_en); > > + zynqmp_disp_clk_disable(disp->aclk, &disp->aclk_en); > > + zynqmp_disp_clk_disable(disp->pclk, &disp->pclk_en); > > + dpsub->disp = NULL; > > + > > + return 0; > > +} > > diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.h > b/drivers/gpu/drm/xlnx/zynqmp_disp.h > > new file mode 100644 > > index 0000000..0291fc2 > > --- /dev/null > > +++ b/drivers/gpu/drm/xlnx/zynqmp_disp.h > > @@ -0,0 +1,28 @@ > > +/* > > + * ZynqMP Display Driver > > + * > > + * Copyright (C) 2017 - 2018 Xilinx, Inc. > > + * > > + * Author: Hyun Woo Kwon <hyun.kwon@xilinx.com> > > + * > > + * SPDX-License-Identifier: GPL-2.0 > > + */ > > + > > +#ifndef _ZYNQMP_DISP_H_ > > +#define _ZYNQMP_DISP_H_ > > + > > +struct zynqmp_disp; > > + > > +void zynqmp_disp_handle_vblank(struct zynqmp_disp *disp); > > +unsigned int zynqmp_disp_get_apb_clk_rate(struct zynqmp_disp *disp); > > +bool zynqmp_disp_aud_enabled(struct zynqmp_disp *disp); > > +unsigned int zynqmp_disp_get_aud_clk_rate(struct zynqmp_disp *disp); > > +uint32_t zynqmp_disp_get_crtc_mask(struct zynqmp_disp *disp); > > + > > +int zynqmp_disp_bind(struct device *dev, struct device *master, void > *data); > > +void zynqmp_disp_unbind(struct device *dev, struct device *master, > void *data); > > + > > +int zynqmp_disp_probe(struct platform_device *pdev); > > +int zynqmp_disp_remove(struct platform_device *pdev); > > + > > +#endif /* _ZYNQMP_DISP_H_ */ > > -- > > 2.7.4 > > > > _______________________________________________ > > dri-devel mailing list > > dri-devel@lists.freedesktop.org > > https://lists.freedesktop.org/mailman/listinfo/dri-devel > > -- > Daniel Vetter > Software Engineer, Intel Corporation > http://blog.ffwll.ch
diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c new file mode 100644 index 0000000..68f829c --- /dev/null +++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c @@ -0,0 +1,2935 @@ +/* + * ZynqMP Display Controller Driver + * + * Copyright (C) 2017 - 2018 Xilinx, Inc. + * + * Author: Hyun Woo Kwon <hyun.kwon@xilinx.com> + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <drm/drmP.h> +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_plane_helper.h> + +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/dmaengine.h> +#include <linux/interrupt.h> +#include <linux/irqreturn.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/of_dma.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/spinlock.h> +#include <linux/uaccess.h> + +#include "xlnx_crtc.h" +#include "xlnx_fb.h" +#include "zynqmp_disp.h" +#include "zynqmp_dp.h" +#include "zynqmp_dpsub.h" + +/* + * Overview + * -------- + * + * The display part of ZynqMP DP subsystem. Internally, the device + * is partitioned into 3 blocks: AV buffer manager, Blender, Audio. + * The driver creates the DRM crtc and plane objectes and maps the DRM + * interface into those 3 blocks. In high level, the driver is layered + * in the following way: + * + * zynqmp_disp_crtc & zynqmp_disp_plane + * |->zynqmp_disp + * |->zynqmp_disp_aud + * |->zynqmp_disp_blend + * |->zynqmp_disp_av_buf + * + * The driver APIs are used externally by + * - zynqmp_dpsub: Top level ZynqMP DP subsystem driver + * - zynqmp_dp: ZynqMP DP driver + * - xlnx_crtc: Xilinx DRM specific crtc functions + */ + +/* Blender registers */ +#define ZYNQMP_DISP_V_BLEND_BG_CLR_0 0x0 +#define ZYNQMP_DISP_V_BLEND_BG_CLR_1 0x4 +#define ZYNQMP_DISP_V_BLEND_BG_CLR_2 0x8 +#define ZYNQMP_DISP_V_BLEND_BG_MAX 0xfff +#define ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA 0xc +#define ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA_MASK 0x1fe +#define ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA_MAX 0xff +#define ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT 0x14 +#define ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_RGB 0x0 +#define ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_YCBCR444 0x1 +#define ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_YCBCR422 0x2 +#define ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_YONLY 0x3 +#define ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_XVYCC 0x4 +#define ZYNQMP_DISP_V_BLEND_OUTPUT_EN_DOWNSAMPLE BIT(4) +#define ZYNQMP_DISP_V_BLEND_LAYER_CONTROL 0x18 +#define ZYNQMP_DISP_V_BLEND_LAYER_CONTROL_EN_US BIT(0) +#define ZYNQMP_DISP_V_BLEND_LAYER_CONTROL_RGB BIT(1) +#define ZYNQMP_DISP_V_BLEND_LAYER_CONTROL_BYPASS BIT(8) +#define ZYNQMP_DISP_V_BLEND_NUM_COEFF 9 +#define ZYNQMP_DISP_V_BLEND_RGB2YCBCR_COEFF0 0x20 +#define ZYNQMP_DISP_V_BLEND_RGB2YCBCR_COEFF1 0x24 +#define ZYNQMP_DISP_V_BLEND_RGB2YCBCR_COEFF2 0x28 +#define ZYNQMP_DISP_V_BLEND_RGB2YCBCR_COEFF3 0x2c +#define ZYNQMP_DISP_V_BLEND_RGB2YCBCR_COEFF4 0x30 +#define ZYNQMP_DISP_V_BLEND_RGB2YCBCR_COEFF5 0x34 +#define ZYNQMP_DISP_V_BLEND_RGB2YCBCR_COEFF6 0x38 +#define ZYNQMP_DISP_V_BLEND_RGB2YCBCR_COEFF7 0x3c +#define ZYNQMP_DISP_V_BLEND_RGB2YCBCR_COEFF8 0x40 +#define ZYNQMP_DISP_V_BLEND_IN1CSC_COEFF0 0x44 +#define ZYNQMP_DISP_V_BLEND_IN1CSC_COEFF1 0x48 +#define ZYNQMP_DISP_V_BLEND_IN1CSC_COEFF2 0x4c +#define ZYNQMP_DISP_V_BLEND_IN1CSC_COEFF3 0x50 +#define ZYNQMP_DISP_V_BLEND_IN1CSC_COEFF4 0x54 +#define ZYNQMP_DISP_V_BLEND_IN1CSC_COEFF5 0x58 +#define ZYNQMP_DISP_V_BLEND_IN1CSC_COEFF6 0x5c +#define ZYNQMP_DISP_V_BLEND_IN1CSC_COEFF7 0x60 +#define ZYNQMP_DISP_V_BLEND_IN1CSC_COEFF8 0x64 +#define ZYNQMP_DISP_V_BLEND_NUM_OFFSET 3 +#define ZYNQMP_DISP_V_BLEND_LUMA_IN1CSC_OFFSET 0x68 +#define ZYNQMP_DISP_V_BLEND_CR_IN1CSC_OFFSET 0x6c +#define ZYNQMP_DISP_V_BLEND_CB_IN1CSC_OFFSET 0x70 +#define ZYNQMP_DISP_V_BLEND_LUMA_OUTCSC_OFFSET 0x74 +#define ZYNQMP_DISP_V_BLEND_CR_OUTCSC_OFFSET 0x78 +#define ZYNQMP_DISP_V_BLEND_CB_OUTCSC_OFFSET 0x7c +#define ZYNQMP_DISP_V_BLEND_IN2CSC_COEFF0 0x80 +#define ZYNQMP_DISP_V_BLEND_IN2CSC_COEFF1 0x84 +#define ZYNQMP_DISP_V_BLEND_IN2CSC_COEFF2 0x88 +#define ZYNQMP_DISP_V_BLEND_IN2CSC_COEFF3 0x8c +#define ZYNQMP_DISP_V_BLEND_IN2CSC_COEFF4 0x90 +#define ZYNQMP_DISP_V_BLEND_IN2CSC_COEFF5 0x94 +#define ZYNQMP_DISP_V_BLEND_IN2CSC_COEFF6 0x98 +#define ZYNQMP_DISP_V_BLEND_IN2CSC_COEFF7 0x9c +#define ZYNQMP_DISP_V_BLEND_IN2CSC_COEFF8 0xa0 +#define ZYNQMP_DISP_V_BLEND_LUMA_IN2CSC_OFFSET 0xa4 +#define ZYNQMP_DISP_V_BLEND_CR_IN2CSC_OFFSET 0xa8 +#define ZYNQMP_DISP_V_BLEND_CB_IN2CSC_OFFSET 0xac +#define ZYNQMP_DISP_V_BLEND_CHROMA_KEY_ENABLE 0x1d0 +#define ZYNQMP_DISP_V_BLEND_CHROMA_KEY_COMP1 0x1d4 +#define ZYNQMP_DISP_V_BLEND_CHROMA_KEY_COMP2 0x1d8 +#define ZYNQMP_DISP_V_BLEND_CHROMA_KEY_COMP3 0x1dc + +/* AV buffer manager registers */ +#define ZYNQMP_DISP_AV_BUF_FMT 0x0 +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_SHIFT 0 +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_MASK (0x1f << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_UYVY (0 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_VYUY (1 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YVYU (2 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YUYV (3 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16 (4 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV24 (5 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI (6 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_MONO (7 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI2 (8 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YUV444 (9 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_RGB888 (10 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_RGBA8880 (11 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_RGB888_10 (12 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YUV444_10 (13 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI2_10 (14 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI_10 (15 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16_10 (16 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV24_10 (17 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YONLY_10 (18 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16_420 (19 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI_420 (20 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI2_420 (21 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16_420_10 (22 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI_420_10 (23 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI2_420_10 (24 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_SHIFT 8 +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_MASK (0xf << 8) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGBA8888 (0 << 8) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_ABGR8888 (1 << 8) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGB888 (2 << 8) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_BGR888 (3 << 8) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGBA5551 (4 << 8) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGBA4444 (5 << 8) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGB565 (6 << 8) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_8BPP (7 << 8) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_4BPP (8 << 8) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_2BPP (9 << 8) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_1BPP (10 << 8) +#define ZYNQMP_DISP_AV_BUF_NON_LIVE_LATENCY 0x8 +#define ZYNQMP_DISP_AV_BUF_CHBUF 0x10 +#define ZYNQMP_DISP_AV_BUF_CHBUF_EN BIT(0) +#define ZYNQMP_DISP_AV_BUF_CHBUF_FLUSH BIT(1) +#define ZYNQMP_DISP_AV_BUF_CHBUF_BURST_LEN_SHIFT 2 +#define ZYNQMP_DISP_AV_BUF_CHBUF_BURST_LEN_MASK (0xf << 2) +#define ZYNQMP_DISP_AV_BUF_CHBUF_BURST_LEN_MAX 0xf +#define ZYNQMP_DISP_AV_BUF_CHBUF_BURST_LEN_AUD_MAX 0x3 +#define ZYNQMP_DISP_AV_BUF_STATUS 0x28 +#define ZYNQMP_DISP_AV_BUF_STC_CTRL 0x2c +#define ZYNQMP_DISP_AV_BUF_STC_CTRL_EN BIT(0) +#define ZYNQMP_DISP_AV_BUF_STC_CTRL_EVENT_SHIFT 1 +#define ZYNQMP_DISP_AV_BUF_STC_CTRL_EVENT_EX_VSYNC 0 +#define ZYNQMP_DISP_AV_BUF_STC_CTRL_EVENT_EX_VID 1 +#define ZYNQMP_DISP_AV_BUF_STC_CTRL_EVENT_EX_AUD 2 +#define ZYNQMP_DISP_AV_BUF_STC_CTRL_EVENT_INT_VSYNC 3 +#define ZYNQMP_DISP_AV_BUF_STC_INIT_VALUE0 0x30 +#define ZYNQMP_DISP_AV_BUF_STC_INIT_VALUE1 0x34 +#define ZYNQMP_DISP_AV_BUF_STC_ADJ 0x38 +#define ZYNQMP_DISP_AV_BUF_STC_VID_VSYNC_TS0 0x3c +#define ZYNQMP_DISP_AV_BUF_STC_VID_VSYNC_TS1 0x40 +#define ZYNQMP_DISP_AV_BUF_STC_EXT_VSYNC_TS0 0x44 +#define ZYNQMP_DISP_AV_BUF_STC_EXT_VSYNC_TS1 0x48 +#define ZYNQMP_DISP_AV_BUF_STC_CUSTOM_EVENT_TS0 0x4c +#define ZYNQMP_DISP_AV_BUF_STC_CUSTOM_EVENT_TS1 0x50 +#define ZYNQMP_DISP_AV_BUF_STC_CUSTOM_EVENT2_TS0 0x54 +#define ZYNQMP_DISP_AV_BUF_STC_CUSTOM_EVENT2_TS1 0x58 +#define ZYNQMP_DISP_AV_BUF_STC_SNAPSHOT0 0x60 +#define ZYNQMP_DISP_AV_BUF_STC_SNAPSHOT1 0x64 +#define ZYNQMP_DISP_AV_BUF_OUTPUT 0x70 +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_SHIFT 0 +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_MASK (0x3 << 0) +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_LIVE (0 << 0) +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_MEM (1 << 0) +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_PATTERN (2 << 0) +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_NONE (3 << 0) +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_SHIFT 2 +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_MASK (0x3 << 2) +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_DISABLE (0 << 2) +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_MEM (1 << 2) +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_LIVE (2 << 2) +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_NONE (3 << 2) +#define ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_SHIFT 4 +#define ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_MASK (0x3 << 4) +#define ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_PL (0 << 4) +#define ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_MEM (1 << 4) +#define ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_PATTERN (2 << 4) +#define ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_DISABLE (3 << 4) +#define ZYNQMP_DISP_AV_BUF_OUTPUT_AUD2_EN BIT(6) +#define ZYNQMP_DISP_AV_BUF_HCOUNT_VCOUNT_INT0 0x74 +#define ZYNQMP_DISP_AV_BUF_HCOUNT_VCOUNT_INT1 0x78 +#define ZYNQMP_DISP_AV_BUF_PATTERN_GEN_SELECT 0x100 +#define ZYNQMP_DISP_AV_BUF_CLK_SRC 0x120 +#define ZYNQMP_DISP_AV_BUF_CLK_SRC_VID_FROM_PS BIT(0) +#define ZYNQMP_DISP_AV_BUF_CLK_SRC_AUD_FROM_PS BIT(1) +#define ZYNQMP_DISP_AV_BUF_CLK_SRC_VID_INTERNAL_TIMING BIT(2) +#define ZYNQMP_DISP_AV_BUF_SRST_REG 0x124 +#define ZYNQMP_DISP_AV_BUF_SRST_REG_VID_RST BIT(1) +#define ZYNQMP_DISP_AV_BUF_AUDIO_CH_CONFIG 0x12c +#define ZYNQMP_DISP_AV_BUF_GFX_COMP0_SF 0x200 +#define ZYNQMP_DISP_AV_BUF_GFX_COMP1_SF 0x204 +#define ZYNQMP_DISP_AV_BUF_GFX_COMP2_SF 0x208 +#define ZYNQMP_DISP_AV_BUF_VID_COMP0_SF 0x20c +#define ZYNQMP_DISP_AV_BUF_VID_COMP1_SF 0x210 +#define ZYNQMP_DISP_AV_BUF_VID_COMP2_SF 0x214 +#define ZYNQMP_DISP_AV_BUF_LIVE_VID_COMP0_SF 0x218 +#define ZYNQMP_DISP_AV_BUF_LIVE_VID_COMP1_SF 0x21c +#define ZYNQMP_DISP_AV_BUF_LIVE_VID_COMP2_SF 0x220 +#define ZYNQMP_DISP_AV_BUF_LIVE_VID_CONFIG 0x224 +#define ZYNQMP_DISP_AV_BUF_LIVE_GFX_COMP0_SF 0x228 +#define ZYNQMP_DISP_AV_BUF_LIVE_GFX_COMP1_SF 0x22c +#define ZYNQMP_DISP_AV_BUF_LIVE_GFX_COMP2_SF 0x230 +#define ZYNQMP_DISP_AV_BUF_LIVE_GFX_CONFIG 0x234 +#define ZYNQMP_DISP_AV_BUF_4BIT_SF 0x11111 +#define ZYNQMP_DISP_AV_BUF_5BIT_SF 0x10842 +#define ZYNQMP_DISP_AV_BUF_6BIT_SF 0x10410 +#define ZYNQMP_DISP_AV_BUF_8BIT_SF 0x10101 +#define ZYNQMP_DISP_AV_BUF_10BIT_SF 0x10040 +#define ZYNQMP_DISP_AV_BUF_NULL_SF 0 +#define ZYNQMP_DISP_AV_BUF_NUM_SF 3 +#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_6 0x0 +#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_8 0x1 +#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_10 0x2 +#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_12 0x3 +#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_MASK GENMASK(2, 0) +#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_RGB 0x0 +#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_YUV444 0x1 +#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_YUV422 0x2 +#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_YONLY 0x3 +#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_MASK GENMASK(5, 4) +#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_CB_FIRST BIT(8) +#define ZYNQMP_DISP_AV_BUF_PALETTE_MEMORY 0x400 + +/* Audio registers */ +#define ZYNQMP_DISP_AUD_MIXER_VOLUME 0x0 +#define ZYNQMP_DISP_AUD_MIXER_VOLUME_NO_SCALE 0x20002000 +#define ZYNQMP_DISP_AUD_MIXER_META_DATA 0x4 +#define ZYNQMP_DISP_AUD_CH_STATUS0 0x8 +#define ZYNQMP_DISP_AUD_CH_STATUS1 0xc +#define ZYNQMP_DISP_AUD_CH_STATUS2 0x10 +#define ZYNQMP_DISP_AUD_CH_STATUS3 0x14 +#define ZYNQMP_DISP_AUD_CH_STATUS4 0x18 +#define ZYNQMP_DISP_AUD_CH_STATUS5 0x1c +#define ZYNQMP_DISP_AUD_CH_A_DATA0 0x20 +#define ZYNQMP_DISP_AUD_CH_A_DATA1 0x24 +#define ZYNQMP_DISP_AUD_CH_A_DATA2 0x28 +#define ZYNQMP_DISP_AUD_CH_A_DATA3 0x2c +#define ZYNQMP_DISP_AUD_CH_A_DATA4 0x30 +#define ZYNQMP_DISP_AUD_CH_A_DATA5 0x34 +#define ZYNQMP_DISP_AUD_CH_B_DATA0 0x38 +#define ZYNQMP_DISP_AUD_CH_B_DATA1 0x3c +#define ZYNQMP_DISP_AUD_CH_B_DATA2 0x40 +#define ZYNQMP_DISP_AUD_CH_B_DATA3 0x44 +#define ZYNQMP_DISP_AUD_CH_B_DATA4 0x48 +#define ZYNQMP_DISP_AUD_CH_B_DATA5 0x4c +#define ZYNQMP_DISP_AUD_SOFT_RESET 0xc00 +#define ZYNQMP_DISP_AUD_SOFT_RESET_AUD_SRST BIT(0) + +#define ZYNQMP_DISP_AV_BUF_NUM_VID_GFX_BUFFERS 4 +#define ZYNQMP_DISP_AV_BUF_NUM_BUFFERS 6 + +#define ZYNQMP_DISP_NUM_LAYERS 2 +#define ZYNQMP_DISP_MAX_NUM_SUB_PLANES 3 +/* + * 3840x2160 is advertised max resolution, but almost any resolutions under + * 300Mhz pixel rate would work. Thus put 4096 as maximum width and height. + */ +#define ZYNQMP_DISP_MAX_WIDTH 4096 +#define ZYNQMP_DISP_MAX_HEIGHT 4096 +/* 44 bit addressing. This is acutally DPDMA limitation */ +#define ZYNQMP_DISP_MAX_DMA_BIT 44 + +/** + * enum zynqmp_disp_layer_type - Layer type (can be used for hw ID) + * @ZYNQMP_DISP_LAYER_VID: Video layer + * @ZYNQMP_DISP_LAYER_GFX: Graphics layer + */ +enum zynqmp_disp_layer_type { + ZYNQMP_DISP_LAYER_VID, + ZYNQMP_DISP_LAYER_GFX +}; + +/** + * enum zynqmp_disp_layer_mode - Layer mode + * @ZYNQMP_DISP_LAYER_NONLIVE: non-live (memory) mode + * @ZYNQMP_DISP_LAYER_LIVE: live (stream) mode + */ +enum zynqmp_disp_layer_mode { + ZYNQMP_DISP_LAYER_NONLIVE, + ZYNQMP_DISP_LAYER_LIVE +}; + +/** + * struct zynqmp_disp_layer_dma - struct for DMA engine + * @chan: DMA channel + * @is_active: flag if the DMA is active + * @xt: Interleaved desc config container + * @sgl: Data chunk for dma_interleaved_template + */ +struct zynqmp_disp_layer_dma { + struct dma_chan *chan; + bool is_active; + struct dma_interleaved_template xt; + struct data_chunk sgl[1]; +}; + +/** + * struct zynqmp_disp_layer - Display subsystem layer + * @plane: DRM plane + * @of_node: device node + * @dma: struct for DMA engine + * @num_chan: Number of DMA channel + * @id: Layer ID + * @offset: Layer offset in the register space + * @enabled: flag if enabled + * @fmt: Current format descriptor + * @drm_fmts: Array of supported DRM formats + * @num_fmts: Number of supported DRM formats + * @bus_fmts: Array of supported bus formats + * @num_bus_fmts: Number of supported bus formats + * @w: Width + * @h: Height + * @mode: the operation mode + * @other: other layer + * @disp: back pointer to struct zynqmp_disp + */ +struct zynqmp_disp_layer { + struct drm_plane plane; + struct device_node *of_node; + struct zynqmp_disp_layer_dma dma[ZYNQMP_DISP_MAX_NUM_SUB_PLANES]; + unsigned int num_chan; + enum zynqmp_disp_layer_type id; + u32 offset; + u8 enabled; + const struct zynqmp_disp_fmt *fmt; + u32 *drm_fmts; + unsigned int num_fmts; + u32 *bus_fmts; + unsigned int num_bus_fmts; + u32 w; + u32 h; + enum zynqmp_disp_layer_mode mode; + struct zynqmp_disp_layer *other; + struct zynqmp_disp *disp; +}; + +/** + * struct zynqmp_disp_blend - Blender + * @base: Base address offset + */ +struct zynqmp_disp_blend { + void __iomem *base; +}; + +/** + * struct zynqmp_disp_av_buf - AV buffer manager + * @base: Base address offset + */ +struct zynqmp_disp_av_buf { + void __iomem *base; +}; + +/** + * struct zynqmp_disp_aud - Audio + * @base: Base address offset + */ +struct zynqmp_disp_aud { + void __iomem *base; +}; + +/** + * struct zynqmp_disp - Display subsystem + * @xlnx_crtc: Xilinx DRM crtc + * @dev: device structure + * @dpsub: Display subsystem + * @drm: DRM core + * @enabled: flag if enabled + * @blend: Blender block + * @av_buf: AV buffer manager block + * @aud:Audio block + * @layers: layers + * @g_alpha_prop: global alpha property + * @alpha: current global alpha value + * @g_alpha_en_prop: the global alpha enable property + * @alpha_en: flag if the global alpha is enabled + * @color_prop: output color format property + * @color: current output color value + * @bg_c0_prop: 1st component of background color property + * @bg_c0: current value of 1st background color component + * @bg_c1_prop: 2nd component of background color property + * @bg_c1: current value of 2nd background color component + * @bg_c2_prop: 3rd component of background color property + * @bg_c2: current value of 3rd background color component + * @tpg_prop: Test Pattern Generation mode property + * @tpg_on: current TPG mode state + * @event: pending vblank event request + * @_ps_pclk: Pixel clock from PS + * @_pl_pclk: Pixel clock from PL + * @pclk: Pixel clock + * @pclk_en: Flag if the pixel clock is enabled + * @_ps_audclk: Audio clock from PS + * @_pl_audclk: Audio clock from PL + * @audclk: Audio clock + * @audclk_en: Flag if the audio clock is enabled + * @aclk: APB clock + * @aclk_en: Flag if the APB clock is enabled + */ +struct zynqmp_disp { + struct xlnx_crtc xlnx_crtc; + struct device *dev; + struct zynqmp_dpsub *dpsub; + struct drm_device *drm; + bool enabled; + struct zynqmp_disp_blend blend; + struct zynqmp_disp_av_buf av_buf; + struct zynqmp_disp_aud aud; + struct zynqmp_disp_layer layers[ZYNQMP_DISP_NUM_LAYERS]; + struct drm_property *g_alpha_prop; + u32 alpha; + struct drm_property *g_alpha_en_prop; + bool alpha_en; + struct drm_property *color_prop; + unsigned int color; + struct drm_property *bg_c0_prop; + u32 bg_c0; + struct drm_property *bg_c1_prop; + u32 bg_c1; + struct drm_property *bg_c2_prop; + u32 bg_c2; + struct drm_property *tpg_prop; + bool tpg_on; + struct drm_pending_vblank_event *event; + /* Don't operate directly on _ps_ */ + struct clk *_ps_pclk; + struct clk *_pl_pclk; + struct clk *pclk; + bool pclk_en; + struct clk *_ps_audclk; + struct clk *_pl_audclk; + struct clk *audclk; + bool audclk_en; + struct clk *aclk; + bool aclk_en; +}; + +/** + * struct zynqmp_disp_fmt - Display subsystem format mapping + * @drm_fmt: drm format + * @disp_fmt: Display subsystem format + * @bus_fmt: Bus formats (live formats) + * @rgb: flag for RGB formats + * @swap: flag to swap r & b for rgb formats, and u & v for yuv formats + * @chroma_sub: flag for chroma subsampled formats + * @sf: scaling factors for upto 3 color components + */ +struct zynqmp_disp_fmt { + u32 drm_fmt; + u32 disp_fmt; + u32 bus_fmt; + bool rgb; + bool swap; + bool chroma_sub; + u32 sf[3]; +}; + +static void zynqmp_disp_write(void __iomem *base, int offset, u32 val) +{ + writel(val, base + offset); +} + +static u32 zynqmp_disp_read(void __iomem *base, int offset) +{ + return readl(base + offset); +} + +static void zynqmp_disp_clr(void __iomem *base, int offset, u32 clr) +{ + zynqmp_disp_write(base, offset, zynqmp_disp_read(base, offset) & ~clr); +} + +static void zynqmp_disp_set(void __iomem *base, int offset, u32 set) +{ + zynqmp_disp_write(base, offset, zynqmp_disp_read(base, offset) | set); +} + +/* + * Clock functions + */ + +/** + * zynqmp_disp_clk_enable - Enable the clock if needed + * @clk: clk device + * @flag: flag if the clock is enabled + * + * Enable the clock only if it's not enabled @flag. + * + * Return: value from clk_prepare_enable(). + */ +static int zynqmp_disp_clk_enable(struct clk *clk, bool *flag) +{ + int ret = 0; + + if (!*flag) { + ret = clk_prepare_enable(clk); + if (!ret) + *flag = true; + } + + return ret; +} + +/** + * zynqmp_disp_clk_enable - Enable the clock if needed + * @clk: clk device + * @flag: flag if the clock is enabled + * + * Disable the clock only if it's enabled @flag. + */ +static void zynqmp_disp_clk_disable(struct clk *clk, bool *flag) +{ + if (*flag) { + clk_disable_unprepare(clk); + *flag = false; + } +} + +/** + * zynqmp_disp_clk_enable - Enable and disable the clock + * @clk: clk device + * @flag: flag if the clock is enabled + * + * This is to ensure the clock is disabled. The initial hardware state is + * unknown, and this makes sure that the clock is disabled. + * + * Return: value from clk_prepare_enable(). + */ +static int zynqmp_disp_clk_enable_disable(struct clk *clk, bool *flag) +{ + int ret = 0; + + if (!*flag) { + ret = clk_prepare_enable(clk); + clk_disable_unprepare(clk); + } + + return ret; +} + +/* + * Blender functions + */ + +/** + * zynqmp_disp_blend_set_output_fmt - Set the output format of the blend + * @blend: blend object + * @fmt: output format + * + * Set the output format to @fmt. + */ +static void +zynqmp_disp_blend_set_output_fmt(struct zynqmp_disp_blend *blend, u32 fmt) +{ + u16 reset_coeffs[] = { 0x1000, 0x0, 0x0, + 0x0, 0x1000, 0x0, + 0x0, 0x0, 0x1000 }; + u32 reset_offsets[] = { 0x0, 0x0, 0x0 }; + u16 sdtv_coeffs[] = { 0x4c9, 0x864, 0x1d3, + 0x7d4d, 0x7ab3, 0x800, + 0x800, 0x794d, 0x7eb3 }; + u32 full_range_offsets[] = { 0x0, 0x8000000, 0x8000000 }; + u16 *coeffs; + u32 *offsets; + u32 offset, i; + + zynqmp_disp_write(blend->base, ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT, fmt); + if (fmt == ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_RGB) { + coeffs = reset_coeffs; + offsets = reset_offsets; + } else { + /* Hardcode Full-range SDTV values. Can be runtime config */ + coeffs = sdtv_coeffs; + offsets = full_range_offsets; + } + + offset = ZYNQMP_DISP_V_BLEND_RGB2YCBCR_COEFF0; + for (i = 0; i < ZYNQMP_DISP_V_BLEND_NUM_COEFF; i++) + zynqmp_disp_write(blend->base, offset + i * 4, coeffs[i]); + + offset = ZYNQMP_DISP_V_BLEND_LUMA_OUTCSC_OFFSET; + for (i = 0; i < ZYNQMP_DISP_V_BLEND_NUM_OFFSET; i++) + zynqmp_disp_write(blend->base, offset + i * 4, offsets[i]); +} + +/** + * zynqmp_disp_blend_layer_enable - Enable a layer + * @blend: blend object + * @layer: layer to enable + * + * Enable a layer @layer. + */ +static void zynqmp_disp_blend_layer_enable(struct zynqmp_disp_blend *blend, + struct zynqmp_disp_layer *layer) +{ + u32 reg, offset, i, s0, s1; + u16 sdtv_coeffs[] = { 0x1000, 0x166f, 0x0, + 0x1000, 0x7483, 0x7a7f, + 0x1000, 0x0, 0x1c5a }; + u16 swap_coeffs[] = { 0x1000, 0x0, 0x0, + 0x0, 0x1000, 0x0, + 0x0, 0x0, 0x1000 }; + u16 *coeffs; + u32 offsets[] = { 0x0, 0x1800, 0x1800 }; + + reg = layer->fmt->rgb ? ZYNQMP_DISP_V_BLEND_LAYER_CONTROL_RGB : 0; + reg |= layer->fmt->chroma_sub ? + ZYNQMP_DISP_V_BLEND_LAYER_CONTROL_EN_US : 0; + + zynqmp_disp_write(blend->base, + ZYNQMP_DISP_V_BLEND_LAYER_CONTROL + layer->offset, + reg); + + if (layer->id == ZYNQMP_DISP_LAYER_VID) + offset = ZYNQMP_DISP_V_BLEND_IN1CSC_COEFF0; + else + offset = ZYNQMP_DISP_V_BLEND_IN2CSC_COEFF0; + + if (!layer->fmt->rgb) { + coeffs = sdtv_coeffs; + s0 = 1; + s1 = 2; + } else { + coeffs = swap_coeffs; + s0 = 0; + s1 = 2; + + /* No offset for RGB formats */ + for (i = 0; i < ZYNQMP_DISP_V_BLEND_NUM_OFFSET; i++) + offsets[i] = 0; + } + + if (layer->fmt->swap) { + for (i = 0; i < 3; i++) { + coeffs[i * 3 + s0] ^= coeffs[i * 3 + s1]; + coeffs[i * 3 + s1] ^= coeffs[i * 3 + s0]; + coeffs[i * 3 + s0] ^= coeffs[i * 3 + s1]; + } + } + + /* Program coefficients. Can be runtime configurable */ + for (i = 0; i < ZYNQMP_DISP_V_BLEND_NUM_COEFF; i++) + zynqmp_disp_write(blend->base, offset + i * 4, coeffs[i]); + + if (layer->id == ZYNQMP_DISP_LAYER_VID) + offset = ZYNQMP_DISP_V_BLEND_LUMA_IN1CSC_OFFSET; + else + offset = ZYNQMP_DISP_V_BLEND_LUMA_IN2CSC_OFFSET; + + /* Program offsets. Can be runtime configurable */ + for (i = 0; i < ZYNQMP_DISP_V_BLEND_NUM_OFFSET; i++) + zynqmp_disp_write(blend->base, offset + i * 4, offsets[i]); +} + +/** + * zynqmp_disp_blend_layer_disable - Disable a layer + * @blend: blend object + * @layer: layer to disable + * + * Disable a layer @layer. + */ +static void zynqmp_disp_blend_layer_disable(struct zynqmp_disp_blend *blend, + struct zynqmp_disp_layer *layer) +{ + u32 offset; + unsigned int i; + + zynqmp_disp_write(blend->base, + ZYNQMP_DISP_V_BLEND_LAYER_CONTROL + layer->offset, 0); + + if (layer->id == ZYNQMP_DISP_LAYER_VID) + offset = ZYNQMP_DISP_V_BLEND_IN1CSC_COEFF0; + else + offset = ZYNQMP_DISP_V_BLEND_IN2CSC_COEFF0; + for (i = 0; i < ZYNQMP_DISP_V_BLEND_NUM_COEFF; i++) + zynqmp_disp_write(blend->base, offset + i * 4, 0); + + if (layer->id == ZYNQMP_DISP_LAYER_VID) + offset = ZYNQMP_DISP_V_BLEND_LUMA_IN1CSC_OFFSET; + else + offset = ZYNQMP_DISP_V_BLEND_LUMA_IN2CSC_OFFSET; + + for (i = 0; i < ZYNQMP_DISP_V_BLEND_NUM_OFFSET; i++) + zynqmp_disp_write(blend->base, offset + i * 4, 0); +} + +/** + * zynqmp_disp_blend_set_bg_color - Set the background color + * @blend: blend object + * @c0: color component 0 + * @c1: color component 1 + * @c2: color component 2 + * + * Set the background color. + */ +static void zynqmp_disp_blend_set_bg_color(struct zynqmp_disp_blend *blend, + u32 c0, u32 c1, u32 c2) +{ + zynqmp_disp_write(blend->base, ZYNQMP_DISP_V_BLEND_BG_CLR_0, c0); + zynqmp_disp_write(blend->base, ZYNQMP_DISP_V_BLEND_BG_CLR_1, c1); + zynqmp_disp_write(blend->base, ZYNQMP_DISP_V_BLEND_BG_CLR_2, c2); +} + +/** + * zynqmp_disp_blend_set_alpha - Set the alpha for blending + * @blend: blend object + * @alpha: alpha value to be used + * + * Set the alpha for blending. + */ +static void +zynqmp_disp_blend_set_alpha(struct zynqmp_disp_blend *blend, u32 alpha) +{ + u32 reg; + + reg = zynqmp_disp_read(blend->base, + ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA); + reg &= ~ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA_MASK; + reg |= alpha << 1; + zynqmp_disp_write(blend->base, ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA, + reg); +} + +/** + * zynqmp_disp_blend_enable_alpha - Enable/disable the global alpha + * @blend: blend object + * @enable: flag to enable or disable alpha blending + * + * Enable/disable the global alpha blending based on @enable. + */ +static void +zynqmp_disp_blend_enable_alpha(struct zynqmp_disp_blend *blend, bool enable) +{ + if (enable) + zynqmp_disp_set(blend->base, + ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA, BIT(0)); + else + zynqmp_disp_clr(blend->base, + ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA, BIT(0)); +} + +/* List of blend output formats */ +/* The id / order should be aligned with zynqmp_disp_color_enum */ +static const struct zynqmp_disp_fmt blend_output_fmts[] = { + { + .disp_fmt = ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_RGB, + }, { + .disp_fmt = ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_YCBCR444, + }, { + .disp_fmt = ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_YCBCR422, + }, { + .disp_fmt = ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_YONLY, + } +}; + +/* + * AV buffer manager functions + */ + +/* List of video layer formats */ +#define ZYNQMP_DISP_AV_BUF_VID_FMT_YUYV 2 +static const struct zynqmp_disp_fmt av_buf_vid_fmts[] = { + { + .drm_fmt = DRM_FORMAT_VYUY, + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_VYUY, + .rgb = false, + .swap = true, + .chroma_sub = true, + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + }, { + .drm_fmt = DRM_FORMAT_UYVY, + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_VYUY, + .rgb = false, + .swap = false, + .chroma_sub = true, + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + }, { + .drm_fmt = DRM_FORMAT_YUYV, + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YUYV, + .rgb = false, + .swap = false, + .chroma_sub = true, + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + }, { + .drm_fmt = DRM_FORMAT_YVYU, + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YUYV, + .rgb = false, + .swap = true, + .chroma_sub = true, + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + }, { + .drm_fmt = DRM_FORMAT_YUV422, + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16, + .rgb = false, + .swap = false, + .chroma_sub = true, + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + }, { + .drm_fmt = DRM_FORMAT_YVU422, + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16, + .rgb = false, + .swap = true, + .chroma_sub = true, + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + }, { + .drm_fmt = DRM_FORMAT_YUV444, + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV24, + .rgb = false, + .swap = false, + .chroma_sub = false, + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + }, { + .drm_fmt = DRM_FORMAT_YVU444, + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV24, + .rgb = false, + .swap = true, + .chroma_sub = false, + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + }, { + .drm_fmt = DRM_FORMAT_NV16, + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI, + .rgb = false, + .swap = false, + .chroma_sub = true, + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + }, { + .drm_fmt = DRM_FORMAT_NV61, + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI, + .rgb = false, + .swap = true, + .chroma_sub = true, + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + }, { + .drm_fmt = DRM_FORMAT_BGR888, + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_RGB888, + .rgb = true, + .swap = false, + .chroma_sub = false, + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + }, { + .drm_fmt = DRM_FORMAT_RGB888, + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_RGB888, + .rgb = true, + .swap = true, + .chroma_sub = false, + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + }, { + .drm_fmt = DRM_FORMAT_XBGR8888, + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_RGBA8880, + .rgb = true, + .swap = false, + .chroma_sub = false, + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + }, { + .drm_fmt = DRM_FORMAT_XRGB8888, + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_RGBA8880, + .rgb = true, + .swap = true, + .chroma_sub = false, + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + }, { + .drm_fmt = DRM_FORMAT_XBGR2101010, + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_RGB888_10, + .rgb = true, + .swap = false, + .chroma_sub = false, + .sf[0] = ZYNQMP_DISP_AV_BUF_10BIT_SF, + .sf[1] = ZYNQMP_DISP_AV_BUF_10BIT_SF, + .sf[2] = ZYNQMP_DISP_AV_BUF_10BIT_SF, + }, { + .drm_fmt = DRM_FORMAT_XRGB2101010, + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_RGB888_10, + .rgb = true, + .swap = true, + .chroma_sub = false, + .sf[0] = ZYNQMP_DISP_AV_BUF_10BIT_SF, + .sf[1] = ZYNQMP_DISP_AV_BUF_10BIT_SF, + .sf[2] = ZYNQMP_DISP_AV_BUF_10BIT_SF, + }, { + .drm_fmt = DRM_FORMAT_YUV420, + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16_420, + .rgb = false, + .swap = false, + .chroma_sub = true, + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + }, { + .drm_fmt = DRM_FORMAT_YVU420, + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16_420, + .rgb = false, + .swap = true, + .chroma_sub = true, + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + }, { + .drm_fmt = DRM_FORMAT_NV12, + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI_420, + .rgb = false, + .swap = false, + .chroma_sub = true, + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + }, { + .drm_fmt = DRM_FORMAT_NV21, + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI_420, + .rgb = false, + .swap = true, + .chroma_sub = true, + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + } +}; + +/* List of graphics layer formats */ +#define ZYNQMP_DISP_AV_BUF_GFX_FMT_RGB565 10 +static const struct zynqmp_disp_fmt av_buf_gfx_fmts[] = { + { + .drm_fmt = DRM_FORMAT_ABGR8888, + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGBA8888, + .rgb = true, + .swap = false, + .chroma_sub = false, + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + }, { + .drm_fmt = DRM_FORMAT_ARGB8888, + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGBA8888, + .rgb = true, + .swap = true, + .chroma_sub = false, + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + }, { + .drm_fmt = DRM_FORMAT_RGBA8888, + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_ABGR8888, + .rgb = true, + .swap = false, + .chroma_sub = false, + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + }, { + .drm_fmt = DRM_FORMAT_BGRA8888, + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_ABGR8888, + .rgb = true, + .swap = true, + .chroma_sub = false, + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + }, { + .drm_fmt = DRM_FORMAT_BGR888, + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGB888, + .rgb = true, + .swap = false, + .chroma_sub = false, + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + }, { + .drm_fmt = DRM_FORMAT_RGB888, + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_BGR888, + .rgb = true, + .swap = false, + .chroma_sub = false, + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + }, { + .drm_fmt = DRM_FORMAT_RGBA5551, + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGBA5551, + .rgb = true, + .swap = false, + .chroma_sub = false, + .sf[0] = ZYNQMP_DISP_AV_BUF_5BIT_SF, + .sf[1] = ZYNQMP_DISP_AV_BUF_5BIT_SF, + .sf[2] = ZYNQMP_DISP_AV_BUF_5BIT_SF, + }, { + .drm_fmt = DRM_FORMAT_BGRA5551, + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGBA5551, + .rgb = true, + .swap = true, + .chroma_sub = false, + .sf[0] = ZYNQMP_DISP_AV_BUF_5BIT_SF, + .sf[1] = ZYNQMP_DISP_AV_BUF_5BIT_SF, + .sf[2] = ZYNQMP_DISP_AV_BUF_5BIT_SF, + }, { + .drm_fmt = DRM_FORMAT_RGBA4444, + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGBA4444, + .rgb = true, + .swap = false, + .chroma_sub = false, + .sf[0] = ZYNQMP_DISP_AV_BUF_4BIT_SF, + .sf[1] = ZYNQMP_DISP_AV_BUF_4BIT_SF, + .sf[2] = ZYNQMP_DISP_AV_BUF_4BIT_SF, + }, { + .drm_fmt = DRM_FORMAT_BGRA4444, + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGBA4444, + .rgb = true, + .swap = true, + .chroma_sub = false, + .sf[0] = ZYNQMP_DISP_AV_BUF_4BIT_SF, + .sf[1] = ZYNQMP_DISP_AV_BUF_4BIT_SF, + .sf[2] = ZYNQMP_DISP_AV_BUF_4BIT_SF, + }, { + .drm_fmt = DRM_FORMAT_RGB565, + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGB565, + .rgb = true, + .swap = false, + .chroma_sub = false, + .sf[0] = ZYNQMP_DISP_AV_BUF_5BIT_SF, + .sf[1] = ZYNQMP_DISP_AV_BUF_6BIT_SF, + .sf[2] = ZYNQMP_DISP_AV_BUF_5BIT_SF, + }, { + .drm_fmt = DRM_FORMAT_BGR565, + .disp_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGB565, + .rgb = true, + .swap = true, + .chroma_sub = false, + .sf[0] = ZYNQMP_DISP_AV_BUF_5BIT_SF, + .sf[1] = ZYNQMP_DISP_AV_BUF_6BIT_SF, + .sf[2] = ZYNQMP_DISP_AV_BUF_5BIT_SF, + } +}; + +/* List of live formats */ +/* Format can be combination of color, bpc, and cb-cr order. + * - Color: RGB / YUV444 / YUV422 / Y only + * - BPC: 6, 8, 10, 12 + * - Swap: Cb and Cr swap + * which can be 32 bus formats. Only list the subset of those for now. + */ +static const struct zynqmp_disp_fmt av_buf_live_fmts[] = { + { + .bus_fmt = MEDIA_BUS_FMT_RGB666_1X18, + .disp_fmt = ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_6 || + ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_RGB, + .rgb = true, + .swap = false, + .chroma_sub = false, + .sf[0] = ZYNQMP_DISP_AV_BUF_6BIT_SF, + .sf[1] = ZYNQMP_DISP_AV_BUF_6BIT_SF, + .sf[2] = ZYNQMP_DISP_AV_BUF_6BIT_SF, + }, { + .bus_fmt = MEDIA_BUS_FMT_RBG888_1X24, + .disp_fmt = ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_8 || + ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_RGB, + .rgb = true, + .swap = false, + .chroma_sub = false, + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + }, { + .bus_fmt = MEDIA_BUS_FMT_UYVY8_1X16, + .disp_fmt = ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_8 || + ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_YUV422, + .rgb = false, + .swap = false, + .chroma_sub = true, + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + }, { + .bus_fmt = MEDIA_BUS_FMT_VUY8_1X24, + .disp_fmt = ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_8 || + ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_YUV444, + .rgb = false, + .swap = false, + .chroma_sub = false, + .sf[0] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[1] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + .sf[2] = ZYNQMP_DISP_AV_BUF_8BIT_SF, + }, { + .bus_fmt = MEDIA_BUS_FMT_UYVY10_1X20, + .disp_fmt = ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_10 || + ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_YUV422, + .rgb = false, + .swap = false, + .chroma_sub = true, + .sf[0] = ZYNQMP_DISP_AV_BUF_10BIT_SF, + .sf[1] = ZYNQMP_DISP_AV_BUF_10BIT_SF, + .sf[2] = ZYNQMP_DISP_AV_BUF_10BIT_SF, + } +}; + +/** + * zynqmp_disp_av_buf_set_fmt - Set the input formats + * @av_buf: av buffer manager + * @fmt: formats + * + * Set the av buffer manager format to @fmt. @fmt should have valid values + * for both video and graphics layer. + */ +static void +zynqmp_disp_av_buf_set_fmt(struct zynqmp_disp_av_buf *av_buf, u32 fmt) +{ + zynqmp_disp_write(av_buf->base, ZYNQMP_DISP_AV_BUF_FMT, fmt); +} + +/** + * zynqmp_disp_av_buf_get_fmt - Get the input formats + * @av_buf: av buffer manager + * + * Get the input formats (which include video and graphics) of + * av buffer manager. + * + * Return: value of ZYNQMP_DISP_AV_BUF_FMT register. + */ +static u32 +zynqmp_disp_av_buf_get_fmt(struct zynqmp_disp_av_buf *av_buf) +{ + return zynqmp_disp_read(av_buf->base, ZYNQMP_DISP_AV_BUF_FMT); +} + +/** + * zynqmp_disp_av_buf_set_vid_clock_src - Set the video clock source + * @av_buf: av buffer manager + * @from_ps: flag if the video clock is from ps + * + * Set the video clock source based on @from_ps. It can come from either PS or + * PL. + */ +static void +zynqmp_disp_av_buf_set_vid_clock_src(struct zynqmp_disp_av_buf *av_buf, + bool from_ps) +{ + u32 reg = zynqmp_disp_read(av_buf->base, ZYNQMP_DISP_AV_BUF_CLK_SRC); + + if (from_ps) + reg |= ZYNQMP_DISP_AV_BUF_CLK_SRC_VID_FROM_PS; + else + reg &= ~ZYNQMP_DISP_AV_BUF_CLK_SRC_VID_FROM_PS; + zynqmp_disp_write(av_buf->base, ZYNQMP_DISP_AV_BUF_CLK_SRC, reg); +} + +/** + * zynqmp_disp_av_buf_set_vid_timing_src - Set the video timing source + * @av_buf: av buffer manager + * @internal: flag if the video timing is generated internally + * + * Set the video timing source based on @internal. It can come externally or + * be generated internally. + */ +static void +zynqmp_disp_av_buf_set_vid_timing_src(struct zynqmp_disp_av_buf *av_buf, + bool internal) +{ + u32 reg = zynqmp_disp_read(av_buf->base, ZYNQMP_DISP_AV_BUF_CLK_SRC); + + if (internal) + reg |= ZYNQMP_DISP_AV_BUF_CLK_SRC_VID_INTERNAL_TIMING; + else + reg &= ~ZYNQMP_DISP_AV_BUF_CLK_SRC_VID_INTERNAL_TIMING; + zynqmp_disp_write(av_buf->base, ZYNQMP_DISP_AV_BUF_CLK_SRC, reg); +} + +/** + * zynqmp_disp_av_buf_set_aud_clock_src - Set the audio clock source + * @av_buf: av buffer manager + * @from_ps: flag if the video clock is from ps + * + * Set the audio clock source based on @from_ps. It can come from either PS or + * PL. + */ +static void +zynqmp_disp_av_buf_set_aud_clock_src(struct zynqmp_disp_av_buf *av_buf, + bool from_ps) +{ + u32 reg = zynqmp_disp_read(av_buf->base, ZYNQMP_DISP_AV_BUF_CLK_SRC); + + if (from_ps) + reg |= ZYNQMP_DISP_AV_BUF_CLK_SRC_AUD_FROM_PS; + else + reg &= ~ZYNQMP_DISP_AV_BUF_CLK_SRC_AUD_FROM_PS; + zynqmp_disp_write(av_buf->base, ZYNQMP_DISP_AV_BUF_CLK_SRC, reg); +} + +/** + * zynqmp_disp_av_buf_enable_buf - Enable buffers + * @av_buf: av buffer manager + * + * Enable all (video and audio) buffers. + */ +static void +zynqmp_disp_av_buf_enable_buf(struct zynqmp_disp_av_buf *av_buf) +{ + u32 reg, i; + + reg = ZYNQMP_DISP_AV_BUF_CHBUF_EN; + reg |= ZYNQMP_DISP_AV_BUF_CHBUF_BURST_LEN_MAX << + ZYNQMP_DISP_AV_BUF_CHBUF_BURST_LEN_SHIFT; + + for (i = 0; i < ZYNQMP_DISP_AV_BUF_NUM_VID_GFX_BUFFERS; i++) + zynqmp_disp_write(av_buf->base, + ZYNQMP_DISP_AV_BUF_CHBUF + i * 4, reg); + + reg = ZYNQMP_DISP_AV_BUF_CHBUF_EN; + reg |= ZYNQMP_DISP_AV_BUF_CHBUF_BURST_LEN_AUD_MAX << + ZYNQMP_DISP_AV_BUF_CHBUF_BURST_LEN_SHIFT; + + for (; i < ZYNQMP_DISP_AV_BUF_NUM_BUFFERS; i++) + zynqmp_disp_write(av_buf->base, + ZYNQMP_DISP_AV_BUF_CHBUF + i * 4, reg); +} + +/** + * zynqmp_disp_av_buf_disable_buf - Disable buffers + * @av_buf: av buffer manager + * + * Disable all (video and audio) buffers. + */ +static void +zynqmp_disp_av_buf_disable_buf(struct zynqmp_disp_av_buf *av_buf) +{ + u32 reg, i; + + reg = ZYNQMP_DISP_AV_BUF_CHBUF_FLUSH & ~ZYNQMP_DISP_AV_BUF_CHBUF_EN; + for (i = 0; i < ZYNQMP_DISP_AV_BUF_NUM_BUFFERS; i++) + zynqmp_disp_write(av_buf->base, + ZYNQMP_DISP_AV_BUF_CHBUF + i * 4, reg); +} + +/** + * zynqmp_disp_av_buf_enable_aud - Enable audio + * @av_buf: av buffer manager + * + * Enable all audio buffers. + */ +static void +zynqmp_disp_av_buf_enable_aud(struct zynqmp_disp_av_buf *av_buf) +{ + u32 reg; + + reg = zynqmp_disp_read(av_buf->base, ZYNQMP_DISP_AV_BUF_OUTPUT); + reg &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_MASK; + reg |= ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_MEM; + reg |= ZYNQMP_DISP_AV_BUF_OUTPUT_AUD2_EN; + zynqmp_disp_write(av_buf->base, ZYNQMP_DISP_AV_BUF_OUTPUT, reg); +} + +/** + * zynqmp_disp_av_buf_enable - Enable the video pipe + * @av_buf: av buffer manager + * + * De-assert the video pipe reset + */ +static void +zynqmp_disp_av_buf_enable(struct zynqmp_disp_av_buf *av_buf) +{ + zynqmp_disp_write(av_buf->base, ZYNQMP_DISP_AV_BUF_SRST_REG, 0); +} + +/** + * zynqmp_disp_av_buf_disable - Disable the video pipe + * @av_buf: av buffer manager + * + * Assert the video pipe reset + */ +static void +zynqmp_disp_av_buf_disable(struct zynqmp_disp_av_buf *av_buf) +{ + zynqmp_disp_write(av_buf->base, ZYNQMP_DISP_AV_BUF_SRST_REG, + ZYNQMP_DISP_AV_BUF_SRST_REG_VID_RST); +} + +/** + * zynqmp_disp_av_buf_disable_aud - Disable audio + * @av_buf: av buffer manager + * + * Disable all audio buffers. + */ +static void +zynqmp_disp_av_buf_disable_aud(struct zynqmp_disp_av_buf *av_buf) +{ + u32 reg; + + reg = zynqmp_disp_read(av_buf->base, ZYNQMP_DISP_AV_BUF_OUTPUT); + reg &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_MASK; + reg |= ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_DISABLE; + reg &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_AUD2_EN; + zynqmp_disp_write(av_buf->base, ZYNQMP_DISP_AV_BUF_OUTPUT, reg); +} + +/** + * zynqmp_disp_av_buf_set_tpg - Set TPG mode + * @av_buf: av buffer manager + * @tpg_on: if TPG should be on + * + * Set the TPG mode based on @tpg_on. + */ +static void zynqmp_disp_av_buf_set_tpg(struct zynqmp_disp_av_buf *av_buf, + bool tpg_on) +{ + u32 reg; + + reg = zynqmp_disp_read(av_buf->base, ZYNQMP_DISP_AV_BUF_OUTPUT); + reg &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_MASK; + if (tpg_on) + reg |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_PATTERN; + else + reg &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_PATTERN; + zynqmp_disp_write(av_buf->base, ZYNQMP_DISP_AV_BUF_OUTPUT, reg); +} + +/** + * zynqmp_disp_av_buf_enable_vid - Enable the video layer buffer + * @av_buf: av buffer manager + * @layer: layer to enable + * @mode: operation mode of layer + * + * Enable the video/graphics buffer for @layer. + */ +static void zynqmp_disp_av_buf_enable_vid(struct zynqmp_disp_av_buf *av_buf, + struct zynqmp_disp_layer *layer, + enum zynqmp_disp_layer_mode mode) +{ + u32 reg; + + reg = zynqmp_disp_read(av_buf->base, ZYNQMP_DISP_AV_BUF_OUTPUT); + if (layer->id == ZYNQMP_DISP_LAYER_VID) { + reg &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_MASK; + if (mode == ZYNQMP_DISP_LAYER_NONLIVE) + reg |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_MEM; + else + reg |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_LIVE; + } else { + reg &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_MASK; + reg |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_MEM; + if (mode == ZYNQMP_DISP_LAYER_NONLIVE) + reg |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_MEM; + else + reg |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_LIVE; + } + zynqmp_disp_write(av_buf->base, ZYNQMP_DISP_AV_BUF_OUTPUT, reg); +} + +/** + * zynqmp_disp_av_buf_disable_vid - Disable the video layer buffer + * @av_buf: av buffer manager + * @layer: layer to disable + * + * Disable the video/graphics buffer for @layer. + */ +static void +zynqmp_disp_av_buf_disable_vid(struct zynqmp_disp_av_buf *av_buf, + struct zynqmp_disp_layer *layer) +{ + u32 reg; + + reg = zynqmp_disp_read(av_buf->base, ZYNQMP_DISP_AV_BUF_OUTPUT); + if (layer->id == ZYNQMP_DISP_LAYER_VID) { + reg &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_MASK; + reg |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_NONE; + } else { + reg &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_MASK; + reg |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_DISABLE; + } + zynqmp_disp_write(av_buf->base, ZYNQMP_DISP_AV_BUF_OUTPUT, reg); +} + +/** + * zynqmp_disp_av_buf_init_sf - Initialize scaling factors + * @av_buf: av buffer manager + * @vid_fmt: video format descriptor + * @gfx_fmt: graphics format descriptor + * + * Initialize scaling factors for both video and graphics layers. + * If the format descriptor is NULL, the function skips the programming. + */ +static void zynqmp_disp_av_buf_init_sf(struct zynqmp_disp_av_buf *av_buf, + const struct zynqmp_disp_fmt *vid_fmt, + const struct zynqmp_disp_fmt *gfx_fmt) +{ + unsigned int i; + u32 offset; + + if (gfx_fmt) { + offset = ZYNQMP_DISP_AV_BUF_GFX_COMP0_SF; + for (i = 0; i < ZYNQMP_DISP_AV_BUF_NUM_SF; i++) + zynqmp_disp_write(av_buf->base, offset + i * 4, + gfx_fmt->sf[i]); + } + + if (vid_fmt) { + offset = ZYNQMP_DISP_AV_BUF_VID_COMP0_SF; + for (i = 0; i < ZYNQMP_DISP_AV_BUF_NUM_SF; i++) + zynqmp_disp_write(av_buf->base, offset + i * 4, + vid_fmt->sf[i]); + } +} + +/* + * Audio functions + */ + +/** + * zynqmp_disp_aud_init - Initialize the audio + * @aud: audio + * + * Initialize the audio with default mixer volume. The de-assertion will + * initialize the audio states. + */ +static void zynqmp_disp_aud_init(struct zynqmp_disp_aud *aud) +{ + /* Clear the audio soft reset register as it's an non-reset flop */ + zynqmp_disp_write(aud->base, ZYNQMP_DISP_AUD_SOFT_RESET, 0); + zynqmp_disp_write(aud->base, ZYNQMP_DISP_AUD_MIXER_VOLUME, + ZYNQMP_DISP_AUD_MIXER_VOLUME_NO_SCALE); +} + +/** + * zynqmp_disp_aud_deinit - De-initialize the audio + * @aud: audio + * + * Put the audio in reset. + */ +static void zynqmp_disp_aud_deinit(struct zynqmp_disp_aud *aud) +{ + zynqmp_disp_set(aud->base, ZYNQMP_DISP_AUD_SOFT_RESET, + ZYNQMP_DISP_AUD_SOFT_RESET_AUD_SRST); +} + +/* + * ZynqMP Display layer functions + */ + +/** + * zynqmp_disp_layer_check_size - Verify width and height for the layer + * @disp: Display subsystem + * @layer: layer + * @width: width + * @height: height + * + * The Display subsystem has the limitation that both layers should have + * identical size. This function stores width and height of @layer, and verifies + * if the size (width and height) is valid. + * + * Return: 0 on success, or -EINVAL if width or/and height is invalid. + */ +static int zynqmp_disp_layer_check_size(struct zynqmp_disp *disp, + struct zynqmp_disp_layer *layer, + u32 width, u32 height) +{ + struct zynqmp_disp_layer *other = layer->other; + + if (other->enabled && (other->w != width || other->h != height)) { + dev_err(disp->dev, "Layer width:height must be %d:%d\n", + other->w, other->h); + return -EINVAL; + } + + layer->w = width; + layer->h = height; + + return 0; +} + +/** + * zynqmp_disp_map_fmt - Find the Display subsystem format for given drm format + * @fmts: format table to look up + * @size: size of the table @fmts + * @drm_fmt: DRM format to search + * + * Search a Display subsystem format corresponding to the given DRM format + * @drm_fmt, and return the format descriptor which contains the Display + * subsystem format value. + * + * Return: a Display subsystem format descriptor on success, or NULL. + */ +static const struct zynqmp_disp_fmt * +zynqmp_disp_map_fmt(const struct zynqmp_disp_fmt fmts[], + unsigned int size, uint32_t drm_fmt) +{ + unsigned int i; + + for (i = 0; i < size; i++) + if (fmts[i].drm_fmt == drm_fmt) + return &fmts[i]; + + return NULL; +} + +/** + * zynqmp_disp_set_fmt - Set the format of the layer + * @disp: Display subsystem + * @layer: layer to set the format + * @drm_fmt: DRM format to set + * + * Set the format of the given layer to @drm_fmt. + * + * Return: 0 on success. -EINVAL if @drm_fmt is not supported by the layer. + */ +static int zynqmp_disp_layer_set_fmt(struct zynqmp_disp *disp, + struct zynqmp_disp_layer *layer, + uint32_t drm_fmt) +{ + const struct zynqmp_disp_fmt *fmt; + const struct zynqmp_disp_fmt *vid_fmt = NULL, *gfx_fmt = NULL; + u32 size, fmts, mask; + + if (layer->id == ZYNQMP_DISP_LAYER_VID) { + size = ARRAY_SIZE(av_buf_vid_fmts); + mask = ~ZYNQMP_DISP_AV_BUF_FMT_NL_VID_MASK; + fmt = zynqmp_disp_map_fmt(av_buf_vid_fmts, size, drm_fmt); + vid_fmt = fmt; + } else { + size = ARRAY_SIZE(av_buf_gfx_fmts); + mask = ~ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_MASK; + fmt = zynqmp_disp_map_fmt(av_buf_gfx_fmts, size, drm_fmt); + gfx_fmt = fmt; + } + + if (!fmt) + return -EINVAL; + + fmts = zynqmp_disp_av_buf_get_fmt(&disp->av_buf); + fmts &= mask; + fmts |= fmt->disp_fmt; + zynqmp_disp_av_buf_set_fmt(&disp->av_buf, fmts); + zynqmp_disp_av_buf_init_sf(&disp->av_buf, vid_fmt, gfx_fmt); + layer->fmt = fmt; + + return 0; +} + +/** + * zynqmp_disp_set_tpg - Enable or disable TPG + * @disp: Display subsystem + * @layer: Video layer + * @tpg_on: flag if TPG needs to be enabled or disabled + * + * Enable / disable the TPG mode on the video layer @layer depending on + * @tpg_on. The video layer should be disabled prior to enable request. + * + * Return: 0 on success. -ENODEV if it's not video layer. -EIO if + * the video layer is enabled. + */ +static int zynqmp_disp_layer_set_tpg(struct zynqmp_disp *disp, + struct zynqmp_disp_layer *layer, + bool tpg_on) +{ + if (layer->id != ZYNQMP_DISP_LAYER_VID) { + dev_err(disp->dev, + "only the video layer has the tpg mode\n"); + return -ENODEV; + } + + if (layer->enabled) { + dev_err(disp->dev, + "the video layer should be disabled for tpg mode\n"); + return -EIO; + } + + zynqmp_disp_av_buf_set_tpg(&disp->av_buf, tpg_on); + disp->tpg_on = tpg_on; + + return 0; +} + +/** + * zynqmp_disp_get_tpg - Get the TPG mode status + * @disp: Display subsystem + * @layer: Video layer + * + * Return if the TPG is enabled or not. + * + * Return: true if TPG is on, otherwise false + */ +static bool zynqmp_disp_layer_get_tpg(struct zynqmp_disp *disp, + struct zynqmp_disp_layer *layer) +{ + return disp->tpg_on; +} + +/** + * zynqmp_disp_get_fmt - Get the supported DRM formats of the layer + * @disp: Display subsystem + * @layer: layer to get the formats + * @drm_fmts: pointer to array of DRM format strings + * @num_fmts: pointer to number of returned DRM formats + * + * Get the supported DRM formats of the given layer. + */ +static void zynqmp_disp_layer_get_fmts(struct zynqmp_disp *disp, + struct zynqmp_disp_layer *layer, + u32 **drm_fmts, unsigned int *num_fmts) +{ + *drm_fmts = layer->drm_fmts; + *num_fmts = layer->num_fmts; +} + +/** + * zynqmp_disp_layer_enable - Enable the layer + * @disp: Display subsystem + * @layer: layer to esable + * @mode: operation mode + * + * Enable the layer @layer. + * + * Return: 0 on success, otherwise error code. + */ +static int zynqmp_disp_layer_enable(struct zynqmp_disp *disp, + struct zynqmp_disp_layer *layer, + enum zynqmp_disp_layer_mode mode) +{ + struct device *dev = disp->dev; + struct dma_async_tx_descriptor *desc; + enum dma_ctrl_flags flags; + unsigned int i; + + if (layer->enabled && layer->mode != mode) { + dev_err(dev, "layer is already enabled in different mode\n"); + return -EBUSY; + } + + zynqmp_disp_av_buf_enable_vid(&disp->av_buf, layer, mode); + zynqmp_disp_blend_layer_enable(&disp->blend, layer); + + layer->enabled = true; + layer->mode = mode; + + if (mode == ZYNQMP_DISP_LAYER_LIVE) + return 0; + + for (i = 0; i < ZYNQMP_DISP_MAX_NUM_SUB_PLANES; i++) { + struct zynqmp_disp_layer_dma *dma = &layer->dma[i]; + + if (dma->chan && dma->is_active) { + flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; + desc = dmaengine_prep_interleaved_dma(dma->chan, + &dma->xt, flags); + if (!desc) { + dev_err(dev, "failed to prep DMA descriptor\n"); + return -ENOMEM; + } + + dmaengine_submit(desc); + dma_async_issue_pending(dma->chan); + } + } + + return 0; +} + +/** + * zynqmp_disp_layer_disable - Disable the layer + * @disp: Display subsystem + * @layer: layer to disable + * @mode: operation mode + * + * Disable the layer @layer. + * + * Return: 0 on success, or -EBUSY if the layer is in different mode. + */ +static int zynqmp_disp_layer_disable(struct zynqmp_disp *disp, + struct zynqmp_disp_layer *layer, + enum zynqmp_disp_layer_mode mode) +{ + struct device *dev = disp->dev; + unsigned int i; + + if (layer->mode != mode) { + dev_err(dev, "the layer is operating in different mode\n"); + return -EBUSY; + } + + for (i = 0; i < ZYNQMP_DISP_MAX_NUM_SUB_PLANES; i++) + if (layer->dma[i].chan && layer->dma[i].is_active) + dmaengine_terminate_sync(layer->dma[i].chan); + + zynqmp_disp_av_buf_disable_vid(&disp->av_buf, layer); + zynqmp_disp_blend_layer_disable(&disp->blend, layer); + layer->enabled = false; + + return 0; +} + +/** + * zynqmp_disp_layer_request_dma - Request DMA channels for a layer + * @disp: Display subsystem + * @layer: layer to request DMA channels + * @name: identifier string for layer type + * + * Request DMA engine channels for corresponding layer. + * + * Return: 0 on success, or err value from of_dma_request_slave_channel(). + */ +static int +zynqmp_disp_layer_request_dma(struct zynqmp_disp *disp, + struct zynqmp_disp_layer *layer, const char *name) +{ + struct zynqmp_disp_layer_dma *dma; + unsigned int i; + int ret; + + for (i = 0; i < layer->num_chan; i++) { + char temp[16]; + + dma = &layer->dma[i]; + snprintf(temp, sizeof(temp), "%s%d", name, i); + dma->chan = of_dma_request_slave_channel(layer->of_node, + temp); + if (IS_ERR(dma->chan)) { + dev_err(disp->dev, "failed to request dma channel\n"); + ret = PTR_ERR(dma->chan); + dma->chan = NULL; + return ret; + } + } + + return 0; +} + +/** + * zynqmp_disp_layer_release_dma - Release DMA channels for a layer + * @disp: Display subsystem + * @layer: layer to release DMA channels + * + * Release the dma channels associated with @layer. + */ +static void zynqmp_disp_layer_release_dma(struct zynqmp_disp *disp, + struct zynqmp_disp_layer *layer) +{ + unsigned int i; + + for (i = 0; i < layer->num_chan; i++) { + if (layer->dma[i].chan) { + /* Make sure the channel is terminated before release */ + dmaengine_terminate_all(layer->dma[i].chan); + dma_release_channel(layer->dma[i].chan); + } + } +} + +/** + * zynqmp_disp_layer_is_live - if any layer is live + * @disp: Display subsystem + * + * Return: true if any layer is live + */ +static bool zynqmp_disp_layer_is_live(struct zynqmp_disp *disp) +{ + unsigned int i; + + for (i = 0; i < ZYNQMP_DISP_NUM_LAYERS; i++) { + if (disp->layers[i].enabled && + disp->layers[i].mode == ZYNQMP_DISP_LAYER_LIVE) + return true; + } + + return false; +} + +/** + * zynqmp_disp_layer_is_enabled - if any layer is enabled + * @disp: Display subsystem + * + * Return: true if any layer is enabled + */ +static bool zynqmp_disp_layer_is_enabled(struct zynqmp_disp *disp) +{ + unsigned int i; + + for (i = 0; i < ZYNQMP_DISP_NUM_LAYERS; i++) + if (disp->layers[i].enabled) + return true; + + return false; +} + +/** + * zynqmp_disp_layer_destroy - Destroy all layers + * @disp: Display subsystem + * + * Destroy all layers. + */ +static void zynqmp_disp_layer_destroy(struct zynqmp_disp *disp) +{ + unsigned int i; + + for (i = 0; i < ZYNQMP_DISP_NUM_LAYERS; i++) { + zynqmp_disp_layer_release_dma(disp, &disp->layers[i]); + if (disp->layers[i].of_node) + of_node_put(disp->layers[i].of_node); + } +} + +/** + * zynqmp_disp_layer_create - Create all layers + * @disp: Display subsystem + * + * Create all layers. + * + * Return: 0 on success, otherwise error code from failed function + */ +static int zynqmp_disp_layer_create(struct zynqmp_disp *disp) +{ + struct zynqmp_disp_layer *layer; + unsigned int i; + int num_chans[ZYNQMP_DISP_NUM_LAYERS] = { 3, 1 }; + const char * const dma_name[] = { "vid", "gfx" }; + int ret; + + for (i = 0; i < ZYNQMP_DISP_NUM_LAYERS; i++) { + char temp[16]; + + layer = &disp->layers[i]; + layer->id = i; + layer->offset = i * 4; + layer->other = &disp->layers[!i]; + layer->num_chan = num_chans[i]; + snprintf(temp, sizeof(temp), "%s-layer", dma_name[i]); + layer->of_node = of_get_child_by_name(disp->dev->of_node, temp); + if (!layer->of_node) + goto err; + ret = zynqmp_disp_layer_request_dma(disp, layer, dma_name[i]); + if (ret) + goto err; + layer->disp = disp; + } + + return 0; + +err: + zynqmp_disp_layer_destroy(disp); + return ret; +} + +/* + * ZynqMP Display internal functions + */ + +/* + * Output format enumeration for DRM property. + * The ID should be aligned with blend_output_fmts. + * The string should be aligned with how zynqmp_dp_set_color() decodes. + */ +static struct drm_prop_enum_list zynqmp_disp_color_enum[] = { + { 0, "rgb" }, + { 1, "ycrcb444" }, + { 2, "ycrcb422" }, + { 3, "yonly" }, +}; + +/** + * zynqmp_disp_set_output_fmt - Set the output format + * @disp: Display subsystem + * @id: the format ID. Refer to zynqmp_disp_color_enum[]. + * + * This function sets the output format of the display / blender as well as + * the format of DP controller. The @id should be aligned with + * zynqmp_disp_color_enum, thus function needs to be used for DRM property. + */ +static void +zynqmp_disp_set_output_fmt(struct zynqmp_disp *disp, unsigned int id) +{ + const struct zynqmp_disp_fmt *fmt = &blend_output_fmts[id]; + + zynqmp_dp_set_color(disp->dpsub->dp, zynqmp_disp_color_enum[id].name); + zynqmp_disp_blend_set_output_fmt(&disp->blend, fmt->disp_fmt); +} + +/** + * zynqmp_disp_set_bg_color - Set the background color + * @disp: Display subsystem + * @c0: color component 0 + * @c1: color component 1 + * @c2: color component 2 + * + * Set the background color with given color components (@c0, @c1, @c2). + */ +static void zynqmp_disp_set_bg_color(struct zynqmp_disp *disp, + u32 c0, u32 c1, u32 c2) +{ + zynqmp_disp_blend_set_bg_color(&disp->blend, c0, c1, c2); +} + +/** + * zynqmp_disp_set_alpha - Set the alpha value + * @disp: Display subsystem + * @alpha: alpha value to set + * + * Set the alpha value for blending. + */ +static void zynqmp_disp_set_alpha(struct zynqmp_disp *disp, u32 alpha) +{ + disp->alpha = alpha; + zynqmp_disp_blend_set_alpha(&disp->blend, alpha); +} + +/** + * zynqmp_disp_get_alpha - Get the alpha value + * @disp: Display subsystem + * + * Get the alpha value for blending. + * + * Return: current alpha value. + */ +static u32 zynqmp_disp_get_alpha(struct zynqmp_disp *disp) +{ + return disp->alpha; +} + +/** + * zynqmp_disp_set_g_alpha - Enable/disable the global alpha blending + * @disp: Display subsystem + * @enable: flag to enable or disable alpha blending + * + * Set the alpha value for blending. + */ +static void zynqmp_disp_set_g_alpha(struct zynqmp_disp *disp, bool enable) +{ + disp->alpha_en = enable; + zynqmp_disp_blend_enable_alpha(&disp->blend, enable); +} + +/** + * zynqmp_disp_get_g_alpha - Get the global alpha status + * @disp: Display subsystem + * + * Get the global alpha statue. + * + * Return: true if global alpha is enabled, or false. + */ +static bool zynqmp_disp_get_g_alpha(struct zynqmp_disp *disp) +{ + return disp->alpha_en; +} + +/** + * zynqmp_disp_enable - Enable the Display subsystem + * @disp: Display subsystem + * + * Enable the Display subsystem. + */ +static void zynqmp_disp_enable(struct zynqmp_disp *disp) +{ + bool live; + + if (disp->enabled) + return; + + zynqmp_disp_av_buf_enable(&disp->av_buf); + /* Choose clock source based on the DT clock handle */ + zynqmp_disp_av_buf_set_vid_clock_src(&disp->av_buf, !!disp->_ps_pclk); + zynqmp_disp_av_buf_set_aud_clock_src(&disp->av_buf, !!disp->_ps_audclk); + live = zynqmp_disp_layer_is_live(disp); + zynqmp_disp_av_buf_set_vid_timing_src(&disp->av_buf, !live); + zynqmp_disp_av_buf_enable_buf(&disp->av_buf); + zynqmp_disp_av_buf_enable_aud(&disp->av_buf); + zynqmp_disp_aud_init(&disp->aud); + disp->enabled = true; +} + +/** + * zynqmp_disp_disable - Disable the Display subsystem + * @disp: Display subsystem + * @force: flag to disable forcefully + * + * Disable the Display subsystem. + */ +static void zynqmp_disp_disable(struct zynqmp_disp *disp, bool force) +{ + struct drm_crtc *crtc = &disp->xlnx_crtc.crtc; + + if (!force && (!disp->enabled || zynqmp_disp_layer_is_enabled(disp))) + return; + + zynqmp_disp_aud_deinit(&disp->aud); + zynqmp_disp_av_buf_disable_aud(&disp->av_buf); + zynqmp_disp_av_buf_disable_buf(&disp->av_buf); + zynqmp_disp_av_buf_disable(&disp->av_buf); + + /* Mark the flip is done as crtc is disabled anyway */ + if (crtc->state->event) { + complete_all(crtc->state->event->base.completion); + crtc->state->event = NULL; + } + + disp->enabled = false; +} + +/* + * ZynqMP Display external functions for zynqmp_dp + */ + +/** + * zynqmp_disp_handle_vblank - Handle the vblank event + * @disp: Display subsystem + * + * This function handles the vblank interrupt, and sends an event to + * CRTC object. This will be called by the DP vblank interrupt handler. + */ +void zynqmp_disp_handle_vblank(struct zynqmp_disp *disp) +{ + struct drm_crtc *crtc = &disp->xlnx_crtc.crtc; + struct drm_device *drm = crtc->dev; + struct drm_pending_vblank_event *event; + unsigned long flags; + + drm_crtc_handle_vblank(crtc); + + /* Finish page flip */ + spin_lock_irqsave(&drm->event_lock, flags); + event = disp->event; + disp->event = NULL; + if (event) { + drm_crtc_send_vblank_event(crtc, event); + drm_crtc_vblank_put(crtc); + } + spin_unlock_irqrestore(&drm->event_lock, flags); +} + +/** + * zynqmp_disp_get_apb_clk_rate - Get the current APB clock rate + * @disp: Display subsystem + * + * Return: the current APB clock rate. + */ +unsigned int zynqmp_disp_get_apb_clk_rate(struct zynqmp_disp *disp) +{ + return clk_get_rate(disp->aclk); +} + +/** + * zynqmp_disp_aud_enabled - If the audio is enabled + * @disp: Display subsystem + * + * Return if the audio is enabled depending on the audio clock. + * + * Return: true if audio is enabled, or false. + */ +bool zynqmp_disp_aud_enabled(struct zynqmp_disp *disp) +{ + return !!disp->audclk; +} + +/** + * zynqmp_disp_get_aud_clk_rate - Get the current audio clock rate + * @disp: Display subsystem + * + * Return: the current audio clock rate. + */ +unsigned int zynqmp_disp_get_aud_clk_rate(struct zynqmp_disp *disp) +{ + if (zynqmp_disp_aud_enabled(disp)) + return 0; + return clk_get_rate(disp->aclk); +} + +/** + * zynqmp_disp_get_crtc_mask - Return the CRTC bit mask + * @disp: Display subsystem + * + * Return: the crtc mask of the zyqnmp_disp CRTC. + */ +uint32_t zynqmp_disp_get_crtc_mask(struct zynqmp_disp *disp) +{ + return drm_crtc_mask(&disp->xlnx_crtc.crtc); +} + +/* + * DRM property functions + */ + +static void zynqmp_disp_attach_vid_plane_property(struct zynqmp_disp *disp, + struct drm_mode_object *obj) +{ + drm_object_attach_property(obj, disp->tpg_prop, false); +} + +static void zynqmp_disp_attach_gfx_plane_property(struct zynqmp_disp *disp, + struct drm_mode_object *obj) +{ + drm_object_attach_property(obj, disp->g_alpha_prop, + ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA_MAX); + disp->alpha = ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA_MAX; + /* Enable the global alpha as default */ + drm_object_attach_property(obj, disp->g_alpha_en_prop, true); + disp->alpha_en = true; +} + +static void zynqmp_disp_attach_crtc_property(struct zynqmp_disp *disp, + struct drm_mode_object *obj) +{ + drm_object_attach_property(obj, disp->color_prop, 0); + zynqmp_dp_set_color(disp->dpsub->dp, zynqmp_disp_color_enum[0].name); + drm_object_attach_property(obj, disp->bg_c0_prop, 0); + drm_object_attach_property(obj, disp->bg_c1_prop, 0); + drm_object_attach_property(obj, disp->bg_c2_prop, 0); +} + +static void zynqmp_disp_create_property(struct zynqmp_disp *disp) +{ + int num; + u64 max; + + max = ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA_MAX; + disp->g_alpha_prop = drm_property_create_range(disp->drm, 0, "alpha", 0, + max); + disp->g_alpha_en_prop = drm_property_create_bool(disp->drm, 0, + "g_alpha_en"); + num = ARRAY_SIZE(zynqmp_disp_color_enum); + disp->color_prop = drm_property_create_enum(disp->drm, 0, + "output_color", + zynqmp_disp_color_enum, + num); + max = ZYNQMP_DISP_V_BLEND_BG_MAX; + disp->bg_c0_prop = drm_property_create_range(disp->drm, 0, "bg_c0", 0, + max); + disp->bg_c1_prop = drm_property_create_range(disp->drm, 0, "bg_c1", 0, + max); + disp->bg_c2_prop = drm_property_create_range(disp->drm, 0, "bg_c2", 0, + max); + disp->tpg_prop = drm_property_create_bool(disp->drm, 0, "tpg"); +} + +static void zynqmp_disp_destroy_property(struct zynqmp_disp *disp) +{ + drm_property_destroy(disp->drm, disp->bg_c2_prop); + drm_property_destroy(disp->drm, disp->bg_c1_prop); + drm_property_destroy(disp->drm, disp->bg_c0_prop); + drm_property_destroy(disp->drm, disp->color_prop); + drm_property_destroy(disp->drm, disp->g_alpha_en_prop); + drm_property_destroy(disp->drm, disp->g_alpha_prop); +} + +/* + * DRM plane functions + */ + +static inline struct zynqmp_disp_layer *plane_to_layer(struct drm_plane *plane) +{ + return container_of(plane, struct zynqmp_disp_layer, plane); +} + +static int zynqmp_disp_plane_enable(struct drm_plane *plane) +{ + struct zynqmp_disp_layer *layer = plane_to_layer(plane); + struct zynqmp_disp *disp = layer->disp; + int ret; + + zynqmp_disp_set_g_alpha(disp, disp->alpha_en); + zynqmp_disp_set_alpha(disp, disp->alpha); + ret = zynqmp_disp_layer_enable(layer->disp, layer, + ZYNQMP_DISP_LAYER_NONLIVE); + if (ret) + return ret; + + if (layer->id == ZYNQMP_DISP_LAYER_GFX && disp->tpg_on) { + layer = &disp->layers[ZYNQMP_DISP_LAYER_VID]; + zynqmp_disp_layer_set_tpg(disp, layer, disp->tpg_on); + } + + return 0; +} + +static int zynqmp_disp_plane_disable(struct drm_plane *plane) +{ + struct zynqmp_disp_layer *layer = plane_to_layer(plane); + struct zynqmp_disp *disp = layer->disp; + + zynqmp_disp_layer_disable(disp, layer, ZYNQMP_DISP_LAYER_NONLIVE); + if (layer->id == ZYNQMP_DISP_LAYER_VID && disp->tpg_on) + zynqmp_disp_layer_set_tpg(disp, layer, disp->tpg_on); + + return 0; +} + +static int zynqmp_disp_plane_mode_set(struct drm_plane *plane, + struct drm_framebuffer *fb, + int crtc_x, int crtc_y, + unsigned int crtc_w, unsigned int crtc_h, + u32 src_x, u32 src_y, + u32 src_w, u32 src_h) +{ + struct zynqmp_disp_layer *layer = plane_to_layer(plane); + const struct drm_format_info *info = fb->format; + struct drm_format_name_buf format_name; + struct device *dev = layer->disp->dev; + dma_addr_t paddr; + size_t offset; + unsigned int i; + int ret; + + if (!info) { + dev_err(dev, "unsupported framebuffer format %s\n", + drm_get_format_name(info->format, &format_name)); + return -EINVAL; + } + + ret = zynqmp_disp_layer_check_size(layer->disp, layer, src_w, src_h); + if (ret) + return ret; + + for (i = 0; i < info->num_planes; i++) { + unsigned int width = src_w / (i ? info->hsub : 1); + unsigned int height = src_h / (i ? info->vsub : 1); + + paddr = xlnx_fb_get_paddr(fb, i); + if (!paddr) { + dev_err(dev, "failed to get a paddr\n"); + return -EINVAL; + } + + layer->dma[i].xt.numf = height; + layer->dma[i].sgl[0].size = width * info->cpp[i]; + layer->dma[i].sgl[0].icg = fb->pitches[i] - + layer->dma[i].sgl[0].size; + offset = src_x * info->cpp[i] + src_y * fb->pitches[i]; + offset += fb->offsets[i]; + layer->dma[i].xt.src_start = paddr + offset; + layer->dma[i].xt.frame_size = 1; + layer->dma[i].xt.dir = DMA_MEM_TO_DEV; + layer->dma[i].xt.src_sgl = true; + layer->dma[i].xt.dst_sgl = false; + layer->dma[i].is_active = true; + } + + for (; i < ZYNQMP_DISP_MAX_NUM_SUB_PLANES; i++) + layer->dma[i].is_active = false; + + ret = zynqmp_disp_layer_set_fmt(layer->disp, layer, info->format); + if (ret) + dev_err(dev, "failed to set dp_sub layer fmt\n"); + + return ret; +} + +static void zynqmp_disp_plane_destroy(struct drm_plane *plane) +{ + drm_plane_cleanup(plane); +} + +static int +zynqmp_disp_plane_atomic_set_property(struct drm_plane *plane, + struct drm_plane_state *state, + struct drm_property *property, u64 val) +{ + struct zynqmp_disp_layer *layer = plane_to_layer(plane); + struct zynqmp_disp *disp = layer->disp; + int ret = 0; + + if (property == disp->g_alpha_prop) + zynqmp_disp_set_alpha(disp, val); + else if (property == disp->g_alpha_en_prop) + zynqmp_disp_set_g_alpha(disp, val); + else if (property == disp->tpg_prop) + ret = zynqmp_disp_layer_set_tpg(disp, layer, val); + else + return -EINVAL; + + return ret; +} + +static int +zynqmp_disp_plane_atomic_get_property(struct drm_plane *plane, + const struct drm_plane_state *state, + struct drm_property *property, + uint64_t *val) +{ + struct zynqmp_disp_layer *layer = plane_to_layer(plane); + struct zynqmp_disp *disp = layer->disp; + int ret = 0; + + if (property == disp->g_alpha_prop) + *val = zynqmp_disp_get_alpha(disp); + else if (property == disp->g_alpha_en_prop) + *val = zynqmp_disp_get_g_alpha(disp); + else if (property == disp->tpg_prop) + *val = zynqmp_disp_layer_get_tpg(disp, layer); + else + return -EINVAL; + + return ret; +} + +static struct drm_plane_funcs zynqmp_disp_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .atomic_set_property = zynqmp_disp_plane_atomic_set_property, + .atomic_get_property = zynqmp_disp_plane_atomic_get_property, + .destroy = zynqmp_disp_plane_destroy, + .reset = drm_atomic_helper_plane_reset, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, +}; + +static int zynqmp_disp_plane_prepare_fb(struct drm_plane *plane, + struct drm_plane_state *new_state) +{ + return 0; +} + +static void zynqmp_disp_plane_cleanup_fb(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ +} + +static int zynqmp_disp_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) +{ + return 0; +} + +static void +zynqmp_disp_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + int ret; + + if (!plane->state->crtc || !plane->state->fb) + return; + + ret = zynqmp_disp_plane_mode_set(plane, plane->state->fb, + plane->state->crtc_x, + plane->state->crtc_y, + plane->state->crtc_w, + plane->state->crtc_h, + plane->state->src_x >> 16, + plane->state->src_y >> 16, + plane->state->src_w >> 16, + plane->state->src_h >> 16); + if (ret) + return; + + zynqmp_disp_plane_enable(plane); +} + +static void +zynqmp_disp_plane_atomic_disable(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + zynqmp_disp_plane_disable(plane); +} + +static const struct drm_plane_helper_funcs zynqmp_disp_plane_helper_funcs = { + .prepare_fb = zynqmp_disp_plane_prepare_fb, + .cleanup_fb = zynqmp_disp_plane_cleanup_fb, + .atomic_check = zynqmp_disp_plane_atomic_check, + .atomic_update = zynqmp_disp_plane_atomic_update, + .atomic_disable = zynqmp_disp_plane_atomic_disable, +}; + +static int zynqmp_disp_create_plane(struct zynqmp_disp *disp) +{ + struct zynqmp_disp_layer *layer; + unsigned int i; + u32 *fmts = NULL; + unsigned int num_fmts = 0; + enum drm_plane_type type; + int ret; + + /* graphics layer is primary, and video layer is overaly */ + type = DRM_PLANE_TYPE_OVERLAY; + for (i = 0; i < ZYNQMP_DISP_NUM_LAYERS; i++) { + layer = &disp->layers[i]; + zynqmp_disp_layer_get_fmts(disp, layer, &fmts, &num_fmts); + ret = drm_universal_plane_init(disp->drm, &layer->plane, 0, + &zynqmp_disp_plane_funcs, fmts, + num_fmts, NULL, type, NULL); + if (ret) + goto err_plane; + drm_plane_helper_add(&layer->plane, + &zynqmp_disp_plane_helper_funcs); + type = DRM_PLANE_TYPE_PRIMARY; + } + + /* Attach properties to each layers */ + zynqmp_disp_attach_gfx_plane_property(disp, &layer->plane.base); + layer = &disp->layers[ZYNQMP_DISP_LAYER_VID]; + zynqmp_disp_attach_vid_plane_property(disp, &layer->plane.base); + + return ret; + +err_plane: + if (i) + drm_plane_cleanup(&disp->layers[0].plane); + return ret; +} + +static void zynqmp_disp_destroy_plane(struct zynqmp_disp *disp) +{ + unsigned int i; + + for (i = 0; i < ZYNQMP_DISP_NUM_LAYERS; i++) + zynqmp_disp_plane_destroy(&disp->layers[i].plane); +} + +/* + * Xlnx crtc functions + */ + +static inline struct zynqmp_disp *xlnx_crtc_to_disp(struct xlnx_crtc *xlnx_crtc) +{ + return container_of(xlnx_crtc, struct zynqmp_disp, xlnx_crtc); +} + +static int zynqmp_disp_enable_vblank(struct xlnx_crtc *xlnx_crtc) +{ + struct zynqmp_disp *disp = xlnx_crtc_to_disp(xlnx_crtc); + + zynqmp_dp_enable_vblank(disp->dpsub->dp); + return 0; +} + +static void zynqmp_disp_disable_vblank(struct xlnx_crtc *xlnx_crtc) +{ + struct zynqmp_disp *disp = xlnx_crtc_to_disp(xlnx_crtc); + + zynqmp_dp_disable_vblank(disp->dpsub->dp); +} + +static int zynqmp_disp_get_max_width(struct xlnx_crtc *xlnx_crtc) +{ + return ZYNQMP_DISP_MAX_WIDTH; +} + +static int zynqmp_disp_get_max_height(struct xlnx_crtc *xlnx_crtc) +{ + return ZYNQMP_DISP_MAX_HEIGHT; +} + +static uint32_t zynqmp_disp_get_format(struct xlnx_crtc *xlnx_crtc) +{ + struct zynqmp_disp *disp = xlnx_crtc_to_disp(xlnx_crtc); + + return disp->layers[ZYNQMP_DISP_LAYER_GFX].fmt->drm_fmt; +} + +static unsigned int zynqmp_disp_get_align(struct xlnx_crtc *xlnx_crtc) +{ + struct zynqmp_disp *disp = xlnx_crtc_to_disp(xlnx_crtc); + struct zynqmp_disp_layer *layer = &disp->layers[ZYNQMP_DISP_LAYER_VID]; + + return 1 << layer->dma->chan->device->copy_align; +} + +static u64 zynqmp_disp_get_dma_mask(struct xlnx_crtc *xlnx_crtc) +{ + return DMA_BIT_MASK(ZYNQMP_DISP_MAX_DMA_BIT); +} + +/* + * DRM crtc functions + */ + +static inline struct zynqmp_disp *crtc_to_disp(struct drm_crtc *crtc) +{ + struct xlnx_crtc *xlnx_crtc = to_xlnx_crtc(crtc); + + return xlnx_crtc_to_disp(xlnx_crtc); +} + +static int zynqmp_disp_crtc_mode_set(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode, + int x, int y, + struct drm_framebuffer *old_fb) +{ + struct zynqmp_disp *disp = crtc_to_disp(crtc); + unsigned long rate; + long diff; + int ret; + + zynqmp_disp_clk_disable(disp->pclk, &disp->pclk_en); + ret = clk_set_rate(disp->pclk, adjusted_mode->clock * 1000); + if (ret) { + dev_err(disp->dev, "failed to set a pixel clock\n"); + return ret; + } + + rate = clk_get_rate(disp->pclk); + diff = rate - adjusted_mode->clock * 1000; + if (abs(diff) > (adjusted_mode->clock * 1000) / 20) { + dev_info(disp->dev, "request pixel rate: %d actual rate: %lu\n", + adjusted_mode->clock, rate); + } else { + dev_dbg(disp->dev, "request pixel rate: %d actual rate: %lu\n", + adjusted_mode->clock, rate); + } + + /* The timing register should be programmed always */ + zynqmp_dp_encoder_mode_set_stream(disp->dpsub->dp, adjusted_mode); + + return 0; +} + +static void +zynqmp_disp_crtc_atomic_enable(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + struct zynqmp_disp *disp = crtc_to_disp(crtc); + struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode; + int ret, vrefresh; + + zynqmp_disp_crtc_mode_set(crtc, &crtc->state->mode, + adjusted_mode, crtc->x, crtc->y, NULL); + + pm_runtime_get_sync(disp->dev); + ret = zynqmp_disp_clk_enable(disp->pclk, &disp->pclk_en); + if (ret) { + dev_err(disp->dev, "failed to enable a pixel clock\n"); + return; + } + zynqmp_disp_set_output_fmt(disp, disp->color); + zynqmp_disp_set_bg_color(disp, disp->bg_c0, disp->bg_c1, disp->bg_c2); + zynqmp_disp_enable(disp); + /* Delay of 3 vblank intervals for timing gen to be stable */ + vrefresh = (adjusted_mode->clock * 1000) / + (adjusted_mode->vtotal * adjusted_mode->htotal); + msleep(3 * 1000 / vrefresh); +} + +static void +zynqmp_disp_crtc_atomic_disable(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + struct zynqmp_disp *disp = crtc_to_disp(crtc); + + zynqmp_disp_clk_disable(disp->pclk, &disp->pclk_en); + zynqmp_disp_plane_disable(crtc->primary); + zynqmp_disp_disable(disp, true); + pm_runtime_put_sync(disp->dev); +} + +static void zynqmp_disp_crtc_mode_set_nofb(struct drm_crtc *crtc) +{ +} + +static int zynqmp_disp_crtc_atomic_check(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + return drm_atomic_add_affected_planes(state->state, crtc); +} + +static void +zynqmp_disp_crtc_atomic_begin(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + /* Don't rely on vblank when disabling crtc */ + if (crtc->primary->state->fb && crtc->state->event) { + struct zynqmp_disp *disp = crtc_to_disp(crtc); + + /* Consume the flip_done event from atomic helper */ + crtc->state->event->pipe = drm_crtc_index(crtc); + WARN_ON(drm_crtc_vblank_get(crtc) != 0); + disp->event = crtc->state->event; + crtc->state->event = NULL; + } +} + +static struct drm_crtc_helper_funcs zynqmp_disp_crtc_helper_funcs = { + .atomic_enable = zynqmp_disp_crtc_atomic_enable, + .atomic_disable = zynqmp_disp_crtc_atomic_disable, + .mode_set_nofb = zynqmp_disp_crtc_mode_set_nofb, + .atomic_check = zynqmp_disp_crtc_atomic_check, + .atomic_begin = zynqmp_disp_crtc_atomic_begin, +}; + +static void zynqmp_disp_crtc_destroy(struct drm_crtc *crtc) +{ + zynqmp_disp_crtc_atomic_disable(crtc, NULL); + drm_crtc_cleanup(crtc); +} + +static int +zynqmp_disp_crtc_atomic_set_property(struct drm_crtc *crtc, + struct drm_crtc_state *state, + struct drm_property *property, + uint64_t val) +{ + struct zynqmp_disp *disp = crtc_to_disp(crtc); + + /* + * CRTC prop values are just stored here and applied when CRTC gets + * enabled + */ + if (property == disp->color_prop) + disp->color = val; + else if (property == disp->bg_c0_prop) + disp->bg_c0 = val; + else if (property == disp->bg_c1_prop) + disp->bg_c1 = val; + else if (property == disp->bg_c2_prop) + disp->bg_c2 = val; + else + return -EINVAL; + + return 0; +} + +static int +zynqmp_disp_crtc_atomic_get_property(struct drm_crtc *crtc, + const struct drm_crtc_state *state, + struct drm_property *property, + uint64_t *val) +{ + struct zynqmp_disp *disp = crtc_to_disp(crtc); + + if (property == disp->color_prop) + *val = disp->color; + else if (property == disp->bg_c0_prop) + *val = disp->bg_c0; + else if (property == disp->bg_c1_prop) + *val = disp->bg_c1; + else if (property == disp->bg_c2_prop) + *val = disp->bg_c2; + else + return -EINVAL; + + return 0; +} + +static struct drm_crtc_funcs zynqmp_disp_crtc_funcs = { + .destroy = zynqmp_disp_crtc_destroy, + .set_config = drm_atomic_helper_set_config, + .page_flip = drm_atomic_helper_page_flip, + .atomic_set_property = zynqmp_disp_crtc_atomic_set_property, + .atomic_get_property = zynqmp_disp_crtc_atomic_get_property, + .reset = drm_atomic_helper_crtc_reset, + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, +}; + +static void zynqmp_disp_create_crtc(struct zynqmp_disp *disp) +{ + struct drm_plane *plane = &disp->layers[ZYNQMP_DISP_LAYER_GFX].plane; + int ret; + + ret = drm_crtc_init_with_planes(disp->drm, &disp->xlnx_crtc.crtc, plane, + NULL, &zynqmp_disp_crtc_funcs, NULL); + drm_crtc_helper_add(&disp->xlnx_crtc.crtc, + &zynqmp_disp_crtc_helper_funcs); + zynqmp_disp_attach_crtc_property(disp, &disp->xlnx_crtc.crtc.base); + + disp->xlnx_crtc.enable_vblank = &zynqmp_disp_enable_vblank; + disp->xlnx_crtc.disable_vblank = &zynqmp_disp_disable_vblank; + disp->xlnx_crtc.get_max_width = &zynqmp_disp_get_max_width; + disp->xlnx_crtc.get_max_height = &zynqmp_disp_get_max_height; + disp->xlnx_crtc.get_format = &zynqmp_disp_get_format; + disp->xlnx_crtc.get_align = &zynqmp_disp_get_align; + disp->xlnx_crtc.get_dma_mask = &zynqmp_disp_get_dma_mask; + xlnx_crtc_register(disp->drm, &disp->xlnx_crtc); +} + +static void zynqmp_disp_destroy_crtc(struct zynqmp_disp *disp) +{ + xlnx_crtc_unregister(disp->drm, &disp->xlnx_crtc); + zynqmp_disp_crtc_destroy(&disp->xlnx_crtc.crtc); +} + +static void zynqmp_disp_map_crtc_to_plane(struct zynqmp_disp *disp) +{ + u32 possible_crtcs = drm_crtc_mask(&disp->xlnx_crtc.crtc); + unsigned int i; + + for (i = 0; i < ZYNQMP_DISP_NUM_LAYERS; i++) + disp->layers[i].plane.possible_crtcs = possible_crtcs; +} + +/* + * Component functions + */ + +int zynqmp_disp_bind(struct device *dev, struct device *master, void *data) +{ + struct zynqmp_dpsub *dpsub = dev_get_drvdata(dev); + struct zynqmp_disp *disp = dpsub->disp; + struct drm_device *drm = data; + int ret; + + disp->drm = drm; + zynqmp_disp_create_property(disp); + ret = zynqmp_disp_create_plane(disp); + if (ret) + return ret; + zynqmp_disp_create_crtc(disp); + zynqmp_disp_map_crtc_to_plane(disp); + + return 0; +} + +void zynqmp_disp_unbind(struct device *dev, struct device *master, void *data) +{ + struct zynqmp_dpsub *dpsub = dev_get_drvdata(dev); + struct zynqmp_disp *disp = dpsub->disp; + + zynqmp_disp_destroy_crtc(disp); + zynqmp_disp_destroy_plane(disp); + zynqmp_disp_destroy_property(disp); +} + +/* + * Platform initialization functions + */ + +static int zynqmp_disp_enumerate_fmts(struct zynqmp_disp *disp) +{ + struct zynqmp_disp_layer *layer; + u32 *bus_fmts; + u32 i, size, num_bus_fmts; + + num_bus_fmts = ARRAY_SIZE(av_buf_live_fmts); + bus_fmts = devm_kzalloc(disp->dev, sizeof(*bus_fmts) * num_bus_fmts, + GFP_KERNEL); + if (!bus_fmts) + return -ENOMEM; + for (i = 0; i < num_bus_fmts; i++) + bus_fmts[i] = av_buf_live_fmts[i].bus_fmt; + + layer = &disp->layers[ZYNQMP_DISP_LAYER_VID]; + layer->num_bus_fmts = num_bus_fmts; + layer->bus_fmts = bus_fmts; + size = ARRAY_SIZE(av_buf_vid_fmts); + layer->num_fmts = size; + layer->drm_fmts = devm_kzalloc(disp->dev, + sizeof(*layer->drm_fmts) * size, + GFP_KERNEL); + if (!layer->drm_fmts) + return -ENOMEM; + for (i = 0; i < layer->num_fmts; i++) + layer->drm_fmts[i] = av_buf_vid_fmts[i].drm_fmt; + layer->fmt = &av_buf_vid_fmts[ZYNQMP_DISP_AV_BUF_VID_FMT_YUYV]; + + layer = &disp->layers[ZYNQMP_DISP_LAYER_GFX]; + layer->num_bus_fmts = num_bus_fmts; + layer->bus_fmts = bus_fmts; + size = ARRAY_SIZE(av_buf_gfx_fmts); + layer->num_fmts = size; + layer->drm_fmts = devm_kzalloc(disp->dev, + sizeof(*layer->drm_fmts) * size, + GFP_KERNEL); + if (!layer->drm_fmts) + return -ENOMEM; + + for (i = 0; i < layer->num_fmts; i++) + layer->drm_fmts[i] = av_buf_gfx_fmts[i].drm_fmt; + layer->fmt = &av_buf_gfx_fmts[ZYNQMP_DISP_AV_BUF_GFX_FMT_RGB565]; + + return 0; +} + +int zynqmp_disp_probe(struct platform_device *pdev) +{ + struct zynqmp_dpsub *dpsub; + struct zynqmp_disp *disp; + struct resource *res; + int ret; + + disp = devm_kzalloc(&pdev->dev, sizeof(*disp), GFP_KERNEL); + if (!disp) + return -ENOMEM; + disp->dev = &pdev->dev; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "blend"); + disp->blend.base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(disp->blend.base)) + return PTR_ERR(disp->blend.base); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "av_buf"); + disp->av_buf.base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(disp->av_buf.base)) + return PTR_ERR(disp->av_buf.base); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "aud"); + disp->aud.base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(disp->aud.base)) + return PTR_ERR(disp->aud.base); + + dpsub = platform_get_drvdata(pdev); + dpsub->disp = disp; + disp->dpsub = dpsub; + + ret = zynqmp_disp_enumerate_fmts(disp); + if (ret) + return ret; + + /* Try the live PL video clock */ + disp->_pl_pclk = devm_clk_get(disp->dev, "dp_live_video_in_clk"); + if (!IS_ERR(disp->_pl_pclk)) { + disp->pclk = disp->_pl_pclk; + ret = zynqmp_disp_clk_enable_disable(disp->pclk, + &disp->pclk_en); + if (ret) + disp->pclk = NULL; + } else if (PTR_ERR(disp->_pl_pclk) == -EPROBE_DEFER) { + return PTR_ERR(disp->_pl_pclk); + } + + /* If the live PL video clock is not valid, fall back to PS clock */ + if (!disp->pclk) { + disp->_ps_pclk = devm_clk_get(disp->dev, "dp_vtc_pixel_clk_in"); + if (IS_ERR(disp->_ps_pclk)) { + dev_err(disp->dev, "failed to init any video clock\n"); + return PTR_ERR(disp->_ps_pclk); + } + disp->pclk = disp->_ps_pclk; + ret = zynqmp_disp_clk_enable_disable(disp->pclk, + &disp->pclk_en); + if (ret) { + dev_err(disp->dev, "failed to init any video clock\n"); + return ret; + } + } + + disp->aclk = devm_clk_get(disp->dev, "dp_apb_clk"); + if (IS_ERR(disp->aclk)) + return PTR_ERR(disp->aclk); + ret = zynqmp_disp_clk_enable(disp->aclk, &disp->aclk_en); + if (ret) { + dev_err(disp->dev, "failed to enable the APB clk\n"); + return ret; + } + + /* Try the live PL audio clock */ + disp->_pl_audclk = devm_clk_get(disp->dev, "dp_live_audio_aclk"); + if (!IS_ERR(disp->_pl_audclk)) { + disp->audclk = disp->_pl_audclk; + ret = zynqmp_disp_clk_enable_disable(disp->audclk, + &disp->audclk_en); + if (ret) + disp->audclk = NULL; + } + + /* If the live PL audio clock is not valid, fall back to PS clock */ + if (!disp->audclk) { + disp->_ps_audclk = devm_clk_get(disp->dev, "dp_aud_clk"); + if (!IS_ERR(disp->_ps_audclk)) { + disp->audclk = disp->_ps_audclk; + ret = zynqmp_disp_clk_enable_disable(disp->audclk, + &disp->audclk_en); + if (ret) + disp->audclk = NULL; + } + + if (!disp->audclk) { + dev_err(disp->dev, + "audio is disabled due to clock failure\n"); + } + } + + ret = zynqmp_disp_layer_create(disp); + if (ret) + goto error_aclk; + + return 0; + +error_aclk: + zynqmp_disp_clk_disable(disp->aclk, &disp->aclk_en); + return ret; +} + +int zynqmp_disp_remove(struct platform_device *pdev) +{ + struct zynqmp_dpsub *dpsub = platform_get_drvdata(pdev); + struct zynqmp_disp *disp = dpsub->disp; + + zynqmp_disp_layer_destroy(disp); + if (disp->audclk) + zynqmp_disp_clk_disable(disp->audclk, &disp->audclk_en); + zynqmp_disp_clk_disable(disp->aclk, &disp->aclk_en); + zynqmp_disp_clk_disable(disp->pclk, &disp->pclk_en); + dpsub->disp = NULL; + + return 0; +} diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.h b/drivers/gpu/drm/xlnx/zynqmp_disp.h new file mode 100644 index 0000000..0291fc2 --- /dev/null +++ b/drivers/gpu/drm/xlnx/zynqmp_disp.h @@ -0,0 +1,28 @@ +/* + * ZynqMP Display Driver + * + * Copyright (C) 2017 - 2018 Xilinx, Inc. + * + * Author: Hyun Woo Kwon <hyun.kwon@xilinx.com> + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _ZYNQMP_DISP_H_ +#define _ZYNQMP_DISP_H_ + +struct zynqmp_disp; + +void zynqmp_disp_handle_vblank(struct zynqmp_disp *disp); +unsigned int zynqmp_disp_get_apb_clk_rate(struct zynqmp_disp *disp); +bool zynqmp_disp_aud_enabled(struct zynqmp_disp *disp); +unsigned int zynqmp_disp_get_aud_clk_rate(struct zynqmp_disp *disp); +uint32_t zynqmp_disp_get_crtc_mask(struct zynqmp_disp *disp); + +int zynqmp_disp_bind(struct device *dev, struct device *master, void *data); +void zynqmp_disp_unbind(struct device *dev, struct device *master, void *data); + +int zynqmp_disp_probe(struct platform_device *pdev); +int zynqmp_disp_remove(struct platform_device *pdev); + +#endif /* _ZYNQMP_DISP_H_ */
Xilinx ZynqMP has a hardened display pipeline. The pipeline can be logically partitioned into 2 parts: display and DisplayPort. This driver handles the display part of the pipeline that handles buffer management and blending. Signed-off-by: Hyun Kwon <hyun.kwon@xilinx.com> --- drivers/gpu/drm/xlnx/zynqmp_disp.c | 2935 ++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/xlnx/zynqmp_disp.h | 28 + 2 files changed, 2963 insertions(+) create mode 100644 drivers/gpu/drm/xlnx/zynqmp_disp.c create mode 100644 drivers/gpu/drm/xlnx/zynqmp_disp.h