Commit 97bda032 authored by Harry Wentland's avatar Harry Wentland Committed by Alex Deucher

drm/amd/display: Add DSC support for Navi (v2)

Add support for DCN2 DSC (Display Stream Compression)

HW Blocks:

 +--------++------+       +----------+
 | HUBBUB || HUBP |  <--  | MMHUBBUB |
 +--------++------+       +----------+
        |                     ^
        v                     |
    +--------+            +--------+
    |  DPP   |            |  DWB   |
    +--------+            +--------+
        |
        v                      ^
    +--------+                 |
    |  MPC   |                 |
    +--------+                 |
        |                      |
        v                      |
    +-------+      +-------+   |
    |  OPP  | <--> |  DSC  |   |
    +-------+      +-------+   |
        |                      |
        v                      |
    +--------+                /
    |  OPTC  |  --------------
    +--------+
        |
        v
    +--------+       +--------+
    |  DIO   |       |  DCCG  |
    +--------+       +--------+

v2: rebase (Alex)
Signed-off-by: default avatarHarry Wentland <harry.wentland@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent b4f199c7
......@@ -25,6 +25,16 @@ config DRM_AMD_DC_DCN2_0
Choose this option if you want to have
Navi support for display engine
config DRM_AMD_DC_DSC_SUPPORT
bool "DSC support"
default n
depends on DRM_AMD_DC && X86
depends on DRM_AMD_DC_DCN1_0
depends on DRM_AMD_DC_DCN2_0
help
Choose this option if you want to have
Dynamic Stream Compression support
config DEBUG_KERNEL_DC
bool "Enable kgdb break in DC"
depends on DRM_AMD_DC
......
......@@ -542,6 +542,16 @@ bool dm_helpers_submit_i2c(
return result;
}
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
bool dm_helpers_dp_write_dsc_enable(
struct dc_context *ctx,
const struct dc_stream_state *stream,
bool enable
)
{
return false;
}
#endif
bool dm_helpers_is_dp_sink_present(struct dc_link *link)
{
......
......@@ -30,6 +30,9 @@ DC_LIBS += dcn20
endif
ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
DC_LIBS += dsc
endif
ifdef CONFIG_DRM_AMD_DC_DCN1_0
DC_LIBS += dcn10 dml
......
......@@ -56,6 +56,10 @@
#include "dc_link_dp.h"
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
#include "dsc.h"
#endif
#ifdef CONFIG_DRM_AMD_DC_DCN2_0
#include "vm_helper.h"
#endif
......@@ -1730,6 +1734,23 @@ static void commit_planes_do_stream_update(struct dc *dc,
#endif
}
#if defined(CONFIG_DRM_AMD_DC_DSC_SUPPORT)
if (stream_update->dsc_config && dc->hwss.pipe_control_lock_global) {
if (stream_update->dsc_config->num_slices_h &&
stream_update->dsc_config->num_slices_v) {
/* dsc enable */
dc->hwss.pipe_control_lock_global(dc, pipe_ctx, true);
dp_set_dsc_enable(pipe_ctx, true);
dc->hwss.pipe_control_lock_global(dc, pipe_ctx, false);
} else {
/* dsc disable */
dc->hwss.pipe_control_lock_global(dc, pipe_ctx, true);
dp_set_dsc_enable(pipe_ctx, false);
dc->hwss.pipe_control_lock_global(dc, pipe_ctx, false);
}
}
#endif
/* Full fe update*/
if (update_type == UPDATE_TYPE_FAST)
continue;
......
......@@ -1508,6 +1508,9 @@ static enum dc_status enable_link_dp(
if (link_settings.link_rate == LINK_RATE_LOW)
skip_video_pattern = false;
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
dp_set_fec_ready(link, true);
#endif
if (perform_link_training_with_retries(
link,
......@@ -1520,6 +1523,9 @@ static enum dc_status enable_link_dp(
else
status = DC_FAIL_DP_LINK_TRAINING;
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
dp_set_fec_enable(link, true);
#endif
return status;
}
......@@ -2142,6 +2148,14 @@ static void disable_link(struct dc_link *link, enum signal_type signal)
dp_disable_link_phy(link, signal);
else
dp_disable_link_phy_mst(link, signal);
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
if (dc_is_dp_sst_signal(signal) ||
link->mst_stream_alloc_table.stream_count == 0) {
dp_set_fec_enable(link, false);
dp_set_fec_ready(link, false);
}
#endif
} else
link->link_enc->funcs->disable_output(link->link_enc, signal);
......@@ -2376,6 +2390,11 @@ static struct fixed31_32 get_pbn_per_slot(struct dc_stream_state *stream)
&stream->link->cur_link_settings);
link_rate_in_mbytes_per_sec /= 8000; /* Kbits to MBytes */
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
if (stream->link->fec_state != dc_link_fec_not_ready)
link_rate_in_mbytes_per_sec = (link_rate_in_mbytes_per_sec * 970)/1000;
#endif
mbytes_per_sec = dc_fixpt_from_int(link_rate_in_mbytes_per_sec);
return dc_fixpt_div_int(mbytes_per_sec, 54);
......@@ -2738,12 +2757,30 @@ void core_link_enable_stream(
if (pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST)
allocate_mst_payload(pipe_ctx);
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
if (pipe_ctx->stream->timing.flags.DSC &&
(dc_is_dp_signal(pipe_ctx->stream->signal) ||
dc_is_virtual_signal(pipe_ctx->stream->signal))) {
dp_set_dsc_enable(pipe_ctx, true);
pipe_ctx->stream_res.tg->funcs->wait_for_state(
pipe_ctx->stream_res.tg,
CRTC_STATE_VBLANK);
}
#endif
core_dc->hwss.unblank_stream(pipe_ctx,
&pipe_ctx->stream->link->cur_link_settings);
if (dc_is_dp_signal(pipe_ctx->stream->signal))
enable_stream_features(pipe_ctx);
}
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
else { // if (IS_FPGA_MAXIMUS_DC(core_dc->ctx->dce_environment))
if (dc_is_dp_signal(pipe_ctx->stream->signal) ||
dc_is_virtual_signal(pipe_ctx->stream->signal))
dp_set_dsc_enable(pipe_ctx, true);
}
#endif
}
void core_link_disable_stream(struct pipe_ctx *pipe_ctx, int option)
......@@ -2784,6 +2821,12 @@ void core_link_disable_stream(struct pipe_ctx *pipe_ctx, int option)
core_dc->hwss.disable_stream(pipe_ctx, option);
disable_link(pipe_ctx->stream->link, pipe_ctx->stream->signal);
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
if (pipe_ctx->stream->timing.flags.DSC &&
dc_is_dp_signal(pipe_ctx->stream->signal)) {
dp_set_dsc_enable(pipe_ctx, false);
}
#endif
}
void core_link_set_avmute(struct pipe_ctx *pipe_ctx, bool enable)
......@@ -2851,6 +2894,14 @@ uint32_t dc_bandwidth_in_kbps_from_timing(
uint32_t bits_per_channel = 0;
uint32_t kbps;
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
if (timing->flags.DSC) {
kbps = (timing->pix_clk_100hz * timing->dsc_cfg.bits_per_pixel);
kbps = kbps / 160 + ((kbps % 160) ? 1 : 0);
return kbps;
}
#endif
switch (timing->display_color_depth) {
case COLOR_DEPTH_666:
bits_per_channel = 6;
......
......@@ -4,6 +4,9 @@
#include "dc_link_dp.h"
#include "dm_helpers.h"
#include "opp.h"
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
#include "dsc.h"
#endif
#if defined(CONFIG_DRM_AMD_DC_DCN2_0)
#include "resource.h"
#endif
......@@ -2379,6 +2382,10 @@ static bool retrieve_link_cap(struct dc_link *link)
uint32_t read_dpcd_retry_cnt = 3;
int i;
struct dp_sink_hw_fw_revision dp_hw_fw_revision;
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
uint8_t dsc_data[16];
struct dsc_dec_dpcd_caps *dsc_caps;
#endif
memset(dpcd_data, '\0', sizeof(dpcd_data));
memset(&down_strm_port_count,
......@@ -2550,6 +2557,90 @@ static bool retrieve_link_cap(struct dc_link *link)
dp_hw_fw_revision.ieee_fw_rev,
sizeof(dp_hw_fw_revision.ieee_fw_rev));
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
dsc_caps = &link->dpcd_caps.dsc_sink_caps;
memset(dsc_caps, '\0', sizeof(*dsc_caps));
memset(&link->dpcd_caps.dsc_sink_caps, '\0',
sizeof(link->dpcd_caps.dsc_sink_caps));
memset(&link->dpcd_caps.fec_cap, '\0', sizeof(link->dpcd_caps.fec_cap));
/* Read DSC and FEC sink capabilities if DP revision is 1.4 and up */
if (link->dpcd_caps.dpcd_rev.raw >= DPCD_REV_14) {
status = core_link_read_dpcd(
link,
DP_DSC_SUPPORT,
dsc_data,
sizeof(dsc_data));
if (status == DC_OK) {
DC_LOG_DSC("DSC capability read at link %d:",
link->link_index);
DC_LOG_DSC("\t%02x %02x %02x %02x",
dsc_data[0], dsc_data[1],
dsc_data[2], dsc_data[3]);
DC_LOG_DSC("\t%02x %02x %02x %02x",
dsc_data[4], dsc_data[5],
dsc_data[6], dsc_data[7]);
DC_LOG_DSC("\t%02x %02x %02x %02x",
dsc_data[8], dsc_data[9],
dsc_data[10], dsc_data[11]);
DC_LOG_DSC("\t%02x %02x %02x %02x",
dsc_data[12], dsc_data[13],
dsc_data[14], dsc_data[15]);
} else {
dm_error("%s: Read DSC dpcd data failed.\n", __func__);
return false;
}
if (dc_dsc_parse_dsc_dpcd(dsc_data,
dsc_caps)) {
DC_LOG_DSC("DSC capability parsed at link %d:",
link->link_index);
DC_LOG_DSC("\tis_dsc_supported:\t%d",
dsc_caps->is_dsc_supported);
DC_LOG_DSC("\tdsc_version:\t%d", dsc_caps->dsc_version);
DC_LOG_DSC("\trc_buffer_size:\t%d",
dsc_caps->rc_buffer_size);
DC_LOG_DSC("\tslice_caps1:\t0x%x20",
dsc_caps->slice_caps1.raw);
DC_LOG_DSC("\tslice_caps2:\t0x%x20",
dsc_caps->slice_caps2.raw);
DC_LOG_DSC("\tlb_bit_depth:\t%d",
dsc_caps->lb_bit_depth);
DC_LOG_DSC("\tis_block_pred_supported:\t%d",
dsc_caps->is_block_pred_supported);
DC_LOG_DSC("\tedp_max_bits_per_pixel:\t%d",
dsc_caps->edp_max_bits_per_pixel);
DC_LOG_DSC("\tcolor_formats:\t%d",
dsc_caps->color_formats.raw);
DC_LOG_DSC("\tcolor_depth:\t%d",
dsc_caps->color_depth.raw);
DC_LOG_DSC("\tthroughput_mode_0_mps:\t%d",
dsc_caps->throughput_mode_0_mps);
DC_LOG_DSC("\tthroughput_mode_1_mps:\t%d",
dsc_caps->throughput_mode_1_mps);
DC_LOG_DSC("\tmax_slice_width:\t%d",
dsc_caps->max_slice_width);
DC_LOG_DSC("\tbpp_increment_div:\t%d",
dsc_caps->bpp_increment_div);
} else {
/* Some sinks return bogus DSC DPCD data
* when they don't support DSC.
*/
dm_error("%s: DSC DPCD data doesn't make sense. "
"DSC will be disabled.\n", __func__);
memset(&link->dpcd_caps.dsc_sink_caps, '\0',
sizeof(link->dpcd_caps.dsc_sink_caps));
}
status = core_link_read_dpcd(
link,
DP_FEC_CAPABILITY,
&link->dpcd_caps.fec_cap.raw,
sizeof(link->dpcd_caps.fec_cap.raw));
if (status != DC_OK)
dm_error("%s: Read FEC dpcd register failed.\n",
__func__);
}
#endif
/* Connectivity log: detection */
CONN_DATA_DETECT(link, dpcd_data, sizeof(dpcd_data), "Rx Caps: ");
......@@ -2964,4 +3055,66 @@ void dp_enable_mst_on_sink(struct dc_link *link, bool enable)
core_link_write_dpcd(link, DP_MSTM_CTRL, &mstmCntl, 1);
}
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
void dp_set_fec_ready(struct dc_link *link, bool ready)
{
/* FEC has to be "set ready" before the link training.
* The policy is to always train with FEC
* if the sink supports it and leave it enabled on link.
* If FEC is not supported, disable it.
*/
struct link_encoder *link_enc = link->link_enc;
uint8_t fec_config = 0;
if (link->dc->debug.disable_fec ||
IS_FPGA_MAXIMUS_DC(link->ctx->dce_environment))
return;
if (link_enc->funcs->fec_set_ready &&
link->dpcd_caps.fec_cap.bits.FEC_CAPABLE) {
if (link->fec_state == dc_link_fec_not_ready && ready) {
fec_config = 1;
if (core_link_write_dpcd(link,
DP_FEC_CONFIGURATION,
&fec_config,
sizeof(fec_config)) == DC_OK) {
link_enc->funcs->fec_set_ready(link_enc, true);
link->fec_state = dc_link_fec_ready;
} else {
dm_error("dpcd write failed to set fec_ready");
}
} else if (link->fec_state == dc_link_fec_ready && !ready) {
fec_config = 0;
core_link_write_dpcd(link,
DP_FEC_CONFIGURATION,
&fec_config,
sizeof(fec_config));
link->link_enc->funcs->fec_set_ready(
link->link_enc, false);
link->fec_state = dc_link_fec_not_ready;
}
}
}
void dp_set_fec_enable(struct dc_link *link, bool enable)
{
struct link_encoder *link_enc = link->link_enc;
if (link->dc->debug.disable_fec ||
IS_FPGA_MAXIMUS_DC(link->ctx->dce_environment))
return;
if (link_enc->funcs->fec_set_enable &&
link->dpcd_caps.fec_cap.bits.FEC_CAPABLE) {
if (link->fec_state == dc_link_fec_ready && enable) {
msleep(1);
link_enc->funcs->fec_set_enable(link_enc, true);
link->fec_state = dc_link_fec_enabled;
} else if (link->fec_state == dc_link_fec_enabled && !enable) {
link_enc->funcs->fec_set_enable(link_enc, false);
link->fec_state = dc_link_fec_ready;
}
}
}
#endif
......@@ -12,6 +12,12 @@
#include "dc_link_ddc.h"
#include "dm_helpers.h"
#include "dpcd_defs.h"
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
#include "dsc.h"
#endif
#if defined(CONFIG_DRM_AMD_DC_DCN2_0)
#include "resource.h"
#endif
enum dc_status core_link_read_dpcd(
struct dc_link *link,
......@@ -360,3 +366,142 @@ void dp_retrain_link_dp_test(struct dc_link *link,
}
}
}
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
#define DC_LOGGER \
dsc->ctx->logger
static void dsc_optc_config_log(struct display_stream_compressor *dsc,
struct dsc_optc_config *config)
{
DC_LOG_DSC("Setting optc DSC config at DSC inst %d", dsc->inst);
DC_LOG_DSC("\n\tbytes_per_pixel %d\n\tis_pixel_format_444 %d\n\tslice_width %d",
config->bytes_per_pixel,
config->is_pixel_format_444, config->slice_width);
}
static bool dp_set_dsc_on_rx(struct pipe_ctx *pipe_ctx, bool enable)
{
struct dc *core_dc = pipe_ctx->stream->ctx->dc;
struct dc_stream_state *stream = pipe_ctx->stream;
bool result = false;
if (IS_FPGA_MAXIMUS_DC(core_dc->ctx->dce_environment))
result = true;
else
result = dm_helpers_dp_write_dsc_enable(core_dc->ctx, stream, enable);
return result;
}
/* This has to be done after DSC was enabled on RX first, i.e. after dp_enable_dsc_on_rx() had been called
*/
static void dp_set_dsc_on_stream(struct pipe_ctx *pipe_ctx, bool enable)
{
struct display_stream_compressor *dsc = pipe_ctx->stream_res.dsc;
struct dc *core_dc = pipe_ctx->stream->ctx->dc;
struct dc_stream_state *stream = pipe_ctx->stream;
struct pipe_ctx *odm_pipe = dc_res_get_odm_bottom_pipe(pipe_ctx);
if (enable) {
/* TODO proper function */
struct dsc_config dsc_cfg;
struct dsc_optc_config dsc_optc_cfg;
enum optc_dsc_mode optc_dsc_mode;
uint8_t dsc_packed_pps[128];
/* Enable DSC hw block */
dsc_cfg.pic_width = stream->timing.h_addressable + stream->timing.h_border_left + stream->timing.h_border_right;
dsc_cfg.pic_height = stream->timing.v_addressable + stream->timing.v_border_top + stream->timing.v_border_bottom;
dsc_cfg.pixel_encoding = stream->timing.pixel_encoding;
dsc_cfg.color_depth = stream->timing.display_color_depth;
dsc_cfg.dc_dsc_cfg = stream->timing.dsc_cfg;
dsc->funcs->dsc_set_config(dsc, &dsc_cfg, &dsc_optc_cfg, &dsc_packed_pps[0]);
if (odm_pipe) {
struct display_stream_compressor *bot_dsc = odm_pipe->stream_res.dsc;
uint8_t dsc_packed_pps_odm[128];
dsc_cfg.pic_width /= 2;
ASSERT(dsc_cfg.dc_dsc_cfg.num_slices_h % 2 == 0);
dsc_cfg.dc_dsc_cfg.num_slices_h /= 2;
dsc->funcs->dsc_set_config(dsc, &dsc_cfg, &dsc_optc_cfg, &dsc_packed_pps_odm[0]);
bot_dsc->funcs->dsc_set_config(bot_dsc, &dsc_cfg, &dsc_optc_cfg, &dsc_packed_pps_odm[0]);
bot_dsc->funcs->dsc_enable(bot_dsc, odm_pipe->stream_res.opp->inst);
}
dsc->funcs->dsc_enable(dsc, pipe_ctx->stream_res.opp->inst);
optc_dsc_mode = dsc_optc_cfg.is_pixel_format_444 ? OPTC_DSC_ENABLED_444 : OPTC_DSC_ENABLED_NATIVE_SUBSAMPLED;
dsc_optc_config_log(dsc, &dsc_optc_cfg);
/* Enable DSC in encoder */
if (!IS_FPGA_MAXIMUS_DC(core_dc->ctx->dce_environment) && pipe_ctx->stream_res.stream_enc->funcs->dp_set_dsc_config)
pipe_ctx->stream_res.stream_enc->funcs->dp_set_dsc_config(pipe_ctx->stream_res.stream_enc,
optc_dsc_mode,
dsc_optc_cfg.bytes_per_pixel,
dsc_optc_cfg.slice_width,
&dsc_packed_pps[0]);
/* Enable DSC in OPTC */
pipe_ctx->stream_res.tg->funcs->set_dsc_config(pipe_ctx->stream_res.tg,
optc_dsc_mode,
dsc_optc_cfg.bytes_per_pixel,
dsc_optc_cfg.slice_width);
} else {
/* disable DSC in OPTC */
pipe_ctx->stream_res.tg->funcs->set_dsc_config(
pipe_ctx->stream_res.tg,
OPTC_DSC_DISABLED, 0, 0);
/* disable DSC in stream encoder */
if (!IS_FPGA_MAXIMUS_DC(core_dc->ctx->dce_environment)) {
pipe_ctx->stream_res.stream_enc->funcs->dp_set_dsc_config(
pipe_ctx->stream_res.stream_enc,
OPTC_DSC_DISABLED, 0, 0, NULL);
}
/* disable DSC block */
pipe_ctx->stream_res.dsc->funcs->dsc_disable(pipe_ctx->stream_res.dsc);
if (odm_pipe)
odm_pipe->stream_res.dsc->funcs->dsc_disable(odm_pipe->stream_res.dsc);
}
}
bool dp_set_dsc_enable(struct pipe_ctx *pipe_ctx, bool enable)
{
struct dc_stream_state *stream = pipe_ctx->stream;
struct display_stream_compressor *dsc = pipe_ctx->stream_res.dsc;
bool result = false;
if (!dsc)
goto out;
if (enable && stream->is_dsc_enabled) {
/* update dsc stream */
dp_set_dsc_on_stream(pipe_ctx, true);
stream->is_dsc_enabled = true;
result = true;
} else if (enable && !stream->is_dsc_enabled) {
/* enable dsc on non dsc stream */
if (dp_set_dsc_on_rx(pipe_ctx, true)) {
dp_set_dsc_on_stream(pipe_ctx, true);
stream->is_dsc_enabled = true;
result = true;
} else {
stream->is_dsc_enabled = false;
result = false;
}
} else if (!enable && stream->is_dsc_enabled) {
/* disable dsc on dsc stream */
dp_set_dsc_on_rx(pipe_ctx, false);
dp_set_dsc_on_stream(pipe_ctx, false);
stream->is_dsc_enabled = false;
result = true;
} else {
/* disable dsc on non dsc stream */
result = true;
}
out:
return result;
}
#endif
......@@ -105,6 +105,16 @@ static void construct(struct dc_stream_state *stream,
/* EDID CAP translation for HDMI 2.0 */
stream->timing.flags.LTE_340MCSC_SCRAMBLE = dc_sink_data->edid_caps.lte_340mcsc_scramble;
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
memset(&stream->timing.dsc_cfg, 0, sizeof(stream->timing.dsc_cfg));
stream->timing.dsc_cfg.num_slices_h = 0;
stream->timing.dsc_cfg.num_slices_v = 0;
stream->timing.dsc_cfg.bits_per_pixel = 128;
stream->timing.dsc_cfg.block_pred_enable = 1;
stream->timing.dsc_cfg.linebuf_depth = 9;
stream->timing.dsc_cfg.version_minor = 2;
stream->timing.dsc_cfg.ycbcr422_simple = 0;
#endif
update_stream_signal(stream, dc_sink_data);
......
......@@ -331,6 +331,9 @@ struct dc_debug_options {
bool disable_dfs_bypass;
bool disable_dpp_power_gate;
bool disable_hubp_power_gate;
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
bool disable_dsc_power_gate;
#endif
bool disable_pplib_wm_range;
enum wm_report_mode pplib_wm_report_mode;
unsigned int min_disp_clk_khz;
......@@ -363,6 +366,9 @@ struct dc_debug_options {
unsigned int force_fclk_khz;
bool disable_tri_buf;
struct dc_bw_validation_profile bw_val_profile;
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
bool disable_fec;
#endif
};
struct dc_debug_data {
......@@ -894,6 +900,10 @@ struct dpcd_caps {
bool panel_mode_edp;
bool dpcd_display_control_capable;
bool ext_receiver_cap_field_present;
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
union fec_capability fec_cap;
struct dsc_dec_dpcd_caps dsc_sink_caps;
#endif
};
#include "dc_link.h"
......@@ -928,6 +938,14 @@ struct dc_sink {
struct stereo_3d_features features_3d[TIMING_3D_FORMAT_MAX];
bool converter_disable_audio;
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
struct dc_sink_dsc_caps {
// 'true' if these are virtual DPCD's DSC caps (immediately upstream of sink in MST topology),
// 'false' if they are sink's DSC caps
bool is_virtual_dpcd_dsc;
struct dsc_dec_dpcd_caps dsc_dec_caps;
} sink_dsc_caps;
#endif
/* private to DC core */
struct dc_link *link;
......@@ -986,4 +1004,10 @@ unsigned int dc_get_target_backlight_pwm(struct dc *dc);
bool dc_is_dmcu_initialized(struct dc *dc);
#if defined(CONFIG_DRM_AMD_DC_DSC_SUPPORT)
/*******************************************************************************
* DSC Interfaces
******************************************************************************/
#include "dc_dsc.h"
#endif
#endif /* DC_INTERFACE_H_ */
......@@ -512,4 +512,18 @@ union test_misc {
unsigned char raw;
};
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
/* FEC capability DPCD register field bits-*/
union fec_capability {
struct {
uint8_t FEC_CAPABLE:1;
uint8_t UNCORRECTED_BLOCK_ERROR_COUNT_CAPABLE:1;
uint8_t CORRECTED_BLOCK_ERROR_COUNT_CAPABLE:1;
uint8_t BIT_ERROR_COUNT_CAPABLE:1;
uint8_t RESERVED:4;
} bits;
uint8_t raw;
};
#endif /* CONFIG_DRM_AMD_DC_DSC_SUPPORT */
#endif /* DC_DP_TYPES_H */
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
#ifndef DC_DSC_H_
#define DC_DSC_H_
/*
* 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.
*
* Author: AMD
*/
struct dc_dsc_bw_range {
uint32_t min_kbps;
uint32_t min_target_bpp_x16;
uint32_t max_kbps;
uint32_t max_target_bpp_x16;
uint32_t stream_kbps;
};
bool dc_dsc_parse_dsc_dpcd(const uint8_t *dpcd_dsc_data,
struct dsc_dec_dpcd_caps *dsc_sink_caps);
bool dc_dsc_compute_bandwidth_range(
const struct dc *dc,
const struct dsc_dec_dpcd_caps *dsc_sink_caps,
const struct dc_crtc_timing *timing,
struct dc_dsc_bw_range *range);
bool dc_dsc_compute_config(
const struct dc *dc,
const struct dsc_dec_dpcd_caps *dsc_sink_caps,
int target_bandwidth,
const struct dc_crtc_timing *timing,
struct dc_dsc_config *dsc_cfg);
bool dc_check_and_fit_timing_into_bandwidth_with_dsc_legacy(
const struct dc *pDC,
const struct dc_link *link,
struct dc_crtc_timing *timing);
bool dc_setup_dsc_in_timing_legacy(const struct dc *pDC,
const struct dsc_dec_dpcd_caps *dsc_sink_caps,
int available_bandwidth_kbps,
struct dc_crtc_timing *timing);
#endif
#endif
......@@ -709,6 +709,9 @@ struct dc_crtc_timing_flags {
* rates less than or equal to 340Mcsc */
uint32_t LTE_340MCSC_SCRAMBLE:1;
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
uint32_t DSC : 1; /* Use DSC with this timing */
#endif
};
enum dc_timing_3d_format {
......@@ -755,6 +758,18 @@ struct dc_crtc_timing_adjust {
uint32_t v_total_max;
};
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
struct dc_dsc_config {
uint32_t num_slices_h; /* Number of DSC slices - horizontal */
uint32_t num_slices_v; /* Number of DSC slices - vertical */
uint32_t bits_per_pixel; /* DSC target bitrate in 1/16 of bpp (e.g. 128 -> 8bpp) */
bool block_pred_enable; /* DSC block prediction enable */
uint32_t linebuf_depth; /* DSC line buffer depth */
uint32_t version_minor; /* DSC minor version. Full version is formed as 1.version_minor. */
bool ycbcr422_simple; /* Tell DSC engine to convert YCbCr 4:2:2 to 'YCbCr 4:2:2 simple'. */
int32_t rc_buffer_size; /* DSC RC buffer block size in bytes */
};
#endif
struct dc_crtc_timing {
uint32_t h_total;
uint32_t h_border_left;
......@@ -781,6 +796,9 @@ struct dc_crtc_timing {
enum scanning_type scan_type;
struct dc_crtc_timing_flags flags;
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
struct dc_dsc_config dsc_cfg;
#endif
};
/* Passed on init */
......
......@@ -29,6 +29,13 @@
#include "dc_types.h"
#include "grph_object_defs.h"
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
enum dc_link_fec_state {
dc_link_fec_not_ready,
dc_link_fec_ready,
dc_link_fec_enabled
};
#endif
struct dc_link_status {
bool link_active;
struct dpcd_caps *dpcd_caps;
......@@ -129,6 +136,9 @@ struct dc_link {
struct link_trace link_trace;
struct gpio *hpd_gpio;
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
enum dc_link_fec_state fec_state;
#endif
};
const struct dc_link_status *dc_link_get_status(const struct dc_link *dc_link);
......
......@@ -211,6 +211,9 @@ struct dc_stream_state {
bool apply_seamless_boot_optimization;
uint32_t stream_id;
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
bool is_dsc_enabled;
#endif
};
struct dc_stream_update {
......@@ -238,6 +241,9 @@ struct dc_stream_update {
#if defined(CONFIG_DRM_AMD_DC_DCN2_0)
struct dc_writeback_update *wb_update;
#endif
#if defined(CONFIG_DRM_AMD_DC_DSC_SUPPORT)
struct dc_dsc_config *dsc_config;
#endif
};
bool dc_is_stream_unchanged(
......
......@@ -553,6 +553,9 @@ enum dc_infoframe_type {
DC_HDMI_INFOFRAME_TYPE_AVI = 0x82,
DC_HDMI_INFOFRAME_TYPE_SPD = 0x83,
DC_HDMI_INFOFRAME_TYPE_AUDIO = 0x84,
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
DC_DP_INFOFRAME_TYPE_PPS = 0x10,
#endif
};
struct dc_info_packet {
......@@ -706,4 +709,70 @@ struct AsicStateEx {
unsigned int phyClock;
};
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
/* DSC DPCD capabilities */
union dsc_slice_caps1 {
struct {
uint8_t NUM_SLICES_1 : 1;
uint8_t NUM_SLICES_2 : 1;
uint8_t RESERVED : 1;
uint8_t NUM_SLICES_4 : 1;
uint8_t NUM_SLICES_6 : 1;
uint8_t NUM_SLICES_8 : 1;
uint8_t NUM_SLICES_10 : 1;
uint8_t NUM_SLICES_12 : 1;
} bits;
uint8_t raw;
};
union dsc_slice_caps2 {
struct {
uint8_t NUM_SLICES_16 : 1;
uint8_t NUM_SLICES_20 : 1;
uint8_t NUM_SLICES_24 : 1;
uint8_t RESERVED : 5;
} bits;
uint8_t raw;
};
union dsc_color_formats {
struct {
uint8_t RGB : 1;
uint8_t YCBCR_444 : 1;
uint8_t YCBCR_SIMPLE_422 : 1;
uint8_t YCBCR_NATIVE_422 : 1;
uint8_t YCBCR_NATIVE_420 : 1;
uint8_t RESERVED : 3;
} bits;
uint8_t raw;
};
union dsc_color_depth {
struct {
uint8_t RESERVED1 : 1;
uint8_t COLOR_DEPTH_8_BPC : 1;
uint8_t COLOR_DEPTH_10_BPC : 1;
uint8_t COLOR_DEPTH_12_BPC : 1;
uint8_t RESERVED2 : 3;
} bits;
uint8_t raw;
};
struct dsc_dec_dpcd_caps {
bool is_dsc_supported;
uint8_t dsc_version;
int32_t rc_buffer_size; /* DSC RC buffer block size in bytes */
union dsc_slice_caps1 slice_caps1;
union dsc_slice_caps2 slice_caps2;
int32_t lb_bit_depth;
bool is_block_pred_supported;
int32_t edp_max_bits_per_pixel; /* Valid only in eDP */
union dsc_color_formats color_formats;
union dsc_color_depth color_depth;
int32_t throughput_mode_0_mps; /* In MPs */
int32_t throughput_mode_1_mps; /* In MPs */
int32_t max_slice_width;
uint32_t bpp_increment_div; /* bpp increment divisor, e.g. if 16, it's 1/16th of a bit */
};
#endif
#endif /* DC_TYPES_H_ */
......@@ -49,6 +49,9 @@
#include "clk_mgr.h"
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
#include "dsc.h"
#endif
#define DC_LOGGER_INIT(logger)
......@@ -347,6 +350,61 @@ void dcn10_log_hw_state(struct dc *dc,
}
DTN_INFO("\n");
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
DTN_INFO("DSC: CLOCK_EN SLICE_WIDTH Bytes_pp\n");
for (i = 0; i < pool->res_cap->num_dsc; i++) {
struct display_stream_compressor *dsc = pool->dscs[i];
struct dcn_dsc_state s = {0};
dsc->funcs->dsc_read_state(dsc, &s);
DTN_INFO("[%d]: %-9d %-12d %-10d\n",
dsc->inst,
s.dsc_clock_en,
s.dsc_slice_width,
s.dsc_bytes_per_pixel);
DTN_INFO("\n");
}
DTN_INFO("\n");
DTN_INFO("S_ENC: DSC_MODE SEC_GSP7_LINE_NUM"
" VBID6_LINE_REFERENCE VBID6_LINE_NUM SEC_GSP7_ENABLE SEC_STREAM_ENABLE\n");
for (i = 0; i < pool->stream_enc_count; i++) {
struct stream_encoder *enc = pool->stream_enc[i];
struct enc_state s = {0};
if (enc->funcs->enc_read_state) {
enc->funcs->enc_read_state(enc, &s);
DTN_INFO("[%-3d]: %-9d %-18d %-21d %-15d %-16d %-17d\n",
enc->id,
s.dsc_mode,
s.sec_gsp7_line_num,
s.vbid6_line_reference,
s.vbid6_line_num,
s.sec_gsp7_enable,
s.sec_stream_enable);
DTN_INFO("\n");
}
}
DTN_INFO("\n");
DTN_INFO("L_ENC: DPHY_FEC_EN DPHY_FEC_READY_SHADOW DPHY_FEC_ACTIVE_STATUS\n");
for (i = 0; i < dc->link_count; i++) {
struct link_encoder *lenc = dc->links[i]->link_enc;
struct link_enc_state s = {0};
if (lenc->funcs->read_state) {
lenc->funcs->read_state(lenc, &s);
DTN_INFO("[%-3d]: %-12d %-22d %-22d\n",
i,
s.dphy_fec_en,
s.dphy_fec_ready_shadow,
s.dphy_fec_active_status);
DTN_INFO("\n");
}
}
DTN_INFO("\n");
#endif
DTN_INFO("\nCALCULATED Clocks: dcfclk_khz:%d dcfclk_deep_sleep_khz:%d dispclk_khz:%d\n"
"dppclk_khz:%d max_supported_dppclk_khz:%d fclk_khz:%d socclk_khz:%d\n\n",
......
......@@ -1507,10 +1507,28 @@ void dcn10_timing_generator_init(struct optc *optc1)
optc1->comb_opp_id = 0xf;
}
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
/* "Containter" vs. "pixel" is a concept within HW blocks, mostly those closer to the back-end. It works like this:
*
* - In most of the formats (RGB or YCbCr 4:4:4, 4:2:2 uncompressed and DSC 4:2:2 Simple) pixel rate is the same as
* containter rate.
*
* - In 4:2:0 (DSC or uncompressed) there are two pixels per container, hence the target container rate has to be
* halved to maintain the correct pixel rate.
*
* - Unlike 4:2:2 uncompressed, DSC 4:2:2 Native also has two pixels per container (this happens when DSC is applied
* to it) and has to be treated the same as 4:2:0, i.e. target containter rate has to be halved in this case as well.
*
*/
#endif
bool optc1_is_two_pixels_per_containter(const struct dc_crtc_timing *timing)
{
bool two_pix = timing->pixel_encoding == PIXEL_ENCODING_YCBCR420;
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
two_pix = two_pix || (timing->flags.DSC && timing->pixel_encoding == PIXEL_ENCODING_YCBCR422
&& !timing->dsc_cfg.ycbcr422_simple);
#endif
return two_pix;
}
......@@ -6,6 +6,10 @@ DCN20 = dcn20_resource.o dcn20_hwseq.o dcn20_dpp.o dcn20_dpp_cm.o dcn20_hubp.o \
dcn20_stream_encoder.o dcn20_link_encoder.o dcn20_dccg.o \
dcn20_vmid.o dcn20_dwb.o dcn20_dwb_scl.o
ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
DCN20 += dcn20_dsc.o
endif
CFLAGS_dcn20_resource.o := -mhard-float -msse -mpreferred-stack-boundary=4
......
This diff is collapsed.
This diff is collapsed.
......@@ -32,6 +32,9 @@
#include "dcn10/dcn10_hw_sequencer.h"
#include "dcn20_hwseq.h"
#include "dce/dce_hwseq.h"
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
#include "dcn20/dcn20_dsc.h"
#endif
#include "abm.h"
#include "clk_mgr.h"
#include "dmcu.h"
......@@ -211,6 +214,76 @@ static void dcn20_init_blank(
dcn20_hwss_wait_for_blank_complete(opp);
}
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
static void dcn20_dsc_pg_control(
struct dce_hwseq *hws,
unsigned int dsc_inst,
bool power_on)
{
uint32_t power_gate = power_on ? 0 : 1;
uint32_t pwr_status = power_on ? 0 : 2;
if (hws->ctx->dc->debug.disable_dsc_power_gate)
return;
if (REG(DOMAIN16_PG_CONFIG) == 0)
return;
switch (dsc_inst) {
case 0: /* DSC0 */
REG_UPDATE(DOMAIN16_PG_CONFIG,
DOMAIN16_POWER_GATE, power_gate);
REG_WAIT(DOMAIN16_PG_STATUS,
DOMAIN16_PGFSM_PWR_STATUS, pwr_status,
1, 1000);
break;
case 1: /* DSC1 */
REG_UPDATE(DOMAIN17_PG_CONFIG,
DOMAIN17_POWER_GATE, power_gate);
REG_WAIT(DOMAIN17_PG_STATUS,
DOMAIN17_PGFSM_PWR_STATUS, pwr_status,
1, 1000);
break;
case 2: /* DSC2 */
REG_UPDATE(DOMAIN18_PG_CONFIG,
DOMAIN18_POWER_GATE, power_gate);
REG_WAIT(DOMAIN18_PG_STATUS,
DOMAIN18_PGFSM_PWR_STATUS, pwr_status,
1, 1000);
break;
case 3: /* DSC3 */
REG_UPDATE(DOMAIN19_PG_CONFIG,
DOMAIN19_POWER_GATE, power_gate);
REG_WAIT(DOMAIN19_PG_STATUS,
DOMAIN19_PGFSM_PWR_STATUS, pwr_status,
1, 1000);
break;
case 4: /* DSC4 */
REG_UPDATE(DOMAIN20_PG_CONFIG,
DOMAIN20_POWER_GATE, power_gate);
REG_WAIT(DOMAIN20_PG_STATUS,
DOMAIN20_PGFSM_PWR_STATUS, pwr_status,
1, 1000);
break;
case 5: /* DSC5 */
REG_UPDATE(DOMAIN21_PG_CONFIG,
DOMAIN21_POWER_GATE, power_gate);
REG_WAIT(DOMAIN21_PG_STATUS,
DOMAIN21_PGFSM_PWR_STATUS, pwr_status,
1, 1000);
break;
default:
BREAK_TO_DEBUGGER();
break;
}
}
#endif
static void dcn20_dpp_pg_control(
struct dce_hwseq *hws,
......@@ -1457,10 +1530,30 @@ bool dcn20_dmdata_status_done(struct pipe_ctx *pipe_ctx)
static void dcn20_disable_stream_gating(struct dc *dc, struct pipe_ctx *pipe_ctx)
{
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
struct dce_hwseq *hws = dc->hwseq;
struct pipe_ctx *bot_odm_pipe = dc_res_get_odm_bottom_pipe(pipe_ctx);
if (pipe_ctx->stream_res.dsc) {
dcn20_dsc_pg_control(hws, pipe_ctx->stream_res.dsc->inst, true);
if (bot_odm_pipe)
dcn20_dsc_pg_control(hws, bot_odm_pipe->stream_res.dsc->inst, true);
}
#endif
}
static void dcn20_enable_stream_gating(struct dc *dc, struct pipe_ctx *pipe_ctx)
{
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
struct dce_hwseq *hws = dc->hwseq;
struct pipe_ctx *bot_odm_pipe = dc_res_get_odm_bottom_pipe(pipe_ctx);
if (pipe_ctx->stream_res.dsc) {
dcn20_dsc_pg_control(hws, pipe_ctx->stream_res.dsc->inst, false);
if (bot_odm_pipe)
dcn20_dsc_pg_control(hws, bot_odm_pipe->stream_res.dsc->inst, false);
}
#endif
}
void dcn20_set_dmdata_attributes(struct pipe_ctx *pipe_ctx)
......
......@@ -168,6 +168,10 @@ static struct mpll_cfg dcn2_mpll_cfg[] = {
void enc2_fec_set_enable(struct link_encoder *enc, bool enable)
{
struct dcn10_link_encoder *enc10 = TO_DCN10_LINK_ENC(enc);
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
DC_LOG_DSC("%s FEC at link encoder inst %d",
enable ? "Enabling" : "Disabling", enc->id.enum_id);
#endif
REG_UPDATE(DP_DPHY_CNTL, DPHY_FEC_EN, enable);
}
......@@ -188,6 +192,19 @@ bool enc2_fec_is_active(struct link_encoder *enc)
return (active != 0);
}
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
/* this function reads dsc related register fields to be logged later in dcn10_log_hw_state
* into a dcn_dsc_state struct.
*/
void link_enc2_read_state(struct link_encoder *enc, struct link_enc_state *s)
{
struct dcn10_link_encoder *enc10 = TO_DCN10_LINK_ENC(enc);
REG_GET(DP_DPHY_CNTL, DPHY_FEC_EN, &s->dphy_fec_en);
REG_GET(DP_DPHY_CNTL, DPHY_FEC_READY_SHADOW, &s->dphy_fec_ready_shadow);
REG_GET(DP_DPHY_CNTL, DPHY_FEC_ACTIVE_STATUS, &s->dphy_fec_active_status);
}
#endif
static bool update_cfg_data(
struct dcn10_link_encoder *enc10,
......@@ -298,6 +315,9 @@ void enc2_hw_init(struct link_encoder *enc)
}
static const struct link_encoder_funcs dcn20_link_enc_funcs = {
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
.read_state = link_enc2_read_state,
#endif
.validate_output_with_stream =
dcn10_link_encoder_validate_output_with_stream,
.hw_init = enc2_hw_init,
......
......@@ -151,6 +151,9 @@ void enc2_fec_set_ready(struct link_encoder *enc, bool ready);
bool enc2_fec_is_active(struct link_encoder *enc);
void enc2_hw_init(struct link_encoder *enc);
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
void link_enc2_read_state(struct link_encoder *enc, struct link_enc_state *s);
#endif
void dcn20_link_encoder_construct(
struct dcn20_link_encoder *enc20,
......
......@@ -167,6 +167,41 @@ void optc2_set_gsl_source_select(
}
}
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
/* DSC encoder frame start controls: x = h position, line_num = # of lines from vstartup */
void optc2_set_dsc_encoder_frame_start(struct timing_generator *optc,
int x_position,
int line_num)
{
struct optc *optc1 = DCN10TG_FROM_TG(optc);
REG_SET_2(OTG_DSC_START_POSITION, 0,
OTG_DSC_START_POSITION_X, x_position,
OTG_DSC_START_POSITION_LINE_NUM, line_num);
}
/* Set DSC-related configuration.
* dsc_mode: 0 disables DSC, other values enable DSC in specified format
* sc_bytes_per_pixel: Bytes per pixel in u3.28 format
* dsc_slice_width: Slice width in pixels
*/
void optc2_set_dsc_config(struct timing_generator *optc,
enum optc_dsc_mode dsc_mode,
uint32_t dsc_bytes_per_pixel,
uint32_t dsc_slice_width)
{
struct optc *optc1 = DCN10TG_FROM_TG(optc);
REG_UPDATE(OPTC_DATA_FORMAT_CONTROL,
OPTC_DSC_MODE, dsc_mode);
REG_SET(OPTC_BYTES_PER_PIXEL, 0,
OPTC_DSC_BYTES_PER_PIXEL, dsc_bytes_per_pixel);
REG_UPDATE(OPTC_WIDTH_CONTROL,
OPTC_DSC_SLICE_WIDTH, dsc_slice_width);
}
#endif
/**
* PTI i think is already done somewhere else for 2ka
......@@ -390,6 +425,9 @@ static struct timing_generator_funcs dcn20_tg_funcs = {
.setup_global_swap_lock = NULL,
.get_crc = optc1_get_crc,
.configure_crc = optc1_configure_crc,
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
.set_dsc_config = optc2_set_dsc_config,
#endif
.set_dwb_source = optc2_set_dwb_source,
.set_odm_bypass = optc2_set_odm_bypass,
.set_odm_combine = optc2_set_odm_combine,
......
......@@ -83,6 +83,12 @@ void optc2_set_gsl_source_select(struct timing_generator *optc,
int group_idx,
uint32_t gsl_ready_signal);
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
void optc2_set_dsc_config(struct timing_generator *optc,
enum optc_dsc_mode dsc_mode,
uint32_t dsc_bytes_per_pixel,
uint32_t dsc_slice_width);
#endif
void optc2_set_odm_bypass(struct timing_generator *optc,
const struct dc_crtc_timing *dc_crtc_timing);
......
......@@ -42,6 +42,10 @@
#include "dce110/dce110_hw_sequencer.h"
#include "dcn20_opp.h"
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
#include "dcn20_dsc.h"
#endif
#include "dcn20_link_encoder.h"
#include "dcn20_stream_encoder.h"
#include "dce/dce_clock_source.h"
......@@ -83,7 +87,11 @@ struct _vcs_dpi_ip_params_st dcn2_0_ip = {
.hostvm_max_page_table_levels = 4,
.hostvm_cached_page_table_levels = 0,
.pte_group_size_bytes = 2048,
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
.num_dsc = 6,
#else
.num_dsc = 0,
#endif
.rob_buffer_size_kbytes = 168,
.det_buffer_size_kbytes = 164,
.dpte_buffer_size_in_pte_reqs_luma = 84,
......@@ -572,6 +580,29 @@ static const struct dcn20_vmid_mask vmid_masks = {
DCN20_VMID_MASK_SH_LIST(_MASK)
};
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
#define dsc_regsDCN20(id)\
[id] = {\
DSC_REG_LIST_DCN20(id)\
}
static const struct dcn20_dsc_registers dsc_regs[] = {
dsc_regsDCN20(0),
dsc_regsDCN20(1),
dsc_regsDCN20(2),
dsc_regsDCN20(3),
dsc_regsDCN20(4),
dsc_regsDCN20(5)
};
static const struct dcn20_dsc_shift dsc_shift = {
DSC_REG_LIST_SH_MASK_DCN20(__SHIFT)
};
static const struct dcn20_dsc_mask dsc_mask = {
DSC_REG_LIST_SH_MASK_DCN20(_MASK)
};
#endif
static const struct dccg_registers dccg_regs = {
DCCG_REG_LIST_DCN2()
......@@ -595,6 +626,9 @@ static const struct resource_caps res_cap_nv10 = {
.num_dwb = 1,
.num_ddc = 6,
.num_vmid = 16,
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
.num_dsc = 6,
#endif
};
static const struct dc_plane_cap plane_cap = {
......@@ -961,6 +995,30 @@ void dcn20_clock_source_destroy(struct clock_source **clk_src)
*clk_src = NULL;
}
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
struct display_stream_compressor *dcn20_dsc_create(
struct dc_context *ctx, uint32_t inst)
{
struct dcn20_dsc *dsc =
kzalloc(sizeof(struct dcn20_dsc), GFP_KERNEL);
if (!dsc) {
BREAK_TO_DEBUGGER();
return NULL;
}
dsc2_construct(dsc, ctx, inst, &dsc_regs[inst], &dsc_shift, &dsc_mask);
return &dsc->base;
}
void dcn20_dsc_destroy(struct display_stream_compressor **dsc)
{
kfree(container_of(*dsc, struct dcn20_dsc, base));
*dsc = NULL;
}
#endif
static void destruct(struct dcn20_resource_pool *pool)
{
......@@ -973,6 +1031,12 @@ static void destruct(struct dcn20_resource_pool *pool)
}
}
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
for (i = 0; i < pool->base.res_cap->num_dsc; i++) {
if (pool->base.dscs[i] != NULL)
dcn20_dsc_destroy(&pool->base.dscs[i]);
}
#endif
if (pool->base.mpc != NULL) {
kfree(TO_DCN20_MPC(pool->base.mpc));
......@@ -1164,6 +1228,39 @@ enum dc_status dcn20_build_mapped_resource(const struct dc *dc, struct dc_state
return status;
}
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
static struct display_stream_compressor *acquire_dsc(struct resource_context *res_ctx,
const struct resource_pool *pool)
{
int i;
struct display_stream_compressor *dsc = NULL;
/* Find first free DSC */
for (i = 0; i < pool->res_cap->num_dsc; i++)
if (!res_ctx->is_dsc_acquired[i]) {
dsc = pool->dscs[i];
res_ctx->is_dsc_acquired[i] = true;
break;
}
return dsc;
}
static void release_dsc(struct resource_context *res_ctx,
const struct resource_pool *pool,
const struct display_stream_compressor *dsc)
{
int i;
for (i = 0; i < pool->res_cap->num_dsc; i++)
if (pool->dscs[i] == dsc) {
res_ctx->is_dsc_acquired[i] = false;
break;
}
}
#endif
enum dc_status dcn20_add_stream_to_ctx(struct dc *dc, struct dc_state *new_ctx, struct dc_stream_state *dc_stream)
{
......@@ -1174,6 +1271,33 @@ enum dc_status dcn20_add_stream_to_ctx(struct dc *dc, struct dc_state *new_ctx,
if (result == DC_OK)
result = resource_map_phy_clock_resources(dc, new_ctx, dc_stream);
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
/* Get a DSC if required and available */
if (result == DC_OK) {
int i;
const struct resource_pool *pool = dc->res_pool;
bool is_add_dsc = true;
for (i = 0; i < dc->res_pool->pipe_count; i++) {
struct pipe_ctx *pipe_ctx = &new_ctx->res_ctx.pipe_ctx[i];
if (pipe_ctx->stream != dc_stream)
continue;
if (is_add_dsc) {
pipe_ctx->stream_res.dsc = acquire_dsc(&new_ctx->res_ctx, pool);
/* The number of DSCs can be less than the number of pipes */
if (!pipe_ctx->stream_res.dsc) {
dm_output_to_console("No DSCs available\n");
result = DC_NO_DSC_RESOURCE;
}
}
break;
}
}
#endif
if (result == DC_OK)
result = dcn20_build_mapped_resource(dc, new_ctx, dc_stream);
......@@ -1198,6 +1322,18 @@ enum dc_status dcn20_remove_stream_from_ctx(struct dc *dc, struct dc_state *new_
if (!pipe_ctx)
return DC_ERROR_UNEXPECTED;
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
if (pipe_ctx->stream_res.dsc) {
struct pipe_ctx *odm_pipe = dc_res_get_odm_bottom_pipe(pipe_ctx);
release_dsc(&new_ctx->res_ctx, dc->res_pool, pipe_ctx->stream_res.dsc);
pipe_ctx->stream_res.dsc = NULL;
if (odm_pipe) {
release_dsc(&new_ctx->res_ctx, dc->res_pool, odm_pipe->stream_res.dsc);
odm_pipe->stream_res.dsc = NULL;
}
}
#endif
return DC_OK;
}
......@@ -1331,6 +1467,14 @@ static bool dcn20_split_stream_for_combine(
sd->recout.x = 0;
}
secondary_pipe->stream_res.opp = pool->opps[secondary_pipe->pipe_idx];
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
if (is_add_dsc) {
secondary_pipe->stream_res.dsc = acquire_dsc(res_ctx, pool);
ASSERT(secondary_pipe->stream_res.dsc);
if (secondary_pipe->stream_res.dsc == NULL)
return false;
}
#endif
} else {
ASSERT(primary_pipe->plane_state);
resource_build_scaling_params(primary_pipe);
......@@ -1410,6 +1554,11 @@ int dcn20_populate_dml_pipes_from_context(
pipes[pipe_cnt].pipe.src.dcc = 0;
pipes[pipe_cnt].pipe.src.vm = 0;*/
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
pipes[pipe_cnt].dout.dsc_enable = res_ctx->pipe_ctx[i].stream->timing.flags.DSC;
/* todo: rotation?*/
pipes[pipe_cnt].dout.dsc_slices = res_ctx->pipe_ctx[i].stream->timing.dsc_cfg.num_slices_h;
#endif
if (res_ctx->pipe_ctx[i].stream->use_dynamic_meta) {
pipes[pipe_cnt].pipe.src.dynamic_metadata_enable = true;
/* 1/2 vblank */
......@@ -1751,6 +1900,10 @@ bool dcn20_validate_bandwidth(struct dc *dc,
hsplit_pipe->stream = NULL;
hsplit_pipe->top_pipe = NULL;
hsplit_pipe->bottom_pipe = NULL;
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
if (hsplit_pipe->stream_res.dsc && hsplit_pipe->stream_res.dsc != pipe->stream_res.dsc)
release_dsc(&context->res_ctx, dc->res_pool, hsplit_pipe->stream_res.dsc);
#endif
/* Clear plane_res and stream_res */
memset(&hsplit_pipe->plane_res, 0, sizeof(hsplit_pipe->plane_res));
memset(&hsplit_pipe->stream_res, 0, sizeof(hsplit_pipe->stream_res));
......@@ -1997,6 +2150,10 @@ bool dcn20_validate_bandwidth(struct dc *dc,
context->bw_ctx.bw.dcn.clk.dppclk_khz = pipes[pipe_idx].clks_cfg.dppclk_mhz * 1000;
context->res_ctx.pipe_ctx[i].plane_res.bw.dppclk_khz =
pipes[pipe_idx].clks_cfg.dppclk_mhz * 1000;
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
context->res_ctx.pipe_ctx[i].plane_res.bw.dscclk_khz =
context->bw_ctx.dml.vba.DSCCLK_calculated[pipe_idx] * 1000;
#endif
context->res_ctx.pipe_ctx[i].pipe_dlg_param = pipes[pipe_idx].pipe.dest;
pipe_idx++;
}
......@@ -2045,6 +2202,23 @@ enum dc_status dcn20_validate_global(struct dc *dc, struct dc_state *new_ctx)
if (pipe_ctx->stream != stream)
continue;
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
if (stream->timing.flags.DSC) {
if (pipe_ctx->stream_res.dsc != NULL) {
struct dsc_config dsc_cfg;
dsc_cfg.pic_width = stream->timing.h_addressable + stream->timing.h_border_left + stream->timing.h_border_right;
dsc_cfg.pic_height = stream->timing.v_addressable + stream->timing.v_border_top + stream->timing.v_border_bottom;
dsc_cfg.pixel_encoding = stream->timing.pixel_encoding;
dsc_cfg.color_depth = stream->timing.display_color_depth;
dsc_cfg.dc_dsc_cfg = stream->timing.dsc_cfg;
if (!pipe_ctx->stream_res.dsc->funcs->dsc_validate_stream(pipe_ctx->stream_res.dsc, &dsc_cfg))
result = DC_FAIL_DSC_VALIDATE;
} else
result = DC_FAIL_DSC_VALIDATE; // DSC enabled for this stream, but no free DSCs available
}
#endif
}
}
......@@ -2715,6 +2889,16 @@ static bool construct(
goto create_fail;
}
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
for (i = 0; i < pool->base.res_cap->num_dsc; i++) {
pool->base.dscs[i] = dcn20_dsc_create(ctx, i);
if (pool->base.dscs[i] == NULL) {
BREAK_TO_DEBUGGER();
dm_error("DC: failed to create display stream compressor %d!\n", i);
goto create_fail;
}
}
#endif
if (!resource_construct(num_virtual_links, dc, &pool->base,
(!IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment) ?
......
......@@ -203,7 +203,165 @@ static void enc2_stream_encoder_stop_hdmi_info_packets(
HDMI_GENERIC7_LINE, 0);
}
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
struct dc_info_packet_128 {
bool valid;
uint8_t hb0;
uint8_t hb1;
uint8_t hb2;
uint8_t hb3;
uint8_t sb[128];
};
/* Update GSP7 SDP 128 byte long */
static void enc2_send_gsp7_128_info_packet(
struct dcn10_stream_encoder *enc1,
const struct dc_info_packet_128 *info_packet)
{
uint32_t i;
/* TODOFPGA Figure out a proper number for max_retries polling for lock
* use 50 for now.
*/
uint32_t max_retries = 50;
const uint32_t *content = (const uint32_t *) &info_packet->sb[0];
ASSERT(info_packet->hb1 == DC_DP_INFOFRAME_TYPE_PPS);
/* Configure for PPS packet size (128 bytes) */
REG_UPDATE(DP_SEC_CNTL2, DP_SEC_GSP7_PPS, 1);
/* We need turn on clock before programming AFMT block*/
REG_UPDATE(AFMT_CNTL, AFMT_AUDIO_CLOCK_EN, 1);
/* Poll dig_update_lock is not locked -> asic internal signal
* assumes otg master lock will unlock it
*/
/*REG_WAIT(AFMT_VBI_PACKET_CONTROL, AFMT_GENERIC_LOCK_STATUS, 0, 10, max_retries);*/
/* Wait for HW/SW GSP memory access conflict to go away */
REG_WAIT(AFMT_VBI_PACKET_CONTROL, AFMT_GENERIC_CONFLICT,
0, 10, max_retries);
/* Clear HW/SW memory access conflict flag */
REG_UPDATE(AFMT_VBI_PACKET_CONTROL, AFMT_GENERIC_CONFLICT_CLR, 1);
/* write generic packet header */
REG_UPDATE(AFMT_VBI_PACKET_CONTROL, AFMT_GENERIC_INDEX, 7);
REG_SET_4(AFMT_GENERIC_HDR, 0,
AFMT_GENERIC_HB0, info_packet->hb0,
AFMT_GENERIC_HB1, info_packet->hb1,
AFMT_GENERIC_HB2, info_packet->hb2,
AFMT_GENERIC_HB3, info_packet->hb3);
/* Write generic packet content 128 bytes long. Four sets are used (indexes 7
* through 10) to fit 128 bytes.
*/
for (i = 0; i < 4; i++) {
uint32_t packet_index = 7 + i;
REG_UPDATE(AFMT_VBI_PACKET_CONTROL, AFMT_GENERIC_INDEX, packet_index);
REG_WRITE(AFMT_GENERIC_0, *content++);
REG_WRITE(AFMT_GENERIC_1, *content++);
REG_WRITE(AFMT_GENERIC_2, *content++);
REG_WRITE(AFMT_GENERIC_3, *content++);
REG_WRITE(AFMT_GENERIC_4, *content++);
REG_WRITE(AFMT_GENERIC_5, *content++);
REG_WRITE(AFMT_GENERIC_6, *content++);
REG_WRITE(AFMT_GENERIC_7, *content++);
}
REG_UPDATE(AFMT_VBI_PACKET_CONTROL1, AFMT_GENERIC7_FRAME_UPDATE, 1);
}
/* Set DSC-related configuration.
* dsc_mode: 0 disables DSC, other values enable DSC in specified format
* sc_bytes_per_pixel: Bytes per pixel in u3.28 format
* dsc_slice_width: Slice width in pixels
*/
static void enc2_dp_set_dsc_config(struct stream_encoder *enc,
enum optc_dsc_mode dsc_mode,
uint32_t dsc_bytes_per_pixel,
uint32_t dsc_slice_width,
uint8_t *dsc_packed_pps)
{
struct dcn10_stream_encoder *enc1 = DCN10STRENC_FROM_STRENC(enc);
REG_UPDATE_2(DP_DSC_CNTL,
DP_DSC_MODE, dsc_mode,
DP_DSC_SLICE_WIDTH, dsc_slice_width);
REG_SET(DP_DSC_BYTES_PER_PIXEL, 0,
DP_DSC_BYTES_PER_PIXEL, dsc_bytes_per_pixel);
if (dsc_mode != OPTC_DSC_DISABLED) {
struct dc_info_packet_128 pps_sdp;
ASSERT(dsc_packed_pps);
/* Load PPS into infoframe (SDP) registers */
pps_sdp.valid = true;
pps_sdp.hb0 = 0;
pps_sdp.hb1 = DC_DP_INFOFRAME_TYPE_PPS;
pps_sdp.hb2 = 127;
pps_sdp.hb3 = 0;
memcpy(&pps_sdp.sb[0], dsc_packed_pps, sizeof(pps_sdp.sb));
enc2_send_gsp7_128_info_packet(enc1, &pps_sdp);
/* Enable Generic Stream Packet 7 (GSP) transmission */
//REG_UPDATE(DP_SEC_CNTL,
// DP_SEC_GSP7_ENABLE, 1);
/* SW should make sure VBID[6] update line number is bigger
* than PPS transmit line number
*/
REG_UPDATE(DP_SEC_CNTL6,
DP_SEC_GSP7_LINE_NUM, 2);
REG_UPDATE_2(DP_MSA_VBID_MISC,
DP_VBID6_LINE_REFERENCE, 0,
DP_VBID6_LINE_NUM, 3);
/* Send PPS data at the line number specified above.
* DP spec requires PPS to be sent only when it changes, however since
* decoder has to be able to handle its change on every frame, we're
* sending it always (i.e. on every frame) to reduce the chance it'd be
* missed by decoder. If it turns out required to send PPS only when it
* changes, we can use DP_SEC_GSP7_SEND register.
*/
REG_UPDATE_2(DP_SEC_CNTL,
DP_SEC_GSP7_ENABLE, 1,
DP_SEC_STREAM_ENABLE, 1);
} else {
/* Disable Generic Stream Packet 7 (GSP) transmission */
REG_UPDATE(DP_SEC_CNTL, DP_SEC_GSP7_ENABLE, 0);
REG_UPDATE(DP_SEC_CNTL2, DP_SEC_GSP7_PPS, 0);
}
}
#endif
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
/* this function read dsc related register fields to be logged later in dcn10_log_hw_state
* into a dcn_dsc_state struct.
*/
static void enc2_read_state(struct stream_encoder *enc, struct enc_state *s)
{
struct dcn10_stream_encoder *enc1 = DCN10STRENC_FROM_STRENC(enc);
//if dsc is enabled, continue to read
REG_GET(DP_DSC_CNTL, DP_DSC_MODE, &s->dsc_mode);
if (s->dsc_mode) {
REG_GET(DP_DSC_CNTL, DP_DSC_SLICE_WIDTH, &s->dsc_slice_width);
REG_GET(DP_SEC_CNTL6, DP_SEC_GSP7_LINE_NUM, &s->sec_gsp7_line_num);
REG_GET(DP_SEC_CNTL6, DP_SEC_GSP7_LINE_NUM, &s->sec_gsp7_line_num);
REG_GET(DP_MSA_VBID_MISC, DP_VBID6_LINE_REFERENCE, &s->vbid6_line_reference);
REG_GET(DP_MSA_VBID_MISC, DP_VBID6_LINE_NUM, &s->vbid6_line_num);
REG_GET(DP_SEC_CNTL, DP_SEC_GSP7_ENABLE, &s->sec_gsp7_enable);
REG_GET(DP_SEC_CNTL, DP_SEC_STREAM_ENABLE, &s->sec_stream_enable);
}
}
#endif
/* Set Dynamic Metadata-configuration.
* enable_dme: TRUE: enables Dynamic Metadata Enfine, FALSE: disables DME
......@@ -283,6 +441,10 @@ static bool is_two_pixels_per_containter(const struct dc_crtc_timing *timing)
{
bool two_pix = timing->pixel_encoding == PIXEL_ENCODING_YCBCR420;
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
two_pix = two_pix || (timing->flags.DSC && timing->pixel_encoding == PIXEL_ENCODING_YCBCR422
&& !timing->dsc_cfg.ycbcr422_simple);
#endif
return two_pix;
}
......@@ -416,7 +578,13 @@ static const struct stream_encoder_funcs dcn20_str_enc_funcs = {
.setup_stereo_sync = enc1_setup_stereo_sync,
.set_avmute = enc1_stream_encoder_set_avmute,
.dig_connect_to_otg = enc1_dig_connect_to_otg,
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
.enc_read_state = enc2_read_state,
#endif
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
.dp_set_dsc_config = enc2_dp_set_dsc_config,
#endif
.set_dynamic_metadata = enc2_set_dynamic_metadata,
};
......
......@@ -118,6 +118,13 @@ bool dm_helpers_submit_i2c(
const struct dc_link *link,
struct i2c_command *cmd);
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
bool dm_helpers_dp_write_dsc_enable(
struct dc_context *ctx,
const struct dc_stream_state *stream,
bool enable
);
#endif
bool dm_helpers_is_dp_sink_present(
struct dc_link *link);
......
#
# Makefile for the 'dsc' sub-component of DAL.
CFLAGS_rc_calc.o := -mhard-float -msse -mpreferred-stack-boundary=4
CFLAGS_rc_calc_dpi.o := -mhard-float -msse -mpreferred-stack-boundary=4
CFLAGS_codec_main_amd.o := -mhard-float -msse -mpreferred-stack-boundary=4
CFLAGS_dc_dsc.o := -mhard-float -msse -mpreferred-stack-boundary=4
DSC = dc_dsc.o rc_calc.o rc_calc_dpi.o
AMD_DAL_DSC = $(addprefix $(AMDDALPATH)/dc/dsc/,$(DSC))
AMD_DISPLAY_FILES += $(AMD_DAL_DSC)
This diff is collapsed.
This diff is collapsed.
/*
* Copyright 2018 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
*
*/
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
#include "dc.h"
#include "dsc.h"
#include "dc_hw_types.h"
#include <drm/drm_dp_helper.h>
#define DC_LOGGER \
dsc->ctx->logger
static bool dsc_buff_block_size_from_dpcd(int dpcd_buff_block_size, int *buff_block_size);
static bool dsc_line_buff_depth_from_dpcd(int dpcd_line_buff_bit_depth, int *line_buff_bit_depth);
static bool dsc_throughput_from_dpcd(int dpcd_throughput, int *throughput);
static bool dsc_bpp_increment_div_from_dpcd(int bpp_increment_dpcd, uint32_t *bpp_increment_div);
void dsc_optc_config_log(struct display_stream_compressor *dsc,
struct dsc_optc_config *config)
{
DC_LOG_DSC("Setting optc DSC config at DSC inst %d", dsc->inst);
DC_LOG_DSC("\n\tbytes_per_pixel %d\n\tis_pixel_format_444 %d\n\tslice_width %d",
config->bytes_per_pixel,
config->is_pixel_format_444, config->slice_width);
}
void dsc_config_log(struct display_stream_compressor *dsc,
const struct dsc_config *config)
{
DC_LOG_DSC("Setting DSC Config at DSC inst %d", dsc->inst);
DC_LOG_DSC("\n\tnum_slices_h %d\n\tnum_slices_v %d\n\tbits_per_pixel %d\n\tcolor_depth %d",
config->dc_dsc_cfg.num_slices_h,
config->dc_dsc_cfg.num_slices_v,
config->dc_dsc_cfg.bits_per_pixel,
config->color_depth);
}
bool dsc_parse_dsc_dpcd(const uint8_t *dpcd_dsc_data, struct dsc_dec_dpcd_caps *dsc_sink_caps)
{
dsc_sink_caps->is_dsc_supported = (dpcd_dsc_data[DP_DSC_SUPPORT - DP_DSC_SUPPORT] & DP_DSC_DECOMPRESSION_IS_SUPPORTED) != 0;
if (!dsc_sink_caps->is_dsc_supported)
return true;
dsc_sink_caps->dsc_version = dpcd_dsc_data[DP_DSC_REV - DP_DSC_SUPPORT];
{
int buff_block_size;
int buff_size;
if (!dsc_buff_block_size_from_dpcd(dpcd_dsc_data[DP_DSC_RC_BUF_BLK_SIZE - DP_DSC_SUPPORT], &buff_block_size))
return false;
buff_size = dpcd_dsc_data[DP_DSC_RC_BUF_SIZE - DP_DSC_SUPPORT] + 1;
dsc_sink_caps->rc_buffer_size = buff_size * buff_block_size;
}
dsc_sink_caps->slice_caps1.raw = dpcd_dsc_data[DP_DSC_SLICE_CAP_1 - DP_DSC_SUPPORT];
if (!dsc_line_buff_depth_from_dpcd(dpcd_dsc_data[DP_DSC_LINE_BUF_BIT_DEPTH - DP_DSC_SUPPORT], &dsc_sink_caps->lb_bit_depth))
return false;
dsc_sink_caps->is_block_pred_supported =
(dpcd_dsc_data[DP_DSC_BLK_PREDICTION_SUPPORT - DP_DSC_SUPPORT] & DP_DSC_BLK_PREDICTION_IS_SUPPORTED) != 0;
dsc_sink_caps->edp_max_bits_per_pixel =
dpcd_dsc_data[DP_DSC_MAX_BITS_PER_PIXEL_LOW - DP_DSC_SUPPORT] |
dpcd_dsc_data[DP_DSC_MAX_BITS_PER_PIXEL_HI - DP_DSC_SUPPORT] << 8;
dsc_sink_caps->color_formats.raw = dpcd_dsc_data[DP_DSC_DEC_COLOR_FORMAT_CAP - DP_DSC_SUPPORT];
dsc_sink_caps->color_depth.raw = dpcd_dsc_data[DP_DSC_DEC_COLOR_DEPTH_CAP - DP_DSC_SUPPORT];
{
int dpcd_throughput = dpcd_dsc_data[DP_DSC_PEAK_THROUGHPUT - DP_DSC_SUPPORT];
if (!dsc_throughput_from_dpcd(dpcd_throughput & DP_DSC_THROUGHPUT_MODE_0_MASK, &dsc_sink_caps->throughput_mode_0_mps))
return false;
dpcd_throughput = (dpcd_throughput & DP_DSC_THROUGHPUT_MODE_1_MASK) >> DP_DSC_THROUGHPUT_MODE_1_SHIFT;
if (!dsc_throughput_from_dpcd(dpcd_throughput, &dsc_sink_caps->throughput_mode_1_mps))
return false;
}
dsc_sink_caps->max_slice_width = dpcd_dsc_data[DP_DSC_MAX_SLICE_WIDTH - DP_DSC_SUPPORT] * 320;
dsc_sink_caps->slice_caps2.raw = dpcd_dsc_data[DP_DSC_SLICE_CAP_2 - DP_DSC_SUPPORT];
if (!dsc_bpp_increment_div_from_dpcd(dpcd_dsc_data[DP_DSC_BITS_PER_PIXEL_INC - DP_DSC_SUPPORT], &dsc_sink_caps->bpp_increment_div))
return false;
return true;
}
/* This module's internal functions */
static bool dsc_buff_block_size_from_dpcd(int dpcd_buff_block_size, int *buff_block_size)
{
switch (dpcd_buff_block_size) {
case DP_DSC_RC_BUF_BLK_SIZE_1:
*buff_block_size = 1024;
break;
case DP_DSC_RC_BUF_BLK_SIZE_4:
*buff_block_size = 4 * 1024;
break;
case DP_DSC_RC_BUF_BLK_SIZE_16:
*buff_block_size = 16 * 1024;
break;
case DP_DSC_RC_BUF_BLK_SIZE_64:
*buff_block_size = 64 * 1024;
break;
default: {
dm_error("%s: DPCD DSC buffer size not recoginzed.\n", __func__);
return false;
}
}
return true;
}
static bool dsc_line_buff_depth_from_dpcd(int dpcd_line_buff_bit_depth, int *line_buff_bit_depth)
{
if (0 <= dpcd_line_buff_bit_depth && dpcd_line_buff_bit_depth <= 7)
*line_buff_bit_depth = dpcd_line_buff_bit_depth + 9;
else if (dpcd_line_buff_bit_depth == 8)
*line_buff_bit_depth = 8;
else {
dm_error("%s: DPCD DSC buffer depth not recoginzed.\n", __func__);
return false;
}
return true;
}
static bool dsc_throughput_from_dpcd(int dpcd_throughput, int *throughput)
{
switch (dpcd_throughput) {
case DP_DSC_THROUGHPUT_MODE_0_340:
*throughput = 340;
break;
case DP_DSC_THROUGHPUT_MODE_0_400:
*throughput = 400;
break;
case DP_DSC_THROUGHPUT_MODE_0_450:
*throughput = 450;
break;
case DP_DSC_THROUGHPUT_MODE_0_500:
*throughput = 500;
break;
case DP_DSC_THROUGHPUT_MODE_0_550:
*throughput = 550;
break;
case DP_DSC_THROUGHPUT_MODE_0_600:
*throughput = 600;
break;
case DP_DSC_THROUGHPUT_MODE_0_650:
*throughput = 650;
break;
case DP_DSC_THROUGHPUT_MODE_0_700:
*throughput = 700;
break;
case DP_DSC_THROUGHPUT_MODE_0_750:
*throughput = 750;
break;
case DP_DSC_THROUGHPUT_MODE_0_800:
*throughput = 800;
break;
case DP_DSC_THROUGHPUT_MODE_0_850:
*throughput = 850;
break;
case DP_DSC_THROUGHPUT_MODE_0_900:
*throughput = 900;
break;
case DP_DSC_THROUGHPUT_MODE_0_950:
*throughput = 950;
break;
case DP_DSC_THROUGHPUT_MODE_0_1000:
*throughput = 1000;
break;
default: {
dm_error("%s: DPCD DSC througput mode not recoginzed.\n", __func__);
return false;
}
}
return true;
}
static bool dsc_bpp_increment_div_from_dpcd(int bpp_increment_dpcd, uint32_t *bpp_increment_div)
{
switch (bpp_increment_dpcd) {
case 0:
*bpp_increment_div = 16;
break;
case 1:
*bpp_increment_div = 8;
break;
case 2:
*bpp_increment_div = 4;
break;
case 3:
*bpp_increment_div = 2;
break;
case 4:
*bpp_increment_div = 1;
break;
default: {
dm_error("%s: DPCD DSC bits-per-pixel increment not recoginzed.\n", __func__);
return false;
}
}
return true;
}
#endif // CONFIG_DRM_AMD_DC_DSC_SUPPORT
#if defined(CONFIG_DRM_AMD_DC_DCN2_0)
/*
* Copyright 2017 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 __DSCC_TYPES_H__
#define __DSCC_TYPES_H__
#include <drm/drm_dsc.h>
#ifndef NUM_BUF_RANGES
#define NUM_BUF_RANGES 15
#endif
struct dsc_pps_rc_range {
int range_min_qp;
int range_max_qp;
int range_bpg_offset;
};
struct dsc_parameters {
struct drm_dsc_config pps;
/* Additional parameters for register programming */
uint32_t bytes_per_pixel; /* In u3.28 format */
uint32_t rc_buffer_model_size;
};
int dscc_compute_dsc_parameters(const struct drm_dsc_config *pps, struct dsc_parameters *dsc_params);
#endif
#endif
This diff is collapsed.
This diff is collapsed.
#if defined(CONFIG_DRM_AMD_DC_DCN2_0)
/*
* Copyright 2017 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 __RC_CALC_H__
#define __RC_CALC_H__
#define QP_SET_SIZE 15
typedef int qp_set[QP_SET_SIZE];
struct rc_params {
int rc_quant_incr_limit0;
int rc_quant_incr_limit1;
int initial_fullness_offset;
int initial_xmit_delay;
int first_line_bpg_offset;
int second_line_bpg_offset;
int flatness_min_qp;
int flatness_max_qp;
int flatness_det_thresh;
qp_set qp_min;
qp_set qp_max;
qp_set ofs;
int rc_model_size;
int rc_edge_factor;
int rc_tgt_offset_hi;
int rc_tgt_offset_lo;
int rc_buf_thresh[QP_SET_SIZE - 1];
};
enum colour_mode {
CM_RGB, /* 444 RGB */
CM_444, /* 444 YUV or simple 422 */
CM_422, /* native 422 */
CM_420 /* native 420 */
};
enum bits_per_comp {
BPC_8 = 8,
BPC_10 = 10,
BPC_12 = 12
};
enum max_min {
MM_MIN = 0,
MM_MAX = 1
};
struct qp_entry {
float bpp;
const qp_set qps;
};
typedef struct qp_entry qp_table[];
void calc_rc_params(struct rc_params *rc, enum colour_mode cm, enum bits_per_comp bpc, float bpp, int slice_width, int slice_height, int minor_version);
#endif
#endif
#if defined(CONFIG_DRM_AMD_DC_DSC_SUPPORT)
/*
* Copyright 2012-17 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 "os_types.h"
#include <drm/drm_dsc.h>
#include "dscc_types.h"
#include "rc_calc.h"
double dsc_ceil(double num);
static void copy_pps_fields(struct drm_dsc_config *to, const struct drm_dsc_config *from)
{
to->line_buf_depth = from->line_buf_depth;
to->bits_per_component = from->bits_per_component;
to->convert_rgb = from->convert_rgb;
to->slice_width = from->slice_width;
to->slice_height = from->slice_height;
to->simple_422 = from->simple_422;
to->native_422 = from->native_422;
to->native_420 = from->native_420;
to->pic_width = from->pic_width;
to->pic_height = from->pic_height;
to->rc_tgt_offset_high = from->rc_tgt_offset_high;
to->rc_tgt_offset_low = from->rc_tgt_offset_low;
to->bits_per_pixel = from->bits_per_pixel;
to->rc_edge_factor = from->rc_edge_factor;
to->rc_quant_incr_limit1 = from->rc_quant_incr_limit1;
to->rc_quant_incr_limit0 = from->rc_quant_incr_limit0;
to->initial_xmit_delay = from->initial_xmit_delay;
to->initial_dec_delay = from->initial_dec_delay;
to->block_pred_enable = from->block_pred_enable;
to->first_line_bpg_offset = from->first_line_bpg_offset;
to->second_line_bpg_offset = from->second_line_bpg_offset;
to->initial_offset = from->initial_offset;
memcpy(&to->rc_buf_thresh, &from->rc_buf_thresh, sizeof(from->rc_buf_thresh));
memcpy(&to->rc_range_params, &from->rc_range_params, sizeof(from->rc_range_params));
to->rc_model_size = from->rc_model_size;
to->flatness_min_qp = from->flatness_min_qp;
to->flatness_max_qp = from->flatness_max_qp;
to->initial_scale_value = from->initial_scale_value;
to->scale_decrement_interval = from->scale_decrement_interval;
to->scale_increment_interval = from->scale_increment_interval;
to->nfl_bpg_offset = from->nfl_bpg_offset;
to->nsl_bpg_offset = from->nsl_bpg_offset;
to->slice_bpg_offset = from->slice_bpg_offset;
to->final_offset = from->final_offset;
to->vbr_enable = from->vbr_enable;
to->slice_chunk_size = from->slice_chunk_size;
to->second_line_offset_adj = from->second_line_offset_adj;
to->dsc_version_minor = from->dsc_version_minor;
}
static void copy_rc_to_cfg(struct drm_dsc_config *dsc_cfg, const struct rc_params *rc)
{
int i;
dsc_cfg->rc_quant_incr_limit0 = rc->rc_quant_incr_limit0;
dsc_cfg->rc_quant_incr_limit1 = rc->rc_quant_incr_limit1;
dsc_cfg->initial_offset = rc->initial_fullness_offset;
dsc_cfg->initial_xmit_delay = rc->initial_xmit_delay;
dsc_cfg->first_line_bpg_offset = rc->first_line_bpg_offset;
dsc_cfg->second_line_bpg_offset = rc->second_line_bpg_offset;
dsc_cfg->flatness_min_qp = rc->flatness_min_qp;
dsc_cfg->flatness_max_qp = rc->flatness_max_qp;
for (i = 0; i < QP_SET_SIZE; ++i) {
dsc_cfg->rc_range_params[i].range_min_qp = rc->qp_min[i];
dsc_cfg->rc_range_params[i].range_max_qp = rc->qp_max[i];
/* Truncate 8-bit signed value to 6-bit signed value */
dsc_cfg->rc_range_params[i].range_bpg_offset = 0x3f & rc->ofs[i];
}
dsc_cfg->rc_model_size = rc->rc_model_size;
dsc_cfg->rc_edge_factor = rc->rc_edge_factor;
dsc_cfg->rc_tgt_offset_high = rc->rc_tgt_offset_hi;
dsc_cfg->rc_tgt_offset_low = rc->rc_tgt_offset_lo;
for (i = 0; i < QP_SET_SIZE - 1; ++i)
dsc_cfg->rc_buf_thresh[i] = rc->rc_buf_thresh[i];
}
int dscc_compute_dsc_parameters(const struct drm_dsc_config *pps, struct dsc_parameters *dsc_params)
{
enum colour_mode mode = pps->convert_rgb ? CM_RGB :
(pps->simple_422 ? CM_444 :
(pps->native_422 ? CM_422 :
pps->native_420 ? CM_420 : CM_444));
enum bits_per_comp bpc = (pps->bits_per_component == 8) ? BPC_8 :
(pps->bits_per_component == 10) ? BPC_10 : BPC_12;
float bpp = ((float) pps->bits_per_pixel / 16.0);
int slice_width = pps->slice_width;
int slice_height = pps->slice_height;
int ret;
struct rc_params rc;
struct drm_dsc_config dsc_cfg;
double d_bytes_per_pixel = dsc_ceil(bpp * slice_width / 8.0) / slice_width;
// TODO: Make sure the formula for calculating this is precise (ceiling vs. floor, and at what point they should be applied)
if (pps->native_422 || pps->native_420)
d_bytes_per_pixel /= 2;
dsc_params->bytes_per_pixel = (uint32_t)dsc_ceil(d_bytes_per_pixel * 0x10000000);
/* in native_422 or native_420 modes, the bits_per_pixel is double the target bpp
* (the latter is what calc_rc_params expects)
*/
if (pps->native_422 || pps->native_420)
bpp /= 2.0;
calc_rc_params(&rc, mode, bpc, bpp, slice_width, slice_height, pps->dsc_version_minor);
dsc_params->pps = *pps;
dsc_params->pps.initial_scale_value = 8 * rc.rc_model_size / (rc.rc_model_size - rc.initial_fullness_offset);
copy_pps_fields(&dsc_cfg, &dsc_params->pps);
copy_rc_to_cfg(&dsc_cfg, &rc);
dsc_cfg.mux_word_size = dsc_params->pps.bits_per_component <= 10 ? 48 : 64;
ret = drm_dsc_compute_rc_parameters(&dsc_cfg);
copy_pps_fields(&dsc_params->pps, &dsc_cfg);
dsc_params->rc_buffer_model_size = dsc_cfg.rc_bits;
return ret;
}
#endif
......@@ -43,7 +43,12 @@ enum dc_status {
DC_FAIL_BANDWIDTH_VALIDATE = 13, /* BW and Watermark validation */
DC_FAIL_SCALING = 14,
DC_FAIL_DP_LINK_TRAINING = 15,
#ifdef CONFIG_DRM_AMD_DC_DCN2_0
DC_FAIL_DSC_VALIDATE = 16,
DC_NO_DSC_RESOURCE = 17,
#endif
DC_FAIL_UNSUPPORTED_1 = 18,
DC_ERROR_UNEXPECTED = -1
};
......
......@@ -179,6 +179,9 @@ struct resource_pool {
} gsl_groups;
#endif
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
struct display_stream_compressor *dscs[MAX_PIPES];
#endif
unsigned int pipe_count;
unsigned int underlay_pipe_index;
......@@ -219,10 +222,16 @@ struct resource_pool {
struct dcn_fe_bandwidth {
int dppclk_khz;
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
int dscclk_khz;
#endif
};
struct stream_resource {
struct output_pixel_processor *opp;
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
struct display_stream_compressor *dsc;
#endif
struct timing_generator *tg;
struct stream_encoder *stream_enc;
struct audio *audio;
......
This diff is collapsed.
......@@ -46,6 +46,9 @@ struct resource_caps {
int num_ddc;
#ifdef CONFIG_DRM_AMD_DC_DCN2_0
int num_vmid;
#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
int num_dsc;
#endif
#endif
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment