mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-27 19:45:08 +07:00
d9515c5ec1
In order to cache the EDID properly for tiled displays, we need to retrieve it before we register the connector with userspace, otherwise userspace can call get resources and try and get the edid before we've even cached it. This fixes some problems when hotplugging mst monitors, with X/mutter running. As mutter seems to get 0 modes for one of the monitors in the tile. v2: fix warning in radeon handle tile setting in cached path rather than get edid path. Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch> Cc: stable@vger.kernel.org Signed-off-by: Dave Airlie <airlied@redhat.com>
796 lines
23 KiB
C
796 lines
23 KiB
C
|
|
#include <drm/drmP.h>
|
|
#include <drm/drm_dp_mst_helper.h>
|
|
#include <drm/drm_fb_helper.h>
|
|
|
|
#include "radeon.h"
|
|
#include "atom.h"
|
|
#include "ni_reg.h"
|
|
|
|
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 {
|
|
temp = RREG32(NI_DP_MSE_SAT_UPDATE + primary->offset);
|
|
} while ((temp & 0x1) && retries++ < 10000);
|
|
|
|
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;
|
|
}
|
|
|
|
static int radeon_dp_mst_set_vcp_size(struct radeon_encoder *mst, uint32_t x, uint32_t y)
|
|
{
|
|
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;
|
|
|
|
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);
|
|
} 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) {
|
|
drm_mode_connector_update_edid_property(&radeon_connector->base, radeon_connector->edid);
|
|
ret = drm_add_edid_modes(&radeon_connector->base, radeon_connector->edid);
|
|
drm_edid_to_eld(&radeon_connector->base, radeon_connector->edid);
|
|
return ret;
|
|
}
|
|
drm_mode_connector_update_edid_property(&radeon_connector->base, NULL);
|
|
|
|
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;
|
|
}
|
|
|
|
struct drm_encoder *radeon_mst_best_encoder(struct drm_connector *connector)
|
|
{
|
|
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
|
|
|
|
return &radeon_connector->mst_encoder->base;
|
|
}
|
|
|
|
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,
|
|
};
|
|
|
|
static enum drm_connector_status
|
|
radeon_dp_mst_detect(struct drm_connector *connector, bool force)
|
|
{
|
|
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
|
|
struct radeon_connector *master = radeon_connector->mst_port;
|
|
|
|
return drm_dp_mst_detect_port(connector, &master->mst_mgr, radeon_connector->port);
|
|
}
|
|
|
|
static void
|
|
radeon_dp_mst_connector_destroy(struct drm_connector *connector)
|
|
{
|
|
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 int radeon_connector_dpms(struct drm_connector *connector, int mode)
|
|
{
|
|
DRM_DEBUG_KMS("\n");
|
|
return 0;
|
|
}
|
|
|
|
static const struct drm_connector_funcs radeon_dp_mst_connector_funcs = {
|
|
.dpms = radeon_connector_dpms,
|
|
.detect = radeon_dp_mst_detect,
|
|
.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);
|
|
drm_mode_connector_set_path_property(connector, pathprop);
|
|
|
|
return connector;
|
|
}
|
|
|
|
static void radeon_dp_register_mst_connector(struct drm_connector *connector)
|
|
{
|
|
struct drm_device *dev = connector->dev;
|
|
struct radeon_device *rdev = dev->dev_private;
|
|
|
|
drm_modeset_lock_all(dev);
|
|
radeon_fb_add_connector(rdev, connector);
|
|
drm_modeset_unlock_all(dev);
|
|
|
|
drm_connector_register(connector);
|
|
}
|
|
|
|
static void radeon_dp_destroy_mst_connector(struct drm_dp_mst_topology_mgr *mgr,
|
|
struct drm_connector *connector)
|
|
{
|
|
struct radeon_connector *master = container_of(mgr, struct radeon_connector, mst_mgr);
|
|
struct drm_device *dev = master->base.dev;
|
|
struct radeon_device *rdev = dev->dev_private;
|
|
|
|
drm_connector_unregister(connector);
|
|
/* need to nuke the connector */
|
|
drm_modeset_lock_all(dev);
|
|
/* dpms off */
|
|
radeon_fb_remove_connector(rdev, connector);
|
|
|
|
drm_connector_cleanup(connector);
|
|
drm_modeset_unlock_all(dev);
|
|
|
|
kfree(connector);
|
|
DRM_DEBUG_KMS("\n");
|
|
}
|
|
|
|
static void radeon_dp_mst_hotplug(struct drm_dp_mst_topology_mgr *mgr)
|
|
{
|
|
struct radeon_connector *master = container_of(mgr, struct radeon_connector, mst_mgr);
|
|
struct drm_device *dev = master->base.dev;
|
|
|
|
drm_kms_helper_hotplug_event(dev);
|
|
}
|
|
|
|
struct drm_dp_mst_topology_cbs mst_cbs = {
|
|
.add_connector = radeon_dp_add_mst_connector,
|
|
.register_connector = radeon_dp_register_mst_connector,
|
|
.destroy_connector = radeon_dp_destroy_mst_connector,
|
|
.hotplug = radeon_dp_mst_hotplug,
|
|
};
|
|
|
|
struct radeon_connector *radeon_mst_find_connector(struct drm_encoder *encoder)
|
|
{
|
|
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;
|
|
int ret, slots;
|
|
|
|
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);
|
|
|
|
ret = drm_dp_mst_allocate_vcpi(&radeon_connector->mst_port->mst_mgr,
|
|
radeon_connector->port,
|
|
mst_enc->pbn, &slots);
|
|
ret = drm_dp_update_payload_part1(&radeon_connector->mst_port->mst_mgr);
|
|
|
|
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);
|
|
radeon_dp_mst_set_vcp_size(radeon_encoder, slots, 0);
|
|
|
|
atombios_dig_encoder_setup2(&primary->base, ATOM_ENCODER_CMD_DP_VIDEO_ON, 0,
|
|
mst_enc->fe);
|
|
ret = drm_dp_check_act_status(&radeon_connector->mst_port->mst_mgr);
|
|
|
|
ret = drm_dp_update_payload_part2(&radeon_connector->mst_port->mst_mgr);
|
|
|
|
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);
|
|
ret = drm_dp_update_payload_part1(&radeon_connector->mst_port->mst_mgr);
|
|
|
|
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);
|
|
int bpp = 24;
|
|
|
|
mst_enc = radeon_encoder->enc_priv;
|
|
|
|
mst_enc->pbn = drm_dp_calc_pbn_mode(adjusted_mode->clock, bpp);
|
|
|
|
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);
|
|
{
|
|
struct radeon_connector_atom_dig *dig_connector;
|
|
|
|
dig_connector = mst_enc->connector->con_priv;
|
|
dig_connector->dp_lane_count = drm_dp_max_lane_count(dig_connector->dpcd);
|
|
dig_connector->dp_clock = radeon_dp_get_max_link_rate(&mst_enc->connector->base,
|
|
dig_connector->dpcd);
|
|
DRM_DEBUG_KMS("dig clock %p %d %d\n", dig_connector,
|
|
dig_connector->dp_lane_count, dig_connector->dp_clock);
|
|
}
|
|
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,
|
|
};
|
|
|
|
void radeon_dp_mst_encoder_destroy(struct drm_encoder *encoder)
|
|
{
|
|
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;
|
|
const struct drm_connector_helper_funcs *connector_funcs = connector->base.helper_private;
|
|
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_MODE_ENCODER_DPMST);
|
|
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;
|
|
|
|
if (!radeon_connector->ddc_bus->has_aux)
|
|
return 0;
|
|
|
|
radeon_connector->mst_mgr.cbs = &mst_cbs;
|
|
return drm_dp_mst_topology_mgr_init(&radeon_connector->mst_mgr, dev->dev,
|
|
&radeon_connector->ddc_bus->aux, 16, 6,
|
|
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;
|
|
struct drm_device *dev = radeon_connector->base.dev;
|
|
struct radeon_device *rdev = dev->dev_private;
|
|
int ret;
|
|
u8 msg[1];
|
|
|
|
if (!radeon_mst)
|
|
return 0;
|
|
|
|
if (!ASIC_IS_DCE5(rdev))
|
|
return 0;
|
|
|
|
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) {
|
|
DRM_DEBUG_KMS("got esi %02x %02x %02x\n", esi[0], esi[1], esi[2]);
|
|
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) {
|
|
DRM_DEBUG_KMS("got esi2 %02x %02x %02x\n", esi[0], esi[1], esi[2]);
|
|
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)
|
|
|
|
static int radeon_debugfs_mst_info(struct seq_file *m, void *data)
|
|
{
|
|
struct drm_info_node *node = (struct drm_info_node *)m->private;
|
|
struct drm_device *dev = node->minor->dev;
|
|
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;
|
|
}
|
|
|
|
static struct drm_info_list radeon_debugfs_mst_list[] = {
|
|
{"radeon_mst_info", &radeon_debugfs_mst_info, 0, NULL},
|
|
};
|
|
#endif
|
|
|
|
int radeon_mst_debugfs_init(struct radeon_device *rdev)
|
|
{
|
|
#if defined(CONFIG_DEBUG_FS)
|
|
return radeon_debugfs_add_files(rdev, radeon_debugfs_mst_list, 1);
|
|
#endif
|
|
return 0;
|
|
}
|