mirror of
https://github.com/torvalds/linux.git
synced 2024-11-30 08:01:59 +00:00
Merge tag 'topic/mst-suspend-resume-reprobe-2019-10-29-2' of git://anongit.freedesktop.org/drm/drm-misc into drm-next
UAPI Changes: Cross-subsystem Changes: Core Changes: * Handle UP requests asynchronously in the DP MST helpers, fixing hotplug notifications and allowing us to implement suspend/resume reprobing * Add basic suspend/resume reprobing to the DP MST helpers * Improve locking for link address reprobing and connection status request handling in the DP MST helpers * Miscellaneous refactoring in the DP MST helpers * Add a Kconfig option to the DP MST helpers to enable tracking of gets/puts for topology references for debugging purposes Driver Changes: * nouveau: Resume hotplug interrupts earlier, so that sideband messages may be transmitted during resume and thus allow suspend/resume reprobing for DP MST to work * nouveau: Avoid grabbing runtime PM references when handling short DP pulses, so that handling sideband messages in resume codepaths with the DP MST helpers doesn't deadlock us * i915, nouveau, amdgpu, radeon: Use detect_ctx for probing MST connectors, so that we can grab the topology manager's atomic lock Note: there's some amdgpu patches that I didn't realize were pushed upstream already when creating this topic branch. When they fail to apply, you can just ignore and skip them. Signed-off-by: Dave Airlie <airlied@redhat.com> From: Lyude Paul <lyude@redhat.com> Link: https://patchwork.freedesktop.org/patch/msgid/a74c6446bc960190d195a751cb6d8a00a98f3974.camel@redhat.com
This commit is contained in:
commit
57c2af791b
@ -93,6 +93,20 @@ config DRM_KMS_FB_HELPER
|
||||
help
|
||||
FBDEV helpers for KMS drivers.
|
||||
|
||||
config DRM_DEBUG_DP_MST_TOPOLOGY_REFS
|
||||
bool "Enable refcount backtrace history in the DP MST helpers"
|
||||
select STACKDEPOT
|
||||
depends on DRM_KMS_HELPER
|
||||
depends on DEBUG_KERNEL
|
||||
depends on EXPERT
|
||||
help
|
||||
Enables debug tracing for topology refs in DRM's DP MST helpers. A
|
||||
history of each topology reference/dereference will be printed to the
|
||||
kernel log once a port or branch device's topology refcount reaches 0.
|
||||
|
||||
This has the potential to use a lot of memory and print some very
|
||||
large kernel messages. If in doubt, say "N".
|
||||
|
||||
config DRM_FBDEV_EMULATION
|
||||
bool "Enable legacy fbdev support for your modesetting driver"
|
||||
depends on DRM
|
||||
|
@ -1028,7 +1028,7 @@ static void s3_handle_mst(struct drm_device *dev, bool suspend)
|
||||
if (suspend) {
|
||||
drm_dp_mst_topology_mgr_suspend(mgr);
|
||||
} else {
|
||||
ret = drm_dp_mst_topology_mgr_resume(mgr);
|
||||
ret = drm_dp_mst_topology_mgr_resume(mgr, true);
|
||||
if (ret < 0) {
|
||||
drm_dp_mst_topology_mgr_set_mst(mgr, false);
|
||||
need_hotplug = true;
|
||||
@ -1246,7 +1246,7 @@ static int dm_resume(void *handle)
|
||||
*/
|
||||
amdgpu_dm_irq_resume_early(adev);
|
||||
|
||||
/* On resume we need to rewrite the MSTM control bits to enable MST*/
|
||||
/* On resume we need to rewrite the MSTM control bits to enable MST*/
|
||||
s3_handle_mst(ddev, false);
|
||||
|
||||
/* Do detection*/
|
||||
|
@ -123,21 +123,6 @@ static ssize_t dm_dp_aux_transfer(struct drm_dp_aux *aux,
|
||||
return result;
|
||||
}
|
||||
|
||||
static enum drm_connector_status
|
||||
dm_dp_mst_detect(struct drm_connector *connector, bool force)
|
||||
{
|
||||
struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
|
||||
struct amdgpu_dm_connector *master = aconnector->mst_port;
|
||||
|
||||
enum drm_connector_status status =
|
||||
drm_dp_mst_detect_port(
|
||||
connector,
|
||||
&master->mst_mgr,
|
||||
aconnector->port);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static void
|
||||
dm_dp_mst_connector_destroy(struct drm_connector *connector)
|
||||
{
|
||||
@ -175,7 +160,6 @@ amdgpu_dm_mst_connector_early_unregister(struct drm_connector *connector)
|
||||
}
|
||||
|
||||
static const struct drm_connector_funcs dm_dp_mst_connector_funcs = {
|
||||
.detect = dm_dp_mst_detect,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.destroy = dm_dp_mst_connector_destroy,
|
||||
.reset = amdgpu_dm_connector_funcs_reset,
|
||||
@ -250,10 +234,22 @@ dm_mst_atomic_best_encoder(struct drm_connector *connector,
|
||||
return &to_amdgpu_dm_connector(connector)->mst_encoder->base;
|
||||
}
|
||||
|
||||
static int
|
||||
dm_dp_mst_detect(struct drm_connector *connector,
|
||||
struct drm_modeset_acquire_ctx *ctx, bool force)
|
||||
{
|
||||
struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
|
||||
struct amdgpu_dm_connector *master = aconnector->mst_port;
|
||||
|
||||
return drm_dp_mst_detect_port(connector, ctx, &master->mst_mgr,
|
||||
aconnector->port);
|
||||
}
|
||||
|
||||
static const struct drm_connector_helper_funcs dm_dp_mst_connector_helper_funcs = {
|
||||
.get_modes = dm_dp_mst_get_modes,
|
||||
.mode_valid = amdgpu_dm_connector_mode_valid,
|
||||
.atomic_best_encoder = dm_mst_atomic_best_encoder,
|
||||
.detect_ctx = dm_dp_mst_detect,
|
||||
};
|
||||
|
||||
static void amdgpu_dm_encoder_destroy(struct drm_encoder *encoder)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -7625,7 +7625,8 @@ void intel_dp_mst_resume(struct drm_i915_private *dev_priv)
|
||||
if (!intel_dp->can_mst)
|
||||
continue;
|
||||
|
||||
ret = drm_dp_mst_topology_mgr_resume(&intel_dp->mst_mgr);
|
||||
ret = drm_dp_mst_topology_mgr_resume(&intel_dp->mst_mgr,
|
||||
true);
|
||||
if (ret) {
|
||||
intel_dp->is_mst = false;
|
||||
drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr,
|
||||
|
@ -391,20 +391,7 @@ static int intel_dp_mst_get_ddc_modes(struct drm_connector *connector)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static enum drm_connector_status
|
||||
intel_dp_mst_detect(struct drm_connector *connector, bool force)
|
||||
{
|
||||
struct intel_connector *intel_connector = to_intel_connector(connector);
|
||||
struct intel_dp *intel_dp = intel_connector->mst_port;
|
||||
|
||||
if (drm_connector_is_unregistered(connector))
|
||||
return connector_status_disconnected;
|
||||
return drm_dp_mst_detect_port(connector, &intel_dp->mst_mgr,
|
||||
intel_connector->port);
|
||||
}
|
||||
|
||||
static const struct drm_connector_funcs intel_dp_mst_connector_funcs = {
|
||||
.detect = intel_dp_mst_detect,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.atomic_get_property = intel_digital_connector_atomic_get_property,
|
||||
.atomic_set_property = intel_digital_connector_atomic_set_property,
|
||||
@ -465,11 +452,26 @@ static struct drm_encoder *intel_mst_atomic_best_encoder(struct drm_connector *c
|
||||
return &intel_dp->mst_encoders[crtc->pipe]->base.base;
|
||||
}
|
||||
|
||||
static int
|
||||
intel_dp_mst_detect(struct drm_connector *connector,
|
||||
struct drm_modeset_acquire_ctx *ctx, bool force)
|
||||
{
|
||||
struct intel_connector *intel_connector = to_intel_connector(connector);
|
||||
struct intel_dp *intel_dp = intel_connector->mst_port;
|
||||
|
||||
if (drm_connector_is_unregistered(connector))
|
||||
return connector_status_disconnected;
|
||||
|
||||
return drm_dp_mst_detect_port(connector, ctx, &intel_dp->mst_mgr,
|
||||
intel_connector->port);
|
||||
}
|
||||
|
||||
static const struct drm_connector_helper_funcs intel_dp_mst_connector_helper_funcs = {
|
||||
.get_modes = intel_dp_mst_get_modes,
|
||||
.mode_valid = intel_dp_mst_mode_valid,
|
||||
.atomic_best_encoder = intel_mst_atomic_best_encoder,
|
||||
.atomic_check = intel_dp_mst_atomic_check,
|
||||
.detect_ctx = intel_dp_mst_detect,
|
||||
};
|
||||
|
||||
static void intel_dp_mst_encoder_destroy(struct drm_encoder *encoder)
|
||||
|
@ -986,20 +986,11 @@ nv50_mstc_atomic_check(struct drm_connector *connector,
|
||||
return drm_dp_atomic_release_vcpi_slots(state, mgr, mstc->port);
|
||||
}
|
||||
|
||||
static const struct drm_connector_helper_funcs
|
||||
nv50_mstc_help = {
|
||||
.get_modes = nv50_mstc_get_modes,
|
||||
.mode_valid = nv50_mstc_mode_valid,
|
||||
.best_encoder = nv50_mstc_best_encoder,
|
||||
.atomic_best_encoder = nv50_mstc_atomic_best_encoder,
|
||||
.atomic_check = nv50_mstc_atomic_check,
|
||||
};
|
||||
|
||||
static enum drm_connector_status
|
||||
nv50_mstc_detect(struct drm_connector *connector, bool force)
|
||||
static int
|
||||
nv50_mstc_detect(struct drm_connector *connector,
|
||||
struct drm_modeset_acquire_ctx *ctx, bool force)
|
||||
{
|
||||
struct nv50_mstc *mstc = nv50_mstc(connector);
|
||||
enum drm_connector_status conn_status;
|
||||
int ret;
|
||||
|
||||
if (drm_connector_is_unregistered(connector))
|
||||
@ -1009,14 +1000,24 @@ nv50_mstc_detect(struct drm_connector *connector, bool force)
|
||||
if (ret < 0 && ret != -EACCES)
|
||||
return connector_status_disconnected;
|
||||
|
||||
conn_status = drm_dp_mst_detect_port(connector, mstc->port->mgr,
|
||||
mstc->port);
|
||||
ret = drm_dp_mst_detect_port(connector, ctx, mstc->port->mgr,
|
||||
mstc->port);
|
||||
|
||||
pm_runtime_mark_last_busy(connector->dev->dev);
|
||||
pm_runtime_put_autosuspend(connector->dev->dev);
|
||||
return conn_status;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct drm_connector_helper_funcs
|
||||
nv50_mstc_help = {
|
||||
.get_modes = nv50_mstc_get_modes,
|
||||
.mode_valid = nv50_mstc_mode_valid,
|
||||
.best_encoder = nv50_mstc_best_encoder,
|
||||
.atomic_best_encoder = nv50_mstc_atomic_best_encoder,
|
||||
.atomic_check = nv50_mstc_atomic_check,
|
||||
.detect_ctx = nv50_mstc_detect,
|
||||
};
|
||||
|
||||
static void
|
||||
nv50_mstc_destroy(struct drm_connector *connector)
|
||||
{
|
||||
@ -1031,7 +1032,6 @@ nv50_mstc_destroy(struct drm_connector *connector)
|
||||
static const struct drm_connector_funcs
|
||||
nv50_mstc = {
|
||||
.reset = nouveau_conn_reset,
|
||||
.detect = nv50_mstc_detect,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.destroy = nv50_mstc_destroy,
|
||||
.atomic_duplicate_state = nouveau_conn_atomic_duplicate_state,
|
||||
@ -1309,14 +1309,14 @@ nv50_mstm_fini(struct nv50_mstm *mstm)
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_mstm_init(struct nv50_mstm *mstm)
|
||||
nv50_mstm_init(struct nv50_mstm *mstm, bool runtime)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!mstm || !mstm->mgr.mst_state)
|
||||
return;
|
||||
|
||||
ret = drm_dp_mst_topology_mgr_resume(&mstm->mgr);
|
||||
ret = drm_dp_mst_topology_mgr_resume(&mstm->mgr, !runtime);
|
||||
if (ret == -1) {
|
||||
drm_dp_mst_topology_mgr_set_mst(&mstm->mgr, false);
|
||||
drm_kms_helper_hotplug_event(mstm->mgr.dev);
|
||||
@ -2263,7 +2263,7 @@ nv50_display_init(struct drm_device *dev, bool resume, bool runtime)
|
||||
if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST) {
|
||||
struct nouveau_encoder *nv_encoder =
|
||||
nouveau_encoder(encoder);
|
||||
nv50_mstm_init(nv_encoder->dp.mstm);
|
||||
nv50_mstm_init(nv_encoder->dp.mstm, runtime);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1130,6 +1130,16 @@ nouveau_connector_hotplug(struct nvif_notify *notify)
|
||||
const char *name = connector->name;
|
||||
struct nouveau_encoder *nv_encoder;
|
||||
int ret;
|
||||
bool plugged = (rep->mask != NVIF_NOTIFY_CONN_V0_UNPLUG);
|
||||
|
||||
if (rep->mask & NVIF_NOTIFY_CONN_V0_IRQ) {
|
||||
NV_DEBUG(drm, "service %s\n", name);
|
||||
drm_dp_cec_irq(&nv_connector->aux);
|
||||
if ((nv_encoder = find_encoder(connector, DCB_OUTPUT_DP)))
|
||||
nv50_mstm_service(nv_encoder->dp.mstm);
|
||||
|
||||
return NVIF_NOTIFY_KEEP;
|
||||
}
|
||||
|
||||
ret = pm_runtime_get(drm->dev->dev);
|
||||
if (ret == 0) {
|
||||
@ -1150,25 +1160,16 @@ nouveau_connector_hotplug(struct nvif_notify *notify)
|
||||
return NVIF_NOTIFY_DROP;
|
||||
}
|
||||
|
||||
if (rep->mask & NVIF_NOTIFY_CONN_V0_IRQ) {
|
||||
NV_DEBUG(drm, "service %s\n", name);
|
||||
drm_dp_cec_irq(&nv_connector->aux);
|
||||
if ((nv_encoder = find_encoder(connector, DCB_OUTPUT_DP)))
|
||||
nv50_mstm_service(nv_encoder->dp.mstm);
|
||||
} else {
|
||||
bool plugged = (rep->mask != NVIF_NOTIFY_CONN_V0_UNPLUG);
|
||||
|
||||
if (!plugged)
|
||||
drm_dp_cec_unset_edid(&nv_connector->aux);
|
||||
NV_DEBUG(drm, "%splugged %s\n", plugged ? "" : "un", name);
|
||||
if ((nv_encoder = find_encoder(connector, DCB_OUTPUT_DP))) {
|
||||
if (!plugged)
|
||||
drm_dp_cec_unset_edid(&nv_connector->aux);
|
||||
NV_DEBUG(drm, "%splugged %s\n", plugged ? "" : "un", name);
|
||||
if ((nv_encoder = find_encoder(connector, DCB_OUTPUT_DP))) {
|
||||
if (!plugged)
|
||||
nv50_mstm_remove(nv_encoder->dp.mstm);
|
||||
}
|
||||
|
||||
drm_helper_hpd_irq_event(connector->dev);
|
||||
nv50_mstm_remove(nv_encoder->dp.mstm);
|
||||
}
|
||||
|
||||
drm_helper_hpd_irq_event(connector->dev);
|
||||
|
||||
pm_runtime_mark_last_busy(drm->dev->dev);
|
||||
pm_runtime_put_autosuspend(drm->dev->dev);
|
||||
return NVIF_NOTIFY_KEEP;
|
||||
|
@ -407,6 +407,17 @@ nouveau_display_init(struct drm_device *dev, bool resume, bool runtime)
|
||||
struct drm_connector_list_iter conn_iter;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Enable hotplug interrupts (done as early as possible, since we need
|
||||
* them for MST)
|
||||
*/
|
||||
drm_connector_list_iter_begin(dev, &conn_iter);
|
||||
nouveau_for_each_non_mst_connector_iter(connector, &conn_iter) {
|
||||
struct nouveau_connector *conn = nouveau_connector(connector);
|
||||
nvif_notify_get(&conn->hpd);
|
||||
}
|
||||
drm_connector_list_iter_end(&conn_iter);
|
||||
|
||||
ret = disp->init(dev, resume, runtime);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -416,14 +427,6 @@ nouveau_display_init(struct drm_device *dev, bool resume, bool runtime)
|
||||
*/
|
||||
drm_kms_helper_poll_enable(dev);
|
||||
|
||||
/* enable hotplug interrupts */
|
||||
drm_connector_list_iter_begin(dev, &conn_iter);
|
||||
nouveau_for_each_non_mst_connector_iter(connector, &conn_iter) {
|
||||
struct nouveau_connector *conn = nouveau_connector(connector);
|
||||
nvif_notify_get(&conn->hpd);
|
||||
}
|
||||
drm_connector_list_iter_end(&conn_iter);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -233,21 +233,26 @@ drm_encoder *radeon_mst_best_encoder(struct drm_connector *connector)
|
||||
return &radeon_connector->mst_encoder->base;
|
||||
}
|
||||
|
||||
static int
|
||||
radeon_dp_mst_detect(struct drm_connector *connector,
|
||||
struct drm_modeset_acquire_ctx *ctx,
|
||||
bool force)
|
||||
{
|
||||
struct radeon_connector *radeon_connector =
|
||||
to_radeon_connector(connector);
|
||||
struct radeon_connector *master = radeon_connector->mst_port;
|
||||
|
||||
return drm_dp_mst_detect_port(connector, ctx, &master->mst_mgr,
|
||||
radeon_connector->port);
|
||||
}
|
||||
|
||||
static const struct drm_connector_helper_funcs radeon_dp_mst_connector_helper_funcs = {
|
||||
.get_modes = radeon_dp_mst_get_modes,
|
||||
.mode_valid = radeon_dp_mst_mode_valid,
|
||||
.best_encoder = radeon_mst_best_encoder,
|
||||
.detect_ctx = radeon_dp_mst_detect,
|
||||
};
|
||||
|
||||
static enum drm_connector_status
|
||||
radeon_dp_mst_detect(struct drm_connector *connector, bool force)
|
||||
{
|
||||
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
|
||||
struct radeon_connector *master = radeon_connector->mst_port;
|
||||
|
||||
return drm_dp_mst_detect_port(connector, &master->mst_mgr, radeon_connector->port);
|
||||
}
|
||||
|
||||
static void
|
||||
radeon_dp_mst_connector_destroy(struct drm_connector *connector)
|
||||
{
|
||||
@ -262,7 +267,6 @@ radeon_dp_mst_connector_destroy(struct drm_connector *connector)
|
||||
|
||||
static const struct drm_connector_funcs radeon_dp_mst_connector_funcs = {
|
||||
.dpms = drm_helper_connector_dpms,
|
||||
.detect = radeon_dp_mst_detect,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.destroy = radeon_dp_mst_connector_destroy,
|
||||
};
|
||||
|
@ -26,6 +26,26 @@
|
||||
#include <drm/drm_dp_helper.h>
|
||||
#include <drm/drm_atomic.h>
|
||||
|
||||
#if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS)
|
||||
#include <linux/stackdepot.h>
|
||||
#include <linux/timekeeping.h>
|
||||
|
||||
enum drm_dp_mst_topology_ref_type {
|
||||
DRM_DP_MST_TOPOLOGY_REF_GET,
|
||||
DRM_DP_MST_TOPOLOGY_REF_PUT,
|
||||
};
|
||||
|
||||
struct drm_dp_mst_topology_ref_history {
|
||||
struct drm_dp_mst_topology_ref_entry {
|
||||
enum drm_dp_mst_topology_ref_type type;
|
||||
int count;
|
||||
ktime_t ts_nsec;
|
||||
depot_stack_handle_t backtrace;
|
||||
} *entries;
|
||||
int len;
|
||||
};
|
||||
#endif /* IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS) */
|
||||
|
||||
struct drm_dp_mst_branch;
|
||||
|
||||
/**
|
||||
@ -45,21 +65,31 @@ struct drm_dp_vcpi {
|
||||
/**
|
||||
* struct drm_dp_mst_port - MST port
|
||||
* @port_num: port number
|
||||
* @input: if this port is an input port.
|
||||
* @mcs: message capability status - DP 1.2 spec.
|
||||
* @ddps: DisplayPort Device Plug Status - DP 1.2
|
||||
* @pdt: Peer Device Type
|
||||
* @ldps: Legacy Device Plug Status
|
||||
* @dpcd_rev: DPCD revision of device on this port
|
||||
* @num_sdp_streams: Number of simultaneous streams
|
||||
* @num_sdp_stream_sinks: Number of stream sinks
|
||||
* @available_pbn: Available bandwidth for this port.
|
||||
* @input: if this port is an input port. Protected by
|
||||
* &drm_dp_mst_topology_mgr.base.lock.
|
||||
* @mcs: message capability status - DP 1.2 spec. Protected by
|
||||
* &drm_dp_mst_topology_mgr.base.lock.
|
||||
* @ddps: DisplayPort Device Plug Status - DP 1.2. Protected by
|
||||
* &drm_dp_mst_topology_mgr.base.lock.
|
||||
* @pdt: Peer Device Type. Protected by
|
||||
* &drm_dp_mst_topology_mgr.base.lock.
|
||||
* @ldps: Legacy Device Plug Status. Protected by
|
||||
* &drm_dp_mst_topology_mgr.base.lock.
|
||||
* @dpcd_rev: DPCD revision of device on this port. Protected by
|
||||
* &drm_dp_mst_topology_mgr.base.lock.
|
||||
* @num_sdp_streams: Number of simultaneous streams. Protected by
|
||||
* &drm_dp_mst_topology_mgr.base.lock.
|
||||
* @num_sdp_stream_sinks: Number of stream sinks. Protected by
|
||||
* &drm_dp_mst_topology_mgr.base.lock.
|
||||
* @available_pbn: Available bandwidth for this port. Protected by
|
||||
* &drm_dp_mst_topology_mgr.base.lock.
|
||||
* @next: link to next port on this branch device
|
||||
* @mstb: branch device attach below this port
|
||||
* @aux: i2c aux transport to talk to device connected to this port.
|
||||
* @aux: i2c aux transport to talk to device connected to this port, protected
|
||||
* by &drm_dp_mst_topology_mgr.base.lock.
|
||||
* @parent: branch device parent of this port
|
||||
* @vcpi: Virtual Channel Payload info for this port.
|
||||
* @connector: DRM connector this port is connected to.
|
||||
* @connector: DRM connector this port is connected to. Protected by
|
||||
* &drm_dp_mst_topology_mgr.base.lock.
|
||||
* @mgr: topology manager this port lives under.
|
||||
*
|
||||
* This structure represents an MST port endpoint on a device somewhere
|
||||
@ -79,6 +109,14 @@ struct drm_dp_mst_port {
|
||||
*/
|
||||
struct kref malloc_kref;
|
||||
|
||||
#if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS)
|
||||
/**
|
||||
* @topology_ref_history: A history of each topology
|
||||
* reference/dereference. See CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS.
|
||||
*/
|
||||
struct drm_dp_mst_topology_ref_history topology_ref_history;
|
||||
#endif
|
||||
|
||||
u8 port_num;
|
||||
bool input;
|
||||
bool mcs;
|
||||
@ -90,7 +128,17 @@ struct drm_dp_mst_port {
|
||||
u8 num_sdp_stream_sinks;
|
||||
uint16_t available_pbn;
|
||||
struct list_head next;
|
||||
struct drm_dp_mst_branch *mstb; /* pointer to an mstb if this port has one */
|
||||
/**
|
||||
* @mstb: the branch device connected to this port, if there is one.
|
||||
* This should be considered protected for reading by
|
||||
* &drm_dp_mst_topology_mgr.lock. There are two exceptions to this:
|
||||
* &drm_dp_mst_topology_mgr.up_req_work and
|
||||
* &drm_dp_mst_topology_mgr.work, which do not grab
|
||||
* &drm_dp_mst_topology_mgr.lock during reads but are the only
|
||||
* updaters of this list and are protected from writing concurrently
|
||||
* by &drm_dp_mst_topology_mgr.probe_lock.
|
||||
*/
|
||||
struct drm_dp_mst_branch *mstb;
|
||||
struct drm_dp_aux aux; /* i2c bus for this port? */
|
||||
struct drm_dp_mst_branch *parent;
|
||||
|
||||
@ -116,7 +164,6 @@ struct drm_dp_mst_port {
|
||||
* @lct: Link count total to talk to this branch device.
|
||||
* @num_ports: number of ports on the branch.
|
||||
* @msg_slots: one bit per transmitted msg slot.
|
||||
* @ports: linked list of ports on this branch.
|
||||
* @port_parent: pointer to the port parent, NULL if toplevel.
|
||||
* @mgr: topology manager for this branch device.
|
||||
* @tx_slots: transmission slots for this device.
|
||||
@ -143,11 +190,35 @@ struct drm_dp_mst_branch {
|
||||
*/
|
||||
struct kref malloc_kref;
|
||||
|
||||
#if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS)
|
||||
/**
|
||||
* @topology_ref_history: A history of each topology
|
||||
* reference/dereference. See CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS.
|
||||
*/
|
||||
struct drm_dp_mst_topology_ref_history topology_ref_history;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @destroy_next: linked-list entry used by
|
||||
* drm_dp_delayed_destroy_work()
|
||||
*/
|
||||
struct list_head destroy_next;
|
||||
|
||||
u8 rad[8];
|
||||
u8 lct;
|
||||
int num_ports;
|
||||
|
||||
int msg_slots;
|
||||
/**
|
||||
* @ports: the list of ports on this branch device. This should be
|
||||
* considered protected for reading by &drm_dp_mst_topology_mgr.lock.
|
||||
* There are two exceptions to this:
|
||||
* &drm_dp_mst_topology_mgr.up_req_work and
|
||||
* &drm_dp_mst_topology_mgr.work, which do not grab
|
||||
* &drm_dp_mst_topology_mgr.lock during reads but are the only
|
||||
* updaters of this list and are protected from updating the list
|
||||
* concurrently by @drm_dp_mst_topology_mgr.probe_lock
|
||||
*/
|
||||
struct list_head ports;
|
||||
|
||||
/* list of tx ops queue for this port */
|
||||
@ -494,6 +565,13 @@ struct drm_dp_mst_topology_mgr {
|
||||
*/
|
||||
struct mutex lock;
|
||||
|
||||
/**
|
||||
* @probe_lock: Prevents @work and @up_req_work, the only writers of
|
||||
* &drm_dp_mst_port.mstb and &drm_dp_mst_branch.ports, from racing
|
||||
* while they update the topology.
|
||||
*/
|
||||
struct mutex probe_lock;
|
||||
|
||||
/**
|
||||
* @mst_state: If this manager is enabled for an MST capable port. False
|
||||
* if no MST sink/branch devices is connected.
|
||||
@ -571,18 +649,49 @@ struct drm_dp_mst_topology_mgr {
|
||||
struct work_struct tx_work;
|
||||
|
||||
/**
|
||||
* @destroy_connector_list: List of to be destroyed connectors.
|
||||
* @destroy_port_list: List of to be destroyed connectors.
|
||||
*/
|
||||
struct list_head destroy_connector_list;
|
||||
struct list_head destroy_port_list;
|
||||
/**
|
||||
* @destroy_connector_lock: Protects @connector_list.
|
||||
* @destroy_branch_device_list: List of to be destroyed branch
|
||||
* devices.
|
||||
*/
|
||||
struct mutex destroy_connector_lock;
|
||||
struct list_head destroy_branch_device_list;
|
||||
/**
|
||||
* @destroy_connector_work: Work item to destroy connectors. Needed to
|
||||
* avoid locking inversion.
|
||||
* @delayed_destroy_lock: Protects @destroy_port_list and
|
||||
* @destroy_branch_device_list.
|
||||
*/
|
||||
struct work_struct destroy_connector_work;
|
||||
struct mutex delayed_destroy_lock;
|
||||
/**
|
||||
* @delayed_destroy_work: Work item to destroy MST port and branch
|
||||
* devices, needed to avoid locking inversion.
|
||||
*/
|
||||
struct work_struct delayed_destroy_work;
|
||||
|
||||
/**
|
||||
* @up_req_list: List of pending up requests from the topology that
|
||||
* need to be processed, in chronological order.
|
||||
*/
|
||||
struct list_head up_req_list;
|
||||
/**
|
||||
* @up_req_lock: Protects @up_req_list
|
||||
*/
|
||||
struct mutex up_req_lock;
|
||||
/**
|
||||
* @up_req_work: Work item to process up requests received from the
|
||||
* topology. Needed to avoid blocking hotplug handling and sideband
|
||||
* transmissions.
|
||||
*/
|
||||
struct work_struct up_req_work;
|
||||
|
||||
#if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS)
|
||||
/**
|
||||
* @topology_ref_history_lock: protects
|
||||
* &drm_dp_mst_port.topology_ref_history and
|
||||
* &drm_dp_mst_branch.topology_ref_history.
|
||||
*/
|
||||
struct mutex topology_ref_history_lock;
|
||||
#endif
|
||||
};
|
||||
|
||||
int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr,
|
||||
@ -599,7 +708,11 @@ int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool ms
|
||||
int drm_dp_mst_hpd_irq(struct drm_dp_mst_topology_mgr *mgr, u8 *esi, bool *handled);
|
||||
|
||||
|
||||
enum drm_connector_status drm_dp_mst_detect_port(struct drm_connector *connector, struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port);
|
||||
int
|
||||
drm_dp_mst_detect_port(struct drm_connector *connector,
|
||||
struct drm_modeset_acquire_ctx *ctx,
|
||||
struct drm_dp_mst_topology_mgr *mgr,
|
||||
struct drm_dp_mst_port *port);
|
||||
|
||||
bool drm_dp_mst_port_has_audio(struct drm_dp_mst_topology_mgr *mgr,
|
||||
struct drm_dp_mst_port *port);
|
||||
@ -638,7 +751,8 @@ void drm_dp_mst_dump_topology(struct seq_file *m,
|
||||
|
||||
void drm_dp_mst_topology_mgr_suspend(struct drm_dp_mst_topology_mgr *mgr);
|
||||
int __must_check
|
||||
drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr);
|
||||
drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr,
|
||||
bool sync);
|
||||
|
||||
ssize_t drm_dp_mst_dpcd_read(struct drm_dp_aux *aux,
|
||||
unsigned int offset, void *buffer, size_t size);
|
||||
|
Loading…
Reference in New Issue
Block a user