2018-10-15 12:47:01 +08:00
|
|
|
// SPDX-License-Identifier: MIT
|
2015-02-24 07:24:04 +08:00
|
|
|
|
|
|
|
#include <drm/drm_dp_mst_helper.h>
|
|
|
|
#include <drm/drm_fb_helper.h>
|
2019-06-08 16:02:40 +08:00
|
|
|
#include <drm/drm_file.h>
|
2019-01-18 05:03:34 +08:00
|
|
|
#include <drm/drm_probe_helper.h>
|
2015-02-24 07:24:04 +08:00
|
|
|
|
|
|
|
#include "atom.h"
|
|
|
|
#include "ni_reg.h"
|
2019-06-08 16:02:40 +08:00
|
|
|
#include "radeon.h"
|
2015-02-24 07:24:04 +08:00
|
|
|
|
|
|
|
static struct radeon_encoder *radeon_dp_create_fake_mst_encoder(struct radeon_connector *connector);
|
|
|
|
|
|
|
|
static int radeon_atom_set_enc_offset(int id)
|
|
|
|
{
|
|
|
|
static const int offsets[] = { EVERGREEN_CRTC0_REGISTER_OFFSET,
|
|
|
|
EVERGREEN_CRTC1_REGISTER_OFFSET,
|
|
|
|
EVERGREEN_CRTC2_REGISTER_OFFSET,
|
|
|
|
EVERGREEN_CRTC3_REGISTER_OFFSET,
|
|
|
|
EVERGREEN_CRTC4_REGISTER_OFFSET,
|
|
|
|
EVERGREEN_CRTC5_REGISTER_OFFSET,
|
|
|
|
0x13830 - 0x7030 };
|
|
|
|
|
|
|
|
return offsets[id];
|
|
|
|
}
|
|
|
|
|
|
|
|
static int radeon_dp_mst_set_be_cntl(struct radeon_encoder *primary,
|
|
|
|
struct radeon_encoder_mst *mst_enc,
|
|
|
|
enum radeon_hpd_id hpd, bool enable)
|
|
|
|
{
|
|
|
|
struct drm_device *dev = primary->base.dev;
|
|
|
|
struct radeon_device *rdev = dev->dev_private;
|
|
|
|
uint32_t reg;
|
|
|
|
int retries = 0;
|
|
|
|
uint32_t temp;
|
|
|
|
|
|
|
|
reg = RREG32(NI_DIG_BE_CNTL + primary->offset);
|
|
|
|
|
|
|
|
/* set MST mode */
|
|
|
|
reg &= ~NI_DIG_FE_DIG_MODE(7);
|
|
|
|
reg |= NI_DIG_FE_DIG_MODE(NI_DIG_MODE_DP_MST);
|
|
|
|
|
|
|
|
if (enable)
|
|
|
|
reg |= NI_DIG_FE_SOURCE_SELECT(1 << mst_enc->fe);
|
|
|
|
else
|
|
|
|
reg &= ~NI_DIG_FE_SOURCE_SELECT(1 << mst_enc->fe);
|
|
|
|
|
|
|
|
reg |= NI_DIG_HPD_SELECT(hpd);
|
|
|
|
DRM_DEBUG_KMS("writing 0x%08x 0x%08x\n", NI_DIG_BE_CNTL + primary->offset, reg);
|
|
|
|
WREG32(NI_DIG_BE_CNTL + primary->offset, reg);
|
|
|
|
|
|
|
|
if (enable) {
|
|
|
|
uint32_t offset = radeon_atom_set_enc_offset(mst_enc->fe);
|
|
|
|
|
|
|
|
do {
|
|
|
|
temp = RREG32(NI_DIG_FE_CNTL + offset);
|
|
|
|
} while ((temp & NI_DIG_SYMCLK_FE_ON) && retries++ < 10000);
|
|
|
|
if (retries == 10000)
|
|
|
|
DRM_ERROR("timed out waiting for FE %d %d\n", primary->offset, mst_enc->fe);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int radeon_dp_mst_set_stream_attrib(struct radeon_encoder *primary,
|
|
|
|
int stream_number,
|
|
|
|
int fe,
|
|
|
|
int slots)
|
|
|
|
{
|
|
|
|
struct drm_device *dev = primary->base.dev;
|
|
|
|
struct radeon_device *rdev = dev->dev_private;
|
|
|
|
u32 temp, val;
|
|
|
|
int retries = 0;
|
|
|
|
int satreg, satidx;
|
|
|
|
|
|
|
|
satreg = stream_number >> 1;
|
|
|
|
satidx = stream_number & 1;
|
|
|
|
|
|
|
|
temp = RREG32(NI_DP_MSE_SAT0 + satreg + primary->offset);
|
|
|
|
|
|
|
|
val = NI_DP_MSE_SAT_SLOT_COUNT0(slots) | NI_DP_MSE_SAT_SRC0(fe);
|
|
|
|
|
|
|
|
val <<= (16 * satidx);
|
|
|
|
|
|
|
|
temp &= ~(0xffff << (16 * satidx));
|
|
|
|
|
|
|
|
temp |= val;
|
|
|
|
|
|
|
|
DRM_DEBUG_KMS("writing 0x%08x 0x%08x\n", NI_DP_MSE_SAT0 + satreg + primary->offset, temp);
|
|
|
|
WREG32(NI_DP_MSE_SAT0 + satreg + primary->offset, temp);
|
|
|
|
|
|
|
|
WREG32(NI_DP_MSE_SAT_UPDATE + primary->offset, 1);
|
|
|
|
|
|
|
|
do {
|
2016-04-06 07:24:48 +08:00
|
|
|
unsigned value1, value2;
|
|
|
|
udelay(10);
|
2015-02-24 07:24:04 +08:00
|
|
|
temp = RREG32(NI_DP_MSE_SAT_UPDATE + primary->offset);
|
2016-04-06 07:24:48 +08:00
|
|
|
|
|
|
|
value1 = temp & NI_DP_MSE_SAT_UPDATE_MASK;
|
|
|
|
value2 = temp & NI_DP_MSE_16_MTP_KEEPOUT;
|
|
|
|
|
|
|
|
if (!value1 && !value2)
|
|
|
|
break;
|
|
|
|
} while (retries++ < 50);
|
2015-02-24 07:24:04 +08:00
|
|
|
|
|
|
|
if (retries == 10000)
|
|
|
|
DRM_ERROR("timed out waitin for SAT update %d\n", primary->offset);
|
|
|
|
|
|
|
|
/* MTP 16 ? */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int radeon_dp_mst_update_stream_attribs(struct radeon_connector *mst_conn,
|
|
|
|
struct radeon_encoder *primary)
|
|
|
|
{
|
|
|
|
struct drm_device *dev = mst_conn->base.dev;
|
|
|
|
struct stream_attribs new_attribs[6];
|
|
|
|
int i;
|
|
|
|
int idx = 0;
|
|
|
|
struct radeon_connector *radeon_connector;
|
|
|
|
struct drm_connector *connector;
|
|
|
|
|
|
|
|
memset(new_attribs, 0, sizeof(new_attribs));
|
|
|
|
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
|
|
|
|
struct radeon_encoder *subenc;
|
|
|
|
struct radeon_encoder_mst *mst_enc;
|
|
|
|
|
|
|
|
radeon_connector = to_radeon_connector(connector);
|
|
|
|
if (!radeon_connector->is_mst_connector)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (radeon_connector->mst_port != mst_conn)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
subenc = radeon_connector->mst_encoder;
|
|
|
|
mst_enc = subenc->enc_priv;
|
|
|
|
|
|
|
|
if (!mst_enc->enc_active)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
new_attribs[idx].fe = mst_enc->fe;
|
|
|
|
new_attribs[idx].slots = drm_dp_mst_get_vcpi_slots(&mst_conn->mst_mgr, mst_enc->port);
|
|
|
|
idx++;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < idx; i++) {
|
|
|
|
if (new_attribs[i].fe != mst_conn->cur_stream_attribs[i].fe ||
|
|
|
|
new_attribs[i].slots != mst_conn->cur_stream_attribs[i].slots) {
|
|
|
|
radeon_dp_mst_set_stream_attrib(primary, i, new_attribs[i].fe, new_attribs[i].slots);
|
|
|
|
mst_conn->cur_stream_attribs[i].fe = new_attribs[i].fe;
|
|
|
|
mst_conn->cur_stream_attribs[i].slots = new_attribs[i].slots;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = idx; i < mst_conn->enabled_attribs; i++) {
|
|
|
|
radeon_dp_mst_set_stream_attrib(primary, i, 0, 0);
|
|
|
|
mst_conn->cur_stream_attribs[i].fe = 0;
|
|
|
|
mst_conn->cur_stream_attribs[i].slots = 0;
|
|
|
|
}
|
|
|
|
mst_conn->enabled_attribs = idx;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-04-06 07:24:48 +08:00
|
|
|
static int radeon_dp_mst_set_vcp_size(struct radeon_encoder *mst, s64 avg_time_slots_per_mtp)
|
2015-02-24 07:24:04 +08:00
|
|
|
{
|
|
|
|
struct drm_device *dev = mst->base.dev;
|
|
|
|
struct radeon_device *rdev = dev->dev_private;
|
|
|
|
struct radeon_encoder_mst *mst_enc = mst->enc_priv;
|
|
|
|
uint32_t val, temp;
|
|
|
|
uint32_t offset = radeon_atom_set_enc_offset(mst_enc->fe);
|
|
|
|
int retries = 0;
|
2016-04-06 07:24:48 +08:00
|
|
|
uint32_t x = drm_fixp2int(avg_time_slots_per_mtp);
|
|
|
|
uint32_t y = drm_fixp2int_ceil((avg_time_slots_per_mtp - x) << 26);
|
2015-02-24 07:24:04 +08:00
|
|
|
|
|
|
|
val = NI_DP_MSE_RATE_X(x) | NI_DP_MSE_RATE_Y(y);
|
|
|
|
|
|
|
|
WREG32(NI_DP_MSE_RATE_CNTL + offset, val);
|
|
|
|
|
|
|
|
do {
|
|
|
|
temp = RREG32(NI_DP_MSE_RATE_UPDATE + offset);
|
2016-04-06 07:24:48 +08:00
|
|
|
udelay(10);
|
2015-02-24 07:24:04 +08:00
|
|
|
} while ((temp & 0x1) && (retries++ < 10000));
|
|
|
|
|
|
|
|
if (retries >= 10000)
|
|
|
|
DRM_ERROR("timed out wait for rate cntl %d\n", mst_enc->fe);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int radeon_dp_mst_get_ddc_modes(struct drm_connector *connector)
|
|
|
|
{
|
|
|
|
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
|
|
|
|
struct radeon_connector *master = radeon_connector->mst_port;
|
|
|
|
struct edid *edid;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
edid = drm_dp_mst_get_edid(connector, &master->mst_mgr, radeon_connector->port);
|
|
|
|
radeon_connector->edid = edid;
|
|
|
|
DRM_DEBUG_KMS("edid retrieved %p\n", edid);
|
|
|
|
if (radeon_connector->edid) {
|
2018-07-09 16:40:06 +08:00
|
|
|
drm_connector_update_edid_property(&radeon_connector->base, radeon_connector->edid);
|
2015-02-24 07:24:04 +08:00
|
|
|
ret = drm_add_edid_modes(&radeon_connector->base, radeon_connector->edid);
|
|
|
|
return ret;
|
|
|
|
}
|
2018-07-09 16:40:06 +08:00
|
|
|
drm_connector_update_edid_property(&radeon_connector->base, NULL);
|
2015-02-24 07:24:04 +08:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int radeon_dp_mst_get_modes(struct drm_connector *connector)
|
|
|
|
{
|
|
|
|
return radeon_dp_mst_get_ddc_modes(connector);
|
|
|
|
}
|
|
|
|
|
|
|
|
static enum drm_mode_status
|
|
|
|
radeon_dp_mst_mode_valid(struct drm_connector *connector,
|
|
|
|
struct drm_display_mode *mode)
|
|
|
|
{
|
|
|
|
/* 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;
|
|
|
|
|
|
|
|
return MODE_OK;
|
|
|
|
}
|
|
|
|
|
2016-09-30 16:13:02 +08:00
|
|
|
static struct
|
|
|
|
drm_encoder *radeon_mst_best_encoder(struct drm_connector *connector)
|
2015-02-24 07:24:04 +08:00
|
|
|
{
|
|
|
|
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
|
|
|
|
|
|
|
|
return &radeon_connector->mst_encoder->base;
|
|
|
|
}
|
|
|
|
|
drm/dp_mst: Protect drm_dp_mst_port members with locking
This is a complicated one. Essentially, there's currently a problem in the MST
core that hasn't really caused any issues that we're aware of (emphasis on "that
we're aware of"): locking.
When we go through and probe the link addresses and path resources in a
topology, we hold no locks when updating ports with said information. The
members I'm referring to in particular are:
- ldps
- ddps
- mcs
- pdt
- dpcd_rev
- num_sdp_streams
- num_sdp_stream_sinks
- available_pbn
- input
- connector
Now that we're handling UP requests asynchronously and will be using some of
the struct members mentioned above in atomic modesetting in the future for
features such as PBN validation, this is going to become a lot more important.
As well, the next few commits that prepare us for and introduce suspend/resume
reprobing will also need clear locking in order to prevent from additional
racing hilarities that we never could have hit in the past.
So, let's solve this issue by using &mgr->base.lock, the modesetting
lock which currently only protects &mgr->base.state. This works
perfectly because it allows us to avoid blocking connection_mutex
unnecessarily, and we can grab this in connector detection paths since
it's a ww mutex. We start by having drm_dp_mst_handle_up_req() hold this
when updating ports. For drm_dp_mst_handle_link_address_port() things
are a bit more complicated. As I've learned the hard way, we can grab
&mgr->lock.base for everything except for port->connector. See, our
normal driver probing paths end up generating this rather obvious
lockdep chain:
&drm->mode_config.mutex
-> crtc_ww_class_mutex/crtc_ww_class_acquire
-> &connector->mutex
However, sysfs grabs &drm->mode_config.mutex in order to protect itself
from connector state changing under it. Because this entails grabbing
kn->count, e.g. the lock that the kernel provides for protecting sysfs
contexts, we end up grabbing kn->count followed by
&drm->mode_config.mutex. This ends up creating an extremely rude chain:
&kn->count
-> &drm->mode_config.mutex
-> crtc_ww_class_mutex/crtc_ww_class_acquire
-> &connector->mutex
I mean, look at that thing! It's just evil!!! This gross thing ends up
making any calls to drm_connector_register()/drm_connector_unregister()
impossible when holding any kind of modesetting lock. This is annoying
because ideally, we always want to ensure that
drm_dp_mst_port->connector never changes when doing an atomic commit or
check that would affect the atomic topology state so that it can
reliably and easily be used from future DRM DP MST helpers to assist
with tasks such as scanning through the current VCPI allocations and
adding connectors which need to have their allocations updated in
response to a bandwidth change or the like.
Being able to hold &mgr->base.lock throughout the entire link probe
process would have been _great_, since we could prevent userspace from
ever seeing any states in-between individual port changes and as a
result likely end up with a much faster probe and more consistent
results from said probes. But without some rework of how we handle
connector probing in sysfs it's not at all currently possible. In the
future, maybe we can try using the sysfs locks to protect updates to
connector probing state and fix this mess.
So for now, to protect everything other than port->connector under
&mgr->base.lock and ensure that we still have the guarantee that atomic
check/commit contexts will never see port->connector change we use a
silly trick. See: port->connector only needs to change in order to
ensure that input ports (see the MST spec) never have a ghost connector
associated with them. But, there's nothing stopping us from simply
throwing the entire port out and creating a new one in order to maintain
that requirement while still keeping port->connector consistent across
the lifetime of the port in atomic check/commit contexts. For all
intended purposes this works fine, as we validate ports in any contexts
we care about before using them and as such will end up reporting the
connector as disconnected until it's port's destruction finalizes. So,
we just do that in cases where we detect port->input has transitioned
from true->false. We don't need to worry about the other direction,
since a port without a connector isn't visible to userspace and as such
doesn't need to be protected by &mgr->base.lock until we finish
registering a connector for it.
For updating members of drm_dp_mst_port other than port->connector, we
simply grab &mgr->base.lock in drm_dp_mst_link_probe_work() for already
registered ports, update said members and drop the lock before
potentially registering a connector and probing the link address of it's
children.
Finally, we modify drm_dp_mst_detect_port() to take a modesetting lock
acquisition context in order to acquire &mgr->base.lock under
&connection_mutex and convert all it's users over to using the
.detect_ctx probe hooks.
With that, we finally have well defined locking.
Changes since v4:
* Get rid of port->mutex, stop using connection_mutex and just use our own
modesetting lock - mgr->base.lock. Also, add a probe_lock that comes
before this patch.
* Just throw out ports that get changed from an output to an input, and
replace them with new ports. This lets us ensure that modesetting
contexts never see port->connector go from having a connector to being
NULL.
* Write an extremely detailed explanation of what problems this is
trying to fix, since there's a _lot_ of context here and I honestly
forgot some of it myself a couple times.
* Don't grab mgr->lock when reading port->mstb in
drm_dp_mst_handle_link_address_port(). It's not needed.
Cc: Juston Li <juston.li@intel.com>
Cc: Imre Deak <imre.deak@intel.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Harry Wentland <hwentlan@amd.com>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Reviewed-by: Sean Paul <sean@poorly.run>
Signed-off-by: Lyude Paul <lyude@redhat.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20191022023641.8026-7-lyude@redhat.com
2019-06-18 05:59:29 +08:00
|
|
|
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;
|
|
|
|
|
2021-04-07 11:10:04 +08:00
|
|
|
if (drm_connector_is_unregistered(connector))
|
|
|
|
return connector_status_disconnected;
|
|
|
|
|
drm/dp_mst: Protect drm_dp_mst_port members with locking
This is a complicated one. Essentially, there's currently a problem in the MST
core that hasn't really caused any issues that we're aware of (emphasis on "that
we're aware of"): locking.
When we go through and probe the link addresses and path resources in a
topology, we hold no locks when updating ports with said information. The
members I'm referring to in particular are:
- ldps
- ddps
- mcs
- pdt
- dpcd_rev
- num_sdp_streams
- num_sdp_stream_sinks
- available_pbn
- input
- connector
Now that we're handling UP requests asynchronously and will be using some of
the struct members mentioned above in atomic modesetting in the future for
features such as PBN validation, this is going to become a lot more important.
As well, the next few commits that prepare us for and introduce suspend/resume
reprobing will also need clear locking in order to prevent from additional
racing hilarities that we never could have hit in the past.
So, let's solve this issue by using &mgr->base.lock, the modesetting
lock which currently only protects &mgr->base.state. This works
perfectly because it allows us to avoid blocking connection_mutex
unnecessarily, and we can grab this in connector detection paths since
it's a ww mutex. We start by having drm_dp_mst_handle_up_req() hold this
when updating ports. For drm_dp_mst_handle_link_address_port() things
are a bit more complicated. As I've learned the hard way, we can grab
&mgr->lock.base for everything except for port->connector. See, our
normal driver probing paths end up generating this rather obvious
lockdep chain:
&drm->mode_config.mutex
-> crtc_ww_class_mutex/crtc_ww_class_acquire
-> &connector->mutex
However, sysfs grabs &drm->mode_config.mutex in order to protect itself
from connector state changing under it. Because this entails grabbing
kn->count, e.g. the lock that the kernel provides for protecting sysfs
contexts, we end up grabbing kn->count followed by
&drm->mode_config.mutex. This ends up creating an extremely rude chain:
&kn->count
-> &drm->mode_config.mutex
-> crtc_ww_class_mutex/crtc_ww_class_acquire
-> &connector->mutex
I mean, look at that thing! It's just evil!!! This gross thing ends up
making any calls to drm_connector_register()/drm_connector_unregister()
impossible when holding any kind of modesetting lock. This is annoying
because ideally, we always want to ensure that
drm_dp_mst_port->connector never changes when doing an atomic commit or
check that would affect the atomic topology state so that it can
reliably and easily be used from future DRM DP MST helpers to assist
with tasks such as scanning through the current VCPI allocations and
adding connectors which need to have their allocations updated in
response to a bandwidth change or the like.
Being able to hold &mgr->base.lock throughout the entire link probe
process would have been _great_, since we could prevent userspace from
ever seeing any states in-between individual port changes and as a
result likely end up with a much faster probe and more consistent
results from said probes. But without some rework of how we handle
connector probing in sysfs it's not at all currently possible. In the
future, maybe we can try using the sysfs locks to protect updates to
connector probing state and fix this mess.
So for now, to protect everything other than port->connector under
&mgr->base.lock and ensure that we still have the guarantee that atomic
check/commit contexts will never see port->connector change we use a
silly trick. See: port->connector only needs to change in order to
ensure that input ports (see the MST spec) never have a ghost connector
associated with them. But, there's nothing stopping us from simply
throwing the entire port out and creating a new one in order to maintain
that requirement while still keeping port->connector consistent across
the lifetime of the port in atomic check/commit contexts. For all
intended purposes this works fine, as we validate ports in any contexts
we care about before using them and as such will end up reporting the
connector as disconnected until it's port's destruction finalizes. So,
we just do that in cases where we detect port->input has transitioned
from true->false. We don't need to worry about the other direction,
since a port without a connector isn't visible to userspace and as such
doesn't need to be protected by &mgr->base.lock until we finish
registering a connector for it.
For updating members of drm_dp_mst_port other than port->connector, we
simply grab &mgr->base.lock in drm_dp_mst_link_probe_work() for already
registered ports, update said members and drop the lock before
potentially registering a connector and probing the link address of it's
children.
Finally, we modify drm_dp_mst_detect_port() to take a modesetting lock
acquisition context in order to acquire &mgr->base.lock under
&connection_mutex and convert all it's users over to using the
.detect_ctx probe hooks.
With that, we finally have well defined locking.
Changes since v4:
* Get rid of port->mutex, stop using connection_mutex and just use our own
modesetting lock - mgr->base.lock. Also, add a probe_lock that comes
before this patch.
* Just throw out ports that get changed from an output to an input, and
replace them with new ports. This lets us ensure that modesetting
contexts never see port->connector go from having a connector to being
NULL.
* Write an extremely detailed explanation of what problems this is
trying to fix, since there's a _lot_ of context here and I honestly
forgot some of it myself a couple times.
* Don't grab mgr->lock when reading port->mstb in
drm_dp_mst_handle_link_address_port(). It's not needed.
Cc: Juston Li <juston.li@intel.com>
Cc: Imre Deak <imre.deak@intel.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Harry Wentland <hwentlan@amd.com>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Reviewed-by: Sean Paul <sean@poorly.run>
Signed-off-by: Lyude Paul <lyude@redhat.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20191022023641.8026-7-lyude@redhat.com
2019-06-18 05:59:29 +08:00
|
|
|
return drm_dp_mst_detect_port(connector, ctx, &master->mst_mgr,
|
|
|
|
radeon_connector->port);
|
|
|
|
}
|
|
|
|
|
2015-02-24 07:24:04 +08:00
|
|
|
static const struct drm_connector_helper_funcs radeon_dp_mst_connector_helper_funcs = {
|
|
|
|
.get_modes = radeon_dp_mst_get_modes,
|
|
|
|
.mode_valid = radeon_dp_mst_mode_valid,
|
|
|
|
.best_encoder = radeon_mst_best_encoder,
|
drm/dp_mst: Protect drm_dp_mst_port members with locking
This is a complicated one. Essentially, there's currently a problem in the MST
core that hasn't really caused any issues that we're aware of (emphasis on "that
we're aware of"): locking.
When we go through and probe the link addresses and path resources in a
topology, we hold no locks when updating ports with said information. The
members I'm referring to in particular are:
- ldps
- ddps
- mcs
- pdt
- dpcd_rev
- num_sdp_streams
- num_sdp_stream_sinks
- available_pbn
- input
- connector
Now that we're handling UP requests asynchronously and will be using some of
the struct members mentioned above in atomic modesetting in the future for
features such as PBN validation, this is going to become a lot more important.
As well, the next few commits that prepare us for and introduce suspend/resume
reprobing will also need clear locking in order to prevent from additional
racing hilarities that we never could have hit in the past.
So, let's solve this issue by using &mgr->base.lock, the modesetting
lock which currently only protects &mgr->base.state. This works
perfectly because it allows us to avoid blocking connection_mutex
unnecessarily, and we can grab this in connector detection paths since
it's a ww mutex. We start by having drm_dp_mst_handle_up_req() hold this
when updating ports. For drm_dp_mst_handle_link_address_port() things
are a bit more complicated. As I've learned the hard way, we can grab
&mgr->lock.base for everything except for port->connector. See, our
normal driver probing paths end up generating this rather obvious
lockdep chain:
&drm->mode_config.mutex
-> crtc_ww_class_mutex/crtc_ww_class_acquire
-> &connector->mutex
However, sysfs grabs &drm->mode_config.mutex in order to protect itself
from connector state changing under it. Because this entails grabbing
kn->count, e.g. the lock that the kernel provides for protecting sysfs
contexts, we end up grabbing kn->count followed by
&drm->mode_config.mutex. This ends up creating an extremely rude chain:
&kn->count
-> &drm->mode_config.mutex
-> crtc_ww_class_mutex/crtc_ww_class_acquire
-> &connector->mutex
I mean, look at that thing! It's just evil!!! This gross thing ends up
making any calls to drm_connector_register()/drm_connector_unregister()
impossible when holding any kind of modesetting lock. This is annoying
because ideally, we always want to ensure that
drm_dp_mst_port->connector never changes when doing an atomic commit or
check that would affect the atomic topology state so that it can
reliably and easily be used from future DRM DP MST helpers to assist
with tasks such as scanning through the current VCPI allocations and
adding connectors which need to have their allocations updated in
response to a bandwidth change or the like.
Being able to hold &mgr->base.lock throughout the entire link probe
process would have been _great_, since we could prevent userspace from
ever seeing any states in-between individual port changes and as a
result likely end up with a much faster probe and more consistent
results from said probes. But without some rework of how we handle
connector probing in sysfs it's not at all currently possible. In the
future, maybe we can try using the sysfs locks to protect updates to
connector probing state and fix this mess.
So for now, to protect everything other than port->connector under
&mgr->base.lock and ensure that we still have the guarantee that atomic
check/commit contexts will never see port->connector change we use a
silly trick. See: port->connector only needs to change in order to
ensure that input ports (see the MST spec) never have a ghost connector
associated with them. But, there's nothing stopping us from simply
throwing the entire port out and creating a new one in order to maintain
that requirement while still keeping port->connector consistent across
the lifetime of the port in atomic check/commit contexts. For all
intended purposes this works fine, as we validate ports in any contexts
we care about before using them and as such will end up reporting the
connector as disconnected until it's port's destruction finalizes. So,
we just do that in cases where we detect port->input has transitioned
from true->false. We don't need to worry about the other direction,
since a port without a connector isn't visible to userspace and as such
doesn't need to be protected by &mgr->base.lock until we finish
registering a connector for it.
For updating members of drm_dp_mst_port other than port->connector, we
simply grab &mgr->base.lock in drm_dp_mst_link_probe_work() for already
registered ports, update said members and drop the lock before
potentially registering a connector and probing the link address of it's
children.
Finally, we modify drm_dp_mst_detect_port() to take a modesetting lock
acquisition context in order to acquire &mgr->base.lock under
&connection_mutex and convert all it's users over to using the
.detect_ctx probe hooks.
With that, we finally have well defined locking.
Changes since v4:
* Get rid of port->mutex, stop using connection_mutex and just use our own
modesetting lock - mgr->base.lock. Also, add a probe_lock that comes
before this patch.
* Just throw out ports that get changed from an output to an input, and
replace them with new ports. This lets us ensure that modesetting
contexts never see port->connector go from having a connector to being
NULL.
* Write an extremely detailed explanation of what problems this is
trying to fix, since there's a _lot_ of context here and I honestly
forgot some of it myself a couple times.
* Don't grab mgr->lock when reading port->mstb in
drm_dp_mst_handle_link_address_port(). It's not needed.
Cc: Juston Li <juston.li@intel.com>
Cc: Imre Deak <imre.deak@intel.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Harry Wentland <hwentlan@amd.com>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Reviewed-by: Sean Paul <sean@poorly.run>
Signed-off-by: Lyude Paul <lyude@redhat.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20191022023641.8026-7-lyude@redhat.com
2019-06-18 05:59:29 +08:00
|
|
|
.detect_ctx = radeon_dp_mst_detect,
|
2015-02-24 07:24:04 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static void
|
|
|
|
radeon_dp_mst_connector_destroy(struct drm_connector *connector)
|
|
|
|
{
|
|
|
|
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
|
|
|
|
struct radeon_encoder *radeon_encoder = radeon_connector->mst_encoder;
|
|
|
|
|
|
|
|
drm_encoder_cleanup(&radeon_encoder->base);
|
|
|
|
kfree(radeon_encoder);
|
|
|
|
drm_connector_cleanup(connector);
|
|
|
|
kfree(radeon_connector);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct drm_connector_funcs radeon_dp_mst_connector_funcs = {
|
2016-04-06 07:24:49 +08:00
|
|
|
.dpms = drm_helper_connector_dpms,
|
2015-02-24 07:24:04 +08:00
|
|
|
.fill_modes = drm_helper_probe_single_connector_modes,
|
|
|
|
.destroy = radeon_dp_mst_connector_destroy,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct drm_connector *radeon_dp_add_mst_connector(struct drm_dp_mst_topology_mgr *mgr,
|
|
|
|
struct drm_dp_mst_port *port,
|
|
|
|
const char *pathprop)
|
|
|
|
{
|
|
|
|
struct radeon_connector *master = container_of(mgr, struct radeon_connector, mst_mgr);
|
|
|
|
struct drm_device *dev = master->base.dev;
|
|
|
|
struct radeon_connector *radeon_connector;
|
|
|
|
struct drm_connector *connector;
|
|
|
|
|
|
|
|
radeon_connector = kzalloc(sizeof(*radeon_connector), GFP_KERNEL);
|
|
|
|
if (!radeon_connector)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
radeon_connector->is_mst_connector = true;
|
|
|
|
connector = &radeon_connector->base;
|
|
|
|
radeon_connector->port = port;
|
|
|
|
radeon_connector->mst_port = master;
|
|
|
|
DRM_DEBUG_KMS("\n");
|
|
|
|
|
|
|
|
drm_connector_init(dev, connector, &radeon_dp_mst_connector_funcs, DRM_MODE_CONNECTOR_DisplayPort);
|
|
|
|
drm_connector_helper_add(connector, &radeon_dp_mst_connector_helper_funcs);
|
|
|
|
radeon_connector->mst_encoder = radeon_dp_create_fake_mst_encoder(master);
|
|
|
|
|
|
|
|
drm_object_attach_property(&connector->base, dev->mode_config.path_property, 0);
|
2015-10-15 07:04:21 +08:00
|
|
|
drm_object_attach_property(&connector->base, dev->mode_config.tile_property, 0);
|
2018-07-09 16:40:08 +08:00
|
|
|
drm_connector_set_path_property(connector, pathprop);
|
2015-02-24 07:24:04 +08:00
|
|
|
|
2015-09-16 15:55:23 +08:00
|
|
|
return connector;
|
|
|
|
}
|
|
|
|
|
2018-01-02 16:02:51 +08:00
|
|
|
static const struct drm_dp_mst_topology_cbs mst_cbs = {
|
2015-02-24 07:24:04 +08:00
|
|
|
.add_connector = radeon_dp_add_mst_connector,
|
|
|
|
};
|
|
|
|
|
2016-09-30 16:13:02 +08:00
|
|
|
static struct
|
|
|
|
radeon_connector *radeon_mst_find_connector(struct drm_encoder *encoder)
|
2015-02-24 07:24:04 +08:00
|
|
|
{
|
|
|
|
struct drm_device *dev = encoder->dev;
|
|
|
|
struct drm_connector *connector;
|
|
|
|
|
|
|
|
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
|
|
|
|
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
|
|
|
|
if (!connector->encoder)
|
|
|
|
continue;
|
|
|
|
if (!radeon_connector->is_mst_connector)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
DRM_DEBUG_KMS("checking %p vs %p\n", connector->encoder, encoder);
|
|
|
|
if (connector->encoder == encoder)
|
|
|
|
return radeon_connector;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void radeon_dp_mst_prepare_pll(struct drm_crtc *crtc, struct drm_display_mode *mode)
|
|
|
|
{
|
|
|
|
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
|
|
|
|
struct drm_device *dev = crtc->dev;
|
|
|
|
struct radeon_device *rdev = dev->dev_private;
|
|
|
|
struct radeon_encoder *radeon_encoder = to_radeon_encoder(radeon_crtc->encoder);
|
|
|
|
struct radeon_encoder_mst *mst_enc = radeon_encoder->enc_priv;
|
|
|
|
struct radeon_connector *radeon_connector = radeon_mst_find_connector(&radeon_encoder->base);
|
|
|
|
int dp_clock;
|
|
|
|
struct radeon_connector_atom_dig *dig_connector = mst_enc->connector->con_priv;
|
|
|
|
|
|
|
|
if (radeon_connector) {
|
|
|
|
radeon_connector->pixelclock_for_modeset = mode->clock;
|
|
|
|
if (radeon_connector->base.display_info.bpc)
|
|
|
|
radeon_crtc->bpc = radeon_connector->base.display_info.bpc;
|
|
|
|
else
|
|
|
|
radeon_crtc->bpc = 8;
|
|
|
|
}
|
|
|
|
|
|
|
|
DRM_DEBUG_KMS("dp_clock %p %d\n", dig_connector, dig_connector->dp_clock);
|
|
|
|
dp_clock = dig_connector->dp_clock;
|
|
|
|
radeon_crtc->ss_enabled =
|
|
|
|
radeon_atombios_get_asic_ss_info(rdev, &radeon_crtc->ss,
|
|
|
|
ASIC_INTERNAL_SS_ON_DP,
|
|
|
|
dp_clock);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
radeon_mst_encoder_dpms(struct drm_encoder *encoder, int mode)
|
|
|
|
{
|
|
|
|
struct drm_device *dev = encoder->dev;
|
|
|
|
struct radeon_device *rdev = dev->dev_private;
|
|
|
|
struct radeon_encoder *radeon_encoder, *primary;
|
|
|
|
struct radeon_encoder_mst *mst_enc;
|
|
|
|
struct radeon_encoder_atom_dig *dig_enc;
|
|
|
|
struct radeon_connector *radeon_connector;
|
|
|
|
struct drm_crtc *crtc;
|
|
|
|
struct radeon_crtc *radeon_crtc;
|
2020-11-11 03:30:54 +08:00
|
|
|
int slots;
|
2016-04-06 07:24:48 +08:00
|
|
|
s64 fixed_pbn, fixed_pbn_per_slot, avg_time_slots_per_mtp;
|
2015-02-24 07:24:04 +08:00
|
|
|
if (!ASIC_IS_DCE5(rdev)) {
|
|
|
|
DRM_ERROR("got mst dpms on non-DCE5\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
radeon_connector = radeon_mst_find_connector(encoder);
|
|
|
|
if (!radeon_connector)
|
|
|
|
return;
|
|
|
|
|
|
|
|
radeon_encoder = to_radeon_encoder(encoder);
|
|
|
|
|
|
|
|
mst_enc = radeon_encoder->enc_priv;
|
|
|
|
|
|
|
|
primary = mst_enc->primary;
|
|
|
|
|
|
|
|
dig_enc = primary->enc_priv;
|
|
|
|
|
|
|
|
crtc = encoder->crtc;
|
|
|
|
DRM_DEBUG_KMS("got connector %d\n", dig_enc->active_mst_links);
|
|
|
|
|
|
|
|
switch (mode) {
|
|
|
|
case DRM_MODE_DPMS_ON:
|
|
|
|
dig_enc->active_mst_links++;
|
|
|
|
|
|
|
|
radeon_crtc = to_radeon_crtc(crtc);
|
|
|
|
|
|
|
|
if (dig_enc->active_mst_links == 1) {
|
|
|
|
mst_enc->fe = dig_enc->dig_encoder;
|
|
|
|
mst_enc->fe_from_be = true;
|
|
|
|
atombios_set_mst_encoder_crtc_source(encoder, mst_enc->fe);
|
|
|
|
|
|
|
|
atombios_dig_encoder_setup(&primary->base, ATOM_ENCODER_CMD_SETUP, 0);
|
|
|
|
atombios_dig_transmitter_setup2(&primary->base, ATOM_TRANSMITTER_ACTION_ENABLE,
|
|
|
|
0, 0, dig_enc->dig_encoder);
|
|
|
|
|
|
|
|
if (radeon_dp_needs_link_train(mst_enc->connector) ||
|
|
|
|
dig_enc->active_mst_links == 1) {
|
|
|
|
radeon_dp_link_train(&primary->base, &mst_enc->connector->base);
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
mst_enc->fe = radeon_atom_pick_dig_encoder(encoder, radeon_crtc->crtc_id);
|
|
|
|
if (mst_enc->fe == -1)
|
|
|
|
DRM_ERROR("failed to get frontend for dig encoder\n");
|
|
|
|
mst_enc->fe_from_be = false;
|
|
|
|
atombios_set_mst_encoder_crtc_source(encoder, mst_enc->fe);
|
|
|
|
}
|
|
|
|
|
|
|
|
DRM_DEBUG_KMS("dig encoder is %d %d %d\n", dig_enc->dig_encoder,
|
|
|
|
dig_enc->linkb, radeon_crtc->crtc_id);
|
|
|
|
|
2017-03-16 15:10:26 +08:00
|
|
|
slots = drm_dp_find_vcpi_slots(&radeon_connector->mst_port->mst_mgr,
|
|
|
|
mst_enc->pbn);
|
2020-11-11 03:30:54 +08:00
|
|
|
drm_dp_mst_allocate_vcpi(&radeon_connector->mst_port->mst_mgr,
|
|
|
|
radeon_connector->port,
|
|
|
|
mst_enc->pbn, slots);
|
2021-10-26 06:38:22 +08:00
|
|
|
drm_dp_update_payload_part1(&radeon_connector->mst_port->mst_mgr, 1);
|
2015-02-24 07:24:04 +08:00
|
|
|
|
|
|
|
radeon_dp_mst_set_be_cntl(primary, mst_enc,
|
|
|
|
radeon_connector->mst_port->hpd.hpd, true);
|
|
|
|
|
|
|
|
mst_enc->enc_active = true;
|
|
|
|
radeon_dp_mst_update_stream_attribs(radeon_connector->mst_port, primary);
|
2016-04-06 07:24:48 +08:00
|
|
|
|
|
|
|
fixed_pbn = drm_int2fixp(mst_enc->pbn);
|
|
|
|
fixed_pbn_per_slot = drm_int2fixp(radeon_connector->mst_port->mst_mgr.pbn_div);
|
|
|
|
avg_time_slots_per_mtp = drm_fixp_div(fixed_pbn, fixed_pbn_per_slot);
|
|
|
|
radeon_dp_mst_set_vcp_size(radeon_encoder, avg_time_slots_per_mtp);
|
2015-02-24 07:24:04 +08:00
|
|
|
|
|
|
|
atombios_dig_encoder_setup2(&primary->base, ATOM_ENCODER_CMD_DP_VIDEO_ON, 0,
|
|
|
|
mst_enc->fe);
|
2020-11-11 03:30:54 +08:00
|
|
|
drm_dp_check_act_status(&radeon_connector->mst_port->mst_mgr);
|
2015-02-24 07:24:04 +08:00
|
|
|
|
2020-11-11 03:30:54 +08:00
|
|
|
drm_dp_update_payload_part2(&radeon_connector->mst_port->mst_mgr);
|
2015-02-24 07:24:04 +08:00
|
|
|
|
|
|
|
break;
|
|
|
|
case DRM_MODE_DPMS_STANDBY:
|
|
|
|
case DRM_MODE_DPMS_SUSPEND:
|
|
|
|
case DRM_MODE_DPMS_OFF:
|
|
|
|
DRM_ERROR("DPMS OFF %d\n", dig_enc->active_mst_links);
|
|
|
|
|
|
|
|
if (!mst_enc->enc_active)
|
|
|
|
return;
|
|
|
|
|
|
|
|
drm_dp_mst_reset_vcpi_slots(&radeon_connector->mst_port->mst_mgr, mst_enc->port);
|
2021-10-26 06:38:22 +08:00
|
|
|
drm_dp_update_payload_part1(&radeon_connector->mst_port->mst_mgr, 1);
|
2015-02-24 07:24:04 +08:00
|
|
|
|
|
|
|
drm_dp_check_act_status(&radeon_connector->mst_port->mst_mgr);
|
|
|
|
/* and this can also fail */
|
|
|
|
drm_dp_update_payload_part2(&radeon_connector->mst_port->mst_mgr);
|
|
|
|
|
|
|
|
drm_dp_mst_deallocate_vcpi(&radeon_connector->mst_port->mst_mgr, mst_enc->port);
|
|
|
|
|
|
|
|
mst_enc->enc_active = false;
|
|
|
|
radeon_dp_mst_update_stream_attribs(radeon_connector->mst_port, primary);
|
|
|
|
|
|
|
|
radeon_dp_mst_set_be_cntl(primary, mst_enc,
|
|
|
|
radeon_connector->mst_port->hpd.hpd, false);
|
|
|
|
atombios_dig_encoder_setup2(&primary->base, ATOM_ENCODER_CMD_DP_VIDEO_OFF, 0,
|
|
|
|
mst_enc->fe);
|
|
|
|
|
|
|
|
if (!mst_enc->fe_from_be)
|
|
|
|
radeon_atom_release_dig_encoder(rdev, mst_enc->fe);
|
|
|
|
|
|
|
|
mst_enc->fe_from_be = false;
|
|
|
|
dig_enc->active_mst_links--;
|
|
|
|
if (dig_enc->active_mst_links == 0) {
|
|
|
|
/* drop link */
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool radeon_mst_mode_fixup(struct drm_encoder *encoder,
|
|
|
|
const struct drm_display_mode *mode,
|
|
|
|
struct drm_display_mode *adjusted_mode)
|
|
|
|
{
|
|
|
|
struct radeon_encoder_mst *mst_enc;
|
|
|
|
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
|
2016-03-22 07:38:19 +08:00
|
|
|
struct radeon_connector_atom_dig *dig_connector;
|
2015-02-24 07:24:04 +08:00
|
|
|
int bpp = 24;
|
|
|
|
|
|
|
|
mst_enc = radeon_encoder->enc_priv;
|
|
|
|
|
2019-08-21 22:33:26 +08:00
|
|
|
mst_enc->pbn = drm_dp_calc_pbn_mode(adjusted_mode->clock, bpp, false);
|
2015-02-24 07:24:04 +08:00
|
|
|
|
|
|
|
mst_enc->primary->active_device = mst_enc->primary->devices & mst_enc->connector->devices;
|
|
|
|
DRM_DEBUG_KMS("setting active device to %08x from %08x %08x for encoder %d\n",
|
|
|
|
mst_enc->primary->active_device, mst_enc->primary->devices,
|
|
|
|
mst_enc->connector->devices, mst_enc->primary->base.encoder_type);
|
|
|
|
|
|
|
|
|
|
|
|
drm_mode_set_crtcinfo(adjusted_mode, 0);
|
2016-03-22 07:38:19 +08:00
|
|
|
dig_connector = mst_enc->connector->con_priv;
|
|
|
|
dig_connector->dp_lane_count = drm_dp_max_lane_count(dig_connector->dpcd);
|
|
|
|
dig_connector->dp_clock = drm_dp_max_link_rate(dig_connector->dpcd);
|
|
|
|
DRM_DEBUG_KMS("dig clock %p %d %d\n", dig_connector,
|
|
|
|
dig_connector->dp_lane_count, dig_connector->dp_clock);
|
2015-02-24 07:24:04 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void radeon_mst_encoder_prepare(struct drm_encoder *encoder)
|
|
|
|
{
|
|
|
|
struct radeon_connector *radeon_connector;
|
|
|
|
struct radeon_encoder *radeon_encoder, *primary;
|
|
|
|
struct radeon_encoder_mst *mst_enc;
|
|
|
|
struct radeon_encoder_atom_dig *dig_enc;
|
|
|
|
|
|
|
|
radeon_connector = radeon_mst_find_connector(encoder);
|
|
|
|
if (!radeon_connector) {
|
|
|
|
DRM_DEBUG_KMS("failed to find connector %p\n", encoder);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
radeon_encoder = to_radeon_encoder(encoder);
|
|
|
|
|
|
|
|
radeon_mst_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
|
|
|
|
|
|
|
|
mst_enc = radeon_encoder->enc_priv;
|
|
|
|
|
|
|
|
primary = mst_enc->primary;
|
|
|
|
|
|
|
|
dig_enc = primary->enc_priv;
|
|
|
|
|
|
|
|
mst_enc->port = radeon_connector->port;
|
|
|
|
|
|
|
|
if (dig_enc->dig_encoder == -1) {
|
|
|
|
dig_enc->dig_encoder = radeon_atom_pick_dig_encoder(&primary->base, -1);
|
|
|
|
primary->offset = radeon_atom_set_enc_offset(dig_enc->dig_encoder);
|
|
|
|
atombios_set_mst_encoder_crtc_source(encoder, dig_enc->dig_encoder);
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
DRM_DEBUG_KMS("%d %d\n", dig_enc->dig_encoder, primary->offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
radeon_mst_encoder_mode_set(struct drm_encoder *encoder,
|
|
|
|
struct drm_display_mode *mode,
|
|
|
|
struct drm_display_mode *adjusted_mode)
|
|
|
|
{
|
|
|
|
DRM_DEBUG_KMS("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void radeon_mst_encoder_commit(struct drm_encoder *encoder)
|
|
|
|
{
|
|
|
|
radeon_mst_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
|
|
|
|
DRM_DEBUG_KMS("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct drm_encoder_helper_funcs radeon_mst_helper_funcs = {
|
|
|
|
.dpms = radeon_mst_encoder_dpms,
|
|
|
|
.mode_fixup = radeon_mst_mode_fixup,
|
|
|
|
.prepare = radeon_mst_encoder_prepare,
|
|
|
|
.mode_set = radeon_mst_encoder_mode_set,
|
|
|
|
.commit = radeon_mst_encoder_commit,
|
|
|
|
};
|
|
|
|
|
2016-09-30 16:13:02 +08:00
|
|
|
static void radeon_dp_mst_encoder_destroy(struct drm_encoder *encoder)
|
2015-02-24 07:24:04 +08:00
|
|
|
{
|
|
|
|
drm_encoder_cleanup(encoder);
|
|
|
|
kfree(encoder);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct drm_encoder_funcs radeon_dp_mst_enc_funcs = {
|
|
|
|
.destroy = radeon_dp_mst_encoder_destroy,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct radeon_encoder *
|
|
|
|
radeon_dp_create_fake_mst_encoder(struct radeon_connector *connector)
|
|
|
|
{
|
|
|
|
struct drm_device *dev = connector->base.dev;
|
|
|
|
struct radeon_device *rdev = dev->dev_private;
|
|
|
|
struct radeon_encoder *radeon_encoder;
|
|
|
|
struct radeon_encoder_mst *mst_enc;
|
|
|
|
struct drm_encoder *encoder;
|
2015-04-13 16:21:40 +08:00
|
|
|
const struct drm_connector_helper_funcs *connector_funcs = connector->base.helper_private;
|
2015-02-24 07:24:04 +08:00
|
|
|
struct drm_encoder *enc_master = connector_funcs->best_encoder(&connector->base);
|
|
|
|
|
|
|
|
DRM_DEBUG_KMS("enc master is %p\n", enc_master);
|
|
|
|
radeon_encoder = kzalloc(sizeof(*radeon_encoder), GFP_KERNEL);
|
|
|
|
if (!radeon_encoder)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
radeon_encoder->enc_priv = kzalloc(sizeof(*mst_enc), GFP_KERNEL);
|
|
|
|
if (!radeon_encoder->enc_priv) {
|
|
|
|
kfree(radeon_encoder);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
encoder = &radeon_encoder->base;
|
|
|
|
switch (rdev->num_crtc) {
|
|
|
|
case 1:
|
|
|
|
encoder->possible_crtcs = 0x1;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
default:
|
|
|
|
encoder->possible_crtcs = 0x3;
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
encoder->possible_crtcs = 0xf;
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
encoder->possible_crtcs = 0x3f;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
drm_encoder_init(dev, &radeon_encoder->base, &radeon_dp_mst_enc_funcs,
|
drm: Pass 'name' to drm_encoder_init()
Done with coccinelle for the most part. However, it thinks '...' is
part of the semantic patch, so I put an 'int DOTDOTDOT' placeholder
in its place and got rid of it with sed afterwards.
@@
identifier dev, encoder, funcs;
@@
int drm_encoder_init(struct drm_device *dev,
struct drm_encoder *encoder,
const struct drm_encoder_funcs *funcs,
int encoder_type
+ ,const char *name, int DOTDOTDOT
)
{ ... }
@@
identifier dev, encoder, funcs;
@@
int drm_encoder_init(struct drm_device *dev,
struct drm_encoder *encoder,
const struct drm_encoder_funcs *funcs,
int encoder_type
+ ,const char *name, int DOTDOTDOT
);
@@
expression E1, E2, E3, E4;
@@
drm_encoder_init(E1, E2, E3, E4
+ ,NULL
)
v2: Add ', or NULL...' to @name kernel doc (Jani)
Annotate the function with __printf() attribute (Jani)
Signed-off-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/1449670818-2966-1-git-send-email-ville.syrjala@linux.intel.com
2015-12-09 22:20:18 +08:00
|
|
|
DRM_MODE_ENCODER_DPMST, NULL);
|
2015-02-24 07:24:04 +08:00
|
|
|
drm_encoder_helper_add(encoder, &radeon_mst_helper_funcs);
|
|
|
|
|
|
|
|
mst_enc = radeon_encoder->enc_priv;
|
|
|
|
mst_enc->connector = connector;
|
|
|
|
mst_enc->primary = to_radeon_encoder(enc_master);
|
|
|
|
radeon_encoder->is_mst_encoder = true;
|
|
|
|
return radeon_encoder;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
radeon_dp_mst_init(struct radeon_connector *radeon_connector)
|
|
|
|
{
|
|
|
|
struct drm_device *dev = radeon_connector->base.dev;
|
2021-04-30 06:11:51 +08:00
|
|
|
int max_link_rate;
|
2015-02-24 07:24:04 +08:00
|
|
|
|
|
|
|
if (!radeon_connector->ddc_bus->has_aux)
|
|
|
|
return 0;
|
|
|
|
|
2021-04-30 06:11:51 +08:00
|
|
|
if (radeon_connector_is_dp12_capable(&radeon_connector->base))
|
|
|
|
max_link_rate = 0x14;
|
|
|
|
else
|
|
|
|
max_link_rate = 0x0a;
|
|
|
|
|
2015-02-24 07:24:04 +08:00
|
|
|
radeon_connector->mst_mgr.cbs = &mst_cbs;
|
2017-01-25 07:49:29 +08:00
|
|
|
return drm_dp_mst_topology_mgr_init(&radeon_connector->mst_mgr, dev,
|
2015-02-24 07:24:04 +08:00
|
|
|
&radeon_connector->ddc_bus->aux, 16, 6,
|
2021-05-13 05:00:11 +08:00
|
|
|
4, drm_dp_bw_code_to_link_rate(max_link_rate),
|
2015-02-24 07:24:04 +08:00
|
|
|
radeon_connector->base.base.id);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
radeon_dp_mst_probe(struct radeon_connector *radeon_connector)
|
|
|
|
{
|
|
|
|
struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
|
2015-06-18 12:29:18 +08:00
|
|
|
struct drm_device *dev = radeon_connector->base.dev;
|
|
|
|
struct radeon_device *rdev = dev->dev_private;
|
2015-02-24 07:24:04 +08:00
|
|
|
int ret;
|
|
|
|
u8 msg[1];
|
|
|
|
|
2015-05-13 07:51:01 +08:00
|
|
|
if (!radeon_mst)
|
|
|
|
return 0;
|
|
|
|
|
2015-06-18 12:29:18 +08:00
|
|
|
if (!ASIC_IS_DCE5(rdev))
|
|
|
|
return 0;
|
|
|
|
|
2015-02-24 07:24:04 +08:00
|
|
|
if (dig_connector->dpcd[DP_DPCD_REV] < 0x12)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
ret = drm_dp_dpcd_read(&radeon_connector->ddc_bus->aux, DP_MSTM_CAP, msg,
|
|
|
|
1);
|
|
|
|
if (ret) {
|
|
|
|
if (msg[0] & DP_MST_CAP) {
|
|
|
|
DRM_DEBUG_KMS("Sink is MST capable\n");
|
|
|
|
dig_connector->is_mst = true;
|
|
|
|
} else {
|
|
|
|
DRM_DEBUG_KMS("Sink is not MST capable\n");
|
|
|
|
dig_connector->is_mst = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
drm_dp_mst_topology_mgr_set_mst(&radeon_connector->mst_mgr,
|
|
|
|
dig_connector->is_mst);
|
|
|
|
return dig_connector->is_mst;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
radeon_dp_mst_check_status(struct radeon_connector *radeon_connector)
|
|
|
|
{
|
|
|
|
struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
|
|
|
|
int retry;
|
|
|
|
|
|
|
|
if (dig_connector->is_mst) {
|
|
|
|
u8 esi[16] = { 0 };
|
|
|
|
int dret;
|
|
|
|
int ret = 0;
|
|
|
|
bool handled;
|
|
|
|
|
|
|
|
dret = drm_dp_dpcd_read(&radeon_connector->ddc_bus->aux,
|
|
|
|
DP_SINK_COUNT_ESI, esi, 8);
|
|
|
|
go_again:
|
|
|
|
if (dret == 8) {
|
2017-12-21 18:04:53 +08:00
|
|
|
DRM_DEBUG_KMS("got esi %3ph\n", esi);
|
2015-02-24 07:24:04 +08:00
|
|
|
ret = drm_dp_mst_hpd_irq(&radeon_connector->mst_mgr, esi, &handled);
|
|
|
|
|
|
|
|
if (handled) {
|
|
|
|
for (retry = 0; retry < 3; retry++) {
|
|
|
|
int wret;
|
|
|
|
wret = drm_dp_dpcd_write(&radeon_connector->ddc_bus->aux,
|
|
|
|
DP_SINK_COUNT_ESI + 1, &esi[1], 3);
|
|
|
|
if (wret == 3)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
dret = drm_dp_dpcd_read(&radeon_connector->ddc_bus->aux,
|
|
|
|
DP_SINK_COUNT_ESI, esi, 8);
|
|
|
|
if (dret == 8) {
|
2017-12-21 18:04:53 +08:00
|
|
|
DRM_DEBUG_KMS("got esi2 %3ph\n", esi);
|
2015-02-24 07:24:04 +08:00
|
|
|
goto go_again;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
} else {
|
|
|
|
DRM_DEBUG_KMS("failed to get ESI - device may have failed %d\n", ret);
|
|
|
|
dig_connector->is_mst = false;
|
|
|
|
drm_dp_mst_topology_mgr_set_mst(&radeon_connector->mst_mgr,
|
|
|
|
dig_connector->is_mst);
|
|
|
|
/* send a hotplug event */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(CONFIG_DEBUG_FS)
|
|
|
|
|
2021-02-18 07:34:30 +08:00
|
|
|
static int radeon_debugfs_mst_info_show(struct seq_file *m, void *unused)
|
2015-02-24 07:24:04 +08:00
|
|
|
{
|
2021-02-18 07:34:30 +08:00
|
|
|
struct radeon_device *rdev = (struct radeon_device *)m->private;
|
|
|
|
struct drm_device *dev = rdev->ddev;
|
2015-02-24 07:24:04 +08:00
|
|
|
struct drm_connector *connector;
|
|
|
|
struct radeon_connector *radeon_connector;
|
|
|
|
struct radeon_connector_atom_dig *dig_connector;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
drm_modeset_lock_all(dev);
|
|
|
|
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
|
|
|
|
if (connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
radeon_connector = to_radeon_connector(connector);
|
|
|
|
dig_connector = radeon_connector->con_priv;
|
|
|
|
if (radeon_connector->is_mst_connector)
|
|
|
|
continue;
|
|
|
|
if (!dig_connector->is_mst)
|
|
|
|
continue;
|
|
|
|
drm_dp_mst_dump_topology(m, &radeon_connector->mst_mgr);
|
|
|
|
|
|
|
|
for (i = 0; i < radeon_connector->enabled_attribs; i++)
|
|
|
|
seq_printf(m, "attrib %d: %d %d\n", i,
|
|
|
|
radeon_connector->cur_stream_attribs[i].fe,
|
|
|
|
radeon_connector->cur_stream_attribs[i].slots);
|
|
|
|
}
|
|
|
|
drm_modeset_unlock_all(dev);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-02-18 07:34:30 +08:00
|
|
|
DEFINE_SHOW_ATTRIBUTE(radeon_debugfs_mst_info);
|
2015-02-24 07:24:04 +08:00
|
|
|
#endif
|
|
|
|
|
2021-02-18 07:34:30 +08:00
|
|
|
void radeon_mst_debugfs_init(struct radeon_device *rdev)
|
2015-02-24 07:24:04 +08:00
|
|
|
{
|
|
|
|
#if defined(CONFIG_DEBUG_FS)
|
2021-02-18 07:34:30 +08:00
|
|
|
struct dentry *root = rdev->ddev->primary->debugfs_root;
|
|
|
|
|
|
|
|
debugfs_create_file("radeon_mst_info", 0444, root, rdev,
|
|
|
|
&radeon_debugfs_mst_info_fops);
|
|
|
|
|
2015-02-24 07:24:04 +08:00
|
|
|
#endif
|
|
|
|
}
|