summaryrefslogtreecommitdiff
path: root/shared/via_dma.c
diff options
context:
space:
mode:
Diffstat (limited to 'shared/via_dma.c')
-rw-r--r--shared/via_dma.c100
1 files changed, 33 insertions, 67 deletions
diff --git a/shared/via_dma.c b/shared/via_dma.c
index 4b591fa4..fbd3f6cb 100644
--- a/shared/via_dma.c
+++ b/shared/via_dma.c
@@ -37,57 +37,6 @@ static void via_cmdbuf_start(drm_via_private_t * dev_priv);
static void via_cmdbuf_pause(drm_via_private_t * dev_priv);
static void via_cmdbuf_reset(drm_via_private_t * dev_priv);
static void via_cmdbuf_rewind(drm_via_private_t * dev_priv);
-static int via_wait_idle(drm_via_private_t * dev_priv);
-
-
-/*
- * This function needs to be extended whenever a new command set
- * is implemented. Currently it works only for the 2D engine
- * command, which on the Unichrome allows writing to
- * at least the 2D engine and the mpeg engine, but not the
- * video engine.
- *
- * If you update this function with new commands, please also
- * consider implementing these commands in
- * via_parse_pci_cmdbuffer below.
- *
- * Carefully review this function for security holes
- * after an update!!!!!!!!!
- */
-
-static int via_check_command_stream(const uint32_t * buf, unsigned int size)
-{
-
- uint32_t offset;
- unsigned int i;
-
- if (size & 7) {
- DRM_ERROR("Illegal command buffer size.\n");
- return DRM_ERR(EINVAL);
- }
- size >>= 3;
- for (i = 0; i < size; ++i) {
- offset = *buf;
- buf += 2;
- if ((offset > ((0x3FF >> 2) | HALCYON_HEADER1)) &&
- (offset < ((0xC00 >> 2) | HALCYON_HEADER1))) {
- DRM_ERROR
- ("Attempt to access Burst Command / 3D Area.\n");
- return DRM_ERR(EINVAL);
- } else if (offset > ((0xDFF >> 2) | HALCYON_HEADER1)) {
- DRM_ERROR("Attempt to access DMA or VGA registers.\n");
- return DRM_ERR(EINVAL);
- }
-
- /*
- * ...
- * A volunteer should complete this to allow non-root
- * usage of accelerated 3D OpenGL.
- */
-
- }
- return 0;
-}
static inline int
via_cmdbuf_wait(drm_via_private_t * dev_priv, unsigned int size)
@@ -98,7 +47,7 @@ via_cmdbuf_wait(drm_via_private_t * dev_priv, unsigned int size)
uint32_t count;
hw_addr_ptr = dev_priv->hw_addr_ptr;
cur_addr = dev_priv->dma_low;
- next_addr = cur_addr + size;
+ next_addr = cur_addr + size + 512*1024;
count = 1000000; /* How long is this? */
do {
hw_addr = *hw_addr_ptr - agp_base;
@@ -142,6 +91,7 @@ int via_dma_cleanup(drm_device_t * dev)
drm_core_ioremapfree(&dev_priv->ring.map, dev);
dev_priv->ring.virtual_start = NULL;
}
+
}
return 0;
@@ -225,17 +175,32 @@ static int via_dispatch_cmdbuffer(drm_device_t * dev, drm_via_cmdbuffer_t * cmd)
uint32_t *vb;
int ret;
+
+ if (cmd->size > pci_bufsiz && pci_bufsiz > 0) {
+ return DRM_ERR(ENOMEM);
+ }
+
vb = via_check_dma(dev_priv, cmd->size);
if (vb == NULL) {
return DRM_ERR(EAGAIN);
}
- if (DRM_COPY_FROM_USER(vb, cmd->buf, cmd->size)) {
+
+ if (DRM_COPY_FROM_USER(pci_buf, cmd->buf, cmd->size))
return DRM_ERR(EFAULT);
- }
- if ((ret = via_check_command_stream(vb, cmd->size)))
- return ret;
+ /*
+ * Running this function on AGP memory is dead slow. Therefore
+ * we run it on a temporary cacheable system memory buffer and
+ * copy it to AGP memory when ready.
+ */
+
+ if ((ret = via_verify_command_stream((uint32_t *)pci_buf, cmd->size, dev))) {
+ return ret;
+ }
+
+ memcpy(vb, pci_buf, cmd->size);
+
dev_priv->dma_low += cmd->size;
via_cmdbuf_pause(dev_priv);
@@ -286,12 +251,12 @@ static int via_parse_pci_cmdbuffer(drm_device_t * dev, const char *buf,
unsigned int size)
{
drm_via_private_t *dev_priv = dev->dev_private;
- const uint32_t *regbuf = (uint32_t *) buf;
+ const uint32_t *regbuf = (const uint32_t *) buf;
const uint32_t *regend = regbuf + (size >> 2);
int ret;
int check_2d_cmd = 1;
- if ((ret = via_check_command_stream(regbuf, size)))
+ if ((ret = via_verify_command_stream(regbuf, size, dev)))
return ret;
while (regbuf != regend) {
@@ -455,7 +420,7 @@ static int via_hook_segment(drm_via_private_t *dev_priv,
-static int via_wait_idle(drm_via_private_t * dev_priv)
+int via_wait_idle(drm_via_private_t * dev_priv)
{
int count = 10000000;
while (count-- && (VIA_READ(VIA_REG_STATUS) &
@@ -466,14 +431,16 @@ static int via_wait_idle(drm_via_private_t * dev_priv)
static uint32_t *via_align_cmd(drm_via_private_t * dev_priv, uint32_t cmd_type,
uint32_t addr, uint32_t *cmd_addr_hi,
- uint32_t *cmd_addr_lo)
+ uint32_t *cmd_addr_lo,
+ int skip_wait)
{
uint32_t agp_base;
uint32_t cmd_addr, addr_lo, addr_hi;
uint32_t *vb;
uint32_t qw_pad_count;
- via_cmdbuf_wait(dev_priv, 2*CMDBUF_ALIGNMENT_SIZE);
+ if (!skip_wait)
+ via_cmdbuf_wait(dev_priv, 2*CMDBUF_ALIGNMENT_SIZE);
vb = via_get_dma(dev_priv);
VIA_OUT_RING_QW( HC_HEADER2 | ((VIA_REG_TRANSET >> 2) << 12) |
@@ -520,7 +487,7 @@ static void via_cmdbuf_start(drm_via_private_t * dev_priv)
dev_priv->last_pause_ptr =
via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0,
- &pause_addr_hi, & pause_addr_lo) - 1;
+ &pause_addr_hi, & pause_addr_lo, 1) - 1;
via_flush_write_combine();
while(! *dev_priv->last_pause_ptr);
@@ -553,7 +520,7 @@ static void via_cmdbuf_jump(drm_via_private_t * dev_priv)
agp_base = dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr;
via_align_cmd(dev_priv, HC_HAGPBpID_JUMP, 0, &jump_addr_hi,
- &jump_addr_lo);
+ &jump_addr_lo, 0);
dev_priv->dma_low = 0;
if (via_cmdbuf_wait(dev_priv, CMDBUF_ALIGNMENT_SIZE) != 0) {
@@ -569,7 +536,7 @@ static void via_cmdbuf_jump(drm_via_private_t * dev_priv)
via_dummy_bitblt(dev_priv);
via_dummy_bitblt(dev_priv);
last_pause_ptr = via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0, &pause_addr_hi,
- &pause_addr_lo) -1;
+ &pause_addr_lo, 0) -1;
/*
* The regulator may still be suffering from the shock of the jump.
@@ -578,13 +545,12 @@ static void via_cmdbuf_jump(drm_via_private_t * dev_priv)
*/
via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0, &pause_addr_hi,
- &pause_addr_lo);
+ &pause_addr_lo, 0);
*last_pause_ptr = pause_addr_lo;
via_hook_segment( dev_priv, jump_addr_hi, jump_addr_lo, 0);
}
-
static void via_cmdbuf_rewind(drm_via_private_t * dev_priv)
{
via_cmdbuf_jump(dev_priv);
@@ -594,7 +560,7 @@ static void via_cmdbuf_flush(drm_via_private_t * dev_priv, uint32_t cmd_type)
{
uint32_t pause_addr_lo, pause_addr_hi;
- via_align_cmd(dev_priv, cmd_type, 0, &pause_addr_hi, &pause_addr_lo);
+ via_align_cmd(dev_priv, cmd_type, 0, &pause_addr_hi, &pause_addr_lo, 0);
via_hook_segment( dev_priv, pause_addr_hi, pause_addr_lo, 0);
}