diff options
Diffstat (limited to 'drivers/gpu/drm/bridge/analogix/analogix_dp_core.c')
-rw-r--r-- | drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 291 |
1 files changed, 207 insertions, 84 deletions
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c index 37bd541166a5..c82c7d5f3c6d 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c @@ -19,6 +19,7 @@ #include <linux/platform_device.h> #include <drm/bridge/analogix_dp.h> +#include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc.h> #include <drm/drm_device.h> @@ -101,63 +102,7 @@ static int analogix_dp_detect_hpd(struct analogix_dp_device *dp) return 0; } -int analogix_dp_psr_enabled(struct analogix_dp_device *dp) -{ - - return dp->psr_enable; -} -EXPORT_SYMBOL_GPL(analogix_dp_psr_enabled); - -int analogix_dp_enable_psr(struct analogix_dp_device *dp) -{ - struct dp_sdp psr_vsc; - - if (!dp->psr_enable) - return 0; - - /* Prepare VSC packet as per EDP 1.4 spec, Table 6.9 */ - memset(&psr_vsc, 0, sizeof(psr_vsc)); - psr_vsc.sdp_header.HB0 = 0; - psr_vsc.sdp_header.HB1 = 0x7; - psr_vsc.sdp_header.HB2 = 0x2; - psr_vsc.sdp_header.HB3 = 0x8; - - psr_vsc.db[0] = 0; - psr_vsc.db[1] = EDP_VSC_PSR_STATE_ACTIVE | EDP_VSC_PSR_CRC_VALUES_VALID; - - return analogix_dp_send_psr_spd(dp, &psr_vsc, true); -} -EXPORT_SYMBOL_GPL(analogix_dp_enable_psr); - -int analogix_dp_disable_psr(struct analogix_dp_device *dp) -{ - struct dp_sdp psr_vsc; - int ret; - - if (!dp->psr_enable) - return 0; - - /* Prepare VSC packet as per EDP 1.4 spec, Table 6.9 */ - memset(&psr_vsc, 0, sizeof(psr_vsc)); - psr_vsc.sdp_header.HB0 = 0; - psr_vsc.sdp_header.HB1 = 0x7; - psr_vsc.sdp_header.HB2 = 0x2; - psr_vsc.sdp_header.HB3 = 0x8; - - psr_vsc.db[0] = 0; - psr_vsc.db[1] = 0; - - ret = drm_dp_dpcd_writeb(&dp->aux, DP_SET_POWER, DP_SET_POWER_D0); - if (ret != 1) { - dev_err(dp->dev, "Failed to set DP Power0 %d\n", ret); - return ret; - } - - return analogix_dp_send_psr_spd(dp, &psr_vsc, false); -} -EXPORT_SYMBOL_GPL(analogix_dp_disable_psr); - -static int analogix_dp_detect_sink_psr(struct analogix_dp_device *dp) +static bool analogix_dp_detect_sink_psr(struct analogix_dp_device *dp) { unsigned char psr_version; int ret; @@ -165,14 +110,11 @@ static int analogix_dp_detect_sink_psr(struct analogix_dp_device *dp) ret = drm_dp_dpcd_readb(&dp->aux, DP_PSR_SUPPORT, &psr_version); if (ret != 1) { dev_err(dp->dev, "failed to get PSR version, disable it\n"); - return ret; + return false; } dev_dbg(dp->dev, "Panel PSR version : %x\n", psr_version); - - dp->psr_enable = (psr_version & DP_PSR_IS_SUPPORTED) ? true : false; - - return 0; + return psr_version & DP_PSR_IS_SUPPORTED; } static int analogix_dp_enable_sink_psr(struct analogix_dp_device *dp) @@ -195,7 +137,7 @@ static int analogix_dp_enable_sink_psr(struct analogix_dp_device *dp) } /* Main-Link transmitter remains active during PSR active states */ - psr_en = DP_PSR_MAIN_LINK_ACTIVE | DP_PSR_CRC_VERIFICATION; + psr_en = DP_PSR_CRC_VERIFICATION; ret = drm_dp_dpcd_writeb(&dp->aux, DP_PSR_EN_CFG, psr_en); if (ret != 1) { dev_err(dp->dev, "failed to set panel psr\n"); @@ -203,8 +145,7 @@ static int analogix_dp_enable_sink_psr(struct analogix_dp_device *dp) } /* Enable psr function */ - psr_en = DP_PSR_ENABLE | DP_PSR_MAIN_LINK_ACTIVE | - DP_PSR_CRC_VERIFICATION; + psr_en = DP_PSR_ENABLE | DP_PSR_CRC_VERIFICATION; ret = drm_dp_dpcd_writeb(&dp->aux, DP_PSR_EN_CFG, psr_en); if (ret != 1) { dev_err(dp->dev, "failed to set panel psr\n"); @@ -213,10 +154,11 @@ static int analogix_dp_enable_sink_psr(struct analogix_dp_device *dp) analogix_dp_enable_psr_crc(dp); + dp->psr_supported = true; + return 0; end: dev_err(dp->dev, "enable psr fail, force to disable psr\n"); - dp->psr_enable = false; return ret; } @@ -1031,25 +973,90 @@ static int analogix_dp_commit(struct analogix_dp_device *dp) } } - ret = analogix_dp_detect_sink_psr(dp); - if (ret) - return ret; - /* Check whether panel supports fast training */ ret = analogix_dp_fast_link_train_detection(dp); if (ret) - dp->psr_enable = false; + return ret; - if (dp->psr_enable) { + if (analogix_dp_detect_sink_psr(dp)) { ret = analogix_dp_enable_sink_psr(dp); if (ret) return ret; } + return ret; +} + +static int analogix_dp_enable_psr(struct analogix_dp_device *dp) +{ + struct dp_sdp psr_vsc; + int ret; + u8 sink; + + ret = drm_dp_dpcd_readb(&dp->aux, DP_PSR_STATUS, &sink); + if (ret != 1) + DRM_DEV_ERROR(dp->dev, "Failed to read psr status %d\n", ret); + else if (sink == DP_PSR_SINK_ACTIVE_RFB) + return 0; + + /* Prepare VSC packet as per EDP 1.4 spec, Table 6.9 */ + memset(&psr_vsc, 0, sizeof(psr_vsc)); + psr_vsc.sdp_header.HB0 = 0; + psr_vsc.sdp_header.HB1 = 0x7; + psr_vsc.sdp_header.HB2 = 0x2; + psr_vsc.sdp_header.HB3 = 0x8; + psr_vsc.db[0] = 0; + psr_vsc.db[1] = EDP_VSC_PSR_STATE_ACTIVE | EDP_VSC_PSR_CRC_VALUES_VALID; + + ret = analogix_dp_send_psr_spd(dp, &psr_vsc, true); + if (!ret) + analogix_dp_set_analog_power_down(dp, POWER_ALL, true); return ret; } +static int analogix_dp_disable_psr(struct analogix_dp_device *dp) +{ + struct dp_sdp psr_vsc; + int ret; + u8 sink; + + analogix_dp_set_analog_power_down(dp, POWER_ALL, false); + + ret = drm_dp_dpcd_writeb(&dp->aux, DP_SET_POWER, DP_SET_POWER_D0); + if (ret != 1) { + DRM_DEV_ERROR(dp->dev, "Failed to set DP Power0 %d\n", ret); + return ret; + } + + ret = drm_dp_dpcd_readb(&dp->aux, DP_PSR_STATUS, &sink); + if (ret != 1) { + DRM_DEV_ERROR(dp->dev, "Failed to read psr status %d\n", ret); + return ret; + } else if (sink == DP_PSR_SINK_INACTIVE) { + DRM_DEV_ERROR(dp->dev, "sink inactive, skip disable psr"); + return 0; + } + + ret = analogix_dp_train_link(dp); + if (ret) { + DRM_DEV_ERROR(dp->dev, "Failed to train the link %d\n", ret); + return ret; + } + + /* Prepare VSC packet as per EDP 1.4 spec, Table 6.9 */ + memset(&psr_vsc, 0, sizeof(psr_vsc)); + psr_vsc.sdp_header.HB0 = 0; + psr_vsc.sdp_header.HB1 = 0x7; + psr_vsc.sdp_header.HB2 = 0x2; + psr_vsc.sdp_header.HB3 = 0x8; + + psr_vsc.db[0] = 0; + psr_vsc.db[1] = 0; + + return analogix_dp_send_psr_spd(dp, &psr_vsc, true); +} + /* * This function is a bit of a catch-all for panel preparation, hopefully * simplifying the logic of functions that need to prepare/unprepare the panel @@ -1140,9 +1147,37 @@ analogix_dp_best_encoder(struct drm_connector *connector) return dp->encoder; } + +int analogix_dp_atomic_check(struct drm_connector *connector, + struct drm_atomic_state *state) +{ + struct analogix_dp_device *dp = to_dp(connector); + struct drm_connector_state *conn_state; + struct drm_crtc_state *crtc_state; + + conn_state = drm_atomic_get_new_connector_state(state, connector); + if (WARN_ON(!conn_state)) + return -ENODEV; + + conn_state->self_refresh_aware = true; + + if (!conn_state->crtc) + return 0; + + crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc); + if (!crtc_state) + return 0; + + if (crtc_state->self_refresh_active && !dp->psr_supported) + return -EINVAL; + + return 0; +} + static const struct drm_connector_helper_funcs analogix_dp_connector_helper_funcs = { .get_modes = analogix_dp_get_modes, .best_encoder = analogix_dp_best_encoder, + .atomic_check = analogix_dp_atomic_check, }; static enum drm_connector_status @@ -1234,11 +1269,42 @@ static int analogix_dp_bridge_attach(struct drm_bridge *bridge) return 0; } -static void analogix_dp_bridge_pre_enable(struct drm_bridge *bridge) +static +struct drm_crtc *analogix_dp_get_new_crtc(struct analogix_dp_device *dp, + struct drm_atomic_state *state) +{ + struct drm_encoder *encoder = dp->encoder; + struct drm_connector *connector; + struct drm_connector_state *conn_state; + + connector = drm_atomic_get_new_connector_for_encoder(state, encoder); + if (!connector) + return NULL; + + conn_state = drm_atomic_get_new_connector_state(state, connector); + if (!conn_state) + return NULL; + + return conn_state->crtc; +} + +static void analogix_dp_bridge_atomic_pre_enable(struct drm_bridge *bridge, + struct drm_atomic_state *state) { struct analogix_dp_device *dp = bridge->driver_private; + struct drm_crtc *crtc; + struct drm_crtc_state *old_crtc_state; int ret; + crtc = analogix_dp_get_new_crtc(dp, state); + if (!crtc) + return; + + old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc); + /* Don't touch the panel if we're coming back from PSR */ + if (old_crtc_state && old_crtc_state->self_refresh_active) + return; + ret = analogix_dp_prepare_panel(dp, true, true); if (ret) DRM_ERROR("failed to setup the panel ret = %d\n", ret); @@ -1299,10 +1365,27 @@ out_dp_clk_pre: return ret; } -static void analogix_dp_bridge_enable(struct drm_bridge *bridge) +static void analogix_dp_bridge_atomic_enable(struct drm_bridge *bridge, + struct drm_atomic_state *state) { struct analogix_dp_device *dp = bridge->driver_private; + struct drm_crtc *crtc; + struct drm_crtc_state *old_crtc_state; int timeout_loop = 0; + int ret; + + crtc = analogix_dp_get_new_crtc(dp, state); + if (!crtc) + return; + + old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc); + /* Not a full enable, just disable PSR and continue */ + if (old_crtc_state && old_crtc_state->self_refresh_active) { + ret = analogix_dp_disable_psr(dp); + if (ret) + DRM_ERROR("Failed to disable psr %d\n", ret); + return; + } if (dp->dpms_mode == DRM_MODE_DPMS_ON) return; @@ -1351,11 +1434,56 @@ static void analogix_dp_bridge_disable(struct drm_bridge *bridge) if (ret) DRM_ERROR("failed to setup the panel ret = %d\n", ret); - dp->psr_enable = false; dp->fast_train_enable = false; + dp->psr_supported = false; dp->dpms_mode = DRM_MODE_DPMS_OFF; } +static void analogix_dp_bridge_atomic_disable(struct drm_bridge *bridge, + struct drm_atomic_state *state) +{ + struct analogix_dp_device *dp = bridge->driver_private; + struct drm_crtc *crtc; + struct drm_crtc_state *new_crtc_state = NULL; + + crtc = analogix_dp_get_new_crtc(dp, state); + if (!crtc) + goto out; + + new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + if (!new_crtc_state) + goto out; + + /* Don't do a full disable on PSR transitions */ + if (new_crtc_state->self_refresh_active) + return; + +out: + analogix_dp_bridge_disable(bridge); +} + +static +void analogix_dp_bridge_atomic_post_disable(struct drm_bridge *bridge, + struct drm_atomic_state *state) +{ + struct analogix_dp_device *dp = bridge->driver_private; + struct drm_crtc *crtc; + struct drm_crtc_state *new_crtc_state; + int ret; + + crtc = analogix_dp_get_new_crtc(dp, state); + if (!crtc) + return; + + new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + if (!new_crtc_state || !new_crtc_state->self_refresh_active) + return; + + ret = analogix_dp_enable_psr(dp); + if (ret) + DRM_ERROR("Failed to enable psr (%d)\n", ret); +} + static void analogix_dp_bridge_mode_set(struct drm_bridge *bridge, const struct drm_display_mode *orig_mode, const struct drm_display_mode *mode) @@ -1433,16 +1561,11 @@ static void analogix_dp_bridge_mode_set(struct drm_bridge *bridge, video->interlaced = true; } -static void analogix_dp_bridge_nop(struct drm_bridge *bridge) -{ - /* do nothing */ -} - static const struct drm_bridge_funcs analogix_dp_bridge_funcs = { - .pre_enable = analogix_dp_bridge_pre_enable, - .enable = analogix_dp_bridge_enable, - .disable = analogix_dp_bridge_disable, - .post_disable = analogix_dp_bridge_nop, + .atomic_pre_enable = analogix_dp_bridge_atomic_pre_enable, + .atomic_enable = analogix_dp_bridge_atomic_enable, + .atomic_disable = analogix_dp_bridge_atomic_disable, + .atomic_post_disable = analogix_dp_bridge_atomic_post_disable, .mode_set = analogix_dp_bridge_mode_set, .attach = analogix_dp_bridge_attach, }; |