/* mach64_dma.c -- DMA support for mach64 (Rage Pro) driver -*- linux-c -*- */
/**
* \file mach64_dma.c
* DMA support for mach64 (Rage Pro) driver
*
* \author Gareth Hughes <gareth@valinux.com>
* \author Frank C. Earl <fearl@airmail.net>
* \author Leif Delgass <ldelgass@retinalburn.net>
* \author José Fonseca <j_r_fonseca@yahoo.co.uk>
*/
/*
* Copyright 2000 Gareth Hughes
* Copyright 2002 Frank C. Earl
* Copyright 2002-2003 Leif Delgass
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT OWNER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "drmP.h"
#include "drm.h"
#include "mach64_drm.h"
#include "mach64_drv.h"
/*******************************************************************/
/** \name Engine, FIFO control */
/*@{*/
/**
* Waits for free entries in the FIFO.
*
* \note Most writes to Mach64 registers are automatically routed through
* command FIFO which is 16 entry deep. Prior to writing to any draw engine
* register one has to ensure that enough FIFO entries are available by calling
* this function. Failure to do so may cause the engine to lock.
*
* \param dev_priv pointer to device private data structure.
* \param entries number of free entries in the FIFO to wait for.
*
* \returns zero on success, or -EBUSY if the timeout (specificed by
* drm_mach64_private::usec_timeout) occurs.
*/
int mach64_do_wait_for_fifo(drm_mach64_private_t *dev_priv, int entries)
{
int slots = 0, i;
for (i = 0; i < dev_priv->usec_timeout; i++) {
slots = (MACH64_READ(MACH64_FIFO_STAT) & MACH64_FIFO_SLOT_MASK);
if (slots <= (0x8000 >> entries))
return 0;
DRM_UDELAY(1);
}
DRM_INFO("failed! slots=%d entries=%d\n", slots, entries);
return -EBUSY;
}
/**
* Wait for the draw engine to be idle.
*/
int mach64_do_wait_for_idle(drm_mach64_private_t *dev_priv)
{
int i, ret;
ret = mach64_do_wait_for_fifo(dev_priv, 16);
if (ret < 0)
return ret;
for (i = 0; i < dev_priv->usec_timeout; i++) {
if (!(MACH64_READ(MACH64_GUI_STAT) & MACH64_GUI_ACTIVE))
return 0;
DRM_UDELAY(1);
}
DRM_INFO("failed! GUI_STAT=0x%08x\n", MACH64_READ(MACH64_GUI_STAT));
mach64_dump_ring_info(dev_priv);
return -EBUSY;
}
/**
* Wait for free entries in the ring buffer.
*
* The Mach64 bus master can be configured to act as a virtual FIFO, using a
* circular buffer (commonly referred as "ring buffer" in other drivers) with
* pointers to engine commands. This allows the CPU to do other things while
* the graphics engine is busy, i.e., DMA mode.
*
* This function should be called before writing new entries to the ring
* buffer.
*
* \param dev_priv pointer to device private data structure.
* \param n number of free entries in the ring buffer to wait for.
*
* \returns zero on success, or -EBUSY if the timeout (specificed by
* drm_mach64_private_t::usec_timeout) occurs.
*
* \sa mach64_dump_ring_info()
*/
int mach64_wait_ring(drm_mach64_private_t *dev_priv, int n)
{
drm_mach64_descriptor_ring_t *ring = &dev_priv->ring;
int i;
for (i = 0; i < dev_priv->usec_timeout; i++) {
mach64_update_ring_snapshot(dev_priv);
if (ring->space >= n) {
if (i > 0)
DRM_DEBUG("%d usecs\n", i);
return 0;
}
DRM_UDELAY(1);
}
/* FIXME: This is being ignored... */
DRM_ERROR("failed!\n");
mach64_dump_ring_info(dev_priv);
return -EBUSY;
}
/**
* Wait until all DMA requests have been processed...
*
* \sa mach64_wait_ring()
*/
static int mach64_ring_idle(drm_mach64_private_t *dev_priv)
{
drm_mach64_descriptor_ring_t *ring = &dev_priv->ring;
u32 head;
int i;
head = ring->head;
i = 0;
while (i < dev_priv->usec_timeout) {
mach64_update_ring_snapshot(dev_priv);
if (ring->head == ring->tail &&
!(MACH64_READ(MACH64_GUI_STAT) & MACH64_GUI_ACTIVE)) {
if (i > 0)
DRM_DEBUG("%d usecs\n", i);
return 0;
}
if (ring->head == head) {
++i;
} else {
head = ring->head;
i = 0;
}
DRM_UDELAY(1);
}
DRM_INFO("failed! GUI_STAT=0x%08x\n", MACH64_READ(MACH64_GUI_STAT));
mach64_dump_ring_info(dev_priv);
return -EBUSY;
}
/**
* Reset the the ring buffer descriptors.
*
* \sa mach64_do_engine_reset()
*/
static void mach64_ring_reset(drm_mach64_private_t *dev_priv)
{
drm_mach64_descriptor_ring_t *ring = &dev_priv->ring;
mach64_do_release_used_buffers(dev_priv);
ring->head_addr = ring->start_addr;
ring->head = ring->tail = 0;
ring->space = ring->size;
MACH64_WRITE(MACH64_BM_GUI_TABLE_CMD,
ring->head_addr | MACH64_CIRCULAR_BUF_SIZE_16KB);
dev_priv->ring_running = 0;
}
/**
* Ensure the all the queued commands will be processed.
*/
int mach64_do_dma_flush(drm_mach64_private_t *dev_priv)
{
/* FIXME: It's not necessary to wait for idle when flushing
* we just need to ensure the ring will be completely processed
* in finite time without another ioctl
*/
return mach64_ring_idle(dev_priv);
}
/**
* Stop all DMA activity.
*/
int mach64_do_dma_idle(drm_mach64_private_t *dev_priv)
{
int ret;
/* wait for completion */
if ((ret = mach64_ring_idle(dev_priv)) < 0) {
DRM_ERROR("failed BM_GUI_TABLE=0x%08x tail: %u\n",
MACH64_READ(MACH64_BM_GUI_TABLE),
dev_priv->ring.tail);
return ret;
}
mach64_ring_stop(dev_priv);
/* clean up after pass */
mach64_do_release_used_buffers(dev_priv);
return 0;
}
/**
|