drm/i915: Defend against userspace creating a gem object with size==0
[pandora-kernel.git] / drivers / gpu / drm / i915 / intel_dp.c
index f797fb5..3d73374 100644 (file)
@@ -50,14 +50,24 @@ struct intel_dp {
        bool has_audio;
        int force_audio;
        uint32_t color_range;
+       int dpms_mode;
        uint8_t link_bw;
        uint8_t lane_count;
-       uint8_t dpcd[4];
+       uint8_t dpcd[8];
        struct i2c_adapter adapter;
        struct i2c_algo_dp_aux_data algo;
        bool is_pch_edp;
        uint8_t train_set[4];
        uint8_t link_status[DP_LINK_STATUS_SIZE];
+       int panel_power_up_delay;
+       int panel_power_down_delay;
+       int panel_power_cycle_delay;
+       int backlight_on_delay;
+       int backlight_off_delay;
+       struct drm_display_mode *panel_fixed_mode;  /* for eDP */
+       struct delayed_work panel_vdd_work;
+       bool want_panel_vdd;
+       unsigned long panel_off_jiffies;
 };
 
 /**
@@ -120,7 +130,7 @@ static void intel_dp_complete_link_train(struct intel_dp *intel_dp);
 static void intel_dp_link_down(struct intel_dp *intel_dp);
 
 void
-intel_edp_link_config (struct intel_encoder *intel_encoder,
+intel_edp_link_config(struct intel_encoder *intel_encoder,
                       int *lane_num, int *link_bw)
 {
        struct intel_dp *intel_dp = container_of(intel_encoder, struct intel_dp, base);
@@ -199,16 +209,14 @@ intel_dp_mode_valid(struct drm_connector *connector,
                    struct drm_display_mode *mode)
 {
        struct intel_dp *intel_dp = intel_attached_dp(connector);
-       struct drm_device *dev = connector->dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
        int max_link_clock = intel_dp_link_clock(intel_dp_max_link_bw(intel_dp));
        int max_lanes = intel_dp_max_lane_count(intel_dp);
 
-       if (is_edp(intel_dp) && dev_priv->panel_fixed_mode) {
-               if (mode->hdisplay > dev_priv->panel_fixed_mode->hdisplay)
+       if (is_edp(intel_dp) && intel_dp->panel_fixed_mode) {
+               if (mode->hdisplay > intel_dp->panel_fixed_mode->hdisplay)
                        return MODE_PANEL;
 
-               if (mode->vdisplay > dev_priv->panel_fixed_mode->vdisplay)
+               if (mode->vdisplay > intel_dp->panel_fixed_mode->vdisplay)
                        return MODE_PANEL;
        }
 
@@ -278,6 +286,38 @@ intel_hrawclk(struct drm_device *dev)
        }
 }
 
+static bool ironlake_edp_have_panel_power(struct intel_dp *intel_dp)
+{
+       struct drm_device *dev = intel_dp->base.base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       return (I915_READ(PCH_PP_STATUS) & PP_ON) != 0;
+}
+
+static bool ironlake_edp_have_panel_vdd(struct intel_dp *intel_dp)
+{
+       struct drm_device *dev = intel_dp->base.base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       return (I915_READ(PCH_PP_CONTROL) & EDP_FORCE_VDD) != 0;
+}
+
+static void
+intel_dp_check_edp(struct intel_dp *intel_dp)
+{
+       struct drm_device *dev = intel_dp->base.base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       if (!is_edp(intel_dp))
+               return;
+       if (!ironlake_edp_have_panel_power(intel_dp) && !ironlake_edp_have_panel_vdd(intel_dp)) {
+               WARN(1, "eDP powered off while attempting aux channel communication.\n");
+               DRM_DEBUG_KMS("Status 0x%08x Control 0x%08x\n",
+                             I915_READ(PCH_PP_STATUS),
+                             I915_READ(PCH_PP_CONTROL));
+       }
+}
+
 static int
 intel_dp_aux_ch(struct intel_dp *intel_dp,
                uint8_t *send, int send_bytes,
@@ -294,6 +334,7 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
        uint32_t aux_clock_divider;
        int try, precharge;
 
+       intel_dp_check_edp(intel_dp);
        /* The clock divider is based off the hrawclk,
         * and would like to run at 2MHz. So, take the
         * hrawclk value and divide by 2 and use that
@@ -316,9 +357,17 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
        else
                precharge = 5;
 
-       if (I915_READ(ch_ctl) & DP_AUX_CH_CTL_SEND_BUSY) {
-               DRM_ERROR("dp_aux_ch not started status 0x%08x\n",
-                         I915_READ(ch_ctl));
+       /* Try to wait for any previous AUX channel activity */
+       for (try = 0; try < 3; try++) {
+               status = I915_READ(ch_ctl);
+               if ((status & DP_AUX_CH_CTL_SEND_BUSY) == 0)
+                       break;
+               msleep(1);
+       }
+
+       if (try == 3) {
+               WARN(1, "dp_aux_ch not started status 0x%08x\n",
+                    I915_READ(ch_ctl));
                return -EBUSY;
        }
 
@@ -328,7 +377,7 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
                for (i = 0; i < send_bytes; i += 4)
                        I915_WRITE(ch_data + i,
                                   pack_aux(send + i, send_bytes - i));
-       
+
                /* Send the command and wait for it to complete */
                I915_WRITE(ch_ctl,
                           DP_AUX_CH_CTL_SEND_BUSY |
@@ -345,7 +394,7 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
                                break;
                        udelay(100);
                }
