diff options
Diffstat (limited to 'drivers/char/drm/r300_cmdbuf.c')
-rw-r--r-- | drivers/char/drm/r300_cmdbuf.c | 67 |
1 files changed, 67 insertions, 0 deletions
diff --git a/drivers/char/drm/r300_cmdbuf.c b/drivers/char/drm/r300_cmdbuf.c index 20b6cb39213d..b108c7f913b2 100644 --- a/drivers/char/drm/r300_cmdbuf.c +++ b/drivers/char/drm/r300_cmdbuf.c @@ -704,6 +704,64 @@ static void r300_discard_buffer(drm_device_t * dev, drm_buf_t * buf) buf->used = 0; } +static int r300_scratch(drm_radeon_private_t *dev_priv, + drm_radeon_kcmd_buffer_t *cmdbuf, + drm_r300_cmd_header_t header) +{ + u32 *ref_age_base; + u32 i, buf_idx, h_pending; + RING_LOCALS; + + if (cmdbuf->bufsz < + (sizeof(u64) + header.scratch.n_bufs * sizeof(buf_idx))) { + return DRM_ERR(EINVAL); + } + + if (header.scratch.reg >= 5) { + return DRM_ERR(EINVAL); + } + + dev_priv->scratch_ages[header.scratch.reg]++; + + ref_age_base = *(u32 **)cmdbuf->buf; + + cmdbuf->buf += sizeof(u64); + cmdbuf->bufsz -= sizeof(u64); + + for (i=0; i < header.scratch.n_bufs; i++) { + buf_idx = *(u32 *)cmdbuf->buf; + buf_idx *= 2; /* 8 bytes per buf */ + + if (DRM_COPY_TO_USER(ref_age_base + buf_idx, &dev_priv->scratch_ages[header.scratch.reg], sizeof(u32))) { + return DRM_ERR(EINVAL); + } + + if (DRM_COPY_FROM_USER(&h_pending, ref_age_base + buf_idx + 1, sizeof(u32))) { + return DRM_ERR(EINVAL); + } + + if (h_pending == 0) { + return DRM_ERR(EINVAL); + } + + h_pending--; + + if (DRM_COPY_TO_USER(ref_age_base + buf_idx + 1, &h_pending, sizeof(u32))) { + return DRM_ERR(EINVAL); + } + + cmdbuf->buf += sizeof(buf_idx); + cmdbuf->bufsz -= sizeof(buf_idx); + } + + BEGIN_RING(2); + OUT_RING(CP_PACKET0(RADEON_SCRATCH_REG0 + header.scratch.reg * 4, 0)); + OUT_RING(dev_priv->scratch_ages[header.scratch.reg]); + ADVANCE_RING(); + + return 0; +} + /** * Parses and validates a user-supplied command buffer and emits appropriate * commands on the DMA ring buffer. @@ -841,6 +899,15 @@ int r300_do_cp_cmdbuf(drm_device_t *dev, } break; + case R300_CMD_SCRATCH: + DRM_DEBUG("R300_CMD_SCRATCH\n"); + ret = r300_scratch(dev_priv, cmdbuf, header); + if (ret) { + DRM_ERROR("r300_scratch failed\n"); + goto cleanup; + } + break; + default: DRM_ERROR("bad cmd_type %i at %p\n", header.header.cmd_type, |