Commit 688486a4 authored by Dave Airlie's avatar Dave Airlie

Merge tag 'amd-drm-next-5.6-2020-01-10-dp-mst-dsc' of...

Merge tag 'amd-drm-next-5.6-2020-01-10-dp-mst-dsc' of git://people.freedesktop.org/~agd5f/linux into drm-next

amd-drm-next-5.6-2020-01-10-dp-mst-dsc:

drm:
- Add MST helper for PBN calculation of DSC modes
- Parse FEC caps on MST ports
- Add MST DPCD R/W functions
- Add MST helpers for virtual DPCD aux
- Add MST HUB quirk
- Add MST DSC enablement helpers

amdgpu:
- Enable MST DSC
- Add fair share algo for DSC bandwidth calcs
- Fix for 32 bit builds
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
From: Alex Deucher <alexdeucher@gmail.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20200110214328.308549-1-alexander.deucher@amd.com
parents d5d88cd6 485b747e
...@@ -4933,12 +4933,13 @@ static int dm_encoder_helper_atomic_check(struct drm_encoder *encoder, ...@@ -4933,12 +4933,13 @@ static int dm_encoder_helper_atomic_check(struct drm_encoder *encoder,
is_y420); is_y420);
bpp = convert_dc_color_depth_into_bpc(color_depth) * 3; bpp = convert_dc_color_depth_into_bpc(color_depth) * 3;
clock = adjusted_mode->clock; clock = adjusted_mode->clock;
dm_new_connector_state->pbn = drm_dp_calc_pbn_mode(clock, bpp); dm_new_connector_state->pbn = drm_dp_calc_pbn_mode(clock, bpp, false);
} }
dm_new_connector_state->vcpi_slots = drm_dp_atomic_find_vcpi_slots(state, dm_new_connector_state->vcpi_slots = drm_dp_atomic_find_vcpi_slots(state,
mst_mgr, mst_mgr,
mst_port, mst_port,
dm_new_connector_state->pbn); dm_new_connector_state->pbn,
0);
if (dm_new_connector_state->vcpi_slots < 0) { if (dm_new_connector_state->vcpi_slots < 0) {
DRM_DEBUG_ATOMIC("failed finding vcpi slots: %d\n", (int)dm_new_connector_state->vcpi_slots); DRM_DEBUG_ATOMIC("failed finding vcpi slots: %d\n", (int)dm_new_connector_state->vcpi_slots);
return dm_new_connector_state->vcpi_slots; return dm_new_connector_state->vcpi_slots;
...@@ -4951,6 +4952,71 @@ const struct drm_encoder_helper_funcs amdgpu_dm_encoder_helper_funcs = { ...@@ -4951,6 +4952,71 @@ const struct drm_encoder_helper_funcs amdgpu_dm_encoder_helper_funcs = {
.atomic_check = dm_encoder_helper_atomic_check .atomic_check = dm_encoder_helper_atomic_check
}; };
#if defined(CONFIG_DRM_AMD_DC_DCN)
static int dm_update_mst_vcpi_slots_for_dsc(struct drm_atomic_state *state,
struct dc_state *dc_state)
{
struct dc_stream_state *stream = NULL;
struct drm_connector *connector;
struct drm_connector_state *new_con_state, *old_con_state;
struct amdgpu_dm_connector *aconnector;
struct dm_connector_state *dm_conn_state;
int i, j, clock, bpp;
int vcpi, pbn_div, pbn = 0;
for_each_oldnew_connector_in_state(state, connector, old_con_state, new_con_state, i) {
aconnector = to_amdgpu_dm_connector(connector);
if (!aconnector->port)
continue;
if (!new_con_state || !new_con_state->crtc)
continue;
dm_conn_state = to_dm_connector_state(new_con_state);
for (j = 0; j < dc_state->stream_count; j++) {
stream = dc_state->streams[j];
if (!stream)
continue;
if ((struct amdgpu_dm_connector*)stream->dm_stream_context == aconnector)
break;
stream = NULL;
}
if (!stream)
continue;
if (stream->timing.flags.DSC != 1) {
drm_dp_mst_atomic_enable_dsc(state,
aconnector->port,
dm_conn_state->pbn,
0,
false);
continue;
}
pbn_div = dm_mst_get_pbn_divider(stream->link);
bpp = stream->timing.dsc_cfg.bits_per_pixel;
clock = stream->timing.pix_clk_100hz / 10;
pbn = drm_dp_calc_pbn_mode(clock, bpp, true);
vcpi = drm_dp_mst_atomic_enable_dsc(state,
aconnector->port,
pbn, pbn_div,
true);
if (vcpi < 0)
return vcpi;
dm_conn_state->pbn = pbn;
dm_conn_state->vcpi_slots = vcpi;
}
return 0;
}
#endif
static void dm_drm_plane_reset(struct drm_plane *plane) static void dm_drm_plane_reset(struct drm_plane *plane)
{ {
struct dm_plane_state *amdgpu_state = NULL; struct dm_plane_state *amdgpu_state = NULL;
...@@ -7829,6 +7895,29 @@ dm_determine_update_type_for_commit(struct amdgpu_display_manager *dm, ...@@ -7829,6 +7895,29 @@ dm_determine_update_type_for_commit(struct amdgpu_display_manager *dm,
return ret; return ret;
} }
static int add_affected_mst_dsc_crtcs(struct drm_atomic_state *state, struct drm_crtc *crtc)
{
struct drm_connector *connector;
struct drm_connector_state *conn_state;
struct amdgpu_dm_connector *aconnector = NULL;
int i;
for_each_new_connector_in_state(state, connector, conn_state, i) {
if (conn_state->crtc != crtc)
continue;
aconnector = to_amdgpu_dm_connector(connector);
if (!aconnector->port || !aconnector->mst_port)
aconnector = NULL;
else
break;
}
if (!aconnector)
return 0;
return drm_dp_mst_add_affected_dsc_crtcs(state, &aconnector->mst_port->mst_mgr);
}
/** /**
* amdgpu_dm_atomic_check() - Atomic check implementation for AMDgpu DM. * amdgpu_dm_atomic_check() - Atomic check implementation for AMDgpu DM.
* @dev: The DRM device * @dev: The DRM device
...@@ -7881,6 +7970,16 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev, ...@@ -7881,6 +7970,16 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
if (ret) if (ret)
goto fail; goto fail;
if (adev->asic_type >= CHIP_NAVI10) {
for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
if (drm_atomic_crtc_needs_modeset(new_crtc_state)) {
ret = add_affected_mst_dsc_crtcs(state, crtc);
if (ret)
goto fail;
}
}
}
for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
if (!drm_atomic_crtc_needs_modeset(new_crtc_state) && if (!drm_atomic_crtc_needs_modeset(new_crtc_state) &&
!new_crtc_state->color_mgmt_changed && !new_crtc_state->color_mgmt_changed &&
...@@ -7984,11 +8083,6 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev, ...@@ -7984,11 +8083,6 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
if (ret) if (ret)
goto fail; goto fail;
/* Perform validation of MST topology in the state*/
ret = drm_dp_mst_atomic_check(state);
if (ret)
goto fail;
if (state->legacy_cursor_update) { if (state->legacy_cursor_update) {
/* /*
* This is a fast cursor update coming from the plane update * This is a fast cursor update coming from the plane update
...@@ -8057,6 +8151,15 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev, ...@@ -8057,6 +8151,15 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
if (ret) if (ret)
goto fail; goto fail;
#if defined(CONFIG_DRM_AMD_DC_DCN)
if (!compute_mst_dsc_configs_for_state(state, dm_state->context))
goto fail;
ret = dm_update_mst_vcpi_slots_for_dsc(state, dm_state->context);
if (ret)
goto fail;
#endif
if (dc_validate_global_state(dc, dm_state->context, false) != DC_OK) { if (dc_validate_global_state(dc, dm_state->context, false) != DC_OK) {
ret = -EINVAL; ret = -EINVAL;
goto fail; goto fail;
...@@ -8085,6 +8188,10 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev, ...@@ -8085,6 +8188,10 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
dc_retain_state(old_dm_state->context); dc_retain_state(old_dm_state->context);
} }
} }
/* Perform validation of MST topology in the state*/
ret = drm_dp_mst_atomic_check(state);
if (ret)
goto fail;
/* Store the overall update type for use later in atomic check. */ /* Store the overall update type for use later in atomic check. */
for_each_new_crtc_in_state (state, crtc, new_crtc_state, i) { for_each_new_crtc_in_state (state, crtc, new_crtc_state, i) {
......
...@@ -330,6 +330,7 @@ struct amdgpu_dm_connector { ...@@ -330,6 +330,7 @@ struct amdgpu_dm_connector {
struct drm_dp_mst_port *port; struct drm_dp_mst_port *port;
struct amdgpu_dm_connector *mst_port; struct amdgpu_dm_connector *mst_port;
struct amdgpu_encoder *mst_encoder; struct amdgpu_encoder *mst_encoder;
struct drm_dp_aux *dsc_aux;
/* TODO see if we can merge with ddc_bus or make a dm_connector */ /* TODO see if we can merge with ddc_bus or make a dm_connector */
struct amdgpu_i2c_adapter *i2c; struct amdgpu_i2c_adapter *i2c;
......
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
#include "dc.h" #include "dc.h"
#include "amdgpu_dm.h" #include "amdgpu_dm.h"
#include "amdgpu_dm_irq.h" #include "amdgpu_dm_irq.h"
#include "amdgpu_dm_mst_types.h"
#include "dm_helpers.h" #include "dm_helpers.h"
...@@ -516,8 +517,24 @@ bool dm_helpers_dp_write_dsc_enable( ...@@ -516,8 +517,24 @@ bool dm_helpers_dp_write_dsc_enable(
) )
{ {
uint8_t enable_dsc = enable ? 1 : 0; uint8_t enable_dsc = enable ? 1 : 0;
struct amdgpu_dm_connector *aconnector;
if (!stream)
return false;
if (stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) {
aconnector = (struct amdgpu_dm_connector *)stream->dm_stream_context;
if (!aconnector->dsc_aux)
return false;
return (drm_dp_dpcd_write(aconnector->dsc_aux, DP_DSC_ENABLE, &enable_dsc, 1) >= 0);
}
if (stream->signal == SIGNAL_TYPE_DISPLAY_PORT)
return dm_helpers_dp_write_dpcd(ctx, stream->link, DP_DSC_ENABLE, &enable_dsc, 1);
return dm_helpers_dp_write_dpcd(ctx, stream->sink->link, DP_DSC_ENABLE, &enable_dsc, 1); return false;
} }
bool dm_helpers_is_dp_sink_present(struct dc_link *link) bool dm_helpers_is_dp_sink_present(struct dc_link *link)
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include <linux/version.h> #include <linux/version.h>
#include <drm/drm_atomic_helper.h> #include <drm/drm_atomic_helper.h>
#include <drm/drm_dp_mst_helper.h>
#include "dm_services.h" #include "dm_services.h"
#include "amdgpu.h" #include "amdgpu.h"
#include "amdgpu_dm.h" #include "amdgpu_dm.h"
...@@ -39,6 +40,12 @@ ...@@ -39,6 +40,12 @@
#if defined(CONFIG_DEBUG_FS) #if defined(CONFIG_DEBUG_FS)
#include "amdgpu_dm_debugfs.h" #include "amdgpu_dm_debugfs.h"
#endif #endif
#if defined(CONFIG_DRM_AMD_DC_DCN)
#include "dc/dcn20/dcn20_resource.h"
#endif
/* #define TRACE_DPCD */ /* #define TRACE_DPCD */
#ifdef TRACE_DPCD #ifdef TRACE_DPCD
...@@ -180,6 +187,30 @@ static const struct drm_connector_funcs dm_dp_mst_connector_funcs = { ...@@ -180,6 +187,30 @@ static const struct drm_connector_funcs dm_dp_mst_connector_funcs = {
.early_unregister = amdgpu_dm_mst_connector_early_unregister, .early_unregister = amdgpu_dm_mst_connector_early_unregister,
}; };
#if defined(CONFIG_DRM_AMD_DC_DCN)
static bool validate_dsc_caps_on_connector(struct amdgpu_dm_connector *aconnector)
{
struct dc_sink *dc_sink = aconnector->dc_sink;
struct drm_dp_mst_port *port = aconnector->port;
u8 dsc_caps[16] = { 0 };
aconnector->dsc_aux = drm_dp_mst_dsc_aux_for_port(port);
if (!aconnector->dsc_aux)
return false;
if (drm_dp_dpcd_read(aconnector->dsc_aux, DP_DSC_SUPPORT, dsc_caps, 16) < 0)
return false;
if (!dc_dsc_parse_dsc_dpcd(aconnector->dc_link->ctx->dc,
dsc_caps, NULL,
&dc_sink->sink_dsc_caps.dsc_dec_caps))
return false;
return true;
}
#endif
static int dm_dp_mst_get_modes(struct drm_connector *connector) static int dm_dp_mst_get_modes(struct drm_connector *connector)
{ {
struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
...@@ -222,10 +253,16 @@ static int dm_dp_mst_get_modes(struct drm_connector *connector) ...@@ -222,10 +253,16 @@ static int dm_dp_mst_get_modes(struct drm_connector *connector)
/* dc_link_add_remote_sink returns a new reference */ /* dc_link_add_remote_sink returns a new reference */
aconnector->dc_sink = dc_sink; aconnector->dc_sink = dc_sink;
if (aconnector->dc_sink) if (aconnector->dc_sink) {
amdgpu_dm_update_freesync_caps( amdgpu_dm_update_freesync_caps(
connector, aconnector->edid); connector, aconnector->edid);
#if defined(CONFIG_DRM_AMD_DC_DCN)
if (!validate_dsc_caps_on_connector(aconnector))
memset(&aconnector->dc_sink->sink_dsc_caps,
0, sizeof(aconnector->dc_sink->sink_dsc_caps));
#endif
}
} }
drm_connector_update_edid_property( drm_connector_update_edid_property(
...@@ -466,3 +503,384 @@ void amdgpu_dm_initialize_dp_connector(struct amdgpu_display_manager *dm, ...@@ -466,3 +503,384 @@ void amdgpu_dm_initialize_dp_connector(struct amdgpu_display_manager *dm,
aconnector->connector_id); aconnector->connector_id);
} }
int dm_mst_get_pbn_divider(struct dc_link *link)
{
if (!link)
return 0;
return dc_link_bandwidth_kbps(link,
dc_link_get_link_cap(link)) / (8 * 1000 * 54);
}
#if defined(CONFIG_DRM_AMD_DC_DCN)
struct dsc_mst_fairness_params {
struct dc_crtc_timing *timing;
struct dc_sink *sink;
struct dc_dsc_bw_range bw_range;
bool compression_possible;
struct drm_dp_mst_port *port;
};
struct dsc_mst_fairness_vars {
int pbn;
bool dsc_enabled;
int bpp_x16;
};
static int kbps_to_peak_pbn(int kbps)
{
u64 peak_kbps = kbps;
peak_kbps *= 1006;
peak_kbps = div_u64(peak_kbps, 1000);
return (int) DIV_ROUND_UP(peak_kbps * 64, (54 * 8 * 1000));
}
static void set_dsc_configs_from_fairness_vars(struct dsc_mst_fairness_params *params,
struct dsc_mst_fairness_vars *vars,
int count)
{
int i;
for (i = 0; i < count; i++) {
memset(&params[i].timing->dsc_cfg, 0, sizeof(params[i].timing->dsc_cfg));
if (vars[i].dsc_enabled && dc_dsc_compute_config(
params[i].sink->ctx->dc->res_pool->dscs[0],
&params[i].sink->sink_dsc_caps.dsc_dec_caps,
params[i].sink->ctx->dc->debug.dsc_min_slice_height_override,
0,
params[i].timing,
&params[i].timing->dsc_cfg)) {
params[i].timing->flags.DSC = 1;
params[i].timing->dsc_cfg.bits_per_pixel = vars[i].bpp_x16;
} else {
params[i].timing->flags.DSC = 0;
}
}
}
static int bpp_x16_from_pbn(struct dsc_mst_fairness_params param, int pbn)
{
struct dc_dsc_config dsc_config;
u64 kbps;
kbps = div_u64((u64)pbn * 994 * 8 * 54, 64);
dc_dsc_compute_config(
param.sink->ctx->dc->res_pool->dscs[0],
&param.sink->sink_dsc_caps.dsc_dec_caps,
param.sink->ctx->dc->debug.dsc_min_slice_height_override,
(int) kbps, param.timing, &dsc_config);
return dsc_config.bits_per_pixel;
}
static void increase_dsc_bpp(struct drm_atomic_state *state,
struct dc_link *dc_link,
struct dsc_mst_fairness_params *params,
struct dsc_mst_fairness_vars *vars,
int count)
{
int i;
bool bpp_increased[MAX_PIPES];
int initial_slack[MAX_PIPES];
int min_initial_slack;
int next_index;
int remaining_to_increase = 0;
int pbn_per_timeslot;
int link_timeslots_used;
int fair_pbn_alloc;
for (i = 0; i < count; i++) {
if (vars[i].dsc_enabled) {
initial_slack[i] = kbps_to_peak_pbn(params[i].bw_range.max_kbps) - vars[i].pbn;
bpp_increased[i] = false;
remaining_to_increase += 1;
} else {
initial_slack[i] = 0;
bpp_increased[i] = true;
}
}
pbn_per_timeslot = dc_link_bandwidth_kbps(dc_link,
dc_link_get_link_cap(dc_link)) / (8 * 1000 * 54);
while (remaining_to_increase) {
next_index = -1;
min_initial_slack = -1;
for (i = 0; i < count; i++) {
if (!bpp_increased[i]) {
if (min_initial_slack == -1 || min_initial_slack > initial_slack[i]) {
min_initial_slack = initial_slack[i];
next_index = i;
}
}
}
if (next_index == -1)
break;
link_timeslots_used = 0;
for (i = 0; i < count; i++)
link_timeslots_used += DIV_ROUND_UP(vars[i].pbn, pbn_per_timeslot);
fair_pbn_alloc = (63 - link_timeslots_used) / remaining_to_increase * pbn_per_timeslot;
if (initial_slack[next_index] > fair_pbn_alloc) {
vars[next_index].pbn += fair_pbn_alloc;
if (drm_dp_atomic_find_vcpi_slots(state,
params[next_index].port->mgr,
params[next_index].port,
vars[next_index].pbn,\
dm_mst_get_pbn_divider(dc_link)) < 0)
return;
if (!drm_dp_mst_atomic_check(state)) {
vars[next_index].bpp_x16 = bpp_x16_from_pbn(params[next_index], vars[next_index].pbn);
} else {
vars[next_index].pbn -= fair_pbn_alloc;
if (drm_dp_atomic_find_vcpi_slots(state,
params[next_index].port->mgr,
params[next_index].port,
vars[next_index].pbn,
dm_mst_get_pbn_divider(dc_link)) < 0)
return;
}
} else {
vars[next_index].pbn += initial_slack[next_index];
if (drm_dp_atomic_find_vcpi_slots(state,
params[next_index].port->mgr,
params[next_index].port,
vars[next_index].pbn,
dm_mst_get_pbn_divider(dc_link)) < 0)
return;
if (!drm_dp_mst_atomic_check(state)) {
vars[next_index].bpp_x16 = params[next_index].bw_range.max_target_bpp_x16;
} else {
vars[next_index].pbn -= initial_slack[next_index];
if (drm_dp_atomic_find_vcpi_slots(state,
params[next_index].port->mgr,
params[next_index].port,
vars[next_index].pbn,
dm_mst_get_pbn_divider(dc_link)) < 0)
return;
}
}
bpp_increased[next_index] = true;
remaining_to_increase--;
}
}
static void try_disable_dsc(struct drm_atomic_state *state,
struct dc_link *dc_link,
struct dsc_mst_fairness_params *params,
struct dsc_mst_fairness_vars *vars,
int count)
{
int i;
bool tried[MAX_PIPES];
int kbps_increase[MAX_PIPES];
int max_kbps_increase;
int next_index;
int remaining_to_try = 0;
for (i = 0; i < count; i++) {
if (vars[i].dsc_enabled && vars[i].bpp_x16 == params[i].bw_range.max_target_bpp_x16) {
kbps_increase[i] = params[i].bw_range.stream_kbps - params[i].bw_range.max_kbps;
tried[i] = false;
remaining_to_try += 1;
} else {
kbps_increase[i] = 0;
tried[i] = true;
}
}
while (remaining_to_try) {
next_index = -1;
max_kbps_increase = -1;
for (i = 0; i < count; i++) {
if (!tried[i]) {
if (max_kbps_increase == -1 || max_kbps_increase < kbps_increase[i]) {
max_kbps_increase = kbps_increase[i];
next_index = i;
}
}
}
if (next_index == -1)
break;
vars[next_index].pbn = kbps_to_peak_pbn(params[next_index].bw_range.stream_kbps);
if (drm_dp_atomic_find_vcpi_slots(state,
params[next_index].port->mgr,
params[next_index].port,
vars[next_index].pbn,
0) < 0)
return;
if (!drm_dp_mst_atomic_check(state)) {
vars[next_index].dsc_enabled = false;
vars[next_index].bpp_x16 = 0;
} else {
vars[next_index].pbn = kbps_to_peak_pbn(params[next_index].bw_range.max_kbps);
if (drm_dp_atomic_find_vcpi_slots(state,
params[next_index].port->mgr,
params[next_index].port,
vars[next_index].pbn,
dm_mst_get_pbn_divider(dc_link)) < 0)
return;
}
tried[next_index] = true;
remaining_to_try--;
}
}
static bool compute_mst_dsc_configs_for_link(struct drm_atomic_state *state,
struct dc_state *dc_state,
struct dc_link *dc_link)
{
int i;
struct dc_stream_state *stream;
struct dsc_mst_fairness_params params[MAX_PIPES];
struct dsc_mst_fairness_vars vars[MAX_PIPES];
struct amdgpu_dm_connector *aconnector;
int count = 0;
memset(params, 0, sizeof(params));
/* Set up params */
for (i = 0; i < dc_state->stream_count; i++) {
struct dc_dsc_policy dsc_policy = {0};
stream = dc_state->streams[i];
if (stream->link != dc_link)
continue;
stream->timing.flags.DSC = 0;
params[count].timing = &stream->timing;
params[count].sink = stream->sink;
aconnector = (struct amdgpu_dm_connector *)stream->dm_stream_context;
params[count].port = aconnector->port;
params[count].compression_possible = stream->sink->sink_dsc_caps.dsc_dec_caps.is_dsc_supported;
dc_dsc_get_policy_for_timing(params[count].timing, &dsc_policy);
if (!dc_dsc_compute_bandwidth_range(
stream->sink->ctx->dc->res_pool->dscs[0],
stream->sink->ctx->dc->debug.dsc_min_slice_height_override,
dsc_policy.min_target_bpp,
dsc_policy.max_target_bpp,
&stream->sink->sink_dsc_caps.dsc_dec_caps,
&stream->timing, &params[count].bw_range))
params[count].bw_range.stream_kbps = dc_bandwidth_in_kbps_from_timing(&stream->timing);
count++;
}
/* Try no compression */
for (i = 0; i < count; i++) {
vars[i].pbn = kbps_to_peak_pbn(params[i].bw_range.stream_kbps);
vars[i].dsc_enabled = false;
vars[i].bpp_x16 = 0;
if (drm_dp_atomic_find_vcpi_slots(state,
params[i].port->mgr,
params[i].port,
vars[i].pbn,
0) < 0)
return false;
}
if (!drm_dp_mst_atomic_check(state)) {
set_dsc_configs_from_fairness_vars(params, vars, count);
return true;
}
/* Try max compression */
for (i = 0; i < count; i++) {
if (params[i].compression_possible) {
vars[i].pbn = kbps_to_peak_pbn(params[i].bw_range.min_kbps);
vars[i].dsc_enabled = true;
vars[i].bpp_x16 = params[i].bw_range.min_target_bpp_x16;
if (drm_dp_atomic_find_vcpi_slots(state,
params[i].port->mgr,
params[i].port,
vars[i].pbn,
dm_mst_get_pbn_divider(dc_link)) < 0)
return false;
} else {
vars[i].pbn = kbps_to_peak_pbn(params[i].bw_range.stream_kbps);
vars[i].dsc_enabled = false;
vars[i].bpp_x16 = 0;
if (drm_dp_atomic_find_vcpi_slots(state,
params[i].port->mgr,
params[i].port,
vars[i].pbn,
0) < 0)
return false;
}
}
if (drm_dp_mst_atomic_check(state))
return false;
/* Optimize degree of compression */
increase_dsc_bpp(state, dc_link, params, vars, count);
try_disable_dsc(state, dc_link, params, vars, count);
set_dsc_configs_from_fairness_vars(params, vars, count);
return true;
}
bool compute_mst_dsc_configs_for_state(struct drm_atomic_state *state,
struct dc_state *dc_state)
{
int i, j;
struct dc_stream_state *stream;
bool computed_streams[MAX_PIPES];
struct amdgpu_dm_connector *aconnector;
for (i = 0; i < dc_state->stream_count; i++)
computed_streams[i] = false;
for (i = 0; i < dc_state->stream_count; i++) {
stream = dc_state->streams[i];
if (stream->signal != SIGNAL_TYPE_DISPLAY_PORT_MST)
continue;
aconnector = (struct amdgpu_dm_connector *)stream->dm_stream_context;
if (!aconnector || !aconnector->dc_sink)
continue;
if (!aconnector->dc_sink->sink_dsc_caps.dsc_dec_caps.is_dsc_supported)
continue;
if (computed_streams[i])
continue;
mutex_lock(&aconnector->mst_mgr.lock);
if (!compute_mst_dsc_configs_for_link(state, dc_state, stream->link)) {
mutex_unlock(&aconnector->mst_mgr.lock);
return false;
}
mutex_unlock(&aconnector->mst_mgr.lock);
for (j = 0; j < dc_state->stream_count; j++) {
if (dc_state->streams[j]->link == stream->link)
computed_streams[j] = true;
}
}
for (i = 0; i < dc_state->stream_count; i++) {
stream = dc_state->streams[i];
if (stream->timing.flags.DSC == 1)
dcn20_add_dsc_to_stream_resource(stream->ctx->dc, dc_state, stream);
}
return true;
}
#endif
...@@ -29,7 +29,14 @@ ...@@ -29,7 +29,14 @@
struct amdgpu_display_manager; struct amdgpu_display_manager;
struct amdgpu_dm_connector; struct amdgpu_dm_connector;
int dm_mst_get_pbn_divider(struct dc_link *link);
void amdgpu_dm_initialize_dp_connector(struct amdgpu_display_manager *dm, void amdgpu_dm_initialize_dp_connector(struct amdgpu_display_manager *dm,
struct amdgpu_dm_connector *aconnector); struct amdgpu_dm_connector *aconnector);
#if defined(CONFIG_DRM_AMD_DC_DCN)
bool compute_mst_dsc_configs_for_state(struct drm_atomic_state *state,
struct dc_state *dc_state);
#endif
#endif #endif
...@@ -525,6 +525,9 @@ bool dp_set_dsc_pps_sdp(struct pipe_ctx *pipe_ctx, bool enable) ...@@ -525,6 +525,9 @@ bool dp_set_dsc_pps_sdp(struct pipe_ctx *pipe_ctx, bool enable)
struct dsc_config dsc_cfg; struct dsc_config dsc_cfg;
uint8_t dsc_packed_pps[128]; uint8_t dsc_packed_pps[128];
memset(&dsc_cfg, 0, sizeof(dsc_cfg));
memset(dsc_packed_pps, 0, 128);
/* Enable DSC hw block */ /* 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_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.pic_height = stream->timing.v_addressable + stream->timing.v_border_top + stream->timing.v_border_bottom;
......
...@@ -206,6 +206,9 @@ static bool dsc2_get_packed_pps(struct display_stream_compressor *dsc, const str ...@@ -206,6 +206,9 @@ static bool dsc2_get_packed_pps(struct display_stream_compressor *dsc, const str
struct dsc_reg_values dsc_reg_vals; struct dsc_reg_values dsc_reg_vals;
struct dsc_optc_config dsc_optc_cfg; struct dsc_optc_config dsc_optc_cfg;
memset(&dsc_reg_vals, 0, sizeof(dsc_reg_vals));
memset(&dsc_optc_cfg, 0, sizeof(dsc_optc_cfg));
DC_LOG_DSC("Getting packed DSC PPS for DSC Config:"); DC_LOG_DSC("Getting packed DSC PPS for DSC Config:");
dsc_config_log(dsc, dsc_cfg); dsc_config_log(dsc, dsc_cfg);
DC_LOG_DSC("DSC Picture Parameter Set (PPS):"); DC_LOG_DSC("DSC Picture Parameter Set (PPS):");
......
...@@ -1569,7 +1569,7 @@ static void release_dsc(struct resource_context *res_ctx, ...@@ -1569,7 +1569,7 @@ static void release_dsc(struct resource_context *res_ctx,
static enum dc_status add_dsc_to_stream_resource(struct dc *dc, enum dc_status dcn20_add_dsc_to_stream_resource(struct dc *dc,
struct dc_state *dc_ctx, struct dc_state *dc_ctx,
struct dc_stream_state *dc_stream) struct dc_stream_state *dc_stream)
{ {
...@@ -1584,6 +1584,9 @@ static enum dc_status add_dsc_to_stream_resource(struct dc *dc, ...@@ -1584,6 +1584,9 @@ static enum dc_status add_dsc_to_stream_resource(struct dc *dc,
if (pipe_ctx->stream != dc_stream) if (pipe_ctx->stream != dc_stream)
continue; continue;
if (pipe_ctx->stream_res.dsc)
continue;
acquire_dsc(&dc_ctx->res_ctx, pool, &pipe_ctx->stream_res.dsc, i); acquire_dsc(&dc_ctx->res_ctx, pool, &pipe_ctx->stream_res.dsc, i);
/* The number of DSCs can be less than the number of pipes */ /* The number of DSCs can be less than the number of pipes */
...@@ -1632,7 +1635,7 @@ enum dc_status dcn20_add_stream_to_ctx(struct dc *dc, struct dc_state *new_ctx, ...@@ -1632,7 +1635,7 @@ enum dc_status dcn20_add_stream_to_ctx(struct dc *dc, struct dc_state *new_ctx,
/* Get a DSC if required and available */ /* Get a DSC if required and available */
if (result == DC_OK && dc_stream->timing.flags.DSC) if (result == DC_OK && dc_stream->timing.flags.DSC)
result = add_dsc_to_stream_resource(dc, new_ctx, dc_stream); result = dcn20_add_dsc_to_stream_resource(dc, new_ctx, dc_stream);
if (result == DC_OK) if (result == DC_OK)
result = dcn20_build_mapped_resource(dc, new_ctx, dc_stream); result = dcn20_build_mapped_resource(dc, new_ctx, dc_stream);
......
...@@ -157,6 +157,7 @@ void dcn20_calculate_dlg_params( ...@@ -157,6 +157,7 @@ void dcn20_calculate_dlg_params(
enum dc_status dcn20_build_mapped_resource(const struct dc *dc, struct dc_state *context, struct dc_stream_state *stream); enum dc_status dcn20_build_mapped_resource(const struct dc *dc, struct dc_state *context, struct dc_stream_state *stream);
enum dc_status dcn20_add_stream_to_ctx(struct dc *dc, struct dc_state *new_ctx, struct dc_stream_state *dc_stream); enum dc_status dcn20_add_stream_to_ctx(struct dc *dc, struct dc_state *new_ctx, struct dc_stream_state *dc_stream);
enum dc_status dcn20_add_dsc_to_stream_resource(struct dc *dc, struct dc_state *dc_ctx, struct dc_stream_state *dc_stream);
enum dc_status dcn20_remove_stream_from_ctx(struct dc *dc, struct dc_state *new_ctx, struct dc_stream_state *dc_stream); enum dc_status dcn20_remove_stream_from_ctx(struct dc *dc, struct dc_state *new_ctx, struct dc_stream_state *dc_stream);
enum dc_status dcn20_get_default_swizzle_mode(struct dc_plane_state *plane_state); enum dc_status dcn20_get_default_swizzle_mode(struct dc_plane_state *plane_state);
......
...@@ -163,11 +163,7 @@ static ssize_t auxdev_read_iter(struct kiocb *iocb, struct iov_iter *to) ...@@ -163,11 +163,7 @@ static ssize_t auxdev_read_iter(struct kiocb *iocb, struct iov_iter *to)
break; break;
} }
if (aux_dev->aux->is_remote) res = drm_dp_dpcd_read(aux_dev->aux, pos, buf, todo);
res = drm_dp_mst_dpcd_read(aux_dev->aux, pos, buf,
todo);
else
res = drm_dp_dpcd_read(aux_dev->aux, pos, buf, todo);
if (res <= 0) if (res <= 0)
break; break;
...@@ -215,11 +211,7 @@ static ssize_t auxdev_write_iter(struct kiocb *iocb, struct iov_iter *from) ...@@ -215,11 +211,7 @@ static ssize_t auxdev_write_iter(struct kiocb *iocb, struct iov_iter *from)
break; break;
} }
if (aux_dev->aux->is_remote) res = drm_dp_dpcd_write(aux_dev->aux, pos, buf, todo);
res = drm_dp_mst_dpcd_write(aux_dev->aux, pos, buf,
todo);
else
res = drm_dp_dpcd_write(aux_dev->aux, pos, buf, todo);
if (res <= 0) if (res <= 0)
break; break;
......
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#include <drm/drm_dp_helper.h> #include <drm/drm_dp_helper.h>
#include <drm/drm_print.h> #include <drm/drm_print.h>
#include <drm/drm_vblank.h> #include <drm/drm_vblank.h>
#include <drm/drm_dp_mst_helper.h>
#include "drm_crtc_helper_internal.h" #include "drm_crtc_helper_internal.h"
...@@ -266,7 +267,7 @@ static int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 request, ...@@ -266,7 +267,7 @@ static int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 request,
/** /**
* drm_dp_dpcd_read() - read a series of bytes from the DPCD * drm_dp_dpcd_read() - read a series of bytes from the DPCD
* @aux: DisplayPort AUX channel * @aux: DisplayPort AUX channel (SST or MST)
* @offset: address of the (first) register to read * @offset: address of the (first) register to read
* @buffer: buffer to store the register values * @buffer: buffer to store the register values
* @size: number of bytes in @buffer * @size: number of bytes in @buffer
...@@ -295,13 +296,18 @@ ssize_t drm_dp_dpcd_read(struct drm_dp_aux *aux, unsigned int offset, ...@@ -295,13 +296,18 @@ ssize_t drm_dp_dpcd_read(struct drm_dp_aux *aux, unsigned int offset,
* We just have to do it before any DPCD access and hope that the * We just have to do it before any DPCD access and hope that the
* monitor doesn't power down exactly after the throw away read. * monitor doesn't power down exactly after the throw away read.
*/ */
ret = drm_dp_dpcd_access(aux, DP_AUX_NATIVE_READ, DP_DPCD_REV, buffer, if (!aux->is_remote) {
1); ret = drm_dp_dpcd_access(aux, DP_AUX_NATIVE_READ, DP_DPCD_REV,
if (ret != 1) buffer, 1);
goto out; if (ret != 1)
goto out;
}
ret = drm_dp_dpcd_access(aux, DP_AUX_NATIVE_READ, offset, buffer, if (aux->is_remote)
size); ret = drm_dp_mst_dpcd_read(aux, offset, buffer, size);
else
ret = drm_dp_dpcd_access(aux, DP_AUX_NATIVE_READ, offset,
buffer, size);
out: out:
drm_dp_dump_access(aux, DP_AUX_NATIVE_READ, offset, buffer, ret); drm_dp_dump_access(aux, DP_AUX_NATIVE_READ, offset, buffer, ret);
...@@ -311,7 +317,7 @@ EXPORT_SYMBOL(drm_dp_dpcd_read); ...@@ -311,7 +317,7 @@ EXPORT_SYMBOL(drm_dp_dpcd_read);
/** /**
* drm_dp_dpcd_write() - write a series of bytes to the DPCD * drm_dp_dpcd_write() - write a series of bytes to the DPCD
* @aux: DisplayPort AUX channel * @aux: DisplayPort AUX channel (SST or MST)
* @offset: address of the (first) register to write * @offset: address of the (first) register to write
* @buffer: buffer containing the values to write * @buffer: buffer containing the values to write
* @size: number of bytes in @buffer * @size: number of bytes in @buffer
...@@ -328,8 +334,12 @@ ssize_t drm_dp_dpcd_write(struct drm_dp_aux *aux, unsigned int offset, ...@@ -328,8 +334,12 @@ ssize_t drm_dp_dpcd_write(struct drm_dp_aux *aux, unsigned int offset,
{ {
int ret; int ret;
ret = drm_dp_dpcd_access(aux, DP_AUX_NATIVE_WRITE, offset, buffer, if (aux->is_remote)
size); ret = drm_dp_mst_dpcd_write(aux, offset, buffer, size);
else
ret = drm_dp_dpcd_access(aux, DP_AUX_NATIVE_WRITE, offset,
buffer, size);
drm_dp_dump_access(aux, DP_AUX_NATIVE_WRITE, offset, buffer, ret); drm_dp_dump_access(aux, DP_AUX_NATIVE_WRITE, offset, buffer, ret);
return ret; return ret;
} }
...@@ -968,6 +978,19 @@ static void drm_dp_aux_crc_work(struct work_struct *work) ...@@ -968,6 +978,19 @@ static void drm_dp_aux_crc_work(struct work_struct *work)
} }
} }
/**
* drm_dp_remote_aux_init() - minimally initialise a remote aux channel
* @aux: DisplayPort AUX channel
*
* Used for remote aux channel in general. Merely initialize the crc work
* struct.
*/
void drm_dp_remote_aux_init(struct drm_dp_aux *aux)
{
INIT_WORK(&aux->crc_work, drm_dp_aux_crc_work);
}
EXPORT_SYMBOL(drm_dp_remote_aux_init);
/** /**
* drm_dp_aux_init() - minimally initialise an aux channel * drm_dp_aux_init() - minimally initialise an aux channel
* @aux: DisplayPort AUX channel * @aux: DisplayPort AUX channel
...@@ -1155,6 +1178,8 @@ static const struct dpcd_quirk dpcd_quirk_list[] = { ...@@ -1155,6 +1178,8 @@ static const struct dpcd_quirk dpcd_quirk_list[] = {
{ OUI(0x00, 0x10, 0xfa), DEVICE_ID_ANY, false, BIT(DP_DPCD_QUIRK_NO_PSR) }, { OUI(0x00, 0x10, 0xfa), DEVICE_ID_ANY, false, BIT(DP_DPCD_QUIRK_NO_PSR) },
/* CH7511 seems to leave SINK_COUNT zeroed */ /* CH7511 seems to leave SINK_COUNT zeroed */
{ OUI(0x00, 0x00, 0x00), DEVICE_ID('C', 'H', '7', '5', '1', '1'), false, BIT(DP_DPCD_QUIRK_NO_SINK_COUNT) }, { OUI(0x00, 0x00, 0x00), DEVICE_ID('C', 'H', '7', '5', '1', '1'), false, BIT(DP_DPCD_QUIRK_NO_SINK_COUNT) },
/* Synaptics DP1.4 MST hubs can support DSC without virtual DPCD */
{ OUI(0x90, 0xCC, 0x24), DEVICE_ID_ANY, true, BIT(DP_DPCD_QUIRK_DSC_WITHOUT_VIRTUAL_DPCD) },
}; };
#undef OUI #undef OUI
......
...@@ -853,6 +853,7 @@ static bool drm_dp_sideband_parse_enum_path_resources_ack(struct drm_dp_sideband ...@@ -853,6 +853,7 @@ static bool drm_dp_sideband_parse_enum_path_resources_ack(struct drm_dp_sideband
{ {
int idx = 1; int idx = 1;
repmsg->u.path_resources.port_number = (raw->msg[idx] >> 4) & 0xf; repmsg->u.path_resources.port_number = (raw->msg[idx] >> 4) & 0xf;
repmsg->u.path_resources.fec_capable = raw->msg[idx] & 0x1;
idx++; idx++;
if (idx > raw->curlen) if (idx > raw->curlen)
goto fail_len; goto fail_len;
...@@ -2174,6 +2175,7 @@ drm_dp_mst_topology_unlink_port(struct drm_dp_mst_topology_mgr *mgr, ...@@ -2174,6 +2175,7 @@ drm_dp_mst_topology_unlink_port(struct drm_dp_mst_topology_mgr *mgr,
struct drm_dp_mst_port *port) struct drm_dp_mst_port *port)
{ {
mutex_lock(&mgr->lock); mutex_lock(&mgr->lock);
port->parent->num_ports--;
list_del(&port->next); list_del(&port->next);
mutex_unlock(&mgr->lock); mutex_unlock(&mgr->lock);
drm_dp_mst_topology_put_port(port); drm_dp_mst_topology_put_port(port);
...@@ -2198,6 +2200,9 @@ drm_dp_mst_add_port(struct drm_device *dev, ...@@ -2198,6 +2200,9 @@ drm_dp_mst_add_port(struct drm_device *dev,
port->aux.dev = dev->dev; port->aux.dev = dev->dev;
port->aux.is_remote = true; port->aux.is_remote = true;
/* initialize the MST downstream port's AUX crc work queue */
drm_dp_remote_aux_init(&port->aux);
/* /*
* Make sure the memory allocation for our parent branch stays * Make sure the memory allocation for our parent branch stays
* around until our own memory allocation is released * around until our own memory allocation is released
...@@ -2273,6 +2278,7 @@ drm_dp_mst_handle_link_address_port(struct drm_dp_mst_branch *mstb, ...@@ -2273,6 +2278,7 @@ drm_dp_mst_handle_link_address_port(struct drm_dp_mst_branch *mstb,
mutex_lock(&mgr->lock); mutex_lock(&mgr->lock);
drm_dp_mst_topology_get_port(port); drm_dp_mst_topology_get_port(port);
list_add(&port->next, &mstb->ports); list_add(&port->next, &mstb->ports);
mstb->num_ports++;
mutex_unlock(&mgr->lock); mutex_unlock(&mgr->lock);
} }
...@@ -2951,6 +2957,7 @@ drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr, ...@@ -2951,6 +2957,7 @@ drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr,
path_res->avail_payload_bw_number); path_res->avail_payload_bw_number);
port->available_pbn = port->available_pbn =
path_res->avail_payload_bw_number; path_res->avail_payload_bw_number;
port->fec_capable = path_res->fec_capable;
} }
} }
...@@ -4089,6 +4096,7 @@ static int drm_dp_init_vcpi(struct drm_dp_mst_topology_mgr *mgr, ...@@ -4089,6 +4096,7 @@ static int drm_dp_init_vcpi(struct drm_dp_mst_topology_mgr *mgr,
* @mgr: MST topology manager for the port * @mgr: MST topology manager for the port
* @port: port to find vcpi slots for * @port: port to find vcpi slots for
* @pbn: bandwidth required for the mode in PBN * @pbn: bandwidth required for the mode in PBN
* @pbn_div: divider for DSC mode that takes FEC into account
* *
* Allocates VCPI slots to @port, replacing any previous VCPI allocations it * Allocates VCPI slots to @port, replacing any previous VCPI allocations it
* may have had. Any atomic drivers which support MST must call this function * may have had. Any atomic drivers which support MST must call this function
...@@ -4115,11 +4123,12 @@ static int drm_dp_init_vcpi(struct drm_dp_mst_topology_mgr *mgr, ...@@ -4115,11 +4123,12 @@ static int drm_dp_init_vcpi(struct drm_dp_mst_topology_mgr *mgr,
*/ */
int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state, int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state,
struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_topology_mgr *mgr,
struct drm_dp_mst_port *port, int pbn) struct drm_dp_mst_port *port, int pbn,
int pbn_div)
{ {
struct drm_dp_mst_topology_state *topology_state; struct drm_dp_mst_topology_state *topology_state;
struct drm_dp_vcpi_allocation *pos, *vcpi = NULL; struct drm_dp_vcpi_allocation *pos, *vcpi = NULL;
int prev_slots, req_slots; int prev_slots, prev_bw, req_slots;
topology_state = drm_atomic_get_mst_topology_state(state, mgr); topology_state = drm_atomic_get_mst_topology_state(state, mgr);
if (IS_ERR(topology_state)) if (IS_ERR(topology_state))
...@@ -4130,6 +4139,7 @@ int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state, ...@@ -4130,6 +4139,7 @@ int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state,
if (pos->port == port) { if (pos->port == port) {
vcpi = pos; vcpi = pos;
prev_slots = vcpi->vcpi; prev_slots = vcpi->vcpi;
prev_bw = vcpi->pbn;
/* /*
* This should never happen, unless the driver tries * This should never happen, unless the driver tries
...@@ -4145,14 +4155,22 @@ int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state, ...@@ -4145,14 +4155,22 @@ int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state,
break; break;
} }
} }
if (!vcpi) if (!vcpi) {
prev_slots = 0; prev_slots = 0;
prev_bw = 0;
}
req_slots = DIV_ROUND_UP(pbn, mgr->pbn_div); if (pbn_div <= 0)
pbn_div = mgr->pbn_div;
req_slots = DIV_ROUND_UP(pbn, pbn_div);
DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] [MST PORT:%p] VCPI %d -> %d\n", DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] [MST PORT:%p] VCPI %d -> %d\n",
port->connector->base.id, port->connector->name, port->connector->base.id, port->connector->name,
port, prev_slots, req_slots); port, prev_slots, req_slots);
DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] [MST PORT:%p] PBN %d -> %d\n",
port->connector->base.id, port->connector->name,
port, prev_bw, pbn);
/* Add the new allocation to the state */ /* Add the new allocation to the state */
if (!vcpi) { if (!vcpi) {
...@@ -4165,6 +4183,7 @@ int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state, ...@@ -4165,6 +4183,7 @@ int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state,
list_add(&vcpi->next, &topology_state->vcpis); list_add(&vcpi->next, &topology_state->vcpis);
} }
vcpi->vcpi = req_slots; vcpi->vcpi = req_slots;
vcpi->pbn = pbn;
return req_slots; return req_slots;
} }
...@@ -4415,10 +4434,11 @@ EXPORT_SYMBOL(drm_dp_check_act_status); ...@@ -4415,10 +4434,11 @@ EXPORT_SYMBOL(drm_dp_check_act_status);
* drm_dp_calc_pbn_mode() - Calculate the PBN for a mode. * drm_dp_calc_pbn_mode() - Calculate the PBN for a mode.
* @clock: dot clock for the mode * @clock: dot clock for the mode
* @bpp: bpp for the mode. * @bpp: bpp for the mode.
* @dsc: DSC mode. If true, bpp has units of 1/16 of a bit per pixel
* *
* This uses the formula in the spec to calculate the PBN value for a mode. * This uses the formula in the spec to calculate the PBN value for a mode.
*/ */
int drm_dp_calc_pbn_mode(int clock, int bpp) int drm_dp_calc_pbn_mode(int clock, int bpp, bool dsc)
{ {
/* /*
* margin 5300ppm + 300ppm ~ 0.6% as per spec, factor is 1.006 * margin 5300ppm + 300ppm ~ 0.6% as per spec, factor is 1.006
...@@ -4429,7 +4449,16 @@ int drm_dp_calc_pbn_mode(int clock, int bpp) ...@@ -4429,7 +4449,16 @@ int drm_dp_calc_pbn_mode(int clock, int bpp)
* peak_kbps *= (1006/1000) * peak_kbps *= (1006/1000)
* peak_kbps *= (64/54) * peak_kbps *= (64/54)
* peak_kbps *= 8 convert to bytes * peak_kbps *= 8 convert to bytes
*
* If the bpp is in units of 1/16, further divide by 16. Put this
* factor in the numerator rather than the denominator to avoid
* integer overflow
*/ */
if (dsc)
return DIV_ROUND_UP_ULL(mul_u32_u32(clock * (bpp / 16), 64 * 1006),
8 * 54 * 1000 * 1000);
return DIV_ROUND_UP_ULL(mul_u32_u32(clock * bpp, 64 * 1006), return DIV_ROUND_UP_ULL(mul_u32_u32(clock * bpp, 64 * 1006),
8 * 54 * 1000 * 1000); 8 * 54 * 1000 * 1000);
} }
...@@ -4731,9 +4760,61 @@ static void drm_dp_mst_destroy_state(struct drm_private_obj *obj, ...@@ -4731,9 +4760,61 @@ static void drm_dp_mst_destroy_state(struct drm_private_obj *obj,
kfree(mst_state); kfree(mst_state);
} }
static bool drm_dp_mst_port_downstream_of_branch(struct drm_dp_mst_port *port,
struct drm_dp_mst_branch *branch)
{
while (port->parent) {
if (port->parent == branch)
return true;
if (port->parent->port_parent)
port = port->parent->port_parent;
else
break;
}
return false;
}
static inline
int drm_dp_mst_atomic_check_bw_limit(struct drm_dp_mst_branch *branch,
struct drm_dp_mst_topology_state *mst_state)
{
struct drm_dp_mst_port *port;
struct drm_dp_vcpi_allocation *vcpi;
int pbn_limit = 0, pbn_used = 0;
list_for_each_entry(port, &branch->ports, next) {
if (port->mstb)
if (drm_dp_mst_atomic_check_bw_limit(port->mstb, mst_state))
return -ENOSPC;
if (port->available_pbn > 0)
pbn_limit = port->available_pbn;
}
DRM_DEBUG_ATOMIC("[MST BRANCH:%p] branch has %d PBN available\n",
branch, pbn_limit);
list_for_each_entry(vcpi, &mst_state->vcpis, next) {
if (!vcpi->pbn)
continue;
if (drm_dp_mst_port_downstream_of_branch(vcpi->port, branch))
pbn_used += vcpi->pbn;
}
DRM_DEBUG_ATOMIC("[MST BRANCH:%p] branch used %d PBN\n",
branch, pbn_used);
if (pbn_used > pbn_limit) {
DRM_DEBUG_ATOMIC("[MST BRANCH:%p] No available bandwidth\n",
branch);
return -ENOSPC;
}
return 0;
}
static inline int static inline int
drm_dp_mst_atomic_check_topology_state(struct drm_dp_mst_topology_mgr *mgr, drm_dp_mst_atomic_check_vcpi_alloc_limit(struct drm_dp_mst_topology_mgr *mgr,
struct drm_dp_mst_topology_state *mst_state) struct drm_dp_mst_topology_state *mst_state)
{ {
struct drm_dp_vcpi_allocation *vcpi; struct drm_dp_vcpi_allocation *vcpi;
int avail_slots = 63, payload_count = 0; int avail_slots = 63, payload_count = 0;
...@@ -4770,6 +4851,128 @@ drm_dp_mst_atomic_check_topology_state(struct drm_dp_mst_topology_mgr *mgr, ...@@ -4770,6 +4851,128 @@ drm_dp_mst_atomic_check_topology_state(struct drm_dp_mst_topology_mgr *mgr,
return 0; return 0;
} }
/**
* drm_dp_mst_add_affected_dsc_crtcs
* @state: Pointer to the new struct drm_dp_mst_topology_state
* @mgr: MST topology manager
*
* Whenever there is a change in mst topology
* DSC configuration would have to be recalculated
* therefore we need to trigger modeset on all affected
* CRTCs in that topology
*
* See also:
* drm_dp_mst_atomic_enable_dsc()
*/
int drm_dp_mst_add_affected_dsc_crtcs(struct drm_atomic_state *state, struct drm_dp_mst_topology_mgr *mgr)
{
struct drm_dp_mst_topology_state *mst_state;
struct drm_dp_vcpi_allocation *pos;
struct drm_connector *connector;
struct drm_connector_state *conn_state;
struct drm_crtc *crtc;
struct drm_crtc_state *crtc_state;
mst_state = drm_atomic_get_mst_topology_state(state, mgr);
if (IS_ERR(mst_state))
return -EINVAL;
list_for_each_entry(pos, &mst_state->vcpis, next) {
connector = pos->port->connector;
if (!connector)
return -EINVAL;
conn_state = drm_atomic_get_connector_state(state, connector);
if (IS_ERR(conn_state))
return PTR_ERR(conn_state);
crtc = conn_state->crtc;
if (WARN_ON(!crtc))
return -EINVAL;
if (!drm_dp_mst_dsc_aux_for_port(pos->port))
continue;
crtc_state = drm_atomic_get_crtc_state(mst_state->base.state, crtc);
if (IS_ERR(crtc_state))
return PTR_ERR(crtc_state);
DRM_DEBUG_ATOMIC("[MST MGR:%p] Setting mode_changed flag on CRTC %p\n",
mgr, crtc);
crtc_state->mode_changed = true;
}
return 0;
}
EXPORT_SYMBOL(drm_dp_mst_add_affected_dsc_crtcs);
/**
* drm_dp_mst_atomic_enable_dsc - Set DSC Enable Flag to On/Off
* @state: Pointer to the new drm_atomic_state
* @port: Pointer to the affected MST Port
* @pbn: Newly recalculated bw required for link with DSC enabled
* @pbn_div: Divider to calculate correct number of pbn per slot
* @enable: Boolean flag to enable or disable DSC on the port
*
* This function enables DSC on the given Port
* by recalculating its vcpi from pbn provided
* and sets dsc_enable flag to keep track of which
* ports have DSC enabled
*
*/
int drm_dp_mst_atomic_enable_dsc(struct drm_atomic_state *state,
struct drm_dp_mst_port *port,
int pbn, int pbn_div,
bool enable)
{
struct drm_dp_mst_topology_state *mst_state;
struct drm_dp_vcpi_allocation *pos;
bool found = false;
int vcpi = 0;
mst_state = drm_atomic_get_mst_topology_state(state, port->mgr);
if (IS_ERR(mst_state))
return PTR_ERR(mst_state);
list_for_each_entry(pos, &mst_state->vcpis, next) {
if (pos->port == port) {
found = true;
break;
}
}
if (!found) {
DRM_DEBUG_ATOMIC("[MST PORT:%p] Couldn't find VCPI allocation in mst state %p\n",
port, mst_state);
return -EINVAL;
}
if (pos->dsc_enabled == enable) {
DRM_DEBUG_ATOMIC("[MST PORT:%p] DSC flag is already set to %d, returning %d VCPI slots\n",
port, enable, pos->vcpi);
vcpi = pos->vcpi;
}
if (enable) {
vcpi = drm_dp_atomic_find_vcpi_slots(state, port->mgr, port, pbn, pbn_div);
DRM_DEBUG_ATOMIC("[MST PORT:%p] Enabling DSC flag, reallocating %d VCPI slots on the port\n",
port, vcpi);
if (vcpi < 0)
return -EINVAL;
}
pos->dsc_enabled = enable;
return vcpi;
}
EXPORT_SYMBOL(drm_dp_mst_atomic_enable_dsc);
/** /**
* drm_dp_mst_atomic_check - Check that the new state of an MST topology in an * drm_dp_mst_atomic_check - Check that the new state of an MST topology in an
* atomic update is valid * atomic update is valid
...@@ -4798,7 +5001,10 @@ int drm_dp_mst_atomic_check(struct drm_atomic_state *state) ...@@ -4798,7 +5001,10 @@ int drm_dp_mst_atomic_check(struct drm_atomic_state *state)
int i, ret = 0; int i, ret = 0;
for_each_new_mst_mgr_in_state(state, mgr, mst_state, i) { for_each_new_mst_mgr_in_state(state, mgr, mst_state, i) {
ret = drm_dp_mst_atomic_check_topology_state(mgr, mst_state); ret = drm_dp_mst_atomic_check_vcpi_alloc_limit(mgr, mst_state);
if (ret)
break;
ret = drm_dp_mst_atomic_check_bw_limit(mgr->mst_primary, mst_state);
if (ret) if (ret)
break; break;
} }
...@@ -5062,3 +5268,173 @@ static void drm_dp_mst_unregister_i2c_bus(struct drm_dp_aux *aux) ...@@ -5062,3 +5268,173 @@ static void drm_dp_mst_unregister_i2c_bus(struct drm_dp_aux *aux)
{ {
i2c_del_adapter(&aux->ddc); i2c_del_adapter(&aux->ddc);
} }
/**
* drm_dp_mst_is_virtual_dpcd() - Is the given port a virtual DP Peer Device
* @port: The port to check
*
* A single physical MST hub object can be represented in the topology
* by multiple branches, with virtual ports between those branches.
*
* As of DP1.4, An MST hub with internal (virtual) ports must expose
* certain DPCD registers over those ports. See sections 2.6.1.1.1
* and 2.6.1.1.2 of Display Port specification v1.4 for details.
*
* May acquire mgr->lock
*
* Returns:
* true if the port is a virtual DP peer device, false otherwise
*/
static bool drm_dp_mst_is_virtual_dpcd(struct drm_dp_mst_port *port)
{
struct drm_dp_mst_port *downstream_port;
if (!port || port->dpcd_rev < DP_DPCD_REV_14)
return false;
/* Virtual DP Sink (Internal Display Panel) */
if (port->port_num >= 8)
return true;
/* DP-to-HDMI Protocol Converter */
if (port->pdt == DP_PEER_DEVICE_DP_LEGACY_CONV &&
!port->mcs &&
port->ldps)
return true;
/* DP-to-DP */
mutex_lock(&port->mgr->lock);
if (port->pdt == DP_PEER_DEVICE_MST_BRANCHING &&
port->mstb &&
port->mstb->num_ports == 2) {
list_for_each_entry(downstream_port, &port->mstb->ports, next) {
if (downstream_port->pdt == DP_PEER_DEVICE_SST_SINK &&
!downstream_port->input) {
mutex_unlock(&port->mgr->lock);
return true;
}
}
}
mutex_unlock(&port->mgr->lock);
return false;
}
/**
* drm_dp_mst_dsc_aux_for_port() - Find the correct aux for DSC
* @port: The port to check. A leaf of the MST tree with an attached display.
*
* Depending on the situation, DSC may be enabled via the endpoint aux,
* the immediately upstream aux, or the connector's physical aux.
*
* This is both the correct aux to read DSC_CAPABILITY and the
* correct aux to write DSC_ENABLED.
*
* This operation can be expensive (up to four aux reads), so
* the caller should cache the return.
*
* Returns:
* NULL if DSC cannot be enabled on this port, otherwise the aux device
*/
struct drm_dp_aux *drm_dp_mst_dsc_aux_for_port(struct drm_dp_mst_port *port)
{
struct drm_dp_mst_port *immediate_upstream_port;
struct drm_dp_mst_port *fec_port;
struct drm_dp_desc desc = { 0 };
u8 endpoint_fec;
u8 endpoint_dsc;
if (!port)
return NULL;
if (port->parent->port_parent)
immediate_upstream_port = port->parent->port_parent;
else
immediate_upstream_port = NULL;
fec_port = immediate_upstream_port;
while (fec_port) {
/*
* Each physical link (i.e. not a virtual port) between the
* output and the primary device must support FEC
*/
if (!drm_dp_mst_is_virtual_dpcd(fec_port) &&
!fec_port->fec_capable)
return NULL;
fec_port = fec_port->parent->port_parent;
}
/* DP-to-DP peer device */
if (drm_dp_mst_is_virtual_dpcd(immediate_upstream_port)) {
u8 upstream_dsc;
if (drm_dp_dpcd_read(&port->aux,
DP_DSC_SUPPORT, &endpoint_dsc, 1) != 1)
return NULL;
if (drm_dp_dpcd_read(&port->aux,
DP_FEC_CAPABILITY, &endpoint_fec, 1) != 1)
return NULL;
if (drm_dp_dpcd_read(&immediate_upstream_port->aux,
DP_DSC_SUPPORT, &upstream_dsc, 1) != 1)
return NULL;
/* Enpoint decompression with DP-to-DP peer device */
if ((endpoint_dsc & DP_DSC_DECOMPRESSION_IS_SUPPORTED) &&
(endpoint_fec & DP_FEC_CAPABLE) &&
(upstream_dsc & 0x2) /* DSC passthrough */)
return &port->aux;
/* Virtual DPCD decompression with DP-to-DP peer device */
return &immediate_upstream_port->aux;
}
/* Virtual DPCD decompression with DP-to-HDMI or Virtual DP Sink */
if (drm_dp_mst_is_virtual_dpcd(port))
return &port->aux;
/*
* Synaptics quirk
* Applies to ports for which:
* - Physical aux has Synaptics OUI
* - DPv1.4 or higher
* - Port is on primary branch device
* - Not a VGA adapter (DP_DWN_STRM_PORT_TYPE_ANALOG)
*/
if (drm_dp_read_desc(port->mgr->aux, &desc, true))
return NULL;
if (drm_dp_has_quirk(&desc, DP_DPCD_QUIRK_DSC_WITHOUT_VIRTUAL_DPCD) &&
port->mgr->dpcd[DP_DPCD_REV] >= DP_DPCD_REV_14 &&
port->parent == port->mgr->mst_primary) {
u8 downstreamport;
if (drm_dp_dpcd_read(&port->aux, DP_DOWNSTREAMPORT_PRESENT,
&downstreamport, 1) < 0)
return NULL;
if ((downstreamport & DP_DWN_STRM_PORT_PRESENT) &&
((downstreamport & DP_DWN_STRM_PORT_TYPE_MASK)
!= DP_DWN_STRM_PORT_TYPE_ANALOG))
return port->mgr->aux;
}
/*
* The check below verifies if the MST sink
* connected to the GPU is capable of DSC -
* therefore the endpoint needs to be
* both DSC and FEC capable.
*/
if (drm_dp_dpcd_read(&port->aux,
DP_DSC_SUPPORT, &endpoint_dsc, 1) != 1)
return NULL;
if (drm_dp_dpcd_read(&port->aux,
DP_FEC_CAPABILITY, &endpoint_fec, 1) != 1)
return NULL;
if ((endpoint_dsc & DP_DSC_DECOMPRESSION_IS_SUPPORTED) &&
(endpoint_fec & DP_FEC_CAPABLE))
return &port->aux;
return NULL;
}
EXPORT_SYMBOL(drm_dp_mst_dsc_aux_for_port);
...@@ -61,10 +61,11 @@ static int intel_dp_mst_compute_link_config(struct intel_encoder *encoder, ...@@ -61,10 +61,11 @@ static int intel_dp_mst_compute_link_config(struct intel_encoder *encoder,
crtc_state->pipe_bpp = bpp; crtc_state->pipe_bpp = bpp;
crtc_state->pbn = drm_dp_calc_pbn_mode(adjusted_mode->crtc_clock, crtc_state->pbn = drm_dp_calc_pbn_mode(adjusted_mode->crtc_clock,
crtc_state->pipe_bpp); crtc_state->pipe_bpp,
false);
slots = drm_dp_atomic_find_vcpi_slots(state, &intel_dp->mst_mgr, slots = drm_dp_atomic_find_vcpi_slots(state, &intel_dp->mst_mgr,
port, crtc_state->pbn); port, crtc_state->pbn, 0);
if (slots == -EDEADLK) if (slots == -EDEADLK)
return slots; return slots;
if (slots >= 0) if (slots >= 0)
......
...@@ -806,11 +806,11 @@ nv50_msto_atomic_check(struct drm_encoder *encoder, ...@@ -806,11 +806,11 @@ nv50_msto_atomic_check(struct drm_encoder *encoder,
* topology * topology
*/ */
asyh->or.bpc = min(connector->display_info.bpc, 8U); asyh->or.bpc = min(connector->display_info.bpc, 8U);
asyh->dp.pbn = drm_dp_calc_pbn_mode(clock, asyh->or.bpc * 3); asyh->dp.pbn = drm_dp_calc_pbn_mode(clock, asyh->or.bpc * 3, false);
} }
slots = drm_dp_atomic_find_vcpi_slots(state, &mstm->mgr, mstc->port, slots = drm_dp_atomic_find_vcpi_slots(state, &mstm->mgr, mstc->port,
asyh->dp.pbn); asyh->dp.pbn, 0);
if (slots < 0) if (slots < 0)
return slots; return slots;
......
...@@ -518,7 +518,7 @@ static bool radeon_mst_mode_fixup(struct drm_encoder *encoder, ...@@ -518,7 +518,7 @@ static bool radeon_mst_mode_fixup(struct drm_encoder *encoder,
mst_enc = radeon_encoder->enc_priv; mst_enc = radeon_encoder->enc_priv;
mst_enc->pbn = drm_dp_calc_pbn_mode(adjusted_mode->clock, bpp); mst_enc->pbn = drm_dp_calc_pbn_mode(adjusted_mode->clock, bpp, false);
mst_enc->primary->active_device = mst_enc->primary->devices & mst_enc->connector->devices; mst_enc->primary->active_device = mst_enc->primary->devices & mst_enc->connector->devices;
DRM_DEBUG_KMS("setting active device to %08x from %08x %08x for encoder %d\n", DRM_DEBUG_KMS("setting active device to %08x from %08x %08x for encoder %d\n",
......
...@@ -18,15 +18,19 @@ int igt_dp_mst_calc_pbn_mode(void *ignored) ...@@ -18,15 +18,19 @@ int igt_dp_mst_calc_pbn_mode(void *ignored)
int rate; int rate;
int bpp; int bpp;
int expected; int expected;
bool dsc;
} test_params[] = { } test_params[] = {
{ 154000, 30, 689 }, { 154000, 30, 689, false },
{ 234000, 30, 1047 }, { 234000, 30, 1047, false },
{ 297000, 24, 1063 }, { 297000, 24, 1063, false },
{ 332880, 24, 50, true },
{ 324540, 24, 49, true },
}; };
for (i = 0; i < ARRAY_SIZE(test_params); i++) { for (i = 0; i < ARRAY_SIZE(test_params); i++) {
pbn = drm_dp_calc_pbn_mode(test_params[i].rate, pbn = drm_dp_calc_pbn_mode(test_params[i].rate,
test_params[i].bpp); test_params[i].bpp,
test_params[i].dsc);
FAIL(pbn != test_params[i].expected, FAIL(pbn != test_params[i].expected,
"Expected PBN %d for clock %d bpp %d, got %d\n", "Expected PBN %d for clock %d bpp %d, got %d\n",
test_params[i].expected, test_params[i].rate, test_params[i].expected, test_params[i].rate,
......
...@@ -1465,6 +1465,7 @@ int drm_dp_downstream_id(struct drm_dp_aux *aux, char id[6]); ...@@ -1465,6 +1465,7 @@ int drm_dp_downstream_id(struct drm_dp_aux *aux, char id[6]);
void drm_dp_downstream_debug(struct seq_file *m, const u8 dpcd[DP_RECEIVER_CAP_SIZE], void drm_dp_downstream_debug(struct seq_file *m, const u8 dpcd[DP_RECEIVER_CAP_SIZE],
const u8 port_cap[4], struct drm_dp_aux *aux); const u8 port_cap[4], struct drm_dp_aux *aux);
void drm_dp_remote_aux_init(struct drm_dp_aux *aux);
void drm_dp_aux_init(struct drm_dp_aux *aux); void drm_dp_aux_init(struct drm_dp_aux *aux);
int drm_dp_aux_register(struct drm_dp_aux *aux); int drm_dp_aux_register(struct drm_dp_aux *aux);
void drm_dp_aux_unregister(struct drm_dp_aux *aux); void drm_dp_aux_unregister(struct drm_dp_aux *aux);
...@@ -1522,6 +1523,13 @@ enum drm_dp_quirk { ...@@ -1522,6 +1523,13 @@ enum drm_dp_quirk {
* The driver should ignore SINK_COUNT during detection. * The driver should ignore SINK_COUNT during detection.
*/ */
DP_DPCD_QUIRK_NO_SINK_COUNT, DP_DPCD_QUIRK_NO_SINK_COUNT,
/**
* @DP_DPCD_QUIRK_DSC_WITHOUT_VIRTUAL_DPCD:
*
* The device supports MST DSC despite not supporting Virtual DPCD.
* The DSC caps can be read from the physical aux instead.
*/
DP_DPCD_QUIRK_DSC_WITHOUT_VIRTUAL_DPCD,
}; };
/** /**
......
...@@ -156,6 +156,8 @@ struct drm_dp_mst_port { ...@@ -156,6 +156,8 @@ struct drm_dp_mst_port {
* audio-capable. * audio-capable.
*/ */
bool has_audio; bool has_audio;
bool fec_capable;
}; };
/** /**
...@@ -383,6 +385,7 @@ struct drm_dp_port_number_req { ...@@ -383,6 +385,7 @@ struct drm_dp_port_number_req {
struct drm_dp_enum_path_resources_ack_reply { struct drm_dp_enum_path_resources_ack_reply {
u8 port_number; u8 port_number;
bool fec_capable;
u16 full_payload_bw_number; u16 full_payload_bw_number;
u16 avail_payload_bw_number; u16 avail_payload_bw_number;
}; };
...@@ -499,6 +502,8 @@ struct drm_dp_payload { ...@@ -499,6 +502,8 @@ struct drm_dp_payload {
struct drm_dp_vcpi_allocation { struct drm_dp_vcpi_allocation {
struct drm_dp_mst_port *port; struct drm_dp_mst_port *port;
int vcpi; int vcpi;
int pbn;
bool dsc_enabled;
struct list_head next; struct list_head next;
}; };
...@@ -727,8 +732,7 @@ bool drm_dp_mst_port_has_audio(struct drm_dp_mst_topology_mgr *mgr, ...@@ -727,8 +732,7 @@ bool drm_dp_mst_port_has_audio(struct drm_dp_mst_topology_mgr *mgr,
struct edid *drm_dp_mst_get_edid(struct drm_connector *connector, struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port); struct edid *drm_dp_mst_get_edid(struct drm_connector *connector, struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port);
int drm_dp_calc_pbn_mode(int clock, int bpp); int drm_dp_calc_pbn_mode(int clock, int bpp, bool dsc);
bool drm_dp_mst_allocate_vcpi(struct drm_dp_mst_topology_mgr *mgr, bool drm_dp_mst_allocate_vcpi(struct drm_dp_mst_topology_mgr *mgr,
struct drm_dp_mst_port *port, int pbn, int slots); struct drm_dp_mst_port *port, int pbn, int slots);
...@@ -777,7 +781,15 @@ struct drm_dp_mst_topology_state *drm_atomic_get_mst_topology_state(struct drm_a ...@@ -777,7 +781,15 @@ struct drm_dp_mst_topology_state *drm_atomic_get_mst_topology_state(struct drm_a
int __must_check int __must_check
drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state, drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state,
struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_topology_mgr *mgr,
struct drm_dp_mst_port *port, int pbn); struct drm_dp_mst_port *port, int pbn,
int pbn_div);
int drm_dp_mst_atomic_enable_dsc(struct drm_atomic_state *state,
struct drm_dp_mst_port *port,
int pbn, int pbn_div,
bool enable);
int __must_check
drm_dp_mst_add_affected_dsc_crtcs(struct drm_atomic_state *state,
struct drm_dp_mst_topology_mgr *mgr);
int __must_check int __must_check
drm_dp_atomic_release_vcpi_slots(struct drm_atomic_state *state, drm_dp_atomic_release_vcpi_slots(struct drm_atomic_state *state,
struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_topology_mgr *mgr,
...@@ -789,6 +801,8 @@ int __must_check drm_dp_mst_atomic_check(struct drm_atomic_state *state); ...@@ -789,6 +801,8 @@ int __must_check drm_dp_mst_atomic_check(struct drm_atomic_state *state);
void drm_dp_mst_get_port_malloc(struct drm_dp_mst_port *port); void drm_dp_mst_get_port_malloc(struct drm_dp_mst_port *port);
void drm_dp_mst_put_port_malloc(struct drm_dp_mst_port *port); void drm_dp_mst_put_port_malloc(struct drm_dp_mst_port *port);
struct drm_dp_aux *drm_dp_mst_dsc_aux_for_port(struct drm_dp_mst_port *port);
extern const struct drm_private_state_funcs drm_dp_mst_topology_state_funcs; extern const struct drm_private_state_funcs drm_dp_mst_topology_state_funcs;
/** /**
......
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