-       
+
                /* Clear done status and any errors */
                I915_WRITE(ch_ctl,
                           status |
@@ -381,7 +430,7 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
                      DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT);
        if (recv_bytes > recv_size)
                recv_bytes = recv_size;
-       
+
        for (i = 0; i < recv_bytes; i += 4)
                unpack_aux(I915_READ(ch_data + i),
                           recv + i, recv_bytes - i);
@@ -399,6 +448,7 @@ intel_dp_aux_native_write(struct intel_dp *intel_dp,
        int msg_bytes;
        uint8_t ack;
 
+       intel_dp_check_edp(intel_dp);
        if (send_bytes > 16)
                return -1;
        msg[0] = AUX_NATIVE_WRITE << 4;
@@ -441,6 +491,7 @@ intel_dp_aux_native_read(struct intel_dp *intel_dp,
        uint8_t ack;
        int ret;
 
+       intel_dp_check_edp(intel_dp);
        msg[0] = AUX_NATIVE_READ << 4;
        msg[1] = address >> 8;
        msg[2] = address & 0xff;
@@ -484,6 +535,7 @@ intel_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode,
        int reply_bytes;
        int ret;
 
+       intel_dp_check_edp(intel_dp);
        /* Set up the command byte */
        if (mode & MODE_I2C_READ)
                msg[0] = AUX_I2C_READ << 4;
@@ -564,24 +616,32 @@ intel_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode,
        return -EREMOTEIO;
 }
 
+static void ironlake_edp_panel_vdd_on(struct intel_dp *intel_dp);
+static void ironlake_edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync);
+
 static int
 intel_dp_i2c_init(struct intel_dp *intel_dp,
                  struct intel_connector *intel_connector, const char *name)
 {
+       int     ret;
+
        DRM_DEBUG_KMS("i2c_init %s\n", name);
        intel_dp->algo.running = false;
        intel_dp->algo.address = 0;
        intel_dp->algo.aux_ch = intel_dp_i2c_aux_ch;
 
-       memset(&intel_dp->adapter, '\0', sizeof (intel_dp->adapter));
+       memset(&intel_dp->adapter, '\0', sizeof(intel_dp->adapter));
        intel_dp->adapter.owner = THIS_MODULE;
        intel_dp->adapter.class = I2C_CLASS_DDC;
-       strncpy (intel_dp->adapter.name, name, sizeof(intel_dp->adapter.name) - 1);
+       strncpy(intel_dp->adapter.name, name, sizeof(intel_dp->adapter.name) - 1);
        intel_dp->adapter.name[sizeof(intel_dp->adapter.name) - 1] = '\0';
        intel_dp->adapter.algo_data = &intel_dp->algo;
        intel_dp->adapter.dev.parent = &intel_connector->base.kdev;
 
-       return i2c_dp_aux_add_bus(&intel_dp->adapter);
+       ironlake_edp_panel_vdd_on(intel_dp);
+       ret = i2c_dp_aux_add_bus(&intel_dp->adapter);
+       ironlake_edp_panel_vdd_off(intel_dp, false);
+       return ret;
 }
 
 static bool
@@ -589,22 +649,21 @@ intel_dp_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode,
                    struct drm_display_mode *adjusted_mode)
 {
        struct drm_device *dev = encoder->dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
        int lane_count, clock;
        int max_lane_count = intel_dp_max_lane_count(intel_dp);
        int max_clock = intel_dp_max_link_bw(intel_dp) == DP_LINK_BW_2_7 ? 1 : 0;
        static int bws[2] = { DP_LINK_BW_1_62, DP_LINK_BW_2_7 };
 
-       if (is_edp(intel_dp) && dev_priv->panel_fixed_mode) {
-               intel_fixed_panel_mode(dev_priv->panel_fixed_mode, adjusted_mode);
+       if (is_edp(intel_dp) && intel_dp->panel_fixed_mode) {
+               intel_fixed_panel_mode(intel_dp->panel_fixed_mode, adjusted_mode);
                intel_pch_panel_fitting(dev, DRM_MODE_SCALE_FULLSCREEN,
                                        mode, adjusted_mode);
                /*
                 * the mode->clock is used to calculate the Data&Link M/N
                 * of the pipe. For the eDP the fixed clock should be used.
                 */
-               mode->clock = dev_priv->panel_fixed_mode->clock;
+               mode->clock = intel_dp->panel_fixed_mode->clock;
        }
 
        for (lane_count = 1; lane_count <= max_lane_count; lane_count <<= 1) {
@@ -731,6 +790,9 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
        }
 }
 
+static void ironlake_edp_pll_on(struct drm_encoder *encoder);
+static void ironlake_edp_pll_off(struct drm_encoder *encoder);
+
 static void
 intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
                  struct drm_display_mode *adjusted_mode)
@@ -740,6 +802,14 @@ 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);
 
+       /* Turn on the eDP PLL if needed */
+       if (is_edp(intel_dp)) {
+               if (!is_pch_edp(intel_dp))
+                       ironlake_edp_pll_on(encoder);
+               else
+                       ironlake_edp_pll_off(encoder);
+       }
+
        intel_dp->DP = DP_VOLTAGE_0_4 | DP_PRE_EMPHASIS_0;
        intel_dp->DP |= intel_dp->color_range;
 
@@ -764,12 +834,17 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
                intel_dp->DP |= DP_PORT_WIDTH_4;
                break;
        }
