drm/amd/display: add functionality to grab DPRX CRC entries.

[Why]
We need to compare DPRX CRCs with framebuffer CRCs for digital bypass mode.

[How]
Hook into DRM to grab DP receiver CRCs through drm_dp_start_crc.

Signed-off-by: Dingchen Zhang <dingchen.zhang@amd.com>
Reviewed-by: Harry Wentland <Harry.Wentland@amd.com>
Acked-by: Leo Li <sunpeng.li@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
Dingchen Zhang 2019-05-15 17:15:05 -04:00 committed by Alex Deucher
parent 2425347697
commit 14b2584636
4 changed files with 136 additions and 44 deletions

View File

@ -3665,7 +3665,7 @@ dm_crtc_duplicate_state(struct drm_crtc *crtc)
state->abm_level = cur->abm_level; state->abm_level = cur->abm_level;
state->vrr_supported = cur->vrr_supported; state->vrr_supported = cur->vrr_supported;
state->freesync_config = cur->freesync_config; state->freesync_config = cur->freesync_config;
state->crc_enabled = cur->crc_enabled; state->crc_src = cur->crc_src;
state->cm_has_degamma = cur->cm_has_degamma; state->cm_has_degamma = cur->cm_has_degamma;
state->cm_is_degamma_srgb = cur->cm_is_degamma_srgb; state->cm_is_degamma_srgb = cur->cm_is_degamma_srgb;
@ -5975,6 +5975,7 @@ static void amdgpu_dm_enable_crtc_interrupts(struct drm_device *dev,
struct drm_crtc *crtc; struct drm_crtc *crtc;
struct drm_crtc_state *old_crtc_state, *new_crtc_state; struct drm_crtc_state *old_crtc_state, *new_crtc_state;
int i; int i;
enum amdgpu_dm_pipe_crc_source source;
for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state,
new_crtc_state, i) { new_crtc_state, i) {
@ -6000,9 +6001,13 @@ static void amdgpu_dm_enable_crtc_interrupts(struct drm_device *dev,
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
/* The stream has changed so CRC capture needs to re-enabled. */ /* The stream has changed so CRC capture needs to re-enabled. */
if (dm_new_crtc_state->crc_enabled) { source = dm_new_crtc_state->crc_src;
dm_new_crtc_state->crc_enabled = false; if (amdgpu_dm_is_valid_crc_source(source)) {
amdgpu_dm_crtc_set_crc_source(crtc, "auto"); dm_new_crtc_state->crc_src = AMDGPU_DM_PIPE_CRC_SOURCE_NONE;
if (source == AMDGPU_DM_PIPE_CRC_SOURCE_CRTC)
amdgpu_dm_crtc_set_crc_source(crtc, "crtc");
else if (source == AMDGPU_DM_PIPE_CRC_SOURCE_DPRX)
amdgpu_dm_crtc_set_crc_source(crtc, "dprx");
} }
#endif #endif
} }
@ -6058,7 +6063,7 @@ static int amdgpu_dm_atomic_commit(struct drm_device *dev,
* Drop the extra vblank reference added by CRC * Drop the extra vblank reference added by CRC
* capture if applicable. * capture if applicable.
*/ */
if (dm_new_crtc_state->crc_enabled) if (amdgpu_dm_is_valid_crc_source(dm_new_crtc_state->crc_src))
drm_crtc_vblank_put(crtc); drm_crtc_vblank_put(crtc);
/* /*
@ -6066,7 +6071,7 @@ static int amdgpu_dm_atomic_commit(struct drm_device *dev,
* still a stream for the CRTC. * still a stream for the CRTC.
*/ */
if (!dm_new_crtc_state->stream) if (!dm_new_crtc_state->stream)
dm_new_crtc_state->crc_enabled = false; dm_new_crtc_state->crc_src = AMDGPU_DM_PIPE_CRC_SOURCE_NONE;
manage_dm_interrupts(adev, acrtc, false); manage_dm_interrupts(adev, acrtc, false);
} }

View File

