Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 357897
b: refs/heads/master
c: 9ee32fe
h: refs/heads/master
i:
  357895: 2feb939
v: v3
  • Loading branch information
Daniel Vetter committed Dec 6, 2012
1 parent 39b69ee commit 847a5eb
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 14 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: ce99c2569dfcec9662993961db7f433e160c50f3
refs/heads/master: 9ee32fea5fe810ec06af3a15e4c65478de56d4f5
1 change: 1 addition & 0 deletions trunk/drivers/gpu/drm/i915/i915_dma.c
Original file line number Diff line number Diff line change
Expand Up @@ -1737,6 +1737,7 @@ int i915_driver_unload(struct drm_device *dev)
intel_teardown_mchbar(dev);

destroy_workqueue(dev_priv->wq);
pm_qos_remove_request(&dev_priv->pm_qos);

if (dev_priv->slab)
kmem_cache_destroy(dev_priv->slab);
Expand Down
4 changes: 4 additions & 0 deletions trunk/drivers/gpu/drm/i915/i915_drv.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
#include <linux/backlight.h>
#include <linux/intel-iommu.h>
#include <linux/kref.h>
#include <linux/pm_qos.h>

/* General customization:
*/
Expand Down Expand Up @@ -665,6 +666,9 @@ typedef struct drm_i915_private {
/* protects the irq masks */
spinlock_t irq_lock;

/* To control wakeup latency, e.g. for irq-driven dp aux transfers. */
struct pm_qos_request pm_qos;

/* DPIO indirect register protection */
spinlock_t dpio_lock;

Expand Down
6 changes: 6 additions & 0 deletions trunk/drivers/gpu/drm/i915/i915_irq.c
Original file line number Diff line number Diff line change
Expand Up @@ -536,7 +536,11 @@ static void gmbus_irq_handler(struct drm_device *dev)

static void dp_aux_irq_handler(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = (drm_i915_private_t *) dev->dev_private;

DRM_DEBUG_DRIVER("AUX channel interrupt\n");

wake_up_all(&dev_priv->gmbus_wait_queue);
}

static irqreturn_t valleyview_irq_handler(int irq, void *arg)
Expand Down Expand Up @@ -2732,6 +2736,8 @@ void intel_irq_init(struct drm_device *dev)
setup_timer(&dev_priv->hangcheck_timer, i915_hangcheck_elapsed,
(unsigned long) dev);

pm_qos_add_request(&dev_priv->pm_qos, PM_QOS_CPU_DMA_LATENCY, 0);

dev->driver->get_vblank_counter = i915_get_vblank_counter;
dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */
if (IS_G4X(dev) || INTEL_INFO(dev)->gen >= 5) {
Expand Down
79 changes: 66 additions & 13 deletions trunk/drivers/gpu/drm/i915/intel_dp.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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) {
Expand Down Expand Up @@ -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 */
Expand All @@ -413,19 +462,16 @@ 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) |
(aux_clock_divider << DP_AUX_CH_CTL_BIT_CLOCK_2X_SHIFT) |
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,
Expand All @@ -443,22 +489,25 @@ 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.
* Timeouts occur when the sink is not connected
*/
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 */
Expand All @@ -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 */
Expand Down

0 comments on commit 847a5eb

Please sign in to comment.