This page gives an overview of the DisplayPort driver which is available as part of the ZynqMP Linux distribution. The main driver is based on the Linux DRM KMS display subsystem. Some additional drivers are based on other subsystem: DMA engine driver for DPDMA and ALSA driver for audio support.
...
Table of Contents
Table of Contents |
---|
HW IP features
Features in bold are supported by the driver- Based on the VESA DisplayPort V.12a source-only specification.
- Video support for the following:
° Resolution up to 4K x 2K at 30Fps.
° Y-only, YCbCr444, YCbCr422, YCbCr420, and RGB video formats.
° 6, 8, 10, or 12 bits per color components.
° Progressive video.
° A 36-bit native video input interface to capture live video.
° Non-live video from frame buffers using internal DPDMA. - Graphics support for the following:
° Non-live graphics from the frame buffer.
° 36-bit native video interface along with an 8-bit alpha channel to capture live graphics.
° 2-plane, on-the-fly rendering of video and graphics.
° Chroma upsampling.
° Chroma downsampling.
° Color space conversion from YCbCr to RGB and vice versa.
° Video blending.
° Chroma keying. - Audio support for the following:
° Up to two audio channels.
° Sample size of up to 24 bits.
° Maximum sample rate of 48 KHz.
° Live 24-bit audio from the PL.
° Non-live 16-bit audio from the frame buffer. - Audio mixer and volume control.
° Mixing of two audio streams of the same sampling rate and channel count.
° Provides gain control for audio streams. - Streaming A/V output back to the PL.
- Includes a system time clock (STC) that is compliant with the ISO/IEC 13818-1 standard. Provides time stamping of the A/V presentation unit.
Missing Features, Known Issues and Limitations
Missing Features
This section summarizes the known issues and missing features (or experimental)- .Live support is experimental feature
- Live input is supported as experimental (live input)
- Live output hasn't been
- Input from / out to PL
- fully verified
- Pixel formats
- 12 bit YUV formats are not supported
- Upstreaming on going
- Interop
- Some monitor may not sync reliably. Please refer to ZU+ supported monitors
Limitation
- Both layers (drm planes) should be in the same size. Otherwise, if the requested size is different, the driver returns an error. This is hardware limitation.
Important AR links
Note
The driver implementation has switched to the new driver in 2018.1, which supports new software features with more modular / scalable structure. The behavior of some APIs may be different, as well as the driver name from "xilinx_drm" to "xlnx".Kernel Configuration
The dedicated DMA engine for DisplayPort should be enabled.
CONFIG_XILINX_DPDMA=y
The following config options are optional and required for audio support.
CONFIG_SOUND=y
CONFIG_SND=y
CONFIG_SND_SOC=y
CONFIG_SND_SOC_XILINX_DP=y
The driver, as some other multimedia drivers, needs CMA chunk. The following config options are recommended.
CONFIG_CMA=y
CONFIG_DMA_CMA=y
CONFIG_CMA_SIZE_MBYTES=256
The below is for new driver (>= 2018.1)
The following config options should be enabled in order to build the DisplayPort driver
CONFIG_DRM_XLNX=y
CONFIG_DRM_XLNX_BRIDGE=y
CONFIG_DRM_XLNX_BRIDGE_DEBUG_FS=y
CONFIG_DRM_ZYNQMP_DPSUB=y
The below is for old driver (< 2018.1)
The following config options should be enabled in order to build the DisplayPort driver
CONFIG_DRM=y
CONFIG_DRM_XILINX=y
CONFIG_DRM_XILINX_DP=y
CONFIG_DRM_XILINX_DP_SUB=y
Devicetree
The below is for new driver (>= 2018.1)The snippet:
- zynqmp-display: zynqmp-display
The dt binding doc: Documentation/devicetree/bindings/display/xlnx/xlnx,zynqmp-dpsub.txt
- xlnx,zynqmp-dpsub.txt
The below is for old driver (< 2018.1)
Device tree nodes required are as below in "arch/arm64/boot/dts/xilinx/zynqmp.dtsi"
- Xilinx drm: xilinx drm node
- dp: dp
- dp subsystem: dp_sub
- dpdma: dpdma
For audio support
- alsa nodes: card, codec, pcm
For more details, Please refer to "Documentation/devicetree/bindings/drm/xilinx"
- dp.txt
- dp_sub.txt
- xilinx_drm.txt
Implementation
Power Management
The display pipeline is enabled and disabled upon the monitor HPD (hot plug detect) events. When there's disconnect event the DRM framework disables the pipeline. For the connect event the pipeline is enabled. The detect callback below from DP driver reports the connection status. This management is called DPMS (Display Power Management Signaling).Code Block | ||
---|---|---|
| ||
static enum drm_connector_status zynqmp_dp_connector_detect(struct drm_connector *connector, bool force) { if () { .... return connector_status_connected; } disconnected: return connector_status_disconnected; } |
Upon the event, the DRM framework enables and disables the pipeline if possible. The below, the drm_kms_helper_hotplug_event() will disable or enable the fb console based on the event type.
Code Block | ||
---|---|---|
| ||
bool drm_helper_hpd_irq_event(struct drm_device *dev) { ... connector->status = drm_helper_probe_detect(connector, NULL, false); .... if (changed) drm_kms_helper_hotplug_event(dev); return changed; } EXPORT_SYMBOL(drm_helper_hpd_irq_event); |
Code Block | ||
---|---|---|
| ||
void drm_kms_helper_hotplug_event(struct drm_device *dev) { /* send a uevent + call fbdev */ drm_sysfs_hotplug_event(dev); if (dev->mode_config.funcs->output_poll_changed) dev->mode_config.funcs->output_poll_changed(dev); } EXPORT_SYMBOL(drm_kms_helper_hotplug_event); |
https://github.com/Xilinx/meta-petalinux/blob/4e84d60865bf505d1ea425f3e5c84e37bf8f7455/recipes-core/udev/eudev/monitor-hotplug.sh
Code Block | ||
---|---|---|
| ||
#!/bin/sh #Adapt this script to your needs. DEVICES=$(find /sys/class/drm/*/status) #inspired by /etc/acpd/lid.sh and the function it sources displaynum=`ls /tmp/.X11-unix/* | sed s#/tmp/.X11-unix/X##` display=":$displaynum.0" export DISPLAY=":$displaynum.0" # from https://wiki.archlinux.org/index.php/Acpid#Laptop_Monitor_Power_Off export XAUTHORITY=$(ps -C Xorg -f --no-header | sed -n 's/.*-auth //; s/ -[^ ].*//; p') for i in /sys/class/drm/*/*/status ; do status=$(cat $i); connector=${i%/status*}; connector=${connector#*-}; if [ "$status" == "disconnected" ]; then xset dpms force off elif [ "$status" == "connected" ]; then xset dpms force on if [ "$(xrandr | grep '\*')" = "" ]; then xrandr --output $connector --auto fi fi done |
Code Block | ||
---|---|---|
| ||
static void zynqmp_disp_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_crtc_state *old_crtc_state) { ... pm_runtime_get_sync(disp->dev); ret = zynqmp_disp_clk_enable(disp->pclk, &disp->pclk_en); if (ret) { dev_err(disp->dev, "failed to enable a pixel clock\n"); return; } ... zynqmp_disp_enable(disp); ... } |
Code Block | ||
---|---|---|
| ||
static void zynqmp_disp_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_crtc_state *old_crtc_state) { ... zynqmp_disp_clk_disable(disp->pclk, &disp->pclk_en); zynqmp_disp_plane_disable(crtc->primary); zynqmp_disp_disable(disp, true); pm_runtime_put_sync(disp->dev); } |
Code Block | ||
---|---|---|
| ||
static void zynqmp_dp_encoder_enable(struct drm_encoder *encoder) { ... pm_runtime_get_sync(dp->dev); dp->enabled = true; zynqmp_dp_init_aux(dp); ... if (zynqmp_disp_aud_enabled(dp->dpsub->disp)) zynqmp_dp_write(iomem, ZYNQMP_DP_TX_AUDIO_CONTROL, 1); zynqmp_dp_write(iomem, ZYNQMP_DP_TX_PHY_POWER_DOWN, 0); if (dp->status == connector_status_connected) { for (i = 0; i < 3; i++) { ret = drm_dp_dpcd_writeb(&dp->aux, DP_SET_POWER, DP_SET_POWER_D0); if (ret == 1) break; usleep_range(300, 500); } /* Some monitors take time to wake up properly */ msleep(zynqmp_dp_power_on_delay_ms); } ... zynqmp_dp_write(iomem, ZYNQMP_DP_TX_SW_RESET, ZYNQMP_DP_TX_SW_RESET_ALL); zynqmp_dp_write(iomem, ZYNQMP_DP_TX_ENABLE_MAIN_STREAM, 1); } |
Code Block | ||
---|---|---|
| ||
static void zynqmp_dp_encoder_disable(struct drm_encoder *encoder) { ... zynqmp_dp_write(iomem, ZYNQMP_DP_TX_ENABLE_MAIN_STREAM, 0); drm_dp_dpcd_writeb(&dp->aux, DP_SET_POWER, DP_SET_POWER_D3); zynqmp_dp_write(iomem, ZYNQMP_DP_TX_PHY_POWER_DOWN, ZYNQMP_DP_TX_PHY_POWER_DOWN_ALL); if (zynqmp_disp_aud_enabled(dp->dpsub->disp)) zynqmp_dp_write(iomem, ZYNQMP_DP_TX_AUDIO_CONTROL, 0); pm_runtime_put_sync(dp->dev); } |
Code Block | ||
---|---|---|
| ||
static int __maybe_unused xlnx_pm_suspend(struct device *dev) { struct xlnx_drm *xlnx_drm = dev_get_drvdata(dev); struct drm_device *drm = xlnx_drm->drm; drm_kms_helper_poll_disable(drm); xlnx_drm->suspend_state = drm_atomic_helper_suspend(drm); if (IS_ERR(xlnx_drm->suspend_state)) { drm_kms_helper_poll_enable(drm); return PTR_ERR(xlnx_drm->suspend_state); } return 0; } static int __maybe_unused xlnx_pm_resume(struct device *dev) { struct xlnx_drm *xlnx_drm = dev_get_drvdata(dev); struct drm_device *drm = xlnx_drm->drm; drm_atomic_helper_resume(drm, xlnx_drm->suspend_state); drm_kms_helper_poll_enable(drm); return 0; } |
Applications
This section describes the example commands in drm or Xorg environment.DRM
modetest is the test application available from libdrm: https://cgit.freedesktop.org/mesa/drm/tree/tests/modetest....