Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * ZynqMP Display Controller Driver
0004  *
0005  * Copyright (C) 2017 - 2020 Xilinx, Inc.
0006  *
0007  * Authors:
0008  * - Hyun Woo Kwon <hyun.kwon@xilinx.com>
0009  * - Laurent Pinchart <laurent.pinchart@ideasonboard.com>
0010  */
0011 
0012 #include <drm/drm_atomic.h>
0013 #include <drm/drm_atomic_helper.h>
0014 #include <drm/drm_atomic_uapi.h>
0015 #include <drm/drm_blend.h>
0016 #include <drm/drm_crtc.h>
0017 #include <drm/drm_device.h>
0018 #include <drm/drm_fb_cma_helper.h>
0019 #include <drm/drm_fourcc.h>
0020 #include <drm/drm_framebuffer.h>
0021 #include <drm/drm_managed.h>
0022 #include <drm/drm_plane.h>
0023 #include <drm/drm_plane_helper.h>
0024 #include <drm/drm_vblank.h>
0025 
0026 #include <linux/clk.h>
0027 #include <linux/delay.h>
0028 #include <linux/dma/xilinx_dpdma.h>
0029 #include <linux/dma-mapping.h>
0030 #include <linux/dmaengine.h>
0031 #include <linux/module.h>
0032 #include <linux/of.h>
0033 #include <linux/platform_device.h>
0034 #include <linux/pm_runtime.h>
0035 #include <linux/spinlock.h>
0036 
0037 #include "zynqmp_disp.h"
0038 #include "zynqmp_disp_regs.h"
0039 #include "zynqmp_dp.h"
0040 #include "zynqmp_dpsub.h"
0041 
0042 /*
0043  * Overview
0044  * --------
0045  *
0046  * The display controller part of ZynqMP DP subsystem, made of the Audio/Video
0047  * Buffer Manager, the Video Rendering Pipeline (blender) and the Audio Mixer.
0048  *
0049  *              +------------------------------------------------------------+
0050  * +--------+   | +----------------+     +-----------+                       |
0051  * | DPDMA  | --->|                | --> |   Video   | Video +-------------+ |
0052  * | 4x vid |   | |                |     | Rendering | -+--> |             | |   +------+
0053  * | 2x aud |   | |  Audio/Video   | --> | Pipeline  |  |    | DisplayPort |---> | PHY0 |
0054  * +--------+   | | Buffer Manager |     +-----------+  |    |   Source    | |   +------+
0055  *              | |    and STC     |     +-----------+  |    | Controller  | |   +------+
0056  * Live Video --->|                | --> |   Audio   | Audio |             |---> | PHY1 |
0057  *              | |                |     |   Mixer   | --+-> |             | |   +------+
0058  * Live Audio --->|                | --> |           |  ||   +-------------+ |
0059  *              | +----------------+     +-----------+  ||                   |
0060  *              +---------------------------------------||-------------------+
0061  *                                                      vv
0062  *                                                Blended Video and
0063  *                                                Mixed Audio to PL
0064  *
0065  * Only non-live input from the DPDMA and output to the DisplayPort Source
0066  * Controller are currently supported. Interface with the programmable logic
0067  * for live streams is not implemented.
0068  *
0069  * The display controller code creates planes for the DPDMA video and graphics
0070  * layers, and a CRTC for the Video Rendering Pipeline.
0071  */
0072 
0073 #define ZYNQMP_DISP_AV_BUF_NUM_VID_GFX_BUFFERS      4
0074 #define ZYNQMP_DISP_AV_BUF_NUM_BUFFERS          6
0075 
0076 #define ZYNQMP_DISP_NUM_LAYERS              2
0077 #define ZYNQMP_DISP_MAX_NUM_SUB_PLANES          3
0078 
0079 /**
0080  * struct zynqmp_disp_format - Display subsystem format information
0081  * @drm_fmt: DRM format (4CC)
0082  * @buf_fmt: AV buffer format
0083  * @bus_fmt: Media bus formats (live formats)
0084  * @swap: Flag to swap R & B for RGB formats, and U & V for YUV formats
0085  * @sf: Scaling factors for color components
0086  */
0087 struct zynqmp_disp_format {
0088     u32 drm_fmt;
0089     u32 buf_fmt;
0090     u32 bus_fmt;
0091     bool swap;
0092     const u32 *sf;
0093 };
0094 
0095 /**
0096  * enum zynqmp_disp_layer_id - Layer identifier
0097  * @ZYNQMP_DISP_LAYER_VID: Video layer
0098  * @ZYNQMP_DISP_LAYER_GFX: Graphics layer
0099  */
0100 enum zynqmp_disp_layer_id {
0101     ZYNQMP_DISP_LAYER_VID,
0102     ZYNQMP_DISP_LAYER_GFX
0103 };
0104 
0105 /**
0106  * enum zynqmp_disp_layer_mode - Layer mode
0107  * @ZYNQMP_DISP_LAYER_NONLIVE: non-live (memory) mode
0108  * @ZYNQMP_DISP_LAYER_LIVE: live (stream) mode
0109  */
0110 enum zynqmp_disp_layer_mode {
0111     ZYNQMP_DISP_LAYER_NONLIVE,
0112     ZYNQMP_DISP_LAYER_LIVE
0113 };
0114 
0115 /**
0116  * struct zynqmp_disp_layer_dma - DMA channel for one data plane of a layer
0117  * @chan: DMA channel
0118  * @xt: Interleaved DMA descriptor template
0119  * @sgl: Data chunk for dma_interleaved_template
0120  */
0121 struct zynqmp_disp_layer_dma {
0122     struct dma_chan *chan;
0123     struct dma_interleaved_template xt;
0124     struct data_chunk sgl;
0125 };
0126 
0127 /**
0128  * struct zynqmp_disp_layer_info - Static layer information
0129  * @formats: Array of supported formats
0130  * @num_formats: Number of formats in @formats array
0131  * @num_channels: Number of DMA channels
0132  */
0133 struct zynqmp_disp_layer_info {
0134     const struct zynqmp_disp_format *formats;
0135     unsigned int num_formats;
0136     unsigned int num_channels;
0137 };
0138 
0139 /**
0140  * struct zynqmp_disp_layer - Display layer (DRM plane)
0141  * @plane: DRM plane
0142  * @id: Layer ID
0143  * @disp: Back pointer to struct zynqmp_disp
0144  * @info: Static layer information
0145  * @dmas: DMA channels
0146  * @disp_fmt: Current format information
0147  * @drm_fmt: Current DRM format information
0148  * @mode: Current operation mode
0149  */
0150 struct zynqmp_disp_layer {
0151     struct drm_plane plane;
0152     enum zynqmp_disp_layer_id id;
0153     struct zynqmp_disp *disp;
0154     const struct zynqmp_disp_layer_info *info;
0155 
0156     struct zynqmp_disp_layer_dma dmas[ZYNQMP_DISP_MAX_NUM_SUB_PLANES];
0157 
0158     const struct zynqmp_disp_format *disp_fmt;
0159     const struct drm_format_info *drm_fmt;
0160     enum zynqmp_disp_layer_mode mode;
0161 };
0162 
0163 /**
0164  * struct zynqmp_disp - Display controller
0165  * @dev: Device structure
0166  * @drm: DRM core
0167  * @dpsub: Display subsystem
0168  * @crtc: DRM CRTC
0169  * @blend.base: Register I/O base address for the blender
0170  * @avbuf.base: Register I/O base address for the audio/video buffer manager
0171  * @audio.base: Registers I/O base address for the audio mixer
0172  * @audio.clk: Audio clock
0173  * @audio.clk_from_ps: True of the audio clock comes from PS, false from PL
0174  * @layers: Layers (planes)
0175  * @event: Pending vblank event request
0176  * @pclk: Pixel clock
0177  * @pclk_from_ps: True of the video clock comes from PS, false from PL
0178  */
0179 struct zynqmp_disp {
0180     struct device *dev;
0181     struct drm_device *drm;
0182     struct zynqmp_dpsub *dpsub;
0183 
0184     struct drm_crtc crtc;
0185 
0186     struct {
0187         void __iomem *base;
0188     } blend;
0189     struct {
0190         void __iomem *base;
0191     } avbuf;
0192     struct {
0193         void __iomem *base;
0194         struct clk *clk;
0195         bool clk_from_ps;
0196     } audio;
0197 
0198     struct zynqmp_disp_layer layers[ZYNQMP_DISP_NUM_LAYERS];
0199 
0200     struct drm_pending_vblank_event *event;
0201 
0202     struct clk *pclk;
0203     bool pclk_from_ps;
0204 };
0205 
0206 /* -----------------------------------------------------------------------------
0207  * Audio/Video Buffer Manager
0208  */
0209 
0210 static const u32 scaling_factors_444[] = {
0211     ZYNQMP_DISP_AV_BUF_4BIT_SF,
0212     ZYNQMP_DISP_AV_BUF_4BIT_SF,
0213     ZYNQMP_DISP_AV_BUF_4BIT_SF,
0214 };
0215 
0216 static const u32 scaling_factors_555[] = {
0217     ZYNQMP_DISP_AV_BUF_5BIT_SF,
0218     ZYNQMP_DISP_AV_BUF_5BIT_SF,
0219     ZYNQMP_DISP_AV_BUF_5BIT_SF,
0220 };
0221 
0222 static const u32 scaling_factors_565[] = {
0223     ZYNQMP_DISP_AV_BUF_5BIT_SF,
0224     ZYNQMP_DISP_AV_BUF_6BIT_SF,
0225     ZYNQMP_DISP_AV_BUF_5BIT_SF,
0226 };
0227 
0228 static const u32 scaling_factors_888[] = {
0229     ZYNQMP_DISP_AV_BUF_8BIT_SF,
0230     ZYNQMP_DISP_AV_BUF_8BIT_SF,
0231     ZYNQMP_DISP_AV_BUF_8BIT_SF,
0232 };
0233 
0234 static const u32 scaling_factors_101010[] = {
0235     ZYNQMP_DISP_AV_BUF_10BIT_SF,
0236     ZYNQMP_DISP_AV_BUF_10BIT_SF,
0237     ZYNQMP_DISP_AV_BUF_10BIT_SF,
0238 };
0239 
0240 /* List of video layer formats */
0241 static const struct zynqmp_disp_format avbuf_vid_fmts[] = {
0242     {
0243         .drm_fmt    = DRM_FORMAT_VYUY,
0244         .buf_fmt    = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_VYUY,
0245         .swap       = true,
0246         .sf     = scaling_factors_888,
0247     }, {
0248         .drm_fmt    = DRM_FORMAT_UYVY,
0249         .buf_fmt    = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_VYUY,
0250         .swap       = false,
0251         .sf     = scaling_factors_888,
0252     }, {
0253         .drm_fmt    = DRM_FORMAT_YUYV,
0254         .buf_fmt    = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YUYV,
0255         .swap       = false,
0256         .sf     = scaling_factors_888,
0257     }, {
0258         .drm_fmt    = DRM_FORMAT_YVYU,
0259         .buf_fmt    = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YUYV,
0260         .swap       = true,
0261         .sf     = scaling_factors_888,
0262     }, {
0263         .drm_fmt    = DRM_FORMAT_YUV422,
0264         .buf_fmt    = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16,
0265         .swap       = false,
0266         .sf     = scaling_factors_888,
0267     }, {
0268         .drm_fmt    = DRM_FORMAT_YVU422,
0269         .buf_fmt    = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16,
0270         .swap       = true,
0271         .sf     = scaling_factors_888,
0272     }, {
0273         .drm_fmt    = DRM_FORMAT_YUV444,
0274         .buf_fmt    = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV24,
0275         .swap       = false,
0276         .sf     = scaling_factors_888,
0277     }, {
0278         .drm_fmt    = DRM_FORMAT_YVU444,
0279         .buf_fmt    = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV24,
0280         .swap       = true,
0281         .sf     = scaling_factors_888,
0282     }, {
0283         .drm_fmt    = DRM_FORMAT_NV16,
0284         .buf_fmt    = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI,
0285         .swap       = false,
0286         .sf     = scaling_factors_888,
0287     }, {
0288         .drm_fmt    = DRM_FORMAT_NV61,
0289         .buf_fmt    = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI,
0290         .swap       = true,
0291         .sf     = scaling_factors_888,
0292     }, {
0293         .drm_fmt    = DRM_FORMAT_BGR888,
0294         .buf_fmt    = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_RGB888,
0295         .swap       = false,
0296         .sf     = scaling_factors_888,
0297     }, {
0298         .drm_fmt    = DRM_FORMAT_RGB888,
0299         .buf_fmt    = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_RGB888,
0300         .swap       = true,
0301         .sf     = scaling_factors_888,
0302     }, {
0303         .drm_fmt    = DRM_FORMAT_XBGR8888,
0304         .buf_fmt    = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_RGBA8880,
0305         .swap       = false,
0306         .sf     = scaling_factors_888,
0307     }, {
0308         .drm_fmt    = DRM_FORMAT_XRGB8888,
0309         .buf_fmt    = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_RGBA8880,
0310         .swap       = true,
0311         .sf     = scaling_factors_888,
0312     }, {
0313         .drm_fmt    = DRM_FORMAT_XBGR2101010,
0314         .buf_fmt    = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_RGB888_10,
0315         .swap       = false,
0316         .sf     = scaling_factors_101010,
0317     }, {
0318         .drm_fmt    = DRM_FORMAT_XRGB2101010,
0319         .buf_fmt    = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_RGB888_10,
0320         .swap       = true,
0321         .sf     = scaling_factors_101010,
0322     }, {
0323         .drm_fmt    = DRM_FORMAT_YUV420,
0324         .buf_fmt    = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16_420,
0325         .swap       = false,
0326         .sf     = scaling_factors_888,
0327     }, {
0328         .drm_fmt    = DRM_FORMAT_YVU420,
0329         .buf_fmt    = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16_420,
0330         .swap       = true,
0331         .sf     = scaling_factors_888,
0332     }, {
0333         .drm_fmt    = DRM_FORMAT_NV12,
0334         .buf_fmt    = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI_420,
0335         .swap       = false,
0336         .sf     = scaling_factors_888,
0337     }, {
0338         .drm_fmt    = DRM_FORMAT_NV21,
0339         .buf_fmt    = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI_420,
0340         .swap       = true,
0341         .sf     = scaling_factors_888,
0342     },
0343 };
0344 
0345 /* List of graphics layer formats */
0346 static const struct zynqmp_disp_format avbuf_gfx_fmts[] = {
0347     {
0348         .drm_fmt    = DRM_FORMAT_ABGR8888,
0349         .buf_fmt    = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGBA8888,
0350         .swap       = false,
0351         .sf     = scaling_factors_888,
0352     }, {
0353         .drm_fmt    = DRM_FORMAT_ARGB8888,
0354         .buf_fmt    = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGBA8888,
0355         .swap       = true,
0356         .sf     = scaling_factors_888,
0357     }, {
0358         .drm_fmt    = DRM_FORMAT_RGBA8888,
0359         .buf_fmt    = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_ABGR8888,
0360         .swap       = false,
0361         .sf     = scaling_factors_888,
0362     }, {
0363         .drm_fmt    = DRM_FORMAT_BGRA8888,
0364         .buf_fmt    = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_ABGR8888,
0365         .swap       = true,
0366         .sf     = scaling_factors_888,
0367     }, {
0368         .drm_fmt    = DRM_FORMAT_BGR888,
0369         .buf_fmt    = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGB888,
0370         .swap       = false,
0371         .sf     = scaling_factors_888,
0372     }, {
0373         .drm_fmt    = DRM_FORMAT_RGB888,
0374         .buf_fmt    = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_BGR888,
0375         .swap       = false,
0376         .sf     = scaling_factors_888,
0377     }, {
0378         .drm_fmt    = DRM_FORMAT_RGBA5551,
0379         .buf_fmt    = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGBA5551,
0380         .swap       = false,
0381         .sf     = scaling_factors_555,
0382     }, {
0383         .drm_fmt    = DRM_FORMAT_BGRA5551,
0384         .buf_fmt    = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGBA5551,
0385         .swap       = true,
0386         .sf     = scaling_factors_555,
0387     }, {
0388         .drm_fmt    = DRM_FORMAT_RGBA4444,
0389         .buf_fmt    = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGBA4444,
0390         .swap       = false,
0391         .sf     = scaling_factors_444,
0392     }, {
0393         .drm_fmt    = DRM_FORMAT_BGRA4444,
0394         .buf_fmt    = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGBA4444,
0395         .swap       = true,
0396         .sf     = scaling_factors_444,
0397     }, {
0398         .drm_fmt    = DRM_FORMAT_RGB565,
0399         .buf_fmt    = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGB565,
0400         .swap       = false,
0401         .sf     = scaling_factors_565,
0402     }, {
0403         .drm_fmt    = DRM_FORMAT_BGR565,
0404         .buf_fmt    = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGB565,
0405         .swap       = true,
0406         .sf     = scaling_factors_565,
0407     },
0408 };
0409 
0410 static u32 zynqmp_disp_avbuf_read(struct zynqmp_disp *disp, int reg)
0411 {
0412     return readl(disp->avbuf.base + reg);
0413 }
0414 
0415 static void zynqmp_disp_avbuf_write(struct zynqmp_disp *disp, int reg, u32 val)
0416 {
0417     writel(val, disp->avbuf.base + reg);
0418 }
0419 
0420 static bool zynqmp_disp_layer_is_gfx(const struct zynqmp_disp_layer *layer)
0421 {
0422     return layer->id == ZYNQMP_DISP_LAYER_GFX;
0423 }
0424 
0425 static bool zynqmp_disp_layer_is_video(const struct zynqmp_disp_layer *layer)
0426 {
0427     return layer->id == ZYNQMP_DISP_LAYER_VID;
0428 }
0429 
0430 /**
0431  * zynqmp_disp_avbuf_set_format - Set the input format for a layer
0432  * @disp: Display controller
0433  * @layer: The layer
0434  * @fmt: The format information
0435  *
0436  * Set the video buffer manager format for @layer to @fmt.
0437  */
0438 static void zynqmp_disp_avbuf_set_format(struct zynqmp_disp *disp,
0439                      struct zynqmp_disp_layer *layer,
0440                      const struct zynqmp_disp_format *fmt)
0441 {
0442     unsigned int i;
0443     u32 val;
0444 
0445     val = zynqmp_disp_avbuf_read(disp, ZYNQMP_DISP_AV_BUF_FMT);
0446     val &= zynqmp_disp_layer_is_video(layer)
0447         ? ~ZYNQMP_DISP_AV_BUF_FMT_NL_VID_MASK
0448         : ~ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_MASK;
0449     val |= fmt->buf_fmt;
0450     zynqmp_disp_avbuf_write(disp, ZYNQMP_DISP_AV_BUF_FMT, val);
0451 
0452     for (i = 0; i < ZYNQMP_DISP_AV_BUF_NUM_SF; i++) {
0453         unsigned int reg = zynqmp_disp_layer_is_video(layer)
0454                  ? ZYNQMP_DISP_AV_BUF_VID_COMP_SF(i)
0455                  : ZYNQMP_DISP_AV_BUF_GFX_COMP_SF(i);
0456 
0457         zynqmp_disp_avbuf_write(disp, reg, fmt->sf[i]);
0458     }
0459 }
0460 
0461 /**
0462  * zynqmp_disp_avbuf_set_clocks_sources - Set the clocks sources
0463  * @disp: Display controller
0464  * @video_from_ps: True if the video clock originates from the PS
0465  * @audio_from_ps: True if the audio clock originates from the PS
0466  * @timings_internal: True if video timings are generated internally
0467  *
0468  * Set the source for the video and audio clocks, as well as for the video
0469  * timings. Clocks can originate from the PS or PL, and timings can be
0470  * generated internally or externally.
0471  */
0472 static void
0473 zynqmp_disp_avbuf_set_clocks_sources(struct zynqmp_disp *disp,
0474                      bool video_from_ps, bool audio_from_ps,
0475                      bool timings_internal)
0476 {
0477     u32 val = 0;
0478 
0479     if (video_from_ps)
0480         val |= ZYNQMP_DISP_AV_BUF_CLK_SRC_VID_FROM_PS;
0481     if (audio_from_ps)
0482         val |= ZYNQMP_DISP_AV_BUF_CLK_SRC_AUD_FROM_PS;
0483     if (timings_internal)
0484         val |= ZYNQMP_DISP_AV_BUF_CLK_SRC_VID_INTERNAL_TIMING;
0485 
0486     zynqmp_disp_avbuf_write(disp, ZYNQMP_DISP_AV_BUF_CLK_SRC, val);
0487 }
0488 
0489 /**
0490  * zynqmp_disp_avbuf_enable_channels - Enable buffer channels
0491  * @disp: Display controller
0492  *
0493  * Enable all (video and audio) buffer channels.
0494  */
0495 static void zynqmp_disp_avbuf_enable_channels(struct zynqmp_disp *disp)
0496 {
0497     unsigned int i;
0498     u32 val;
0499 
0500     val = ZYNQMP_DISP_AV_BUF_CHBUF_EN |
0501           (ZYNQMP_DISP_AV_BUF_CHBUF_BURST_LEN_MAX <<
0502            ZYNQMP_DISP_AV_BUF_CHBUF_BURST_LEN_SHIFT);
0503 
0504     for (i = 0; i < ZYNQMP_DISP_AV_BUF_NUM_VID_GFX_BUFFERS; i++)
0505         zynqmp_disp_avbuf_write(disp, ZYNQMP_DISP_AV_BUF_CHBUF(i),
0506                     val);
0507 
0508     val = ZYNQMP_DISP_AV_BUF_CHBUF_EN |
0509           (ZYNQMP_DISP_AV_BUF_CHBUF_BURST_LEN_AUD_MAX <<
0510            ZYNQMP_DISP_AV_BUF_CHBUF_BURST_LEN_SHIFT);
0511 
0512     for (; i < ZYNQMP_DISP_AV_BUF_NUM_BUFFERS; i++)
0513         zynqmp_disp_avbuf_write(disp, ZYNQMP_DISP_AV_BUF_CHBUF(i),
0514                     val);
0515 }
0516 
0517 /**
0518  * zynqmp_disp_avbuf_disable_channels - Disable buffer channels
0519  * @disp: Display controller
0520  *
0521  * Disable all (video and audio) buffer channels.
0522  */
0523 static void zynqmp_disp_avbuf_disable_channels(struct zynqmp_disp *disp)
0524 {
0525     unsigned int i;
0526 
0527     for (i = 0; i < ZYNQMP_DISP_AV_BUF_NUM_BUFFERS; i++)
0528         zynqmp_disp_avbuf_write(disp, ZYNQMP_DISP_AV_BUF_CHBUF(i),
0529                     ZYNQMP_DISP_AV_BUF_CHBUF_FLUSH);
0530 }
0531 
0532 /**
0533  * zynqmp_disp_avbuf_enable_audio - Enable audio
0534  * @disp: Display controller
0535  *
0536  * Enable all audio buffers with a non-live (memory) source.
0537  */
0538 static void zynqmp_disp_avbuf_enable_audio(struct zynqmp_disp *disp)
0539 {
0540     u32 val;
0541 
0542     val = zynqmp_disp_avbuf_read(disp, ZYNQMP_DISP_AV_BUF_OUTPUT);
0543     val &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_MASK;
0544     val |= ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_MEM;
0545     val |= ZYNQMP_DISP_AV_BUF_OUTPUT_AUD2_EN;
0546     zynqmp_disp_avbuf_write(disp, ZYNQMP_DISP_AV_BUF_OUTPUT, val);
0547 }
0548 
0549 /**
0550  * zynqmp_disp_avbuf_disable_audio - Disable audio
0551  * @disp: Display controller
0552  *
0553  * Disable all audio buffers.
0554  */
0555 static void zynqmp_disp_avbuf_disable_audio(struct zynqmp_disp *disp)
0556 {
0557     u32 val;
0558 
0559     val = zynqmp_disp_avbuf_read(disp, ZYNQMP_DISP_AV_BUF_OUTPUT);
0560     val &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_MASK;
0561     val |= ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_DISABLE;
0562     val &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_AUD2_EN;
0563     zynqmp_disp_avbuf_write(disp, ZYNQMP_DISP_AV_BUF_OUTPUT, val);
0564 }
0565 
0566 /**
0567  * zynqmp_disp_avbuf_enable_video - Enable a video layer
0568  * @disp: Display controller
0569  * @layer: The layer
0570  * @mode: Operating mode of layer
0571  *
0572  * Enable the video/graphics buffer for @layer.
0573  */
0574 static void zynqmp_disp_avbuf_enable_video(struct zynqmp_disp *disp,
0575                        struct zynqmp_disp_layer *layer,
0576                        enum zynqmp_disp_layer_mode mode)
0577 {
0578     u32 val;
0579 
0580     val = zynqmp_disp_avbuf_read(disp, ZYNQMP_DISP_AV_BUF_OUTPUT);
0581     if (zynqmp_disp_layer_is_video(layer)) {
0582         val &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_MASK;
0583         if (mode == ZYNQMP_DISP_LAYER_NONLIVE)
0584             val |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_MEM;
0585         else
0586             val |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_LIVE;
0587     } else {
0588         val &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_MASK;
0589         val |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_MEM;
0590         if (mode == ZYNQMP_DISP_LAYER_NONLIVE)
0591             val |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_MEM;
0592         else
0593             val |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_LIVE;
0594     }
0595     zynqmp_disp_avbuf_write(disp, ZYNQMP_DISP_AV_BUF_OUTPUT, val);
0596 }
0597 
0598 /**
0599  * zynqmp_disp_avbuf_disable_video - Disable a video layer
0600  * @disp: Display controller
0601  * @layer: The layer
0602  *
0603  * Disable the video/graphics buffer for @layer.
0604  */
0605 static void zynqmp_disp_avbuf_disable_video(struct zynqmp_disp *disp,
0606                         struct zynqmp_disp_layer *layer)
0607 {
0608     u32 val;
0609 
0610     val = zynqmp_disp_avbuf_read(disp, ZYNQMP_DISP_AV_BUF_OUTPUT);
0611     if (zynqmp_disp_layer_is_video(layer)) {
0612         val &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_MASK;
0613         val |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_NONE;
0614     } else {
0615         val &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_MASK;
0616         val |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_DISABLE;
0617     }
0618     zynqmp_disp_avbuf_write(disp, ZYNQMP_DISP_AV_BUF_OUTPUT, val);
0619 }
0620 
0621 /**
0622  * zynqmp_disp_avbuf_enable - Enable the video pipe
0623  * @disp: Display controller
0624  *
0625  * De-assert the video pipe reset.
0626  */
0627 static void zynqmp_disp_avbuf_enable(struct zynqmp_disp *disp)
0628 {
0629     zynqmp_disp_avbuf_write(disp, ZYNQMP_DISP_AV_BUF_SRST_REG, 0);
0630 }
0631 
0632 /**
0633  * zynqmp_disp_avbuf_disable - Disable the video pipe
0634  * @disp: Display controller
0635  *
0636  * Assert the video pipe reset.
0637  */
0638 static void zynqmp_disp_avbuf_disable(struct zynqmp_disp *disp)
0639 {
0640     zynqmp_disp_avbuf_write(disp, ZYNQMP_DISP_AV_BUF_SRST_REG,
0641                 ZYNQMP_DISP_AV_BUF_SRST_REG_VID_RST);
0642 }
0643 
0644 /* -----------------------------------------------------------------------------
0645  * Blender (Video Pipeline)
0646  */
0647 
0648 static void zynqmp_disp_blend_write(struct zynqmp_disp *disp, int reg, u32 val)
0649 {
0650     writel(val, disp->blend.base + reg);
0651 }
0652 
0653 /*
0654  * Colorspace conversion matrices.
0655  *
0656  * Hardcode RGB <-> YUV conversion to full-range SDTV for now.
0657  */
0658 static const u16 csc_zero_matrix[] = {
0659     0x0,    0x0,    0x0,
0660     0x0,    0x0,    0x0,
0661     0x0,    0x0,    0x0
0662 };
0663 
0664 static const u16 csc_identity_matrix[] = {
0665     0x1000, 0x0,    0x0,
0666     0x0,    0x1000, 0x0,
0667     0x0,    0x0,    0x1000
0668 };
0669 
0670 static const u32 csc_zero_offsets[] = {
0671     0, 0, 0
0672 };
0673 
0674 static const u16 csc_rgb_to_sdtv_matrix[] = {
0675     0x4c9,  0x864,  0x1d3,
0676     0x7d4d, 0x7ab3, 0x800,
0677     0x800,  0x794d, 0x7eb3
0678 };
0679 
0680 static const u32 csc_rgb_to_sdtv_offsets[] = {
0681     0x0, 0x8000000, 0x8000000
0682 };
0683 
0684 static const u16 csc_sdtv_to_rgb_matrix[] = {
0685     0x1000, 0x166f, 0x0,
0686     0x1000, 0x7483, 0x7a7f,
0687     0x1000, 0x0,    0x1c5a
0688 };
0689 
0690 static const u32 csc_sdtv_to_rgb_offsets[] = {
0691     0x0, 0x1800, 0x1800
0692 };
0693 
0694 /**
0695  * zynqmp_disp_blend_set_output_format - Set the output format of the blender
0696  * @disp: Display controller
0697  * @format: Output format
0698  *
0699  * Set the output format of the blender to @format.
0700  */
0701 static void zynqmp_disp_blend_set_output_format(struct zynqmp_disp *disp,
0702                         enum zynqmp_dpsub_format format)
0703 {
0704     static const unsigned int blend_output_fmts[] = {
0705         [ZYNQMP_DPSUB_FORMAT_RGB] = ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_RGB,
0706         [ZYNQMP_DPSUB_FORMAT_YCRCB444] = ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_YCBCR444,
0707         [ZYNQMP_DPSUB_FORMAT_YCRCB422] = ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_YCBCR422
0708                            | ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_EN_DOWNSAMPLE,
0709         [ZYNQMP_DPSUB_FORMAT_YONLY] = ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_YONLY,
0710     };
0711 
0712     u32 fmt = blend_output_fmts[format];
0713     const u16 *coeffs;
0714     const u32 *offsets;
0715     unsigned int i;
0716 
0717     zynqmp_disp_blend_write(disp, ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT, fmt);
0718     if (fmt == ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_RGB) {
0719         coeffs = csc_identity_matrix;
0720         offsets = csc_zero_offsets;
0721     } else {
0722         coeffs = csc_rgb_to_sdtv_matrix;
0723         offsets = csc_rgb_to_sdtv_offsets;
0724     }
0725 
0726     for (i = 0; i < ZYNQMP_DISP_V_BLEND_NUM_COEFF; i++)
0727         zynqmp_disp_blend_write(disp,
0728                     ZYNQMP_DISP_V_BLEND_RGB2YCBCR_COEFF(i),
0729                     coeffs[i]);
0730 
0731     for (i = 0; i < ZYNQMP_DISP_V_BLEND_NUM_OFFSET; i++)
0732         zynqmp_disp_blend_write(disp,
0733                     ZYNQMP_DISP_V_BLEND_OUTCSC_OFFSET(i),
0734                     offsets[i]);
0735 }
0736 
0737 /**
0738  * zynqmp_disp_blend_set_bg_color - Set the background color
0739  * @disp: Display controller
0740  * @rcr: Red/Cr color component
0741  * @gy: Green/Y color component
0742  * @bcb: Blue/Cb color component
0743  *
0744  * Set the background color to (@rcr, @gy, @bcb), corresponding to the R, G and
0745  * B or Cr, Y and Cb components respectively depending on the selected output
0746  * format.
0747  */
0748 static void zynqmp_disp_blend_set_bg_color(struct zynqmp_disp *disp,
0749                        u32 rcr, u32 gy, u32 bcb)
0750 {
0751     zynqmp_disp_blend_write(disp, ZYNQMP_DISP_V_BLEND_BG_CLR_0, rcr);
0752     zynqmp_disp_blend_write(disp, ZYNQMP_DISP_V_BLEND_BG_CLR_1, gy);
0753     zynqmp_disp_blend_write(disp, ZYNQMP_DISP_V_BLEND_BG_CLR_2, bcb);
0754 }
0755 
0756 /**
0757  * zynqmp_disp_blend_set_global_alpha - Configure global alpha blending
0758  * @disp: Display controller
0759  * @enable: True to enable global alpha blending
0760  * @alpha: Global alpha value (ignored if @enabled is false)
0761  */
0762 static void zynqmp_disp_blend_set_global_alpha(struct zynqmp_disp *disp,
0763                            bool enable, u32 alpha)
0764 {
0765     zynqmp_disp_blend_write(disp, ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA,
0766                 ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA_VALUE(alpha) |
0767                 (enable ? ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA_EN : 0));
0768 }
0769 
0770 /**
0771  * zynqmp_disp_blend_layer_set_csc - Configure colorspace conversion for layer
0772  * @disp: Display controller
0773  * @layer: The layer
0774  * @coeffs: Colorspace conversion matrix
0775  * @offsets: Colorspace conversion offsets
0776  *
0777  * Configure the input colorspace conversion matrix and offsets for the @layer.
0778  * Columns of the matrix are automatically swapped based on the input format to
0779  * handle RGB and YCrCb components permutations.
0780  */
0781 static void zynqmp_disp_blend_layer_set_csc(struct zynqmp_disp *disp,
0782                         struct zynqmp_disp_layer *layer,
0783                         const u16 *coeffs,
0784                         const u32 *offsets)
0785 {
0786     unsigned int swap[3] = { 0, 1, 2 };
0787     unsigned int reg;
0788     unsigned int i;
0789 
0790     if (layer->disp_fmt->swap) {
0791         if (layer->drm_fmt->is_yuv) {
0792             /* Swap U and V. */
0793             swap[1] = 2;
0794             swap[2] = 1;
0795         } else {
0796             /* Swap R and B. */
0797             swap[0] = 2;
0798             swap[2] = 0;
0799         }
0800     }
0801 
0802     if (zynqmp_disp_layer_is_video(layer))
0803         reg = ZYNQMP_DISP_V_BLEND_IN1CSC_COEFF(0);
0804     else
0805         reg = ZYNQMP_DISP_V_BLEND_IN2CSC_COEFF(0);
0806 
0807     for (i = 0; i < ZYNQMP_DISP_V_BLEND_NUM_COEFF; i += 3, reg += 12) {
0808         zynqmp_disp_blend_write(disp, reg + 0, coeffs[i + swap[0]]);
0809         zynqmp_disp_blend_write(disp, reg + 4, coeffs[i + swap[1]]);
0810         zynqmp_disp_blend_write(disp, reg + 8, coeffs[i + swap[2]]);
0811     }
0812 
0813     if (zynqmp_disp_layer_is_video(layer))
0814         reg = ZYNQMP_DISP_V_BLEND_IN1CSC_OFFSET(0);
0815     else
0816         reg = ZYNQMP_DISP_V_BLEND_IN2CSC_OFFSET(0);
0817 
0818     for (i = 0; i < ZYNQMP_DISP_V_BLEND_NUM_OFFSET; i++)
0819         zynqmp_disp_blend_write(disp, reg + i * 4, offsets[i]);
0820 }
0821 
0822 /**
0823  * zynqmp_disp_blend_layer_enable - Enable a layer
0824  * @disp: Display controller
0825  * @layer: The layer
0826  */
0827 static void zynqmp_disp_blend_layer_enable(struct zynqmp_disp *disp,
0828                        struct zynqmp_disp_layer *layer)
0829 {
0830     const u16 *coeffs;
0831     const u32 *offsets;
0832     u32 val;
0833 
0834     val = (layer->drm_fmt->is_yuv ?
0835            0 : ZYNQMP_DISP_V_BLEND_LAYER_CONTROL_RGB) |
0836           (layer->drm_fmt->hsub > 1 ?
0837            ZYNQMP_DISP_V_BLEND_LAYER_CONTROL_EN_US : 0);
0838 
0839     zynqmp_disp_blend_write(disp,
0840                 ZYNQMP_DISP_V_BLEND_LAYER_CONTROL(layer->id),
0841                 val);
0842 
0843     if (layer->drm_fmt->is_yuv) {
0844         coeffs = csc_sdtv_to_rgb_matrix;
0845         offsets = csc_sdtv_to_rgb_offsets;
0846     } else {
0847         coeffs = csc_identity_matrix;
0848         offsets = csc_zero_offsets;
0849     }
0850 
0851     zynqmp_disp_blend_layer_set_csc(disp, layer, coeffs, offsets);
0852 }
0853 
0854 /**
0855  * zynqmp_disp_blend_layer_disable - Disable a layer
0856  * @disp: Display controller
0857  * @layer: The layer
0858  */
0859 static void zynqmp_disp_blend_layer_disable(struct zynqmp_disp *disp,
0860                         struct zynqmp_disp_layer *layer)
0861 {
0862     zynqmp_disp_blend_write(disp,
0863                 ZYNQMP_DISP_V_BLEND_LAYER_CONTROL(layer->id),
0864                 0);
0865 
0866     zynqmp_disp_blend_layer_set_csc(disp, layer, csc_zero_matrix,
0867                     csc_zero_offsets);
0868 }
0869 
0870 /* -----------------------------------------------------------------------------
0871  * Audio Mixer
0872  */
0873 
0874 static void zynqmp_disp_audio_write(struct zynqmp_disp *disp, int reg, u32 val)
0875 {
0876     writel(val, disp->audio.base + reg);
0877 }
0878 
0879 /**
0880  * zynqmp_disp_audio_enable - Enable the audio mixer
0881  * @disp: Display controller
0882  *
0883  * Enable the audio mixer by de-asserting the soft reset. The audio state is set to
0884  * default values by the reset, set the default mixer volume explicitly.
0885  */
0886 static void zynqmp_disp_audio_enable(struct zynqmp_disp *disp)
0887 {
0888     /* Clear the audio soft reset register as it's an non-reset flop. */
0889     zynqmp_disp_audio_write(disp, ZYNQMP_DISP_AUD_SOFT_RESET, 0);
0890     zynqmp_disp_audio_write(disp, ZYNQMP_DISP_AUD_MIXER_VOLUME,
0891                 ZYNQMP_DISP_AUD_MIXER_VOLUME_NO_SCALE);
0892 }
0893 
0894 /**
0895  * zynqmp_disp_audio_disable - Disable the audio mixer
0896  * @disp: Display controller
0897  *
0898  * Disable the audio mixer by asserting its soft reset.
0899  */
0900 static void zynqmp_disp_audio_disable(struct zynqmp_disp *disp)
0901 {
0902     zynqmp_disp_audio_write(disp, ZYNQMP_DISP_AUD_SOFT_RESET,
0903                 ZYNQMP_DISP_AUD_SOFT_RESET_AUD_SRST);
0904 }
0905 
0906 static void zynqmp_disp_audio_init(struct zynqmp_disp *disp)
0907 {
0908     /* Try the live PL audio clock. */
0909     disp->audio.clk = devm_clk_get(disp->dev, "dp_live_audio_aclk");
0910     if (!IS_ERR(disp->audio.clk)) {
0911         disp->audio.clk_from_ps = false;
0912         return;
0913     }
0914 
0915     /* If the live PL audio clock is not valid, fall back to PS clock. */
0916     disp->audio.clk = devm_clk_get(disp->dev, "dp_aud_clk");
0917     if (!IS_ERR(disp->audio.clk)) {
0918         disp->audio.clk_from_ps = true;
0919         return;
0920     }
0921 
0922     dev_err(disp->dev, "audio disabled due to missing clock\n");
0923 }
0924 
0925 /* -----------------------------------------------------------------------------
0926  * ZynqMP Display external functions for zynqmp_dp
0927  */
0928 
0929 /**
0930  * zynqmp_disp_handle_vblank - Handle the vblank event
0931  * @disp: Display controller
0932  *
0933  * This function handles the vblank interrupt, and sends an event to
0934  * CRTC object. This will be called by the DP vblank interrupt handler.
0935  */
0936 void zynqmp_disp_handle_vblank(struct zynqmp_disp *disp)
0937 {
0938     struct drm_crtc *crtc = &disp->crtc;
0939 
0940     drm_crtc_handle_vblank(crtc);
0941 }
0942 
0943 /**
0944  * zynqmp_disp_audio_enabled - If the audio is enabled
0945  * @disp: Display controller
0946  *
0947  * Return if the audio is enabled depending on the audio clock.
0948  *
0949  * Return: true if audio is enabled, or false.
0950  */
0951 bool zynqmp_disp_audio_enabled(struct zynqmp_disp *disp)
0952 {
0953     return !!disp->audio.clk;
0954 }
0955 
0956 /**
0957  * zynqmp_disp_get_audio_clk_rate - Get the current audio clock rate
0958  * @disp: Display controller
0959  *
0960  * Return: the current audio clock rate.
0961  */
0962 unsigned int zynqmp_disp_get_audio_clk_rate(struct zynqmp_disp *disp)
0963 {
0964     if (zynqmp_disp_audio_enabled(disp))
0965         return 0;
0966     return clk_get_rate(disp->audio.clk);
0967 }
0968 
0969 /**
0970  * zynqmp_disp_get_crtc_mask - Return the CRTC bit mask
0971  * @disp: Display controller
0972  *
0973  * Return: the crtc mask of the zyqnmp_disp CRTC.
0974  */
0975 uint32_t zynqmp_disp_get_crtc_mask(struct zynqmp_disp *disp)
0976 {
0977     return drm_crtc_mask(&disp->crtc);
0978 }
0979 
0980 /* -----------------------------------------------------------------------------
0981  * ZynqMP Display Layer & DRM Plane
0982  */
0983 
0984 /**
0985  * zynqmp_disp_layer_find_format - Find format information for a DRM format
0986  * @layer: The layer
0987  * @drm_fmt: DRM format to search
0988  *
0989  * Search display subsystem format information corresponding to the given DRM
0990  * format @drm_fmt for the @layer, and return a pointer to the format
0991  * descriptor.
0992  *
0993  * Return: A pointer to the format descriptor if found, NULL otherwise
0994  */
0995 static const struct zynqmp_disp_format *
0996 zynqmp_disp_layer_find_format(struct zynqmp_disp_layer *layer,
0997                   u32 drm_fmt)
0998 {
0999     unsigned int i;
1000 
1001     for (i = 0; i < layer->info->num_formats; i++) {
1002         if (layer->info->formats[i].drm_fmt == drm_fmt)
1003             return &layer->info->formats[i];
1004     }
1005 
1006     return NULL;
1007 }
1008 
1009 /**
1010  * zynqmp_disp_layer_enable - Enable a layer
1011  * @layer: The layer
1012  *
1013  * Enable the @layer in the audio/video buffer manager and the blender. DMA
1014  * channels are started separately by zynqmp_disp_layer_update().
1015  */
1016 static void zynqmp_disp_layer_enable(struct zynqmp_disp_layer *layer)
1017 {
1018     zynqmp_disp_avbuf_enable_video(layer->disp, layer,
1019                        ZYNQMP_DISP_LAYER_NONLIVE);
1020     zynqmp_disp_blend_layer_enable(layer->disp, layer);
1021 
1022     layer->mode = ZYNQMP_DISP_LAYER_NONLIVE;
1023 }
1024 
1025 /**
1026  * zynqmp_disp_layer_disable - Disable the layer
1027  * @layer: The layer
1028  *
1029  * Disable the layer by stopping its DMA channels and disabling it in the
1030  * audio/video buffer manager and the blender.
1031  */
1032 static void zynqmp_disp_layer_disable(struct zynqmp_disp_layer *layer)
1033 {
1034     unsigned int i;
1035 
1036     for (i = 0; i < layer->drm_fmt->num_planes; i++)
1037         dmaengine_terminate_sync(layer->dmas[i].chan);
1038 
1039     zynqmp_disp_avbuf_disable_video(layer->disp, layer);
1040     zynqmp_disp_blend_layer_disable(layer->disp, layer);
1041 }
1042 
1043 /**
1044  * zynqmp_disp_layer_set_format - Set the layer format
1045  * @layer: The layer
1046  * @state: The plane state
1047  *
1048  * Set the format for @layer based on @state->fb->format. The layer must be
1049  * disabled.
1050  */
1051 static void zynqmp_disp_layer_set_format(struct zynqmp_disp_layer *layer,
1052                      struct drm_plane_state *state)
1053 {
1054     const struct drm_format_info *info = state->fb->format;
1055     unsigned int i;
1056 
1057     layer->disp_fmt = zynqmp_disp_layer_find_format(layer, info->format);
1058     layer->drm_fmt = info;
1059 
1060     zynqmp_disp_avbuf_set_format(layer->disp, layer, layer->disp_fmt);
1061 
1062     /*
1063      * Set pconfig for each DMA channel to indicate they're part of a
1064      * video group.
1065      */
1066     for (i = 0; i < info->num_planes; i++) {
1067         struct zynqmp_disp_layer_dma *dma = &layer->dmas[i];
1068         struct xilinx_dpdma_peripheral_config pconfig = {
1069             .video_group = true,
1070         };
1071         struct dma_slave_config config = {
1072             .direction = DMA_MEM_TO_DEV,
1073             .peripheral_config = &pconfig,
1074             .peripheral_size = sizeof(pconfig),
1075         };
1076 
1077         dmaengine_slave_config(dma->chan, &config);
1078     }
1079 }
1080 
1081 /**
1082  * zynqmp_disp_layer_update - Update the layer framebuffer
1083  * @layer: The layer
1084  * @state: The plane state
1085  *
1086  * Update the framebuffer for the layer by issuing a new DMA engine transaction
1087  * for the new framebuffer.
1088  *
1089  * Return: 0 on success, or the DMA descriptor failure error otherwise
1090  */
1091 static int zynqmp_disp_layer_update(struct zynqmp_disp_layer *layer,
1092                     struct drm_plane_state *state)
1093 {
1094     const struct drm_format_info *info = layer->drm_fmt;
1095     unsigned int i;
1096 
1097     for (i = 0; i < layer->drm_fmt->num_planes; i++) {
1098         unsigned int width = state->crtc_w / (i ? info->hsub : 1);
1099         unsigned int height = state->crtc_h / (i ? info->vsub : 1);
1100         struct zynqmp_disp_layer_dma *dma = &layer->dmas[i];
1101         struct dma_async_tx_descriptor *desc;
1102         dma_addr_t paddr;
1103 
1104         paddr = drm_fb_cma_get_gem_addr(state->fb, state, i);
1105 
1106         dma->xt.numf = height;
1107         dma->sgl.size = width * info->cpp[i];
1108         dma->sgl.icg = state->fb->pitches[i] - dma->sgl.size;
1109         dma->xt.src_start = paddr;
1110         dma->xt.frame_size = 1;
1111         dma->xt.dir = DMA_MEM_TO_DEV;
1112         dma->xt.src_sgl = true;
1113         dma->xt.dst_sgl = false;
1114 
1115         desc = dmaengine_prep_interleaved_dma(dma->chan, &dma->xt,
1116                               DMA_CTRL_ACK |
1117                               DMA_PREP_REPEAT |
1118                               DMA_PREP_LOAD_EOT);
1119         if (!desc) {
1120             dev_err(layer->disp->dev,
1121                 "failed to prepare DMA descriptor\n");
1122             return -ENOMEM;
1123         }
1124 
1125         dmaengine_submit(desc);
1126         dma_async_issue_pending(dma->chan);
1127     }
1128 
1129     return 0;
1130 }
1131 
1132 static inline struct zynqmp_disp_layer *plane_to_layer(struct drm_plane *plane)
1133 {
1134     return container_of(plane, struct zynqmp_disp_layer, plane);
1135 }
1136 
1137 static int
1138 zynqmp_disp_plane_atomic_check(struct drm_plane *plane,
1139                    struct drm_atomic_state *state)
1140 {
1141     struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
1142                                          plane);
1143     struct drm_crtc_state *crtc_state;
1144 
1145     if (!new_plane_state->crtc)
1146         return 0;
1147 
1148     crtc_state = drm_atomic_get_crtc_state(state, new_plane_state->crtc);
1149     if (IS_ERR(crtc_state))
1150         return PTR_ERR(crtc_state);
1151 
1152     return drm_atomic_helper_check_plane_state(new_plane_state,
1153                            crtc_state,
1154                            DRM_PLANE_HELPER_NO_SCALING,
1155                            DRM_PLANE_HELPER_NO_SCALING,
1156                            false, false);
1157 }
1158 
1159 static void
1160 zynqmp_disp_plane_atomic_disable(struct drm_plane *plane,
1161                  struct drm_atomic_state *state)
1162 {
1163     struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
1164                                        plane);
1165     struct zynqmp_disp_layer *layer = plane_to_layer(plane);
1166 
1167     if (!old_state->fb)
1168         return;
1169 
1170     zynqmp_disp_layer_disable(layer);
1171 
1172     if (zynqmp_disp_layer_is_gfx(layer))
1173         zynqmp_disp_blend_set_global_alpha(layer->disp, false,
1174                            plane->state->alpha >> 8);
1175 }
1176 
1177 static void
1178 zynqmp_disp_plane_atomic_update(struct drm_plane *plane,
1179                 struct drm_atomic_state *state)
1180 {
1181     struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane);
1182     struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane);
1183     struct zynqmp_disp_layer *layer = plane_to_layer(plane);
1184     bool format_changed = false;
1185 
1186     if (!old_state->fb ||
1187         old_state->fb->format->format != new_state->fb->format->format)
1188         format_changed = true;
1189 
1190     /*
1191      * If the format has changed (including going from a previously
1192      * disabled state to any format), reconfigure the format. Disable the
1193      * plane first if needed.
1194      */
1195     if (format_changed) {
1196         if (old_state->fb)
1197             zynqmp_disp_layer_disable(layer);
1198 
1199         zynqmp_disp_layer_set_format(layer, new_state);
1200     }
1201 
1202     zynqmp_disp_layer_update(layer, new_state);
1203 
1204     if (zynqmp_disp_layer_is_gfx(layer))
1205         zynqmp_disp_blend_set_global_alpha(layer->disp, true,
1206                            plane->state->alpha >> 8);
1207 
1208     /* Enable or re-enable the plane is the format has changed. */
1209     if (format_changed)
1210         zynqmp_disp_layer_enable(layer);
1211 }
1212 
1213 static const struct drm_plane_helper_funcs zynqmp_disp_plane_helper_funcs = {
1214     .atomic_check       = zynqmp_disp_plane_atomic_check,
1215     .atomic_update      = zynqmp_disp_plane_atomic_update,
1216     .atomic_disable     = zynqmp_disp_plane_atomic_disable,
1217 };
1218 
1219 static const struct drm_plane_funcs zynqmp_disp_plane_funcs = {
1220     .update_plane       = drm_atomic_helper_update_plane,
1221     .disable_plane      = drm_atomic_helper_disable_plane,
1222     .destroy        = drm_plane_cleanup,
1223     .reset          = drm_atomic_helper_plane_reset,
1224     .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
1225     .atomic_destroy_state   = drm_atomic_helper_plane_destroy_state,
1226 };
1227 
1228 static int zynqmp_disp_create_planes(struct zynqmp_disp *disp)
1229 {
1230     unsigned int i, j;
1231     int ret;
1232 
1233     for (i = 0; i < ZYNQMP_DISP_NUM_LAYERS; i++) {
1234         struct zynqmp_disp_layer *layer = &disp->layers[i];
1235         enum drm_plane_type type;
1236         u32 *drm_formats;
1237 
1238         drm_formats = drmm_kcalloc(disp->drm, sizeof(*drm_formats),
1239                        layer->info->num_formats,
1240                        GFP_KERNEL);
1241         if (!drm_formats)
1242             return -ENOMEM;
1243 
1244         for (j = 0; j < layer->info->num_formats; ++j)
1245             drm_formats[j] = layer->info->formats[j].drm_fmt;
1246 
1247         /* Graphics layer is primary, and video layer is overlay. */
1248         type = zynqmp_disp_layer_is_video(layer)
1249              ? DRM_PLANE_TYPE_OVERLAY : DRM_PLANE_TYPE_PRIMARY;
1250         ret = drm_universal_plane_init(disp->drm, &layer->plane, 0,
1251                            &zynqmp_disp_plane_funcs,
1252                            drm_formats,
1253                            layer->info->num_formats,
1254                            NULL, type, NULL);
1255         if (ret)
1256             return ret;
1257 
1258         drm_plane_helper_add(&layer->plane,
1259                      &zynqmp_disp_plane_helper_funcs);
1260 
1261         drm_plane_create_zpos_immutable_property(&layer->plane, i);
1262         if (zynqmp_disp_layer_is_gfx(layer))
1263             drm_plane_create_alpha_property(&layer->plane);
1264     }
1265 
1266     return 0;
1267 }
1268 
1269 /**
1270  * zynqmp_disp_layer_release_dma - Release DMA channels for a layer
1271  * @disp: Display controller
1272  * @layer: The layer
1273  *
1274  * Release the DMA channels associated with @layer.
1275  */
1276 static void zynqmp_disp_layer_release_dma(struct zynqmp_disp *disp,
1277                       struct zynqmp_disp_layer *layer)
1278 {
1279     unsigned int i;
1280 
1281     if (!layer->info)
1282         return;
1283 
1284     for (i = 0; i < layer->info->num_channels; i++) {
1285         struct zynqmp_disp_layer_dma *dma = &layer->dmas[i];
1286 
1287         if (!dma->chan)
1288             continue;
1289 
1290         /* Make sure the channel is terminated before release. */
1291         dmaengine_terminate_sync(dma->chan);
1292         dma_release_channel(dma->chan);
1293     }
1294 }
1295 
1296 /**
1297  * zynqmp_disp_destroy_layers - Destroy all layers
1298  * @disp: Display controller
1299  */
1300 static void zynqmp_disp_destroy_layers(struct zynqmp_disp *disp)
1301 {
1302     unsigned int i;
1303 
1304     for (i = 0; i < ZYNQMP_DISP_NUM_LAYERS; i++)
1305         zynqmp_disp_layer_release_dma(disp, &disp->layers[i]);
1306 }
1307 
1308 /**
1309  * zynqmp_disp_layer_request_dma - Request DMA channels for a layer
1310  * @disp: Display controller
1311  * @layer: The layer
1312  *
1313  * Request all DMA engine channels needed by @layer.
1314  *
1315  * Return: 0 on success, or the DMA channel request error otherwise
1316  */
1317 static int zynqmp_disp_layer_request_dma(struct zynqmp_disp *disp,
1318                      struct zynqmp_disp_layer *layer)
1319 {
1320     static const char * const dma_names[] = { "vid", "gfx" };
1321     unsigned int i;
1322     int ret;
1323 
1324     for (i = 0; i < layer->info->num_channels; i++) {
1325         struct zynqmp_disp_layer_dma *dma = &layer->dmas[i];
1326         char dma_channel_name[16];
1327 
1328         snprintf(dma_channel_name, sizeof(dma_channel_name),
1329              "%s%u", dma_names[layer->id], i);
1330         dma->chan = dma_request_chan(disp->dev, dma_channel_name);
1331         if (IS_ERR(dma->chan)) {
1332             dev_err(disp->dev, "failed to request dma channel\n");
1333             ret = PTR_ERR(dma->chan);
1334             dma->chan = NULL;
1335             return ret;
1336         }
1337     }
1338 
1339     return 0;
1340 }
1341 
1342 /**
1343  * zynqmp_disp_create_layers - Create and initialize all layers
1344  * @disp: Display controller
1345  *
1346  * Return: 0 on success, or the DMA channel request error otherwise
1347  */
1348 static int zynqmp_disp_create_layers(struct zynqmp_disp *disp)
1349 {
1350     static const struct zynqmp_disp_layer_info layer_info[] = {
1351         [ZYNQMP_DISP_LAYER_VID] = {
1352             .formats = avbuf_vid_fmts,
1353             .num_formats = ARRAY_SIZE(avbuf_vid_fmts),
1354             .num_channels = 3,
1355         },
1356         [ZYNQMP_DISP_LAYER_GFX] = {
1357             .formats = avbuf_gfx_fmts,
1358             .num_formats = ARRAY_SIZE(avbuf_gfx_fmts),
1359             .num_channels = 1,
1360         },
1361     };
1362 
1363     unsigned int i;
1364     int ret;
1365 
1366     for (i = 0; i < ZYNQMP_DISP_NUM_LAYERS; i++) {
1367         struct zynqmp_disp_layer *layer = &disp->layers[i];
1368 
1369         layer->id = i;
1370         layer->disp = disp;
1371         layer->info = &layer_info[i];
1372 
1373         ret = zynqmp_disp_layer_request_dma(disp, layer);
1374         if (ret)
1375             goto err;
1376     }
1377 
1378     return 0;
1379 
1380 err:
1381     zynqmp_disp_destroy_layers(disp);
1382     return ret;
1383 }
1384 
1385 /* -----------------------------------------------------------------------------
1386  * ZynqMP Display & DRM CRTC
1387  */
1388 
1389 /**
1390  * zynqmp_disp_enable - Enable the display controller
1391  * @disp: Display controller
1392  */
1393 static void zynqmp_disp_enable(struct zynqmp_disp *disp)
1394 {
1395     zynqmp_disp_avbuf_enable(disp);
1396     /* Choose clock source based on the DT clock handle. */
1397     zynqmp_disp_avbuf_set_clocks_sources(disp, disp->pclk_from_ps,
1398                          disp->audio.clk_from_ps, true);
1399     zynqmp_disp_avbuf_enable_channels(disp);
1400     zynqmp_disp_avbuf_enable_audio(disp);
1401 
1402     zynqmp_disp_audio_enable(disp);
1403 }
1404 
1405 /**
1406  * zynqmp_disp_disable - Disable the display controller
1407  * @disp: Display controller
1408  */
1409 static void zynqmp_disp_disable(struct zynqmp_disp *disp)
1410 {
1411     zynqmp_disp_audio_disable(disp);
1412 
1413     zynqmp_disp_avbuf_disable_audio(disp);
1414     zynqmp_disp_avbuf_disable_channels(disp);
1415     zynqmp_disp_avbuf_disable(disp);
1416 }
1417 
1418 static inline struct zynqmp_disp *crtc_to_disp(struct drm_crtc *crtc)
1419 {
1420     return container_of(crtc, struct zynqmp_disp, crtc);
1421 }
1422 
1423 static int zynqmp_disp_crtc_setup_clock(struct drm_crtc *crtc,
1424                     struct drm_display_mode *adjusted_mode)
1425 {
1426     struct zynqmp_disp *disp = crtc_to_disp(crtc);
1427     unsigned long mode_clock = adjusted_mode->clock * 1000;
1428     unsigned long rate;
1429     long diff;
1430     int ret;
1431 
1432     ret = clk_set_rate(disp->pclk, mode_clock);
1433     if (ret) {
1434         dev_err(disp->dev, "failed to set a pixel clock\n");
1435         return ret;
1436     }
1437 
1438     rate = clk_get_rate(disp->pclk);
1439     diff = rate - mode_clock;
1440     if (abs(diff) > mode_clock / 20)
1441         dev_info(disp->dev,
1442              "requested pixel rate: %lu actual rate: %lu\n",
1443              mode_clock, rate);
1444     else
1445         dev_dbg(disp->dev,
1446             "requested pixel rate: %lu actual rate: %lu\n",
1447             mode_clock, rate);
1448 
1449     return 0;
1450 }
1451 
1452 static void
1453 zynqmp_disp_crtc_atomic_enable(struct drm_crtc *crtc,
1454                    struct drm_atomic_state *state)
1455 {
1456     struct zynqmp_disp *disp = crtc_to_disp(crtc);
1457     struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode;
1458     int ret, vrefresh;
1459 
1460     pm_runtime_get_sync(disp->dev);
1461 
1462     zynqmp_disp_crtc_setup_clock(crtc, adjusted_mode);
1463 
1464     ret = clk_prepare_enable(disp->pclk);
1465     if (ret) {
1466         dev_err(disp->dev, "failed to enable a pixel clock\n");
1467         pm_runtime_put_sync(disp->dev);
1468         return;
1469     }
1470 
1471     zynqmp_disp_blend_set_output_format(disp, ZYNQMP_DPSUB_FORMAT_RGB);
1472     zynqmp_disp_blend_set_bg_color(disp, 0, 0, 0);
1473 
1474     zynqmp_disp_enable(disp);
1475 
1476     /* Delay of 3 vblank intervals for timing gen to be stable */
1477     vrefresh = (adjusted_mode->clock * 1000) /
1478            (adjusted_mode->vtotal * adjusted_mode->htotal);
1479     msleep(3 * 1000 / vrefresh);
1480 }
1481 
1482 static void
1483 zynqmp_disp_crtc_atomic_disable(struct drm_crtc *crtc,
1484                 struct drm_atomic_state *state)
1485 {
1486     struct zynqmp_disp *disp = crtc_to_disp(crtc);
1487     struct drm_plane_state *old_plane_state;
1488 
1489     /*
1490      * Disable the plane if active. The old plane state can be NULL in the
1491      * .shutdown() path if the plane is already disabled, skip
1492      * zynqmp_disp_plane_atomic_disable() in that case.
1493      */
1494     old_plane_state = drm_atomic_get_old_plane_state(state, crtc->primary);
1495     if (old_plane_state)
1496         zynqmp_disp_plane_atomic_disable(crtc->primary, state);
1497 
1498     zynqmp_disp_disable(disp);
1499 
1500     drm_crtc_vblank_off(&disp->crtc);
1501 
1502     spin_lock_irq(&crtc->dev->event_lock);
1503     if (crtc->state->event) {
1504         drm_crtc_send_vblank_event(crtc, crtc->state->event);
1505         crtc->state->event = NULL;
1506     }
1507     spin_unlock_irq(&crtc->dev->event_lock);
1508 
1509     clk_disable_unprepare(disp->pclk);
1510     pm_runtime_put_sync(disp->dev);
1511 }
1512 
1513 static int zynqmp_disp_crtc_atomic_check(struct drm_crtc *crtc,
1514                      struct drm_atomic_state *state)
1515 {
1516     return drm_atomic_add_affected_planes(state, crtc);
1517 }
1518 
1519 static void
1520 zynqmp_disp_crtc_atomic_begin(struct drm_crtc *crtc,
1521                   struct drm_atomic_state *state)
1522 {
1523     drm_crtc_vblank_on(crtc);
1524 }
1525 
1526 static void
1527 zynqmp_disp_crtc_atomic_flush(struct drm_crtc *crtc,
1528                   struct drm_atomic_state *state)
1529 {
1530     if (crtc->state->event) {
1531         struct drm_pending_vblank_event *event;
1532 
1533         /* Consume the flip_done event from atomic helper. */
1534         event = crtc->state->event;
1535         crtc->state->event = NULL;
1536 
1537         event->pipe = drm_crtc_index(crtc);
1538 
1539         WARN_ON(drm_crtc_vblank_get(crtc) != 0);
1540 
1541         spin_lock_irq(&crtc->dev->event_lock);
1542         drm_crtc_arm_vblank_event(crtc, event);
1543         spin_unlock_irq(&crtc->dev->event_lock);
1544     }
1545 }
1546 
1547 static const struct drm_crtc_helper_funcs zynqmp_disp_crtc_helper_funcs = {
1548     .atomic_enable  = zynqmp_disp_crtc_atomic_enable,
1549     .atomic_disable = zynqmp_disp_crtc_atomic_disable,
1550     .atomic_check   = zynqmp_disp_crtc_atomic_check,
1551     .atomic_begin   = zynqmp_disp_crtc_atomic_begin,
1552     .atomic_flush   = zynqmp_disp_crtc_atomic_flush,
1553 };
1554 
1555 static int zynqmp_disp_crtc_enable_vblank(struct drm_crtc *crtc)
1556 {
1557     struct zynqmp_disp *disp = crtc_to_disp(crtc);
1558 
1559     zynqmp_dp_enable_vblank(disp->dpsub->dp);
1560 
1561     return 0;
1562 }
1563 
1564 static void zynqmp_disp_crtc_disable_vblank(struct drm_crtc *crtc)
1565 {
1566     struct zynqmp_disp *disp = crtc_to_disp(crtc);
1567 
1568     zynqmp_dp_disable_vblank(disp->dpsub->dp);
1569 }
1570 
1571 static const struct drm_crtc_funcs zynqmp_disp_crtc_funcs = {
1572     .destroy        = drm_crtc_cleanup,
1573     .set_config     = drm_atomic_helper_set_config,
1574     .page_flip      = drm_atomic_helper_page_flip,
1575     .reset          = drm_atomic_helper_crtc_reset,
1576     .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
1577     .atomic_destroy_state   = drm_atomic_helper_crtc_destroy_state,
1578     .enable_vblank      = zynqmp_disp_crtc_enable_vblank,
1579     .disable_vblank     = zynqmp_disp_crtc_disable_vblank,
1580 };
1581 
1582 static int zynqmp_disp_create_crtc(struct zynqmp_disp *disp)
1583 {
1584     struct drm_plane *plane = &disp->layers[ZYNQMP_DISP_LAYER_GFX].plane;
1585     int ret;
1586 
1587     ret = drm_crtc_init_with_planes(disp->drm, &disp->crtc, plane,
1588                     NULL, &zynqmp_disp_crtc_funcs, NULL);
1589     if (ret < 0)
1590         return ret;
1591 
1592     drm_crtc_helper_add(&disp->crtc, &zynqmp_disp_crtc_helper_funcs);
1593 
1594     /* Start with vertical blanking interrupt reporting disabled. */
1595     drm_crtc_vblank_off(&disp->crtc);
1596 
1597     return 0;
1598 }
1599 
1600 static void zynqmp_disp_map_crtc_to_plane(struct zynqmp_disp *disp)
1601 {
1602     u32 possible_crtcs = drm_crtc_mask(&disp->crtc);
1603     unsigned int i;
1604 
1605     for (i = 0; i < ZYNQMP_DISP_NUM_LAYERS; i++)
1606         disp->layers[i].plane.possible_crtcs = possible_crtcs;
1607 }
1608 
1609 /* -----------------------------------------------------------------------------
1610  * Initialization & Cleanup
1611  */
1612 
1613 int zynqmp_disp_drm_init(struct zynqmp_dpsub *dpsub)
1614 {
1615     struct zynqmp_disp *disp = dpsub->disp;
1616     int ret;
1617 
1618     ret = zynqmp_disp_create_planes(disp);
1619     if (ret)
1620         return ret;
1621 
1622     ret = zynqmp_disp_create_crtc(disp);
1623     if (ret < 0)
1624         return ret;
1625 
1626     zynqmp_disp_map_crtc_to_plane(disp);
1627 
1628     return 0;
1629 }
1630 
1631 int zynqmp_disp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm)
1632 {
1633     struct platform_device *pdev = to_platform_device(dpsub->dev);
1634     struct zynqmp_disp *disp;
1635     struct zynqmp_disp_layer *layer;
1636     struct resource *res;
1637     int ret;
1638 
1639     disp = drmm_kzalloc(drm, sizeof(*disp), GFP_KERNEL);
1640     if (!disp)
1641         return -ENOMEM;
1642 
1643     disp->dev = &pdev->dev;
1644     disp->dpsub = dpsub;
1645     disp->drm = drm;
1646 
1647     dpsub->disp = disp;
1648 
1649     res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "blend");
1650     disp->blend.base = devm_ioremap_resource(disp->dev, res);
1651     if (IS_ERR(disp->blend.base))
1652         return PTR_ERR(disp->blend.base);
1653 
1654     res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "av_buf");
1655     disp->avbuf.base = devm_ioremap_resource(disp->dev, res);
1656     if (IS_ERR(disp->avbuf.base))
1657         return PTR_ERR(disp->avbuf.base);
1658 
1659     res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "aud");
1660     disp->audio.base = devm_ioremap_resource(disp->dev, res);
1661     if (IS_ERR(disp->audio.base))
1662         return PTR_ERR(disp->audio.base);
1663 
1664     /* Try the live PL video clock */
1665     disp->pclk = devm_clk_get(disp->dev, "dp_live_video_in_clk");
1666     if (!IS_ERR(disp->pclk))
1667         disp->pclk_from_ps = false;
1668     else if (PTR_ERR(disp->pclk) == -EPROBE_DEFER)
1669         return PTR_ERR(disp->pclk);
1670 
1671     /* If the live PL video clock is not valid, fall back to PS clock */
1672     if (IS_ERR_OR_NULL(disp->pclk)) {
1673         disp->pclk = devm_clk_get(disp->dev, "dp_vtc_pixel_clk_in");
1674         if (IS_ERR(disp->pclk)) {
1675             dev_err(disp->dev, "failed to init any video clock\n");
1676             return PTR_ERR(disp->pclk);
1677         }
1678         disp->pclk_from_ps = true;
1679     }
1680 
1681     zynqmp_disp_audio_init(disp);
1682 
1683     ret = zynqmp_disp_create_layers(disp);
1684     if (ret)
1685         return ret;
1686 
1687     layer = &disp->layers[ZYNQMP_DISP_LAYER_VID];
1688     dpsub->dma_align = 1 << layer->dmas[0].chan->device->copy_align;
1689 
1690     return 0;
1691 }
1692 
1693 void zynqmp_disp_remove(struct zynqmp_dpsub *dpsub)
1694 {
1695     struct zynqmp_disp *disp = dpsub->disp;
1696 
1697     zynqmp_disp_destroy_layers(disp);
1698 }