@ -50,6 +50,7 @@
#include "irq_types.h" #include "irq_types.h"
#include "signal_types.h" #include "signal_types.h"
#include "amdgpu_dm_crc.h"
/* Forward declarations */ /* Forward declarations */
struct amdgpu_device; struct amdgpu_device;
@ -313,7 +314,7 @@ struct dm_crtc_state {
bool interrupts_enabled; bool interrupts_enabled;
int crc_skip_count; int crc_skip_count;
bool crc_enabled; enum amdgpu_dm_pipe_crc_source crc_src;
bool freesync_timing_changed; bool freesync_timing_changed;
bool freesync_vrr_info_changed; bool freesync_vrr_info_changed;
@ -380,19 +381,6 @@ void dm_restore_drm_connector_state(struct drm_device *dev,
void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, void amdgpu_dm_update_freesync_caps(struct drm_connector *connector,
struct edid *edid); struct edid *edid);
/* amdgpu_dm_crc.c */
#ifdef CONFIG_DEBUG_FS
int amdgpu_dm_crtc_set_crc_source(struct drm_crtc *crtc, const char *src_name);
int amdgpu_dm_crtc_verify_crc_source(struct drm_crtc *crtc,
const char *src_name,
size_t *values_cnt);
void amdgpu_dm_crtc_handle_crc_irq(struct drm_crtc *crtc);
#else
#define amdgpu_dm_crtc_set_crc_source NULL
#define amdgpu_dm_crtc_verify_crc_source NULL
#define amdgpu_dm_crtc_handle_crc_irq(x)
#endif
#define MAX_COLOR_LUT_ENTRIES 4096 #define MAX_COLOR_LUT_ENTRIES 4096
/* Legacy gamm LUT users such as X doesn't like large LUT sizes */ /* Legacy gamm LUT users such as X doesn't like large LUT sizes */
#define MAX_COLOR_LEGACY_LUT_ENTRIES 256 #define MAX_COLOR_LEGACY_LUT_ENTRIES 256

View File

@ -30,19 +30,14 @@
#include "amdgpu_dm.h" #include "amdgpu_dm.h"
#include "dc.h" #include "dc.h"
enum amdgpu_dm_pipe_crc_source {
AMDGPU_DM_PIPE_CRC_SOURCE_NONE = 0,
AMDGPU_DM_PIPE_CRC_SOURCE_AUTO,
AMDGPU_DM_PIPE_CRC_SOURCE_MAX,
AMDGPU_DM_PIPE_CRC_SOURCE_INVALID = -1,
};
static enum amdgpu_dm_pipe_crc_source dm_parse_crc_source(const char *source) static enum amdgpu_dm_pipe_crc_source dm_parse_crc_source(const char *source)
{ {
if (!source || !strcmp(source, "none")) if (!source || !strcmp(source, "none"))
return AMDGPU_DM_PIPE_CRC_SOURCE_NONE; return AMDGPU_DM_PIPE_CRC_SOURCE_NONE;
if (!strcmp(source, "auto")) if (!strcmp(source, "auto") || !strcmp(source, "crtc"))
return AMDGPU_DM_PIPE_CRC_SOURCE_AUTO; return AMDGPU_DM_PIPE_CRC_SOURCE_CRTC;
if (!strcmp(source, "dprx"))
return AMDGPU_DM_PIPE_CRC_SOURCE_DPRX;
return AMDGPU_DM_PIPE_CRC_SOURCE_INVALID; return AMDGPU_DM_PIPE_CRC_SOURCE_INVALID;
} }
@ -68,7 +63,10 @@ int amdgpu_dm_crtc_set_crc_source(struct drm_crtc *crtc, const char *src_name)
struct amdgpu_device *adev = crtc->dev->dev_private; struct amdgpu_device *adev = crtc->dev->dev_private;
struct dm_crtc_state *crtc_state = to_dm_crtc_state(crtc->state); struct dm_crtc_state *crtc_state = to_dm_crtc_state(crtc->state);
struct dc_stream_state *stream_state = crtc_state->stream; struct dc_stream_state *stream_state = crtc_state->stream;
bool enable; struct amdgpu_dm_connector *aconn;
struct drm_dp_aux *aux = NULL;
bool enable = false;
bool enabled = false;
enum amdgpu_dm_pipe_crc_source source = dm_parse_crc_source(src_name); enum amdgpu_dm_pipe_crc_source source = dm_parse_crc_source(src_name);
@ -83,14 +81,43 @@ int amdgpu_dm_crtc_set_crc_source(struct drm_crtc *crtc, const char *src_name)
return -EINVAL; return -EINVAL;
} }
enable = (source == AMDGPU_DM_PIPE_CRC_SOURCE_AUTO); enable = amdgpu_dm_is_valid_crc_source(source);
mutex_lock(&adev->dm.dc_lock); mutex_lock(&adev->dm.dc_lock);
/*
* USER REQ SRC | CURRENT SRC | BEHAVIOR
* -----------------------------
* None | None | Do nothing
* None | CRTC | Disable CRTC CRC
* None | DPRX | Disable DPRX CRC, need 'aux'
* CRTC | XXXX | Enable CRTC CRC, configure DC strm
* DPRX | XXXX | Enable DPRX CRC, need 'aux'
*/
if (source == AMDGPU_DM_PIPE_CRC_SOURCE_DPRX ||
(source == AMDGPU_DM_PIPE_CRC_SOURCE_NONE &&
crtc_state->crc_src == AMDGPU_DM_PIPE_CRC_SOURCE_DPRX)) {
aconn = stream_state->link->priv;
if (!aconn) {
DRM_DEBUG_DRIVER("No amd connector matching CRTC-%d\n", crtc->index);
mutex_unlock(&adev->dm.dc_lock);
return -EINVAL;
}
aux = &aconn->dm_dp_aux.aux;
if (!aux) {
DRM_DEBUG_DRIVER("No dp aux for amd connector\n");
mutex_unlock(&adev->dm.dc_lock);
return -EINVAL;
}
} else if (source == AMDGPU_DM_PIPE_CRC_SOURCE_CRTC) {
if (!dc_stream_configure_crc(stream_state->ctx->dc, stream_state, if (!dc_stream_configure_crc(stream_state->ctx->dc, stream_state,
enable, enable)) { enable, enable)) {
mutex_unlock(&adev->dm.dc_lock); mutex_unlock(&adev->dm.dc_lock);
return -EINVAL; return -EINVAL;
} }
}
/* When enabling CRC, we should also disable dithering. */ /* When enabling CRC, we should also disable dithering. */
dc_stream_set_dither_option(stream_state, dc_stream_set_dither_option(stream_state,
@ -103,12 +130,26 @@ int amdgpu_dm_crtc_set_crc_source(struct drm_crtc *crtc, const char *src_name)
* Reading the CRC requires the vblank interrupt handler to be * Reading the CRC requires the vblank interrupt handler to be
* enabled. Keep a reference until CRC capture stops. * enabled. Keep a reference until CRC capture stops.
*/ */
if (!crtc_state->crc_enabled && enable) enabled = amdgpu_dm_is_valid_crc_source(crtc_state->crc_src);
if (!enabled && enable) {
drm_crtc_vblank_get(crtc); drm_crtc_vblank_get(crtc);
else if (crtc_state->crc_enabled && !enable) if (source == AMDGPU_DM_PIPE_CRC_SOURCE_DPRX) {
if (drm_dp_start_crc(aux, crtc)) {
DRM_DEBUG_DRIVER("dp start crc failed\n");
return -EINVAL;
}
}
} else if (enabled && !enable) {
drm_crtc_vblank_put(crtc); drm_crtc_vblank_put(crtc);
if (crtc_state->crc_src == AMDGPU_DM_PIPE_CRC_SOURCE_DPRX) {
if (drm_dp_stop_crc(aux)) {
DRM_DEBUG_DRIVER("dp stop crc failed\n");
return -EINVAL;
}
}
}
crtc_state->crc_enabled = enable; crtc_state->crc_src = source;
/* Reset crc_skipped on dm state */ /* Reset crc_skipped on dm state */
crtc_state->crc_skip_count = 0; crtc_state->crc_skip_count = 0;
@ -135,7 +176,7 @@ void amdgpu_dm_crtc_handle_crc_irq(struct drm_crtc *crtc)
stream_state = crtc_state->stream; stream_state = crtc_state->stream;
/* Early return if CRC capture is not enabled. */ /* Early return if CRC capture is not enabled. */
if (!crtc_state->crc_enabled) if (!amdgpu_dm_is_valid_crc_source(crtc_state->crc_src))
return; return;
/* /*
@ -149,10 +190,12 @@ void amdgpu_dm_crtc_handle_crc_irq(struct drm_crtc *crtc)
return; return;
} }
if (crtc_state->crc_src == AMDGPU_DM_PIPE_CRC_SOURCE_CRTC) {
if (!dc_stream_get_crc(stream_state->ctx->dc, stream_state, if (!dc_stream_get_crc(stream_state->ctx->dc, stream_state,
&crcs[0], &crcs[1], &crcs[2])) &crcs[0], &crcs[1], &crcs[2]))
return; return;
drm_crtc_add_crc_entry(crtc, true, drm_crtc_add_crc_entry(crtc, true,
drm_crtc_accurate_vblank_count(crtc), crcs); drm_crtc_accurate_vblank_count(crtc), crcs);
}
} }

View File

@ -0,0 +1,56 @@
/*
* Copyright 2019 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: AMD
*
*/
#ifndef AMD_DAL_DEV_AMDGPU_DM_AMDGPU_DM_CRC_H_
#define AMD_DAL_DEV_AMDGPU_DM_AMDGPU_DM_CRC_H_
enum amdgpu_dm_pipe_crc_source {
AMDGPU_DM_PIPE_CRC_SOURCE_NONE = 0,
AMDGPU_DM_PIPE_CRC_SOURCE_CRTC,
AMDGPU_DM_PIPE_CRC_SOURCE_DPRX,
AMDGPU_DM_PIPE_CRC_SOURCE_MAX,
AMDGPU_DM_PIPE_CRC_SOURCE_INVALID = -1,
};
static inline bool amdgpu_dm_is_valid_crc_source(enum amdgpu_dm_pipe_crc_source source)
{
return (source == AMDGPU_DM_PIPE_CRC_SOURCE_CRTC) ||
(source == AMDGPU_DM_PIPE_CRC_SOURCE_DPRX);
}
/* amdgpu_dm_crc.c */
#ifdef CONFIG_DEBUG_FS
int amdgpu_dm_crtc_set_crc_source(struct drm_crtc *crtc, const char *src_name);
int amdgpu_dm_crtc_verify_crc_source(struct drm_crtc *crtc,
const char *src_name,
size_t *values_cnt);
void amdgpu_dm_crtc_handle_crc_irq(struct drm_crtc *crtc);
#else
#define amdgpu_dm_crtc_set_crc_source NULL
#define amdgpu_dm_crtc_verify_crc_source NULL
#define amdgpu_dm_crtc_handle_crc_irq(x)
#endif
#endif /* AMD_DAL_DEV_AMDGPU_DM_AMDGPU_DM_CRC_H_ */