/* radeon_state.c -- State support for Radeon -*- linux-c -*-
*
* Copyright 2000 VA Linux Systems, Inc., Fremont, California.
* 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
* PRECISION INSIGHT AND/OR ITS SUPPLIERS 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.
*
* Authors:
* Gareth Hughes <gareth@valinux.com>
* Kevin E. Martin <martin@valinux.com>
*/
#include "radeon.h"
#include "drmP.h"
#include "drm.h"
#include "drm_sarea.h"
#include "radeon_drm.h"
#include "radeon_drv.h"
/* ================================================================
* Helper functions for client state checking and fixup
*/
static __inline__ int radeon_check_and_fixup_offset( drm_radeon_private_t *dev_priv,
drm_file_t *filp_priv,
u32 *offset ) {
u32 off = *offset;
struct drm_radeon_driver_file_fields *radeon_priv;
if ( off >= dev_priv->fb_location &&
off < ( dev_priv->gart_vm_start + dev_priv->gart_size ) )
return 0;
radeon_priv = filp_priv->driver_priv;
off += radeon_priv->radeon_fb_delta;
DRM_DEBUG( "offset fixed up to 0x%x\n", off );
if ( off < dev_priv->fb_location ||
off >= ( dev_priv->gart_vm_start + dev_priv->gart_size ) )
return DRM_ERR( EINVAL );
*offset = off;
return 0;
}
static __inline__ int radeon_check_and_fixup_offset_user( drm_radeon_private_t *dev_priv,
drm_file_t *filp_priv,
u32 __user *offset ) {
u32 off;
DRM_GET_USER_UNCHECKED( off, offset );
if ( radeon_check_and_fixup_offset( dev_priv, filp_priv, &off ) )
return DRM_ERR( EINVAL );
DRM_PUT_USER_UNCHECKED( offset, off );
return 0;
}
static __inline__ int radeon_check_and_fixup_packets( drm_radeon_private_t *dev_priv,
drm_file_t *filp_priv,
int id,
u32 __user *data ) {
switch ( id ) {
case RADEON_EMIT_PP_MISC:
if ( radeon_check_and_fixup_offset_user( dev_priv, filp_priv,
&data[( RADEON_RB3D_DEPTHOFFSET
- RADEON_PP_MISC ) / 4] ) ) {
DRM_ERROR( "Invalid depth buffer offset\n" );
return DRM_ERR( EINVAL );
}
break;
case RADEON_EMIT_PP_CNTL:
if ( radeon_check_and_fixup_offset_user( dev_priv, filp_priv,
&data[( RADEON_RB3D_COLOROFFSET
- RADEON_PP_CNTL ) / 4] ) ) {
DRM_ERROR( "Invalid colour buffer offset\n" );
return DRM_ERR( EINVAL );
}
break;
case R200_EMIT_PP_TXOFFSET_0:
case R200_EMIT_PP_TXOFFSET_1:
case R200_EMIT_PP_TXOFFSET_2:
case R200_EMIT_PP_TXOFFSET_3:
case R200_EMIT_PP_TXOFFSET_4:
case R200_EMIT_PP_TXOFFSET_5:
if ( radeon_check_and_fixup_offset_user( dev_priv, filp_priv,
&data[0] ) ) {
DRM_ERROR( "Invalid R200 texture offset\n" );
return DRM_ERR( EINVAL );
}
break;
case RADEON_EMIT_PP_TXFILTER_0:
case RADEON_EMIT_PP_TXFILTER_1:
case RADEON_EMIT_PP_TXFILTER_2:
if ( radeon_check_and_fixup_offset_user( dev_priv, filp_priv,
&data[( RADEON_PP_TXOFFSET_0
- RADEON_PP_TXFILTER_0 ) / 4] ) ) {
DRM_ERROR( "Invalid R100 texture offset\n" );
return DRM_ERR( EINVAL );
}
break;
case R200_EMIT_PP_CUBIC_OFFSETS_0:
case R200_EMIT_PP_CUBIC_OFFSETS_1:
case R200_EMIT_PP_CUBIC_OFFSETS_2:
case R200_EMIT_PP_CUBIC_OFFSETS_3:
case R200_EMIT_PP_CUBIC_OFFSETS_4:
case R200_EMIT_PP_CUBIC_OFFSETS_5: {
int i;
for ( i = 0; i < 5; i++ ) {
if ( radeon_check_and_fixup_offset_user( dev_priv,
filp_priv,
&data[i] ) ) {
DRM_ERROR( "Invalid R200 cubic texture offset\n" );
return DRM_ERR( EINVAL );
}
}
break;
}
case RADEON_EMIT_RB3D_COLORPITCH:
case RADEON_EMIT_RE_LINE_PATTERN:
case RADEON_EMIT_SE_LINE_WIDTH:
case RADEON_EMIT_PP_LUM_MATRIX:
case RADEON_EMIT_PP_ROT_MATRIX_0:
case RADEON_EMIT_RB3D_STENCILREFMASK:
case RADEON_EMIT_SE_VPORT_XSCALE:
case RADEON_EMIT_SE_CNTL:
case RADEON_EMIT_SE_CNTL_STATUS:
case RADEON_EMIT_RE_MISC:
case RADEON_EMIT_PP_BORDER_COLOR_0:
case RADEON_EMIT_PP_BORDER_COLOR_1:
case RADEON_EMIT_PP_BORDER_COLOR_2:
case RADEON_EMIT_SE_ZBIAS_FACTOR:
case RADEON_EMIT_SE_TCL_OUTPUT_VTX_FMT:
case RADEON_EMIT_SE_TCL_MATERIAL_EMMISSIVE_RED:
case R200_EMIT_PP_TXCBLEND_0:
case R200_EMIT_PP_TXCBLEND_1:
case R200_EMIT_PP_TXCBLEND_2:
case R200_EMIT_PP_TXCBLEND_3:
case R200_EMIT_PP_TXCBLEND_4:
case R200_EMIT_PP_TXCBLEND_5:
case R200_EMIT_PP_TXCBLEND_6:
case R200_EMIT_PP_TXCBLEND_7:
case R200_EMIT_TCL_LIGHT_MODEL_CTL_0:
case R200_EMIT_TFACTOR_0:
case R200_EMIT_VTX_FMT_0:
case R200_EMIT_VAP_CTL:
case R200_EMIT_MATRIX_SELECT_0:
case R200_EMIT_TEX_PROC_CTL_2:
case R200_EMIT_TCL_UCP_VERT_BLEND_CTL:
case R200_EMIT_PP_TXFILTER_0:
case R200_EMIT_PP_TXFILTER_1:
case R200_EMIT_PP_TXFILTER_2:
case R200_EMIT_PP_TXFILTER_3:
case R200_EMIT_PP_TXFILTER_4:
case R200_EMIT_PP_TXFILTER_5:
case R200_EMIT_VTE_CNTL:
case R200_EMIT_OUTPUT_VTX_COMP_SEL:
case R200_EMIT_PP_TAM_DEBUG3:
case R200_EMIT_PP_CNTL_X:
case R200_EMIT_RB3D_DEPTHXY_OFFSET:
case R200_EMIT_RE_AUX_SCISSOR_CNTL:
case R200_EMIT_RE_SCISSOR_TL_0:
case R200_EMIT_RE_SCISSOR_TL_1:
case R200_EMIT_RE_SCISSOR_TL_2:
case R200_EMIT_SE_VAP_CNTL_STATUS:
case R200_EMIT_SE_VTX_STATE_CNTL:
case R200_EMIT_RE_POINTSIZE:
case R200_EMIT_TCL_INPUT_VTX_VECTOR_ADDR_0:
case R200_EMIT_PP_CUBIC_FACES_0:
case R200_EMIT_PP_CUBIC_FACES_1:
case R200_EMIT_PP_CUBIC_FACES_2:
case R200_EMIT_PP_CUBIC_FACES_3:
case R200_EMIT_PP_CUBIC_FACES_4:
case R200_EMIT_PP_CUBIC_FACES_5:
case RADEON_EMIT_PP_TEX_SIZE_0:
case RADEON_EMIT_PP_TEX_SIZE_1:
case RADEON_EMIT_PP_TEX_SIZE_2:
case R200_EMIT_RB3D_BLENDCOLOR:
case R200_EMIT_TCL_POINT_SPRITE_CNTL:
/* These packets don't contain memory offsets */
break;
default:
DRM_ERROR( "Unknown state packet ID %d\n", id );
return DRM_ERR( EINVAL );
}
return 0;
}
static __inline__ int radeon_check_and_fixup_packet3( drm_radeon_private_t *dev_priv,
drm_file_t *filp_priv,
drm_radeon_cmd_buffer_t *cmdbuf,
unsigned int *cmdsz ) {
u32 tmp[4];
u32 __user *cmd = (u32 __user *)cmdbuf->buf;
if ( DRM_COPY_FROM_USER_UNCHECKED( tmp, cmd, sizeof( tmp ) ) ) {
DRM_ERROR( "Failed to copy data from user space\n" );
return DRM_ERR( EFAULT );
}
*cmdsz = 2 + ( ( tmp[0] & RADEON_CP_PACKET_COUNT_MASK ) >> 16 );
if ( ( tmp[0] & 0xc0000000 ) != RADEON_CP_PACKET3 ) {
DRM_ERROR( "Not a type 3 packet\n" );
return DRM_ERR( EINVAL );
}
if ( 4 * *cmdsz > cmdbuf->bufsz ) {
DRM_ERROR( "Packet size larger than size of data provided\n" );
return DRM_ERR( EINVAL );
}
/* Check client state and fix it up if necessary */
if ( tmp[0] & 0x8000 ) { /* MSB of opcode: next DWORD GUI_CNTL */
u32 offset;
if ( tmp[1] & ( RADEON_GMC_SRC_PITCH_OFFSET_CNTL
| RADEON_GMC_DST_PITCH_OFFSET_CNTL ) ) {
offset = tmp[2] << 10;
if ( radeon_check_and_fixup_offset( dev_priv, filp_priv, &offset ) ) {
DRM_ERROR( "Invalid first packet offset\n" );
return DRM_ERR( EINVAL );
}
tmp[2] = ( tmp[2] & 0xffc00000 ) | offset >> 10;
}
if ( ( tmp[1] & RADEON_GMC_SRC_PITCH_OFFSET_CNTL ) &&
( tmp[1] & RADEON_GMC_DST_PITCH_OFFSET_CNTL ) ) {
offset = tmp[3] << 10;
if ( radeon_check_and_fixup_offset( dev_priv, filp_priv, &offset ) ) {
DRM_ERROR( "Invalid second packet offset\n" );
return DRM_ERR( EINVAL );
}
tmp[3] = ( tmp[3] & 0xffc00000 ) | offset >> 10;
}
if ( DRM_COPY_TO_USER_UNCHECKED( cmd, tmp, sizeof( tmp ) ) ) {
DRM_ERROR( "Failed to copy data to user space\n" );
return DRM_ERR( EFAULT );
}
}
return 0;
}
/* ================================================================
* CP hardware state programming functions
*/
static __inline__ void radeon_emit_clip_rect( drm_radeon_private_t *dev_priv,
drm_clip_rect_t *box )
{
RING_LOCALS;
DRM_DEBUG( " box: x1=%d y1=%d x2=%d y2=%d\n",
box->x1, box->y1, box->x2, box->y2 );
BEGIN_RING( 4 );
OUT_RING( CP_PACKET0( RADEON_RE_TOP_LEFT, 0 ) );
OUT_RING( (box->y1 << 16) | box->x1 );
OUT_RING( CP_PACKET0( RADEON_RE_WIDTH_HEIGHT, 0 ) );
OUT_RING( ((box->y2 - 1) << 16) | (box->x2 - 1) );
ADVANCE_RING();
}
/* Emit 1.1 state
*/
static int radeon_emit_state( drm_radeon_private_t *dev_priv,
drm_file_t *filp_priv,
drm_radeon_context_regs_t *ctx,
drm_radeon_texture_regs_t *tex,
unsigned int dirty )
{
RING_LOCALS;
DRM_DEBUG( "dirty=0x%08x\n", dirty );
if ( dirty & RADEON_UPLOAD_CONTEXT ) {
if ( radeon_check_and_fixup_offset( dev_priv, filp_priv,
&ctx->rb3d_depthoffset ) ) {
DRM_ERROR( "Invalid depth buffer offset\n" );
return DRM_ERR( EINVAL );
}
if ( radeon_check_and_fixup_offset( dev_priv, filp_priv,
&ctx->rb3d_coloroffset ) ) {
DRM_ERROR( "Invalid depth buffer offset\n" );
return DRM_ERR( EINVAL );
}
BEGIN_RING( 14 );
OUT_RING( CP_PACKET0( RADEON_PP_MISC, 6 ) );
OUT_RING( ctx->pp_misc );
OUT_RING( ctx->pp_fog_color );
OUT_RING( ctx->re_solid_color );
OUT_RING( ctx->rb3d_blendcntl );
OUT_RING( ctx->rb3d_depthoffset );
OUT_RING( ctx->rb3d_depthpitch );
OUT_RING( ctx->rb3d_zstencilcntl );
|