mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-10-30 08:02:30 +00:00
drm/amd/display: Implement interface for CRC on CRTC
Add interfaces in DC for per CRTC CRC configuration and fetching. Also implement amdgpu_dm functions to hook onto DRM. Signed-off-by: Leo (Sunpeng) Li <sunpeng.li@amd.com> Reviewed-by: Tony Cheng <Tony.Cheng@amd.com> Reviewed-by: Harry Wentland <Harry.Wentland@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
parent
8cb1545cad
commit
31aec354f9
7 changed files with 271 additions and 0 deletions
|
@ -31,6 +31,10 @@ ifneq ($(CONFIG_DRM_AMD_DC),)
|
|||
AMDGPUDM += amdgpu_dm_services.o amdgpu_dm_helpers.o
|
||||
endif
|
||||
|
||||
ifneq ($(CONFIG_DEBUG_FS),)
|
||||
AMDGPUDM += amdgpu_dm_crc.o
|
||||
endif
|
||||
|
||||
subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc
|
||||
|
||||
AMDGPU_DM = $(addprefix $(AMDDALPATH)/amdgpu_dm/,$(AMDGPUDM))
|
||||
|
|
|
@ -319,6 +319,7 @@ static void dm_crtc_high_irq(void *interrupt_params)
|
|||
crtc_index = acrtc->crtc_id;
|
||||
|
||||
drm_handle_vblank(adev->ddev, crtc_index);
|
||||
amdgpu_dm_crtc_handle_crc_irq(&acrtc->base);
|
||||
}
|
||||
|
||||
static int dm_set_clockgating_state(void *handle,
|
||||
|
@ -2523,6 +2524,7 @@ static const struct drm_crtc_funcs amdgpu_dm_crtc_funcs = {
|
|||
.page_flip = drm_atomic_helper_page_flip,
|
||||
.atomic_duplicate_state = dm_crtc_duplicate_state,
|
||||
.atomic_destroy_state = dm_crtc_destroy_state,
|
||||
.set_crc_source = amdgpu_dm_crtc_set_crc_source,
|
||||
};
|
||||
|
||||
static enum drm_connector_status
|
||||
|
|
|
@ -210,6 +210,8 @@ struct dm_plane_state {
|
|||
struct dm_crtc_state {
|
||||
struct drm_crtc_state base;
|
||||
struct dc_stream_state *stream;
|
||||
|
||||
bool crc_first_skipped;
|
||||
};
|
||||
|
||||
#define to_dm_crtc_state(x) container_of(x, struct dm_crtc_state, base)
|
||||
|
@ -268,6 +270,16 @@ void amdgpu_dm_add_sink_to_freesync_module(struct drm_connector *connector,
|
|||
void
|
||||
amdgpu_dm_remove_sink_from_freesync_module(struct drm_connector *connector);
|
||||
|
||||
/* amdgpu_dm_crc.c */
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
int amdgpu_dm_crtc_set_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
|
||||
void amdgpu_dm_crtc_handle_crc_irq(struct drm_crtc *crtc) {}
|
||||
#endif
|
||||
|
||||
extern const struct drm_encoder_helper_funcs amdgpu_dm_encoder_helper_funcs;
|
||||
|
||||
#endif /* __AMDGPU_DM_H__ */
|
||||
|
|
113
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.c
Normal file
113
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.c
Normal file
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* Copyright 2015 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
|
||||
*
|
||||
*/
|
||||
|
||||
#include <drm/drm_crtc.h>
|
||||
|
||||
#include "amdgpu.h"
|
||||
#include "amdgpu_dm.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)
|
||||
{
|
||||
if (!source || !strcmp(source, "none"))
|
||||
return AMDGPU_DM_PIPE_CRC_SOURCE_NONE;
|
||||
if (!strcmp(source, "auto"))
|
||||
return AMDGPU_DM_PIPE_CRC_SOURCE_AUTO;
|
||||
|
||||
return AMDGPU_DM_PIPE_CRC_SOURCE_INVALID;
|
||||
}
|
||||
|
||||
int amdgpu_dm_crtc_set_crc_source(struct drm_crtc *crtc, const char *src_name,
|
||||
size_t *values_cnt)
|
||||
{
|
||||
struct dm_crtc_state *crtc_state = to_dm_crtc_state(crtc->state);
|
||||
struct dc_stream_state *stream_state = crtc_state->stream;
|
||||
bool ret;
|
||||
|
||||
enum amdgpu_dm_pipe_crc_source source = dm_parse_crc_source(src_name);
|
||||
|
||||
if (source < 0) {
|
||||
DRM_DEBUG_DRIVER("Unknown CRC source %s for CRTC%d\n",
|
||||
src_name, crtc->index);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (source == AMDGPU_DM_PIPE_CRC_SOURCE_AUTO) {
|
||||
ret = dc_stream_configure_crc(stream_state->ctx->dc,
|
||||
stream_state,
|
||||
true, true);
|
||||
} else {
|
||||
ret = dc_stream_configure_crc(stream_state->ctx->dc,
|
||||
stream_state,
|
||||
false, false);
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
*values_cnt = 3;
|
||||
/* Reset crc_skipped flag on dm state */
|
||||
crtc_state->crc_first_skipped = false;
|
||||
return 0;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* amdgpu_dm_crtc_handle_crc_irq: Report to DRM the CRC on given CRTC.
|
||||
* @crtc: DRM CRTC object.
|
||||
*
|
||||
* This function should be called at the end of a vblank, when the fb has been
|
||||
* fully processed through the pipe.
|
||||
*/
|
||||
void amdgpu_dm_crtc_handle_crc_irq(struct drm_crtc *crtc)
|
||||
{
|
||||
struct dm_crtc_state *crtc_state = to_dm_crtc_state(crtc->state);
|
||||
struct dc_stream_state *stream_state = crtc_state->stream;
|
||||
uint32_t crcs[3];
|
||||
|
||||
/*
|
||||
* Since flipping and crc enablement happen asynchronously, we - more
|
||||
* often than not - will be returning an 'uncooked' crc on first frame.
|
||||
* Probably because hw isn't ready yet. Simply skip the first crc
|
||||
* value.
|
||||
*/
|
||||
if (!crtc_state->crc_first_skipped) {
|
||||
crtc_state->crc_first_skipped = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dc_stream_get_crc(stream_state->ctx->dc, stream_state,
|
||||
&crcs[0], &crcs[1], &crcs[2]))
|
||||
return;
|
||||
|
||||
drm_crtc_add_crc_entry(crtc, true,
|
||||
drm_crtc_accurate_vblank_count(crtc), crcs);
|
||||
}
|
|
@ -215,6 +215,91 @@ bool dc_stream_get_crtc_position(struct dc *dc,
|
|||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* dc_stream_configure_crc: Configure CRC capture for the given stream.
|
||||
* @dc: DC Object
|
||||
* @stream: The stream to configure CRC on.
|
||||
* @enable: Enable CRC if true, disable otherwise.
|
||||
* @continuous: Capture CRC on every frame if true. Otherwise, only capture
|
||||
* once.
|
||||
*
|
||||
* By default, only CRC0 is configured, and the entire frame is used to
|
||||
* calculate the crc.
|
||||
*/
|
||||
bool dc_stream_configure_crc(struct dc *dc, struct dc_stream_state *stream,
|
||||
bool enable, bool continuous)
|
||||
{
|
||||
int i;
|
||||
struct pipe_ctx *pipe;
|
||||
struct crc_params param;
|
||||
struct timing_generator *tg;
|
||||
|
||||
for (i = 0; i < MAX_PIPES; i++) {
|
||||
pipe = &dc->current_state->res_ctx.pipe_ctx[i];
|
||||
if (pipe->stream == stream)
|
||||
break;
|
||||
}
|
||||
/* Stream not found */
|
||||
if (i == MAX_PIPES)
|
||||
return false;
|
||||
|
||||
/* Always capture the full frame */
|
||||
param.windowa_x_start = 0;
|
||||
param.windowa_y_start = 0;
|
||||
param.windowa_x_end = pipe->stream->timing.h_addressable;
|
||||
param.windowa_y_end = pipe->stream->timing.v_addressable;
|
||||
param.windowb_x_start = 0;
|
||||
param.windowb_y_start = 0;
|
||||
param.windowb_x_end = pipe->stream->timing.h_addressable;
|
||||
param.windowb_y_end = pipe->stream->timing.v_addressable;
|
||||
|
||||
/* Default to the union of both windows */
|
||||
param.selection = UNION_WINDOW_A_B;
|
||||
param.continuous_mode = continuous;
|
||||
param.enable = enable;
|
||||
|
||||
tg = pipe->stream_res.tg;
|
||||
|
||||
/* Only call if supported */
|
||||
if (tg->funcs->configure_crc)
|
||||
return tg->funcs->configure_crc(tg, ¶m);
|
||||
dm_logger_write(dc->ctx->logger, LOG_WARNING, "CRC capture not supported.");
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* dc_stream_get_crc: Get CRC values for the given stream.
|
||||
* @dc: DC object
|
||||
* @stream: The DC stream state of the stream to get CRCs from.
|
||||
* @r_cr, g_y, b_cb: CRC values for the three channels are stored here.
|
||||
*
|
||||
* dc_stream_configure_crc needs to be called beforehand to enable CRCs.
|
||||
* Return false if stream is not found, or if CRCs are not enabled.
|
||||
*/
|
||||
bool dc_stream_get_crc(struct dc *dc, struct dc_stream_state *stream,
|
||||
uint32_t *r_cr, uint32_t *g_y, uint32_t *b_cb)
|
||||
{
|
||||
int i;
|
||||
struct pipe_ctx *pipe;
|
||||
struct timing_generator *tg;
|
||||
|
||||
for (i = 0; i < MAX_PIPES; i++) {
|
||||
pipe = &dc->current_state->res_ctx.pipe_ctx[i];
|
||||
if (pipe->stream == stream)
|
||||
break;
|
||||
}
|
||||
/* Stream not found */
|
||||
if (i == MAX_PIPES)
|
||||
return false;
|
||||
|
||||
tg = pipe->stream_res.tg;
|
||||
|
||||
if (tg->funcs->get_crc)
|
||||
return tg->funcs->get_crc(tg, r_cr, g_y, b_cb);
|
||||
dm_logger_write(dc->ctx->logger, LOG_WARNING, "CRC capture not supported.");
|
||||
return false;
|
||||
}
|
||||
|
||||
void dc_stream_set_static_screen_events(struct dc *dc,
|
||||
struct dc_stream_state **streams,
|
||||
int num_streams,
|
||||
|
|
|
@ -267,6 +267,17 @@ bool dc_stream_get_crtc_position(struct dc *dc,
|
|||
unsigned int *v_pos,
|
||||
unsigned int *nom_v_pos);
|
||||
|
||||
bool dc_stream_configure_crc(struct dc *dc,
|
||||
struct dc_stream_state *stream,
|
||||
bool enable,
|
||||
bool continuous);
|
||||
|
||||
bool dc_stream_get_crc(struct dc *dc,
|
||||
struct dc_stream_state *stream,
|
||||
uint32_t *r_cr,
|
||||
uint32_t *g_y,
|
||||
uint32_t *b_cb);
|
||||
|
||||
void dc_stream_set_static_screen_events(struct dc *dc,
|
||||
struct dc_stream_state **stream,
|
||||
int num_streams,
|
||||
|
|
|
@ -92,6 +92,36 @@ struct crtc_stereo_flags {
|
|||
uint8_t DISABLE_STEREO_DP_SYNC : 1;
|
||||
};
|
||||
|
||||
enum crc_selection {
|
||||
/* Order must match values expected by hardware */
|
||||
UNION_WINDOW_A_B = 0,
|
||||
UNION_WINDOW_A_NOT_B,
|
||||
UNION_WINDOW_NOT_A_B,
|
||||
UNION_WINDOW_NOT_A_NOT_B,
|
||||
INTERSECT_WINDOW_A_B,
|
||||
INTERSECT_WINDOW_A_NOT_B,
|
||||
INTERSECT_WINDOW_NOT_A_B,
|
||||
INTERSECT_WINDOW_NOT_A_NOT_B,
|
||||
};
|
||||
|
||||
struct crc_params {
|
||||
/* Regions used to calculate CRC*/
|
||||
uint16_t windowa_x_start;
|
||||
uint16_t windowa_x_end;
|
||||
uint16_t windowa_y_start;
|
||||
uint16_t windowa_y_end;
|
||||
|
||||
uint16_t windowb_x_start;
|
||||
uint16_t windowb_x_end;
|
||||
uint16_t windowb_y_start;
|
||||
uint16_t windowb_y_end;
|
||||
|
||||
enum crc_selection selection;
|
||||
|
||||
bool continuous_mode;
|
||||
bool enable;
|
||||
};
|
||||
|
||||
struct timing_generator {
|
||||
const struct timing_generator_funcs *funcs;
|
||||
struct dc_bios *bp;
|
||||
|
@ -173,6 +203,20 @@ struct timing_generator_funcs {
|
|||
bool (*is_tg_enabled)(struct timing_generator *tg);
|
||||
bool (*is_optc_underflow_occurred)(struct timing_generator *tg);
|
||||
void (*clear_optc_underflow)(struct timing_generator *tg);
|
||||
|
||||
/**
|
||||
* Configure CRCs for the given timing generator. Return false if TG is
|
||||
* not on.
|
||||
*/
|
||||
bool (*configure_crc)(struct timing_generator *tg,
|
||||
const struct crc_params *params);
|
||||
|
||||
/**
|
||||
* Get CRCs for the given timing generator. Return false if CRCs are
|
||||
* not enabled (via configure_crc).
|
||||
*/
|
||||
bool (*get_crc)(struct timing_generator *tg,
|
||||
uint32_t *r_cr, uint32_t *g_y, uint32_t *b_cb);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue