Skip to content

Commit

Permalink
drm/amd/display: Implement MPO PSR SU
Browse files Browse the repository at this point in the history
[WHY]

For additional power savings, PSR SU (also referred to as PSR2) can be
enabled on eDP panels with PSR SU support.

PSR2 saves more power compared to PSR1 by allowing more opportunities
for the display hardware to be shut down. In comparison to PSR1, Shut
down can now occur in-between frames, as well as in display regions
where there is no visible update. In otherwords, it allows for some
display hw components to be enabled only for a **selectively updated**
region of the visible display. Hence PSR SU.

[HOW]

To define the SU region, support from the OS is required. OS needs to
inform driver of damaged regions that need to be flushed to the eDP
panel. Today, such support is lacking in most compositors.

Therefore, an in-between solution is to implement PSR SU for MPO and
cursor scenarios. The plane bounds can be used to define the damaged
region to be flushed to panel. This is achieved by:

* Leveraging dm_crtc_state->mpo_requested flag to identify when MPO is
  enabled.
* If MPO is enabled, only add updated plane bounds to dirty region.
  Determine plane update by either:
    * Existence of drm damaged clips attached to the plane (added by a
      damage-aware compositor)
    * Change in fb id (flip)
    * Change in plane bounds (position and dimensions)
* If cursor is enabled, the old_pos and new_pos of cursor plus cursor
  size is used as damaged regions(*).

(*) Cursor updates follow a different code path through DC. PSR SU for
cursor is already implemented in DC, and the only thing required to
enable is to set DC_PSR_VERSION_SU_1 on the eDP link. See
dcn10_dmub_update_cursor_data().

Signed-off-by: Leo Li <[email protected]>
Acked-by: Leo Li <[email protected]>
Reviewed-by: Harry Wentland <[email protected]>
Signed-off-by: Alex Deucher <[email protected]>
  • Loading branch information
leo-sunli1 authored and alexdeucher committed Jun 6, 2022
1 parent 1b0da5a commit 7cc191e
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 4 deletions.
142 changes: 140 additions & 2 deletions drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
Original file line number Diff line number Diff line change
Expand Up @@ -1265,10 +1265,20 @@ static void vblank_control_worker(struct work_struct *work)

DRM_DEBUG_KMS("Allow idle optimizations (MALL): %d\n", dm->active_vblank_irq_count == 0);

