aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm
diff options
context:
space:
mode:
authorDaniel Vetter <daniel.vetter@ffwll.ch>2012-12-01 07:53:48 -0500
committerDaniel Vetter <daniel.vetter@ffwll.ch>2012-12-06 07:18:00 -0500
commit9ee32fea5fe810ec06af3a15e4c65478de56d4f5 (patch)
treebd12375772bcd86ec85e3236d6072aef7e99da40 /drivers/gpu/drm
parentce99c2569dfcec9662993961db7f433e160c50f3 (diff)
drm/i915: irq-drive the dp aux communication
At least on the platforms that have a dp aux irq and also have it enabled - vlvhsw should have one, too. But I don't have a machine to test this on. Judging from docs there's no dp aux interrupt for gm45. Also, I only have an ivb cpu edp machine, so the dp aux A code for snb/ilk is untested. For dpcd probing when nothing is connected it slashes about 5ms of cpu time (cpu time is now negligible), which agrees with 3 * 5 400 usec timeouts. A previous version of this patch increases the time required to go through the dp_detect cycle (which includes reading the edid) from around 33 ms to around 40 ms. Experiments indicated that this is purely due to the irq latency - the hw doesn't allow us to queue up dp aux transactions and hence irq latency directly affects throughput. gmbus is much better, there we have a 8 byte buffer, and we get the irq once another 4 bytes can be queued up. But by using the pm_qos interface to request the lowest possible cpu wake-up latency this slowdown completely disappeared. Since all our output detection logic is single-threaded with the mode_config mutex right now anyway, I've decide not ot play fancy and to just reuse the gmbus wait queue. But this would definitely prep the way to run dp detection on different ports in parallel v2: Add a timeout for dp aux transfers when using interrupts - the hw _does_ prevent this with the hw-based 400 usec timeout, but if the irq somehow doesn't arrive we're screwed. Lesson learned while developing this ;-) v3: While at it also convert the busy-loop to wait_for_atomic, so that we don't run the risk of an infinite loop any more. v4: Ensure we have the smallest possible irq latency by using the pm_qos interface. v5: Add a comment to the code to explain why we frob pm_qos. Suggested by Chris Wilson. v6: Disable dp irq for vlv, that's easier than trying to get at docs and hw. v7: Squash in a fix for Haswell that Paulo Zanoni tracked down - the dp aux registers aren't at a fixed offset any more, but can be on the PCH while the DP port is on the cpu die. Reviewed-by: Imre Deak <imre.deak@intel.com> (v6) Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Diffstat (limited to 'drivers/gpu/drm')
-rw-r--r--drivers/gpu/drm/i915/i915_dma.c1
-rw-r--r--drivers/gpu/drm/i915/i915_drv.h4
-rw-r--r--drivers/gpu/drm/i915/i915_irq.c6
-rw-r--r--drivers/gpu/drm/i915/intel_dp.c79
4 files changed, 77 insertions, 13 deletions
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index 14d679ab52b7..93630669c6dc 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -1737,6 +1737,7 @@ int i915_driver_unload(struct drm_device *dev)
1737 intel_teardown_mchbar(dev); 1737 intel_teardown_mchbar(dev);
1738 1738
1739 destroy_workqueue(dev_priv->wq); 1739 destroy_workqueue(dev_priv->wq);
1740 pm_qos_remove_request(&dev_priv->pm_qos);
1740 1741
1741 if (dev_priv->slab) 1742 if (dev_priv->slab)
1742 kmem_cache_destroy(dev_priv->slab); 1743 kmem_cache_destroy(dev_priv->slab);
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 3843383e13cc..a00ee3da632f 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -40,6 +40,7 @@
40#include <linux/backlight.h> 40#include <linux/backlight.h>
41#include <linux/intel-iommu.h> 41#include <linux/intel-iommu.h>
42#include <linux/kref.h> 42#include <linux/kref.h>
43#include <linux/pm_qos.h>
43 44
44/* General customization: 45/* General customization:
45 */ 46 */
@@ -665,6 +666,9 @@ typedef struct drm_i915_private {
665 /* protects the irq masks */ 666 /* protects the irq masks */
666 spinlock_t irq_lock; 667 spinlock_t irq_lock;
667 668
669 /* To control wakeup latency, e.g. for irq-driven dp aux transfers. */
670 struct pm_qos_request pm_qos;
671
668 /* DPIO indirect register protection */ 672 /* DPIO indirect register protection */
669 spinlock_t dpio_lock; 673 spinlock_t dpio_lock;
670 674
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index 1e245e501e5a..58bb11b58796 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -536,7 +536,11 @@ static void gmbus_irq_handler(struct drm_device *dev)
536 536
537static void dp_aux_irq_handler(struct drm_device *dev) 537static void dp_aux_irq_handler(struct drm_device *dev)
538{ 538{
539 struct drm_i915_private *dev_priv = (drm_i915_private_t *) dev->dev_private;
540
539 DRM_DEBUG_DRIVER("AUX channel interrupt\n"); 541 DRM_DEBUG_DRIVER("AUX channel interrupt\n");
542
543 wake_up_all(&dev_priv->gmbus_wait_queue);
540} 544}
541 545
542static irqreturn_t valleyview_irq_handler(int irq, void *arg) 546static irqreturn_t valleyview_irq_handler(int irq, void *arg)
@@ -2732,6 +2736,8 @@ void intel_irq_init(struct drm_device *dev)
2732 setup_timer(&dev_priv->hangcheck_timer, i915_hangcheck_elapsed, 2736 setup_timer(&dev_priv->hangcheck_timer, i915_hangcheck_elapsed,
2733 (unsigned long) dev); 2737 (unsigned long) dev);
2734 2738
2739 pm_qos_add_request(&dev_priv->pm_qos, PM_QOS_CPU_DMA_LATENCY, 0);
2740
2735 dev->driver->get_vblank_counter = i915_get_vblank_counter; 2741 dev->driver->get_vblank_counter = i915_get_vblank_counter;
2736 dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */ 2742 dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */
2737 if (IS_G4X(dev) || INTEL_INFO(dev)->gen >= 5) { 2743 if (IS_G4X(dev) || INTEL_INFO(dev)->gen >= 5) {
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index b51043e651b5..ada1b316195c 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -322,6 +322,48 @@ intel_dp_check_edp(struct intel_dp *intel_dp)
322 } 322 }
323} 323}
324 324
325static uint32_t
326intel_dp_aux_wait_done(struct intel_dp *intel_dp, bool has_aux_irq)
327{
328 struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
329 struct drm_device *dev = intel_dig_port->base.base.dev;
330 struct drm_i915_private *dev_priv = dev->dev_private;
331 uint32_t ch_ctl = intel_dp->output_reg + 0x10;
332 uint32_t status;
333 bool done;
334
335 if (IS_HASWELL(dev)) {
336 switch (intel_dig_port->port) {
337 case PORT_A:
338 ch_ctl = DPA_AUX_CH_CTL;
339 break;
340 case PORT_B:
341 ch_ctl = PCH_DPB_AUX_CH_CTL;
342 break;
343 case PORT_C:
344 ch_ctl = PCH_DPC_AUX_CH_CTL;
345 break;
346 case PORT_D:
347 ch_ctl = PCH_DPD_AUX_CH_CTL;
348 break;
349 default:
350 BUG();
351 }
352 }
353
354#define C (((status = I915_READ(ch_ctl)) & DP_AUX_CH_CTL_SEND_BUSY) == 0)
355 if (has_aux_irq)
356 done = wait_event_timeout(dev_priv->gmbus_wait_queue, C, 10);
357 else
358 done = wait_for_atomic(C, 10) == 0;
359 if (!done)
360 DRM_ERROR("dp aux hw did not signal timeout (has irq: %i)!\n",
361 has_aux_irq);
362#undef C
363
364 return status;
365}
366
325static int 367static int
326intel_dp_aux_ch(struct intel_dp *intel_dp, 368intel_dp_aux_ch(struct intel_dp *intel_dp,
327 uint8_t *send, int send_bytes, 369 uint8_t *send, int send_bytes,
@@ -333,11 +375,17 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
333 struct drm_i915_private *dev_priv = dev->dev_private; 375 struct drm_i915_private *dev_priv = dev->dev_private;
334 uint32_t ch_ctl = output_reg + 0x10; 376 uint32_t ch_ctl = output_reg + 0x10;
335 uint32_t ch_data = ch_ctl + 4; 377 uint32_t ch_data = ch_ctl + 4;
336 int i; 378 int i, ret, recv_bytes;
337 int recv_bytes;
338 uint32_t status; 379 uint32_t status;
339 uint32_t aux_clock_divider; 380 uint32_t aux_clock_divider;
340 int try, precharge; 381 int try, precharge;
382 bool has_aux_irq = INTEL_INFO(dev)->gen >= 5 && !IS_VALLEYVIEW(dev);
383
384 /* dp aux is extremely sensitive to irq latency, hence request the
385 * lowest possible wakeup latency and so prevent the cpu from going into
386 * deep sleep states.
387 */
388 pm_qos_update_request(&dev_priv->pm_qos, 0);
341 389
342 if (IS_HASWELL(dev)) { 390 if (IS_HASWELL(dev)) {
343 switch (intel_dig_port->port) { 391 switch (intel_dig_port->port) {
@@ -400,7 +448,8 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
400 if (try == 3) { 448 if (try == 3) {
401 WARN(1, "dp_aux_ch not started status 0x%08x\n", 449 WARN(1, "dp_aux_ch not started status 0x%08x\n",
402 I915_READ(ch_ctl)); 450 I915_READ(ch_ctl));
403 return -EBUSY; 451 ret = -EBUSY;
452 goto out;
404 } 453 }
405 454
406 /* Must try at least 3 times according to DP spec */ 455 /* Must try at least 3 times according to DP spec */
@@ -413,6 +462,7 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
413 /* Send the command and wait for it to complete */ 462 /* Send the command and wait for it to complete */
414 I915_WRITE(ch_ctl, 463 I915_WRITE(ch_ctl,
415 DP_AUX_CH_CTL_SEND_BUSY | 464 DP_AUX_CH_CTL_SEND_BUSY |
465 (has_aux_irq ? DP_AUX_CH_CTL_INTERRUPT : 0) |
416 DP_AUX_CH_CTL_TIME_OUT_400us | 466 DP_AUX_CH_CTL_TIME_OUT_400us |
417 (send_bytes << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) | 467 (send_bytes << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) |
418 (precharge << DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT) | 468 (precharge << DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT) |
@@ -420,12 +470,8 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
420 DP_AUX_CH_CTL_DONE | 470 DP_AUX_CH_CTL_DONE |
421 DP_AUX_CH_CTL_TIME_OUT_ERROR | 471 DP_AUX_CH_CTL_TIME_OUT_ERROR |
422 DP_AUX_CH_CTL_RECEIVE_ERROR); 472 DP_AUX_CH_CTL_RECEIVE_ERROR);
423 for (;;) { 473
424 status = I915_READ(ch_ctl); 474 status = intel_dp_aux_wait_done(intel_dp, has_aux_irq);
425 if ((status & DP_AUX_CH_CTL_SEND_BUSY) == 0)
426 break;
427 udelay(100);
428 }
429 475
430 /* Clear done status and any errors */ 476 /* Clear done status and any errors */
431 I915_WRITE(ch_ctl, 477 I915_WRITE(ch_ctl,
@@ -443,7 +489,8 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
443 489
444 if ((status & DP_AUX_CH_CTL_DONE) == 0) { 490 if ((status & DP_AUX_CH_CTL_DONE) == 0) {
445 DRM_ERROR("dp_aux_ch not done status 0x%08x\n", status); 491 DRM_ERROR("dp_aux_ch not done status 0x%08x\n", status);
446 return -EBUSY; 492 ret = -EBUSY;
493 goto out;
447 } 494 }
448 495
449 /* Check for timeout or receive error. 496 /* Check for timeout or receive error.
@@ -451,14 +498,16 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
451 */ 498 */
452 if (status & DP_AUX_CH_CTL_RECEIVE_ERROR) { 499 if (status & DP_AUX_CH_CTL_RECEIVE_ERROR) {
453 DRM_ERROR("dp_aux_ch receive error status 0x%08x\n", status); 500 DRM_ERROR("dp_aux_ch receive error status 0x%08x\n", status);
454 return -EIO; 501 ret = -EIO;
502 goto out;
455 } 503 }
456 504
457 /* Timeouts occur when the device isn't connected, so they're 505 /* Timeouts occur when the device isn't connected, so they're
458 * "normal" -- don't fill the kernel log with these */ 506 * "normal" -- don't fill the kernel log with these */
459 if (status & DP_AUX_CH_CTL_TIME_OUT_ERROR) { 507 if (status & DP_AUX_CH_CTL_TIME_OUT_ERROR) {
460 DRM_DEBUG_KMS("dp_aux_ch timeout status 0x%08x\n", status); 508 DRM_DEBUG_KMS("dp_aux_ch timeout status 0x%08x\n", status);
461 return -ETIMEDOUT; 509 ret = -ETIMEDOUT;
510 goto out;
462 } 511 }
463 512
464 /* Unload any bytes sent back from the other side */ 513 /* Unload any bytes sent back from the other side */
@@ -471,7 +520,11 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
471 unpack_aux(I915_READ(ch_data + i), 520 unpack_aux(I915_READ(ch_data + i),
472 recv + i, recv_bytes - i); 521 recv + i, recv_bytes - i);
473 522
474 return recv_bytes; 523 ret = recv_bytes;
524out:
525 pm_qos_update_request(&dev_priv->pm_qos, PM_QOS_DEFAULT_VALUE);
526
527 return ret;
475} 528}
476 529
477/* Write data to the aux channel in native mode */ 530/* Write data to the aux channel in native mode */