|
@@ -322,6 +322,48 @@ intel_dp_check_edp(struct intel_dp *intel_dp)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+static uint32_t
|
|
|
+intel_dp_aux_wait_done(struct intel_dp *intel_dp, bool has_aux_irq)
|
|
|
+{
|
|
|
+ struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
|
|
|
+ struct drm_device *dev = intel_dig_port->base.base.dev;
|
|
|
+ struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
+ uint32_t ch_ctl = intel_dp->output_reg + 0x10;
|
|
|
+ uint32_t status;
|
|
|
+ bool done;
|
|
|
+
|
|
|
+ if (IS_HASWELL(dev)) {
|
|
|
+ switch (intel_dig_port->port) {
|
|
|
+ case PORT_A:
|
|
|
+ ch_ctl = DPA_AUX_CH_CTL;
|
|
|
+ break;
|
|
|
+ case PORT_B:
|
|
|
+ ch_ctl = PCH_DPB_AUX_CH_CTL;
|
|
|
+ break;
|
|
|
+ case PORT_C:
|
|
|
+ ch_ctl = PCH_DPC_AUX_CH_CTL;
|
|
|
+ break;
|
|
|
+ case PORT_D:
|
|
|
+ ch_ctl = PCH_DPD_AUX_CH_CTL;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ BUG();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+#define C (((status = I915_READ(ch_ctl)) & DP_AUX_CH_CTL_SEND_BUSY) == 0)
|
|
|
+ if (has_aux_irq)
|
|
|
+ done = wait_event_timeout(dev_priv->gmbus_wait_queue, C, 10);
|
|
|
+ else
|
|
|
+ done = wait_for_atomic(C, 10) == 0;
|
|
|
+ if (!done)
|
|
|
+ DRM_ERROR("dp aux hw did not signal timeout (has irq: %i)!\n",
|
|
|
+ has_aux_irq);
|
|
|
+#undef C
|
|
|
+
|
|
|
+ return status;
|
|
|
+}
|
|
|
+
|
|
|
static int
|
|
|
intel_dp_aux_ch(struct intel_dp *intel_dp,
|
|
|
uint8_t *send, int send_bytes,
|
|
@@ -333,11 +375,17 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
|
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
uint32_t ch_ctl = output_reg + 0x10;
|
|
|
uint32_t ch_data = ch_ctl + 4;
|
|
|
- int i;
|
|
|
- int recv_bytes;
|
|
|
+ int i, ret, recv_bytes;
|
|
|
uint32_t status;
|
|
|
uint32_t aux_clock_divider;
|
|
|
int try, precharge;
|
|
|
+ bool has_aux_irq = INTEL_INFO(dev)->gen >= 5 && !IS_VALLEYVIEW(dev);
|
|
|
+
|
|
|
+ /* dp aux is extremely sensitive to irq latency, hence request the
|
|
|
+ * lowest possible wakeup latency and so prevent the cpu from going into
|
|
|
+ * deep sleep states.
|
|
|
+ */
|
|
|
+ pm_qos_update_request(&dev_priv->pm_qos, 0);
|
|
|
|
|
|
if (IS_HASWELL(dev)) {
|
|
|
switch (intel_dig_port->port) {
|
|
@@ -400,7 +448,8 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
|
|
|
if (try == 3) {
|
|
|
WARN(1, "dp_aux_ch not started status 0x%08x\n",
|
|
|
I915_READ(ch_ctl));
|
|
|
- return -EBUSY;
|
|
|
+ ret = -EBUSY;
|
|
|
+ goto out;
|
|
|
}
|
|
|
|
|
|
/* Must try at least 3 times according to DP spec */
|
|
@@ -413,6 +462,7 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
|
|
|
/* Send the command and wait for it to complete */
|
|
|
I915_WRITE(ch_ctl,
|
|
|
DP_AUX_CH_CTL_SEND_BUSY |
|
|
|
+ (has_aux_irq ? DP_AUX_CH_CTL_INTERRUPT : 0) |
|
|
|
DP_AUX_CH_CTL_TIME_OUT_400us |
|
|
|
(send_bytes << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) |
|
|
|
(precharge << DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT) |
|
|
@@ -420,12 +470,8 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
|
|
|
DP_AUX_CH_CTL_DONE |
|
|
|
DP_AUX_CH_CTL_TIME_OUT_ERROR |
|
|
|
DP_AUX_CH_CTL_RECEIVE_ERROR);
|
|
|
- for (;;) {
|
|
|
- status = I915_READ(ch_ctl);
|
|
|
- if ((status & DP_AUX_CH_CTL_SEND_BUSY) == 0)
|
|
|
- break;
|
|
|
- udelay(100);
|
|
|
- }
|
|
|
+
|
|
|
+ status = intel_dp_aux_wait_done(intel_dp, has_aux_irq);
|
|
|
|
|
|
/* Clear done status and any errors */
|
|
|
I915_WRITE(ch_ctl,
|
|
@@ -443,7 +489,8 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
|
|
|
|
|
|
if ((status & DP_AUX_CH_CTL_DONE) == 0) {
|
|
|
DRM_ERROR("dp_aux_ch not done status 0x%08x\n", status);
|
|
|
- return -EBUSY;
|
|
|
+ ret = -EBUSY;
|
|
|
+ goto out;
|
|
|
}
|
|
|
|
|
|
/* Check for timeout or receive error.
|
|
@@ -451,14 +498,16 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
|
|
|
*/
|
|
|
if (status & DP_AUX_CH_CTL_RECEIVE_ERROR) {
|
|
|
DRM_ERROR("dp_aux_ch receive error status 0x%08x\n", status);
|
|
|
- return -EIO;
|
|
|
+ ret = -EIO;
|
|
|
+ goto out;
|
|
|
}
|
|
|
|
|
|
/* Timeouts occur when the device isn't connected, so they're
|
|
|
* "normal" -- don't fill the kernel log with these */
|
|
|
if (status & DP_AUX_CH_CTL_TIME_OUT_ERROR) {
|
|
|
DRM_DEBUG_KMS("dp_aux_ch timeout status 0x%08x\n", status);
|
|
|
- return -ETIMEDOUT;
|
|
|
+ ret = -ETIMEDOUT;
|
|
|
+ goto out;
|
|
|
}
|
|
|
|
|
|
/* Unload any bytes sent back from the other side */
|
|
@@ -471,7 +520,11 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
|
|
|
unpack_aux(I915_READ(ch_data + i),
|
|
|
recv + i, recv_bytes - i);
|
|
|
|
|
|
- return recv_bytes;
|
|
|
+ ret = recv_bytes;
|
|
|
+out:
|
|
|
+ pm_qos_update_request(&dev_priv->pm_qos, PM_QOS_DEFAULT_VALUE);
|
|
|
+
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
/* Write data to the aux channel in native mode */
|