-       if (intel_dp->has_audio)
+       if (intel_dp->has_audio) {
+               DRM_DEBUG_DRIVER("Enabling DP audio on pipe %c\n",
+                                pipe_name(intel_crtc->pipe));
                intel_dp->DP |= DP_AUDIO_OUTPUT_ENABLE;
+               intel_write_eld(encoder, adjusted_mode);
+       }
 
        memset(intel_dp->link_configuration, 0, DP_LINK_CONFIGURATION_SIZE);
        intel_dp->link_configuration[0] = intel_dp->link_bw;
        intel_dp->link_configuration[1] = intel_dp->lane_count;
+       intel_dp->link_configuration[8] = DP_SET_ANSI_8B10B;
 
        /*
         * Check for DPCD version > 1.1 and enhanced framing support
@@ -794,58 +869,150 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
        }
 }
 
+static void ironlake_wait_panel_off(struct intel_dp *intel_dp)
+{
+       unsigned long   off_time;
+       unsigned long   delay;
+
+       DRM_DEBUG_KMS("Wait for panel power off time\n");
+
+       if (ironlake_edp_have_panel_power(intel_dp) ||
+           ironlake_edp_have_panel_vdd(intel_dp))
+       {
+               DRM_DEBUG_KMS("Panel still on, no delay needed\n");
+               return;
+       }
+
+       off_time = intel_dp->panel_off_jiffies + msecs_to_jiffies(intel_dp->panel_power_down_delay);
+       if (time_after(jiffies, off_time)) {
+               DRM_DEBUG_KMS("Time already passed");
+               return;
+       }
+       delay = jiffies_to_msecs(off_time - jiffies);
+       if (delay > intel_dp->panel_power_down_delay)
+               delay = intel_dp->panel_power_down_delay;
+       DRM_DEBUG_KMS("Waiting an additional %ld ms\n", delay);
+       msleep(delay);
+}
+
 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);
+       if (!is_edp(intel_dp))
+               return;
+       DRM_DEBUG_KMS("Turn eDP VDD on\n");
+
+       WARN(intel_dp->want_panel_vdd,
+            "eDP VDD already requested on\n");
+
+       intel_dp->want_panel_vdd = true;
+       if (ironlake_edp_have_panel_vdd(intel_dp)) {
+               DRM_DEBUG_KMS("eDP VDD already on\n");
+               return;
+       }
 
+       ironlake_wait_panel_off(intel_dp);
        pp = I915_READ(PCH_PP_CONTROL);
+       pp &= ~PANEL_UNLOCK_MASK;
+       pp |= PANEL_UNLOCK_REGS;
        pp |= EDP_FORCE_VDD;
        I915_WRITE(PCH_PP_CONTROL, pp);
        POSTING_READ(PCH_PP_CONTROL);
+       DRM_DEBUG_KMS("PCH_PP_STATUS: 0x%08x PCH_PP_CONTROL: 0x%08x\n",
+                     I915_READ(PCH_PP_STATUS), I915_READ(PCH_PP_CONTROL));
+
+       /*
+        * If the panel wasn't on, delay before accessing aux channel
+        */
+       if (!ironlake_edp_have_panel_power(intel_dp)) {
+               DRM_DEBUG_KMS("eDP was not running\n");
+               msleep(intel_dp->panel_power_up_delay);
+       }
 }
 
-static void ironlake_edp_panel_vdd_off(struct intel_dp *intel_dp)
+static void ironlake_panel_vdd_off_sync(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);
+       if (!intel_dp->want_panel_vdd && ironlake_edp_have_panel_vdd(intel_dp)) {
+               pp = I915_READ(PCH_PP_CONTROL);
+               pp &= ~PANEL_UNLOCK_MASK;
+               pp |= PANEL_UNLOCK_REGS;
+               pp &= ~EDP_FORCE_VDD;
+               I915_WRITE(PCH_PP_CONTROL, pp);
+               POSTING_READ(PCH_PP_CONTROL);
+
+               /* Make sure sequencer is idle before allowing subsequent activity */
+               DRM_DEBUG_KMS("PCH_PP_STATUS: 0x%08x PCH_PP_CONTROL: 0x%08x\n",
+                             I915_READ(PCH_PP_STATUS), I915_READ(PCH_PP_CONTROL));
+               intel_dp->panel_off_jiffies = jiffies;
+       }
+}
+
+static void ironlake_panel_vdd_work(struct work_struct *__work)
+{
+       struct intel_dp *intel_dp = container_of(to_delayed_work(__work),
+                                                struct intel_dp, panel_vdd_work);
+       struct drm_device *dev = intel_dp->base.base.dev;
+
+       mutex_lock(&dev->struct_mutex);
+       ironlake_panel_vdd_off_sync(intel_dp);
+       mutex_unlock(&dev->struct_mutex);
+}
+
+static void ironlake_edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync)
+{
+       if (!is_edp(intel_dp))
+               return;
+
+       DRM_DEBUG_KMS("Turn eDP VDD off %d\n", intel_dp->want_panel_vdd);
+       WARN(!intel_dp->want_panel_vdd, "eDP VDD not forced on");
+       
+       intel_dp->want_panel_vdd = false;
 
-       /* Make sure sequencer is idle before allowing subsequent activity */
-       msleep(dev_priv->panel_t12);
+       if (sync) {
+               ironlake_panel_vdd_off_sync(intel_dp);
+       } else {
+               /*
+                * Queue the timer to fire a long
+                * time from now (relative to the power down delay)
+                * to keep the panel power up across a sequence of operations
+                */
+               schedule_delayed_work(&intel_dp->panel_vdd_work,
+                                     msecs_to_jiffies(intel_dp->panel_power_cycle_delay * 5));
+       }
 }
 
 /* Returns true if the panel was already on when called */
