2014-05-02 11:02:48 +07:00
|
|
|
/*
|
|
|
|
* Copyright © 2008 Intel Corporation
|
|
|
|
* 2014 Red Hat Inc.
|
|
|
|
*
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
|
|
* to deal in the Software without restriction, including without limitation
|
|
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice (including the next
|
|
|
|
* paragraph) shall be included in all copies or substantial portions of the
|
|
|
|
* Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
|
|
* IN THE SOFTWARE.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2015-01-23 07:50:32 +07:00
|
|
|
#include <drm/drm_atomic_helper.h>
|
2014-05-02 11:02:48 +07:00
|
|
|
#include <drm/drm_edid.h>
|
2019-01-18 04:03:34 +07:00
|
|
|
#include <drm/drm_probe_helper.h>
|
2014-05-02 11:02:48 +07:00
|
|
|
|
2019-04-05 18:00:03 +07:00
|
|
|
#include "i915_drv.h"
|
2019-04-29 19:53:31 +07:00
|
|
|
#include "intel_atomic.h"
|
2019-04-05 18:00:03 +07:00
|
|
|
#include "intel_audio.h"
|
2019-04-05 18:00:06 +07:00
|
|
|
#include "intel_connector.h"
|
2019-04-05 18:00:05 +07:00
|
|
|
#include "intel_ddi.h"
|
2019-04-05 18:00:17 +07:00
|
|
|
#include "intel_dp.h"
|
2019-04-29 19:29:33 +07:00
|
|
|
#include "intel_dp_mst.h"
|
2019-05-02 22:02:40 +07:00
|
|
|
#include "intel_dpio_phy.h"
|
2019-04-05 18:00:03 +07:00
|
|
|
#include "intel_drv.h"
|
|
|
|
|
2019-03-26 21:25:53 +07:00
|
|
|
static int intel_dp_mst_compute_link_config(struct intel_encoder *encoder,
|
|
|
|
struct intel_crtc_state *crtc_state,
|
|
|
|
struct drm_connector_state *conn_state,
|
|
|
|
struct link_config_limits *limits)
|
|
|
|
{
|
|
|
|
struct drm_atomic_state *state = crtc_state->base.state;
|
|
|
|
struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
|
|
|
|
struct intel_dp *intel_dp = &intel_mst->primary->dp;
|
|
|
|
struct intel_connector *connector =
|
|
|
|
to_intel_connector(conn_state->connector);
|
|
|
|
const struct drm_display_mode *adjusted_mode =
|
|
|
|
&crtc_state->base.adjusted_mode;
|
|
|
|
void *port = connector->port;
|
|
|
|
bool constant_n = drm_dp_has_quirk(&intel_dp->desc,
|
|
|
|
DP_DPCD_QUIRK_CONSTANT_N);
|
|
|
|
int bpp, slots = -EINVAL;
|
|
|
|
|
|
|
|
crtc_state->lane_count = limits->max_lane_count;
|
|
|
|
crtc_state->port_clock = limits->max_clock;
|
|
|
|
|
|
|
|
for (bpp = limits->max_bpp; bpp >= limits->min_bpp; bpp -= 2 * 3) {
|
|
|
|
crtc_state->pipe_bpp = bpp;
|
|
|
|
|
|
|
|
crtc_state->pbn = drm_dp_calc_pbn_mode(adjusted_mode->crtc_clock,
|
|
|
|
crtc_state->pipe_bpp);
|
|
|
|
|
|
|
|
slots = drm_dp_atomic_find_vcpi_slots(state, &intel_dp->mst_mgr,
|
|
|
|
port, crtc_state->pbn);
|
|
|
|
if (slots == -EDEADLK)
|
|
|
|
return slots;
|
|
|
|
if (slots >= 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (slots < 0) {
|
|
|
|
DRM_DEBUG_KMS("failed finding vcpi slots:%d\n", slots);
|
|
|
|
return slots;
|
|
|
|
}
|
|
|
|
|
|
|
|
intel_link_compute_m_n(crtc_state->pipe_bpp,
|
|
|
|
crtc_state->lane_count,
|
|
|
|
adjusted_mode->crtc_clock,
|
|
|
|
crtc_state->port_clock,
|
|
|
|
&crtc_state->dp_m_n,
|
|
|
|
constant_n);
|
|
|
|
crtc_state->dp_m_n.tu = slots;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-01-16 03:08:00 +07:00
|
|
|
static int intel_dp_mst_compute_config(struct intel_encoder *encoder,
|
|
|
|
struct intel_crtc_state *pipe_config,
|
|
|
|
struct drm_connector_state *conn_state)
|
2014-05-02 11:02:48 +07:00
|
|
|
{
|
2017-10-24 16:52:14 +07:00
|
|
|
struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
|
2014-05-02 11:02:48 +07:00
|
|
|
struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
|
2019-03-26 21:25:53 +07:00
|
|
|
struct intel_dp *intel_dp = &intel_mst->primary->dp;
|
|
|
|
struct intel_connector *connector =
|
|
|
|
to_intel_connector(conn_state->connector);
|
2019-03-27 20:19:29 +07:00
|
|
|
struct intel_digital_connector_state *intel_conn_state =
|
|
|
|
to_intel_digital_connector_state(conn_state);
|
2019-03-26 21:25:53 +07:00
|
|
|
const struct drm_display_mode *adjusted_mode =
|
|
|
|
&pipe_config->base.adjusted_mode;
|
|
|
|
void *port = connector->port;
|
|
|
|
struct link_config_limits limits;
|
|
|
|
int ret;
|
2014-05-02 11:02:48 +07:00
|
|
|
|
2018-05-24 19:54:03 +07:00
|
|
|
if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN)
|
2019-01-16 03:08:00 +07:00
|
|
|
return -EINVAL;
|
2018-05-24 19:54:03 +07:00
|
|
|
|
2018-10-12 13:23:07 +07:00
|
|
|
pipe_config->output_format = INTEL_OUTPUT_FORMAT_RGB;
|
2014-05-02 11:02:48 +07:00
|
|
|
pipe_config->has_pch_encoder = false;
|
2019-03-27 20:19:29 +07:00
|
|
|
|
|
|
|
if (intel_conn_state->force_audio == HDMI_AUDIO_AUTO)
|
|
|
|
pipe_config->has_audio =
|
|
|
|
drm_dp_mst_port_has_audio(&intel_dp->mst_mgr, port);
|
|
|
|
else
|
|
|
|
pipe_config->has_audio =
|
|
|
|
intel_conn_state->force_audio == HDMI_AUDIO_ON;
|
|
|
|
|
2014-05-02 11:02:48 +07:00
|
|
|
/*
|
|
|
|
* for MST we always configure max link bw - the spec doesn't
|
|
|
|
* seem to suggest we should do otherwise.
|
|
|
|
*/
|
2019-03-26 21:25:53 +07:00
|
|
|
limits.min_clock =
|
|
|
|
limits.max_clock = intel_dp_max_link_rate(intel_dp);
|
2015-03-12 22:10:36 +07:00
|
|
|
|
2019-03-26 21:25:53 +07:00
|
|
|
limits.min_lane_count =
|
|
|
|
limits.max_lane_count = intel_dp_max_lane_count(intel_dp);
|
2014-05-02 11:02:48 +07:00
|
|
|
|
2019-03-26 21:25:56 +07:00
|
|
|
limits.min_bpp = intel_dp_min_bpp(pipe_config);
|
2019-03-26 21:25:53 +07:00
|
|
|
limits.max_bpp = pipe_config->pipe_bpp;
|
2014-05-02 11:02:48 +07:00
|
|
|
|
2019-03-26 21:25:53 +07:00
|
|
|
intel_dp_adjust_compliance_config(intel_dp, pipe_config, &limits);
|
|
|
|
|
|
|
|
ret = intel_dp_mst_compute_link_config(encoder, pipe_config,
|
|
|
|
conn_state, &limits);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2015-03-20 21:18:13 +07:00
|
|
|
|
2019-03-26 21:25:51 +07:00
|
|
|
pipe_config->limited_color_range =
|
|
|
|
intel_dp_limited_color_range(pipe_config, conn_state);
|
|
|
|
|
2017-10-27 20:43:48 +07:00
|
|
|
if (IS_GEN9_LP(dev_priv))
|
|
|
|
pipe_config->lane_lat_optim_mask =
|
|
|
|
bxt_ddi_phy_calc_lane_lat_optim_mask(pipe_config->lane_count);
|
|
|
|
|
2017-10-24 16:52:14 +07:00
|
|
|
intel_ddi_compute_min_voltage_level(dev_priv, pipe_config);
|
|
|
|
|
2019-01-16 03:08:00 +07:00
|
|
|
return 0;
|
2017-04-29 06:14:20 +07:00
|
|
|
}
|
2014-05-02 11:02:48 +07:00
|
|
|
|
drm/dp_mst: Start tracking per-port VCPI allocations
There has been a TODO waiting for quite a long time in
drm_dp_mst_topology.c:
/* We cannot rely on port->vcpi.num_slots to update
* topology_state->avail_slots as the port may not exist if the parent
* branch device was unplugged. This should be fixed by tracking
* per-port slot allocation in drm_dp_mst_topology_state instead of
* depending on the caller to tell us how many slots to release.
*/
That's not the only reason we should fix this: forcing the driver to
track the VCPI allocations throughout a state's atomic check is
error prone, because it means that extra care has to be taken with the
order that drm_dp_atomic_find_vcpi_slots() and
drm_dp_atomic_release_vcpi_slots() are called in in order to ensure
idempotency. Currently the only driver actually using these helpers,
i915, doesn't even do this correctly: multiple ->best_encoder() checks
with i915's current implementation would not be idempotent and would
over-allocate VCPI slots, something I learned trying to implement
fallback retraining in MST.
So: simplify this whole mess, and teach drm_dp_atomic_find_vcpi_slots()
and drm_dp_atomic_release_vcpi_slots() to track the VCPI allocations for
each port. This allows us to ensure idempotency without having to rely
on the driver as much. Additionally: the driver doesn't need to do any
kind of VCPI slot tracking anymore if it doesn't need it for it's own
internal state.
Additionally; this adds a new drm_dp_mst_atomic_check() helper which
must be used by atomic drivers to perform validity checks for the new
VCPI allocations incurred by a state.
Also: update the documentation and make it more obvious that these
/must/ be called by /all/ atomic drivers supporting MST.
Changes since v9:
* Add some missing changes that were requested by danvet that I forgot
about after I redid all of the kref stuff:
* Remove unnecessary state changes in intel_dp_mst_atomic_check
* Cleanup atomic check logic for VCPI allocations - all we need to check in
compute_config is whether or not this state disables a CRTC, then free
VCPI based off that
Changes since v8:
* Fix compile errors, whoops!
Changes since v7:
- Don't check for mixed stale/valid VCPI allocations, just rely on
connector registration to stop such erroneous modesets
Changes since v6:
- Keep a kref to all of the ports we have allocations on. This required
a good bit of changing to when we call drm_dp_find_vcpi_slots(),
mainly that we need to ensure that we only redo VCPI allocations on
actual mode or CRTC changes, not crtc_state->active changes.
Additionally, we no longer take the registration of the DRM connector
for each port into account because so long as we have a kref to the
port in the new or previous atomic state, the connector will stay
registered.
- Use the small changes to drm_dp_put_port() to add even more error
checking to make misusage of the helpers more obvious. I added this
after having to chase down various use-after-free conditions that
started popping up from the new helpers so no one else has to
troubleshoot that.
- Move some accidental DRM_DEBUG_KMS() calls to DRM_DEBUG_ATOMIC()
- Update documentation again, note that find/release() should both not be
called on the same port in a single atomic check phase (but multiple
calls to one or the other is OK)
Changes since v4:
- Don't skip the atomic checks for VCPI allocations if no new VCPI
allocations happen in a state. This makes the next change I'm about
to list here a lot easier to implement.
- Don't ignore VCPI allocations on destroyed ports, instead ensure that
when ports are destroyed and still have VCPI allocations in the
topology state, the only state changes allowed are releasing said
ports' VCPI. This prevents a state with a mix of VCPI allocations
from destroyed ports, and allocations from valid ports.
Changes since v3:
- Don't release VCPI allocations in the topology state immediately in
drm_dp_atomic_release_vcpi_slots(), instead mark them as 0 and skip
over them in drm_dp_mst_duplicate_state(). This makes it so
drm_dp_atomic_release_vcpi_slots() is still idempotent while also
throwing warnings if the driver messes up it's book keeping and tries
to release VCPI slots on a port that doesn't have any pre-existing
VCPI allocation - danvet
- Change mst_state/state in some debugging messages to "mst state"
Changes since v2:
- Use kmemdup() for duplicating MST state - danvet
- Move port validation out of duplicate state callback - danvet
- Handle looping through MST topology states in
drm_dp_mst_atomic_check() so the driver doesn't have to do it
- Fix documentation in drm_dp_atomic_find_vcpi_slots()
- Move the atomic check for each individual topology state into it's
own function, reduces indenting
- Don't consider "stale" MST ports when calculating the bandwidth
requirements. This is needed because originally we relied on the
state duplication functions to prune any stale ports from the new
state, which would prevent us from incorrectly considering their
bandwidth requirements alongside legitimate new payloads.
- Add function references in drm_dp_atomic_release_vcpi_slots() - danvet
- Annotate atomic VCPI and atomic check functions with __must_check
- danvet
Changes since v1:
- Don't use the now-removed ->atomic_check() for private objects hook,
just give drivers a function to call themselves
Signed-off-by: Lyude Paul <lyude@redhat.com>
Reviewed-by: Daniel Vetter <daniel@ffwll.ch>
Cc: David Airlie <airlied@redhat.com>
Cc: Jerry Zuo <Jerry.Zuo@amd.com>
Cc: Harry Wentland <harry.wentland@amd.com>
Cc: Juston Li <juston.li@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20190111005343.17443-19-lyude@redhat.com
2019-01-11 07:53:41 +07:00
|
|
|
static int
|
|
|
|
intel_dp_mst_atomic_check(struct drm_connector *connector,
|
|
|
|
struct drm_connector_state *new_conn_state)
|
2017-04-29 06:14:20 +07:00
|
|
|
{
|
|
|
|
struct drm_atomic_state *state = new_conn_state->state;
|
drm/dp_mst: Start tracking per-port VCPI allocations
There has been a TODO waiting for quite a long time in
drm_dp_mst_topology.c:
/* We cannot rely on port->vcpi.num_slots to update
* topology_state->avail_slots as the port may not exist if the parent
* branch device was unplugged. This should be fixed by tracking
* per-port slot allocation in drm_dp_mst_topology_state instead of
* depending on the caller to tell us how many slots to release.
*/
That's not the only reason we should fix this: forcing the driver to
track the VCPI allocations throughout a state's atomic check is
error prone, because it means that extra care has to be taken with the
order that drm_dp_atomic_find_vcpi_slots() and
drm_dp_atomic_release_vcpi_slots() are called in in order to ensure
idempotency. Currently the only driver actually using these helpers,
i915, doesn't even do this correctly: multiple ->best_encoder() checks
with i915's current implementation would not be idempotent and would
over-allocate VCPI slots, something I learned trying to implement
fallback retraining in MST.
So: simplify this whole mess, and teach drm_dp_atomic_find_vcpi_slots()
and drm_dp_atomic_release_vcpi_slots() to track the VCPI allocations for
each port. This allows us to ensure idempotency without having to rely
on the driver as much. Additionally: the driver doesn't need to do any
kind of VCPI slot tracking anymore if it doesn't need it for it's own
internal state.
Additionally; this adds a new drm_dp_mst_atomic_check() helper which
must be used by atomic drivers to perform validity checks for the new
VCPI allocations incurred by a state.
Also: update the documentation and make it more obvious that these
/must/ be called by /all/ atomic drivers supporting MST.
Changes since v9:
* Add some missing changes that were requested by danvet that I forgot
about after I redid all of the kref stuff:
* Remove unnecessary state changes in intel_dp_mst_atomic_check
* Cleanup atomic check logic for VCPI allocations - all we need to check in
compute_config is whether or not this state disables a CRTC, then free
VCPI based off that
Changes since v8:
* Fix compile errors, whoops!
Changes since v7:
- Don't check for mixed stale/valid VCPI allocations, just rely on
connector registration to stop such erroneous modesets
Changes since v6:
- Keep a kref to all of the ports we have allocations on. This required
a good bit of changing to when we call drm_dp_find_vcpi_slots(),
mainly that we need to ensure that we only redo VCPI allocations on
actual mode or CRTC changes, not crtc_state->active changes.
Additionally, we no longer take the registration of the DRM connector
for each port into account because so long as we have a kref to the
port in the new or previous atomic state, the connector will stay
registered.
- Use the small changes to drm_dp_put_port() to add even more error
checking to make misusage of the helpers more obvious. I added this
after having to chase down various use-after-free conditions that
started popping up from the new helpers so no one else has to
troubleshoot that.
- Move some accidental DRM_DEBUG_KMS() calls to DRM_DEBUG_ATOMIC()
- Update documentation again, note that find/release() should both not be
called on the same port in a single atomic check phase (but multiple
calls to one or the other is OK)
Changes since v4:
- Don't skip the atomic checks for VCPI allocations if no new VCPI
allocations happen in a state. This makes the next change I'm about
to list here a lot easier to implement.
- Don't ignore VCPI allocations on destroyed ports, instead ensure that
when ports are destroyed and still have VCPI allocations in the
topology state, the only state changes allowed are releasing said
ports' VCPI. This prevents a state with a mix of VCPI allocations
from destroyed ports, and allocations from valid ports.
Changes since v3:
- Don't release VCPI allocations in the topology state immediately in
drm_dp_atomic_release_vcpi_slots(), instead mark them as 0 and skip
over them in drm_dp_mst_duplicate_state(). This makes it so
drm_dp_atomic_release_vcpi_slots() is still idempotent while also
throwing warnings if the driver messes up it's book keeping and tries
to release VCPI slots on a port that doesn't have any pre-existing
VCPI allocation - danvet
- Change mst_state/state in some debugging messages to "mst state"
Changes since v2:
- Use kmemdup() for duplicating MST state - danvet
- Move port validation out of duplicate state callback - danvet
- Handle looping through MST topology states in
drm_dp_mst_atomic_check() so the driver doesn't have to do it
- Fix documentation in drm_dp_atomic_find_vcpi_slots()
- Move the atomic check for each individual topology state into it's
own function, reduces indenting
- Don't consider "stale" MST ports when calculating the bandwidth
requirements. This is needed because originally we relied on the
state duplication functions to prune any stale ports from the new
state, which would prevent us from incorrectly considering their
bandwidth requirements alongside legitimate new payloads.
- Add function references in drm_dp_atomic_release_vcpi_slots() - danvet
- Annotate atomic VCPI and atomic check functions with __must_check
- danvet
Changes since v1:
- Don't use the now-removed ->atomic_check() for private objects hook,
just give drivers a function to call themselves
Signed-off-by: Lyude Paul <lyude@redhat.com>
Reviewed-by: Daniel Vetter <daniel@ffwll.ch>
Cc: David Airlie <airlied@redhat.com>
Cc: Jerry Zuo <Jerry.Zuo@amd.com>
Cc: Harry Wentland <harry.wentland@amd.com>
Cc: Juston Li <juston.li@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20190111005343.17443-19-lyude@redhat.com
2019-01-11 07:53:41 +07:00
|
|
|
struct drm_connector_state *old_conn_state =
|
|
|
|
drm_atomic_get_old_connector_state(state, connector);
|
|
|
|
struct intel_connector *intel_connector =
|
|
|
|
to_intel_connector(connector);
|
|
|
|
struct drm_crtc *new_crtc = new_conn_state->crtc;
|
2017-04-29 06:14:20 +07:00
|
|
|
struct drm_crtc_state *crtc_state;
|
drm/dp_mst: Start tracking per-port VCPI allocations
There has been a TODO waiting for quite a long time in
drm_dp_mst_topology.c:
/* We cannot rely on port->vcpi.num_slots to update
* topology_state->avail_slots as the port may not exist if the parent
* branch device was unplugged. This should be fixed by tracking
* per-port slot allocation in drm_dp_mst_topology_state instead of
* depending on the caller to tell us how many slots to release.
*/
That's not the only reason we should fix this: forcing the driver to
track the VCPI allocations throughout a state's atomic check is
error prone, because it means that extra care has to be taken with the
order that drm_dp_atomic_find_vcpi_slots() and
drm_dp_atomic_release_vcpi_slots() are called in in order to ensure
idempotency. Currently the only driver actually using these helpers,
i915, doesn't even do this correctly: multiple ->best_encoder() checks
with i915's current implementation would not be idempotent and would
over-allocate VCPI slots, something I learned trying to implement
fallback retraining in MST.
So: simplify this whole mess, and teach drm_dp_atomic_find_vcpi_slots()
and drm_dp_atomic_release_vcpi_slots() to track the VCPI allocations for
each port. This allows us to ensure idempotency without having to rely
on the driver as much. Additionally: the driver doesn't need to do any
kind of VCPI slot tracking anymore if it doesn't need it for it's own
internal state.
Additionally; this adds a new drm_dp_mst_atomic_check() helper which
must be used by atomic drivers to perform validity checks for the new
VCPI allocations incurred by a state.
Also: update the documentation and make it more obvious that these
/must/ be called by /all/ atomic drivers supporting MST.
Changes since v9:
* Add some missing changes that were requested by danvet that I forgot
about after I redid all of the kref stuff:
* Remove unnecessary state changes in intel_dp_mst_atomic_check
* Cleanup atomic check logic for VCPI allocations - all we need to check in
compute_config is whether or not this state disables a CRTC, then free
VCPI based off that
Changes since v8:
* Fix compile errors, whoops!
Changes since v7:
- Don't check for mixed stale/valid VCPI allocations, just rely on
connector registration to stop such erroneous modesets
Changes since v6:
- Keep a kref to all of the ports we have allocations on. This required
a good bit of changing to when we call drm_dp_find_vcpi_slots(),
mainly that we need to ensure that we only redo VCPI allocations on
actual mode or CRTC changes, not crtc_state->active changes.
Additionally, we no longer take the registration of the DRM connector
for each port into account because so long as we have a kref to the
port in the new or previous atomic state, the connector will stay
registered.
- Use the small changes to drm_dp_put_port() to add even more error
checking to make misusage of the helpers more obvious. I added this
after having to chase down various use-after-free conditions that
started popping up from the new helpers so no one else has to
troubleshoot that.
- Move some accidental DRM_DEBUG_KMS() calls to DRM_DEBUG_ATOMIC()
- Update documentation again, note that find/release() should both not be
called on the same port in a single atomic check phase (but multiple
calls to one or the other is OK)
Changes since v4:
- Don't skip the atomic checks for VCPI allocations if no new VCPI
allocations happen in a state. This makes the next change I'm about
to list here a lot easier to implement.
- Don't ignore VCPI allocations on destroyed ports, instead ensure that
when ports are destroyed and still have VCPI allocations in the
topology state, the only state changes allowed are releasing said
ports' VCPI. This prevents a state with a mix of VCPI allocations
from destroyed ports, and allocations from valid ports.
Changes since v3:
- Don't release VCPI allocations in the topology state immediately in
drm_dp_atomic_release_vcpi_slots(), instead mark them as 0 and skip
over them in drm_dp_mst_duplicate_state(). This makes it so
drm_dp_atomic_release_vcpi_slots() is still idempotent while also
throwing warnings if the driver messes up it's book keeping and tries
to release VCPI slots on a port that doesn't have any pre-existing
VCPI allocation - danvet
- Change mst_state/state in some debugging messages to "mst state"
Changes since v2:
- Use kmemdup() for duplicating MST state - danvet
- Move port validation out of duplicate state callback - danvet
- Handle looping through MST topology states in
drm_dp_mst_atomic_check() so the driver doesn't have to do it
- Fix documentation in drm_dp_atomic_find_vcpi_slots()
- Move the atomic check for each individual topology state into it's
own function, reduces indenting
- Don't consider "stale" MST ports when calculating the bandwidth
requirements. This is needed because originally we relied on the
state duplication functions to prune any stale ports from the new
state, which would prevent us from incorrectly considering their
bandwidth requirements alongside legitimate new payloads.
- Add function references in drm_dp_atomic_release_vcpi_slots() - danvet
- Annotate atomic VCPI and atomic check functions with __must_check
- danvet
Changes since v1:
- Don't use the now-removed ->atomic_check() for private objects hook,
just give drivers a function to call themselves
Signed-off-by: Lyude Paul <lyude@redhat.com>
Reviewed-by: Daniel Vetter <daniel@ffwll.ch>
Cc: David Airlie <airlied@redhat.com>
Cc: Jerry Zuo <Jerry.Zuo@amd.com>
Cc: Harry Wentland <harry.wentland@amd.com>
Cc: Juston Li <juston.li@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20190111005343.17443-19-lyude@redhat.com
2019-01-11 07:53:41 +07:00
|
|
|
struct drm_dp_mst_topology_mgr *mgr;
|
2019-03-26 21:25:51 +07:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = intel_digital_connector_atomic_check(connector, new_conn_state);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
drm/dp_mst: Start tracking per-port VCPI allocations
There has been a TODO waiting for quite a long time in
drm_dp_mst_topology.c:
/* We cannot rely on port->vcpi.num_slots to update
* topology_state->avail_slots as the port may not exist if the parent
* branch device was unplugged. This should be fixed by tracking
* per-port slot allocation in drm_dp_mst_topology_state instead of
* depending on the caller to tell us how many slots to release.
*/
That's not the only reason we should fix this: forcing the driver to
track the VCPI allocations throughout a state's atomic check is
error prone, because it means that extra care has to be taken with the
order that drm_dp_atomic_find_vcpi_slots() and
drm_dp_atomic_release_vcpi_slots() are called in in order to ensure
idempotency. Currently the only driver actually using these helpers,
i915, doesn't even do this correctly: multiple ->best_encoder() checks
with i915's current implementation would not be idempotent and would
over-allocate VCPI slots, something I learned trying to implement
fallback retraining in MST.
So: simplify this whole mess, and teach drm_dp_atomic_find_vcpi_slots()
and drm_dp_atomic_release_vcpi_slots() to track the VCPI allocations for
each port. This allows us to ensure idempotency without having to rely
on the driver as much. Additionally: the driver doesn't need to do any
kind of VCPI slot tracking anymore if it doesn't need it for it's own
internal state.
Additionally; this adds a new drm_dp_mst_atomic_check() helper which
must be used by atomic drivers to perform validity checks for the new
VCPI allocations incurred by a state.
Also: update the documentation and make it more obvious that these
/must/ be called by /all/ atomic drivers supporting MST.
Changes since v9:
* Add some missing changes that were requested by danvet that I forgot
about after I redid all of the kref stuff:
* Remove unnecessary state changes in intel_dp_mst_atomic_check
* Cleanup atomic check logic for VCPI allocations - all we need to check in
compute_config is whether or not this state disables a CRTC, then free
VCPI based off that
Changes since v8:
* Fix compile errors, whoops!
Changes since v7:
- Don't check for mixed stale/valid VCPI allocations, just rely on
connector registration to stop such erroneous modesets
Changes since v6:
- Keep a kref to all of the ports we have allocations on. This required
a good bit of changing to when we call drm_dp_find_vcpi_slots(),
mainly that we need to ensure that we only redo VCPI allocations on
actual mode or CRTC changes, not crtc_state->active changes.
Additionally, we no longer take the registration of the DRM connector
for each port into account because so long as we have a kref to the
port in the new or previous atomic state, the connector will stay
registered.
- Use the small changes to drm_dp_put_port() to add even more error
checking to make misusage of the helpers more obvious. I added this
after having to chase down various use-after-free conditions that
started popping up from the new helpers so no one else has to
troubleshoot that.
- Move some accidental DRM_DEBUG_KMS() calls to DRM_DEBUG_ATOMIC()
- Update documentation again, note that find/release() should both not be
called on the same port in a single atomic check phase (but multiple
calls to one or the other is OK)
Changes since v4:
- Don't skip the atomic checks for VCPI allocations if no new VCPI
allocations happen in a state. This makes the next change I'm about
to list here a lot easier to implement.
- Don't ignore VCPI allocations on destroyed ports, instead ensure that
when ports are destroyed and still have VCPI allocations in the
topology state, the only state changes allowed are releasing said
ports' VCPI. This prevents a state with a mix of VCPI allocations
from destroyed ports, and allocations from valid ports.
Changes since v3:
- Don't release VCPI allocations in the topology state immediately in
drm_dp_atomic_release_vcpi_slots(), instead mark them as 0 and skip
over them in drm_dp_mst_duplicate_state(). This makes it so
drm_dp_atomic_release_vcpi_slots() is still idempotent while also
throwing warnings if the driver messes up it's book keeping and tries
to release VCPI slots on a port that doesn't have any pre-existing
VCPI allocation - danvet
- Change mst_state/state in some debugging messages to "mst state"
Changes since v2:
- Use kmemdup() for duplicating MST state - danvet
- Move port validation out of duplicate state callback - danvet
- Handle looping through MST topology states in
drm_dp_mst_atomic_check() so the driver doesn't have to do it
- Fix documentation in drm_dp_atomic_find_vcpi_slots()
- Move the atomic check for each individual topology state into it's
own function, reduces indenting
- Don't consider "stale" MST ports when calculating the bandwidth
requirements. This is needed because originally we relied on the
state duplication functions to prune any stale ports from the new
state, which would prevent us from incorrectly considering their
bandwidth requirements alongside legitimate new payloads.
- Add function references in drm_dp_atomic_release_vcpi_slots() - danvet
- Annotate atomic VCPI and atomic check functions with __must_check
- danvet
Changes since v1:
- Don't use the now-removed ->atomic_check() for private objects hook,
just give drivers a function to call themselves
Signed-off-by: Lyude Paul <lyude@redhat.com>
Reviewed-by: Daniel Vetter <daniel@ffwll.ch>
Cc: David Airlie <airlied@redhat.com>
Cc: Jerry Zuo <Jerry.Zuo@amd.com>
Cc: Harry Wentland <harry.wentland@amd.com>
Cc: Juston Li <juston.li@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20190111005343.17443-19-lyude@redhat.com
2019-01-11 07:53:41 +07:00
|
|
|
|
|
|
|
if (!old_conn_state->crtc)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* We only want to free VCPI if this state disables the CRTC on this
|
|
|
|
* connector
|
|
|
|
*/
|
|
|
|
if (new_crtc) {
|
|
|
|
crtc_state = drm_atomic_get_new_crtc_state(state, new_crtc);
|
|
|
|
|
|
|
|
if (!crtc_state ||
|
|
|
|
!drm_atomic_crtc_needs_modeset(crtc_state) ||
|
|
|
|
crtc_state->enable)
|
|
|
|
return 0;
|
2017-04-29 06:14:20 +07:00
|
|
|
}
|
drm/dp_mst: Start tracking per-port VCPI allocations
There has been a TODO waiting for quite a long time in
drm_dp_mst_topology.c:
/* We cannot rely on port->vcpi.num_slots to update
* topology_state->avail_slots as the port may not exist if the parent
* branch device was unplugged. This should be fixed by tracking
* per-port slot allocation in drm_dp_mst_topology_state instead of
* depending on the caller to tell us how many slots to release.
*/
That's not the only reason we should fix this: forcing the driver to
track the VCPI allocations throughout a state's atomic check is
error prone, because it means that extra care has to be taken with the
order that drm_dp_atomic_find_vcpi_slots() and
drm_dp_atomic_release_vcpi_slots() are called in in order to ensure
idempotency. Currently the only driver actually using these helpers,
i915, doesn't even do this correctly: multiple ->best_encoder() checks
with i915's current implementation would not be idempotent and would
over-allocate VCPI slots, something I learned trying to implement
fallback retraining in MST.
So: simplify this whole mess, and teach drm_dp_atomic_find_vcpi_slots()
and drm_dp_atomic_release_vcpi_slots() to track the VCPI allocations for
each port. This allows us to ensure idempotency without having to rely
on the driver as much. Additionally: the driver doesn't need to do any
kind of VCPI slot tracking anymore if it doesn't need it for it's own
internal state.
Additionally; this adds a new drm_dp_mst_atomic_check() helper which
must be used by atomic drivers to perform validity checks for the new
VCPI allocations incurred by a state.
Also: update the documentation and make it more obvious that these
/must/ be called by /all/ atomic drivers supporting MST.
Changes since v9:
* Add some missing changes that were requested by danvet that I forgot
about after I redid all of the kref stuff:
* Remove unnecessary state changes in intel_dp_mst_atomic_check
* Cleanup atomic check logic for VCPI allocations - all we need to check in
compute_config is whether or not this state disables a CRTC, then free
VCPI based off that
Changes since v8:
* Fix compile errors, whoops!
Changes since v7:
- Don't check for mixed stale/valid VCPI allocations, just rely on
connector registration to stop such erroneous modesets
Changes since v6:
- Keep a kref to all of the ports we have allocations on. This required
a good bit of changing to when we call drm_dp_find_vcpi_slots(),
mainly that we need to ensure that we only redo VCPI allocations on
actual mode or CRTC changes, not crtc_state->active changes.
Additionally, we no longer take the registration of the DRM connector
for each port into account because so long as we have a kref to the
port in the new or previous atomic state, the connector will stay
registered.
- Use the small changes to drm_dp_put_port() to add even more error
checking to make misusage of the helpers more obvious. I added this
after having to chase down various use-after-free conditions that
started popping up from the new helpers so no one else has to
troubleshoot that.
- Move some accidental DRM_DEBUG_KMS() calls to DRM_DEBUG_ATOMIC()
- Update documentation again, note that find/release() should both not be
called on the same port in a single atomic check phase (but multiple
calls to one or the other is OK)
Changes since v4:
- Don't skip the atomic checks for VCPI allocations if no new VCPI
allocations happen in a state. This makes the next change I'm about
to list here a lot easier to implement.
- Don't ignore VCPI allocations on destroyed ports, instead ensure that
when ports are destroyed and still have VCPI allocations in the
topology state, the only state changes allowed are releasing said
ports' VCPI. This prevents a state with a mix of VCPI allocations
from destroyed ports, and allocations from valid ports.
Changes since v3:
- Don't release VCPI allocations in the topology state immediately in
drm_dp_atomic_release_vcpi_slots(), instead mark them as 0 and skip
over them in drm_dp_mst_duplicate_state(). This makes it so
drm_dp_atomic_release_vcpi_slots() is still idempotent while also
throwing warnings if the driver messes up it's book keeping and tries
to release VCPI slots on a port that doesn't have any pre-existing
VCPI allocation - danvet
- Change mst_state/state in some debugging messages to "mst state"
Changes since v2:
- Use kmemdup() for duplicating MST state - danvet
- Move port validation out of duplicate state callback - danvet
- Handle looping through MST topology states in
drm_dp_mst_atomic_check() so the driver doesn't have to do it
- Fix documentation in drm_dp_atomic_find_vcpi_slots()
- Move the atomic check for each individual topology state into it's
own function, reduces indenting
- Don't consider "stale" MST ports when calculating the bandwidth
requirements. This is needed because originally we relied on the
state duplication functions to prune any stale ports from the new
state, which would prevent us from incorrectly considering their
bandwidth requirements alongside legitimate new payloads.
- Add function references in drm_dp_atomic_release_vcpi_slots() - danvet
- Annotate atomic VCPI and atomic check functions with __must_check
- danvet
Changes since v1:
- Don't use the now-removed ->atomic_check() for private objects hook,
just give drivers a function to call themselves
Signed-off-by: Lyude Paul <lyude@redhat.com>
Reviewed-by: Daniel Vetter <daniel@ffwll.ch>
Cc: David Airlie <airlied@redhat.com>
Cc: Jerry Zuo <Jerry.Zuo@amd.com>
Cc: Harry Wentland <harry.wentland@amd.com>
Cc: Juston Li <juston.li@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20190111005343.17443-19-lyude@redhat.com
2019-01-11 07:53:41 +07:00
|
|
|
|
|
|
|
mgr = &enc_to_mst(old_conn_state->best_encoder)->primary->dp.mst_mgr;
|
|
|
|
ret = drm_dp_atomic_release_vcpi_slots(state, mgr,
|
|
|
|
intel_connector->port);
|
|
|
|
|
2017-04-29 06:14:20 +07:00
|
|
|
return ret;
|
2014-05-02 11:02:48 +07:00
|
|
|
}
|
|
|
|
|
2016-08-09 22:04:04 +07:00
|
|
|
static void intel_mst_disable_dp(struct intel_encoder *encoder,
|
2017-08-18 20:49:58 +07:00
|
|
|
const struct intel_crtc_state *old_crtc_state,
|
|
|
|
const struct drm_connector_state *old_conn_state)
|
2014-05-02 11:02:48 +07:00
|
|
|
{
|
|
|
|
struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
|
|
|
|
struct intel_digital_port *intel_dig_port = intel_mst->primary;
|
|
|
|
struct intel_dp *intel_dp = &intel_dig_port->dp;
|
2016-08-09 22:04:12 +07:00
|
|
|
struct intel_connector *connector =
|
|
|
|
to_intel_connector(old_conn_state->connector);
|
2014-05-02 11:02:48 +07:00
|
|
|
int ret;
|
|
|
|
|
2017-09-19 05:21:38 +07:00
|
|
|
DRM_DEBUG_KMS("active links %d\n", intel_dp->active_mst_links);
|
2014-05-02 11:02:48 +07:00
|
|
|
|
2016-08-09 22:04:12 +07:00
|
|
|
drm_dp_mst_reset_vcpi_slots(&intel_dp->mst_mgr, connector->port);
|
2014-05-02 11:02:48 +07:00
|
|
|
|
|
|
|
ret = drm_dp_update_payload_part1(&intel_dp->mst_mgr);
|
|
|
|
if (ret) {
|
|
|
|
DRM_ERROR("failed to update payload %d\n", ret);
|
|
|
|
}
|
2016-12-15 21:29:43 +07:00
|
|
|
if (old_crtc_state->has_audio)
|
2017-10-31 01:46:53 +07:00
|
|
|
intel_audio_codec_disable(encoder,
|
|
|
|
old_crtc_state, old_conn_state);
|
2014-05-02 11:02:48 +07:00
|
|
|
}
|
|
|
|
|
2016-08-09 22:04:04 +07:00
|
|
|
static void intel_mst_post_disable_dp(struct intel_encoder *encoder,
|
2017-08-18 20:49:58 +07:00
|
|
|
const struct intel_crtc_state *old_crtc_state,
|
|
|
|
const struct drm_connector_state *old_conn_state)
|
2014-05-02 11:02:48 +07:00
|
|
|
{
|
|
|
|
struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
|
|
|
|
struct intel_digital_port *intel_dig_port = intel_mst->primary;
|
|
|
|
struct intel_dp *intel_dp = &intel_dig_port->dp;
|
2016-08-09 22:04:12 +07:00
|
|
|
struct intel_connector *connector =
|
|
|
|
to_intel_connector(old_conn_state->connector);
|
2014-05-02 11:02:48 +07:00
|
|
|
|
2018-09-01 00:47:39 +07:00
|
|
|
intel_ddi_disable_pipe_clock(old_crtc_state);
|
|
|
|
|
2014-05-02 11:02:48 +07:00
|
|
|
/* this can fail */
|
|
|
|
drm_dp_check_act_status(&intel_dp->mst_mgr);
|
|
|
|
/* and this can also fail */
|
|
|
|
drm_dp_update_payload_part2(&intel_dp->mst_mgr);
|
|
|
|
|
2016-08-09 22:04:12 +07:00
|
|
|
drm_dp_mst_deallocate_vcpi(&intel_dp->mst_mgr, connector->port);
|
2014-05-02 11:02:48 +07:00
|
|
|
|
2017-10-03 21:22:11 +07:00
|
|
|
/*
|
|
|
|
* Power down mst path before disabling the port, otherwise we end
|
|
|
|
* up getting interrupts from the sink upon detecting link loss.
|
|
|
|
*/
|
|
|
|
drm_dp_send_power_updown_phy(&intel_dp->mst_mgr, connector->port,
|
|
|
|
false);
|
|
|
|
|
2016-08-05 23:05:42 +07:00
|
|
|
intel_dp->active_mst_links--;
|
2016-03-09 08:14:38 +07:00
|
|
|
|
|
|
|
intel_mst->connector = NULL;
|
2018-04-07 08:10:53 +07:00
|
|
|
if (intel_dp->active_mst_links == 0) {
|
|
|
|
intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_OFF);
|
2016-08-09 22:04:04 +07:00
|
|
|
intel_dig_port->base.post_disable(&intel_dig_port->base,
|
2017-10-28 02:31:27 +07:00
|
|
|
old_crtc_state, NULL);
|
2018-04-07 08:10:53 +07:00
|
|
|
}
|
2017-10-28 02:31:27 +07:00
|
|
|
|
2017-09-19 05:21:38 +07:00
|
|
|
DRM_DEBUG_KMS("active links %d\n", intel_dp->active_mst_links);
|
2014-05-02 11:02:48 +07:00
|
|
|
}
|
|
|
|
|
2017-10-27 20:43:48 +07:00
|
|
|
static void intel_mst_pre_pll_enable_dp(struct intel_encoder *encoder,
|
|
|
|
const struct intel_crtc_state *pipe_config,
|
|
|
|
const struct drm_connector_state *conn_state)
|
|
|
|
{
|
|
|
|
struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
|
|
|
|
struct intel_digital_port *intel_dig_port = intel_mst->primary;
|
|
|
|
struct intel_dp *intel_dp = &intel_dig_port->dp;
|
|
|
|
|
2018-11-08 06:54:49 +07:00
|
|
|
if (intel_dp->active_mst_links == 0)
|
2017-10-27 20:43:48 +07:00
|
|
|
intel_dig_port->base.pre_pll_enable(&intel_dig_port->base,
|
|
|
|
pipe_config, NULL);
|
|
|
|
}
|
|
|
|
|
2018-11-08 06:54:48 +07:00
|
|
|
static void intel_mst_post_pll_disable_dp(struct intel_encoder *encoder,
|
|
|
|
const struct intel_crtc_state *old_crtc_state,
|
|
|
|
const struct drm_connector_state *old_conn_state)
|
|
|
|
{
|
|
|
|
struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
|
|
|
|
struct intel_digital_port *intel_dig_port = intel_mst->primary;
|
|
|
|
struct intel_dp *intel_dp = &intel_dig_port->dp;
|
|
|
|
|
|
|
|
if (intel_dp->active_mst_links == 0)
|
|
|
|
intel_dig_port->base.post_pll_disable(&intel_dig_port->base,
|
|
|
|
old_crtc_state,
|
|
|
|
old_conn_state);
|
|
|
|
}
|
|
|
|
|
2016-08-09 22:04:04 +07:00
|
|
|
static void intel_mst_pre_enable_dp(struct intel_encoder *encoder,
|
2017-08-18 20:49:58 +07:00
|
|
|
const struct intel_crtc_state *pipe_config,
|
|
|
|
const struct drm_connector_state *conn_state)
|
2014-05-02 11:02:48 +07:00
|
|
|
{
|
|
|
|
struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
|
|
|
|
struct intel_digital_port *intel_dig_port = intel_mst->primary;
|
|
|
|
struct intel_dp *intel_dp = &intel_dig_port->dp;
|
2016-08-09 22:04:12 +07:00
|
|
|
struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
|
2017-11-09 22:24:34 +07:00
|
|
|
enum port port = intel_dig_port->base.port;
|
2016-08-09 22:04:12 +07:00
|
|
|
struct intel_connector *connector =
|
|
|
|
to_intel_connector(conn_state->connector);
|
2014-05-02 11:02:48 +07:00
|
|
|
int ret;
|
2019-01-16 16:15:19 +07:00
|
|
|
u32 temp;
|
2014-05-02 11:02:48 +07:00
|
|
|
|
2015-08-27 18:13:31 +07:00
|
|
|
/* MST encoders are bound to a crtc, not to a connector,
|
|
|
|
* force the mapping here for get_hw_state.
|
|
|
|
*/
|
2016-08-09 22:04:12 +07:00
|
|
|
connector->encoder = encoder;
|
|
|
|
intel_mst->connector = connector;
|
2015-08-27 18:13:31 +07:00
|
|
|
|
2017-09-19 05:21:38 +07:00
|
|
|
DRM_DEBUG_KMS("active links %d\n", intel_dp->active_mst_links);
|
2016-03-09 08:14:38 +07:00
|
|
|
|
2018-04-07 08:10:53 +07:00
|
|
|
if (intel_dp->active_mst_links == 0)
|
|
|
|
intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON);
|
|
|
|
|
2017-10-03 21:22:11 +07:00
|
|
|
drm_dp_send_power_updown_phy(&intel_dp->mst_mgr, connector->port, true);
|
2018-04-07 08:10:53 +07:00
|
|
|
|
2017-03-02 19:58:57 +07:00
|
|
|
if (intel_dp->active_mst_links == 0)
|
|
|
|
intel_dig_port->base.pre_enable(&intel_dig_port->base,
|
|
|
|
pipe_config, NULL);
|
2014-05-02 11:02:48 +07:00
|
|
|
|
|
|
|
ret = drm_dp_mst_allocate_vcpi(&intel_dp->mst_mgr,
|
2016-08-09 22:04:12 +07:00
|
|
|
connector->port,
|
2017-03-16 14:10:26 +07:00
|
|
|
pipe_config->pbn,
|
|
|
|
pipe_config->dp_m_n.tu);
|
2018-07-19 00:19:43 +07:00
|
|
|
if (!ret)
|
2014-05-02 11:02:48 +07:00
|
|
|
DRM_ERROR("failed to allocate vcpi\n");
|
|
|
|
|
2016-08-05 23:05:42 +07:00
|
|
|
intel_dp->active_mst_links++;
|
2014-05-02 11:02:48 +07:00
|
|
|
temp = I915_READ(DP_TP_STATUS(port));
|
|
|
|
I915_WRITE(DP_TP_STATUS(port), temp);
|
|
|
|
|
|
|
|
ret = drm_dp_update_payload_part1(&intel_dp->mst_mgr);
|
2018-09-01 00:47:39 +07:00
|
|
|
|
|
|
|
intel_ddi_enable_pipe_clock(pipe_config);
|
2014-05-02 11:02:48 +07:00
|
|
|
}
|
|
|
|
|
2016-08-09 22:04:04 +07:00
|
|
|
static void intel_mst_enable_dp(struct intel_encoder *encoder,
|
2017-08-18 20:49:58 +07:00
|
|
|
const struct intel_crtc_state *pipe_config,
|
|
|
|
const struct drm_connector_state *conn_state)
|
2014-05-02 11:02:48 +07:00
|
|
|
{
|
|
|
|
struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
|
|
|
|
struct intel_digital_port *intel_dig_port = intel_mst->primary;
|
|
|
|
struct intel_dp *intel_dp = &intel_dig_port->dp;
|
2016-08-09 22:04:12 +07:00
|
|
|
struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
|
2017-11-09 22:24:34 +07:00
|
|
|
enum port port = intel_dig_port->base.port;
|
2014-05-02 11:02:48 +07:00
|
|
|
|
2017-09-19 05:21:38 +07:00
|
|
|
DRM_DEBUG_KMS("active links %d\n", intel_dp->active_mst_links);
|
2014-05-02 11:02:48 +07:00
|
|
|
|
2019-03-26 04:49:39 +07:00
|
|
|
if (intel_wait_for_register(&dev_priv->uncore,
|
2016-06-30 21:33:11 +07:00
|
|
|
DP_TP_STATUS(port),
|
|
|
|
DP_TP_STATUS_ACT_SENT,
|
|
|
|
DP_TP_STATUS_ACT_SENT,
|
|
|
|
1))
|
2014-05-02 11:02:48 +07:00
|
|
|
DRM_ERROR("Timed out waiting for ACT sent\n");
|
|
|
|
|
2018-07-20 06:42:17 +07:00
|
|
|
drm_dp_check_act_status(&intel_dp->mst_mgr);
|
2014-05-02 11:02:48 +07:00
|
|
|
|
2018-07-20 06:42:17 +07:00
|
|
|
drm_dp_update_payload_part2(&intel_dp->mst_mgr);
|
2016-12-15 21:29:43 +07:00
|
|
|
if (pipe_config->has_audio)
|
2016-11-28 19:07:07 +07:00
|
|
|
intel_audio_codec_enable(encoder, pipe_config, conn_state);
|
2014-05-02 11:02:48 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool intel_dp_mst_enc_get_hw_state(struct intel_encoder *encoder,
|
|
|
|
enum pipe *pipe)
|
|
|
|
{
|
|
|
|
struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
|
|
|
|
*pipe = intel_mst->pipe;
|
2016-03-09 08:14:38 +07:00
|
|
|
if (intel_mst->connector)
|
2014-05-02 11:02:48 +07:00
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void intel_dp_mst_enc_get_config(struct intel_encoder *encoder,
|
2015-01-15 19:55:21 +07:00
|
|
|
struct intel_crtc_state *pipe_config)
|
2014-05-02 11:02:48 +07:00
|
|
|
{
|
|
|
|
struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
|
|
|
|
struct intel_digital_port *intel_dig_port = intel_mst->primary;
|
2017-10-24 16:52:14 +07:00
|
|
|
|
2017-10-28 02:31:28 +07:00
|
|
|
intel_ddi_get_config(&intel_dig_port->base, pipe_config);
|
2014-05-02 11:02:48 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int intel_dp_mst_get_ddc_modes(struct drm_connector *connector)
|
|
|
|
{
|
|
|
|
struct intel_connector *intel_connector = to_intel_connector(connector);
|
|
|
|
struct intel_dp *intel_dp = intel_connector->mst_port;
|
|
|
|
struct edid *edid;
|
|
|
|
int ret;
|
|
|
|
|
drm/atomic_helper: Stop modesets on unregistered connectors harder
Unfortunately, it appears our fix in:
commit b5d29843d8ef ("drm/atomic_helper: Allow DPMS On<->Off changes
for unregistered connectors")
Which attempted to work around the problems introduced by:
commit 4d80273976bf ("drm/atomic_helper: Disallow new modesets on
unregistered connectors")
Is still not the right solution, as modesets can still be triggered
outside of drm_atomic_set_crtc_for_connector().
So in order to fix this, while still being careful that we don't break
modesets that a driver may perform before being registered with
userspace, we replace connector->registered with a tristate member,
connector->registration_state. This allows us to keep track of whether
or not a connector is still initializing and hasn't been exposed to
userspace, is currently registered and exposed to userspace, or has been
legitimately removed from the system after having once been present.
Using this info, we can prevent userspace from performing new modesets
on unregistered connectors while still allowing the driver to perform
modesets on unregistered connectors before the driver has finished being
registered.
Changes since v1:
- Fix WARN_ON() in drm_connector_cleanup() that CI caught with this
patchset in igt@drv_module_reload@basic-reload-inject and
igt@drv_module_reload@basic-reload by checking if the connector is
registered instead of unregistered, as calling drm_connector_cleanup()
on a connector that hasn't been registered with userspace yet should
stay valid.
- Remove unregistered_connector_check(), and just go back to what we
were doing before in commit 4d80273976bf ("drm/atomic_helper: Disallow
new modesets on unregistered connectors") except replacing
READ_ONCE(connector->registered) with drm_connector_is_unregistered().
This gets rid of the behavior of allowing DPMS On<->Off, but that should
be fine as it's more consistent with the UAPI we had before - danvet
- s/drm_connector_unregistered/drm_connector_is_unregistered/ - danvet
- Update documentation, fix some typos.
Fixes: b5d29843d8ef ("drm/atomic_helper: Allow DPMS On<->Off changes for unregistered connectors")
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Cc: Rodrigo Vivi <rodrigo.vivi@intel.com>
Cc: stable@vger.kernel.org
Cc: David Airlie <airlied@linux.ie>
Signed-off-by: Lyude Paul <lyude@redhat.com>
Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: https://patchwork.freedesktop.org/patch/msgid/20181016203946.9601-1-lyude@redhat.com
2018-10-17 03:39:46 +07:00
|
|
|
if (drm_connector_is_unregistered(connector))
|
2016-03-09 08:14:38 +07:00
|
|
|
return intel_connector_update_modes(connector, NULL);
|
2014-05-02 11:02:48 +07:00
|
|
|
|
2016-03-09 08:14:38 +07:00
|
|
|
edid = drm_dp_mst_get_edid(connector, &intel_dp->mst_mgr, intel_connector->port);
|
2014-05-02 11:02:48 +07:00
|
|
|
ret = intel_connector_update_modes(connector, edid);
|
|
|
|
kfree(edid);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static enum drm_connector_status
|
2014-10-22 16:06:44 +07:00
|
|
|
intel_dp_mst_detect(struct drm_connector *connector, bool force)
|
2014-05-02 11:02:48 +07:00
|
|
|
{
|
|
|
|
struct intel_connector *intel_connector = to_intel_connector(connector);
|
|
|
|
struct intel_dp *intel_dp = intel_connector->mst_port;
|
|
|
|
|
drm/atomic_helper: Stop modesets on unregistered connectors harder
Unfortunately, it appears our fix in:
commit b5d29843d8ef ("drm/atomic_helper: Allow DPMS On<->Off changes
for unregistered connectors")
Which attempted to work around the problems introduced by:
commit 4d80273976bf ("drm/atomic_helper: Disallow new modesets on
unregistered connectors")
Is still not the right solution, as modesets can still be triggered
outside of drm_atomic_set_crtc_for_connector().
So in order to fix this, while still being careful that we don't break
modesets that a driver may perform before being registered with
userspace, we replace connector->registered with a tristate member,
connector->registration_state. This allows us to keep track of whether
or not a connector is still initializing and hasn't been exposed to
userspace, is currently registered and exposed to userspace, or has been
legitimately removed from the system after having once been present.
Using this info, we can prevent userspace from performing new modesets
on unregistered connectors while still allowing the driver to perform
modesets on unregistered connectors before the driver has finished being
registered.
Changes since v1:
- Fix WARN_ON() in drm_connector_cleanup() that CI caught with this
patchset in igt@drv_module_reload@basic-reload-inject and
igt@drv_module_reload@basic-reload by checking if the connector is
registered instead of unregistered, as calling drm_connector_cleanup()
on a connector that hasn't been registered with userspace yet should
stay valid.
- Remove unregistered_connector_check(), and just go back to what we
were doing before in commit 4d80273976bf ("drm/atomic_helper: Disallow
new modesets on unregistered connectors") except replacing
READ_ONCE(connector->registered) with drm_connector_is_unregistered().
This gets rid of the behavior of allowing DPMS On<->Off, but that should
be fine as it's more consistent with the UAPI we had before - danvet
- s/drm_connector_unregistered/drm_connector_is_unregistered/ - danvet
- Update documentation, fix some typos.
Fixes: b5d29843d8ef ("drm/atomic_helper: Allow DPMS On<->Off changes for unregistered connectors")
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Cc: Rodrigo Vivi <rodrigo.vivi@intel.com>
Cc: stable@vger.kernel.org
Cc: David Airlie <airlied@linux.ie>
Signed-off-by: Lyude Paul <lyude@redhat.com>
Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: https://patchwork.freedesktop.org/patch/msgid/20181016203946.9601-1-lyude@redhat.com
2018-10-17 03:39:46 +07:00
|
|
|
if (drm_connector_is_unregistered(connector))
|
2016-03-09 08:14:38 +07:00
|
|
|
return connector_status_disconnected;
|
2018-10-09 06:24:32 +07:00
|
|
|
return drm_dp_mst_detect_port(connector, &intel_dp->mst_mgr,
|
|
|
|
intel_connector->port);
|
2014-05-02 11:02:48 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static const struct drm_connector_funcs intel_dp_mst_connector_funcs = {
|
|
|
|
.detect = intel_dp_mst_detect,
|
|
|
|
.fill_modes = drm_helper_probe_single_connector_modes,
|
2019-03-26 21:25:51 +07:00
|
|
|
.atomic_get_property = intel_digital_connector_atomic_get_property,
|
|
|
|
.atomic_set_property = intel_digital_connector_atomic_set_property,
|
2016-06-24 20:00:15 +07:00
|
|
|
.late_register = intel_connector_register,
|
2016-06-17 17:40:33 +07:00
|
|
|
.early_unregister = intel_connector_unregister,
|
2018-10-09 21:11:03 +07:00
|
|
|
.destroy = intel_connector_destroy,
|
2015-01-23 07:50:32 +07:00
|
|
|
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
2019-03-26 21:25:51 +07:00
|
|
|
.atomic_duplicate_state = intel_digital_connector_duplicate_state,
|
2014-05-02 11:02:48 +07:00
|
|
|
};
|
|
|
|
|
|
|
|
static int intel_dp_mst_get_modes(struct drm_connector *connector)
|
|
|
|
{
|
|
|
|
return intel_dp_mst_get_ddc_modes(connector);
|
|
|
|
}
|
|
|
|
|
|
|
|
static enum drm_mode_status
|
|
|
|
intel_dp_mst_mode_valid(struct drm_connector *connector,
|
|
|
|
struct drm_display_mode *mode)
|
|
|
|
{
|
2016-11-16 03:59:06 +07:00
|
|
|
struct intel_connector *intel_connector = to_intel_connector(connector);
|
|
|
|
struct intel_dp *intel_dp = intel_connector->mst_port;
|
2016-02-02 20:16:40 +07:00
|
|
|
int max_dotclk = to_i915(connector->dev)->max_dotclk_freq;
|
2016-11-16 03:59:06 +07:00
|
|
|
int max_rate, mode_rate, max_lanes, max_link_clock;
|
|
|
|
|
drm/atomic_helper: Stop modesets on unregistered connectors harder
Unfortunately, it appears our fix in:
commit b5d29843d8ef ("drm/atomic_helper: Allow DPMS On<->Off changes
for unregistered connectors")
Which attempted to work around the problems introduced by:
commit 4d80273976bf ("drm/atomic_helper: Disallow new modesets on
unregistered connectors")
Is still not the right solution, as modesets can still be triggered
outside of drm_atomic_set_crtc_for_connector().
So in order to fix this, while still being careful that we don't break
modesets that a driver may perform before being registered with
userspace, we replace connector->registered with a tristate member,
connector->registration_state. This allows us to keep track of whether
or not a connector is still initializing and hasn't been exposed to
userspace, is currently registered and exposed to userspace, or has been
legitimately removed from the system after having once been present.
Using this info, we can prevent userspace from performing new modesets
on unregistered connectors while still allowing the driver to perform
modesets on unregistered connectors before the driver has finished being
registered.
Changes since v1:
- Fix WARN_ON() in drm_connector_cleanup() that CI caught with this
patchset in igt@drv_module_reload@basic-reload-inject and
igt@drv_module_reload@basic-reload by checking if the connector is
registered instead of unregistered, as calling drm_connector_cleanup()
on a connector that hasn't been registered with userspace yet should
stay valid.
- Remove unregistered_connector_check(), and just go back to what we
were doing before in commit 4d80273976bf ("drm/atomic_helper: Disallow
new modesets on unregistered connectors") except replacing
READ_ONCE(connector->registered) with drm_connector_is_unregistered().
This gets rid of the behavior of allowing DPMS On<->Off, but that should
be fine as it's more consistent with the UAPI we had before - danvet
- s/drm_connector_unregistered/drm_connector_is_unregistered/ - danvet
- Update documentation, fix some typos.
Fixes: b5d29843d8ef ("drm/atomic_helper: Allow DPMS On<->Off changes for unregistered connectors")
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Cc: Rodrigo Vivi <rodrigo.vivi@intel.com>
Cc: stable@vger.kernel.org
Cc: David Airlie <airlied@linux.ie>
Signed-off-by: Lyude Paul <lyude@redhat.com>
Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: https://patchwork.freedesktop.org/patch/msgid/20181016203946.9601-1-lyude@redhat.com
2018-10-17 03:39:46 +07:00
|
|
|
if (drm_connector_is_unregistered(connector))
|
2017-08-10 21:50:43 +07:00
|
|
|
return MODE_ERROR;
|
|
|
|
|
2018-05-24 19:54:03 +07:00
|
|
|
if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
|
|
|
|
return MODE_NO_DBLESCAN;
|
|
|
|
|
2016-11-16 03:59:06 +07:00
|
|
|
max_link_clock = intel_dp_max_link_rate(intel_dp);
|
2017-04-06 20:44:14 +07:00
|
|
|
max_lanes = intel_dp_max_lane_count(intel_dp);
|
2016-11-16 03:59:06 +07:00
|
|
|
|
|
|
|
max_rate = intel_dp_max_data_rate(max_link_clock, max_lanes);
|
2019-03-26 21:25:53 +07:00
|
|
|
mode_rate = intel_dp_link_required(mode->clock, 18);
|
2016-02-02 20:16:40 +07:00
|
|
|
|
2014-05-02 11:02:48 +07:00
|
|
|
/* TODO - validate mode against available PBN for link */
|
|
|
|
if (mode->clock < 10000)
|
|
|
|
return MODE_CLOCK_LOW;
|
|
|
|
|
|
|
|
if (mode->flags & DRM_MODE_FLAG_DBLCLK)
|
|
|
|
return MODE_H_ILLEGAL;
|
|
|
|
|
2016-11-16 03:59:06 +07:00
|
|
|
if (mode_rate > max_rate || mode->clock > max_dotclk)
|
2016-02-02 20:16:40 +07:00
|
|
|
return MODE_CLOCK_HIGH;
|
|
|
|
|
2014-05-02 11:02:48 +07:00
|
|
|
return MODE_OK;
|
|
|
|
}
|
|
|
|
|
2015-08-03 22:24:09 +07:00
|
|
|
static struct drm_encoder *intel_mst_atomic_best_encoder(struct drm_connector *connector,
|
|
|
|
struct drm_connector_state *state)
|
|
|
|
{
|
|
|
|
struct intel_connector *intel_connector = to_intel_connector(connector);
|
|
|
|
struct intel_dp *intel_dp = intel_connector->mst_port;
|
|
|
|
struct intel_crtc *crtc = to_intel_crtc(state->crtc);
|
|
|
|
|
|
|
|
return &intel_dp->mst_encoders[crtc->pipe]->base.base;
|
|
|
|
}
|
|
|
|
|
2014-05-02 11:02:48 +07:00
|
|
|
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,
|
2015-08-03 22:24:09 +07:00
|
|
|
.atomic_best_encoder = intel_mst_atomic_best_encoder,
|
2017-04-29 06:14:20 +07:00
|
|
|
.atomic_check = intel_dp_mst_atomic_check,
|
2014-05-02 11:02:48 +07:00
|
|
|
};
|
|
|
|
|
|
|
|
static void intel_dp_mst_encoder_destroy(struct drm_encoder *encoder)
|
|
|
|
{
|
|
|
|
struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
|
|
|
|
|
|
|
|
drm_encoder_cleanup(encoder);
|
|
|
|
kfree(intel_mst);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct drm_encoder_funcs intel_dp_mst_enc_funcs = {
|
|
|
|
.destroy = intel_dp_mst_encoder_destroy,
|
|
|
|
};
|
|
|
|
|
|
|
|
static bool intel_dp_mst_get_hw_state(struct intel_connector *connector)
|
|
|
|
{
|
2015-08-27 18:13:31 +07:00
|
|
|
if (connector->encoder && connector->base.state->crtc) {
|
2014-05-02 11:02:48 +07:00
|
|
|
enum pipe pipe;
|
|
|
|
if (!connector->encoder->get_hw_state(connector->encoder, &pipe))
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-05-13 16:38:36 +07:00
|
|
|
static struct drm_connector *intel_dp_add_mst_connector(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, const char *pathprop)
|
2014-05-02 11:02:48 +07:00
|
|
|
{
|
|
|
|
struct intel_dp *intel_dp = container_of(mgr, struct intel_dp, mst_mgr);
|
|
|
|
struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
|
|
|
|
struct drm_device *dev = intel_dig_port->base.base.dev;
|
2017-10-13 17:24:46 +07:00
|
|
|
struct drm_i915_private *dev_priv = to_i915(dev);
|
2014-05-02 11:02:48 +07:00
|
|
|
struct intel_connector *intel_connector;
|
|
|
|
struct drm_connector *connector;
|
2017-10-13 17:24:46 +07:00
|
|
|
enum pipe pipe;
|
2017-10-14 01:01:44 +07:00
|
|
|
int ret;
|
2014-05-02 11:02:48 +07:00
|
|
|
|
2015-04-10 14:59:10 +07:00
|
|
|
intel_connector = intel_connector_alloc();
|
2014-05-02 11:02:48 +07:00
|
|
|
if (!intel_connector)
|
|
|
|
return NULL;
|
|
|
|
|
2018-11-07 04:30:12 +07:00
|
|
|
intel_connector->get_hw_state = intel_dp_mst_get_hw_state;
|
|
|
|
intel_connector->mst_port = intel_dp;
|
|
|
|
intel_connector->port = port;
|
2019-01-11 07:53:33 +07:00
|
|
|
drm_dp_mst_get_port_malloc(port);
|
2018-11-07 04:30:12 +07:00
|
|
|
|
2014-05-02 11:02:48 +07:00
|
|
|
connector = &intel_connector->base;
|
2017-10-14 01:01:44 +07:00
|
|
|
ret = drm_connector_init(dev, connector, &intel_dp_mst_connector_funcs,
|
|
|
|
DRM_MODE_CONNECTOR_DisplayPort);
|
|
|
|
if (ret) {
|
|
|
|
intel_connector_free(intel_connector);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-05-02 11:02:48 +07:00
|
|
|
drm_connector_helper_add(connector, &intel_dp_mst_connector_helper_funcs);
|
|
|
|
|
2017-10-13 17:24:46 +07:00
|
|
|
for_each_pipe(dev_priv, pipe) {
|
2017-10-14 01:01:44 +07:00
|
|
|
struct drm_encoder *enc =
|
|
|
|
&intel_dp->mst_encoders[pipe]->base.base;
|
|
|
|
|
2018-07-09 15:40:07 +07:00
|
|
|
ret = drm_connector_attach_encoder(&intel_connector->base, enc);
|
2017-10-14 01:01:44 +07:00
|
|
|
if (ret)
|
|
|
|
goto err;
|
2014-05-02 11:02:48 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
drm_object_attach_property(&connector->base, dev->mode_config.path_property, 0);
|
2014-10-20 13:30:50 +07:00
|
|
|
drm_object_attach_property(&connector->base, dev->mode_config.tile_property, 0);
|
|
|
|
|
2018-07-09 15:40:08 +07:00
|
|
|
ret = drm_connector_set_path_property(connector, pathprop);
|
2017-10-14 01:01:44 +07:00
|
|
|
if (ret)
|
|
|
|
goto err;
|
|
|
|
|
2019-03-27 20:19:29 +07:00
|
|
|
intel_attach_force_audio_property(connector);
|
2019-03-26 21:25:51 +07:00
|
|
|
intel_attach_broadcast_rgb_property(connector);
|
2019-03-26 21:25:54 +07:00
|
|
|
drm_connector_attach_max_bpc_property(connector, 6, 12);
|
2019-03-26 21:25:51 +07:00
|
|
|
|
2015-09-16 14:55:23 +07:00
|
|
|
return connector;
|
2017-10-14 01:01:44 +07:00
|
|
|
|
|
|
|
err:
|
|
|
|
drm_connector_cleanup(connector);
|
|
|
|
return NULL;
|
2015-09-16 14:55:23 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void intel_dp_register_mst_connector(struct drm_connector *connector)
|
|
|
|
{
|
2017-07-04 22:18:22 +07:00
|
|
|
struct drm_i915_private *dev_priv = to_i915(connector->dev);
|
2016-06-24 20:00:14 +07:00
|
|
|
|
2017-07-04 22:18:22 +07:00
|
|
|
if (dev_priv->fbdev)
|
|
|
|
drm_fb_helper_add_one_connector(&dev_priv->fbdev->helper,
|
|
|
|
connector);
|
2016-06-24 20:00:14 +07:00
|
|
|
|
2017-07-04 22:18:22 +07:00
|
|
|
drm_connector_register(connector);
|
2014-05-02 11:02:48 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void intel_dp_destroy_mst_connector(struct drm_dp_mst_topology_mgr *mgr,
|
|
|
|
struct drm_connector *connector)
|
|
|
|
{
|
2017-07-04 22:18:22 +07:00
|
|
|
struct drm_i915_private *dev_priv = to_i915(connector->dev);
|
2015-08-06 18:47:36 +07:00
|
|
|
|
2017-09-19 05:21:37 +07:00
|
|
|
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id, connector->name);
|
2016-06-17 17:40:33 +07:00
|
|
|
drm_connector_unregister(connector);
|
drm/i915: Fix race condition in intel_dp_destroy_mst_connector()
After unplugging a DP MST display from the system, we have to go through
and destroy all of the DRM connectors associated with it since none of
them are valid anymore. Unfortunately, intel_dp_destroy_mst_connector()
doesn't do a good enough job of ensuring that throughout the destruction
process that no modesettings can be done with the connectors. As it is
right now, intel_dp_destroy_mst_connector() works like this:
* Take all modeset locks
* Clear the configuration of the crtc on the connector, if there is one
* Drop all modeset locks, this is required because of circular
dependency issues that arise with trying to remove the connector from
sysfs with modeset locks held
* Unregister the connector
* Take all modeset locks, again
* Do the rest of the required cleaning for destroying the connector
* Finally drop all modeset locks for good
This only works sometimes. During the destruction process, it's very
possible that a userspace application will attempt to do a modesetting
using the connector. When we drop the modeset locks, an ioctl handler
such as drm_mode_setcrtc has the oppurtunity to take all of the modeset
locks from us. When this happens, one thing leads to another and
eventually we end up committing a mode with the non-existent connector:
[drm:intel_dp_link_training_clock_recovery [i915]] *ERROR* failed to enable link training
[drm:intel_dp_aux_ch] dp_aux_ch timeout status 0x7cf0001f
[drm:intel_dp_start_link_train [i915]] *ERROR* failed to start channel equalization
[drm:intel_dp_aux_ch] dp_aux_ch timeout status 0x7cf0001f
[drm:intel_mst_pre_enable_dp [i915]] *ERROR* failed to allocate vcpi
And in some cases, such as with the T460s using an MST dock, this
results in breaking modesetting and/or panicking the system.
To work around this, we now unregister the connector at the very
beginning of intel_dp_destroy_mst_connector(), grab all the modesetting
locks, and then hold them until we finish the rest of the function.
CC: stable@vger.kernel.org
Signed-off-by: Lyude <cpaul@redhat.com>
Signed-off-by: Rob Clark <rclark@redhat.com>
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: http://patchwork.freedesktop.org/patch/msgid/1458155884-13877-1-git-send-email-cpaul@redhat.com
2016-03-17 02:18:04 +07:00
|
|
|
|
2017-07-04 22:18:22 +07:00
|
|
|
if (dev_priv->fbdev)
|
|
|
|
drm_fb_helper_remove_one_connector(&dev_priv->fbdev->helper,
|
|
|
|
connector);
|
2014-05-02 11:02:48 +07:00
|
|
|
|
2018-06-18 18:01:50 +07:00
|
|
|
drm_connector_put(connector);
|
2014-05-02 11:02:48 +07:00
|
|
|
}
|
|
|
|
|
2015-12-31 04:20:30 +07:00
|
|
|
static const struct drm_dp_mst_topology_cbs mst_cbs = {
|
2014-05-02 11:02:48 +07:00
|
|
|
.add_connector = intel_dp_add_mst_connector,
|
2015-09-16 14:55:23 +07:00
|
|
|
.register_connector = intel_dp_register_mst_connector,
|
2014-05-02 11:02:48 +07:00
|
|
|
.destroy_connector = intel_dp_destroy_mst_connector,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct intel_dp_mst_encoder *
|
|
|
|
intel_dp_create_fake_mst_encoder(struct intel_digital_port *intel_dig_port, enum pipe pipe)
|
|
|
|
{
|
|
|
|
struct intel_dp_mst_encoder *intel_mst;
|
|
|
|
struct intel_encoder *intel_encoder;
|
|
|
|
struct drm_device *dev = intel_dig_port->base.base.dev;
|
|
|
|
|
|
|
|
intel_mst = kzalloc(sizeof(*intel_mst), GFP_KERNEL);
|
|
|
|
|
|
|
|
if (!intel_mst)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
intel_mst->pipe = pipe;
|
|
|
|
intel_encoder = &intel_mst->base;
|
|
|
|
intel_mst->primary = intel_dig_port;
|
|
|
|
|
|
|
|
drm_encoder_init(dev, &intel_encoder->base, &intel_dp_mst_enc_funcs,
|
2016-05-28 00:59:24 +07:00
|
|
|
DRM_MODE_ENCODER_DPMST, "DP-MST %c", pipe_name(pipe));
|
2014-05-02 11:02:48 +07:00
|
|
|
|
|
|
|
intel_encoder->type = INTEL_OUTPUT_DP_MST;
|
2017-02-22 13:34:27 +07:00
|
|
|
intel_encoder->power_domain = intel_dig_port->base.power_domain;
|
2017-11-09 22:24:34 +07:00
|
|
|
intel_encoder->port = intel_dig_port->base.port;
|
2014-05-02 11:02:48 +07:00
|
|
|
intel_encoder->crtc_mask = 0x7;
|
|
|
|
intel_encoder->cloneable = 0;
|
|
|
|
|
|
|
|
intel_encoder->compute_config = intel_dp_mst_compute_config;
|
|
|
|
intel_encoder->disable = intel_mst_disable_dp;
|
|
|
|
intel_encoder->post_disable = intel_mst_post_disable_dp;
|
2017-10-27 20:43:48 +07:00
|
|
|
intel_encoder->pre_pll_enable = intel_mst_pre_pll_enable_dp;
|
2018-11-08 06:54:48 +07:00
|
|
|
intel_encoder->post_pll_disable = intel_mst_post_pll_disable_dp;
|
2014-05-02 11:02:48 +07:00
|
|
|
intel_encoder->pre_enable = intel_mst_pre_enable_dp;
|
|
|
|
intel_encoder->enable = intel_mst_enable_dp;
|
|
|
|
intel_encoder->get_hw_state = intel_dp_mst_enc_get_hw_state;
|
|
|
|
intel_encoder->get_config = intel_dp_mst_enc_get_config;
|
|
|
|
|
|
|
|
return intel_mst;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
intel_dp_create_fake_mst_encoders(struct intel_digital_port *intel_dig_port)
|
|
|
|
{
|
|
|
|
struct intel_dp *intel_dp = &intel_dig_port->dp;
|
2017-10-13 17:24:46 +07:00
|
|
|
struct drm_i915_private *dev_priv = to_i915(intel_dig_port->base.base.dev);
|
|
|
|
enum pipe pipe;
|
2014-05-02 11:02:48 +07:00
|
|
|
|
2017-10-13 17:24:46 +07:00
|
|
|
for_each_pipe(dev_priv, pipe)
|
|
|
|
intel_dp->mst_encoders[pipe] = intel_dp_create_fake_mst_encoder(intel_dig_port, pipe);
|
2014-05-02 11:02:48 +07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
intel_dp_mst_encoder_init(struct intel_digital_port *intel_dig_port, int conn_base_id)
|
|
|
|
{
|
|
|
|
struct intel_dp *intel_dp = &intel_dig_port->dp;
|
|
|
|
struct drm_device *dev = intel_dig_port->base.base.dev;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
intel_dp->can_mst = true;
|
|
|
|
intel_dp->mst_mgr.cbs = &mst_cbs;
|
|
|
|
|
|
|
|
/* create encoders */
|
|
|
|
intel_dp_create_fake_mst_encoders(intel_dig_port);
|
2017-01-25 06:49:29 +07:00
|
|
|
ret = drm_dp_mst_topology_mgr_init(&intel_dp->mst_mgr, dev,
|
|
|
|
&intel_dp->aux, 16, 3, conn_base_id);
|
2014-05-02 11:02:48 +07:00
|
|
|
if (ret) {
|
|
|
|
intel_dp->can_mst = false;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
intel_dp_mst_encoder_cleanup(struct intel_digital_port *intel_dig_port)
|
|
|
|
{
|
|
|
|
struct intel_dp *intel_dp = &intel_dig_port->dp;
|
|
|
|
|
|
|
|
if (!intel_dp->can_mst)
|
|
|
|
return;
|
|
|
|
|
|
|
|
drm_dp_mst_topology_mgr_destroy(&intel_dp->mst_mgr);
|
|
|
|
/* encoders will get killed by normal cleanup */
|
|
|
|
}
|