Skip to content

Commit

Permalink
drm/radeon: add DisplayPort MST support (v2)
Browse files Browse the repository at this point in the history
This adds initial DP 1.2 MST support to radeon, on CAYMAN
and up in theory.

This is off by default.

v2: agd5f:
- add UNIPHY3 offsets
- move atom cmd table code into atombios_encoders.c
- whitespace cleanup
- replace some magic numbers with proper defines

Signed-off-by: Dave Airlie <airlied@redhat.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
  • Loading branch information
airlied authored and alexdeucher committed Mar 19, 2015
1 parent 8f0fc08 commit 9843ead
Show file tree
Hide file tree
Showing 13 changed files with 998 additions and 6 deletions.
2 changes: 1 addition & 1 deletion drivers/gpu/drm/radeon/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ radeon-y += radeon_device.o radeon_asic.o radeon_kms.o \
rv770_smc.o cypress_dpm.o btc_dpm.o sumo_dpm.o sumo_smc.o trinity_dpm.o \
trinity_smc.o ni_dpm.o si_smc.o si_dpm.o kv_smc.o kv_dpm.o ci_smc.o \
ci_dpm.o dce6_afmt.o radeon_vm.o radeon_ucode.o radeon_ib.o \
radeon_sync.o radeon_audio.o radeon_dp_auxch.o
radeon_sync.o radeon_audio.o radeon_dp_auxch.o radeon_dp_mst.o

radeon-$(CONFIG_MMU_NOTIFIER) += radeon_mn.o

Expand Down
11 changes: 10 additions & 1 deletion drivers/gpu/drm/radeon/atombios_crtc.c
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,13 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc,
}
}

if (radeon_encoder->is_mst_encoder) {
struct radeon_encoder_mst *mst_enc = radeon_encoder->enc_priv;
struct radeon_connector_atom_dig *dig_connector = mst_enc->connector->con_priv;

dp_clock = dig_connector->dp_clock;
}

