Commit 8c322309 authored by Roman Li's avatar Roman Li Committed by Alex Deucher
Browse files

drm/amd/display: Enable PSR



[Why]
PSR (Panel Self-Refresh) is a power-saving feature for eDP panels.
The feature has support in DMCU (Display MicroController Unit).
DMCU/driver communication is implemented in DC.
DM can use existing DC PSR interface to use PSR feature.

[How]
- Read psr caps via dpcd
- Send vsc infoframe if panel supports psr
- Disable psr before h/w programming (FULL_UPDATE)
- Enable psr after h/w programming
- Disable psr for fb console

Signed-off-by: default avatarRoman Li <Roman.Li@amd.com>
Reviewed-by: default avatarNicholas Kazlauskas <Nicholas.Kazlauskas@amd.com>
Acked-by: default avatarLeo Li <sunpeng.li@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent e0d08a40
Loading
Loading
Loading
Loading
+130 −3
Original line number Diff line number Diff line
@@ -147,6 +147,12 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
static void handle_cursor_update(struct drm_plane *plane,
				 struct drm_plane_state *old_plane_state);

static void amdgpu_dm_set_psr_caps(struct dc_link *link);
static bool amdgpu_dm_psr_enable(struct dc_stream_state *stream);
static bool amdgpu_dm_link_setup_psr(struct dc_stream_state *stream);
static bool amdgpu_dm_psr_disable(struct dc_stream_state *stream);


/*
 * dm_vblank_get_counter
 *
@@ -2418,6 +2424,7 @@ static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev)
		} else if (dc_link_detect(link, DETECT_REASON_BOOT)) {
			amdgpu_dm_update_connector_after_detect(aconnector);
			register_backlight_device(dm, link);
			amdgpu_dm_set_psr_caps(link);
		}


@@ -3743,7 +3750,16 @@ create_stream_for_sink(struct amdgpu_dm_connector *aconnector,

	if (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A)
		mod_build_hf_vsif_infopacket(stream, &stream->vsp_infopacket, false, false);
	if (stream->link->psr_feature_enabled)	{
		struct dc  *core_dc = stream->link->ctx->dc;

		if (dc_is_dmcu_initialized(core_dc)) {
			struct dmcu *dmcu = core_dc->res_pool->dmcu;

			stream->psr_version = dmcu->dmcu_version.psr_version;
			mod_build_vsc_infopacket(stream, &stream->vsc_infopacket);
		}
	}
finish:
	dc_sink_release(sink);

@@ -5821,6 +5837,7 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
	uint32_t target_vblank, last_flip_vblank;
	bool vrr_active = amdgpu_dm_vrr_active(acrtc_state);
	bool pflip_present = false;
	bool swizzle = true;
	struct {
		struct dc_surface_update surface_updates[MAX_SURFACES];
		struct dc_plane_info plane_infos[MAX_SURFACES];
@@ -5866,6 +5883,9 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,

		dc_plane = dm_new_plane_state->dc_state;

		if (dc_plane && !dc_plane->tiling_info.gfx9.swizzle)
			swizzle = false;

		bundle->surface_updates[planes_count].surface = dc_plane;
		if (new_pcrtc_state->color_mgmt_changed) {
			bundle->surface_updates[planes_count].gamma = dc_plane->gamma_correction;
@@ -6057,14 +6077,29 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
				&acrtc_state->vrr_params.adjust);
			spin_unlock_irqrestore(&pcrtc->dev->event_lock, flags);
		}

		mutex_lock(&dm->dc_lock);
		if ((acrtc_state->update_type > UPDATE_TYPE_FAST) &&
				acrtc_state->stream->link->psr_allow_active)
			amdgpu_dm_psr_disable(acrtc_state->stream);

		dc_commit_updates_for_stream(dm->dc,
						     bundle->surface_updates,
						     planes_count,
						     acrtc_state->stream,
						     &bundle->stream_update,
						     dc_state);

		if ((acrtc_state->update_type > UPDATE_TYPE_FAST) &&
						acrtc_state->stream->psr_version &&
						!acrtc_state->stream->link->psr_feature_enabled)
			amdgpu_dm_link_setup_psr(acrtc_state->stream);
		else if ((acrtc_state->update_type == UPDATE_TYPE_FAST) &&
						acrtc_state->stream->link->psr_feature_enabled &&
						!acrtc_state->stream->link->psr_allow_active &&
						swizzle) {
			amdgpu_dm_psr_enable(acrtc_state->stream);
		}

		mutex_unlock(&dm->dc_lock);
	}

@@ -6373,11 +6408,14 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state)
			crtc->hwmode = new_crtc_state->mode;
		} else if (modereset_required(new_crtc_state)) {
			DRM_DEBUG_DRIVER("Atomic commit: RESET. crtc id %d:[%p]\n", acrtc->crtc_id, acrtc);

			/* i.e. reset mode */
			if (dm_old_crtc_state->stream)
			if (dm_old_crtc_state->stream) {
				if (dm_old_crtc_state->stream->link->psr_allow_active)
					amdgpu_dm_psr_disable(dm_old_crtc_state->stream);

				remove_stream(adev, acrtc, dm_old_crtc_state->stream);
			}
		}
	} /* for_each_crtc_in_state() */

	if (dc_state) {
@@ -7752,3 +7790,92 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector,
						       freesync_capable);
}

