From a19effb6dbe5bd1be77a6d68eba04dba8993ffeb Mon Sep 17 00:00:00 2001 From: Quanyang Wang Date: Wed, 10 Mar 2021 12:59:45 +0800 Subject: [PATCH 1/9] drm: xlnx: zynqmp_dpsub: Call pm_runtime_get_sync before setting pixel clock The Runtime PM subsystem will force the device "fd4a0000.zynqmp-display" to enter suspend state while booting if the following conditions are met: - the usage counter is zero (pm_runtime_get_sync hasn't been called yet) - no 'active' children (no zynqmp-dp-snd-xx node under dpsub node) - no other device in the same power domain (dpdma node has no "power-domains = <&zynqmp_firmware PD_DP>" property) So there is a scenario as below: 1) DP device enters suspend state <- call zynqmp_gpd_power_off 2) zynqmp_disp_crtc_setup_clock <- configurate register VPLL_FRAC_CFG 3) pm_runtime_get_sync <- call zynqmp_gpd_power_on and clear previous VPLL_FRAC_CFG configuration 4) clk_prepare_enable(disp->pclk) <- enable failed since VPLL_FRAC_CFG configuration is corrupted From above, we can see that pm_runtime_get_sync may clear register VPLL_FRAC_CFG configuration and result the failure of clk enabling. Putting pm_runtime_get_sync at the very beginning of the function zynqmp_disp_crtc_atomic_enable can resolve this issue. Signed-off-by: Quanyang Wang Reviewed-by: Laurent Pinchart Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/xlnx/zynqmp_disp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c index 109d627968ac..01c6ce7784dd 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_disp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c @@ -1452,9 +1452,10 @@ zynqmp_disp_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode; int ret, vrefresh; + pm_runtime_get_sync(disp->dev); + zynqmp_disp_crtc_setup_clock(crtc, adjusted_mode); - pm_runtime_get_sync(disp->dev); ret = clk_prepare_enable(disp->pclk); if (ret) { dev_err(disp->dev, "failed to enable a pixel clock\n"); From 97271c7ee1cfb2c3fcc951e4031ffabb7c33f5b1 Mon Sep 17 00:00:00 2001 From: Dylan Yip Date: Thu, 18 Feb 2021 12:31:22 -0800 Subject: [PATCH 2/9] drm: xlnx: zynqmp_dpsub: Update dependencies for ZynqMP DP ZynqMP DP requires the ZynqMP PHY and DPDMA to operate properly. So depend on both the PHY and DPDMA. Signed-off-by: Dylan Yip Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/xlnx/Kconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/xlnx/Kconfig b/drivers/gpu/drm/xlnx/Kconfig index b52c6cdfc0b8..c3d08269faa9 100644 --- a/drivers/gpu/drm/xlnx/Kconfig +++ b/drivers/gpu/drm/xlnx/Kconfig @@ -3,6 +3,8 @@ config DRM_ZYNQMP_DPSUB depends on ARCH_ZYNQMP || COMPILE_TEST depends on COMMON_CLK && DRM && OF depends on DMADEVICES + depends on PHY_XILINX_ZYNQMP + depends on XILINX_ZYNQMP_DPDMA select DMA_ENGINE select DRM_GEM_CMA_HELPER select DRM_KMS_CMA_HELPER From a338619bd76011035d462f0f9e8b2f24d7fe5a1e Mon Sep 17 00:00:00 2001 From: Quanyang Wang Date: Tue, 23 Mar 2021 10:55:01 +0800 Subject: [PATCH 3/9] drm: xlnx: zynqmp: release reset to DP controller before accessing DP registers When insmod zynqmp-dpsub.ko after rmmod it, system will hang with the error log as below: root@xilinx-zynqmp:~# insmod zynqmp-dpsub.ko [ 88.391289] [drm] Initialized zynqmp-dpsub 1.0.0 20130509 for fd4a0000.display on minor 0 [ 88.529906] Console: switching to colour frame buffer device 128x48 [ 88.549402] zynqmp-dpsub fd4a0000.display: [drm] fb0: zynqmp-dpsubdrm frame buffer device [ 88.571624] zynqmp-dpsub fd4a0000.display: ZynqMP DisplayPort Subsystem driver probed root@xilinx-zynqmp:~# rmmod zynqmp_dpsub [ 94.023404] Console: switching to colour dummy device 80x25 root@xilinx-zynqmp:~# insmod zynqmp-dpsub.ko This is because that in zynqmp_dp_probe it tries to access some DP registers while the DP controller is still in the reset state. When running "rmmod zynqmp_dpsub", zynqmp_dp_reset(dp, true) in zynqmp_dp_phy_exit is called to force the DP controller into the reset state. Then insmod will call zynqmp_dp_probe to program the DP registers, but at this moment the DP controller hasn't been brought out of the reset state yet since the function zynqmp_dp_reset(dp, false) is called later and this will result the system hang. Releasing the reset to DP controller before any read/write operation to it will fix this issue. And for symmetry, move zynqmp_dp_reset() call from zynqmp_dp_phy_exit() to zynqmp_dp_remove(). Signed-off-by: Quanyang Wang Reviewed-by: Laurent Pinchart Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/xlnx/zynqmp_dp.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c index 82430ca9b913..6f588dc09ba6 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c @@ -402,10 +402,6 @@ static int zynqmp_dp_phy_init(struct zynqmp_dp *dp) } } - ret = zynqmp_dp_reset(dp, false); - if (ret < 0) - return ret; - zynqmp_dp_clr(dp, ZYNQMP_DP_PHY_RESET, ZYNQMP_DP_PHY_RESET_ALL_RESET); /* @@ -441,8 +437,6 @@ static void zynqmp_dp_phy_exit(struct zynqmp_dp *dp) ret); } - zynqmp_dp_reset(dp, true); - for (i = 0; i < dp->num_lanes; i++) { ret = phy_exit(dp->phy[i]); if (ret) @@ -1683,9 +1677,13 @@ int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm) return PTR_ERR(dp->reset); } + ret = zynqmp_dp_reset(dp, false); + if (ret < 0) + return ret; + ret = zynqmp_dp_phy_probe(dp); if (ret) - return ret; + goto err_reset; /* Initialize the hardware. */ zynqmp_dp_write(dp, ZYNQMP_DP_TX_PHY_POWER_DOWN, @@ -1697,7 +1695,7 @@ int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm) ret = zynqmp_dp_phy_init(dp); if (ret) - return ret; + goto err_reset; zynqmp_dp_write(dp, ZYNQMP_DP_TRANSMITTER_ENABLE, 1); @@ -1709,15 +1707,18 @@ int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm) zynqmp_dp_irq_handler, IRQF_ONESHOT, dev_name(dp->dev), dp); if (ret < 0) - goto error; + goto err_phy_exit; dev_dbg(dp->dev, "ZynqMP DisplayPort Tx probed with %u lanes\n", dp->num_lanes); return 0; -error: +err_phy_exit: zynqmp_dp_phy_exit(dp); +err_reset: + zynqmp_dp_reset(dp, true); + return ret; } @@ -1735,4 +1736,5 @@ void zynqmp_dp_remove(struct zynqmp_dpsub *dpsub) zynqmp_dp_write(dp, ZYNQMP_DP_INT_DS, 0xffffffff); zynqmp_dp_phy_exit(dp); + zynqmp_dp_reset(dp, true); } From 1e42874b0df79a65fe9a2f4c2d3a71d5b64bcfb6 Mon Sep 17 00:00:00 2001 From: Quanyang Wang Date: Tue, 18 May 2021 17:50:18 +0800 Subject: [PATCH 4/9] drm: xlnx: zynqmp: Add zynqmp_disp_layer_is_video() to simplify the code Add a new function zynqmp_disp_layer_is_video() to simplify the code that judges if a layer is the video layer. Acked-by: Paul Cercueil Signed-off-by: Quanyang Wang Reviewed-by: Laurent Pinchart [Renamed is_layer_vid() to zynqmp_disp_layer_is_video()]] Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/xlnx/zynqmp_disp.c | 39 +++++++++++++++++------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c index 01c6ce7784dd..a578ab3d5f89 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_disp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c @@ -434,30 +434,35 @@ static void zynqmp_disp_avbuf_write(struct zynqmp_disp_avbuf *avbuf, writel(val, avbuf->base + reg); } +static bool zynqmp_disp_layer_is_video(const struct zynqmp_disp_layer *layer) +{ + return layer->id == ZYNQMP_DISP_LAYER_VID; +} + /** * zynqmp_disp_avbuf_set_format - Set the input format for a layer * @avbuf: Audio/video buffer manager - * @layer: The layer ID + * @layer: The layer * @fmt: The format information * * Set the video buffer manager format for @layer to @fmt. */ static void zynqmp_disp_avbuf_set_format(struct zynqmp_disp_avbuf *avbuf, - enum zynqmp_disp_layer_id layer, + struct zynqmp_disp_layer *layer, const struct zynqmp_disp_format *fmt) { unsigned int i; u32 val; val = zynqmp_disp_avbuf_read(avbuf, ZYNQMP_DISP_AV_BUF_FMT); - val &= layer == ZYNQMP_DISP_LAYER_VID + val &= zynqmp_disp_layer_is_video(layer) ? ~ZYNQMP_DISP_AV_BUF_FMT_NL_VID_MASK : ~ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_MASK; val |= fmt->buf_fmt; zynqmp_disp_avbuf_write(avbuf, ZYNQMP_DISP_AV_BUF_FMT, val); for (i = 0; i < ZYNQMP_DISP_AV_BUF_NUM_SF; i++) { - unsigned int reg = layer == ZYNQMP_DISP_LAYER_VID + unsigned int reg = zynqmp_disp_layer_is_video(layer) ? ZYNQMP_DISP_AV_BUF_VID_COMP_SF(i) : ZYNQMP_DISP_AV_BUF_GFX_COMP_SF(i); @@ -573,19 +578,19 @@ static void zynqmp_disp_avbuf_disable_audio(struct zynqmp_disp_avbuf *avbuf) /** * zynqmp_disp_avbuf_enable_video - Enable a video layer * @avbuf: Audio/video buffer manager - * @layer: The layer ID + * @layer: The layer * @mode: Operating mode of layer * * Enable the video/graphics buffer for @layer. */ static void zynqmp_disp_avbuf_enable_video(struct zynqmp_disp_avbuf *avbuf, - enum zynqmp_disp_layer_id layer, + struct zynqmp_disp_layer *layer, enum zynqmp_disp_layer_mode mode) { u32 val; val = zynqmp_disp_avbuf_read(avbuf, ZYNQMP_DISP_AV_BUF_OUTPUT); - if (layer == ZYNQMP_DISP_LAYER_VID) { + if (zynqmp_disp_layer_is_video(layer)) { val &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_MASK; if (mode == ZYNQMP_DISP_LAYER_NONLIVE) val |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_MEM; @@ -605,17 +610,17 @@ static void zynqmp_disp_avbuf_enable_video(struct zynqmp_disp_avbuf *avbuf, /** * zynqmp_disp_avbuf_disable_video - Disable a video layer * @avbuf: Audio/video buffer manager - * @layer: The layer ID + * @layer: The layer * * Disable the video/graphics buffer for @layer. */ static void zynqmp_disp_avbuf_disable_video(struct zynqmp_disp_avbuf *avbuf, - enum zynqmp_disp_layer_id layer) + struct zynqmp_disp_layer *layer) { u32 val; val = zynqmp_disp_avbuf_read(avbuf, ZYNQMP_DISP_AV_BUF_OUTPUT); - if (layer == ZYNQMP_DISP_LAYER_VID) { + if (zynqmp_disp_layer_is_video(layer)) { val &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_MASK; val |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_NONE; } else { @@ -807,7 +812,7 @@ static void zynqmp_disp_blend_layer_set_csc(struct zynqmp_disp_blend *blend, } } - if (layer->id == ZYNQMP_DISP_LAYER_VID) + if (zynqmp_disp_layer_is_video(layer)) reg = ZYNQMP_DISP_V_BLEND_IN1CSC_COEFF(0); else reg = ZYNQMP_DISP_V_BLEND_IN2CSC_COEFF(0); @@ -818,7 +823,7 @@ static void zynqmp_disp_blend_layer_set_csc(struct zynqmp_disp_blend *blend, zynqmp_disp_blend_write(blend, reg + 8, coeffs[i + swap[2]]); } - if (layer->id == ZYNQMP_DISP_LAYER_VID) + if (zynqmp_disp_layer_is_video(layer)) reg = ZYNQMP_DISP_V_BLEND_IN1CSC_OFFSET(0); else reg = ZYNQMP_DISP_V_BLEND_IN2CSC_OFFSET(0); @@ -1025,7 +1030,7 @@ zynqmp_disp_layer_find_format(struct zynqmp_disp_layer *layer, */ static void zynqmp_disp_layer_enable(struct zynqmp_disp_layer *layer) { - zynqmp_disp_avbuf_enable_video(&layer->disp->avbuf, layer->id, + zynqmp_disp_avbuf_enable_video(&layer->disp->avbuf, layer, ZYNQMP_DISP_LAYER_NONLIVE); zynqmp_disp_blend_layer_enable(&layer->disp->blend, layer); @@ -1046,7 +1051,7 @@ static void zynqmp_disp_layer_disable(struct zynqmp_disp_layer *layer) for (i = 0; i < layer->drm_fmt->num_planes; i++) dmaengine_terminate_sync(layer->dmas[i].chan); - zynqmp_disp_avbuf_disable_video(&layer->disp->avbuf, layer->id); + zynqmp_disp_avbuf_disable_video(&layer->disp->avbuf, layer); zynqmp_disp_blend_layer_disable(&layer->disp->blend, layer); } @@ -1067,7 +1072,7 @@ static void zynqmp_disp_layer_set_format(struct zynqmp_disp_layer *layer, layer->disp_fmt = zynqmp_disp_layer_find_format(layer, info->format); layer->drm_fmt = info; - zynqmp_disp_avbuf_set_format(&layer->disp->avbuf, layer->id, + zynqmp_disp_avbuf_set_format(&layer->disp->avbuf, layer, layer->disp_fmt); /* @@ -1244,8 +1249,8 @@ static int zynqmp_disp_create_planes(struct zynqmp_disp *disp) drm_formats[j] = layer->info->formats[j].drm_fmt; /* Graphics layer is primary, and video layer is overlay. */ - type = i == ZYNQMP_DISP_LAYER_GFX - ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY; + type = zynqmp_disp_layer_is_video(layer) + ? DRM_PLANE_TYPE_OVERLAY : DRM_PLANE_TYPE_PRIMARY; ret = drm_universal_plane_init(disp->drm, &layer->plane, 0, &zynqmp_disp_plane_funcs, drm_formats, From b7f4753d7b71a87f0769a07da8c4beebc6903a2f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 7 Mar 2021 02:56:58 +0200 Subject: [PATCH 5/9] drm: xlnx: zynqmp_dpsub: Pass disp structure to all internal functions The internal functions dealing with the audio/video buffer manager, the blender, and the audio mixer, all receive pointers to the respective objects. Those objects are embedded in the zynqmp_disp structure, and a very small. Treating them as separate objects would require expanding them with back-pointers to the zynqmp_disp in order to access fields such as the device pointer for debug messages, and this isn't worth it. Instead, merge those structures with the zynqmp_disp structure, and pass the zynqmp_disp pointer to all internal functions. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/xlnx/zynqmp_disp.c | 266 +++++++++++++---------------- 1 file changed, 121 insertions(+), 145 deletions(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c index a578ab3d5f89..a061a75a9de7 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_disp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c @@ -158,43 +158,17 @@ struct zynqmp_disp_layer { enum zynqmp_disp_layer_mode mode; }; -/** - * struct zynqmp_disp_blend - Blender - * @base: Registers I/O base address - */ -struct zynqmp_disp_blend { - void __iomem *base; -}; - -/** - * struct zynqmp_disp_avbuf - Audio/video buffer manager - * @base: Registers I/O base address - */ -struct zynqmp_disp_avbuf { - void __iomem *base; -}; - -/** - * struct zynqmp_disp_audio - Audio mixer - * @base: Registers I/O base address - * @clk: Audio clock - * @clk_from_ps: True of the audio clock comes from PS, false from PL - */ -struct zynqmp_disp_audio { - void __iomem *base; - struct clk *clk; - bool clk_from_ps; -}; - /** * struct zynqmp_disp - Display controller * @dev: Device structure * @drm: DRM core * @dpsub: Display subsystem * @crtc: DRM CRTC - * @blend: Blender (video rendering pipeline) - * @avbuf: Audio/video buffer manager - * @audio: Audio mixer + * @blend.base: Register I/O base address for the blender + * @avbuf.base: Register I/O base address for the audio/video buffer manager + * @audio.base: Registers I/O base address for the audio mixer + * @audio.clk: Audio clock + * @audio.clk_from_ps: True of the audio clock comes from PS, false from PL * @layers: Layers (planes) * @event: Pending vblank event request * @pclk: Pixel clock @@ -207,9 +181,17 @@ struct zynqmp_disp { struct drm_crtc crtc; - struct zynqmp_disp_blend blend; - struct zynqmp_disp_avbuf avbuf; - struct zynqmp_disp_audio audio; + struct { + void __iomem *base; + } blend; + struct { + void __iomem *base; + } avbuf; + struct { + void __iomem *base; + struct clk *clk; + bool clk_from_ps; + } audio; struct zynqmp_disp_layer layers[ZYNQMP_DISP_NUM_LAYERS]; @@ -423,15 +405,14 @@ static const struct zynqmp_disp_format avbuf_gfx_fmts[] = { }, }; -static u32 zynqmp_disp_avbuf_read(struct zynqmp_disp_avbuf *avbuf, int reg) +static u32 zynqmp_disp_avbuf_read(struct zynqmp_disp *disp, int reg) { - return readl(avbuf->base + reg); + return readl(disp->avbuf.base + reg); } -static void zynqmp_disp_avbuf_write(struct zynqmp_disp_avbuf *avbuf, - int reg, u32 val) +static void zynqmp_disp_avbuf_write(struct zynqmp_disp *disp, int reg, u32 val) { - writel(val, avbuf->base + reg); + writel(val, disp->avbuf.base + reg); } static bool zynqmp_disp_layer_is_video(const struct zynqmp_disp_layer *layer) @@ -441,38 +422,38 @@ static bool zynqmp_disp_layer_is_video(const struct zynqmp_disp_layer *layer) /** * zynqmp_disp_avbuf_set_format - Set the input format for a layer - * @avbuf: Audio/video buffer manager + * @disp: Display controller * @layer: The layer * @fmt: The format information * * Set the video buffer manager format for @layer to @fmt. */ -static void zynqmp_disp_avbuf_set_format(struct zynqmp_disp_avbuf *avbuf, +static void zynqmp_disp_avbuf_set_format(struct zynqmp_disp *disp, struct zynqmp_disp_layer *layer, const struct zynqmp_disp_format *fmt) { unsigned int i; u32 val; - val = zynqmp_disp_avbuf_read(avbuf, ZYNQMP_DISP_AV_BUF_FMT); + val = zynqmp_disp_avbuf_read(disp, ZYNQMP_DISP_AV_BUF_FMT); val &= zynqmp_disp_layer_is_video(layer) ? ~ZYNQMP_DISP_AV_BUF_FMT_NL_VID_MASK : ~ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_MASK; val |= fmt->buf_fmt; - zynqmp_disp_avbuf_write(avbuf, ZYNQMP_DISP_AV_BUF_FMT, val); + zynqmp_disp_avbuf_write(disp, ZYNQMP_DISP_AV_BUF_FMT, val); for (i = 0; i < ZYNQMP_DISP_AV_BUF_NUM_SF; i++) { unsigned int reg = zynqmp_disp_layer_is_video(layer) ? ZYNQMP_DISP_AV_BUF_VID_COMP_SF(i) : ZYNQMP_DISP_AV_BUF_GFX_COMP_SF(i); - zynqmp_disp_avbuf_write(avbuf, reg, fmt->sf[i]); + zynqmp_disp_avbuf_write(disp, reg, fmt->sf[i]); } } /** * zynqmp_disp_avbuf_set_clocks_sources - Set the clocks sources - * @avbuf: Audio/video buffer manager + * @disp: Display controller * @video_from_ps: True if the video clock originates from the PS * @audio_from_ps: True if the audio clock originates from the PS * @timings_internal: True if video timings are generated internally @@ -482,7 +463,7 @@ static void zynqmp_disp_avbuf_set_format(struct zynqmp_disp_avbuf *avbuf, * generated internally or externally. */ static void -zynqmp_disp_avbuf_set_clocks_sources(struct zynqmp_disp_avbuf *avbuf, +zynqmp_disp_avbuf_set_clocks_sources(struct zynqmp_disp *disp, bool video_from_ps, bool audio_from_ps, bool timings_internal) { @@ -495,16 +476,16 @@ zynqmp_disp_avbuf_set_clocks_sources(struct zynqmp_disp_avbuf *avbuf, if (timings_internal) val |= ZYNQMP_DISP_AV_BUF_CLK_SRC_VID_INTERNAL_TIMING; - zynqmp_disp_avbuf_write(avbuf, ZYNQMP_DISP_AV_BUF_CLK_SRC, val); + zynqmp_disp_avbuf_write(disp, ZYNQMP_DISP_AV_BUF_CLK_SRC, val); } /** * zynqmp_disp_avbuf_enable_channels - Enable buffer channels - * @avbuf: Audio/video buffer manager + * @disp: Display controller * * Enable all (video and audio) buffer channels. */ -static void zynqmp_disp_avbuf_enable_channels(struct zynqmp_disp_avbuf *avbuf) +static void zynqmp_disp_avbuf_enable_channels(struct zynqmp_disp *disp) { unsigned int i; u32 val; @@ -514,7 +495,7 @@ static void zynqmp_disp_avbuf_enable_channels(struct zynqmp_disp_avbuf *avbuf) ZYNQMP_DISP_AV_BUF_CHBUF_BURST_LEN_SHIFT); for (i = 0; i < ZYNQMP_DISP_AV_BUF_NUM_VID_GFX_BUFFERS; i++) - zynqmp_disp_avbuf_write(avbuf, ZYNQMP_DISP_AV_BUF_CHBUF(i), + zynqmp_disp_avbuf_write(disp, ZYNQMP_DISP_AV_BUF_CHBUF(i), val); val = ZYNQMP_DISP_AV_BUF_CHBUF_EN | @@ -522,74 +503,74 @@ static void zynqmp_disp_avbuf_enable_channels(struct zynqmp_disp_avbuf *avbuf) ZYNQMP_DISP_AV_BUF_CHBUF_BURST_LEN_SHIFT); for (; i < ZYNQMP_DISP_AV_BUF_NUM_BUFFERS; i++) - zynqmp_disp_avbuf_write(avbuf, ZYNQMP_DISP_AV_BUF_CHBUF(i), + zynqmp_disp_avbuf_write(disp, ZYNQMP_DISP_AV_BUF_CHBUF(i), val); } /** * zynqmp_disp_avbuf_disable_channels - Disable buffer channels - * @avbuf: Audio/video buffer manager + * @disp: Display controller * * Disable all (video and audio) buffer channels. */ -static void zynqmp_disp_avbuf_disable_channels(struct zynqmp_disp_avbuf *avbuf) +static void zynqmp_disp_avbuf_disable_channels(struct zynqmp_disp *disp) { unsigned int i; for (i = 0; i < ZYNQMP_DISP_AV_BUF_NUM_BUFFERS; i++) - zynqmp_disp_avbuf_write(avbuf, ZYNQMP_DISP_AV_BUF_CHBUF(i), + zynqmp_disp_avbuf_write(disp, ZYNQMP_DISP_AV_BUF_CHBUF(i), ZYNQMP_DISP_AV_BUF_CHBUF_FLUSH); } /** * zynqmp_disp_avbuf_enable_audio - Enable audio - * @avbuf: Audio/video buffer manager + * @disp: Display controller * * Enable all audio buffers with a non-live (memory) source. */ -static void zynqmp_disp_avbuf_enable_audio(struct zynqmp_disp_avbuf *avbuf) +static void zynqmp_disp_avbuf_enable_audio(struct zynqmp_disp *disp) { u32 val; - val = zynqmp_disp_avbuf_read(avbuf, ZYNQMP_DISP_AV_BUF_OUTPUT); + val = zynqmp_disp_avbuf_read(disp, ZYNQMP_DISP_AV_BUF_OUTPUT); val &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_MASK; val |= ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_MEM; val |= ZYNQMP_DISP_AV_BUF_OUTPUT_AUD2_EN; - zynqmp_disp_avbuf_write(avbuf, ZYNQMP_DISP_AV_BUF_OUTPUT, val); + zynqmp_disp_avbuf_write(disp, ZYNQMP_DISP_AV_BUF_OUTPUT, val); } /** * zynqmp_disp_avbuf_disable_audio - Disable audio - * @avbuf: Audio/video buffer manager + * @disp: Display controller * * Disable all audio buffers. */ -static void zynqmp_disp_avbuf_disable_audio(struct zynqmp_disp_avbuf *avbuf) +static void zynqmp_disp_avbuf_disable_audio(struct zynqmp_disp *disp) { u32 val; - val = zynqmp_disp_avbuf_read(avbuf, ZYNQMP_DISP_AV_BUF_OUTPUT); + val = zynqmp_disp_avbuf_read(disp, ZYNQMP_DISP_AV_BUF_OUTPUT); val &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_MASK; val |= ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_DISABLE; val &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_AUD2_EN; - zynqmp_disp_avbuf_write(avbuf, ZYNQMP_DISP_AV_BUF_OUTPUT, val); + zynqmp_disp_avbuf_write(disp, ZYNQMP_DISP_AV_BUF_OUTPUT, val); } /** * zynqmp_disp_avbuf_enable_video - Enable a video layer - * @avbuf: Audio/video buffer manager + * @disp: Display controller * @layer: The layer * @mode: Operating mode of layer * * Enable the video/graphics buffer for @layer. */ -static void zynqmp_disp_avbuf_enable_video(struct zynqmp_disp_avbuf *avbuf, +static void zynqmp_disp_avbuf_enable_video(struct zynqmp_disp *disp, struct zynqmp_disp_layer *layer, enum zynqmp_disp_layer_mode mode) { u32 val; - val = zynqmp_disp_avbuf_read(avbuf, ZYNQMP_DISP_AV_BUF_OUTPUT); + val = zynqmp_disp_avbuf_read(disp, ZYNQMP_DISP_AV_BUF_OUTPUT); if (zynqmp_disp_layer_is_video(layer)) { val &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_MASK; if (mode == ZYNQMP_DISP_LAYER_NONLIVE) @@ -604,22 +585,22 @@ static void zynqmp_disp_avbuf_enable_video(struct zynqmp_disp_avbuf *avbuf, else val |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_LIVE; } - zynqmp_disp_avbuf_write(avbuf, ZYNQMP_DISP_AV_BUF_OUTPUT, val); + zynqmp_disp_avbuf_write(disp, ZYNQMP_DISP_AV_BUF_OUTPUT, val); } /** * zynqmp_disp_avbuf_disable_video - Disable a video layer - * @avbuf: Audio/video buffer manager + * @disp: Display controller * @layer: The layer * * Disable the video/graphics buffer for @layer. */ -static void zynqmp_disp_avbuf_disable_video(struct zynqmp_disp_avbuf *avbuf, +static void zynqmp_disp_avbuf_disable_video(struct zynqmp_disp *disp, struct zynqmp_disp_layer *layer) { u32 val; - val = zynqmp_disp_avbuf_read(avbuf, ZYNQMP_DISP_AV_BUF_OUTPUT); + val = zynqmp_disp_avbuf_read(disp, ZYNQMP_DISP_AV_BUF_OUTPUT); if (zynqmp_disp_layer_is_video(layer)) { val &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_MASK; val |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_NONE; @@ -627,29 +608,29 @@ static void zynqmp_disp_avbuf_disable_video(struct zynqmp_disp_avbuf *avbuf, val &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_MASK; val |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_DISABLE; } - zynqmp_disp_avbuf_write(avbuf, ZYNQMP_DISP_AV_BUF_OUTPUT, val); + zynqmp_disp_avbuf_write(disp, ZYNQMP_DISP_AV_BUF_OUTPUT, val); } /** * zynqmp_disp_avbuf_enable - Enable the video pipe - * @avbuf: Audio/video buffer manager + * @disp: Display controller * * De-assert the video pipe reset. */ -static void zynqmp_disp_avbuf_enable(struct zynqmp_disp_avbuf *avbuf) +static void zynqmp_disp_avbuf_enable(struct zynqmp_disp *disp) { - zynqmp_disp_avbuf_write(avbuf, ZYNQMP_DISP_AV_BUF_SRST_REG, 0); + zynqmp_disp_avbuf_write(disp, ZYNQMP_DISP_AV_BUF_SRST_REG, 0); } /** * zynqmp_disp_avbuf_disable - Disable the video pipe - * @avbuf: Audio/video buffer manager + * @disp: Display controller * * Assert the video pipe reset. */ -static void zynqmp_disp_avbuf_disable(struct zynqmp_disp_avbuf *avbuf) +static void zynqmp_disp_avbuf_disable(struct zynqmp_disp *disp) { - zynqmp_disp_avbuf_write(avbuf, ZYNQMP_DISP_AV_BUF_SRST_REG, + zynqmp_disp_avbuf_write(disp, ZYNQMP_DISP_AV_BUF_SRST_REG, ZYNQMP_DISP_AV_BUF_SRST_REG_VID_RST); } @@ -657,10 +638,9 @@ static void zynqmp_disp_avbuf_disable(struct zynqmp_disp_avbuf *avbuf) * Blender (Video Pipeline) */ -static void zynqmp_disp_blend_write(struct zynqmp_disp_blend *blend, - int reg, u32 val) +static void zynqmp_disp_blend_write(struct zynqmp_disp *disp, int reg, u32 val) { - writel(val, blend->base + reg); + writel(val, disp->blend.base + reg); } /* @@ -706,12 +686,12 @@ static const u32 csc_sdtv_to_rgb_offsets[] = { /** * zynqmp_disp_blend_set_output_format - Set the output format of the blender - * @blend: Blender object + * @disp: Display controller * @format: Output format * * Set the output format of the blender to @format. */ -static void zynqmp_disp_blend_set_output_format(struct zynqmp_disp_blend *blend, +static void zynqmp_disp_blend_set_output_format(struct zynqmp_disp *disp, enum zynqmp_dpsub_format format) { static const unsigned int blend_output_fmts[] = { @@ -727,7 +707,7 @@ static void zynqmp_disp_blend_set_output_format(struct zynqmp_disp_blend *blend, const u32 *offsets; unsigned int i; - zynqmp_disp_blend_write(blend, ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT, fmt); + zynqmp_disp_blend_write(disp, ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT, fmt); if (fmt == ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_RGB) { coeffs = csc_identity_matrix; offsets = csc_zero_offsets; @@ -737,19 +717,19 @@ static void zynqmp_disp_blend_set_output_format(struct zynqmp_disp_blend *blend, } for (i = 0; i < ZYNQMP_DISP_V_BLEND_NUM_COEFF; i++) - zynqmp_disp_blend_write(blend, + zynqmp_disp_blend_write(disp, ZYNQMP_DISP_V_BLEND_RGB2YCBCR_COEFF(i), coeffs[i]); for (i = 0; i < ZYNQMP_DISP_V_BLEND_NUM_OFFSET; i++) - zynqmp_disp_blend_write(blend, + zynqmp_disp_blend_write(disp, ZYNQMP_DISP_V_BLEND_OUTCSC_OFFSET(i), offsets[i]); } /** * zynqmp_disp_blend_set_bg_color - Set the background color - * @blend: Blender object + * @disp: Display controller * @rcr: Red/Cr color component * @gy: Green/Y color component * @bcb: Blue/Cb color component @@ -758,31 +738,31 @@ static void zynqmp_disp_blend_set_output_format(struct zynqmp_disp_blend *blend, * B or Cr, Y and Cb components respectively depending on the selected output * format. */ -static void zynqmp_disp_blend_set_bg_color(struct zynqmp_disp_blend *blend, +static void zynqmp_disp_blend_set_bg_color(struct zynqmp_disp *disp, u32 rcr, u32 gy, u32 bcb) { - zynqmp_disp_blend_write(blend, ZYNQMP_DISP_V_BLEND_BG_CLR_0, rcr); - zynqmp_disp_blend_write(blend, ZYNQMP_DISP_V_BLEND_BG_CLR_1, gy); - zynqmp_disp_blend_write(blend, ZYNQMP_DISP_V_BLEND_BG_CLR_2, bcb); + zynqmp_disp_blend_write(disp, ZYNQMP_DISP_V_BLEND_BG_CLR_0, rcr); + zynqmp_disp_blend_write(disp, ZYNQMP_DISP_V_BLEND_BG_CLR_1, gy); + zynqmp_disp_blend_write(disp, ZYNQMP_DISP_V_BLEND_BG_CLR_2, bcb); } /** * zynqmp_disp_blend_set_global_alpha - Configure global alpha blending - * @blend: Blender object + * @disp: Display controller * @enable: True to enable global alpha blending * @alpha: Global alpha value (ignored if @enabled is false) */ -static void zynqmp_disp_blend_set_global_alpha(struct zynqmp_disp_blend *blend, +static void zynqmp_disp_blend_set_global_alpha(struct zynqmp_disp *disp, bool enable, u32 alpha) { - zynqmp_disp_blend_write(blend, ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA, + zynqmp_disp_blend_write(disp, ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA, ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA_VALUE(alpha) | (enable ? ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA_EN : 0)); } /** * zynqmp_disp_blend_layer_set_csc - Configure colorspace conversion for layer - * @blend: Blender object + * @disp: Display controller * @layer: The layer * @coeffs: Colorspace conversion matrix * @offsets: Colorspace conversion offsets @@ -791,7 +771,7 @@ static void zynqmp_disp_blend_set_global_alpha(struct zynqmp_disp_blend *blend, * Columns of the matrix are automatically swapped based on the input format to * handle RGB and YCrCb components permutations. */ -static void zynqmp_disp_blend_layer_set_csc(struct zynqmp_disp_blend *blend, +static void zynqmp_disp_blend_layer_set_csc(struct zynqmp_disp *disp, struct zynqmp_disp_layer *layer, const u16 *coeffs, const u32 *offsets) @@ -818,9 +798,9 @@ static void zynqmp_disp_blend_layer_set_csc(struct zynqmp_disp_blend *blend, reg = ZYNQMP_DISP_V_BLEND_IN2CSC_COEFF(0); for (i = 0; i < ZYNQMP_DISP_V_BLEND_NUM_COEFF; i += 3, reg += 12) { - zynqmp_disp_blend_write(blend, reg + 0, coeffs[i + swap[0]]); - zynqmp_disp_blend_write(blend, reg + 4, coeffs[i + swap[1]]); - zynqmp_disp_blend_write(blend, reg + 8, coeffs[i + swap[2]]); + zynqmp_disp_blend_write(disp, reg + 0, coeffs[i + swap[0]]); + zynqmp_disp_blend_write(disp, reg + 4, coeffs[i + swap[1]]); + zynqmp_disp_blend_write(disp, reg + 8, coeffs[i + swap[2]]); } if (zynqmp_disp_layer_is_video(layer)) @@ -829,15 +809,15 @@ static void zynqmp_disp_blend_layer_set_csc(struct zynqmp_disp_blend *blend, reg = ZYNQMP_DISP_V_BLEND_IN2CSC_OFFSET(0); for (i = 0; i < ZYNQMP_DISP_V_BLEND_NUM_OFFSET; i++) - zynqmp_disp_blend_write(blend, reg + i * 4, offsets[i]); + zynqmp_disp_blend_write(disp, reg + i * 4, offsets[i]); } /** * zynqmp_disp_blend_layer_enable - Enable a layer - * @blend: Blender object + * @disp: Display controller * @layer: The layer */ -static void zynqmp_disp_blend_layer_enable(struct zynqmp_disp_blend *blend, +static void zynqmp_disp_blend_layer_enable(struct zynqmp_disp *disp, struct zynqmp_disp_layer *layer) { const u16 *coeffs; @@ -849,7 +829,7 @@ static void zynqmp_disp_blend_layer_enable(struct zynqmp_disp_blend *blend, (layer->drm_fmt->hsub > 1 ? ZYNQMP_DISP_V_BLEND_LAYER_CONTROL_EN_US : 0); - zynqmp_disp_blend_write(blend, + zynqmp_disp_blend_write(disp, ZYNQMP_DISP_V_BLEND_LAYER_CONTROL(layer->id), val); @@ -861,22 +841,22 @@ static void zynqmp_disp_blend_layer_enable(struct zynqmp_disp_blend *blend, offsets = csc_zero_offsets; } - zynqmp_disp_blend_layer_set_csc(blend, layer, coeffs, offsets); + zynqmp_disp_blend_layer_set_csc(disp, layer, coeffs, offsets); } /** * zynqmp_disp_blend_layer_disable - Disable a layer - * @blend: Blender object + * @disp: Display controller * @layer: The layer */ -static void zynqmp_disp_blend_layer_disable(struct zynqmp_disp_blend *blend, +static void zynqmp_disp_blend_layer_disable(struct zynqmp_disp *disp, struct zynqmp_disp_layer *layer) { - zynqmp_disp_blend_write(blend, + zynqmp_disp_blend_write(disp, ZYNQMP_DISP_V_BLEND_LAYER_CONTROL(layer->id), 0); - zynqmp_disp_blend_layer_set_csc(blend, layer, csc_zero_matrix, + zynqmp_disp_blend_layer_set_csc(disp, layer, csc_zero_matrix, csc_zero_offsets); } @@ -884,57 +864,55 @@ static void zynqmp_disp_blend_layer_disable(struct zynqmp_disp_blend *blend, * Audio Mixer */ -static void zynqmp_disp_audio_write(struct zynqmp_disp_audio *audio, - int reg, u32 val) +static void zynqmp_disp_audio_write(struct zynqmp_disp *disp, int reg, u32 val) { - writel(val, audio->base + reg); + writel(val, disp->audio.base + reg); } /** * zynqmp_disp_audio_enable - Enable the audio mixer - * @audio: Audio mixer + * @disp: Display controller * * Enable the audio mixer by de-asserting the soft reset. The audio state is set to * default values by the reset, set the default mixer volume explicitly. */ -static void zynqmp_disp_audio_enable(struct zynqmp_disp_audio *audio) +static void zynqmp_disp_audio_enable(struct zynqmp_disp *disp) { /* Clear the audio soft reset register as it's an non-reset flop. */ - zynqmp_disp_audio_write(audio, ZYNQMP_DISP_AUD_SOFT_RESET, 0); - zynqmp_disp_audio_write(audio, ZYNQMP_DISP_AUD_MIXER_VOLUME, + zynqmp_disp_audio_write(disp, ZYNQMP_DISP_AUD_SOFT_RESET, 0); + zynqmp_disp_audio_write(disp, ZYNQMP_DISP_AUD_MIXER_VOLUME, ZYNQMP_DISP_AUD_MIXER_VOLUME_NO_SCALE); } /** * zynqmp_disp_audio_disable - Disable the audio mixer - * @audio: Audio mixer + * @disp: Display controller * * Disable the audio mixer by asserting its soft reset. */ -static void zynqmp_disp_audio_disable(struct zynqmp_disp_audio *audio) +static void zynqmp_disp_audio_disable(struct zynqmp_disp *disp) { - zynqmp_disp_audio_write(audio, ZYNQMP_DISP_AUD_SOFT_RESET, + zynqmp_disp_audio_write(disp, ZYNQMP_DISP_AUD_SOFT_RESET, ZYNQMP_DISP_AUD_SOFT_RESET_AUD_SRST); } -static void zynqmp_disp_audio_init(struct device *dev, - struct zynqmp_disp_audio *audio) +static void zynqmp_disp_audio_init(struct zynqmp_disp *disp) { /* Try the live PL audio clock. */ - audio->clk = devm_clk_get(dev, "dp_live_audio_aclk"); - if (!IS_ERR(audio->clk)) { - audio->clk_from_ps = false; + disp->audio.clk = devm_clk_get(disp->dev, "dp_live_audio_aclk"); + if (!IS_ERR(disp->audio.clk)) { + disp->audio.clk_from_ps = false; return; } /* If the live PL audio clock is not valid, fall back to PS clock. */ - audio->clk = devm_clk_get(dev, "dp_aud_clk"); - if (!IS_ERR(audio->clk)) { - audio->clk_from_ps = true; + disp->audio.clk = devm_clk_get(disp->dev, "dp_aud_clk"); + if (!IS_ERR(disp->audio.clk)) { + disp->audio.clk_from_ps = true; return; } - dev_err(dev, "audio disabled due to missing clock\n"); + dev_err(disp->dev, "audio disabled due to missing clock\n"); } /* ----------------------------------------------------------------------------- @@ -1030,9 +1008,9 @@ zynqmp_disp_layer_find_format(struct zynqmp_disp_layer *layer, */ static void zynqmp_disp_layer_enable(struct zynqmp_disp_layer *layer) { - zynqmp_disp_avbuf_enable_video(&layer->disp->avbuf, layer, + zynqmp_disp_avbuf_enable_video(layer->disp, layer, ZYNQMP_DISP_LAYER_NONLIVE); - zynqmp_disp_blend_layer_enable(&layer->disp->blend, layer); + zynqmp_disp_blend_layer_enable(layer->disp, layer); layer->mode = ZYNQMP_DISP_LAYER_NONLIVE; } @@ -1051,8 +1029,8 @@ static void zynqmp_disp_layer_disable(struct zynqmp_disp_layer *layer) for (i = 0; i < layer->drm_fmt->num_planes; i++) dmaengine_terminate_sync(layer->dmas[i].chan); - zynqmp_disp_avbuf_disable_video(&layer->disp->avbuf, layer); - zynqmp_disp_blend_layer_disable(&layer->disp->blend, layer); + zynqmp_disp_avbuf_disable_video(layer->disp, layer); + zynqmp_disp_blend_layer_disable(layer->disp, layer); } /** @@ -1072,8 +1050,7 @@ static void zynqmp_disp_layer_set_format(struct zynqmp_disp_layer *layer, layer->disp_fmt = zynqmp_disp_layer_find_format(layer, info->format); layer->drm_fmt = info; - zynqmp_disp_avbuf_set_format(&layer->disp->avbuf, layer, - layer->disp_fmt); + zynqmp_disp_avbuf_set_format(layer->disp, layer, layer->disp_fmt); /* * Set slave_id for each DMA channel to indicate they're part of a @@ -1392,14 +1369,14 @@ err: */ static void zynqmp_disp_enable(struct zynqmp_disp *disp) { - zynqmp_disp_avbuf_enable(&disp->avbuf); + zynqmp_disp_avbuf_enable(disp); /* Choose clock source based on the DT clock handle. */ - zynqmp_disp_avbuf_set_clocks_sources(&disp->avbuf, disp->pclk_from_ps, + zynqmp_disp_avbuf_set_clocks_sources(disp, disp->pclk_from_ps, disp->audio.clk_from_ps, true); - zynqmp_disp_avbuf_enable_channels(&disp->avbuf); - zynqmp_disp_avbuf_enable_audio(&disp->avbuf); + zynqmp_disp_avbuf_enable_channels(disp); + zynqmp_disp_avbuf_enable_audio(disp); - zynqmp_disp_audio_enable(&disp->audio); + zynqmp_disp_audio_enable(disp); } /** @@ -1408,11 +1385,11 @@ static void zynqmp_disp_enable(struct zynqmp_disp *disp) */ static void zynqmp_disp_disable(struct zynqmp_disp *disp) { - zynqmp_disp_audio_disable(&disp->audio); + zynqmp_disp_audio_disable(disp); - zynqmp_disp_avbuf_disable_audio(&disp->avbuf); - zynqmp_disp_avbuf_disable_channels(&disp->avbuf); - zynqmp_disp_avbuf_disable(&disp->avbuf); + zynqmp_disp_avbuf_disable_audio(disp); + zynqmp_disp_avbuf_disable_channels(disp); + zynqmp_disp_avbuf_disable(disp); } static inline struct zynqmp_disp *crtc_to_disp(struct drm_crtc *crtc) @@ -1468,10 +1445,9 @@ zynqmp_disp_crtc_atomic_enable(struct drm_crtc *crtc, return; } - zynqmp_disp_blend_set_output_format(&disp->blend, - ZYNQMP_DPSUB_FORMAT_RGB); - zynqmp_disp_blend_set_bg_color(&disp->blend, 0, 0, 0); - zynqmp_disp_blend_set_global_alpha(&disp->blend, false, 0); + zynqmp_disp_blend_set_output_format(disp, ZYNQMP_DPSUB_FORMAT_RGB); + zynqmp_disp_blend_set_bg_color(disp, 0, 0, 0); + zynqmp_disp_blend_set_global_alpha(disp, false, 0); zynqmp_disp_enable(disp); @@ -1680,7 +1656,7 @@ int zynqmp_disp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm) disp->pclk_from_ps = true; } - zynqmp_disp_audio_init(disp->dev, &disp->audio); + zynqmp_disp_audio_init(disp); ret = zynqmp_disp_create_layers(disp); if (ret) From e06926ecc3d04944ffc44069368d9fddf528413f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 7 Mar 2021 02:56:58 +0200 Subject: [PATCH 6/9] drm: xlnx: zynqmp_dpsub: Fix graphics layer blending To display the graphics layer, the global alpha needs to be enabled. Enable it when the graphics plane is enabled (with full opacity), and disable it otherwise. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/xlnx/zynqmp_disp.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c index a061a75a9de7..27b3829fb7e0 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_disp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c @@ -415,6 +415,11 @@ static void zynqmp_disp_avbuf_write(struct zynqmp_disp *disp, int reg, u32 val) writel(val, disp->avbuf.base + reg); } +static bool zynqmp_disp_layer_is_gfx(const struct zynqmp_disp_layer *layer) +{ + return layer->id == ZYNQMP_DISP_LAYER_GFX; +} + static bool zynqmp_disp_layer_is_video(const struct zynqmp_disp_layer *layer) { return layer->id == ZYNQMP_DISP_LAYER_VID; @@ -1157,6 +1162,9 @@ zynqmp_disp_plane_atomic_disable(struct drm_plane *plane, return; zynqmp_disp_layer_disable(layer); + + if (zynqmp_disp_layer_is_gfx(layer)) + zynqmp_disp_blend_set_global_alpha(layer->disp, false, 0); } static void @@ -1186,6 +1194,9 @@ zynqmp_disp_plane_atomic_update(struct drm_plane *plane, zynqmp_disp_layer_update(layer, new_state); + if (zynqmp_disp_layer_is_gfx(layer)) + zynqmp_disp_blend_set_global_alpha(layer->disp, true, 255); + /* Enable or re-enable the plane is the format has changed. */ if (format_changed) zynqmp_disp_layer_enable(layer); @@ -1447,7 +1458,6 @@ zynqmp_disp_crtc_atomic_enable(struct drm_crtc *crtc, zynqmp_disp_blend_set_output_format(disp, ZYNQMP_DPSUB_FORMAT_RGB); zynqmp_disp_blend_set_bg_color(disp, 0, 0, 0); - zynqmp_disp_blend_set_global_alpha(disp, false, 0); zynqmp_disp_enable(disp); From 650f12042b85322e1459b7e49d50ae09c60e28d3 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 7 Mar 2021 02:56:58 +0200 Subject: [PATCH 7/9] drm: xlnx: zynqmp_dpsub: Add global alpha support The graphics plane has a global alpha setting. Expose it through the plane's alpha property. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/xlnx/zynqmp_disp.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c index 27b3829fb7e0..d87af7cb3340 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_disp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c @@ -1164,7 +1164,8 @@ zynqmp_disp_plane_atomic_disable(struct drm_plane *plane, zynqmp_disp_layer_disable(layer); if (zynqmp_disp_layer_is_gfx(layer)) - zynqmp_disp_blend_set_global_alpha(layer->disp, false, 0); + zynqmp_disp_blend_set_global_alpha(layer->disp, false, + plane->state->alpha >> 8); } static void @@ -1195,7 +1196,8 @@ zynqmp_disp_plane_atomic_update(struct drm_plane *plane, zynqmp_disp_layer_update(layer, new_state); if (zynqmp_disp_layer_is_gfx(layer)) - zynqmp_disp_blend_set_global_alpha(layer->disp, true, 255); + zynqmp_disp_blend_set_global_alpha(layer->disp, true, + plane->state->alpha >> 8); /* Enable or re-enable the plane is the format has changed. */ if (format_changed) @@ -1249,6 +1251,9 @@ static int zynqmp_disp_create_planes(struct zynqmp_disp *disp) drm_plane_helper_add(&layer->plane, &zynqmp_disp_plane_helper_funcs); + + if (zynqmp_disp_layer_is_gfx(layer)) + drm_plane_create_alpha_property(&layer->plane); } return 0; From 8c772f0b2b8e94bfd68f9bf19d7aba293332e4bf Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 7 Mar 2021 02:56:58 +0200 Subject: [PATCH 8/9] drm: xlnx: zynqmp_dpsub: Expose plane ordering to userspace While the DPSUB has a fixed plane order, it still makes sense to expose it to userspace to avoid hardcoding assumptions. Do so by adding an immutable zpos property to planes. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/xlnx/zynqmp_disp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c index d87af7cb3340..e377974f8198 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_disp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c @@ -1252,6 +1252,7 @@ static int zynqmp_disp_create_planes(struct zynqmp_disp *disp) drm_plane_helper_add(&layer->plane, &zynqmp_disp_plane_helper_funcs); + drm_plane_create_zpos_immutable_property(&layer->plane, i); if (zynqmp_disp_layer_is_gfx(layer)) drm_plane_create_alpha_property(&layer->plane); } From 6ebfd22c969044bda7b5c3f0a20a958c7ff656b9 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 2 Jun 2021 15:32:51 +0100 Subject: [PATCH 9/9] drm/xlnx/zynqmp_disp: Fix incorrectly named enum 'zynqmp_disp_layer_id' Fixes the following W=1 kernel build warning(s): drivers/gpu/drm/xlnx/zynqmp_disp.c:101: warning: expecting prototype for enum zynqmp_disp_id. Prototype was for enum zynqmp_disp_layer_id instead Signed-off-by: Lee Jones Reviewed-by: Laurent Pinchart Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/xlnx/zynqmp_disp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c index e377974f8198..ff2b308d8651 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_disp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c @@ -91,7 +91,7 @@ struct zynqmp_disp_format { }; /** - * enum zynqmp_disp_id - Layer identifier + * enum zynqmp_disp_layer_id - Layer identifier * @ZYNQMP_DISP_LAYER_VID: Video layer * @ZYNQMP_DISP_LAYER_GFX: Graphics layer */