Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/via: attempt again to stabilise the AGP DMA command submission.

It's worth remembering that all new bright ideas on how to make this command reader work properly and according to docs will probably fail :( Bring in some old code.

Also allow a larger SG-DMA download stride, and remove unnecessary waits for
command regulators pauses.

Signed-off-by: Dave Airlie <airlied@redhat.com>

authored by

Thomas Hellstrom and committed by
Dave Airlie
f0fb6d77 9df5808c

+54 -7
+53 -6
drivers/char/drm/via_dma.c
··· 126 126 hw_addr, cur_addr, next_addr); 127 127 return -1; 128 128 } 129 + if ((cur_addr < hw_addr) && (next_addr >= hw_addr)) 130 + msleep(1); 129 131 } while ((cur_addr < hw_addr) && (next_addr >= hw_addr)); 130 132 return 0; 131 133 } ··· 418 416 int paused, count; 419 417 volatile uint32_t *paused_at = dev_priv->last_pause_ptr; 420 418 uint32_t reader,ptr; 419 + uint32_t diff; 421 420 422 421 paused = 0; 423 422 via_flush_write_combine(); 424 423 (void) *(volatile uint32_t *)(via_get_dma(dev_priv) -1); 424 + 425 425 *paused_at = pause_addr_lo; 426 426 via_flush_write_combine(); 427 427 (void) *paused_at; 428 + 428 429 reader = *(dev_priv->hw_addr_ptr); 429 430 ptr = ((volatile char *)paused_at - dev_priv->dma_ptr) + 430 431 dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr + 4; 432 + 431 433 dev_priv->last_pause_ptr = via_get_dma(dev_priv) - 1; 432 434 433 - if ((ptr - reader) <= dev_priv->dma_diff ) { 434 - count = 10000000; 435 - while (!(paused = (VIA_READ(0x41c) & 0x80000000)) && count--); 435 + /* 436 + * If there is a possibility that the command reader will 437 + * miss the new pause address and pause on the old one, 438 + * In that case we need to program the new start address 439 + * using PCI. 440 + */ 441 + 442 + diff = (uint32_t) (ptr - reader) - dev_priv->dma_diff; 443 + count = 10000000; 444 + while(diff == 0 && count--) { 445 + paused = (VIA_READ(0x41c) & 0x80000000); 446 + if (paused) 447 + break; 448 + reader = *(dev_priv->hw_addr_ptr); 449 + diff = (uint32_t) (ptr - reader) - dev_priv->dma_diff; 436 450 } 451 + 452 + paused = VIA_READ(0x41c) & 0x80000000; 437 453 438 454 if (paused && !no_pci_fire) { 439 455 reader = *(dev_priv->hw_addr_ptr); 440 - if ((ptr - reader) == dev_priv->dma_diff) { 441 - 456 + diff = (uint32_t) (ptr - reader) - dev_priv->dma_diff; 457 + diff &= (dev_priv->dma_high - 1); 458 + if (diff != 0 && diff < (dev_priv->dma_high >> 1)) { 459 + DRM_ERROR("Paused at incorrect address. " 460 + "0x%08x, 0x%08x 0x%08x\n", 461 + ptr, reader, dev_priv->dma_diff); 462 + } else if (diff == 0) { 442 463 /* 443 464 * There is a concern that these writes may stall the PCI bus 444 465 * if the GPU is not idle. However, idling the GPU first ··· 602 577 uint32_t pause_addr_lo, pause_addr_hi; 603 578 uint32_t jump_addr_lo, jump_addr_hi; 604 579 volatile uint32_t *last_pause_ptr; 580 + uint32_t dma_low_save1, dma_low_save2; 605 581 606 582 agp_base = dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr; 607 583 via_align_cmd(dev_priv, HC_HAGPBpID_JUMP, 0, &jump_addr_hi, ··· 629 603 &pause_addr_lo, 0); 630 604 631 605 *last_pause_ptr = pause_addr_lo; 606 + dma_low_save1 = dev_priv->dma_low; 632 607 633 - via_hook_segment( dev_priv, jump_addr_hi, jump_addr_lo, 0); 608 + /* 609 + * Now, set a trap that will pause the regulator if it tries to rerun the old 610 + * command buffer. (Which may happen if via_hook_segment detecs a command regulator pause 611 + * and reissues the jump command over PCI, while the regulator has already taken the jump 612 + * and actually paused at the current buffer end). 613 + * There appears to be no other way to detect this condition, since the hw_addr_pointer 614 + * does not seem to get updated immediately when a jump occurs. 615 + */ 616 + 617 + last_pause_ptr = 618 + via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0, &pause_addr_hi, 619 + &pause_addr_lo, 0) - 1; 620 + via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0, &pause_addr_hi, 621 + &pause_addr_lo, 0); 622 + *last_pause_ptr = pause_addr_lo; 623 + 624 + dma_low_save2 = dev_priv->dma_low; 625 + dev_priv->dma_low = dma_low_save1; 626 + via_hook_segment(dev_priv, jump_addr_hi, jump_addr_lo, 0); 627 + dev_priv->dma_low = dma_low_save2; 628 + via_hook_segment(dev_priv, pause_addr_hi, pause_addr_lo, 0); 634 629 } 635 630 636 631
+1 -1
drivers/char/drm/via_dmablit.c
··· 603 603 * (Not a big limitation anyway.) 604 604 */ 605 605 606 - if ((xfer->mem_stride - xfer->line_length) >= PAGE_SIZE) { 606 + if ((xfer->mem_stride - xfer->line_length) > 2*PAGE_SIZE) { 607 607 DRM_ERROR("Too large system memory stride. Stride: %d, " 608 608 "Length: %d\n", xfer->mem_stride, xfer->line_length); 609 609 return -EINVAL;