From: Dave Airlie <airl...@redhat.com>

booting a Lenovo W500 with LVDS + DP outputs showed up a TODO we had
on our list, to pick a correct digital encoder block. The LVTMA
encoder requires the second digital encoder, all others can use any
encoder at all.

This fixes the digital encoder selection logic to enable LVDS/DP combos
to work okay.

TODO:
test on W500 hw.

Signed-off-by: Dave Airlie <airl...@redhat.com>
---
 drivers/gpu/drm/radeon/radeon_encoders.c |  103 ++++++++++++++++++++----------
 drivers/gpu/drm/radeon/radeon_mode.h     |    1 +
 2 files changed, 70 insertions(+), 34 deletions(-)

diff --git a/drivers/gpu/drm/radeon/radeon_encoders.c 
b/drivers/gpu/drm/radeon/radeon_encoders.c
index 18decfa..9074ad9 100644
--- a/drivers/gpu/drm/radeon/radeon_encoders.c
+++ b/drivers/gpu/drm/radeon/radeon_encoders.c
@@ -156,6 +156,26 @@ radeon_get_encoder_id(struct drm_device *dev, uint32_t 
supported_device, uint8_t
        return ret;
 }
 
+static inline bool radeon_encoder_is_digital(struct drm_encoder *encoder)
+{
+       struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+       switch (radeon_encoder->encoder_id) {
+       case ENCODER_OBJECT_ID_INTERNAL_LVDS:
+       case ENCODER_OBJECT_ID_INTERNAL_TMDS1:
+       case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1:
+       case ENCODER_OBJECT_ID_INTERNAL_LVTM1:
+       case ENCODER_OBJECT_ID_INTERNAL_DVO1:
+       case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1:
+       case ENCODER_OBJECT_ID_INTERNAL_DDI:
+       case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
+       case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
+       case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
+       case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
+               return true;
+       default:
+               return false;
+       }
+}
 void
 radeon_link_encoder_connector(struct drm_device *dev)
 {
@@ -679,31 +699,11 @@ atombios_dig_encoder_setup(struct drm_encoder *encoder, 
int action)
 
        memset(&args, 0, sizeof(args));
 
-       if (ASIC_IS_DCE32(rdev)) {
-               if (dig->dig_block)
-                       index = GetIndexIntoMasterTable(COMMAND, 
DIG2EncoderControl);
-               else
-                       index = GetIndexIntoMasterTable(COMMAND, 
DIG1EncoderControl);
-               num = dig->dig_block + 1;
-       } else {
-               switch (radeon_encoder->encoder_id) {
-               case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
-                       /* 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;
-               }
-       }
+       if (dig->dig_block)
+               index = GetIndexIntoMasterTable(COMMAND, DIG2EncoderControl);
+       else
+               index = GetIndexIntoMasterTable(COMMAND, DIG1EncoderControl);
+       num = dig->dig_block + 1;
 
        atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, 
&crev);
 
@@ -856,10 +856,7 @@ atombios_dig_transmitter_setup(struct drm_encoder 
*encoder, int action, uint8_t
 
                switch (radeon_encoder->encoder_id) {
                case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
-                       /* XXX doesn't really matter which dig encoder we pick 
as long as it's
-                        * not already in use
-                        */
-                       if (dig_connector->linkb)
+                       if (dig_connector->dig_block)
                                args.v1.ucConfig |= 
ATOM_TRANSMITTER_CONFIG_DIG2_ENCODER;
                        else
                                args.v1.ucConfig |= 
ATOM_TRANSMITTER_CONFIG_DIG1_ENCODER;
@@ -1133,10 +1130,7 @@ atombios_set_encoder_crtc_source(struct drm_encoder 
*encoder)
                                                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)
+                                       if (dig_connector->dig_block)
                                                args.v2.ucEncoderID = 
ASIC_INT_DIG2_ENCODER_ID;
                                        else
                                                args.v2.ucEncoderID = 
ASIC_INT_DIG1_ENCODER_ID;
@@ -1208,6 +1202,40 @@ atombios_apply_encoder_quirks(struct drm_encoder 
*encoder,
        }
 }
 
+static int radeon_atom_pick_dig_block(struct drm_encoder *encoder)
+{
+       struct drm_device *dev = encoder->dev;
+       struct radeon_device *rdev = dev->dev_private;
+       struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc);
+       struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+       struct drm_encoder *test_encoder;
+       struct radeon_encoder_atom_dig *dig;
+       uint32_t dig_enc_in_use = 0;
+       /* on DCE32 and encoder can driver any block so just crtc id */
+       if (ASIC_IS_DCE32(rdev))
+               return radeon_crtc->crtc_id;
+
+       /* on DCE3 - LVTMA can only be driven by block 2 */
+       list_for_each_entry(test_encoder, &dev->mode_config.encoder_list, head) 
{
+               if (!radeon_encoder_is_digital(encoder))
+                       continue;
+
+               dig = radeon_encoder->enc_priv;
+
+               if ((encoder != test_encoder) && (dig->dig_block >= 0))
+                       dig_enc_in_use |= (1 << dig->dig_block);
+       }
+
+       if (radeon_encoder->encoder_id == 
ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA) {
+               if (dig_enc_in_use & 0x2)
+                       DRM_ERROR("LVDS required digital encoder 2 but it was 
in use - stealing\n");
+               return 1;
+       }
+       if (!(dig_enc_in_use & 1))
+               return 0;
+       return 1;
+}
+
 static void
 radeon_atom_encoder_mode_set(struct drm_encoder *encoder,
                             struct drm_display_mode *mode,
@@ -1224,7 +1252,7 @@ radeon_atom_encoder_mode_set(struct drm_encoder *encoder,
                        struct radeon_encoder_atom_dig *dig;
 
                        dig = radeon_encoder->enc_priv;
-                       dig->dig_block = radeon_crtc->crtc_id;
+                       dig->dig_block = radeon_atom_pick_dig_block(encoder);
                }
        }
        radeon_encoder->pixel_clock = adjusted_mode->clock;
@@ -1385,7 +1413,13 @@ static void radeon_atom_encoder_commit(struct 
drm_encoder *encoder)
 static void radeon_atom_encoder_disable(struct drm_encoder *encoder)
 {
        struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+       struct radeon_encoder_atom_dig *dig;
        radeon_atom_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
+
+       if (radeon_encoder_is_digital(encoder)) {
+               dig = radeon_encoder->enc_priv;
+               dig->dig_block = -1;
+       }
        radeon_encoder->active_device = 0;
 }
 
@@ -1442,6 +1476,7 @@ radeon_atombios_set_dig_info(struct radeon_encoder 
*radeon_encoder)
 
        /* coherent mode by default */
        dig->coherent_mode = true;
+       dig->dig_block = -1;
 
        return dig;
 }
diff --git a/drivers/gpu/drm/radeon/radeon_mode.h 
b/drivers/gpu/drm/radeon/radeon_mode.h
index a1fe11b..e58a654 100644
--- a/drivers/gpu/drm/radeon/radeon_mode.h
+++ b/drivers/gpu/drm/radeon/radeon_mode.h
@@ -335,6 +335,7 @@ struct radeon_encoder {
 struct radeon_connector_atom_dig {
        uint32_t igp_lane_info;
        bool linkb;
+       int dig_block; /* -1 invalid, 0 == DIG1, 1 == DIG2 */
        /* displayport */
        struct radeon_i2c_chan *dp_i2c_bus;
        u8 dpcd[8];
-- 
1.6.6


------------------------------------------------------------------------------
The Planet: dedicated and managed hosting, cloud storage, colocation
Stay online with enterprise data centers and the best network in the business
Choose flexible plans and management services without long-term contracts
Personal 24x7 support from experience hosting pros just a phone call away.
http://p.sf.net/sfu/theplanet-com
--
_______________________________________________
Dri-devel mailing list
Dri-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/dri-devel

Reply via email to