-static bool ironlake_edp_panel_on (struct intel_dp *intel_dp)
+static void ironlake_edp_panel_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, idle_on_mask = PP_ON | PP_SEQUENCE_STATE_ON_IDLE;
 
-       if (I915_READ(PCH_PP_STATUS) & PP_ON)
-               return true;
+       if (!is_edp(intel_dp))
+               return;
+       if (ironlake_edp_have_panel_power(intel_dp))
+               return;
 
+       ironlake_wait_panel_off(intel_dp);
        pp = I915_READ(PCH_PP_CONTROL);
+       pp &= ~PANEL_UNLOCK_MASK;
+       pp |= PANEL_UNLOCK_REGS;
+
+       if (IS_GEN5(dev)) {
+               /* ILK workaround: disable reset around power sequence */
+               pp &= ~PANEL_POWER_RESET;
+               I915_WRITE(PCH_PP_CONTROL, pp);
+               POSTING_READ(PCH_PP_CONTROL);
+       }
 
-       /* ILK workaround: disable reset around power sequence */
-       pp &= ~PANEL_POWER_RESET;
-       I915_WRITE(PCH_PP_CONTROL, pp);
-       POSTING_READ(PCH_PP_CONTROL);
-
-       pp |= PANEL_UNLOCK_REGS | POWER_TARGET_ON;
+       pp |= POWER_TARGET_ON;
        I915_WRITE(PCH_PP_CONTROL, pp);
        POSTING_READ(PCH_PP_CONTROL);
 
@@ -854,44 +1021,64 @@ static bool ironlake_edp_panel_on (struct intel_dp *intel_dp)
                DRM_ERROR("panel on wait timed out: 0x%08x\n",
                          I915_READ(PCH_PP_STATUS));
 
-       pp |= PANEL_POWER_RESET; /* restore panel reset bit */
-       I915_WRITE(PCH_PP_CONTROL, pp);
-       POSTING_READ(PCH_PP_CONTROL);
-
-       return false;
+       if (IS_GEN5(dev)) {
+               pp |= PANEL_POWER_RESET; /* restore panel reset bit */
+               I915_WRITE(PCH_PP_CONTROL, pp);
+               POSTING_READ(PCH_PP_CONTROL);
+       }
 }
 
-static void ironlake_edp_panel_off (struct drm_device *dev)
+static void ironlake_edp_panel_off(struct drm_encoder *encoder)
 {
+       struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
+       struct drm_device *dev = encoder->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 pp, idle_off_mask = PP_ON | PP_SEQUENCE_MASK |
                PP_CYCLE_DELAY_ACTIVE | PP_SEQUENCE_STATE_MASK;
 
+       if (!is_edp(intel_dp))
+               return;
        pp = I915_READ(PCH_PP_CONTROL);
+       pp &= ~PANEL_UNLOCK_MASK;
+       pp |= PANEL_UNLOCK_REGS;
+
+       if (IS_GEN5(dev)) {
+               /* ILK workaround: disable reset around power sequence */
+               pp &= ~PANEL_POWER_RESET;
+               I915_WRITE(PCH_PP_CONTROL, pp);
+               POSTING_READ(PCH_PP_CONTROL);
+       }
 
-       /* ILK workaround: disable reset around power sequence */
-       pp &= ~PANEL_POWER_RESET;
-       I915_WRITE(PCH_PP_CONTROL, pp);
-       POSTING_READ(PCH_PP_CONTROL);
+       intel_dp->panel_off_jiffies = jiffies;
 
-       pp &= ~POWER_TARGET_ON;
-       I915_WRITE(PCH_PP_CONTROL, pp);
-       POSTING_READ(PCH_PP_CONTROL);
+       if (IS_GEN5(dev)) {
+               pp &= ~POWER_TARGET_ON;
+               I915_WRITE(PCH_PP_CONTROL, pp);
+               POSTING_READ(PCH_PP_CONTROL);
+               pp &= ~POWER_TARGET_ON;
+               I915_WRITE(PCH_PP_CONTROL, pp);
+               POSTING_READ(PCH_PP_CONTROL);
+               msleep(intel_dp->panel_power_cycle_delay);
 
-       if (wait_for((I915_READ(PCH_PP_STATUS) & idle_off_mask) == 0, 5000))
-               DRM_ERROR("panel off wait timed out: 0x%08x\n",
-                         I915_READ(PCH_PP_STATUS));
+               if (wait_for((I915_READ(PCH_PP_STATUS) & idle_off_mask) == 0, 5000))
+                       DRM_ERROR("panel off wait timed out: 0x%08x\n",
+                                 I915_READ(PCH_PP_STATUS));
 
-       pp |= PANEL_POWER_RESET; /* restore panel reset bit */
-       I915_WRITE(PCH_PP_CONTROL, pp);
-       POSTING_READ(PCH_PP_CONTROL);
+               pp |= PANEL_POWER_RESET; /* restore panel reset bit */
+               I915_WRITE(PCH_PP_CONTROL, pp);
+               POSTING_READ(PCH_PP_CONTROL);
+       }
 }
 
-static void ironlake_edp_backlight_on (struct drm_device *dev)
+static void ironlake_edp_backlight_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 (!is_edp(intel_dp))
+               return;
+
        DRM_DEBUG_KMS("\n");
        /*
         * If we enable the backlight right away following a panel power
@@ -899,21 +1086,32 @@ static void ironlake_edp_backlight_on (struct drm_device *dev)
         * link.  So delay a bit to make sure the image is solid before
         * allowing it to appear.
         */
