[PATCH 07/10] drm: xlnx: DRM KMS driver for Xilinx ZynqMP DP subsystem display
Hyun Kwon
hyunk at xilinx.com
Thu Jan 11 02:04:54 UTC 2018
Hi Daniel,
> -----Original Message-----
> From: Daniel Vetter [mailto:daniel.vetter at ffwll.ch] On Behalf Of Daniel
> Vetter
> Sent: Tuesday, January 09, 2018 1:47 AM
> To: Hyun Kwon <hyunk at xilinx.com>
> Cc: dri-devel at lists.freedesktop.org; devicetree at vger.kernel.org; Michal
> Simek <michal.simek at 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 at 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 at 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 at 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 at lists.freedesktop.org
> > https://lists.freedesktop.org/mailman/listinfo/dri-devel
>
> --
> Daniel Vetter
> Software Engineer, Intel Corporation
> http://blog.ffwll.ch
More information about the dri-devel
mailing list