#include "mtypes.h"
#include "macros.h"
#include "dd.h"
#include "swrast/swrast.h"
#include "mm.h"
#include "mgacontext.h"
#include "mgadd.h"
#include "mgastate.h"
#include "mgatex.h"
#include "mgavb.h"
#include "mgaioctl.h"
#include "mgatris.h"
#include "mga_common.h"
#include "vblank.h"
static void mga_iload_dma_ioctl(mgaContextPtr mmesa,
unsigned long dest,
int length)
{
drmBufPtr buf = mmesa->iload_buffer;
drmMGAIload iload;
int ret, i;
if (MGA_DEBUG&DEBUG_VERBOSE_IOCTL)
fprintf(stderr, "DRM_IOCTL_MGA_ILOAD idx %d dst %x length %d\n",
buf->idx, (int) dest, length);
if ( (length & MGA_ILOAD_MASK) != 0 ) {
UNLOCK_HARDWARE( mmesa );
fprintf( stderr, "%s: Invalid ILOAD datasize (%d), must be "
"multiple of %u.\n", __FUNCTION__, length, MGA_ILOAD_ALIGN );
exit( 1 );
}
iload.idx = buf->idx;
iload.dstorg = dest;
iload.length = length;
i = 0;
do {
ret = drmCommandWrite( mmesa->driFd, DRM_MGA_ILOAD,
&iload, sizeof(drmMGAIload) );
} while ( ret == -EBUSY && i++ < DRM_MGA_IDLE_RETRY );
if ( ret < 0 ) {
printf("send iload retcode = %d\n", ret);
exit(1);
}
mmesa->iload_buffer = 0;
if (MGA_DEBUG&DEBUG_VERBOSE_IOCTL)
fprintf(stderr, "finished iload dma put\n");
}
drmBufPtr mga_get_buffer_ioctl( mgaContextPtr mmesa )
{
int idx = 0;
int size = 0;
drmDMAReq dma;
int retcode;
drmBufPtr buf;
if (MGA_DEBUG&DEBUG_VERBOSE_IOCTL)
fprintf(stderr, "Getting dma buffer\n");
dma.context = mmesa->hHWContext;
dma.send_count = 0;
dma.send_list = NULL;
dma.send_sizes = NULL;
dma.flags = 0;
dma.request_count = 1;
dma.request_size = MGA_BUFFER_SIZE;
dma.request_list = &idx;
dma.request_sizes = &size;
dma.granted_count = 0;
if (MGA_DEBUG&DEBUG_VERBOSE_IOCTL)
fprintf(stderr, "drmDMA (get) ctx %d count %d size 0x%x\n",
dma.context, dma.request_count,
dma.request_size);
while (1) {
retcode = drmDMA(mmesa->driFd, &dma);
if (MGA_DEBUG&DEBUG_VERBOSE_IOCTL)
fprintf(stderr, "retcode %d sz %d idx %d count %d\n",
retcode,
dma.request_sizes[0],
dma.request_list[0],
dma.granted_count);
if (retcode == 0 &&
dma.request_sizes[0] &&
dma.granted_count)
break;
if (MGA_DEBUG&DEBUG_VERBOSE_IOCTL)
fprintf(stderr, "\n\nflush");
UPDATE_LOCK( mmesa, DRM_LOCK_FLUSH | DRM_LOCK_QUIESCENT );
}
buf = &(mmesa->mgaScreen->bufs->list[idx]);
buf->used = 0;
if (MGA_DEBUG&DEBUG_VERBOSE_IOCTL)
fprintf(stderr,
"drmDMA (get) returns size[0] 0x%x idx[0] %d\n"
"dma_buffer now: buf idx: %d size: %d used: %d addr %p\n",
dma.request_sizes[0], dma.request_list[0],
buf->idx, buf->total,
buf->used, buf->address);
if (MGA_DEBUG&DEBUG_VERBOSE_IOCTL)
fprintf(stderr, "finished getbuffer\n");
return buf;
}
static void
mgaDDClear( GLcontext *ctx, GLbitfield mask, GLboolean all,
GLint cx, GLint cy, GLint cw, GLint ch )
{
mgaContextPtr mmesa = MGA_CONTEXT(ctx);
__DRIdrawablePrivate *dPriv = mmesa->driDrawable;
GLuint flags = 0;
GLuint clear_color = mmesa->ClearColor;
GLuint clear_depth = 0;
GLuint color_mask = 0;
GLuint depth_mask = 0;
int ret;
int i;
static int nrclears;
drmMGAClearRec clear;
FLUSH_BATCH( mmesa );
if ( mask & DD_FRONT_LEFT_BIT ) {
flags |= MGA_FRONT;
color_mask = mmesa->setup.plnwt;
mask &= ~DD_FRONT_LEFT_BIT;
}
if ( mask & DD_BACK_LEFT_BIT ) {
flags |= MGA_BACK;
color_mask = mmesa->setup.plnwt;
mask &= ~DD_BACK_LEFT_BIT;
}
if ( (mask & DD_DEPTH_BIT) && ctx->Depth.Mask ) {
flags |= MGA_DEPTH;
clear_depth = (mmesa->ClearDepth & mmesa->depth_clear_mask);
depth_mask |= mmesa->depth_clear_mask;
mask &= ~DD_DEPTH_BIT;
}
if ( (mask & DD_STENCIL_BIT) && mmesa->hw_stencil ) {
flags |= MGA_DEPTH;
clear_depth |= (ctx->Stencil.Clear & mmesa->stencil_clear_mask);
depth_mask |= mmesa->stencil_clear_mask;
mask &= ~DD_STENCIL_BIT;
}
if ( flags ) {
LOCK_HARDWARE( mmesa );
if ( mmesa->dirty_cliprects )
mgaUpdateRects( mmesa, (MGA_FRONT | MGA_BACK) );
cy = dPriv->h-cy-ch;
cx += mmesa->drawX;
cy += mmesa->drawY;
if ( MGA_DEBUG & DEBUG_VERBOSE_IOCTL )
fprintf( stderr, "Clear, bufs %x nbox %d\n",
(int)flags, (int)mmesa->numClipRects );
for (i = 0 ; i < mmesa->numClipRects ; )
{
int nr = MIN2(i + MGA_NR_SAREA_CLIPRECTS, mmesa->numClipRects);
XF86DRIClipRectPtr box = mmesa->pClipRects;
XF86DRIClipRectPtr b = mmesa->sarea->boxes;
int n = 0;
if (!all) {
for ( ; i < nr ; i++) {
GLint x = box[i].x1;
GLint y = box[i].y1;
GLint w = box[i].x2 - x;
GLint h = box[i].y2 - y;
if (x < cx) w -= cx - x, x = cx;
if (y < cy) h -= cy - y, y = cy;
if (x + w > cx + cw) w = cx + cw - x;
if (y + h > cy + ch) h = cy + ch - y;
if (w <= 0) continue;
if (h <= 0) continue;
b->x1 = x;
b->y1 = y;
b->x2 = x + w;
b->y2 = y + h;
b++;
n++;
}
} else {
for ( ; i < nr ; i++) {
*b++ = *(XF86DRIClipRectPtr)&box[i];
n++;
}
}
if ( MGA_DEBUG & DEBUG_VERBOSE_IOCTL )
fprintf( stderr,
"DRM_IOCTL_MGA_CLEAR flag 0x%x color %x depth %x nbox %d\n",
flags, clear_color, clear_depth, mmesa->sarea->nbox );
mmesa->sarea->nbox = n;
clear.flags = flags;
clear.clear_color = clear_color;
clear.clear_depth = clear_depth;
clear.color_mask = color_mask;
clear.depth_mask = depth_mask;
ret = drmCommandWrite( mmesa->driFd, DRM_MGA_CLEAR,
&clear, sizeof(drmMGAClearRec));
if ( ret ) {
fprintf( stderr, "send clear retcode = %d\n", ret );
exit( 1 );
}
if ( MGA_DEBUG & DEBUG_VERBOSE_IOCTL )
fprintf( stderr, "finished clear %d\n", ++nrclears );
}
UNLOCK_HARDWARE( mmesa );
mmesa->dirty |= MGA_UPLOAD_CLIPRECTS|MGA_UPLOAD_CONTEXT;
}
if (mask)
_swrast_Clear( ctx, mask, all, cx, cy, cw, ch );
}
static void mgaWaitForFrameCompletion( mgaContextPtr mmesa )
{
unsigned wait = 0;
GLuint last_frame, last_wrap;
last_frame = mmesa->sarea->last_frame.head;
last_wrap = mmesa->sarea->last_frame.wrap;
while ( 1 ) {
if ( last_wrap < mmesa->sarea->last_wrap ||
( last_wrap == mmesa->sarea->last_wrap &&
last_frame <= (MGA_READ( MGAREG_PRIMADDRESS ) -
mmesa->primary_offset) ) ) {
break;
}
if ( 0 ) {
wait++;
fprintf( stderr, " last: head=0x%06x wrap=%d\n",
last_frame, last_wrap );
fprintf( stderr, " head: head=0x%06lx wrap=%d\n",
(long)(MGA_READ( MGAREG_PRIMADDRESS ) - mmesa->primary_offset),
mmesa->sarea->last_wrap );
}
UPDATE_LOCK( mmesa, DRM_LOCK_FLUSH );
UNLOCK_HARDWARE( mmesa );
DO_USLEEP( 1 );
LOCK_HARDWARE( mmesa );
}
if ( wait )
fprintf( stderr, "\n" );
}
void mgaCopyBuffer( const __DRIdrawablePrivate *dPriv )
{
mgaContextPtr mmesa;
XF86DRIClipRectPtr pbox;
GLint nbox;
GLint ret;
GLint i;
GLboolean missed_target;
assert(dPriv);
assert(dPriv->driContextPriv);
assert(dPriv->driContextPriv->driverPrivate);
mmesa = (mgaContextPtr) dPriv->driContextPriv->driverPrivate;
FLUSH_BATCH( mmesa );
LOCK_HARDWARE( mmesa );
mgaWaitForFrameCompletion( mmesa );
UNLOCK_HARDWARE( mmesa );
driWaitForVBlank( dPriv, & mmesa->vbl_seq, mmesa->vblank_flags,
& missed_target );
if ( missed_target ) {
mmesa->swap_missed_count++;
(void) (*mmesa->get_ust)( & mmesa->swap_missed_ust );
}
LOCK_HARDWARE( mmesa );
if (mmesa->dirty_cliprects & MGA_FRONT)
mgaUpdateRects( mmesa, MGA_FRONT );
pbox = dPriv->pClipRects;
nbox = dPriv->numClipRects;
for (i = 0 ; i < nbox ; )
{
int nr = MIN2(i + MGA_NR_SAREA_CLIPRECTS, dPriv->numClipRects);
XF86DRIClipRectPtr b = mmesa->sarea->boxes;
mmesa->sarea->nbox = nr - i;
for ( ; i < nr ; i++)
*b++ = pbox[i];
if (0)
fprintf(stderr, "DRM_IOCTL_MGA_SWAP\n");
ret = drmCommandNone( mmesa->driFd, DRM_MGA_SWAP );
if ( ret ) {
printf("send swap retcode = %d\n", ret);
exit(1);
}
}
UNLOCK_HARDWARE( mmesa );
mmesa->dirty |= MGA_UPLOAD_CLIPRECTS;
mmesa->swap_count++;
(void) (*mmesa->get_ust)( & mmesa->swap_ust );
}
void mgaDDFinish( GLcontext *ctx )
{
mgaContextPtr mmesa = MGA_CONTEXT(ctx);
FLUSH_BATCH( mmesa );
if (1) {
if (MGA_DEBUG&DEBUG_VERBOSE_IOCTL)
fprintf(stderr, "mgaRegetLockQuiescent\n");
LOCK_HARDWARE( mmesa );
UPDATE_LOCK( mmesa, DRM_LOCK_QUIESCENT | DRM_LOCK_FLUSH );
UNLOCK_HARDWARE( mmesa );
mmesa->sarea->last_quiescent = mmesa->sarea->last_enqueue;
}
}
void mgaWaitAgeLocked( mgaContextPtr mmesa, int age )
{
if (GET_DISPATCH_AGE(mmesa) < age) {
UPDATE_LOCK( mmesa, DRM_LOCK_FLUSH );
}
}
void mgaWaitAge( mgaContextPtr mmesa, int age )
{
if (GET_DISPATCH_AGE(mmesa) < age) {
LOCK_HARDWARE(mmesa);
if (GET_DISPATCH_AGE(mmesa) < age) {
UPDATE_LOCK( mmesa, DRM_LOCK_FLUSH );
}
UNLOCK_HARDWARE(mmesa);
}
}
static GLboolean intersect_rect( XF86DRIClipRectPtr out,
const XF86DRIClipRectPtr a,
const XF86DRIClipRectPtr b )
{
*out = *a;
if (b->x1 > out->x1) out->x1 = b->x1;
if (b->y1 > out->y1) out->y1 = b->y1;
if (b->x2 < out->x2) out->x2 = b->x2;
if (b->y2 < out->y2) out->y2 = b->y2;
return ((out->x1 < out->x2) && (out->y1 < out->y2));
}
static void age_mmesa( mgaContextPtr mmesa, int age )
{
if (mmesa->CurrentTexObj[0]) mmesa->CurrentTexObj[0]->age = age;
if (mmesa->CurrentTexObj[1]) mmesa->CurrentTexObj[1]->age = age;
}
#ifdef __i386__
static int __break_vertex = 0;
#endif
void mgaFlushVerticesLocked( mgaContextPtr mmesa )
{
XF86DRIClipRectPtr pbox = mmesa->pClipRects;
int nbox = mmesa->numClipRects;
drmBufPtr buffer = mmesa->vertex_dma_buffer;
drmMGAVertex vertex;
int i;
mmesa->vertex_dma_buffer = 0;
if (!buffer)
return;
if (mmesa->dirty_cliprects & mmesa->draw_buffer)
mgaUpdateRects( mmesa, mmesa->draw_buffer );
if (mmesa->dirty & ~MGA_UPLOAD_CLIPRECTS)
mgaEmitHwStateLocked( mmesa );
mmesa->sarea->dirty |= MGA_UPLOAD_CONTEXT;
if (!nbox)
buffer->used = 0;
if (nbox >= MGA_NR_SAREA_CLIPRECTS)
mmesa->dirty |= MGA_UPLOAD_CLIPRECTS;
#if 0
if (!buffer->used || !(mmesa->dirty & MGA_UPLOAD_CLIPRECTS))
{
if (nbox == 1)
mmesa->sarea->nbox = 0;
else
mmesa->sarea->nbox = nbox;
if (MGA_DEBUG&DEBUG_VERBOSE_IOCTL)
fprintf(stderr, "Firing vertex -- case a nbox %d\n", nbox);
vertex.idx = buffer->idx;
vertex.used = buffer->used;
vertex.discard = 1;
drmCommandWrite( mmesa->driFd, DRM_MGA_VERTEX,
&vertex, sizeof(drmMGAVertex) );
age_mmesa(mmesa, mmesa->sarea->last_enqueue);
}
else
#endif
{
for (i = 0 ; i < nbox ; )
{
int nr = MIN2(i + MGA_NR_SAREA_CLIPRECTS, nbox);
XF86DRIClipRectPtr b = mmesa->sarea->boxes;
int discard = 0;
if (mmesa->scissor) {
mmesa->sarea->nbox = 0;
for ( ; i < nr ; i++) {
*b = pbox[i];
if (intersect_rect(b, b, &mmesa->scissor_rect)) {
mmesa->sarea->nbox++;
b++;
}
}
if (!mmesa->sarea->nbox) {
if (nr < nbox) continue;
buffer->used = 0;
}
} else {
mmesa->sarea->nbox = nr - i;
for ( ; i < nr ; i++)
*b++ = pbox[i];
}
if (nr == nbox)
discard = 1;
mmesa->sarea->dirty |= MGA_UPLOAD_CLIPRECTS;
vertex.idx = buffer->idx;
vertex.used = buffer->used;
vertex.discard = discard;
drmCommandWrite( mmesa->driFd, DRM_MGA_VERTEX,
&vertex, sizeof(drmMGAVertex) );
age_mmesa(mmesa, mmesa->sarea->last_enqueue);
}
}
#ifdef __i386__
if ( __break_vertex ) {
__asm__ __volatile__ ( "int $3" );
}
#endif
mmesa->dirty &= ~MGA_UPLOAD_CLIPRECTS;
}
void mgaFlushVertices( mgaContextPtr mmesa )
{
LOCK_HARDWARE( mmesa );
mgaFlushVerticesLocked( mmesa );
UNLOCK_HARDWARE( mmesa );
}
void mgaFireILoadLocked( mgaContextPtr mmesa,
GLuint offset, GLuint length )
{
if (!mmesa->iload_buffer) {
fprintf(stderr, "mgaFireILoad: no buffer\n");
return;
}
if (MGA_DEBUG&DEBUG_VERBOSE_IOCTL)
fprintf(stderr, "mgaFireILoad idx %d ofs 0x%x length %d\n",
mmesa->iload_buffer->idx, (int)offset, (int)length );
mga_iload_dma_ioctl( mmesa, offset, length );
}
void mgaGetILoadBufferLocked( mgaContextPtr mmesa )
{
if (MGA_DEBUG&DEBUG_VERBOSE_IOCTL)
fprintf(stderr, "mgaGetIloadBuffer (buffer now %p)\n",
(void *)mmesa->iload_buffer);
mmesa->iload_buffer = mga_get_buffer_ioctl( mmesa );
}
drmBufPtr mgaGetBufferLocked( mgaContextPtr mmesa )
{
return mga_get_buffer_ioctl( mmesa );
}
void mgaDDFlush( GLcontext *ctx )
{
mgaContextPtr mmesa = MGA_CONTEXT( ctx );
FLUSH_BATCH( mmesa );
if (1 || GET_DISPATCH_AGE( mmesa ) < mmesa->sarea->last_enqueue) {
LOCK_HARDWARE( mmesa );
UPDATE_LOCK( mmesa, DRM_LOCK_FLUSH );
UNLOCK_HARDWARE( mmesa );
}
}
void mgaReleaseBufLocked( mgaContextPtr mmesa, drmBufPtr buffer )
{
drmMGAVertex vertex;
if (!buffer) return;
vertex.idx = buffer->idx;
vertex.used = 0;
vertex.discard = 1;
drmCommandWrite( mmesa->driFd, DRM_MGA_VERTEX,
&vertex, sizeof(drmMGAVertex) );
}
int mgaFlushDMA( int fd, drmLockFlags flags )
{
drmMGALock lock;
int ret, i = 0;
memset( &lock, 0, sizeof(drmMGALock) );
if ( flags & DRM_LOCK_QUIESCENT ) lock.flags |= DRM_LOCK_QUIESCENT;
if ( flags & DRM_LOCK_FLUSH ) lock.flags |= DRM_LOCK_FLUSH;
if ( flags & DRM_LOCK_FLUSH_ALL ) lock.flags |= DRM_LOCK_FLUSH_ALL;
do {
ret = drmCommandWrite( fd, DRM_MGA_FLUSH, &lock, sizeof(drmMGALock) );
} while ( ret && errno == EBUSY && i++ < DRM_MGA_IDLE_RETRY );
if ( ret == 0 )
return 0;
if ( errno != EBUSY )
return -errno;
if ( lock.flags & DRM_LOCK_QUIESCENT ) {
lock.flags &= ~(DRM_LOCK_FLUSH | DRM_LOCK_FLUSH_ALL);
do {
ret = drmCommandWrite( fd, DRM_MGA_FLUSH, &lock, sizeof(drmMGALock) );
} while ( ret && errno == EBUSY && i++ < DRM_MGA_IDLE_RETRY );
}
if ( ret == 0 ) {
return 0;
} else {
return -errno;
}
}
void mgaDDInitIoctlFuncs( GLcontext *ctx )
{
ctx->Driver.Clear = mgaDDClear;
ctx->Driver.Flush = mgaDDFlush;
ctx->Driver.Finish = mgaDDFinish;
}