static void amdgpu_dm_set_psr_caps(struct dc_link *link)
{
	uint8_t dpcd_data[EDP_PSR_RECEIVER_CAP_SIZE];

	if (!(link->connector_signal & SIGNAL_TYPE_EDP))
		return;
	if (link->type == dc_connection_none)
		return;
	if (dm_helpers_dp_read_dpcd(NULL, link, DP_PSR_SUPPORT,
					dpcd_data, sizeof(dpcd_data))) {
		link->psr_feature_enabled = dpcd_data[0] ? true:false;
		DRM_INFO("PSR support:%d\n", link->psr_feature_enabled);
	}
}

/*
 * amdgpu_dm_link_setup_psr() - configure psr link
 * @stream: stream state
 *
 * Return: true if success
 */
static bool amdgpu_dm_link_setup_psr(struct dc_stream_state *stream)
{
	struct dc_link *link = NULL;
	struct psr_config psr_config = {0};
	struct psr_context psr_context = {0};
	struct dc *dc = NULL;
	bool ret = false;

	if (stream == NULL)
		return false;

	link = stream->link;
	dc = link->ctx->dc;

	psr_config.psr_version = dc->res_pool->dmcu->dmcu_version.psr_version;

	if (psr_config.psr_version > 0) {
		psr_config.psr_exit_link_training_required = 0x1;
		psr_config.psr_frame_capture_indication_req = 0;
		psr_config.psr_rfb_setup_time = 0x37;
		psr_config.psr_sdp_transmit_line_num_deadline = 0x20;
		psr_config.allow_smu_optimizations = 0x0;

		ret = dc_link_setup_psr(link, stream, &psr_config, &psr_context);

	}
	DRM_DEBUG_DRIVER("PSR link: %d\n",	link->psr_feature_enabled);

	return ret;
}

/*
 * amdgpu_dm_psr_enable() - enable psr f/w
 * @stream: stream state
 *
 * Return: true if success
 */
bool amdgpu_dm_psr_enable(struct dc_stream_state *stream)
{
	struct dc_link *link = stream->link;
	struct dc_static_screen_events triggers = {0};

	DRM_DEBUG_DRIVER("Enabling psr...\n");

	triggers.cursor_update = true;
	triggers.overlay_update = true;
	triggers.surface_update = true;

	dc_stream_set_static_screen_events(link->ctx->dc,
					   &stream, 1,
					   &triggers);

	return dc_link_set_psr_allow_active(link, true, false);
}

/*
 * amdgpu_dm_psr_disable() - disable psr f/w
 * @stream:  stream state
 *
 * Return: true if success
 */
static bool amdgpu_dm_psr_disable(struct dc_stream_state *stream)
{

	DRM_DEBUG_DRIVER("Disabling psr...\n");

	return dc_link_set_psr_allow_active(stream->link, false, true);
}