mirror of
https://github.com/torvalds/linux.git
synced 2026-05-30 01:53:29 +02:00
drm/amd/display: Add CRC 32-bit mode support for DCN3.6+
[Why] DCN 3.6+ hardware supports CRC-32 polynomial in addition to the legacy CRC-16. Enable 32-bit CRC values per color component for improvement of precision in display validation. [How] When userspace sets crc_poly_mode (0=CRC-16, 1=CRC-32) via the debugfs interface, the value is stored in dm_irq_params.crc_poly_mode. When CRC source configuration triggers amdgpu_dm_crtc_configure_crc_source(), crc_poly_mode is retrieved from dm_irq_params and passed to dc_stream_configure_crc(). In the DC layer, dc_stream_configure_crc() sets crc_poly_mode into the crc_params structure and passes it to optc35_configure_crc(). If the hardware supports the OTG_CRC_POLY_SEL register, the register is programmed to select CRC-16 or CRC-32 polynomial. When reading CRC values, optc35_get_crc() checks whether CRC32 register masks are available. If present, it reads 32-bit CRC values from OTG_CRC0/1_DATA_R32/G32/B32 registers; otherwise, it falls back to reading 16-bit CRC values from legacy OTG_CRC0/1_DATA_RG/B registers. Reviewed-by: ChiaHsuan Chung <chiahsuan.chung@amd.com> Signed-off-by: Chenyu Chen <chen-yu.chen@amd.com> Signed-off-by: Wayne Lin <wayne.lin@amd.com> Tested-by: Dan Wheeler <daniel.wheeler@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
parent
0b39cb143d
commit
1a77ecec8b
|
|
@ -506,6 +506,7 @@ int amdgpu_dm_crtc_configure_crc_source(struct drm_crtc *crtc,
|
|||
struct amdgpu_dm_connector *aconnector = NULL;
|
||||
bool enable = amdgpu_dm_is_valid_crc_source(source);
|
||||
int ret = 0;
|
||||
enum crc_poly_mode crc_poly_mode = CRC_POLY_MODE_16;
|
||||
|
||||
/* Configuration will be deferred to stream enable. */
|
||||
if (!stream_state)
|
||||
|
|
@ -528,10 +529,18 @@ int amdgpu_dm_crtc_configure_crc_source(struct drm_crtc *crtc,
|
|||
amdgpu_dm_replay_disable(stream_state);
|
||||
}
|
||||
|
||||
/* CRC polynomial selection only support for DCN3.6+ except DCN4.0.1 */
|
||||
if ((amdgpu_ip_version(adev, DCE_HWIP, 0) >= IP_VERSION(3, 6, 0)) &&
|
||||
(amdgpu_ip_version(adev, DCE_HWIP, 0) != IP_VERSION(4, 0, 1))) {
|
||||
struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc);
|
||||
|
||||
crc_poly_mode = acrtc->dm_irq_params.crc_poly_mode;
|
||||
}
|
||||
|
||||
/* Enable or disable CRTC CRC generation */
|
||||
if (dm_is_crc_source_crtc(source) || source == AMDGPU_DM_PIPE_CRC_SOURCE_NONE) {
|
||||
if (!dc_stream_configure_crc(stream_state->ctx->dc,
|
||||
stream_state, NULL, enable, enable, 0, true)) {
|
||||
stream_state, NULL, enable, enable, 0, true, crc_poly_mode)) {
|
||||
ret = -EINVAL;
|
||||
goto unlock;
|
||||
}
|
||||
|
|
@ -877,7 +886,7 @@ void amdgpu_dm_crtc_handle_crc_window_irq(struct drm_crtc *crtc)
|
|||
else if (adev->dm.secure_display_ctx.op_mode == DISPLAY_CRC_MODE)
|
||||
/* update ROI via dm*/
|
||||
dc_stream_configure_crc(stream_state->ctx->dc, stream_state,
|
||||
&crc_window, true, true, i, false);
|
||||
&crc_window, true, true, i, false, (enum crc_poly_mode)acrtc->dm_irq_params.crc_poly_mode);
|
||||
|
||||
reset_crc_frame_count[i] = true;
|
||||
|
||||
|
|
@ -901,7 +910,7 @@ void amdgpu_dm_crtc_handle_crc_window_irq(struct drm_crtc *crtc)
|
|||
else if (adev->dm.secure_display_ctx.op_mode == DISPLAY_CRC_MODE)
|
||||
/* Avoid ROI window get changed, keep overwriting. */
|
||||
dc_stream_configure_crc(stream_state->ctx->dc, stream_state,
|
||||
&crc_window, true, true, i, false);
|
||||
&crc_window, true, true, i, false, (enum crc_poly_mode)acrtc->dm_irq_params.crc_poly_mode);
|
||||
|
||||
/* crc ready for psp to read out */
|
||||
crtc_ctx->crc_info.crc[i].crc_ready = true;
|
||||
|
|
|
|||
|
|
@ -3839,6 +3839,50 @@ static int crc_win_update_get(void *data, u64 *val)
|
|||
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(crc_win_update_fops, crc_win_update_get,
|
||||
crc_win_update_set, "%llu\n");
|
||||
|
||||
/*
|
||||
* Trigger to set crc polynomial mode
|
||||
* 0: 16-bit CRC, 1: 32-bit CRC
|
||||
* only accepts 0 or 1 for supported hwip versions
|
||||
*/
|
||||
static int crc_poly_mode_set(void *data, u64 val)
|
||||
{
|
||||
struct drm_crtc *crtc = data;
|
||||
struct amdgpu_crtc *acrtc;
|
||||
struct amdgpu_device *adev = drm_to_adev(crtc->dev);
|
||||
|
||||
if ((amdgpu_ip_version(adev, DCE_HWIP, 0) >= IP_VERSION(3, 6, 0)) &&
|
||||
(amdgpu_ip_version(adev, DCE_HWIP, 0) != IP_VERSION(4, 0, 1)) &&
|
||||
(val < 2)) {
|
||||
acrtc = to_amdgpu_crtc(crtc);
|
||||
mutex_lock(&adev->dm.dc_lock);
|
||||
spin_lock_irq(&adev_to_drm(adev)->event_lock);
|
||||
acrtc->dm_irq_params.crc_poly_mode = val;
|
||||
spin_unlock_irq(&adev_to_drm(adev)->event_lock);
|
||||
mutex_unlock(&adev->dm.dc_lock);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get crc polynomial mode (0: 16-bit CRC, 1: 32-bit CRC)
|
||||
*/
|
||||
static int crc_poly_mode_get(void *data, u64 *val)
|
||||
{
|
||||
struct drm_crtc *crtc = data;
|
||||
struct drm_device *drm_dev = crtc->dev;
|
||||
struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc);
|
||||
|
||||
spin_lock_irq(&drm_dev->event_lock);
|
||||
*val = acrtc->dm_irq_params.crc_poly_mode;
|
||||
spin_unlock_irq(&drm_dev->event_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(crc_poly_mode_fops, crc_poly_mode_get,
|
||||
crc_poly_mode_set, "%llu\n");
|
||||
#endif
|
||||
void crtc_debugfs_init(struct drm_crtc *crtc)
|
||||
{
|
||||
|
|
@ -3858,6 +3902,8 @@ void crtc_debugfs_init(struct drm_crtc *crtc)
|
|||
&crc_win_y_end_fops);
|
||||
debugfs_create_file_unsafe("crc_win_update", 0644, dir, crtc,
|
||||
&crc_win_update_fops);
|
||||
debugfs_create_file_unsafe("crc_poly_mode", 0644, dir, crtc,
|
||||
&crc_poly_mode_fops);
|
||||
dput(dir);
|
||||
#endif
|
||||
debugfs_create_file("amdgpu_current_bpc", 0644, crtc->debugfs_entry,
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ struct dm_irq_params {
|
|||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
enum amdgpu_dm_pipe_crc_source crc_src;
|
||||
int crc_poly_mode; /* enum crc_poly_mode from timing_generator.h */
|
||||
#ifdef CONFIG_DRM_AMD_SECURE_DISPLAY
|
||||
struct crc_window_param window_param[MAX_CRC_WINDOW_NUM];
|
||||
/* At least one CRC window is activated or not*/
|
||||
|
|
|
|||
|
|
@ -701,6 +701,7 @@ dc_stream_forward_multiple_crc_window(struct dc_stream_state *stream,
|
|||
* once.
|
||||
* @idx: Capture CRC on which CRC engine instance
|
||||
* @reset: Reset CRC engine before the configuration
|
||||
* @crc_poly_mode: CRC polynomial mode
|
||||
*
|
||||
* By default, the entire frame is used to calculate the CRC.
|
||||
*
|
||||
|
|
@ -709,7 +710,7 @@ dc_stream_forward_multiple_crc_window(struct dc_stream_state *stream,
|
|||
*/
|
||||
bool dc_stream_configure_crc(struct dc *dc, struct dc_stream_state *stream,
|
||||
struct crc_params *crc_window, bool enable, bool continuous,
|
||||
uint8_t idx, bool reset)
|
||||
uint8_t idx, bool reset, enum crc_poly_mode crc_poly_mode)
|
||||
{
|
||||
struct pipe_ctx *pipe;
|
||||
struct crc_params param;
|
||||
|
|
@ -733,6 +734,7 @@ bool dc_stream_configure_crc(struct dc *dc, struct dc_stream_state *stream,
|
|||
param.windowb_y_start = 0;
|
||||
param.windowb_x_end = pipe->stream->timing.h_addressable;
|
||||
param.windowb_y_end = pipe->stream->timing.v_addressable;
|
||||
param.crc_poly_mode = crc_poly_mode;
|
||||
|
||||
if (crc_window) {
|
||||
param.windowa_x_start = crc_window->windowa_x_start;
|
||||
|
|
|
|||
|
|
@ -584,7 +584,8 @@ bool dc_stream_configure_crc(struct dc *dc,
|
|||
bool enable,
|
||||
bool continuous,
|
||||
uint8_t idx,
|
||||
bool reset);
|
||||
bool reset,
|
||||
enum crc_poly_mode crc_poly_mode);
|
||||
|
||||
bool dc_stream_get_crc(struct dc *dc,
|
||||
struct dc_stream_state *stream,
|
||||
|
|
|
|||
|
|
@ -122,6 +122,12 @@ enum timing_synchronization_type {
|
|||
VBLANK_SYNCHRONIZABLE
|
||||
};
|
||||
|
||||
enum crc_poly_mode {
|
||||
CRC_POLY_MODE_16,
|
||||
CRC_POLY_MODE_32,
|
||||
CRC_POLY_MODE_MAX,
|
||||
};
|
||||
|
||||
struct crc_params {
|
||||
/* Regions used to calculate CRC*/
|
||||
uint16_t windowa_x_start;
|
||||
|
|
@ -144,6 +150,7 @@ struct crc_params {
|
|||
|
||||
uint8_t crc_eng_inst;
|
||||
bool reset;
|
||||
enum crc_poly_mode crc_poly_mode;
|
||||
};
|
||||
|
||||
struct dcn_otg_state {
|
||||
|
|
|
|||
|
|
@ -244,7 +244,13 @@
|
|||
uint32_t OTG_TRIGB_MANUAL_TRIG; \
|
||||
uint32_t OTG_UPDATE_LOCK; \
|
||||
uint32_t OTG_V_TOTAL_INT_STATUS; \
|
||||
uint32_t OTG_VSYNC_NOM_INT_STATUS
|
||||
uint32_t OTG_VSYNC_NOM_INT_STATUS; \
|
||||
uint32_t OTG_CRC0_DATA_R32; \
|
||||
uint32_t OTG_CRC0_DATA_G32; \
|
||||
uint32_t OTG_CRC0_DATA_B32; \
|
||||
uint32_t OTG_CRC1_DATA_R32; \
|
||||
uint32_t OTG_CRC1_DATA_G32; \
|
||||
uint32_t OTG_CRC1_DATA_B32
|
||||
|
||||
|
||||
struct dcn_optc_registers {
|
||||
|
|
@ -657,6 +663,15 @@ struct dcn_optc_registers {
|
|||
type OTG_V_COUNT_STOP;\
|
||||
type OTG_V_COUNT_STOP_TIMER;
|
||||
|
||||
#define TG_REG_FIELD_LIST_DCN3_6(type) \
|
||||
type OTG_CRC_POLY_SEL; \
|
||||
type CRC0_R_CR32; \
|
||||
type CRC0_G_Y32; \
|
||||
type CRC0_B_CB32; \
|
||||
type CRC1_R_CR32; \
|
||||
type CRC1_G_Y32; \
|
||||
type CRC1_B_CB32;
|
||||
|
||||
#define TG_REG_FIELD_LIST_DCN401(type) \
|
||||
type OPTC_SEGMENT_WIDTH_LAST;\
|
||||
type OTG_PSTATE_KEEPOUT_START;\
|
||||
|
|
@ -670,6 +685,7 @@ struct dcn_optc_shift {
|
|||
TG_REG_FIELD_LIST_DCN2_0(uint8_t)
|
||||
TG_REG_FIELD_LIST_DCN3_2(uint8_t)
|
||||
TG_REG_FIELD_LIST_DCN3_5(uint8_t)
|
||||
TG_REG_FIELD_LIST_DCN3_6(uint8_t)
|
||||
TG_REG_FIELD_LIST_DCN401(uint8_t)
|
||||
};
|
||||
|
||||
|
|
@ -678,6 +694,7 @@ struct dcn_optc_mask {
|
|||
TG_REG_FIELD_LIST_DCN2_0(uint32_t)
|
||||
TG_REG_FIELD_LIST_DCN3_2(uint32_t)
|
||||
TG_REG_FIELD_LIST_DCN3_5(uint32_t)
|
||||
TG_REG_FIELD_LIST_DCN3_6(uint32_t)
|
||||
TG_REG_FIELD_LIST_DCN401(uint32_t)
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -180,6 +180,96 @@ static void optc35_phantom_crtc_post_enable(struct timing_generator *optc)
|
|||
REG_WAIT(OTG_CLOCK_CONTROL, OTG_BUSY, 0, 1, 100000);
|
||||
}
|
||||
|
||||
/**
|
||||
* optc35_get_crc - Capture CRC result per component
|
||||
*
|
||||
* @optc: timing_generator instance.
|
||||
* @idx: index of crc engine to get CRC from
|
||||
* @r_cr: primary CRC signature for red data.
|
||||
* @g_y: primary CRC signature for green data.
|
||||
* @b_cb: primary CRC signature for blue data.
|
||||
*
|
||||
* This function reads the CRC signature from the OPTC registers. Notice that
|
||||
* we have three registers to keep the CRC result per color component (RGB).
|
||||
*
|
||||
* For different DCN versions:
|
||||
* - If CRC32 registers (OTG_CRC0_DATA_R32/G32/B32) are available, read from
|
||||
* 32-bit CRC registers. DCN 3.6+ supports both CRC-32 and CRC-16 polynomials
|
||||
* selectable via OTG_CRC_POLY_SEL.
|
||||
* - Otherwise, read from legacy 16-bit CRC registers (OTG_CRC0_DATA_RG/B)
|
||||
* which only support CRC-16 polynomial.
|
||||
*
|
||||
* Returns:
|
||||
* If CRC is disabled, return false; otherwise, return true, and the CRC
|
||||
* results in the parameters.
|
||||
*/
|
||||
static bool optc35_get_crc(struct timing_generator *optc, uint8_t idx,
|
||||
uint32_t *r_cr, uint32_t *g_y, uint32_t *b_cb)
|
||||
{
|
||||
uint32_t field = 0;
|
||||
struct optc *optc1 = DCN10TG_FROM_TG(optc);
|
||||
|
||||
REG_GET(OTG_CRC_CNTL, OTG_CRC_EN, &field);
|
||||
|
||||
/* Early return if CRC is not enabled for this CRTC */
|
||||
if (!field)
|
||||
return false;
|
||||
|
||||
if (optc1->tg_mask->CRC0_R_CR32 != 0 && optc1->tg_mask->CRC1_R_CR32 != 0 &&
|
||||
optc1->tg_mask->CRC0_G_Y32 != 0 && optc1->tg_mask->CRC1_G_Y32 != 0 &&
|
||||
optc1->tg_mask->CRC0_B_CB32 != 0 && optc1->tg_mask->CRC1_B_CB32 != 0) {
|
||||
switch (idx) {
|
||||
case 0:
|
||||
/* OTG_CRC0_DATA_R32/G32/B32 has the CRC32 results */
|
||||
REG_GET(OTG_CRC0_DATA_R32,
|
||||
CRC0_R_CR32, r_cr);
|
||||
REG_GET(OTG_CRC0_DATA_G32,
|
||||
CRC0_G_Y32, g_y);
|
||||
REG_GET(OTG_CRC0_DATA_B32,
|
||||
CRC0_B_CB32, b_cb);
|
||||
break;
|
||||
case 1:
|
||||
/* OTG_CRC1_DATA_R32/G32/B32 has the CRC32 results */
|
||||
REG_GET(OTG_CRC1_DATA_R32,
|
||||
CRC1_R_CR32, r_cr);
|
||||
REG_GET(OTG_CRC1_DATA_G32,
|
||||
CRC1_G_Y32, g_y);
|
||||
REG_GET(OTG_CRC1_DATA_B32,
|
||||
CRC1_B_CB32, b_cb);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
switch (idx) {
|
||||
case 0:
|
||||
/* OTG_CRC0_DATA_RG has the CRC16 results for the red and green component */
|
||||
REG_GET_2(OTG_CRC0_DATA_RG,
|
||||
CRC0_R_CR, r_cr,
|
||||
CRC0_G_Y, g_y);
|
||||
|
||||
/* OTG_CRC0_DATA_B has the CRC16 results for the blue component */
|
||||
REG_GET(OTG_CRC0_DATA_B,
|
||||
CRC0_B_CB, b_cb);
|
||||
break;
|
||||
case 1:
|
||||
/* OTG_CRC1_DATA_RG has the CRC16 results for the red and green component */
|
||||
REG_GET_2(OTG_CRC1_DATA_RG,
|
||||
CRC1_R_CR, r_cr,
|
||||
CRC1_G_Y, g_y);
|
||||
|
||||
/* OTG_CRC1_DATA_B has the CRC16 results for the blue component */
|
||||
REG_GET(OTG_CRC1_DATA_B,
|
||||
CRC1_B_CB, b_cb);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool optc35_configure_crc(struct timing_generator *optc,
|
||||
const struct crc_params *params)
|
||||
{
|
||||
|
|
@ -266,6 +356,10 @@ static bool optc35_configure_crc(struct timing_generator *optc,
|
|||
default:
|
||||
return false;
|
||||
}
|
||||
if (optc1->tg_mask->OTG_CRC_POLY_SEL != 0) {
|
||||
REG_UPDATE(OTG_CRC_CNTL,
|
||||
OTG_CRC_POLY_SEL, params->crc_poly_mode);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -488,7 +582,7 @@ static const struct timing_generator_funcs dcn35_tg_funcs = {
|
|||
.is_optc_underflow_occurred = optc1_is_optc_underflow_occurred,
|
||||
.clear_optc_underflow = optc1_clear_optc_underflow,
|
||||
.setup_global_swap_lock = NULL,
|
||||
.get_crc = optc1_get_crc,
|
||||
.get_crc = optc35_get_crc,
|
||||
.configure_crc = optc35_configure_crc,
|
||||
.set_dsc_config = optc3_set_dsc_config,
|
||||
.get_dsc_status = optc2_get_dsc_status,
|
||||
|
|
|
|||
|
|
@ -74,6 +74,16 @@
|
|||
SF(OTG0_OTG_PIPE_UPDATE_STATUS, OTG_VUPDATE_KEEPOUT_STATUS, mask_sh),\
|
||||
SF(OTG0_INTERRUPT_DEST, OTG0_IHC_OTG_VERTICAL_INTERRUPT2_DEST, mask_sh)
|
||||
|
||||
#define OPTC_COMMON_MASK_SH_LIST_DCN3_6(mask_sh)\
|
||||
OPTC_COMMON_MASK_SH_LIST_DCN3_5(mask_sh),\
|
||||
SF(OTG0_OTG_CRC_CNTL, OTG_CRC_POLY_SEL, mask_sh),\
|
||||
SF(OTG_CRC320_OTG_CRC0_DATA_R32, CRC0_R_CR32, mask_sh),\
|
||||
SF(OTG_CRC320_OTG_CRC0_DATA_G32, CRC0_G_Y32, mask_sh),\
|
||||
SF(OTG_CRC320_OTG_CRC0_DATA_B32, CRC0_B_CB32, mask_sh),\
|
||||
SF(OTG_CRC320_OTG_CRC1_DATA_R32, CRC1_R_CR32, mask_sh),\
|
||||
SF(OTG_CRC320_OTG_CRC1_DATA_G32, CRC1_G_Y32, mask_sh),\
|
||||
SF(OTG_CRC320_OTG_CRC1_DATA_B32, CRC1_B_CB32, mask_sh)
|
||||
|
||||
void dcn35_timing_generator_init(struct optc *optc1);
|
||||
|
||||
void dcn35_timing_generator_set_fgcg(struct optc *optc1, bool enable);
|
||||
|
|
|
|||
|
|
@ -460,16 +460,22 @@ static const struct dcn30_mpc_mask mpc_mask = {
|
|||
};
|
||||
|
||||
#define optc_regs_init(id)\
|
||||
OPTC_COMMON_REG_LIST_DCN3_5_RI(id)
|
||||
OPTC_COMMON_REG_LIST_DCN3_5_RI(id),\
|
||||
SRI_ARR(OTG_CRC0_DATA_R32, OTG_CRC32, id),\
|
||||
SRI_ARR(OTG_CRC0_DATA_G32, OTG_CRC32, id),\
|
||||
SRI_ARR(OTG_CRC0_DATA_B32, OTG_CRC32, id),\
|
||||
SRI_ARR(OTG_CRC1_DATA_R32, OTG_CRC32, id),\
|
||||
SRI_ARR(OTG_CRC1_DATA_G32, OTG_CRC32, id),\
|
||||
SRI_ARR(OTG_CRC1_DATA_B32, OTG_CRC32, id)
|
||||
|
||||
static struct dcn_optc_registers optc_regs[4];
|
||||
|
||||
static const struct dcn_optc_shift optc_shift = {
|
||||
OPTC_COMMON_MASK_SH_LIST_DCN3_5(__SHIFT)
|
||||
OPTC_COMMON_MASK_SH_LIST_DCN3_6(__SHIFT)
|
||||
};
|
||||
|
||||
static const struct dcn_optc_mask optc_mask = {
|
||||
OPTC_COMMON_MASK_SH_LIST_DCN3_5(_MASK)
|
||||
OPTC_COMMON_MASK_SH_LIST_DCN3_6(_MASK)
|
||||
};
|
||||
|
||||
#define hubp_regs_init(id)\
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user