|
@@ -605,6 +605,30 @@ atombios_get_encoder_mode(struct drm_encoder *encoder)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * DIG Encoder/Transmitter Setup
|
|
|
+ *
|
|
|
+ * DCE 3.0/3.1
|
|
|
+ * - 2 DIG transmitter blocks. UNIPHY (links A and B) and LVTMA.
|
|
|
+ * Supports up to 3 digital outputs
|
|
|
+ * - 2 DIG encoder blocks.
|
|
|
+ * DIG1 can drive UNIPHY link A or link B
|
|
|
+ * DIG2 can drive UNIPHY link B or LVTMA
|
|
|
+ *
|
|
|
+ * DCE 3.2
|
|
|
+ * - 3 DIG transmitter blocks. UNIPHY0/1/2 (links A and B).
|
|
|
+ * Supports up to 5 digital outputs
|
|
|
+ * - 2 DIG encoder blocks.
|
|
|
+ * DIG1/2 can drive UNIPHY0/1/2 link A or link B
|
|
|
+ *
|
|
|
+ * Routing
|
|
|
+ * crtc -> dig encoder -> UNIPHY/LVTMA (1 or 2 links)
|
|
|
+ * Examples:
|
|
|
+ * crtc0 -> dig2 -> LVTMA links A+B -> TMDS/HDMI
|
|
|
+ * crtc1 -> dig1 -> UNIPHY0 link B -> DP
|
|
|
+ * crtc0 -> dig1 -> UNIPHY2 link A -> LVDS
|
|
|
+ * crtc1 -> dig2 -> UNIPHY1 link B+A -> TMDS/HDMI
|
|
|
+ */
|
|
|
static void
|
|
|
atombios_dig_encoder_setup(struct drm_encoder *encoder, int action)
|
|
|
{
|
|
@@ -646,10 +670,17 @@ atombios_dig_encoder_setup(struct drm_encoder *encoder, int action)
|
|
|
} else {
|
|
|
switch (radeon_encoder->encoder_id) {
|
|
|
case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
|
|
|
- index = GetIndexIntoMasterTable(COMMAND, DIG1EncoderControl);
|
|
|
+ /* XXX doesn't really matter which dig encoder we pick as long as it's
|
|
|
+ * not already in use
|
|
|
+ */
|
|
|
+ if (dig_connector->linkb)
|
|
|
+ index = GetIndexIntoMasterTable(COMMAND, DIG2EncoderControl);
|
|
|
+ else
|
|
|
+ index = GetIndexIntoMasterTable(COMMAND, DIG1EncoderControl);
|
|
|
num = 1;
|
|
|
break;
|
|
|
case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
|
|
|
+ /* Only dig2 encoder can drive LVTMA */
|
|
|
index = GetIndexIntoMasterTable(COMMAND, DIG2EncoderControl);
|
|
|
num = 2;
|
|
|
break;
|
|
@@ -684,16 +715,15 @@ atombios_dig_encoder_setup(struct drm_encoder *encoder, int action)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if (radeon_encoder->pixel_clock > 165000) {
|
|
|
- args.ucConfig |= ATOM_ENCODER_CONFIG_LINKA_B;
|
|
|
+ if (radeon_encoder->pixel_clock > 165000)
|
|
|
args.ucLaneNum = 8;
|
|
|
- } else {
|
|
|
- if (dig_connector->linkb)
|
|
|
- args.ucConfig |= ATOM_ENCODER_CONFIG_LINKB;
|
|
|
- else
|
|
|
- args.ucConfig |= ATOM_ENCODER_CONFIG_LINKA;
|
|
|
+ else
|
|
|
args.ucLaneNum = 4;
|
|
|
- }
|
|
|
+
|
|
|
+ if (dig_connector->linkb)
|
|
|
+ args.ucConfig |= ATOM_ENCODER_CONFIG_LINKB;
|
|
|
+ else
|
|
|
+ args.ucConfig |= ATOM_ENCODER_CONFIG_LINKA;
|
|
|
|
|
|
args.ucEncoderMode = atombios_get_encoder_mode(encoder);
|
|
|
|
|
@@ -707,7 +737,7 @@ union dig_transmitter_control {
|
|
|
};
|
|
|
|
|
|
static void
|
|
|
-atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action)
|
|
|
+atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t lane_num, uint8_t lane_set)
|
|
|
{
|
|
|
struct drm_device *dev = encoder->dev;
|
|
|
struct radeon_device *rdev = dev->dev_private;
|
|
@@ -756,6 +786,9 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action)
|
|
|
args.v1.ucAction = action;
|
|
|
if (action == ATOM_TRANSMITTER_ACTION_INIT) {
|
|
|
args.v1.usInitInfo = radeon_connector->connector_object_id;
|
|
|
+ } else if (action == ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH) {
|
|
|
+ args.v1.asMode.ucLaneSel = lane_num;
|
|
|
+ args.v1.asMode.ucLaneSet = lane_set;
|
|
|
} else {
|
|
|
if (radeon_encoder->pixel_clock > 165000)
|
|
|
args.v1.usPixelClock = cpu_to_le16((radeon_encoder->pixel_clock / 2) / 10);
|
|
@@ -767,6 +800,8 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action)
|
|
|
args.v2.usPixelClock = cpu_to_le16((radeon_encoder->pixel_clock / 2) / 10);
|
|
|
if (dig->dig_block)
|
|
|
args.v2.acConfig.ucEncoderSel = 1;
|
|
|
+ if (dig_connector->linkb)
|
|
|
+ args.v2.acConfig.ucLinkSel = 1;
|
|
|
|
|
|
switch (radeon_encoder->encoder_id) {
|
|
|
case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
|
|
@@ -792,17 +827,20 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action)
|
|
|
|
|
|
switch (radeon_encoder->encoder_id) {
|
|
|
case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
|
|
|
- args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_DIG1_ENCODER;
|
|
|
+ /* XXX doesn't really matter which dig encoder we pick as long as it's
|
|
|
+ * not already in use
|
|
|
+ */
|
|
|
+ if (dig_connector->linkb)
|
|
|
+ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_DIG2_ENCODER;
|
|
|
+ else
|
|
|
+ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_DIG1_ENCODER;
|
|
|
if (rdev->flags & RADEON_IS_IGP) {
|
|
|
if (radeon_encoder->pixel_clock > 165000) {
|
|
|
- args.v1.ucConfig |= (ATOM_TRANSMITTER_CONFIG_8LANE_LINK |
|
|
|
- ATOM_TRANSMITTER_CONFIG_LINKA_B);
|
|
|
if (dig_connector->igp_lane_info & 0x3)
|
|
|
args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_0_7;
|
|
|
else if (dig_connector->igp_lane_info & 0xc)
|
|
|
args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_8_15;
|
|
|
} else {
|
|
|
- args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKA;
|
|
|
if (dig_connector->igp_lane_info & 0x1)
|
|
|
args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_0_3;
|
|
|
else if (dig_connector->igp_lane_info & 0x2)
|
|
@@ -812,34 +850,22 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action)
|
|
|
else if (dig_connector->igp_lane_info & 0x8)
|
|
|
args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_12_15;
|
|
|
}
|
|
|
- } else {
|
|
|
- if (radeon_encoder->pixel_clock > 165000)
|
|
|
- args.v1.ucConfig |= (ATOM_TRANSMITTER_CONFIG_8LANE_LINK |
|
|
|
- ATOM_TRANSMITTER_CONFIG_LINKA_B |
|
|
|
- ATOM_TRANSMITTER_CONFIG_LANE_0_7);
|
|
|
- else {
|
|
|
- if (dig_connector->linkb)
|
|
|
- args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKB | ATOM_TRANSMITTER_CONFIG_LANE_0_3;
|
|
|
- else
|
|
|
- args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKA | ATOM_TRANSMITTER_CONFIG_LANE_0_3;
|
|
|
- }
|
|
|
}
|
|
|
break;
|
|
|
case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
|
|
|
+ /* Only dig2 encoder can drive LVTMA */
|
|
|
args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_DIG2_ENCODER;
|
|
|
- if (radeon_encoder->pixel_clock > 165000)
|
|
|
- args.v1.ucConfig |= (ATOM_TRANSMITTER_CONFIG_8LANE_LINK |
|
|
|
- ATOM_TRANSMITTER_CONFIG_LINKA_B |
|
|
|
- ATOM_TRANSMITTER_CONFIG_LANE_0_7);
|
|
|
- else {
|
|
|
- if (dig_connector->linkb)
|
|
|
- args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKB | ATOM_TRANSMITTER_CONFIG_LANE_0_3;
|
|
|
- else
|
|
|
- args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKA | ATOM_TRANSMITTER_CONFIG_LANE_0_3;
|
|
|
- }
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
+ if (radeon_encoder->pixel_clock > 165000)
|
|
|
+ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_8LANE_LINK;
|
|
|
+
|
|
|
+ if (dig_connector->linkb)
|
|
|
+ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKB;
|
|
|
+ else
|
|
|
+ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKA;
|
|
|
+
|
|
|
if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) {
|
|
|
if (dig->coherent_mode)
|
|
|
args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_COHERENT;
|
|
@@ -850,99 +876,6 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action)
|
|
|
|
|
|
}
|
|
|
|
|
|
-static void
|
|
|
-atombios_dig_transmitter_setup_vsemph(struct drm_encoder *encoder, u8 lane_num,
|
|
|
- u8 lane_set)
|
|
|
-{
|
|
|
- struct drm_device *dev = encoder->dev;
|
|
|
- struct radeon_device *rdev = dev->dev_private;
|
|
|
- struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
|
|
|
- union dig_transmitter_control args;
|
|
|
- int index = 0, num = 0;
|
|
|
- uint8_t frev, crev;
|
|
|
- struct radeon_encoder_atom_dig *dig;
|
|
|
- struct drm_connector *connector;
|
|
|
- struct radeon_connector *radeon_connector;
|
|
|
- struct radeon_connector_atom_dig *dig_connector;
|
|
|
-
|
|
|
- connector = radeon_get_connector_for_encoder(encoder);
|
|
|
- if (!connector)
|
|
|
- return;
|
|
|
-
|
|
|
- radeon_connector = to_radeon_connector(connector);
|
|
|
-
|
|
|
- if (!radeon_encoder->enc_priv)
|
|
|
- return;
|
|
|
-
|
|
|
- dig = radeon_encoder->enc_priv;
|
|
|
-
|
|
|
- if (!radeon_connector->con_priv)
|
|
|
- return;
|
|
|
-
|
|
|
- dig_connector = radeon_connector->con_priv;
|
|
|
-
|
|
|
- memset(&args, 0, sizeof(args));
|
|
|
-
|
|
|
- if (ASIC_IS_DCE32(rdev))
|
|
|
- index = GetIndexIntoMasterTable(COMMAND, UNIPHYTransmitterControl);
|
|
|
- else {
|
|
|
- switch (radeon_encoder->encoder_id) {
|
|
|
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
|
|
|
- index = GetIndexIntoMasterTable(COMMAND, DIG1TransmitterControl);
|
|
|
- break;
|
|
|
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
|
|
|
- index = GetIndexIntoMasterTable(COMMAND, DIG2TransmitterControl);
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev);
|
|
|
-
|
|
|
- args.v1.ucAction = ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH;
|
|
|
- args.v1.asMode.ucLaneSel = lane_num;
|
|
|
- args.v1.asMode.ucLaneSet = lane_set;
|
|
|
-
|
|
|
- if (ASIC_IS_DCE32(rdev)) {
|
|
|
- args.v2.acConfig.fDPConnector = 1;
|
|
|
-
|
|
|
- if (dig->dig_block)
|
|
|
- args.v2.acConfig.ucEncoderSel = 1;
|
|
|
-
|
|
|
- switch (radeon_encoder->encoder_id) {
|
|
|
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
|
|
|
- args.v2.acConfig.ucTransmitterSel = 0;
|
|
|
- num = 0;
|
|
|
- break;
|
|
|
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
|
|
|
- args.v2.acConfig.ucTransmitterSel = 1;
|
|
|
- num = 1;
|
|
|
- break;
|
|
|
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
|
|
|
- args.v2.acConfig.ucTransmitterSel = 2;
|
|
|
- num = 2;
|
|
|
- break;
|
|
|
- }
|
|
|
- } else {
|
|
|
- args.v1.ucConfig = ATOM_TRANSMITTER_CONFIG_CLKSRC_PPLL;
|
|
|
-
|
|
|
- switch (radeon_encoder->encoder_id) {
|
|
|
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
|
|
|
- args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_DIG1_ENCODER;
|
|
|
- if (dig_connector->linkb)
|
|
|
- args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKB | ATOM_TRANSMITTER_CONFIG_LANE_0_3;
|
|
|
- else
|
|
|
- args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKA | ATOM_TRANSMITTER_CONFIG_LANE_0_3;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
|
|
|
-
|
|
|
- if (ASIC_IS_DCE32(rdev))
|
|
|
- DRM_INFO("Output UNIPHY%d transmitter VSEMPH setup success\n", num);
|
|
|
- else
|
|
|
- DRM_INFO("Output DIG%d transmitter VSEMPH setup success\n", num);
|
|
|
-}
|
|
|
-
|
|
|
static void
|
|
|
atombios_yuv_setup(struct drm_encoder *encoder, bool enable)
|
|
|
{
|
|
@@ -1150,13 +1083,33 @@ atombios_set_encoder_crtc_source(struct drm_encoder *encoder)
|
|
|
args.v2.ucEncoderID = ASIC_INT_DIG2_ENCODER_ID;
|
|
|
else
|
|
|
args.v2.ucEncoderID = ASIC_INT_DIG1_ENCODER_ID;
|
|
|
- } else
|
|
|
- args.v2.ucEncoderID = ASIC_INT_DIG1_ENCODER_ID;
|
|
|
+ } else {
|
|
|
+ struct drm_connector *connector;
|
|
|
+ struct radeon_connector *radeon_connector;
|
|
|
+ struct radeon_connector_atom_dig *dig_connector;
|
|
|
+
|
|
|
+ connector = radeon_get_connector_for_encoder(encoder);
|
|
|
+ if (!connector)
|
|
|
+ return;
|
|
|
+ radeon_connector = to_radeon_connector(connector);
|
|
|
+ if (!radeon_connector->con_priv)
|
|
|
+ return;
|
|
|
+ dig_connector = radeon_connector->con_priv;
|
|
|
+
|
|
|
+ /* XXX doesn't really matter which dig encoder we pick as long as it's
|
|
|
+ * not already in use
|
|
|
+ */
|
|
|
+ if (dig_connector->linkb)
|
|
|
+ args.v2.ucEncoderID = ASIC_INT_DIG2_ENCODER_ID;
|
|
|
+ else
|
|
|
+ args.v2.ucEncoderID = ASIC_INT_DIG1_ENCODER_ID;
|
|
|
+ }
|
|
|
break;
|
|
|
case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1:
|
|
|
args.v2.ucEncoderID = ASIC_INT_DVO_ENCODER_ID;
|
|
|
break;
|
|
|
case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
|
|
|
+ /* Only dig2 encoder can drive LVTMA */
|
|
|
args.v2.ucEncoderID = ASIC_INT_DIG2_ENCODER_ID;
|
|
|
break;
|
|
|
case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1:
|
|
@@ -1259,14 +1212,14 @@ radeon_atom_encoder_mode_set(struct drm_encoder *encoder,
|
|
|
case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
|
|
|
case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
|
|
|
/* disable the encoder and transmitter */
|
|
|
- atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE);
|
|
|
+ atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0);
|
|
|
atombios_dig_encoder_setup(encoder, ATOM_DISABLE);
|
|
|
|
|
|
/* setup and enable the encoder and transmitter */
|
|
|
atombios_dig_encoder_setup(encoder, ATOM_ENABLE);
|
|
|
- atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_INIT);
|
|
|
- atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_SETUP);
|
|
|
- atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE);
|
|
|
+ atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_INIT, 0, 0);
|
|
|
+ atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_SETUP, 0, 0);
|
|
|
+ atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE, 0, 0);
|
|
|
break;
|
|
|
case ENCODER_OBJECT_ID_INTERNAL_DDI:
|
|
|
atombios_ddia_setup(encoder, ATOM_ENABLE);
|