Commit 57c2af79 authored by Dave Airlie's avatar Dave Airlie

Merge tag 'topic/mst-suspend-resume-reprobe-2019-10-29-2' of...

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: default avatarDave Airlie <airlied@redhat.com>

From: Lyude Paul <lyude@redhat.com>
Link: https://patchwork.freedesktop.org/patch/msgid/a74c6446bc960190d195a751cb6d8a00a98f3974.camel@redhat.com
parents 8c84b43f 12a280c7
...@@ -93,6 +93,20 @@ config DRM_KMS_FB_HELPER ...@@ -93,6 +93,20 @@ config DRM_KMS_FB_HELPER
help help
FBDEV helpers for KMS drivers. 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 config DRM_FBDEV_EMULATION
bool "Enable legacy fbdev support for your modesetting driver" bool "Enable legacy fbdev support for your modesetting driver"
depends on DRM depends on DRM
......
...@@ -1028,7 +1028,7 @@ static void s3_handle_mst(struct drm_device *dev, bool suspend) ...@@ -1028,7 +1028,7 @@ static void s3_handle_mst(struct drm_device *dev, bool suspend)
if (suspend) { if (suspend) {
drm_dp_mst_topology_mgr_suspend(mgr); drm_dp_mst_topology_mgr_suspend(mgr);
} else { } else {
ret = drm_dp_mst_topology_mgr_resume(mgr); ret = drm_dp_mst_topology_mgr_resume(mgr, true);
if (ret < 0) { if (ret < 0) {
drm_dp_mst_topology_mgr_set_mst(mgr, false); drm_dp_mst_topology_mgr_set_mst(mgr, false);
need_hotplug = true; need_hotplug = true;
......
...@@ -123,21 +123,6 @@ static ssize_t dm_dp_aux_transfer(struct drm_dp_aux *aux, ...@@ -123,21 +123,6 @@ static ssize_t dm_dp_aux_transfer(struct drm_dp_aux *aux,
return result; 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 static void
dm_dp_mst_connector_destroy(struct drm_connector *connector) dm_dp_mst_connector_destroy(struct drm_connector *connector)
{ {
...@@ -175,7 +160,6 @@ amdgpu_dm_mst_connector_early_unregister(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 = { static const struct drm_connector_funcs dm_dp_mst_connector_funcs = {
.detect = dm_dp_mst_detect,
.fill_modes = drm_helper_probe_single_connector_modes, .fill_modes = drm_helper_probe_single_connector_modes,
.destroy = dm_dp_mst_connector_destroy, .destroy = dm_dp_mst_connector_destroy,
.reset = amdgpu_dm_connector_funcs_reset, .reset = amdgpu_dm_connector_funcs_reset,
...@@ -250,10 +234,22 @@ dm_mst_atomic_best_encoder(struct drm_connector *connector, ...@@ -250,10 +234,22 @@ dm_mst_atomic_best_encoder(struct drm_connector *connector,
return &to_amdgpu_dm_connector(connector)->mst_encoder->base; 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 = { static const struct drm_connector_helper_funcs dm_dp_mst_connector_helper_funcs = {
.get_modes = dm_dp_mst_get_modes, .get_modes = dm_dp_mst_get_modes,
.mode_valid = amdgpu_dm_connector_mode_valid, .mode_valid = amdgpu_dm_connector_mode_valid,
.atomic_best_encoder = dm_mst_atomic_best_encoder, .atomic_best_encoder = dm_mst_atomic_best_encoder,
.detect_ctx = dm_dp_mst_detect,
}; };
static void amdgpu_dm_encoder_destroy(struct drm_encoder *encoder) static void amdgpu_dm_encoder_destroy(struct drm_encoder *encoder)
......
This diff is collapsed.
...@@ -7625,7 +7625,8 @@ void intel_dp_mst_resume(struct drm_i915_private *dev_priv) ...@@ -7625,7 +7625,8 @@ void intel_dp_mst_resume(struct drm_i915_private *dev_priv)
if (!intel_dp->can_mst) if (!intel_dp->can_mst)
continue; 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) { if (ret) {
intel_dp->is_mst = false; intel_dp->is_mst = false;
drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr, 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) ...@@ -391,20 +391,7 @@ static int intel_dp_mst_get_ddc_modes(struct drm_connector *connector)
return ret; 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 = { static const struct drm_connector_funcs intel_dp_mst_connector_funcs = {
.detect = intel_dp_mst_detect,
.fill_modes = drm_helper_probe_single_connector_modes, .fill_modes = drm_helper_probe_single_connector_modes,
.atomic_get_property = intel_digital_connector_atomic_get_property, .atomic_get_property = intel_digital_connector_atomic_get_property,
.atomic_set_property = intel_digital_connector_atomic_set_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 ...@@ -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; 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 = { static const struct drm_connector_helper_funcs intel_dp_mst_connector_helper_funcs = {
.get_modes = intel_dp_mst_get_modes, .get_modes = intel_dp_mst_get_modes,
.mode_valid = intel_dp_mst_mode_valid, .mode_valid = intel_dp_mst_mode_valid,
.atomic_best_encoder = intel_mst_atomic_best_encoder, .atomic_best_encoder = intel_mst_atomic_best_encoder,
.atomic_check = intel_dp_mst_atomic_check, .atomic_check = intel_dp_mst_atomic_check,
.detect_ctx = intel_dp_mst_detect,
}; };
static void intel_dp_mst_encoder_destroy(struct drm_encoder *encoder) static void intel_dp_mst_encoder_destroy(struct drm_encoder *encoder)
......
...@@ -986,20 +986,11 @@ nv50_mstc_atomic_check(struct drm_connector *connector, ...@@ -986,20 +986,11 @@ nv50_mstc_atomic_check(struct drm_connector *connector,
return drm_dp_atomic_release_vcpi_slots(state, mgr, mstc->port); return drm_dp_atomic_release_vcpi_slots(state, mgr, mstc->port);
} }
static const struct drm_connector_helper_funcs static int
nv50_mstc_help = { nv50_mstc_detect(struct drm_connector *connector,
.get_modes = nv50_mstc_get_modes, struct drm_modeset_acquire_ctx *ctx, bool force)
.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)
{ {
struct nv50_mstc *mstc = nv50_mstc(connector); struct nv50_mstc *mstc = nv50_mstc(connector);
enum drm_connector_status conn_status;
int ret; int ret;
if (drm_connector_is_unregistered(connector)) if (drm_connector_is_unregistered(connector))
...@@ -1009,14 +1000,24 @@ nv50_mstc_detect(struct drm_connector *connector, bool force) ...@@ -1009,14 +1000,24 @@ nv50_mstc_detect(struct drm_connector *connector, bool force)
if (ret < 0 && ret != -EACCES) if (ret < 0 && ret != -EACCES)
return connector_status_disconnected; return connector_status_disconnected;
conn_status = drm_dp_mst_detect_port(connector, mstc->port->mgr, ret = drm_dp_mst_detect_port(connector, ctx, mstc->port->mgr,
mstc->port); mstc->port);
pm_runtime_mark_last_busy(connector->dev->dev); pm_runtime_mark_last_busy(connector->dev->dev);
pm_runtime_put_autosuspend(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 static void
nv50_mstc_destroy(struct drm_connector *connector) nv50_mstc_destroy(struct drm_connector *connector)
{ {
...@@ -1031,7 +1032,6 @@ nv50_mstc_destroy(struct drm_connector *connector) ...@@ -1031,7 +1032,6 @@ nv50_mstc_destroy(struct drm_connector *connector)
static const struct drm_connector_funcs static const struct drm_connector_funcs
nv50_mstc = { nv50_mstc = {
.reset = nouveau_conn_reset, .reset = nouveau_conn_reset,
.detect = nv50_mstc_detect,
.fill_modes = drm_helper_probe_single_connector_modes, .fill_modes = drm_helper_probe_single_connector_modes,
.destroy = nv50_mstc_destroy, .destroy = nv50_mstc_destroy,
.atomic_duplicate_state = nouveau_conn_atomic_duplicate_state, .atomic_duplicate_state = nouveau_conn_atomic_duplicate_state,
...@@ -1309,14 +1309,14 @@ nv50_mstm_fini(struct nv50_mstm *mstm) ...@@ -1309,14 +1309,14 @@ nv50_mstm_fini(struct nv50_mstm *mstm)
} }
static void static void
nv50_mstm_init(struct nv50_mstm *mstm) nv50_mstm_init(struct nv50_mstm *mstm, bool runtime)
{ {
int ret; int ret;
if (!mstm || !mstm->mgr.mst_state) if (!mstm || !mstm->mgr.mst_state)
return; return;
ret = drm_dp_mst_topology_mgr_resume(&mstm->mgr); ret = drm_dp_mst_topology_mgr_resume(&mstm->mgr, !runtime);
if (ret == -1) { if (ret == -1) {
drm_dp_mst_topology_mgr_set_mst(&mstm->mgr, false); drm_dp_mst_topology_mgr_set_mst(&mstm->mgr, false);
drm_kms_helper_hotplug_event(mstm->mgr.dev); drm_kms_helper_hotplug_event(mstm->mgr.dev);
...@@ -2263,7 +2263,7 @@ nv50_display_init(struct drm_device *dev, bool resume, bool runtime) ...@@ -2263,7 +2263,7 @@ nv50_display_init(struct drm_device *dev, bool resume, bool runtime)
if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST) { if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST) {
struct nouveau_encoder *nv_encoder = struct nouveau_encoder *nv_encoder =
nouveau_encoder(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) ...@@ -1130,6 +1130,16 @@ nouveau_connector_hotplug(struct nvif_notify *notify)
const char *name = connector->name; const char *name = connector->name;
struct nouveau_encoder *nv_encoder; struct nouveau_encoder *nv_encoder;
int ret; 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); ret = pm_runtime_get(drm->dev->dev);
if (ret == 0) { if (ret == 0) {
...@@ -1150,14 +1160,6 @@ nouveau_connector_hotplug(struct nvif_notify *notify) ...@@ -1150,14 +1160,6 @@ nouveau_connector_hotplug(struct nvif_notify *notify)
return NVIF_NOTIFY_DROP; 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) if (!plugged)
drm_dp_cec_unset_edid(&nv_connector->aux); drm_dp_cec_unset_edid(&nv_connector->aux);
NV_DEBUG(drm, "%splugged %s\n", plugged ? "" : "un", name); NV_DEBUG(drm, "%splugged %s\n", plugged ? "" : "un", name);
...@@ -1167,7 +1169,6 @@ nouveau_connector_hotplug(struct nvif_notify *notify) ...@@ -1167,7 +1169,6 @@ nouveau_connector_hotplug(struct nvif_notify *notify)
} }
drm_helper_hpd_irq_event(connector->dev); drm_helper_hpd_irq_event(connector->dev);
}
pm_runtime_mark_last_busy(drm->dev->dev); pm_runtime_mark_last_busy(drm->dev->dev);
pm_runtime_put_autosuspend(drm->dev->dev); pm_runtime_put_autosuspend(drm->dev->dev);
......
...@@ -407,6 +407,17 @@ nouveau_display_init(struct drm_device *dev, bool resume, bool runtime) ...@@ -407,6 +407,17 @@ nouveau_display_init(struct drm_device *dev, bool resume, bool runtime)
struct drm_connector_list_iter conn_iter; struct drm_connector_list_iter conn_iter;
int ret; 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); ret = disp->init(dev, resume, runtime);
if (ret) if (ret)
return ret; return ret;
...@@ -416,14 +427,6 @@ nouveau_display_init(struct drm_device *dev, bool resume, bool runtime) ...@@ -416,14 +427,6 @@ nouveau_display_init(struct drm_device *dev, bool resume, bool runtime)
*/ */
drm_kms_helper_poll_enable(dev); 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; return ret;
} }
......
...@@ -233,21 +233,26 @@ drm_encoder *radeon_mst_best_encoder(struct drm_connector *connector) ...@@ -233,21 +233,26 @@ drm_encoder *radeon_mst_best_encoder(struct drm_connector *connector)
return &radeon_connector->mst_encoder->base; 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 = { static const struct drm_connector_helper_funcs radeon_dp_mst_connector_helper_funcs = {
.get_modes = radeon_dp_mst_get_modes, .get_modes = radeon_dp_mst_get_modes,
.mode_valid = radeon_dp_mst_mode_valid, .mode_valid = radeon_dp_mst_mode_valid,
.best_encoder = radeon_mst_best_encoder, .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 static void
radeon_dp_mst_connector_destroy(struct drm_connector *connector) radeon_dp_mst_connector_destroy(struct drm_connector *connector)
{ {
...@@ -262,7 +267,6 @@ 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 = { static const struct drm_connector_funcs radeon_dp_mst_connector_funcs = {
.dpms = drm_helper_connector_dpms, .dpms = drm_helper_connector_dpms,
.detect = radeon_dp_mst_detect,
.fill_modes = drm_helper_probe_single_connector_modes, .fill_modes = drm_helper_probe_single_connector_modes,
.destroy = radeon_dp_mst_connector_destroy, .destroy = radeon_dp_mst_connector_destroy,
}; };
......
...@@ -26,6 +26,26 @@ ...@@ -26,6 +26,26 @@
#include <drm/drm_dp_helper.h> #include <drm/drm_dp_helper.h>
#include <drm/drm_atomic.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; struct drm_dp_mst_branch;
/** /**
...@@ -45,21 +65,31 @@ struct drm_dp_vcpi { ...@@ -45,21 +65,31 @@ struct drm_dp_vcpi {
/** /**
* struct drm_dp_mst_port - MST port * struct drm_dp_mst_port - MST port
* @port_num: port number * @port_num: port number
* @input: if this port is an input port. * @input: if this port is an input port. Protected by
* @mcs: message capability status - DP 1.2 spec. * &drm_dp_mst_topology_mgr.base.lock.
* @ddps: DisplayPort Device Plug Status - DP 1.2 * @mcs: message capability status - DP 1.2 spec. Protected by
* @pdt: Peer Device Type * &drm_dp_mst_topology_mgr.base.lock.
* @ldps: Legacy Device Plug Status * @ddps: DisplayPort Device Plug Status - DP 1.2. Protected by
* @dpcd_rev: DPCD revision of device on this port * &drm_dp_mst_topology_mgr.base.lock.
* @num_sdp_streams: Number of simultaneous streams * @pdt: Peer Device Type. Protected by
* @num_sdp_stream_sinks: Number of stream sinks * &drm_dp_mst_topology_mgr.base.lock.
* @available_pbn: Available bandwidth for this port. * @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 * @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, protected
* @aux: i2c aux transport to talk to device connected to this port. * by &drm_dp_mst_topology_mgr.base.lock.
* @parent: branch device parent of this port * @parent: branch device parent of this port
* @vcpi: Virtual Channel Payload info for 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. * @mgr: topology manager this port lives under.
* *
* This structure represents an MST port endpoint on a device somewhere * This structure represents an MST port endpoint on a device somewhere
...@@ -79,6 +109,14 @@ struct drm_dp_mst_port { ...@@ -79,6 +109,14 @@ struct drm_dp_mst_port {
*/ */
struct kref malloc_kref; 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; u8 port_num;
bool input; bool input;
bool mcs; bool mcs;
...@@ -90,7 +128,17 @@ struct drm_dp_mst_port { ...@@ -90,7 +128,17 @@ struct drm_dp_mst_port {
u8 num_sdp_stream_sinks; u8 num_sdp_stream_sinks;
uint16_t available_pbn; uint16_t available_pbn;
struct list_head next; 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_aux aux; /* i2c bus for this port? */
struct drm_dp_mst_branch *parent; struct drm_dp_mst_branch *parent;
...@@ -116,7 +164,6 @@ struct drm_dp_mst_port { ...@@ -116,7 +164,6 @@ struct drm_dp_mst_port {
* @lct: Link count total to talk to this branch device. * @lct: Link count total to talk to this branch device.
* @num_ports: number of ports on the branch. * @num_ports: number of ports on the branch.
* @msg_slots: one bit per transmitted msg slot. * @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. * @port_parent: pointer to the port parent, NULL if toplevel.
* @mgr: topology manager for this branch device. * @mgr: topology manager for this branch device.
* @tx_slots: transmission slots for this device. * @tx_slots: transmission slots for this device.
...@@ -143,11 +190,35 @@ struct drm_dp_mst_branch { ...@@ -143,11 +190,35 @@ struct drm_dp_mst_branch {
*/ */
struct kref malloc_kref; 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 rad[8];
u8 lct; u8 lct;
int num_ports; int num_ports;
int msg_slots; 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; struct list_head ports;
/* list of tx ops queue for this port */ /* list of tx ops queue for this port */
...@@ -494,6 +565,13 @@ struct drm_dp_mst_topology_mgr { ...@@ -494,6 +565,13 @@ struct drm_dp_mst_topology_mgr {
*/ */
struct mutex lock; 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 * @mst_state: If this manager is enabled for an MST capable port. False
* if no MST sink/branch devices is connected. * if no MST sink/branch devices is connected.
...@@ -571,18 +649,49 @@ struct drm_dp_mst_topology_mgr { ...@@ -571,18 +649,49 @@ struct drm_dp_mst_topology_mgr {
struct work_struct tx_work; 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_port_list;
/**
* @destroy_branch_device_list: List of to be destroyed branch
* devices.
*/ */
struct list_head destroy_connector_list; struct list_head destroy_branch_device_list;
/** /**
* @destroy_connector_lock: Protects @connector_list. * @delayed_destroy_lock: Protects @destroy_port_list and
* @destroy_branch_device_list.
*/ */
struct mutex destroy_connector_lock; struct mutex delayed_destroy_lock;
/** /**
* @destroy_connector_work: Work item to destroy connectors. Needed to * @delayed_destroy_work: Work item to destroy MST port and branch
* avoid locking inversion. * devices, needed to avoid locking inversion.
*/ */
struct work_struct destroy_connector_work; 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, 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 ...@@ -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); 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, bool drm_dp_mst_port_has_audio(struct drm_dp_mst_topology_mgr *mgr,
struct drm_dp_mst_port *port); struct drm_dp_mst_port *port);
...@@ -638,7 +751,8 @@ void drm_dp_mst_dump_topology(struct seq_file *m, ...@@ -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); void drm_dp_mst_topology_mgr_suspend(struct drm_dp_mst_topology_mgr *mgr);
int __must_check 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, ssize_t drm_dp_mst_dpcd_read(struct drm_dp_aux *aux,
unsigned int offset, void *buffer, size_t size); unsigned int offset, void *buffer, size_t size);
......
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