-       msleep(300);
+       msleep(intel_dp->backlight_on_delay);
        pp = I915_READ(PCH_PP_CONTROL);
+       pp &= ~PANEL_UNLOCK_MASK;
+       pp |= PANEL_UNLOCK_REGS;
        pp |= EDP_BLC_ENABLE;
        I915_WRITE(PCH_PP_CONTROL, pp);
+       POSTING_READ(PCH_PP_CONTROL);
 }
 
-static void ironlake_edp_backlight_off (struct drm_device *dev)
+static void ironlake_edp_backlight_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;
 
+       if (!is_edp(intel_dp))
+               return;
+
        DRM_DEBUG_KMS("\n");
        pp = I915_READ(PCH_PP_CONTROL);
+       pp &= ~PANEL_UNLOCK_MASK;
+       pp |= PANEL_UNLOCK_REGS;
        pp &= ~EDP_BLC_ENABLE;
        I915_WRITE(PCH_PP_CONTROL, pp);
+       POSTING_READ(PCH_PP_CONTROL);
+       msleep(intel_dp->backlight_off_delay);
 }
 
 static void ironlake_edp_pll_on(struct drm_encoder *encoder)
@@ -976,41 +1174,34 @@ static void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode)
 static void intel_dp_prepare(struct drm_encoder *encoder)
 {
        struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
-       struct drm_device *dev = encoder->dev;
 
        /* Wake up the sink first */
+       ironlake_edp_panel_vdd_on(intel_dp);
        intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON);
+       ironlake_edp_panel_vdd_off(intel_dp, false);
 
-       if (is_edp(intel_dp)) {
-               ironlake_edp_backlight_off(dev);
-               ironlake_edp_panel_off(dev);
-               if (!is_pch_edp(intel_dp))
-                       ironlake_edp_pll_on(encoder);
-               else
-                       ironlake_edp_pll_off(encoder);
-       }
+       /* Make sure the panel is off before trying to
+        * change the mode
+        */
+       ironlake_edp_backlight_off(intel_dp);
        intel_dp_link_down(intel_dp);
+       ironlake_edp_panel_off(encoder);
 }
 
 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);
 
+       ironlake_edp_panel_vdd_on(intel_dp);
+       intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON);
        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);
-       }
+       ironlake_edp_panel_on(intel_dp);
+       ironlake_edp_panel_vdd_off(intel_dp, true);
 
        intel_dp_complete_link_train(intel_dp);
+       ironlake_edp_backlight_on(intel_dp);
 
-       if (is_edp(intel_dp))
-               ironlake_edp_backlight_on(dev);
+       intel_dp->dpms_mode = DRM_MODE_DPMS_ON;
 }
 
 static void
@@ -1022,29 +1213,29 @@ intel_dp_dpms(struct drm_encoder *encoder, int mode)
        uint32_t dp_reg = I915_READ(intel_dp->output_reg);
 
        if (mode != DRM_MODE_DPMS_ON) {
+               ironlake_edp_panel_vdd_on(intel_dp);
                if (is_edp(intel_dp))
-                       ironlake_edp_backlight_off(dev);
+                       ironlake_edp_backlight_off(intel_dp);
                intel_dp_sink_dpms(intel_dp, mode);
                intel_dp_link_down(intel_dp);
-               if (is_edp(intel_dp))
-                       ironlake_edp_panel_off(dev);
+               ironlake_edp_panel_off(encoder);
                if (is_edp(intel_dp) && !is_pch_edp(intel_dp))
                        ironlake_edp_pll_off(encoder);
+               ironlake_edp_panel_vdd_off(intel_dp, false);
        } else {
-               if (is_edp(intel_dp))
-                       ironlake_edp_panel_vdd_on(intel_dp);
+               ironlake_edp_panel_vdd_on(intel_dp);
                intel_dp_sink_dpms(intel_dp, mode);
                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);
-                       }
+                       ironlake_edp_panel_on(intel_dp);
+                       ironlake_edp_panel_vdd_off(intel_dp, true);
                        intel_dp_complete_link_train(intel_dp);
-               }
-               if (is_edp(intel_dp))
-                       ironlake_edp_backlight_on(dev);
+                       ironlake_edp_backlight_on(intel_dp);
+               } else
+                       ironlake_edp_panel_vdd_off(intel_dp, false);
+               ironlake_edp_backlight_on(intel_dp);
        }
+       intel_dp->dpms_mode = mode;
 }
 
 /*
@@ -1334,10 +1525,16 @@ intel_dp_start_link_train(struct intel_dp *intel_dp)
        u32 reg;
        uint32_t DP = intel_dp->DP;
 
-       /* Enable output, wait for it to become active */
-       I915_WRITE(intel_dp->output_reg, intel_dp->DP);
-       POSTING_READ(intel_dp->output_reg);
-       intel_wait_for_vblank(dev, intel_crtc->pipe);
+       /*
+        * On CPT we have to enable the port in training pattern 1, which
+        * will happen below in intel_dp_set_link_train.  Otherwise, enable
+        * the port and wait for it to become active.
+        */
+       if (!HAS_PCH_CPT(dev)) {
+               I915_WRITE(intel_dp->output_reg, intel_dp->DP);
+               POSTING_READ(intel_dp->output_reg);
+               intel_wait_for_vblank(dev, intel_crtc->pipe);
+       }
 
        /* Write the link configuration data */
        intel_dp_aux_native_write(intel_dp, DP_LINK_BW_SET,
@@ -1370,7 +1567,8 @@ intel_dp_start_link_train(struct intel_dp *intel_dp)
                        reg = DP | DP_LINK_TRAIN_PAT_1;
 
                if (!intel_dp_set_link_train(intel_dp, reg,
-                                            DP_TRAINING_PATTERN_1))
+                                            DP_TRAINING_PATTERN_1 |
+                                            DP_LINK_SCRAMBLING_DISABLE))
                        break;
                /* Set training pattern 1 */
 
