Power Management

Linux Drivers

Release Date
2023-07-22
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;
}
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);
 
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
#!/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
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);
        ...
}
 
 
 
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);
}
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);
}
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);
}
 
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;
}