/* Control PSR based on vblank requirements from OS */
/*
* Control PSR based on vblank requirements from OS
*
* If panel supports PSR SU, there's no need to disable PSR when OS is
* submitting fast atomic commits (we infer this by whether the OS
* requests vblank events). Fast atomic commits will simply trigger a
* full-frame-update (FFU); a specific case of selective-update (SU)
* where the SU region is the full hactive*vactive region. See
* fill_dc_dirty_rects().
*/
if (vblank_work->stream && vblank_work->stream->link) {
if (vblank_work->enable) {
if (vblank_work->stream->link->psr_settings.psr_allow_active)
if (vblank_work->stream->link->psr_settings.psr_version < DC_PSR_VERSION_SU_1 &&
vblank_work->stream->link->psr_settings.psr_allow_active)
amdgpu_dm_psr_disable(vblank_work->stream);
} else if (vblank_work->stream->link->psr_settings.psr_feature_enabled &&
!vblank_work->stream->link->psr_settings.psr_allow_active &&
Expand Down Expand Up @@ -5733,6 +5743,117 @@ static int fill_dc_plane_attributes(struct amdgpu_device *adev,
return 0;
}

/**
* fill_dc_dirty_rects() - Fill DC dirty regions for PSR selective updates
*
* @plane: DRM plane containing dirty regions that need to be flushed to the eDP
* remote fb
* @old_plane_state: Old state of @plane
* @new_plane_state: New state of @plane
* @crtc_state: New state of CRTC connected to the @plane
* @flip_addrs: DC flip tracking struct, which also tracts dirty rects
*
* For PSR SU, DC informs the DMUB uController of dirty rectangle regions
* (referred to as "damage clips" in DRM nomenclature) that require updating on
* the eDP remote buffer. The responsibility of specifying the dirty regions is
* amdgpu_dm's.
*
* A damage-aware DRM client should fill the FB_DAMAGE_CLIPS property on the
* plane with regions that require flushing to the eDP remote buffer. In
* addition, certain use cases - such as cursor and multi-plane overlay (MPO) -
* implicitly provide damage clips without any client support via the plane
* bounds.
*
* Today, amdgpu_dm only supports the MPO and cursor usecase.
*
* TODO: Also enable for FB_DAMAGE_CLIPS
*/
static void fill_dc_dirty_rects(struct drm_plane *plane,
struct drm_plane_state *old_plane_state,
struct drm_plane_state *new_plane_state,
struct drm_crtc_state *crtc_state,
struct dc_flip_addrs *flip_addrs)
{
struct dm_crtc_state *dm_crtc_state = to_dm_crtc_state(crtc_state);
struct rect *dirty_rects = flip_addrs->dirty_rects;
uint32_t num_clips;
bool bb_changed;
bool fb_changed;
uint32_t i = 0;

flip_addrs->dirty_rect_count = 0;

/*
* Cursor plane has it's own dirty rect update interface. See
* dcn10_dmub_update_cursor_data and dmub_cmd_update_cursor_info_data
*/
if (plane->type == DRM_PLANE_TYPE_CURSOR)
return;

/*
* Today, we only consider MPO use-case for PSR SU. If MPO not
* requested, and there is a plane update, do FFU.
*/
if (!dm_crtc_state->mpo_requested) {
dirty_rects[0].x = 0;
dirty_rects[0].y = 0;
dirty_rects[0].width = dm_crtc_state->base.mode.crtc_hdisplay;
dirty_rects[0].height = dm_crtc_state->base.mode.crtc_vdisplay;
flip_addrs->dirty_rect_count = 1;
DRM_DEBUG_DRIVER("[PLANE:%d] PSR FFU dirty rect size (%d, %d)\n",
new_plane_state->plane->base.id,
dm_crtc_state->base.mode.crtc_hdisplay,
dm_crtc_state->base.mode.crtc_vdisplay);
return;
}

/*
* MPO is requested. Add entire plane bounding box to dirty rects if
* flipped to or damaged.
*
* If plane is moved or resized, also add old bounding box to dirty
* rects.
*/
num_clips = drm_plane_get_damage_clips_count(new_plane_state);
fb_changed = old_plane_state->fb->base.id !=
new_plane_state->fb->base.id;
bb_changed = (old_plane_state->crtc_x != new_plane_state->crtc_x ||
old_plane_state->crtc_y != new_plane_state->crtc_y ||
old_plane_state->crtc_w != new_plane_state->crtc_w ||
old_plane_state->crtc_h != new_plane_state->crtc_h);

DRM_DEBUG_DRIVER("[PLANE:%d] PSR bb_changed:%d fb_changed:%d num_clips:%d\n",
new_plane_state->plane->base.id,
bb_changed, fb_changed, num_clips);

if (num_clips || fb_changed || bb_changed) {
dirty_rects[i].x = new_plane_state->crtc_x;
dirty_rects[i].y = new_plane_state->crtc_y;
dirty_rects[i].width = new_plane_state->crtc_w;
dirty_rects[i].height = new_plane_state->crtc_h;
DRM_DEBUG_DRIVER("[PLANE:%d] PSR SU dirty rect at (%d, %d) size (%d, %d)\n",
new_plane_state->plane->base.id,
dirty_rects[i].x, dirty_rects[i].y,
dirty_rects[i].width, dirty_rects[i].height);
i += 1;
}

/* Add old plane bounding-box if plane is moved or resized */
if (bb_changed) {
dirty_rects[i].x = old_plane_state->crtc_x;
dirty_rects[i].y = old_plane_state->crtc_y;
dirty_rects[i].width = old_plane_state->crtc_w;
dirty_rects[i].height = old_plane_state->crtc_h;
DRM_DEBUG_DRIVER("[PLANE:%d] PSR SU dirty rect at (%d, %d) size (%d, %d)\n",
old_plane_state->plane->base.id,
dirty_rects[i].x, dirty_rects[i].y,
dirty_rects[i].width, dirty_rects[i].height);
i += 1;
}

flip_addrs->dirty_rect_count = i;
}

static void update_stream_scaling_settings(const struct drm_display_mode *mode,
const struct dm_connector_state *dm_state,
struct dc_stream_state *stream)
Expand Down Expand Up @@ -6681,6 +6802,7 @@ dm_crtc_duplicate_state(struct drm_crtc *crtc)
state->cm_has_degamma = cur->cm_has_degamma;
state->cm_is_degamma_srgb = cur->cm_is_degamma_srgb;
state->force_dpms_off = cur->force_dpms_off;
state->mpo_requested = cur->mpo_requested;
/* TODO Duplicate dc_stream after objects are stream object is flattened */

return &state->base;
Expand Down Expand Up @@ -9328,6 +9450,10 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
bundle->surface_updates[planes_count].plane_info =
&bundle->plane_infos[planes_count];

fill_dc_dirty_rects(plane, old_plane_state, new_plane_state,
new_crtc_state,
&bundle->flip_addrs[planes_count]);

/*
* Only allow immediate flips for fast updates that don't
* change FB pitch, DCC state, rotation or mirroing.
Expand Down Expand Up @@ -9523,6 +9649,18 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,

/* Allow PSR when skip count is 0. */
acrtc_attach->dm_irq_params.allow_psr_entry = !aconn->psr_skip_count;

/*
* If sink supports PSR SU, there is no need to rely on
* a vblank event disable request to enable PSR. PSR SU
* can be enabled immediately once OS demonstrates an
* adequate number of fast atomic commits to notify KMD
* of update events. See `vblank_control_worker()`.
*/
if (acrtc_state->stream->link->psr_settings.psr_version >= DC_PSR_VERSION_SU_1 &&
acrtc_attach->dm_irq_params.allow_psr_entry &&
!acrtc_state->stream->link->psr_settings.psr_allow_active)
amdgpu_dm_psr_enable(acrtc_state->stream);
} else {
acrtc_attach->dm_irq_params.allow_psr_entry = false;
}
Expand Down
6 changes: 4 additions & 2 deletions drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_psr.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,12 @@ void amdgpu_dm_set_psr_caps(struct dc_link *link)
link->psr_settings.psr_feature_enabled = true;
}

DRM_INFO("PSR support %d, DC PSR ver %d, sink PSR ver %d\n",
DRM_INFO("PSR support %d, DC PSR ver %d, sink PSR ver %d DPCD caps 0x%x su_y_granularity %d\n",
link->psr_settings.psr_feature_enabled,
link->psr_settings.psr_version,
link->dpcd_caps.psr_info.psr_version);
link->dpcd_caps.psr_info.psr_version,
link->dpcd_caps.psr_info.psr_dpcd_caps.raw,
link->dpcd_caps.psr_info.psr2_su_y_granularity_cap);

}

Expand Down

0 comments on commit 7cc191e

Please sign in to comment.