/*
*/
/*
* r300_cmdbuf.c -- Command buffer emission for R300 -*- linux-c -*-
*
* Copyright (C) The Weather Channel, Inc. 2002.
* Copyright (C) 2004 Nicolai Haehnle.
* All Rights Reserved.
*
* The Weather Channel (TM) funded Tungsten Graphics to develop the
* initial release of the Radeon 8500 driver under the XFree86 license.
* This notice must be preserved.
*
* 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,
* 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
* 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.
*
* Authors:
* Nicolai Haehnle <prefect_@gmx.net>
*/
#include "drm.h"
#include "radeon_drm.h"
#include "drmP.h"
#include "radeon_drv.h"
#include "r300_reg.h"
#ifdef u
#undef u
#endif
/*
* Values for R300_RE_CLIPRECT_CNTL depending on the number of
* cliprects
*/
0xAAAA,
0xEEEE,
0xFEFE,
0xFFFE
};
/*
* Emit up to R300_SIMULTANEOUS_CLIPRECTS cliprects from the given command
* buffer, starting with index n.
*/
drm_radeon_kcmd_buffer_t *cmdbuf, int n)
{
int nr;
int i;
if (nr > R300_SIMULTANEOUS_CLIPRECTS)
if (nr) {
for (i = 0; i < nr; ++i) {
DRM_ERROR("copy cliprect faulted\n");
return (EFAULT);
}
}
/*
* the client might be able to trample over memory.
* The impact should be very limited, but I'd rather be safe
* than sorry.
*/
OUT_RING(0);
ADVANCE_RING();
} else {
/*
* Why we allow zero cliprect rendering:
* There are some commands in a command buffer that must be
* submitted even when there are no cliprects, e.g. DMA buffer
* discard or state setting (though state setting could be
* avoided by simulating a loss of context).
*
* Now since the cmdbuf interface is so chaotic right now (and
* is bound to remain that way for a bit until things settle
* down), it is basically impossible to filter out the commands
* that are necessary and those that aren't.
*
* So I choose the safe way and don't do any filtering at all;
* instead, I simply set up the engine so that all rendering
* can't produce any fragments.
*/
BEGIN_RING(2);
ADVANCE_RING();
}
return (0);
}
void
r300_init_reg_flags(void)
{
int i;
r300_reg_flags[i] |= (mark);
/* these match cmducs() command in r300_driver/r300/r300_cmdbuf.c */
/* check offset */
/* check offset */
/* Texture offset is dangerous and needs more checking */
/* Sporadic registers used as primitives are emitted */
}
{
int i;
if (reg & ~0xffff)
return (-1);
if (r300_reg_flags[i] != MARK_SAFE)
return (1);
return (0);
}
static inline int
{
int reg;
int sz;
int i;
DRM_ERROR("Cannot emit more than 64 values at a time "
return (EINVAL);
}
for (i = 0; i < sz; i++) {
case MARK_SAFE:
break;
case MARK_CHECK_OFFSET:
DRM_ERROR("Offset failed range check "
return (EINVAL);
}
break;
default:
DRM_ERROR("Register %04x failed check as flag=%02x\n",
return (EINVAL);
}
}
ADVANCE_RING();
return (0);
}
/*
* Emits a packet0 setting arbitrary registers.
* Called by r300_do_cp_cmdbuf.
*
* Note that checks are performed on contents and addresses of the registers
*/
{
int reg;
int sz;
if (!sz)
return (0);
return (EINVAL);
DRM_ERROR("No such registers in hardware reg=%04x sz=%d\n",
return (EINVAL);
}
/* go and check everything */
}
/*
* the rest of the data is safe to emit, whatever the values
* the user passed
*/
ADVANCE_RING();
return (0);
}
/*
* Uploads user-supplied vertex program instructions or parameters onto
* the graphics card.
* Called by r300_do_cp_cmdbuf.
*/
{
int sz;
int addr;
if (!sz)
return (0);
return (EINVAL);
/* Wait for VAP to come to senses.. */
/*
* there is no need to emit it multiple times, (only once before
* VAP is programmed, but this optimization is for later
*/
ADVANCE_RING();
return (0);
}
/*
* Emit a clear packet from userspace.
* Called by r300_emit_packet3.
*/
{
return (EINVAL);
BEGIN_RING(10);
(1 << R300_PRIM_NUM_VERTICES_SHIFT));
ADVANCE_RING();
return (0);
}
{
int count, i, k;
DRM_ERROR("Too large payload in 3D_LOAD_VBPNTR (count=%d)\n",
count);
return (EINVAL);
}
/* carefully check packet contents */
k = 0;
i = 1;
i++; /* skip attribute field */
DRM_ERROR("Offset failed range check (k=%d i=%d) "
"while processing 3D_LOAD_VBPNTR packet.\n",
k, i);
return (EINVAL);
}
k++;
i++;
if (k == narrays)
break;
/* have one more to process, they come in pairs */
DRM_ERROR("Offset failed range check (k=%d i=%d) "
"while processing 3D_LOAD_VBPNTR packet.\n",
k, i);
return (EINVAL);
}
k++;
i++;
}
/* do the counts match what we expect ? */
DRM_ERROR("Malformed 3D_LOAD_VBPNTR packet "
"(k=%d i=%d narrays=%d count+1=%d).\n",
return (EINVAL);
}
/* all clear, output packet */
ADVANCE_RING();
return (0);
}
{
if (cmd[0] & 0x8000) {
if (ret) {
DRM_ERROR("Invalid bitblt first offset "
"is %08X\n", offset);
return (EINVAL);
}
}
if (ret) {
DRM_ERROR("Invalid bitblt second offset "
"is %08X\n", offset);
return (EINVAL);
}
}
}
ADVANCE_RING();
return (0);
}
{
return (EINVAL);
}
if (ret) {
return (EINVAL);
}
ADVANCE_RING();
return (0);
}
{
int count;
return (EINVAL);
/*
* Fixme !! This simply emits a packet without much checking.
* We need to be smarter.
*/
/* obtain first word - actual packet3 header */
/* Is it packet 3 ? */
return (EINVAL);
}
/* Check again now that we know how much data to expect */
DRM_ERROR("Expected packet3 of length %d but have only "
return (EINVAL);
}
/* Is it a packet type we know about ? */
switch (header & 0xff00) {
case RADEON_3D_LOAD_VBPNTR: /* load vertex array pointers */
case RADEON_CNTL_BITBLT_MULTI:
case RADEON_CP_INDX_BUFFER:
// DRAW_INDX_2 without INDX_BUFFER seems to lock
// up the GPU
case RADEON_CP_3D_DRAW_IMMD_2:
/* triggers drawing using in-packet vertex data */
case RADEON_CP_3D_DRAW_VBUF_2:
/* triggers drawing of vertex buffers setup elsewhere */
case RADEON_CP_3D_DRAW_INDX_2:
/* triggers drawing using indices to vertex buffer */
case RADEON_WAIT_FOR_IDLE:
case RADEON_CP_NOP:
/* these packets are safe */
break;
default:
return (EINVAL);
}
ADVANCE_RING();
return (0);
}
/*
* Emit a rendering packet3 from userspace.
* Called by r300_do_cp_cmdbuf.
*/
{
int n;
int ret;
/*
* This is a do-while-loop so that we run the interior at least once,
* even if cmdbuf->nbox is 0. Compare r300_emit_cliprects for rationale.
*/
n = 0;
do {
if (ret)
return (ret);
}
case R300_CMD_PACKET3_CLEAR:
DRM_DEBUG("R300_CMD_PACKET3_CLEAR\n");
if (ret) {
DRM_ERROR("r300_emit_clear failed\n");
return (ret);
}
break;
case R300_CMD_PACKET3_RAW:
DRM_DEBUG("R300_CMD_PACKET3_RAW\n");
if (ret) {
DRM_ERROR("r300_emit_raw_packet3 failed\n");
return (ret);
}
break;
default:
DRM_ERROR("bad packet3 type %i at %p\n",
return (EINVAL);
}
return (0);
}
/*
* Some of the R300 chips seem to be extremely touchy about the two registers
* that are configured in r300_pacify.
* Among the worst offenders seems to be the R300 ND (0x4E44): When userspace
* sends a command buffer that contains only state setting commands and a
* lockup, unless the sequence is bracketed by calls to r300_pacify.
* So we should take great care to *always* call r300_pacify before
* *anything* 3D related, and again afterwards. This is what the
* call bracket in r300_do_cp_cmdbuf is for.
*/
/*
* Emit the sequence to pacify R300.
*/
{
BEGIN_RING(6);
OUT_RING(0xa);
OUT_RING(0x3);
OUT_RING(0x0);
ADVANCE_RING();
}
/*
* Called by r300_do_cp_cmdbuf to update the internal buffer age and state.
* The actual age emit is done by r300_do_cp_cmdbuf, which is why you must
* be careful about how this function is called.
*/
{
}
{
return (EINVAL);
}
return (EINVAL);
}
sizeof (u32))) {
return (EINVAL);
}
if (DRM_COPY_FROM_USER(&h_pending,
return (EINVAL);
}
if (h_pending == 0) {
return (EINVAL);
}
h_pending--;
return (EINVAL);
}
}
BEGIN_RING(2);
ADVANCE_RING();
return (0);
}
/*
* Parses and validates a user-supplied command buffer and emits appropriate
* commands on the DMA ring buffer.
* Called by the ioctl handler function radeon_cp_cmdbuf.
*/
/*ARGSUSED*/
int
{
int emit_dispatch_age = 0;
int ret = 0;
DRM_DEBUG("\n");
/*
* See the comment above r300_emit_begin3d for why this call
* must be here, and what the cleanup gotos are for.
*/
if (ret)
goto cleanup;
}
int idx;
case R300_CMD_PACKET0:
DRM_DEBUG("R300_CMD_PACKET0\n");
if (ret) {
DRM_ERROR("r300_emit_packet0 failed\n");
goto cleanup;
}
break;
case R300_CMD_VPU:
DRM_DEBUG("R300_CMD_VPU\n");
if (ret) {
DRM_ERROR("r300_emit_vpu failed\n");
goto cleanup;
}
break;
case R300_CMD_PACKET3:
DRM_DEBUG("R300_CMD_PACKET3\n");
if (ret) {
DRM_ERROR("r300_emit_packet3 failed\n");
goto cleanup;
}
break;
case R300_CMD_END3D:
DRM_DEBUG("R300_CMD_END3D\n");
/*
* TODO:
* Ideally userspace driver should not need to issue
* this call, i.e. the drm driver should issue it
* automatically and prevent lockups. In practice, we
* do not understand why this call is needed and what
* it does (except for some vague guesses that it has
* to do with cache coherence) and so the user space
* driver does it.
*
* Once we are sure which uses prevent lockups the code
* could be moved into the kernel and the userspace
* driver will not need to use this command.
*
* Note that issuing this command does not hurt anything
* except, possibly, performance
*/
break;
case R300_CMD_CP_DELAY:
/* simple enough, we can do it here */
DRM_DEBUG("R300_CMD_CP_DELAY\n");
{
int i;
ADVANCE_RING();
}
break;
case R300_CMD_DMA_DISCARD:
DRM_DEBUG("RADEON_CMD_DMA_DISCARD\n");
DRM_ERROR("buffer index %d (of %d max)\n",
goto cleanup;
}
DRM_ERROR("bad buffer %p %p %d\n",
goto cleanup;
}
emit_dispatch_age = 1;
break;
case R300_CMD_WAIT:
/* simple enough, we can do it here */
DRM_DEBUG("R300_CMD_WAIT\n");
break; /* nothing to do */
{
BEGIN_RING(2);
ADVANCE_RING();
}
break;
case R300_CMD_SCRATCH:
DRM_DEBUG("R300_CMD_SCRATCH\n");
if (ret) {
DRM_ERROR("r300_scratch failed\n");
goto cleanup;
}
break;
default:
DRM_ERROR("bad cmd_type %i at %p\n",
goto cleanup;
}
}
DRM_DEBUG("END\n");
/*
* We emit the vertex buffer age here, outside the pacifier "brackets"
* for two reasons:
* (1) This may coalesce multiple age emissions into a single one and
* (2) more importantly, some chips lock up hard when scratch registers
* are written inside the pacifier bracket.
*/
if (emit_dispatch_age) {
/* Emit the vertex buffer age */
BEGIN_RING(2);
ADVANCE_RING();
}
COMMIT_RING();
return (ret);
}