diff options
author | Jesse Barnes <jbarnes@virtuousgeek.org> | 2008-04-22 02:03:07 -0400 |
---|---|---|
committer | Dave Airlie <airlied@linux.ie> | 2008-04-26 04:01:07 -0400 |
commit | ac741ab71bb39e6977694ac0cc26678d8673cda4 (patch) | |
tree | f82e08399a0da5accba930444744b269479185dd /drivers/char/drm/i915_dma.c | |
parent | 2c14f28be2a3f2a2e9861b156d64fbe2bc7000c3 (diff) |
drm/vbl rework: rework how the drm deals with vblank.
Other Authors: Michel Dänzer <michel@tungstengraphics.com>
mga: Ian Romanick <idr@us.ibm.com>
via: Thomas Hellstrom <thomas-at-tungstengraphics-dot-com>
This re-works the DRM internals to provide a better interface for drivers
to expose vblank on multiple crtcs.
It also includes work done by Michel on making i915 triple buffering and pageflipping work properly.
Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/char/drm/i915_dma.c')
-rw-r--r-- | drivers/char/drm/i915_dma.c | 160 |
1 files changed, 119 insertions, 41 deletions
diff --git a/drivers/char/drm/i915_dma.c b/drivers/char/drm/i915_dma.c index a043bb12301..ef7bf143a80 100644 --- a/drivers/char/drm/i915_dma.c +++ b/drivers/char/drm/i915_dma.c | |||
@@ -415,10 +415,13 @@ static void i915_emit_breadcrumb(struct drm_device *dev) | |||
415 | drm_i915_private_t *dev_priv = dev->dev_private; | 415 | drm_i915_private_t *dev_priv = dev->dev_private; |
416 | RING_LOCALS; | 416 | RING_LOCALS; |
417 | 417 | ||
418 | dev_priv->sarea_priv->last_enqueue = ++dev_priv->counter; | 418 | if (++dev_priv->counter > BREADCRUMB_MASK) { |
419 | dev_priv->counter = 1; | ||
420 | DRM_DEBUG("Breadcrumb counter wrapped around\n"); | ||
421 | } | ||
419 | 422 | ||
420 | if (dev_priv->counter > 0x7FFFFFFFUL) | 423 | if (dev_priv->sarea_priv) |
421 | dev_priv->sarea_priv->last_enqueue = dev_priv->counter = 1; | 424 | dev_priv->sarea_priv->last_enqueue = dev_priv->counter; |
422 | 425 | ||
423 | BEGIN_LP_RING(4); | 426 | BEGIN_LP_RING(4); |
424 | OUT_RING(CMD_STORE_DWORD_IDX); | 427 | OUT_RING(CMD_STORE_DWORD_IDX); |
@@ -428,6 +431,26 @@ static void i915_emit_breadcrumb(struct drm_device *dev) | |||
428 | ADVANCE_LP_RING(); | 431 | ADVANCE_LP_RING(); |
429 | } | 432 | } |
430 | 433 | ||
434 | int i915_emit_mi_flush(struct drm_device *dev, uint32_t flush) | ||
435 | { | ||
436 | drm_i915_private_t *dev_priv = dev->dev_private; | ||
437 | uint32_t flush_cmd = CMD_MI_FLUSH; | ||
438 | RING_LOCALS; | ||
439 | |||
440 | flush_cmd |= flush; | ||
441 | |||
442 | i915_kernel_lost_context(dev); | ||
443 | |||
444 | BEGIN_LP_RING(4); | ||
445 | OUT_RING(flush_cmd); | ||
446 | OUT_RING(0); | ||
447 | OUT_RING(0); | ||
448 | OUT_RING(0); | ||
449 | ADVANCE_LP_RING(); | ||
450 | |||
451 | return 0; | ||
452 | } | ||
453 | |||
431 | static int i915_dispatch_cmdbuffer(struct drm_device * dev, | 454 | static int i915_dispatch_cmdbuffer(struct drm_device * dev, |
432 | drm_i915_cmdbuffer_t * cmd) | 455 | drm_i915_cmdbuffer_t * cmd) |
433 | { | 456 | { |
@@ -511,52 +534,74 @@ static int i915_dispatch_batchbuffer(struct drm_device * dev, | |||
511 | return 0; | 534 | return 0; |
512 | } | 535 | } |
513 | 536 | ||
514 | static int i915_dispatch_flip(struct drm_device * dev) | 537 | static void i915_do_dispatch_flip(struct drm_device * dev, int plane, int sync) |
515 | { | 538 | { |
516 | drm_i915_private_t *dev_priv = dev->dev_private; | 539 | drm_i915_private_t *dev_priv = dev->dev_private; |
540 | u32 num_pages, current_page, next_page, dspbase; | ||
541 | int shift = 2 * plane, x, y; | ||
517 | RING_LOCALS; | 542 | RING_LOCALS; |
518 | 543 | ||
519 | DRM_DEBUG("%s: page=%d pfCurrentPage=%d\n", | 544 | /* Calculate display base offset */ |
520 | __FUNCTION__, | 545 | num_pages = dev_priv->sarea_priv->third_handle ? 3 : 2; |
521 | dev_priv->current_page, | 546 | current_page = (dev_priv->sarea_priv->pf_current_page >> shift) & 0x3; |
522 | dev_priv->sarea_priv->pf_current_page); | 547 | next_page = (current_page + 1) % num_pages; |
523 | 548 | ||
524 | i915_kernel_lost_context(dev); | 549 | switch (next_page) { |
525 | 550 | default: | |
526 | BEGIN_LP_RING(2); | 551 | case 0: |
527 | OUT_RING(INST_PARSER_CLIENT | INST_OP_FLUSH | INST_FLUSH_MAP_CACHE); | 552 | dspbase = dev_priv->sarea_priv->front_offset; |
528 | OUT_RING(0); | 553 | break; |
529 | ADVANCE_LP_RING(); | 554 | case 1: |
555 | dspbase = dev_priv->sarea_priv->back_offset; | ||
556 | break; | ||
557 | case 2: | ||
558 | dspbase = dev_priv->sarea_priv->third_offset; | ||
559 | break; | ||
560 | } | ||
530 | 561 | ||
531 | BEGIN_LP_RING(6); | 562 | if (plane == 0) { |
532 | OUT_RING(CMD_OP_DISPLAYBUFFER_INFO | ASYNC_FLIP); | 563 | x = dev_priv->sarea_priv->planeA_x; |
533 | OUT_RING(0); | 564 | y = dev_priv->sarea_priv->planeA_y; |
534 | if (dev_priv->current_page == 0) { | ||
535 | OUT_RING(dev_priv->back_offset); | ||
536 | dev_priv->current_page = 1; | ||
537 | } else { | 565 | } else { |
538 | OUT_RING(dev_priv->front_offset); | 566 | x = dev_priv->sarea_priv->planeB_x; |
539 | dev_priv->current_page = 0; | 567 | y = dev_priv->sarea_priv->planeB_y; |
540 | } | 568 | } |
541 | OUT_RING(0); | ||
542 | ADVANCE_LP_RING(); | ||
543 | 569 | ||
544 | BEGIN_LP_RING(2); | 570 | dspbase += (y * dev_priv->sarea_priv->pitch + x) * dev_priv->cpp; |
545 | OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_PLANE_A_FLIP); | ||
546 | OUT_RING(0); | ||
547 | ADVANCE_LP_RING(); | ||
548 | 571 | ||
549 | dev_priv->sarea_priv->last_enqueue = dev_priv->counter++; | 572 | DRM_DEBUG("plane=%d current_page=%d dspbase=0x%x\n", plane, current_page, |
573 | dspbase); | ||
550 | 574 | ||
551 | BEGIN_LP_RING(4); | 575 | BEGIN_LP_RING(4); |
552 | OUT_RING(CMD_STORE_DWORD_IDX); | 576 | OUT_RING(sync ? 0 : |
553 | OUT_RING(20); | 577 | (MI_WAIT_FOR_EVENT | (plane ? MI_WAIT_FOR_PLANE_B_FLIP : |
554 | OUT_RING(dev_priv->counter); | 578 | MI_WAIT_FOR_PLANE_A_FLIP))); |
555 | OUT_RING(0); | 579 | OUT_RING(CMD_OP_DISPLAYBUFFER_INFO | (sync ? 0 : ASYNC_FLIP) | |
580 | (plane ? DISPLAY_PLANE_B : DISPLAY_PLANE_A)); | ||
581 | OUT_RING(dev_priv->sarea_priv->pitch * dev_priv->cpp); | ||
582 | OUT_RING(dspbase); | ||
556 | ADVANCE_LP_RING(); | 583 | ADVANCE_LP_RING(); |
557 | 584 | ||
558 | dev_priv->sarea_priv->pf_current_page = dev_priv->current_page; | 585 | dev_priv->sarea_priv->pf_current_page &= ~(0x3 << shift); |
559 | return 0; | 586 | dev_priv->sarea_priv->pf_current_page |= next_page << shift; |
587 | } | ||
588 | |||
589 | void i915_dispatch_flip(struct drm_device * dev, int planes, int sync) | ||
590 | { | ||
591 | drm_i915_private_t *dev_priv = dev->dev_private; | ||
592 | int i; | ||
593 | |||
594 | DRM_DEBUG("planes=0x%x pfCurrentPage=%d\n", | ||
595 | planes, dev_priv->sarea_priv->pf_current_page); | ||
596 | |||
597 | i915_emit_mi_flush(dev, MI_READ_FLUSH | MI_EXE_FLUSH); | ||
598 | |||
599 | for (i = 0; i < 2; i++) | ||
600 | if (planes & (1 << i)) | ||
601 | i915_do_dispatch_flip(dev, i, sync); | ||
602 | |||
603 | i915_emit_breadcrumb(dev); | ||
604 | |||
560 | } | 605 | } |
561 | 606 | ||
562 | static int i915_quiescent(struct drm_device * dev) | 607 | static int i915_quiescent(struct drm_device * dev) |
@@ -579,7 +624,6 @@ static int i915_batchbuffer(struct drm_device *dev, void *data, | |||
579 | struct drm_file *file_priv) | 624 | struct drm_file *file_priv) |
580 | { | 625 | { |
581 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | 626 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; |
582 | u32 *hw_status = dev_priv->hw_status_page; | ||
583 | drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *) | 627 | drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *) |
584 | dev_priv->sarea_priv; | 628 | dev_priv->sarea_priv; |
585 | drm_i915_batchbuffer_t *batch = data; | 629 | drm_i915_batchbuffer_t *batch = data; |
@@ -602,7 +646,7 @@ static int i915_batchbuffer(struct drm_device *dev, void *data, | |||
602 | 646 | ||
603 | ret = i915_dispatch_batchbuffer(dev, batch); | 647 | ret = i915_dispatch_batchbuffer(dev, batch); |
604 | 648 | ||
605 | sarea_priv->last_dispatch = (int)hw_status[5]; | 649 | sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); |
606 | return ret; | 650 | return ret; |
607 | } | 651 | } |
608 | 652 | ||
@@ -610,7 +654,6 @@ static int i915_cmdbuffer(struct drm_device *dev, void *data, | |||
610 | struct drm_file *file_priv) | 654 | struct drm_file *file_priv) |
611 | { | 655 | { |
612 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | 656 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; |
613 | u32 *hw_status = dev_priv->hw_status_page; | ||
614 | drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *) | 657 | drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *) |
615 | dev_priv->sarea_priv; | 658 | dev_priv->sarea_priv; |
616 | drm_i915_cmdbuffer_t *cmdbuf = data; | 659 | drm_i915_cmdbuffer_t *cmdbuf = data; |
@@ -635,18 +678,51 @@ static int i915_cmdbuffer(struct drm_device *dev, void *data, | |||
635 | return ret; | 678 | return ret; |
636 | } | 679 | } |
637 | 680 | ||
638 | sarea_priv->last_dispatch = (int)hw_status[5]; | 681 | sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); |
682 | return 0; | ||
683 | } | ||
684 | |||
685 | static int i915_do_cleanup_pageflip(struct drm_device * dev) | ||
686 | { | ||
687 | drm_i915_private_t *dev_priv = dev->dev_private; | ||
688 | int i, planes, num_pages = dev_priv->sarea_priv->third_handle ? 3 : 2; | ||
689 | |||
690 | DRM_DEBUG("\n"); | ||
691 | |||
692 | for (i = 0, planes = 0; i < 2; i++) | ||
693 | if (dev_priv->sarea_priv->pf_current_page & (0x3 << (2 * i))) { | ||
694 | dev_priv->sarea_priv->pf_current_page = | ||
695 | (dev_priv->sarea_priv->pf_current_page & | ||
696 | ~(0x3 << (2 * i))) | ((num_pages - 1) << (2 * i)); | ||
697 | |||
698 | planes |= 1 << i; | ||
699 | } | ||
700 | |||
701 | if (planes) | ||
702 | i915_dispatch_flip(dev, planes, 0); | ||
703 | |||
639 | return 0; | 704 | return 0; |
640 | } | 705 | } |
641 | 706 | ||
642 | static int i915_flip_bufs(struct drm_device *dev, void *data, | 707 | static int i915_flip_bufs(struct drm_device *dev, void *data, |
643 | struct drm_file *file_priv) | 708 | struct drm_file *file_priv) |
644 | { | 709 | { |
645 | DRM_DEBUG("%s\n", __FUNCTION__); | 710 | drm_i915_flip_t *param = data; |
711 | |||
712 | DRM_DEBUG("\n"); | ||
646 | 713 | ||
647 | LOCK_TEST_WITH_RETURN(dev, file_priv); | 714 | LOCK_TEST_WITH_RETURN(dev, file_priv); |
648 | 715 | ||
649 | return i915_dispatch_flip(dev); | 716 | /* This is really planes */ |
717 | if (param->pipes & ~0x3) { | ||
718 | DRM_ERROR("Invalid planes 0x%x, only <= 0x3 is valid\n", | ||
719 | param->pipes); | ||
720 | return -EINVAL; | ||
721 | } | ||
722 | |||
723 | i915_dispatch_flip(dev, param->pipes, 0); | ||
724 | |||
725 | return 0; | ||
650 | } | 726 | } |
651 | 727 | ||
652 | static int i915_getparam(struct drm_device *dev, void *data, | 728 | static int i915_getparam(struct drm_device *dev, void *data, |
@@ -807,6 +883,8 @@ void i915_driver_lastclose(struct drm_device * dev) | |||
807 | if (!dev_priv) | 883 | if (!dev_priv) |
808 | return; | 884 | return; |
809 | 885 | ||
886 | if (drm_getsarea(dev) && dev_priv->sarea_priv) | ||
887 | i915_do_cleanup_pageflip(dev); | ||
810 | if (dev_priv->agp_heap) | 888 | if (dev_priv->agp_heap) |
811 | i915_mem_takedown(&(dev_priv->agp_heap)); | 889 | i915_mem_takedown(&(dev_priv->agp_heap)); |
812 | 890 | ||