/* use recommended ref_div for ss */
if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) {
if (radeon_crtc->ss_enabled) {
Expand Down Expand Up @@ -956,7 +963,9 @@ static bool atombios_crtc_prepare_pll(struct drm_crtc *crtc, struct drm_display_
radeon_crtc->bpc = 8;
radeon_crtc->ss_enabled = false;

if ((radeon_encoder->active_device & (ATOM_DEVICE_LCD_SUPPORT | ATOM_DEVICE_DFP_SUPPORT)) ||
if (radeon_encoder->is_mst_encoder) {
radeon_dp_mst_prepare_pll(crtc, mode);
} else if ((radeon_encoder->active_device & (ATOM_DEVICE_LCD_SUPPORT | ATOM_DEVICE_DFP_SUPPORT)) ||
(radeon_encoder_get_dp_bridge_encoder_id(radeon_crtc->encoder) != ENCODER_OBJECT_ID_NONE)) {
struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
struct drm_connector *connector =
Expand Down
60 changes: 60 additions & 0 deletions drivers/gpu/drm/radeon/atombios_encoders.c
Original file line number Diff line number Diff line change
Expand Up @@ -671,7 +671,15 @@ atombios_get_encoder_mode(struct drm_encoder *encoder)
struct drm_connector *connector;
struct radeon_connector *radeon_connector;
struct radeon_connector_atom_dig *dig_connector;
struct radeon_encoder_atom_dig *dig_enc;

if (radeon_encoder_is_digital(encoder)) {
dig_enc = radeon_encoder->enc_priv;
if (dig_enc->active_mst_links)
return ATOM_ENCODER_MODE_DP_MST;
}
if (radeon_encoder->is_mst_encoder || radeon_encoder->offset)
return ATOM_ENCODER_MODE_DP_MST;
/* dp bridges are always DP */
if (radeon_encoder_get_dp_bridge_encoder_id(encoder) != ENCODER_OBJECT_ID_NONE)
return ATOM_ENCODER_MODE_DP;
Expand Down Expand Up @@ -1706,6 +1714,11 @@ radeon_atom_encoder_dpms_dig(struct drm_encoder *encoder, int mode)
case DRM_MODE_DPMS_STANDBY:
case DRM_MODE_DPMS_SUSPEND:
case DRM_MODE_DPMS_OFF:

/* don't power off encoders with active MST links */
if (dig->active_mst_links)
return;

if (ASIC_IS_DCE4(rdev)) {
if (ENCODER_MODE_IS_DP(atombios_get_encoder_mode(encoder)) && connector)
atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_VIDEO_OFF, 0);
Expand Down Expand Up @@ -1974,6 +1987,53 @@ atombios_set_encoder_crtc_source(struct drm_encoder *encoder)
radeon_atombios_encoder_crtc_scratch_regs(encoder, radeon_crtc->crtc_id);
}

void
atombios_set_mst_encoder_crtc_source(struct drm_encoder *encoder, int fe)
{
struct drm_device *dev = encoder->dev;
struct radeon_device *rdev = dev->dev_private;
struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc);
int index = GetIndexIntoMasterTable(COMMAND, SelectCRTC_Source);
uint8_t frev, crev;
union crtc_source_param args;

memset(&args, 0, sizeof(args));

if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev))
return;

if (frev != 1 && crev != 2)
DRM_ERROR("Unknown table for MST %d, %d\n", frev, crev);

args.v2.ucCRTC = radeon_crtc->crtc_id;
args.v2.ucEncodeMode = ATOM_ENCODER_MODE_DP_MST;

switch (fe) {
case 0:
args.v2.ucEncoderID = ASIC_INT_DIG1_ENCODER_ID;
break;
case 1:
args.v2.ucEncoderID = ASIC_INT_DIG2_ENCODER_ID;
break;
case 2:
args.v2.ucEncoderID = ASIC_INT_DIG3_ENCODER_ID;
break;
case 3:
args.v2.ucEncoderID = ASIC_INT_DIG4_ENCODER_ID;
break;
case 4:
args.v2.ucEncoderID = ASIC_INT_DIG5_ENCODER_ID;
break;
case 5:
args.v2.ucEncoderID = ASIC_INT_DIG6_ENCODER_ID;
break;
case 6:
args.v2.ucEncoderID = ASIC_INT_DIG7_ENCODER_ID;
break;
}
atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
}

static void
atombios_apply_encoder_quirks(struct drm_encoder *encoder,
struct drm_display_mode *mode)
Expand Down
7 changes: 7 additions & 0 deletions drivers/gpu/drm/radeon/ni_reg.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@
# define NI_DP_MSE_ZERO_ENCODER (((x) & 0x1) << 8)

#define NI_DP_MSE_RATE_CNTL 0x7384
# define NI_DP_MSE_RATE_Y(x) (((x) & 0x3ffffff) << 0)
# define NI_DP_MSE_RATE_X(x) (((x) & 0x3f) << 26)

#define NI_DP_MSE_RATE_UPDATE 0x738c

Expand All @@ -111,6 +113,11 @@
#define NI_DIG_BE_CNTL 0x7140
# define NI_DIG_FE_SOURCE_SELECT(x) (((x) & 0x7f) << 8)
# define NI_DIG_FE_DIG_MODE(x) (((x) & 0x7) << 16)
# define NI_DIG_MODE_DP_SST 0
# define NI_DIG_MODE_LVDS 1
# define NI_DIG_MODE_TMDS_DVI 2
# define NI_DIG_MODE_TMDS_HDMI 3
# define NI_DIG_MODE_DP_MST 5
# define NI_DIG_HPD_SELECT(x) (((x) & 0x7) << 28)

#define NI_DIG_FE_CNTL 0x7000
Expand Down
1 change: 1 addition & 0 deletions drivers/gpu/drm/radeon/radeon.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ extern int radeon_use_pflipirq;
extern int radeon_bapm;
extern int radeon_backlight;
extern int radeon_auxch;
extern int radeon_mst;

/*
* Copy from radeon_drv.h so we don't have to include both and have conflicting
Expand Down
1 change: 1 addition & 0 deletions drivers/gpu/drm/radeon/radeon_atombios.c
Original file line number Diff line number Diff line change
Expand Up @@ -845,6 +845,7 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev)

radeon_link_encoder_connector(dev);

radeon_setup_mst_connector(dev);
return true;
}

Expand Down
64 changes: 61 additions & 3 deletions drivers/gpu/drm/radeon/radeon_connectors.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,41 @@
#include <drm/drm_edid.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_dp_mst_helper.h>
#include <drm/radeon_drm.h>
#include "radeon.h"
#include "radeon_audio.h"
#include "atom.h"

#include <linux/pm_runtime.h>

static int radeon_dp_handle_hpd(struct drm_connector *connector)
{
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
int ret;

ret = radeon_dp_mst_check_status(radeon_connector);
if (ret == -EINVAL)
return 1;
return 0;
}
void radeon_connector_hotplug(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
struct radeon_device *rdev = dev->dev_private;
struct radeon_connector *radeon_connector = to_radeon_connector(connector);

if (connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort) {
struct radeon_connector_atom_dig *dig_connector =
radeon_connector->con_priv;

if (radeon_connector->is_mst_connector)
return;
if (dig_connector->is_mst) {
radeon_dp_handle_hpd(connector);
return;
}
}
/* bail if the connector does not have hpd pin, e.g.,
* VGA, TV, etc.
*/
Expand Down Expand Up @@ -1609,6 +1631,9 @@ radeon_dp_detect(struct drm_connector *connector, bool force)
struct drm_encoder *encoder = radeon_best_single_encoder(connector);
int r;

if (radeon_dig_connector->is_mst)
return connector_status_disconnected;

r = pm_runtime_get_sync(connector->dev->dev);
if (r < 0)
return connector_status_disconnected;
Expand Down Expand Up @@ -1667,12 +1692,21 @@ radeon_dp_detect(struct drm_connector *connector, bool force)
radeon_dig_connector->dp_sink_type = radeon_dp_getsinktype(radeon_connector);
if (radeon_hpd_sense(rdev, radeon_connector->hpd.hpd)) {
ret = connector_status_connected;
if (radeon_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT)
if (radeon_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) {
radeon_dp_getdpcd(radeon_connector);
r = radeon_dp_mst_probe(radeon_connector);
if (r == 1)
ret = connector_status_disconnected;
}
} else {
if (radeon_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) {
if (radeon_dp_getdpcd(radeon_connector))
ret = connector_status_connected;
if (radeon_dp_getdpcd(radeon_connector)) {
r = radeon_dp_mst_probe(radeon_connector);
if (r == 1)
ret = connector_status_disconnected;
else
ret = connector_status_connected;
}
} else {
/* try non-aux ddc (DP to DVI/HDMI/etc. adapter) */
if (radeon_ddc_probe(radeon_connector, false))
Expand Down Expand Up @@ -2404,3 +2438,27 @@ radeon_add_legacy_connector(struct drm_device *dev,
connector->display_info.subpixel_order = subpixel_order;
drm_connector_register(connector);
}

void radeon_setup_mst_connector(struct drm_device *dev)
{
struct radeon_device *rdev = dev->dev_private;
struct drm_connector *connector;
struct radeon_connector *radeon_connector;

if (!ASIC_IS_DCE5(rdev))
return;

if (radeon_mst == 0)
return;

list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
int ret;

radeon_connector = to_radeon_connector(connector);

if (connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort)
continue;

ret = radeon_dp_mst_init(radeon_connector);
}
}
5 changes: 5 additions & 0 deletions drivers/gpu/drm/radeon/radeon_device.c
Original file line number Diff line number Diff line change
Expand Up @@ -1442,6 +1442,11 @@ int radeon_device_init(struct radeon_device *rdev,
DRM_ERROR("registering gem debugfs failed (%d).\n", r);
}

r = radeon_mst_debugfs_init(rdev);
if (r) {
DRM_ERROR("registering mst debugfs failed (%d).\n", r);
}

if (rdev->flags & RADEON_IS_AGP && !rdev->accel_working) {
/* Acceleration not working on AGP card try again
* with fallback to PCI or PCIE GART
Expand Down

0 comments on commit 9843ead

Please sign in to comment.