Commit d76271d2 authored by Hyun Kwon's avatar Hyun Kwon Committed by Laurent Pinchart

drm: xlnx: DRM/KMS driver for Xilinx ZynqMP DisplayPort Subsystem

The Xilinx ZynqMP SoC has a hardened display pipeline named DisplayPort
Subsystem. It includes a buffer manager, a video pipeline renderer
(blender), an audio mixer and a DisplayPort source controller
(transmitter). The DMA engine the provide data to the buffer manager, as
well as the DisplayPort PHYs that drive the lanes, are external to the
subsystem and interfaced using the DMA engine and PHY APIs respectively.

This driver supports the DisplayPort Subsystem and implements

- Two planes, for graphics and video
- One CRTC that supports alpha blending
- One encoder for the DisplayPort transmitter
- One connector for an external monitor

It currently doesn't support

- Color keying
- Test pattern generation
- Audio
- Live input from the Programmable Logic (FPGA)
- Output to the Programmable Logic (FPGA)
Signed-off-by: default avatarHyun Kwon <hyun.kwon@xilinx.com>
Signed-off-by: default avatarLaurent Pinchart <laurent.pinchart@ideasonboard.com>
parent e7c7970a
......@@ -5843,6 +5843,15 @@ T: git git://anongit.freedesktop.org/drm/drm-misc
F: Documentation/gpu/xen-front.rst
F: drivers/gpu/drm/xen/
DRM DRIVERS FOR XILINX
M: Hyun Kwon <hyun.kwon@xilinx.com>
M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
L: dri-devel@lists.freedesktop.org
S: Maintained
T: git git://anongit.freedesktop.org/drm/drm-misc
F: Documentation/devicetree/bindings/display/xlnx/
F: drivers/gpu/drm/xlnx/
DRM DRIVERS FOR ZTE ZX
M: Shawn Guo <shawnguo@kernel.org>
L: dri-devel@lists.freedesktop.org
......
......@@ -386,6 +386,8 @@ source "drivers/gpu/drm/mcde/Kconfig"
source "drivers/gpu/drm/tidss/Kconfig"
source "drivers/gpu/drm/xlnx/Kconfig"
# Keep legacy drivers last
menuconfig DRM_LEGACY
......
......@@ -123,3 +123,4 @@ obj-$(CONFIG_DRM_PANFROST) += panfrost/
obj-$(CONFIG_DRM_ASPEED_GFX) += aspeed/
obj-$(CONFIG_DRM_MCDE) += mcde/
obj-$(CONFIG_DRM_TIDSS) += tidss/
obj-y += xlnx/
config DRM_ZYNQMP_DPSUB
tristate "ZynqMP DisplayPort Controller Driver"
depends on ARCH_ZYNQMP || COMPILE_TEST
depends on COMMON_CLK && DRM && OF
select DMA_ENGINE
select DRM_GEM_CMA_HELPER
select DRM_KMS_CMA_HELPER
select DRM_KMS_HELPER
select GENERIC_PHY
help
This is a DRM/KMS driver for ZynqMP DisplayPort controller. Choose
this option if you have a Xilinx ZynqMP SoC with DisplayPort
subsystem.
zynqmp-dpsub-y := zynqmp_disp.o zynqmp_dpsub.o zynqmp_dp.o
obj-$(CONFIG_DRM_ZYNQMP_DPSUB) += zynqmp-dpsub.o
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0 */
/*
* ZynqMP Display Driver
*
* Copyright (C) 2017 - 2020 Xilinx, Inc.
*
* Authors:
* - Hyun Woo Kwon <hyun.kwon@xilinx.com>
* - Laurent Pinchart <laurent.pinchart@ideasonboard.com>
*/
#ifndef _ZYNQMP_DISP_H_
#define _ZYNQMP_DISP_H_
#include <linux/types.h>
/*
* 3840x2160 is advertised as the maximum resolution, but almost any
* resolutions under a 300Mhz pixel rate would work. Pick 4096x4096.
*/
#define ZYNQMP_DISP_MAX_WIDTH 4096
#define ZYNQMP_DISP_MAX_HEIGHT 4096
/* The DPDMA is limited to 44 bit addressing. */
#define ZYNQMP_DISP_MAX_DMA_BIT 44
struct device;
struct drm_device;
struct platform_device;
struct zynqmp_disp;
struct zynqmp_dpsub;
void zynqmp_disp_handle_vblank(struct zynqmp_disp *disp);
bool zynqmp_disp_audio_enabled(struct zynqmp_disp *disp);
unsigned int zynqmp_disp_get_audio_clk_rate(struct zynqmp_disp *disp);
uint32_t zynqmp_disp_get_crtc_mask(struct zynqmp_disp *disp);
int zynqmp_disp_drm_init(struct zynqmp_dpsub *dpsub);
int zynqmp_disp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm);
void zynqmp_disp_remove(struct zynqmp_dpsub *dpsub);
#endif /* _ZYNQMP_DISP_H_ */
/* SPDX-License-Identifier: GPL-2.0 */
/*
* ZynqMP Display Controller Driver - Register Definitions
*
* Copyright (C) 2017 - 2020 Xilinx, Inc.
*
* Authors:
* - Hyun Woo Kwon <hyun.kwon@xilinx.com>
* - Laurent Pinchart <laurent.pinchart@ideasonboard.com>
*/
#ifndef _ZYNQMP_DISP_REGS_H_
#define _ZYNQMP_DISP_REGS_H_
#include <linux/bits.h>
/* Blender registers */
#define ZYNQMP_DISP_V_BLEND_BG_CLR_0 0x0
#define ZYNQMP_DISP_V_BLEND_BG_CLR_1 0x4
#define ZYNQMP_DISP_V_BLEND_BG_CLR_2 0x8
#define ZYNQMP_DISP_V_BLEND_BG_MAX 0xfff
#define ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA 0xc
#define ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA_VALUE(n) ((n) << 1)
#define ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA_EN BIT(0)
#define ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT 0x14
#define ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_RGB 0x0
#define ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_YCBCR444 0x1
#define ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_YCBCR422 0x2
#define ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_YONLY 0x3
#define ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_XVYCC 0x4
#define ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_EN_DOWNSAMPLE BIT(4)
#define ZYNQMP_DISP_V_BLEND_LAYER_CONTROL(n) (0x18 + ((n) * 4))
#define ZYNQMP_DISP_V_BLEND_LAYER_CONTROL_EN_US BIT(0)
#define ZYNQMP_DISP_V_BLEND_LAYER_CONTROL_RGB BIT(1)
#define ZYNQMP_DISP_V_BLEND_LAYER_CONTROL_BYPASS BIT(8)
#define ZYNQMP_DISP_V_BLEND_NUM_COEFF 9
#define ZYNQMP_DISP_V_BLEND_NUM_OFFSET 3
#define ZYNQMP_DISP_V_BLEND_RGB2YCBCR_COEFF(n) (0x20 + ((n) * 4))
#define ZYNQMP_DISP_V_BLEND_IN1CSC_COEFF(n) (0x44 + ((n) * 4))
#define ZYNQMP_DISP_V_BLEND_IN1CSC_OFFSET(n) (0x68 + ((n) * 4))
#define ZYNQMP_DISP_V_BLEND_OUTCSC_OFFSET(n) (0x74 + ((n) * 4))
#define ZYNQMP_DISP_V_BLEND_IN2CSC_COEFF(n) (0x80 + ((n) * 4))
#define ZYNQMP_DISP_V_BLEND_IN2CSC_OFFSET(n) (0xa4 + ((n) * 4))
#define ZYNQMP_DISP_V_BLEND_CHROMA_KEY_ENABLE 0x1d0
#define ZYNQMP_DISP_V_BLEND_CHROMA_KEY_COMP1 0x1d4
#define ZYNQMP_DISP_V_BLEND_CHROMA_KEY_COMP2 0x1d8
#define ZYNQMP_DISP_V_BLEND_CHROMA_KEY_COMP3 0x1dc
/* AV buffer manager registers */
#define ZYNQMP_DISP_AV_BUF_FMT 0x0
#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_SHIFT 0
#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_MASK (0x1f << 0)
#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_UYVY (0 << 0)
#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_VYUY (1 << 0)
#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YVYU (2 << 0)
#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YUYV (3 << 0)
#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16 (4 << 0)
#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV24 (5 << 0)
#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI (6 << 0)
#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_MONO (7 << 0)
#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI2 (8 << 0)
#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YUV444 (9 << 0)
#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_RGB888 (10 << 0)
#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_RGBA8880 (11 << 0)
#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_RGB888_10 (12 << 0)
#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YUV444_10 (13 << 0)
#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI2_10 (14 << 0)
#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI_10 (15 << 0)
#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16_10 (16 << 0)
#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV24_10 (17 << 0)
#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YONLY_10 (18 << 0)
#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16_420 (19 << 0)
#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI_420 (20 << 0)
#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI2_420 (21 << 0)
#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16_420_10 (22 << 0)
#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI_420_10 (23 << 0)
#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI2_420_10 (24 << 0)
#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_SHIFT 8
#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_MASK (0xf << 8)
#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGBA8888 (0 << 8)
#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_ABGR8888 (1 << 8)
#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGB888 (2 << 8)
#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_BGR888 (3 << 8)
#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGBA5551 (4 << 8)
#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGBA4444 (5 << 8)
#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGB565 (6 << 8)
#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_8BPP (7 << 8)
#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_4BPP (8 << 8)
#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_2BPP (9 << 8)
#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_1BPP (10 << 8)
#define ZYNQMP_DISP_AV_BUF_NON_LIVE_LATENCY 0x8
#define ZYNQMP_DISP_AV_BUF_CHBUF(n) (0x10 + ((n) * 4))
#define ZYNQMP_DISP_AV_BUF_CHBUF_EN BIT(0)
#define ZYNQMP_DISP_AV_BUF_CHBUF_FLUSH BIT(1)
#define ZYNQMP_DISP_AV_BUF_CHBUF_BURST_LEN_SHIFT 2
#define ZYNQMP_DISP_AV_BUF_CHBUF_BURST_LEN_MASK (0xf << 2)
#define ZYNQMP_DISP_AV_BUF_CHBUF_BURST_LEN_MAX 0xf
#define ZYNQMP_DISP_AV_BUF_CHBUF_BURST_LEN_AUD_MAX 0x3
#define ZYNQMP_DISP_AV_BUF_STATUS 0x28
#define ZYNQMP_DISP_AV_BUF_STC_CTRL 0x2c
#define ZYNQMP_DISP_AV_BUF_STC_CTRL_EN BIT(0)
#define ZYNQMP_DISP_AV_BUF_STC_CTRL_EVENT_SHIFT 1
#define ZYNQMP_DISP_AV_BUF_STC_CTRL_EVENT_EX_VSYNC 0
#define ZYNQMP_DISP_AV_BUF_STC_CTRL_EVENT_EX_VID 1
#define ZYNQMP_DISP_AV_BUF_STC_CTRL_EVENT_EX_AUD 2
#define ZYNQMP_DISP_AV_BUF_STC_CTRL_EVENT_INT_VSYNC 3
#define ZYNQMP_DISP_AV_BUF_STC_INIT_VALUE0 0x30
#define ZYNQMP_DISP_AV_BUF_STC_INIT_VALUE1 0x34
#define ZYNQMP_DISP_AV_BUF_STC_ADJ 0x38
#define ZYNQMP_DISP_AV_BUF_STC_VID_VSYNC_TS0 0x3c
#define ZYNQMP_DISP_AV_BUF_STC_VID_VSYNC_TS1 0x40
#define ZYNQMP_DISP_AV_BUF_STC_EXT_VSYNC_TS0 0x44
#define ZYNQMP_DISP_AV_BUF_STC_EXT_VSYNC_TS1 0x48
#define ZYNQMP_DISP_AV_BUF_STC_CUSTOM_EVENT_TS0 0x4c
#define ZYNQMP_DISP_AV_BUF_STC_CUSTOM_EVENT_TS1 0x50
#define ZYNQMP_DISP_AV_BUF_STC_CUSTOM_EVENT2_TS0 0x54
#define ZYNQMP_DISP_AV_BUF_STC_CUSTOM_EVENT2_TS1 0x58
#define ZYNQMP_DISP_AV_BUF_STC_SNAPSHOT0 0x60
#define ZYNQMP_DISP_AV_BUF_STC_SNAPSHOT1 0x64
#define ZYNQMP_DISP_AV_BUF_OUTPUT 0x70
#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_SHIFT 0
#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_MASK (0x3 << 0)
#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_LIVE (0 << 0)
#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_MEM (1 << 0)
#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_PATTERN (2 << 0)
#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_NONE (3 << 0)
#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_SHIFT 2
#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_MASK (0x3 << 2)
#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_DISABLE (0 << 2)
#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_MEM (1 << 2)
#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_LIVE (2 << 2)
#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_NONE (3 << 2)
#define ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_SHIFT 4
#define ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_MASK (0x3 << 4)
#define ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_PL (0 << 4)
#define ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_MEM (1 << 4)
#define ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_PATTERN (2 << 4)
#define ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_DISABLE (3 << 4)
#define ZYNQMP_DISP_AV_BUF_OUTPUT_AUD2_EN BIT(6)
#define ZYNQMP_DISP_AV_BUF_HCOUNT_VCOUNT_INT0 0x74
#define ZYNQMP_DISP_AV_BUF_HCOUNT_VCOUNT_INT1 0x78
#define ZYNQMP_DISP_AV_BUF_PATTERN_GEN_SELECT 0x100
#define ZYNQMP_DISP_AV_BUF_CLK_SRC 0x120
#define ZYNQMP_DISP_AV_BUF_CLK_SRC_VID_FROM_PS BIT(0)
#define ZYNQMP_DISP_AV_BUF_CLK_SRC_AUD_FROM_PS BIT(1)
#define ZYNQMP_DISP_AV_BUF_CLK_SRC_VID_INTERNAL_TIMING BIT(2)
#define ZYNQMP_DISP_AV_BUF_SRST_REG 0x124
#define ZYNQMP_DISP_AV_BUF_SRST_REG_VID_RST BIT(1)
#define ZYNQMP_DISP_AV_BUF_AUDIO_CH_CONFIG 0x12c
#define ZYNQMP_DISP_AV_BUF_GFX_COMP_SF(n) (0x200 + ((n) * 4))
#define ZYNQMP_DISP_AV_BUF_VID_COMP_SF(n) (0x20c + ((n) * 4))
#define ZYNQMP_DISP_AV_BUF_LIVD_VID_COMP_SF(n) (0x218 + ((n) * 4))
#define ZYNQMP_DISP_AV_BUF_LIVE_VID_CONFIG 0x224
#define ZYNQMP_DISP_AV_BUF_LIVD_GFX_COMP_SF(n) (0x228 + ((n) * 4))
#define ZYNQMP_DISP_AV_BUF_LIVE_GFX_CONFIG 0x234
#define ZYNQMP_DISP_AV_BUF_4BIT_SF 0x11111
#define ZYNQMP_DISP_AV_BUF_5BIT_SF 0x10842
#define ZYNQMP_DISP_AV_BUF_6BIT_SF 0x10410
#define ZYNQMP_DISP_AV_BUF_8BIT_SF 0x10101
#define ZYNQMP_DISP_AV_BUF_10BIT_SF 0x10040
#define ZYNQMP_DISP_AV_BUF_NULL_SF 0
#define ZYNQMP_DISP_AV_BUF_NUM_SF 3
#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_6 0x0
#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_8 0x1
#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_10 0x2
#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_12 0x3
#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_MASK GENMASK(2, 0)
#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_RGB 0x0
#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_YUV444 0x1
#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_YUV422 0x2
#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_YONLY 0x3
#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_MASK GENMASK(5, 4)
#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_CB_FIRST BIT(8)
#define ZYNQMP_DISP_AV_BUF_PALETTE_MEMORY 0x400
/* Audio registers */
#define ZYNQMP_DISP_AUD_MIXER_VOLUME 0x0
#define ZYNQMP_DISP_AUD_MIXER_VOLUME_NO_SCALE 0x20002000
#define ZYNQMP_DISP_AUD_MIXER_META_DATA 0x4
#define ZYNQMP_DISP_AUD_CH_STATUS0 0x8
#define ZYNQMP_DISP_AUD_CH_STATUS1 0xc
#define ZYNQMP_DISP_AUD_CH_STATUS2 0x10
#define ZYNQMP_DISP_AUD_CH_STATUS3 0x14
#define ZYNQMP_DISP_AUD_CH_STATUS4 0x18
#define ZYNQMP_DISP_AUD_CH_STATUS5 0x1c
#define ZYNQMP_DISP_AUD_CH_A_DATA0 0x20
#define ZYNQMP_DISP_AUD_CH_A_DATA1 0x24
#define ZYNQMP_DISP_AUD_CH_A_DATA2 0x28
#define ZYNQMP_DISP_AUD_CH_A_DATA3 0x2c
#define ZYNQMP_DISP_AUD_CH_A_DATA4 0x30
#define ZYNQMP_DISP_AUD_CH_A_DATA5 0x34
#define ZYNQMP_DISP_AUD_CH_B_DATA0 0x38
#define ZYNQMP_DISP_AUD_CH_B_DATA1 0x3c
#define ZYNQMP_DISP_AUD_CH_B_DATA2 0x40
#define ZYNQMP_DISP_AUD_CH_B_DATA3 0x44
#define ZYNQMP_DISP_AUD_CH_B_DATA4 0x48
#define ZYNQMP_DISP_AUD_CH_B_DATA5 0x4c
#define ZYNQMP_DISP_AUD_SOFT_RESET 0xc00
#define ZYNQMP_DISP_AUD_SOFT_RESET_AUD_SRST BIT(0)
#endif /* _ZYNQMP_DISP_REGS_H_ */
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0 */
/*
* ZynqMP DisplayPort Driver
*
* Copyright (C) 2017 - 2020 Xilinx, Inc.
*
* Authors:
* - Hyun Woo Kwon <hyun.kwon@xilinx.com>
* - Laurent Pinchart <laurent.pinchart@ideasonboard.com>
*/
#ifndef _ZYNQMP_DP_H_
#define _ZYNQMP_DP_H_
struct drm_device;
struct platform_device;
struct zynqmp_dp;
struct zynqmp_dpsub;
void zynqmp_dp_enable_vblank(struct zynqmp_dp *dp);
void zynqmp_dp_disable_vblank(struct zynqmp_dp *dp);
int zynqmp_dp_drm_init(struct zynqmp_dpsub *dpsub);
int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm);
void zynqmp_dp_remove(struct zynqmp_dpsub *dpsub);
#endif /* _ZYNQMP_DP_H_ */
// SPDX-License-Identifier: GPL-2.0
/*
* ZynqMP DisplayPort Subsystem Driver
*
* Copyright (C) 2017 - 2020 Xilinx, Inc.
*
* Authors:
* - Hyun Woo Kwon <hyun.kwon@xilinx.com>
* - Laurent Pinchart <laurent.pinchart@ideasonboard.com>
*/
#include <linux/clk.h>
#include <linux/dma-mapping.h>
#include <linux/module.h>
#include <linux/of_reserved_mem.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_device.h>
#include <drm/drm_drv.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_managed.h>
#include <drm/drm_mode_config.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
#include "zynqmp_disp.h"
#include "zynqmp_dp.h"
#include "zynqmp_dpsub.h"
/* -----------------------------------------------------------------------------
* Dumb Buffer & Framebuffer Allocation
*/
static int zynqmp_dpsub_dumb_create(struct drm_file *file_priv,
struct drm_device *drm,
struct drm_mode_create_dumb *args)
{
struct zynqmp_dpsub *dpsub = to_zynqmp_dpsub(drm);
unsigned int pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
/* Enforce the alignment constraints of the DMA engine. */
args->pitch = ALIGN(pitch, dpsub->dma_align);
return drm_gem_cma_dumb_create_internal(file_priv, drm, args);
}
static struct drm_framebuffer *
zynqmp_dpsub_fb_create(struct drm_device *drm, struct drm_file *file_priv,
const struct drm_mode_fb_cmd2 *mode_cmd)
{
struct zynqmp_dpsub *dpsub = to_zynqmp_dpsub(drm);
struct drm_mode_fb_cmd2 cmd = *mode_cmd;
unsigned int i;
/* Enforce the alignment constraints of the DMA engine. */
for (i = 0; i < ARRAY_SIZE(cmd.pitches); ++i)
cmd.pitches[i] = ALIGN(cmd.pitches[i], dpsub->dma_align);
return drm_gem_fb_create(drm, file_priv, &cmd);
}
static const struct drm_mode_config_funcs zynqmp_dpsub_mode_config_funcs = {
.fb_create = zynqmp_dpsub_fb_create,
.atomic_check = drm_atomic_helper_check,
.atomic_commit = drm_atomic_helper_commit,
};
/* -----------------------------------------------------------------------------
* DRM/KMS Driver
*/
DEFINE_DRM_GEM_CMA_FOPS(zynqmp_dpsub_drm_fops);
static struct drm_driver zynqmp_dpsub_drm_driver = {
.driver_features = DRIVER_MODESET | DRIVER_GEM |
DRIVER_ATOMIC,
.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
.gem_prime_export = drm_gem_prime_export,
.gem_prime_import = drm_gem_prime_import,
.gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
.gem_prime_vmap = drm_gem_cma_prime_vmap,
.gem_prime_vunmap = drm_gem_cma_prime_vunmap,
.gem_prime_mmap = drm_gem_cma_prime_mmap,
.gem_free_object_unlocked = drm_gem_cma_free_object,
.gem_vm_ops = &drm_gem_cma_vm_ops,
.dumb_create = zynqmp_dpsub_dumb_create,
.dumb_destroy = drm_gem_dumb_destroy,
.fops = &zynqmp_dpsub_drm_fops,
.name = "zynqmp-dpsub",
.desc = "Xilinx DisplayPort Subsystem Driver",
.date = "20130509",
.major = 1,
.minor = 0,
};
static int zynqmp_dpsub_drm_init(struct zynqmp_dpsub *dpsub)
{
struct drm_device *drm = &dpsub->drm;
int ret;
/* Initialize mode config, vblank and the KMS poll helper. */
ret = drmm_mode_config_init(drm);
if (ret < 0)
goto err_dev_put;
drm->mode_config.funcs = &zynqmp_dpsub_mode_config_funcs;
drm->mode_config.min_width = 0;
drm->mode_config.min_height = 0;
drm->mode_config.max_width = ZYNQMP_DISP_MAX_WIDTH;
drm->mode_config.max_height = ZYNQMP_DISP_MAX_HEIGHT;
ret = drm_vblank_init(drm, 1);
if (ret)
goto err_dev_put;
drm->irq_enabled = 1;
drm_kms_helper_poll_init(drm);
/*
* Initialize the DISP and DP components. This will creates planes,
* CRTC, encoder and connector. The DISP should be initialized first as
* the DP encoder needs the CRTC.
*/
ret = zynqmp_disp_drm_init(dpsub);
if (ret)
goto err_poll_fini;
ret = zynqmp_dp_drm_init(dpsub);
if (ret)
goto err_poll_fini;
/* Reset all components and register the DRM device. */
drm_mode_config_reset(drm);
ret = drm_dev_register(drm, 0);
if (ret < 0)
goto err_poll_fini;
/* Initialize fbdev generic emulation. */
drm_fbdev_generic_setup(drm, 24);
return 0;
err_poll_fini:
drm_kms_helper_poll_fini(drm);
err_dev_put:
drm_dev_put(drm);
return ret;
}
/* -----------------------------------------------------------------------------
* Power Management
*/
static int __maybe_unused zynqmp_dpsub_suspend(struct device *dev)
{
struct zynqmp_dpsub *dpsub = dev_get_drvdata(dev);
return drm_mode_config_helper_suspend(&dpsub->drm);
}
static int __maybe_unused zynqmp_dpsub_resume(struct device *dev)
{
struct zynqmp_dpsub *dpsub = dev_get_drvdata(dev);
return drm_mode_config_helper_resume(&dpsub->drm);
}
static const struct dev_pm_ops zynqmp_dpsub_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(zynqmp_dpsub_suspend, zynqmp_dpsub_resume)
};
/* -----------------------------------------------------------------------------
* Probe & Remove
*/
static int zynqmp_dpsub_init_clocks(struct zynqmp_dpsub *dpsub)
{
int ret;
dpsub->apb_clk = devm_clk_get(dpsub->dev, "dp_apb_clk");
if (IS_ERR(dpsub->apb_clk))
return PTR_ERR(dpsub->apb_clk);
ret = clk_prepare_enable(dpsub->apb_clk);
if (ret) {
dev_err(dpsub->dev, "failed to enable the APB clock\n");
return ret;
}
return 0;
}
static int zynqmp_dpsub_probe(struct platform_device *pdev)
{
struct zynqmp_dpsub *dpsub;
int ret;
/* Allocate private data. */
dpsub = kzalloc(sizeof(*dpsub), GFP_KERNEL);
if (!dpsub)
return -ENOMEM;
dpsub->dev = &pdev->dev;
platform_set_drvdata(pdev, dpsub);
dma_set_mask(dpsub->dev, DMA_BIT_MASK(ZYNQMP_DISP_MAX_DMA_BIT));
/*
* Initialize the DRM device early, as the DRM core mandates usage of
* the managed memory helpers tied to the DRM device.
*/
ret = drm_dev_init(&dpsub->drm, &zynqmp_dpsub_drm_driver, &pdev->dev);
if (ret < 0) {
kfree(dpsub);
return ret;
}
drmm_add_final_kfree(&dpsub->drm, dpsub);
/* Try the reserved memory. Proceed if there's none. */
of_reserved_mem_device_init(&pdev->dev);
ret = zynqmp_dpsub_init_clocks(dpsub);
if (ret < 0)
goto err_mem;
pm_runtime_enable(&pdev->dev);
/*
* DP should be probed first so that the zynqmp_disp can set the output
* format accordingly.
*/
ret = zynqmp_dp_probe(dpsub, &dpsub->drm);
if (ret)
goto err_pm;
ret = zynqmp_disp_probe(dpsub, &dpsub->drm);
if (ret)
goto err_dp;
ret = zynqmp_dpsub_drm_init(dpsub);
if (ret)
goto err_disp;
dev_info(&pdev->dev, "ZynqMP DisplayPort Subsystem driver probed");
return 0;
err_disp:
zynqmp_disp_remove(dpsub);
err_dp:
zynqmp_dp_remove(dpsub);
err_pm:
pm_runtime_disable(&pdev->dev);
clk_disable_unprepare(dpsub->apb_clk);
err_mem:
of_reserved_mem_device_release(&pdev->dev);
return ret;
}
static int zynqmp_dpsub_remove(struct platform_device *pdev)
{
struct zynqmp_dpsub *dpsub = platform_get_drvdata(pdev);
struct drm_device *drm = &dpsub->drm;
drm_dev_unregister(drm);
drm_atomic_helper_shutdown(drm);
drm_kms_helper_poll_fini(drm);
zynqmp_disp_remove(dpsub);
zynqmp_dp_remove(dpsub);
pm_runtime_disable(&pdev->dev);
clk_disable_unprepare(dpsub->apb_clk);
of_reserved_mem_device_release(&pdev->dev);
drm_dev_put(drm);
return 0;
}
static void zynqmp_dpsub_shutdown(struct platform_device *pdev)
{
struct zynqmp_dpsub *dpsub = platform_get_drvdata(pdev);
drm_atomic_helper_shutdown(&dpsub->drm);
}
static const struct of_device_id zynqmp_dpsub_of_match[] = {
{ .compatible = "xlnx,zynqmp-dpsub-1.7", },
{ /* end of table */ },
};
MODULE_DEVICE_TABLE(of, zynqmp_dpsub_of_match);
static struct platform_driver zynqmp_dpsub_driver = {
.probe = zynqmp_dpsub_probe,
.remove = zynqmp_dpsub_remove,
.shutdown = zynqmp_dpsub_shutdown,
.driver = {
.name = "zynqmp-dpsub",
.pm = &zynqmp_dpsub_pm_ops,
.of_match_table = zynqmp_dpsub_of_match,
},
};
module_platform_driver(zynqmp_dpsub_driver);
MODULE_AUTHOR("Xilinx, Inc.");
MODULE_DESCRIPTION("ZynqMP DP Subsystem Driver");
MODULE_LICENSE("GPL v2");
/* SPDX-License-Identifier: GPL-2.0 */
/*
* ZynqMP DPSUB Subsystem Driver
*
* Copyright (C) 2017 - 2020 Xilinx, Inc.
*
* Authors:
* - Hyun Woo Kwon <hyun.kwon@xilinx.com>
* - Laurent Pinchart <laurent.pinchart@ideasonboard.com>
*/
#ifndef _ZYNQMP_DPSUB_H_
#define _ZYNQMP_DPSUB_H_
struct clk;
struct device;
struct drm_device;
struct zynqmp_disp;
struct zynqmp_dp;
enum zynqmp_dpsub_format {
ZYNQMP_DPSUB_FORMAT_RGB,
ZYNQMP_DPSUB_FORMAT_YCRCB444,
ZYNQMP_DPSUB_FORMAT_YCRCB422,
ZYNQMP_DPSUB_FORMAT_YONLY,
};
/**
* struct zynqmp_dpsub - ZynqMP DisplayPort Subsystem
* @drm: The DRM/KMS device
* @dev: The physical device
* @apb_clk: The APB clock
* @disp: The display controller
* @dp: The DisplayPort controller
* @dma_align: DMA alignment constraint (must be a power of 2)
*/
struct zynqmp_dpsub {
struct drm_device drm;
struct device *dev;
struct clk *apb_clk;
struct zynqmp_disp *disp;
struct zynqmp_dp *dp;
unsigned int dma_align;
};
static inline struct zynqmp_dpsub *to_zynqmp_dpsub(struct drm_device *drm)
{
return container_of(drm, struct zynqmp_dpsub, drm);
}
#endif /* _ZYNQMP_DPSUB_H_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment