drm/i915: Add support for limited color range of broadcast outputs
[pandora-kernel.git] / drivers / gpu / drm / i915 / intel_dp.c
index 1f4242b..3216adc 100644 (file)
@@ -49,6 +49,7 @@ struct intel_dp {
        uint8_t  link_configuration[DP_LINK_CONFIGURATION_SIZE];
        bool has_audio;
        int force_audio;
+       uint32_t color_range;
        int dpms_mode;
        uint8_t link_bw;
        uint8_t lane_count;
@@ -685,6 +686,7 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        int lane_count = 4, bpp = 24;
        struct intel_dp_m_n m_n;
+       int pipe = intel_crtc->pipe;
 
        /*
         * Find the lane count in the intel_encoder private
@@ -715,39 +717,19 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
                             mode->clock, adjusted_mode->clock, &m_n);
 
        if (HAS_PCH_SPLIT(dev)) {
-               if (intel_crtc->pipe == 0) {
-                       I915_WRITE(TRANSA_DATA_M1,
-                                  ((m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) |
-                                  m_n.gmch_m);
-                       I915_WRITE(TRANSA_DATA_N1, m_n.gmch_n);
-                       I915_WRITE(TRANSA_DP_LINK_M1, m_n.link_m);
-                       I915_WRITE(TRANSA_DP_LINK_N1, m_n.link_n);
-               } else {
-                       I915_WRITE(TRANSB_DATA_M1,
-                                  ((m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) |
-                                  m_n.gmch_m);
-                       I915_WRITE(TRANSB_DATA_N1, m_n.gmch_n);
-                       I915_WRITE(TRANSB_DP_LINK_M1, m_n.link_m);
-                       I915_WRITE(TRANSB_DP_LINK_N1, m_n.link_n);
-               }
+               I915_WRITE(TRANSDATA_M1(pipe),
+                          ((m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) |
+                          m_n.gmch_m);
+               I915_WRITE(TRANSDATA_N1(pipe), m_n.gmch_n);
+               I915_WRITE(TRANSDPLINK_M1(pipe), m_n.link_m);
+               I915_WRITE(TRANSDPLINK_N1(pipe), m_n.link_n);
        } else {
-               if (intel_crtc->pipe == 0) {
-                       I915_WRITE(PIPEA_GMCH_DATA_M,
-                                  ((m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) |
-                                  m_n.gmch_m);
-                       I915_WRITE(PIPEA_GMCH_DATA_N,
-                                  m_n.gmch_n);
-                       I915_WRITE(PIPEA_DP_LINK_M, m_n.link_m);
-                       I915_WRITE(PIPEA_DP_LINK_N, m_n.link_n);
-               } else {
-                       I915_WRITE(PIPEB_GMCH_DATA_M,
-                                  ((m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) |
-                                  m_n.gmch_m);
-                       I915_WRITE(PIPEB_GMCH_DATA_N,
-                                       m_n.gmch_n);
-                       I915_WRITE(PIPEB_DP_LINK_M, m_n.link_m);
-                       I915_WRITE(PIPEB_DP_LINK_N, m_n.link_n);
-               }
+               I915_WRITE(PIPE_GMCH_DATA_M(pipe),
+                          ((m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) |
+                          m_n.gmch_m);
+               I915_WRITE(PIPE_GMCH_DATA_N(pipe), m_n.gmch_n);
+               I915_WRITE(PIPE_DP_LINK_M(pipe), m_n.link_m);
+               I915_WRITE(PIPE_DP_LINK_N(pipe), m_n.link_n);
        }
 }
 
@@ -760,8 +742,8 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
        struct drm_crtc *crtc = intel_dp->base.base.crtc;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
 
-       intel_dp->DP = (DP_VOLTAGE_0_4 |
-                      DP_PRE_EMPHASIS_0);
+       intel_dp->DP = DP_VOLTAGE_0_4 | DP_PRE_EMPHASIS_0;
+       intel_dp->DP |= intel_dp->color_range;
 
        if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
                intel_dp->DP |= DP_SYNC_HS_HIGH;
@@ -813,6 +795,40 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
        }
 }
 
+static void ironlake_edp_panel_vdd_on(struct intel_dp *intel_dp)
+{
+       struct drm_device *dev = intel_dp->base.base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 pp;
+
+       /*
+        * If the panel wasn't on, make sure there's not a currently
+        * active PP sequence before enabling AUX VDD.
+        */
+       if (!(I915_READ(PCH_PP_STATUS) & PP_ON))
+               msleep(dev_priv->panel_t3);
+
+       pp = I915_READ(PCH_PP_CONTROL);
+       pp |= EDP_FORCE_VDD;
+       I915_WRITE(PCH_PP_CONTROL, pp);
+       POSTING_READ(PCH_PP_CONTROL);
+}
+
+static void ironlake_edp_panel_vdd_off(struct intel_dp *intel_dp)
+{
+       struct drm_device *dev = intel_dp->base.base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 pp;
+
+       pp = I915_READ(PCH_PP_CONTROL);
+       pp &= ~EDP_FORCE_VDD;
+       I915_WRITE(PCH_PP_CONTROL, pp);
+       POSTING_READ(PCH_PP_CONTROL);
+
+       /* Make sure sequencer is idle before allowing subsequent activity */
+       msleep(dev_priv->panel_t12);
+}
+
 /* Returns true if the panel was already on when called */
 static bool ironlake_edp_panel_on (struct intel_dp *intel_dp)
 {
@@ -834,11 +850,6 @@ static bool ironlake_edp_panel_on (struct intel_dp *intel_dp)
        I915_WRITE(PCH_PP_CONTROL, pp);
        POSTING_READ(PCH_PP_CONTROL);
 
-       /* Ouch. We need to wait here for some panels, like Dell e6510
-        * https://bugs.freedesktop.org/show_bug.cgi?id=29278i
-        */
-       msleep(300);
-
        if (wait_for((I915_READ(PCH_PP_STATUS) & idle_on_mask) == idle_on_mask,
                     5000))
                DRM_ERROR("panel on wait timed out: 0x%08x\n",
@@ -875,11 +886,6 @@ static void ironlake_edp_panel_off (struct drm_device *dev)
        pp |= PANEL_POWER_RESET; /* restore panel reset bit */
        I915_WRITE(PCH_PP_CONTROL, pp);
        POSTING_READ(PCH_PP_CONTROL);
-
-       /* Ouch. We need to wait here for some panels, like Dell e6510
-        * https://bugs.freedesktop.org/show_bug.cgi?id=29278i
-        */
-       msleep(300);
 }
 
 static void ironlake_edp_backlight_on (struct drm_device *dev)
@@ -945,7 +951,7 @@ static void intel_dp_prepare(struct drm_encoder *encoder)
 
        if (is_edp(intel_dp)) {
                ironlake_edp_backlight_off(dev);
-               ironlake_edp_panel_on(intel_dp);
+               ironlake_edp_panel_off(dev);
                if (!is_pch_edp(intel_dp))
                        ironlake_edp_pll_on(encoder);
                else
@@ -959,10 +965,15 @@ static void intel_dp_commit(struct drm_encoder *encoder)
        struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
        struct drm_device *dev = encoder->dev;
 
+       if (is_edp(intel_dp))
+               ironlake_edp_panel_vdd_on(intel_dp);
+
        intel_dp_start_link_train(intel_dp);
 
-       if (is_edp(intel_dp))
+       if (is_edp(intel_dp)) {
                ironlake_edp_panel_on(intel_dp);
+               ironlake_edp_panel_vdd_off(intel_dp);
+       }
 
        intel_dp_complete_link_train(intel_dp);
 
@@ -988,9 +999,13 @@ intel_dp_dpms(struct drm_encoder *encoder, int mode)
                        ironlake_edp_pll_off(encoder);
        } else {
                if (is_edp(intel_dp))
-                       ironlake_edp_panel_on(intel_dp);
+                       ironlake_edp_panel_vdd_on(intel_dp);
                if (!(dp_reg & DP_PORT_EN)) {
                        intel_dp_start_link_train(intel_dp);
+                       if (is_edp(intel_dp)) {
+                               ironlake_edp_panel_on(intel_dp);
+                               ironlake_edp_panel_vdd_off(intel_dp);
+                       }
                        intel_dp_complete_link_train(intel_dp);
                }
                if (is_edp(intel_dp))
@@ -1508,9 +1523,13 @@ ironlake_dp_detect(struct intel_dp *intel_dp)
 {
        enum drm_connector_status status;
 
-       /* Can't disconnect eDP */
-       if (is_edp(intel_dp))
-               return connector_status_connected;
+       /* Can't disconnect eDP, but you can close the lid... */
+       if (is_edp(intel_dp)) {
+               status = intel_panel_detect(intel_dp->base.base.dev);
+               if (status == connector_status_unknown)
+                       status = connector_status_connected;
+               return status;
+       }
 
        status = connector_status_disconnected;
        if (intel_dp_aux_native_read(intel_dp,
@@ -1639,11 +1658,30 @@ static int intel_dp_get_modes(struct drm_connector *connector)
        return 0;
 }
 
+static bool
+intel_dp_detect_audio(struct drm_connector *connector)
+{
+       struct intel_dp *intel_dp = intel_attached_dp(connector);
+       struct edid *edid;
+       bool has_audio = false;
+
+       edid = drm_get_edid(connector, &intel_dp->adapter);
+       if (edid) {
+               has_audio = drm_detect_monitor_audio(edid);
+
+               connector->display_info.raw_edid = NULL;
+               kfree(edid);
+       }
+
+       return has_audio;
+}
+
 static int
 intel_dp_set_property(struct drm_connector *connector,
                      struct drm_property *property,
                      uint64_t val)
 {
+       struct drm_i915_private *dev_priv = connector->dev->dev_private;
        struct intel_dp *intel_dp = intel_attached_dp(connector);
        int ret;
 
@@ -1652,17 +1690,31 @@ intel_dp_set_property(struct drm_connector *connector,
                return ret;
 
        if (property == intel_dp->force_audio_property) {
-               if (val == intel_dp->force_audio)
+               int i = val;
+               bool has_audio;
+
+               if (i == intel_dp->force_audio)
                        return 0;
 
-               intel_dp->force_audio = val;
+               intel_dp->force_audio = i;
+
+               if (i == 0)
+                       has_audio = intel_dp_detect_audio(connector);
+               else
+                       has_audio = i > 0;
 
-               if (val > 0 && intel_dp->has_audio)
+               if (has_audio == intel_dp->has_audio)
                        return 0;
-               if (val < 0 && !intel_dp->has_audio)
+
+               intel_dp->has_audio = has_audio;
+               goto done;
+       }
+
+       if (property == dev_priv->broadcast_rgb_property) {
+               if (val == !!intel_dp->color_range)
                        return 0;
 
-               intel_dp->has_audio = val > 0;
+               intel_dp->color_range = val ? DP_COLOR_RANGE_16_235 : 0;
                goto done;
        }
 
@@ -1785,6 +1837,8 @@ intel_dp_add_properties(struct intel_dp *intel_dp, struct drm_connector *connect
                intel_dp->force_audio_property->values[1] = 1;
                drm_connector_attach_property(connector, intel_dp->force_audio_property, 0);
        }
+
+       intel_attach_broadcast_rgb_property(connector);
 }
 
 void
@@ -1882,9 +1936,18 @@ intel_dp_init(struct drm_device *dev, int output_reg)
        /* Cache some DPCD data in the eDP case */
        if (is_edp(intel_dp)) {
                int ret;
-               bool was_on;
+               u32 pp_on, pp_div;
+
+               pp_on = I915_READ(PCH_PP_ON_DELAYS);
+               pp_div = I915_READ(PCH_PP_DIVISOR);
 
-               was_on = ironlake_edp_panel_on(intel_dp);
+               /* Get T3 & T12 values (note: VESA not bspec terminology) */
+               dev_priv->panel_t3 = (pp_on & 0x1fff0000) >> 16;
+               dev_priv->panel_t3 /= 10; /* t3 in 100us units */
+               dev_priv->panel_t12 = pp_div & 0xf;
+               dev_priv->panel_t12 *= 100; /* t12 in 100ms units */
+
+               ironlake_edp_panel_vdd_on(intel_dp);
                ret = intel_dp_aux_native_read(intel_dp, DP_DPCD_REV,
                                               intel_dp->dpcd,
                                               sizeof(intel_dp->dpcd));
@@ -1895,8 +1958,7 @@ intel_dp_init(struct drm_device *dev, int output_reg)
                } else {
                        DRM_ERROR("failed to retrieve link info\n");
                }
-               if (!was_on)
-                       ironlake_edp_panel_off(dev);
+               ironlake_edp_panel_vdd_off(intel_dp);
        }
 
        intel_encoder->hot_plug = intel_dp_hot_plug;