@@ -1445,7 +1643,8 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp)
 
                /* channel eq pattern */
                if (!intel_dp_set_link_train(intel_dp, reg,
-                                            DP_TRAINING_PATTERN_2))
+                                            DP_TRAINING_PATTERN_2 |
+                                            DP_LINK_SCRAMBLING_DISABLE))
                        break;
 
                udelay(400);
@@ -1557,6 +1756,19 @@ intel_dp_link_down(struct intel_dp *intel_dp)
 
        I915_WRITE(intel_dp->output_reg, DP & ~DP_PORT_EN);
        POSTING_READ(intel_dp->output_reg);
+       msleep(intel_dp->panel_power_down_delay);
+}
+
+static bool
+intel_dp_get_dpcd(struct intel_dp *intel_dp)
+{
+       if (intel_dp_aux_native_read_retry(intel_dp, 0x000, intel_dp->dpcd,
+                                          sizeof(intel_dp->dpcd)) &&
+           (intel_dp->dpcd[DP_DPCD_REV] != 0)) {
+               return true;
+       }
+
+       return false;
 }
 
 /*
@@ -1571,36 +1783,44 @@ intel_dp_link_down(struct intel_dp *intel_dp)
 static void
 intel_dp_check_link_status(struct intel_dp *intel_dp)
 {
-       int ret;
+       if (intel_dp->dpms_mode != DRM_MODE_DPMS_ON)
+               return;
 
        if (!intel_dp->base.base.crtc)
                return;
 
+       /* Try to read receiver status if the link appears to be up */
        if (!intel_dp_get_link_status(intel_dp)) {
                intel_dp_link_down(intel_dp);
                return;
        }
 
-       /* Try to read receiver status if the link appears to be up */
-       ret = intel_dp_aux_native_read(intel_dp,
-                                      0x000, intel_dp->dpcd,
-                                      sizeof (intel_dp->dpcd));
-       if (ret != sizeof(intel_dp->dpcd)) {
+       /* Now read the DPCD to see if it's actually running */
+       if (!intel_dp_get_dpcd(intel_dp)) {
                intel_dp_link_down(intel_dp);
                return;
        }
 
        if (!intel_channel_eq_ok(intel_dp)) {
+               DRM_DEBUG_KMS("%s: channel EQ not ok, retraining\n",
+                             drm_get_encoder_name(&intel_dp->base.base));
                intel_dp_start_link_train(intel_dp);
                intel_dp_complete_link_train(intel_dp);
        }
 }
 
+static enum drm_connector_status
+intel_dp_detect_dpcd(struct intel_dp *intel_dp)
+{
+       if (intel_dp_get_dpcd(intel_dp))
+               return connector_status_connected;
+       return connector_status_disconnected;
+}
+
 static enum drm_connector_status
 ironlake_dp_detect(struct intel_dp *intel_dp)
 {
        enum drm_connector_status status;
-       bool ret;
 
        /* Can't disconnect eDP, but you can close the lid... */
        if (is_edp(intel_dp)) {
@@ -1610,15 +1830,7 @@ ironlake_dp_detect(struct intel_dp *intel_dp)
                return status;
        }
 
-       status = connector_status_disconnected;
-       ret = intel_dp_aux_native_read_retry(intel_dp,
-                                            0x000, intel_dp->dpcd,
-                                            sizeof (intel_dp->dpcd));
-       if (ret && intel_dp->dpcd[DP_DPCD_REV] != 0)
-               status = connector_status_connected;
-       DRM_DEBUG_KMS("DPCD: %hx%hx%hx%hx\n", intel_dp->dpcd[0],
-                     intel_dp->dpcd[1], intel_dp->dpcd[2], intel_dp->dpcd[3]);
-       return status;
+       return intel_dp_detect_dpcd(intel_dp);
 }
 
 static enum drm_connector_status
@@ -1626,7 +1838,6 @@ g4x_dp_detect(struct intel_dp *intel_dp)
 {
        struct drm_device *dev = intel_dp->base.base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       enum drm_connector_status status;
        uint32_t temp, bit;
 
        switch (intel_dp->output_reg) {
@@ -1648,17 +1859,34 @@ g4x_dp_detect(struct intel_dp *intel_dp)
        if ((temp & bit) == 0)
                return connector_status_disconnected;
 
-       status = connector_status_disconnected;
-       if (intel_dp_aux_native_read(intel_dp, 0x000, intel_dp->dpcd,
-                                    sizeof (intel_dp->dpcd)) == sizeof (intel_dp->dpcd))
-       {
-               if (intel_dp->dpcd[DP_DPCD_REV] != 0)
-                       status = connector_status_connected;
-       }
+       return intel_dp_detect_dpcd(intel_dp);
+}
 
-       return status;
+static struct edid *
+intel_dp_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
+{
+       struct intel_dp *intel_dp = intel_attached_dp(connector);
+       struct edid     *edid;
+
+       ironlake_edp_panel_vdd_on(intel_dp);
+       edid = drm_get_edid(connector, adapter);
+       ironlake_edp_panel_vdd_off(intel_dp, false);
+       return edid;
 }
 
+static int
+intel_dp_get_edid_modes(struct drm_connector *connector, struct i2c_adapter *adapter)
+{
+       struct intel_dp *intel_dp = intel_attached_dp(connector);
+       int     ret;
+
+       ironlake_edp_panel_vdd_on(intel_dp);
+       ret = intel_ddc_get_modes(connector, adapter);
+       ironlake_edp_panel_vdd_off(intel_dp, false);
+       return ret;
+}
+
+
 /**
  * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect DP connection.
  *
@@ -1679,13 +1907,19 @@ intel_dp_detect(struct drm_connector *connector, bool force)
                status = ironlake_dp_detect(intel_dp);
        else
                status = g4x_dp_detect(intel_dp);
+
+       DRM_DEBUG_KMS("DPCD: %02hx%02hx%02hx%02hx%02hx%02hx%02hx%02hx\n",
+                     intel_dp->dpcd[0], intel_dp->dpcd[1], intel_dp->dpcd[2],
+                     intel_dp->dpcd[3], intel_dp->dpcd[4], intel_dp->dpcd[5],
+                     intel_dp->dpcd[6], intel_dp->dpcd[7]);
+
        if (status != connector_status_connected)
                return status;
 
        if (intel_dp->force_audio) {
                intel_dp->has_audio = intel_dp->force_audio > 0;
        } else {
-               edid = drm_get_edid(connector, &intel_dp->adapter);
+               edid = intel_dp_get_edid(connector, &intel_dp->adapter);
                if (edid) {
                        intel_dp->has_audio = drm_detect_monitor_audio(edid);
                        connector->display_info.raw_edid = NULL;
@@ -1706,28 +1940,36 @@ static int intel_dp_get_modes(struct drm_connector *connector)
        /* We should parse the EDID data and find out if it has an audio sink
         */
 
-       ret = intel_ddc_get_modes(connector, &intel_dp->adapter);
+       ret = intel_dp_get_edid_modes(connector, &intel_dp->adapter);
        if (ret) {
-               if (is_edp(intel_dp) && !dev_priv->panel_fixed_mode) {
+               if (is_edp(intel_dp) && !intel_dp->panel_fixed_mode) {
                        struct drm_display_mode *newmode;
                        list_for_each_entry(newmode, &connector->probed_modes,
                                            head) {
-                               if (newmode->type & DRM_MODE_TYPE_PREFERRED) {
-                                       dev_priv->panel_fixed_mode =
+                               if ((newmode->type & DRM_MODE_TYPE_PREFERRED)) {
+                                       intel_dp->panel_fixed_mode =
                                                drm_mode_duplicate(dev, newmode);
                                        break;
                                }
                        }
                }
-
                return ret;
        }
 
        /* if eDP has no EDID, try to use fixed panel mode from VBT */
        if (is_edp(intel_dp)) {
-               if (dev_priv->panel_fixed_mode != NULL) {
+               /* initialize panel mode from VBT if available for eDP */
+               if (intel_dp->panel_fixed_mode == NULL && dev_priv->lfp_lvds_vbt_mode != NULL) {
+                       intel_dp->panel_fixed_mode =
+                               drm_mode_duplicate(dev, dev_priv->lfp_lvds_vbt_mode);
+                       if (intel_dp->panel_fixed_mode) {
+                               intel_dp->panel_fixed_mode->type |=
+                                       DRM_MODE_TYPE_PREFERRED;
+                       }
+               }
+               if (intel_dp->panel_fixed_mode) {
                        struct drm_display_mode *mode;
-                       mode = drm_mode_duplicate(dev, dev_priv->panel_fixed_mode);
+                       mode = drm_mode_duplicate(dev, intel_dp->panel_fixed_mode);
                        drm_mode_probed_add(connector, mode);
                        return 1;
                }
@@ -1742,7 +1984,7 @@ intel_dp_detect_audio(struct drm_connector *connector)
        struct edid *edid;
        bool has_audio = false;
 
-       edid = drm_get_edid(connector, &intel_dp->adapter);
+       edid = intel_dp_get_edid(connector, &intel_dp->adapter);
        if (edid) {
                has_audio = drm_detect_monitor_audio(edid);
 
@@ -1809,8 +2051,13 @@ done:
 }
 
 static void
-intel_dp_destroy (struct drm_connector *connector)
+intel_dp_destroy(struct drm_connector *connector)
 {
+       struct drm_device *dev = connector->dev;
+
+       if (intel_dpd_is_edp(dev))
+               intel_panel_destroy_backlight(dev);
+
        drm_sysfs_connector_remove(connector);
        drm_connector_cleanup(connector);
        kfree(connector);
@@ -1822,6 +2069,10 @@ static void intel_dp_encoder_destroy(struct drm_encoder *encoder)
 
        i2c_del_adapter(&intel_dp->adapter);
        drm_encoder_cleanup(encoder);
+       if (is_edp(intel_dp)) {
+               cancel_delayed_work_sync(&intel_dp->panel_vdd_work);
+               ironlake_panel_vdd_off_sync(intel_dp);
+       }
        kfree(intel_dp);
 }
 
@@ -1861,7 +2112,7 @@ intel_dp_hot_plug(struct intel_encoder *intel_encoder)
 
 /* Return which DP Port should be selected for Transcoder DP control */
 int
-intel_trans_dp_port_sel (struct drm_crtc *crtc)
+intel_trans_dp_port_sel(struct drm_crtc *crtc)
 {
        struct drm_device *dev = crtc->dev;
        struct drm_mode_config *mode_config = &dev->mode_config;
@@ -1924,6 +2175,7 @@ intel_dp_init(struct drm_device *dev, int output_reg)
                return;
 
        intel_dp->output_reg = output_reg;
+       intel_dp->dpms_mode = -1;
 
        intel_connector = kzalloc(sizeof(struct intel_connector), GFP_KERNEL);
        if (!intel_connector) {
@@ -1957,8 +2209,11 @@ intel_dp_init(struct drm_device *dev, int output_reg)
        else if (output_reg == DP_D || output_reg == PCH_DP_D)
                intel_encoder->clone_mask = (1 << INTEL_DP_D_CLONE_BIT);
 
-       if (is_edp(intel_dp))
+       if (is_edp(intel_dp)) {
                intel_encoder->clone_mask = (1 << INTEL_EDP_CLONE_BIT);
+               INIT_DELAYED_WORK(&intel_dp->panel_vdd_work,
+                                 ironlake_panel_vdd_work);
+       }
 
        intel_encoder->crtc_mask = (1 << 0) | (1 << 1);
        connector->interlace_allowed = true;
@@ -1996,28 +2251,61 @@ intel_dp_init(struct drm_device *dev, int output_reg)
                        break;
        }
 
-       intel_dp_i2c_init(intel_dp, intel_connector, name);
-
        /* Cache some DPCD data in the eDP case */
        if (is_edp(intel_dp)) {
-               int ret;
-               u32 pp_on, pp_div;
+               bool ret;
+               struct edp_power_seq    cur, vbt;
+               u32 pp_on, pp_off, pp_div;
 
                pp_on = I915_READ(PCH_PP_ON_DELAYS);
+               pp_off = I915_READ(PCH_PP_OFF_DELAYS);
                pp_div = I915_READ(PCH_PP_DIVISOR);
 
-               /* 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 */
+               /* Pull timing values out of registers */
+               cur.t1_t3 = (pp_on & PANEL_POWER_UP_DELAY_MASK) >>
+                       PANEL_POWER_UP_DELAY_SHIFT;
+
+               cur.t8 = (pp_on & PANEL_LIGHT_ON_DELAY_MASK) >>
+                       PANEL_LIGHT_ON_DELAY_SHIFT;
+               
+               cur.t9 = (pp_off & PANEL_LIGHT_OFF_DELAY_MASK) >>
+                       PANEL_LIGHT_OFF_DELAY_SHIFT;
+
+               cur.t10 = (pp_off & PANEL_POWER_DOWN_DELAY_MASK) >>
+                       PANEL_POWER_DOWN_DELAY_SHIFT;
+
+               cur.t11_t12 = ((pp_div & PANEL_POWER_CYCLE_DELAY_MASK) >>
+                              PANEL_POWER_CYCLE_DELAY_SHIFT) * 1000;
+
+               DRM_DEBUG_KMS("cur t1_t3 %d t8 %d t9 %d t10 %d t11_t12 %d\n",
+                             cur.t1_t3, cur.t8, cur.t9, cur.t10, cur.t11_t12);
+
+               vbt = dev_priv->edp.pps;
+
+               DRM_DEBUG_KMS("vbt t1_t3 %d t8 %d t9 %d t10 %d t11_t12 %d\n",
+                             vbt.t1_t3, vbt.t8, vbt.t9, vbt.t10, vbt.t11_t12);
+
+#define get_delay(field)       ((max(cur.field, vbt.field) + 9) / 10)
+
+               intel_dp->panel_power_up_delay = get_delay(t1_t3);
+               intel_dp->backlight_on_delay = get_delay(t8);
+               intel_dp->backlight_off_delay = get_delay(t9);
+               intel_dp->panel_power_down_delay = get_delay(t10);
+               intel_dp->panel_power_cycle_delay = get_delay(t11_t12);
+
+               DRM_DEBUG_KMS("panel power up delay %d, power down delay %d, power cycle delay %d\n",
+                             intel_dp->panel_power_up_delay, intel_dp->panel_power_down_delay,
+                             intel_dp->panel_power_cycle_delay);
+
+               DRM_DEBUG_KMS("backlight on delay %d, off delay %d\n",
+                             intel_dp->backlight_on_delay, intel_dp->backlight_off_delay);
+
+               intel_dp->panel_off_jiffies = jiffies - intel_dp->panel_power_down_delay;
 
                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));
-               ironlake_edp_panel_vdd_off(intel_dp);
-               if (ret == sizeof(intel_dp->dpcd)) {
+               ret = intel_dp_get_dpcd(intel_dp);
+               ironlake_edp_panel_vdd_off(intel_dp, false);
+               if (ret) {
                        if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11)
                                dev_priv->no_aux_handshake =
                                        intel_dp->dpcd[DP_MAX_DOWNSPREAD] &
@@ -2031,18 +2319,13 @@ intel_dp_init(struct drm_device *dev, int output_reg)
                }
        }
 
+       intel_dp_i2c_init(intel_dp, intel_connector, name);
+
        intel_encoder->hot_plug = intel_dp_hot_plug;
 
        if (is_edp(intel_dp)) {
-               /* initialize panel mode from VBT if available for eDP */
-               if (dev_priv->lfp_lvds_vbt_mode) {
-                       dev_priv->panel_fixed_mode =
-                               drm_mode_duplicate(dev, dev_priv->lfp_lvds_vbt_mode);
-                       if (dev_priv->panel_fixed_mode) {
-                               dev_priv->panel_fixed_mode->type |=
-                                       DRM_MODE_TYPE_PREFERRED;
-                       }
-               }
+               dev_priv->int_edp_connector = connector;
+               intel_panel_setup_backlight(dev);
        }
 
        intel_dp_add_properties(intel_dp, connector);