| /* |
| * Copyright 1998-2003 VIA Technologies, Inc. All Rights Reserved. |
| * Copyright 2001-2003 S3 Graphics, Inc. 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, sub license, |
| * 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 NON-INFRINGEMENT. IN NO EVENT SHALL |
| * VIA, S3 GRAPHICS, 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. |
| */ |
| #include <stdio.h> |
| #include <unistd.h> |
| |
| #include "glheader.h" |
| #include "mtypes.h" |
| #include "macros.h" |
| #include "dd.h" |
| #include "swrast/swrast.h" |
| |
| #include "mm.h" |
| #include "via_context.h" |
| #include "via_tris.h" |
| #include "via_ioctl.h" |
| #include "via_state.h" |
| #include "via_fb.h" |
| #include "via_3d_reg.h" |
| |
| #include "vblank.h" |
| #include "drm.h" |
| #include "xf86drm.h" |
| #include <sys/ioctl.h> |
| #include <errno.h> |
| |
| |
| #define VIA_REG_STATUS 0x400 |
| #define VIA_REG_GEMODE 0x004 |
| #define VIA_REG_SRCBASE 0x030 |
| #define VIA_REG_DSTBASE 0x034 |
| #define VIA_REG_PITCH 0x038 |
| #define VIA_REG_SRCCOLORKEY 0x01C |
| #define VIA_REG_KEYCONTROL 0x02C |
| #define VIA_REG_SRCPOS 0x008 |
| #define VIA_REG_DSTPOS 0x00C |
| #define VIA_REG_GECMD 0x000 |
| #define VIA_REG_DIMENSION 0x010 /* width and height */ |
| #define VIA_REG_FGCOLOR 0x018 |
| |
| #define VIA_GEM_8bpp 0x00000000 |
| #define VIA_GEM_16bpp 0x00000100 |
| #define VIA_GEM_32bpp 0x00000300 |
| #define VIA_GEC_BLT 0x00000001 |
| #define VIA_PITCH_ENABLE 0x80000000 |
| #define VIA_GEC_INCX 0x00000000 |
| #define VIA_GEC_DECY 0x00004000 |
| #define VIA_GEC_INCY 0x00000000 |
| #define VIA_GEC_DECX 0x00008000 |
| #define VIA_GEC_FIXCOLOR_PAT 0x00002000 |
| |
| |
| #define VIA_BLIT_CLEAR 0x00 |
| #define VIA_BLIT_COPY 0xCC |
| #define VIA_BLIT_FILL 0xF0 |
| #define VIA_BLIT_SET 0xFF |
| |
| static void dump_dma( struct via_context *vmesa ) |
| { |
| GLuint i; |
| GLuint *data = (GLuint *)vmesa->dma; |
| for (i = 0; i < vmesa->dmaLow; i += 16) { |
| fprintf(stderr, "%04x: ", i); |
| fprintf(stderr, "%08x ", *data++); |
| fprintf(stderr, "%08x ", *data++); |
| fprintf(stderr, "%08x ", *data++); |
| fprintf(stderr, "%08x\n", *data++); |
| } |
| fprintf(stderr, "******************************************\n"); |
| } |
| |
| |
| |
| void viaCheckDma(struct via_context *vmesa, GLuint bytes) |
| { |
| VIA_FINISH_PRIM( vmesa ); |
| if (vmesa->dmaLow + bytes > VIA_DMA_HIGHWATER) { |
| viaFlushDma(vmesa); |
| } |
| } |
| |
| |
| |
| #define SetReg2DAGP(nReg, nData) do { \ |
| OUT_RING( ((nReg) >> 2) | 0xF0000000 ); \ |
| OUT_RING( nData ); \ |
| } while (0) |
| |
| |
| static void viaBlit(struct via_context *vmesa, GLuint bpp, |
| GLuint srcBase, GLuint srcPitch, |
| GLuint dstBase, GLuint dstPitch, |
| GLuint w, GLuint h, |
| GLuint blitMode, |
| GLuint color, GLuint nMask ) |
| { |
| |
| GLuint dwGEMode, srcX, dstX, cmd; |
| RING_VARS; |
| |
| if (VIA_DEBUG & DEBUG_2D) |
| fprintf(stderr, |
| "%s bpp %d src %x/%x dst %x/%x w %d h %d " |
| " mode: %x color: 0x%08x mask 0x%08x\n", |
| __FUNCTION__, bpp, srcBase, srcPitch, dstBase, |
| dstPitch, w,h, blitMode, color, nMask); |
| |
| |
| if (!w || !h) |
| return; |
| |
| switch (bpp) { |
| case 16: |
| dwGEMode = VIA_GEM_16bpp; |
| srcX = (srcBase & 0x1f) >> 1; |
| dstX = (dstBase & 0x1f) >> 1; |
| break; |
| case 32: |
| dwGEMode = VIA_GEM_32bpp; |
| srcX = (srcBase & 0x1f) >> 2; |
| dstX = (dstBase & 0x1f) >> 2; |
| break; |
| default: |
| return; |
| } |
| |
| switch(blitMode) { |
| case VIA_BLIT_FILL: |
| cmd = VIA_GEC_BLT | VIA_GEC_FIXCOLOR_PAT | (VIA_BLIT_FILL << 24); |
| break; |
| case VIA_BLIT_COPY: |
| cmd = VIA_GEC_BLT | (VIA_BLIT_COPY << 24); |
| break; |
| default: |
| return; |
| } |
| |
| BEGIN_RING(22); |
| SetReg2DAGP( VIA_REG_GEMODE, dwGEMode); |
| SetReg2DAGP( VIA_REG_FGCOLOR, color); |
| SetReg2DAGP( 0x2C, nMask); |
| SetReg2DAGP( VIA_REG_SRCBASE, (srcBase & ~0x1f) >> 3); |
| SetReg2DAGP( VIA_REG_DSTBASE, (dstBase & ~0x1f) >> 3); |
| SetReg2DAGP( VIA_REG_PITCH, VIA_PITCH_ENABLE | |
| (srcPitch >> 3) | ((dstPitch >> 3) << 16)); |
| SetReg2DAGP( VIA_REG_SRCPOS, srcX); |
| SetReg2DAGP( VIA_REG_DSTPOS, dstX); |
| SetReg2DAGP( VIA_REG_DIMENSION, (((h - 1) << 16) | (w - 1))); |
| SetReg2DAGP( VIA_REG_GECMD, cmd); |
| SetReg2DAGP( 0x2C, 0x00000000); |
| ADVANCE_RING(); |
| } |
| |
| static void viaFillBuffer(struct via_context *vmesa, |
| struct via_renderbuffer *buffer, |
| drm_clip_rect_t *pbox, |
| int nboxes, |
| GLuint pixel, |
| GLuint mask) |
| { |
| GLuint bytePerPixel = buffer->bpp >> 3; |
| GLuint i; |
| |
| for (i = 0; i < nboxes ; i++) { |
| int x = pbox[i].x1 - buffer->drawX; |
| int y = pbox[i].y1 - buffer->drawY; |
| int w = pbox[i].x2 - pbox[i].x1; |
| int h = pbox[i].y2 - pbox[i].y1; |
| |
| int offset = (buffer->offset + |
| y * buffer->pitch + |
| x * bytePerPixel); |
| |
| viaBlit(vmesa, |
| buffer->bpp, |
| offset, buffer->pitch, |
| offset, buffer->pitch, |
| w, h, |
| VIA_BLIT_FILL, pixel, mask); |
| } |
| } |
| |
| |
| |
| static void viaClear(GLcontext *ctx, GLbitfield mask) |
| { |
| struct via_context *vmesa = VIA_CONTEXT(ctx); |
| __DRIdrawablePrivate *dPriv = vmesa->driDrawable; |
| struct via_renderbuffer *const vrb = |
| (struct via_renderbuffer *) dPriv->driverPrivate; |
| int flag = 0; |
| GLuint i = 0; |
| GLuint clear_depth_mask = 0xf << 28; |
| GLuint clear_depth = 0; |
| |
| VIA_FLUSH_DMA(vmesa); |
| |
| if (mask & BUFFER_BIT_FRONT_LEFT) { |
| flag |= VIA_FRONT; |
| mask &= ~BUFFER_BIT_FRONT_LEFT; |
| } |
| |
| if (mask & BUFFER_BIT_BACK_LEFT) { |
| flag |= VIA_BACK; |
| mask &= ~BUFFER_BIT_BACK_LEFT; |
| } |
| |
| if (mask & BUFFER_BIT_DEPTH) { |
| flag |= VIA_DEPTH; |
| clear_depth = (GLuint)(ctx->Depth.Clear * vmesa->ClearDepth); |
| clear_depth_mask &= ~vmesa->depth_clear_mask; |
| mask &= ~BUFFER_BIT_DEPTH; |
| } |
| |
| if (mask & BUFFER_BIT_STENCIL) { |
| if (vmesa->have_hw_stencil) { |
| if ((ctx->Stencil.WriteMask[0] & 0xff) == 0xff) { |
| flag |= VIA_DEPTH; |
| clear_depth &= ~0xff; |
| clear_depth |= (ctx->Stencil.Clear & 0xff); |
| clear_depth_mask &= ~vmesa->stencil_clear_mask; |
| mask &= ~BUFFER_BIT_STENCIL; |
| } |
| else { |
| if (VIA_DEBUG & DEBUG_2D) |
| fprintf(stderr, "Clear stencil writemask %x\n", |
| ctx->Stencil.WriteMask[0]); |
| } |
| } |
| } |
| |
| /* 16bpp doesn't support masked clears */ |
| if (vmesa->viaScreen->bytesPerPixel == 2 && |
| vmesa->ClearMask & 0xf0000000) { |
| if (flag & VIA_FRONT) |
| mask |= BUFFER_BIT_FRONT_LEFT; |
| if (flag & VIA_BACK) |
| mask |= BUFFER_BIT_BACK_LEFT; |
| flag &= ~(VIA_FRONT | VIA_BACK); |
| } |
| |
| if (flag) { |
| drm_clip_rect_t *boxes, *tmp_boxes = 0; |
| int nr = 0; |
| GLint cx, cy, cw, ch; |
| GLboolean all; |
| |
| LOCK_HARDWARE(vmesa); |
| |
| /* get region after locking: */ |
| cx = ctx->DrawBuffer->_Xmin; |
| cy = ctx->DrawBuffer->_Ymin; |
| cw = ctx->DrawBuffer->_Xmax - cx; |
| ch = ctx->DrawBuffer->_Ymax - cy; |
| all = (cw == ctx->DrawBuffer->Width && ch == ctx->DrawBuffer->Height); |
| |
| /* flip top to bottom */ |
| cy = dPriv->h - cy - ch; |
| cx += vrb->drawX; |
| cy += vrb->drawY; |
| |
| if (!all) { |
| drm_clip_rect_t *b = vmesa->pClipRects; |
| |
| boxes = tmp_boxes = |
| (drm_clip_rect_t *)malloc(vmesa->numClipRects * |
| sizeof(drm_clip_rect_t)); |
| if (!boxes) { |
| UNLOCK_HARDWARE(vmesa); |
| return; |
| } |
| |
| for (; i < vmesa->numClipRects; i++) { |
| GLint x = b[i].x1; |
| GLint y = b[i].y1; |
| GLint w = b[i].x2 - x; |
| GLint h = b[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; |
| |
| boxes[nr].x1 = x; |
| boxes[nr].y1 = y; |
| boxes[nr].x2 = x + w; |
| boxes[nr].y2 = y + h; |
| nr++; |
| } |
| } |
| else { |
| boxes = vmesa->pClipRects; |
| nr = vmesa->numClipRects; |
| } |
| |
| if (flag & VIA_FRONT) { |
| viaFillBuffer(vmesa, &vmesa->front, boxes, nr, vmesa->ClearColor, |
| vmesa->ClearMask); |
| } |
| |
| if (flag & VIA_BACK) { |
| viaFillBuffer(vmesa, &vmesa->back, boxes, nr, vmesa->ClearColor, |
| vmesa->ClearMask); |
| } |
| |
| if (flag & VIA_DEPTH) { |
| viaFillBuffer(vmesa, &vmesa->depth, boxes, nr, clear_depth, |
| clear_depth_mask); |
| } |
| |
| viaFlushDmaLocked(vmesa, VIA_NO_CLIPRECTS); |
| UNLOCK_HARDWARE(vmesa); |
| |
| if (tmp_boxes) |
| free(tmp_boxes); |
| } |
| |
| if (mask) |
| _swrast_Clear(ctx, mask); |
| } |
| |
| |
| |
| |
| static void viaDoSwapBuffers(struct via_context *vmesa, |
| drm_clip_rect_t *b, |
| GLuint nbox) |
| { |
| GLuint bytePerPixel = vmesa->viaScreen->bitsPerPixel >> 3; |
| struct via_renderbuffer *front = &vmesa->front; |
| struct via_renderbuffer *back = &vmesa->back; |
| GLuint i; |
| |
| for (i = 0; i < nbox; i++, b++) { |
| GLint x = b->x1 - back->drawX; |
| GLint y = b->y1 - back->drawY; |
| GLint w = b->x2 - b->x1; |
| GLint h = b->y2 - b->y1; |
| |
| GLuint src = back->offset + y * back->pitch + x * bytePerPixel; |
| GLuint dest = front->offset + y * front->pitch + x * bytePerPixel; |
| |
| viaBlit(vmesa, |
| bytePerPixel << 3, |
| src, back->pitch, |
| dest, front->pitch, |
| w, h, |
| VIA_BLIT_COPY, 0, 0); |
| } |
| |
| viaFlushDmaLocked(vmesa, VIA_NO_CLIPRECTS); /* redundant */ |
| } |
| |
| |
| static void viaEmitBreadcrumbLocked( struct via_context *vmesa ) |
| { |
| struct via_renderbuffer *buffer = &vmesa->breadcrumb; |
| GLuint value = vmesa->lastBreadcrumbWrite + 1; |
| |
| if (VIA_DEBUG & DEBUG_IOCTL) |
| fprintf(stderr, "%s %d\n", __FUNCTION__, value); |
| |
| assert(!vmesa->dmaLow); |
| |
| viaBlit(vmesa, |
| buffer->bpp, |
| buffer->offset, buffer->pitch, |
| buffer->offset, buffer->pitch, |
| 1, 1, |
| VIA_BLIT_FILL, value, 0); |
| |
| viaFlushDmaLocked(vmesa, VIA_NO_CLIPRECTS); /* often redundant */ |
| vmesa->lastBreadcrumbWrite = value; |
| } |
| |
| void viaEmitBreadcrumb( struct via_context *vmesa ) |
| { |
| LOCK_HARDWARE(vmesa); |
| if (vmesa->dmaLow) |
| viaFlushDmaLocked(vmesa, 0); |
| |
| viaEmitBreadcrumbLocked( vmesa ); |
| UNLOCK_HARDWARE(vmesa); |
| } |
| |
| static GLboolean viaCheckIdle( struct via_context *vmesa ) |
| { |
| if ((vmesa->regEngineStatus[0] & 0xFFFEFFFF) == 0x00020000) { |
| return GL_TRUE; |
| } |
| return GL_FALSE; |
| } |
| |
| |
| GLboolean viaCheckBreadcrumb( struct via_context *vmesa, GLuint value ) |
| { |
| GLuint *buf = (GLuint *)vmesa->breadcrumb.map; |
| vmesa->lastBreadcrumbRead = *buf; |
| |
| if (VIA_DEBUG & DEBUG_IOCTL) |
| fprintf(stderr, "%s %d < %d: %d\n", __FUNCTION__, value, |
| vmesa->lastBreadcrumbRead, |
| !VIA_GEQ_WRAP(value, vmesa->lastBreadcrumbRead)); |
| |
| return !VIA_GEQ_WRAP(value, vmesa->lastBreadcrumbRead); |
| } |
| |
| static void viaWaitBreadcrumb( struct via_context *vmesa, GLuint value ) |
| { |
| if (VIA_DEBUG & DEBUG_IOCTL) |
| fprintf(stderr, "%s %d\n", __FUNCTION__, value); |
| |
| assert(!VIA_GEQ_WRAP(value, vmesa->lastBreadcrumbWrite)); |
| |
| while (!viaCheckBreadcrumb( vmesa, value )) { |
| viaSwapOutWork( vmesa ); |
| via_release_pending_textures( vmesa ); |
| } |
| } |
| |
| |
| void viaWaitIdle( struct via_context *vmesa, GLboolean light ) |
| { |
| VIA_FLUSH_DMA(vmesa); |
| |
| if (VIA_DEBUG & DEBUG_IOCTL) |
| fprintf(stderr, "%s lastDma %d lastBreadcrumbWrite %d\n", |
| __FUNCTION__, vmesa->lastDma, vmesa->lastBreadcrumbWrite); |
| |
| /* Need to emit a new breadcrumb? |
| */ |
| if (vmesa->lastDma == vmesa->lastBreadcrumbWrite) { |
| LOCK_HARDWARE(vmesa); |
| viaEmitBreadcrumbLocked( vmesa ); |
| UNLOCK_HARDWARE(vmesa); |
| } |
| |
| /* Need to wait? |
| */ |
| if (VIA_GEQ_WRAP(vmesa->lastDma, vmesa->lastBreadcrumbRead)) |
| viaWaitBreadcrumb( vmesa, vmesa->lastDma ); |
| |
| if (light) return; |
| |
| LOCK_HARDWARE(vmesa); |
| while(!viaCheckIdle(vmesa)) |
| ; |
| UNLOCK_HARDWARE(vmesa); |
| via_release_pending_textures(vmesa); |
| } |
| |
| |
| void viaWaitIdleLocked( struct via_context *vmesa, GLboolean light ) |
| { |
| if (vmesa->dmaLow) |
| viaFlushDmaLocked(vmesa, 0); |
| |
| if (VIA_DEBUG & DEBUG_IOCTL) |
| fprintf(stderr, "%s lastDma %d lastBreadcrumbWrite %d\n", |
| __FUNCTION__, vmesa->lastDma, vmesa->lastBreadcrumbWrite); |
| |
| /* Need to emit a new breadcrumb? |
| */ |
| if (vmesa->lastDma == vmesa->lastBreadcrumbWrite) { |
| viaEmitBreadcrumbLocked( vmesa ); |
| } |
| |
| /* Need to wait? |
| */ |
| if (vmesa->lastDma >= vmesa->lastBreadcrumbRead) |
| viaWaitBreadcrumb( vmesa, vmesa->lastDma ); |
| |
| if (light) return; |
| |
| while(!viaCheckIdle(vmesa)) |
| ; |
| |
| via_release_pending_textures(vmesa); |
| } |
| |
| |
| |
| /* Wait for command stream to be processed *and* the next vblank to |
| * occur. Equivalent to calling WAIT_IDLE() and then WaitVBlank, |
| * except that WAIT_IDLE() will spin the CPU polling, while this is |
| * IRQ driven. |
| */ |
| static void viaWaitIdleVBlank( __DRIdrawablePrivate *dPriv, |
| struct via_context *vmesa, |
| GLuint value ) |
| { |
| GLboolean missed_target; |
| __DRIscreenPrivate *psp = dPriv->driScreenPriv; |
| |
| VIA_FLUSH_DMA(vmesa); |
| |
| if (!value) |
| return; |
| |
| do { |
| if (value < vmesa->lastBreadcrumbRead || |
| vmesa->thrashing) |
| viaSwapOutWork(vmesa); |
| |
| driWaitForVBlank( dPriv, & missed_target ); |
| if ( missed_target ) { |
| vmesa->swap_missed_count++; |
| (*psp->systemTime->getUST)( &vmesa->swap_missed_ust ); |
| } |
| } |
| while (!viaCheckBreadcrumb(vmesa, value)); |
| |
| vmesa->thrashing = 0; /* reset flag on swap */ |
| vmesa->swap_count++; |
| via_release_pending_textures( vmesa ); |
| } |
| |
| |
| |
| static void viaDoPageFlipLocked(struct via_context *vmesa, GLuint offset) |
| { |
| RING_VARS; |
| |
| if (VIA_DEBUG & DEBUG_2D) |
| fprintf(stderr, "%s %x\n", __FUNCTION__, offset); |
| |
| if (!vmesa->nDoneFirstFlip) { |
| vmesa->nDoneFirstFlip = GL_TRUE; |
| BEGIN_RING(4); |
| OUT_RING(HALCYON_HEADER2); |
| OUT_RING(0x00fe0000); |
| OUT_RING(0x0000000e); |
| OUT_RING(0x0000000e); |
| ADVANCE_RING(); |
| } |
| |
| BEGIN_RING(4); |
| OUT_RING( HALCYON_HEADER2 ); |
| OUT_RING( 0x00fe0000 ); |
| OUT_RING((HC_SubA_HFBBasL << 24) | (offset & 0xFFFFF8) | 0x2); |
| OUT_RING((HC_SubA_HFBDrawFirst << 24) | |
| ((offset & 0xFF000000) >> 24) | 0x0100); |
| ADVANCE_RING(); |
| |
| vmesa->pfCurrentOffset = vmesa->sarea->pfCurrentOffset = offset; |
| |
| viaFlushDmaLocked(vmesa, VIA_NO_CLIPRECTS); /* often redundant */ |
| } |
| |
| void viaResetPageFlippingLocked(struct via_context *vmesa) |
| { |
| if (VIA_DEBUG & DEBUG_2D) |
| fprintf(stderr, "%s\n", __FUNCTION__); |
| |
| viaDoPageFlipLocked( vmesa, 0 ); |
| |
| if (vmesa->front.offset != 0) { |
| struct via_renderbuffer buffer_tmp; |
| memcpy(&buffer_tmp, &vmesa->back, sizeof(struct via_renderbuffer)); |
| memcpy(&vmesa->back, &vmesa->front, sizeof(struct via_renderbuffer)); |
| memcpy(&vmesa->front, &buffer_tmp, sizeof(struct via_renderbuffer)); |
| } |
| |
| assert(vmesa->front.offset == 0); |
| vmesa->doPageFlip = vmesa->allowPageFlip = 0; |
| } |
| |
| |
| /* |
| * Copy the back buffer to the front buffer. |
| */ |
| void viaCopyBuffer(__DRIdrawablePrivate *dPriv) |
| { |
| struct via_context *vmesa = |
| (struct via_context *)dPriv->driContextPriv->driverPrivate; |
| __DRIscreenPrivate *psp = dPriv->driScreenPriv; |
| |
| if (VIA_DEBUG & DEBUG_IOCTL) |
| fprintf(stderr, |
| "%s: lastSwap[1] %d lastSwap[0] %d lastWrite %d lastRead %d\n", |
| __FUNCTION__, |
| vmesa->lastSwap[1], |
| vmesa->lastSwap[0], |
| vmesa->lastBreadcrumbWrite, |
| vmesa->lastBreadcrumbRead); |
| |
| VIA_FLUSH_DMA(vmesa); |
| |
| if (dPriv->vblFlags == VBLANK_FLAG_SYNC && |
| vmesa->lastBreadcrumbWrite > 1) |
| viaWaitIdleVBlank(dPriv, vmesa, vmesa->lastBreadcrumbWrite-1); |
| else |
| viaWaitIdleVBlank(dPriv, vmesa, vmesa->lastSwap[1]); |
| |
| LOCK_HARDWARE(vmesa); |
| |
| /* Catch and cleanup situation where we were pageflipping but have |
| * stopped. |
| */ |
| if (dPriv->numClipRects && vmesa->sarea->pfCurrentOffset != 0) { |
| viaResetPageFlippingLocked(vmesa); |
| UNLOCK_HARDWARE(vmesa); |
| return; |
| } |
| |
| viaDoSwapBuffers(vmesa, dPriv->pClipRects, dPriv->numClipRects); |
| vmesa->lastSwap[1] = vmesa->lastSwap[0]; |
| vmesa->lastSwap[0] = vmesa->lastBreadcrumbWrite; |
| viaEmitBreadcrumbLocked(vmesa); |
| UNLOCK_HARDWARE(vmesa); |
| |
| (*psp->systemTime->getUST)( &vmesa->swap_ust ); |
| } |
| |
| |
| void viaPageFlip(__DRIdrawablePrivate *dPriv) |
| { |
| struct via_context *vmesa = |
| (struct via_context *)dPriv->driContextPriv->driverPrivate; |
| struct via_renderbuffer buffer_tmp; |
| __DRIscreenPrivate *psp = dPriv->driScreenPriv; |
| |
| VIA_FLUSH_DMA(vmesa); |
| if (dPriv->vblFlags == VBLANK_FLAG_SYNC && |
| vmesa->lastBreadcrumbWrite > 1) |
| viaWaitIdleVBlank(dPriv, vmesa, vmesa->lastBreadcrumbWrite - 1); |
| else |
| viaWaitIdleVBlank(dPriv, vmesa, vmesa->lastSwap[0]); |
| |
| LOCK_HARDWARE(vmesa); |
| viaDoPageFlipLocked(vmesa, vmesa->back.offset); |
| vmesa->lastSwap[1] = vmesa->lastSwap[0]; |
| vmesa->lastSwap[0] = vmesa->lastBreadcrumbWrite; |
| viaEmitBreadcrumbLocked(vmesa); |
| UNLOCK_HARDWARE(vmesa); |
| |
| (*psp->systemTime->getUST)( &vmesa->swap_ust ); |
| |
| |
| /* KW: FIXME: When buffers are freed, could free frontbuffer by |
| * accident: |
| */ |
| memcpy(&buffer_tmp, &vmesa->back, sizeof(struct via_renderbuffer)); |
| memcpy(&vmesa->back, &vmesa->front, sizeof(struct via_renderbuffer)); |
| memcpy(&vmesa->front, &buffer_tmp, sizeof(struct via_renderbuffer)); |
| } |
| |
| |
| |
| |
| #define VIA_CMDBUF_MAX_LAG 50000 |
| |
| static int fire_buffer(struct via_context *vmesa) |
| { |
| drm_via_cmdbuffer_t bufI; |
| int ret; |
| |
| bufI.buf = (char *)vmesa->dma; |
| bufI.size = vmesa->dmaLow; |
| |
| if (vmesa->useAgp) { |
| drm_via_cmdbuf_size_t bSiz; |
| |
| /* Do the CMDBUF_SIZE ioctl: |
| */ |
| bSiz.func = VIA_CMDBUF_LAG; |
| bSiz.wait = 1; |
| bSiz.size = VIA_CMDBUF_MAX_LAG; |
| do { |
| ret = drmCommandWriteRead(vmesa->driFd, DRM_VIA_CMDBUF_SIZE, |
| &bSiz, sizeof(bSiz)); |
| } while (ret == -EAGAIN); |
| if (ret) { |
| UNLOCK_HARDWARE(vmesa); |
| fprintf(stderr, "%s: DRM_VIA_CMDBUF_SIZE returned %d\n", |
| __FUNCTION__, ret); |
| abort(); |
| return ret; |
| } |
| |
| /* Actually fire the buffer: |
| */ |
| do { |
| ret = drmCommandWrite(vmesa->driFd, DRM_VIA_CMDBUFFER, |
| &bufI, sizeof(bufI)); |
| } while (ret == -EAGAIN); |
| if (ret) { |
| UNLOCK_HARDWARE(vmesa); |
| fprintf(stderr, "%s: DRM_VIA_CMDBUFFER returned %d\n", |
| __FUNCTION__, ret); |
| abort(); |
| /* If this fails, the original code fell back to the PCI path. |
| */ |
| } |
| else |
| return 0; |
| |
| /* Fall through to PCI handling?!? |
| */ |
| viaWaitIdleLocked(vmesa, GL_FALSE); |
| } |
| |
| ret = drmCommandWrite(vmesa->driFd, DRM_VIA_PCICMD, &bufI, sizeof(bufI)); |
| if (ret) { |
| UNLOCK_HARDWARE(vmesa); |
| dump_dma(vmesa); |
| fprintf(stderr, "%s: DRM_VIA_PCICMD returned %d\n", __FUNCTION__, ret); |
| abort(); |
| } |
| |
| return ret; |
| } |
| |
| |
| /* Inserts the surface addresss and active cliprects one at a time |
| * into the head of the DMA buffer being flushed. Fires the buffer |
| * for each cliprect. |
| */ |
| static void via_emit_cliprect(struct via_context *vmesa, |
| drm_clip_rect_t *b) |
| { |
| struct via_renderbuffer *buffer = vmesa->drawBuffer; |
| GLuint *vb = (GLuint *)(vmesa->dma + vmesa->dmaCliprectAddr); |
| |
| GLuint format = (vmesa->viaScreen->bitsPerPixel == 0x20 |
| ? HC_HDBFM_ARGB8888 |
| : HC_HDBFM_RGB565); |
| |
| GLuint pitch = buffer->pitch; |
| GLuint offset = buffer->offset; |
| |
| if (0) |
| fprintf(stderr, "emit cliprect for box %d,%d %d,%d\n", |
| b->x1, b->y1, b->x2, b->y2); |
| |
| vb[0] = HC_HEADER2; |
| vb[1] = (HC_ParaType_NotTex << 16); |
| |
| if (vmesa->driDrawable->w == 0 || vmesa->driDrawable->h == 0) { |
| vb[2] = (HC_SubA_HClipTB << 24) | 0x0; |
| vb[3] = (HC_SubA_HClipLR << 24) | 0x0; |
| } |
| else { |
| vb[2] = (HC_SubA_HClipTB << 24) | (b->y1 << 12) | b->y2; |
| vb[3] = (HC_SubA_HClipLR << 24) | (b->x1 << 12) | b->x2; |
| } |
| |
| vb[4] = (HC_SubA_HDBBasL << 24) | (offset & 0xFFFFFF); |
| vb[5] = (HC_SubA_HDBBasH << 24) | ((offset & 0xFF000000) >> 24); |
| |
| vb[6] = (HC_SubA_HSPXYOS << 24); |
| vb[7] = (HC_SubA_HDBFM << 24) | HC_HDBLoc_Local | format | pitch; |
| } |
| |
| |
| |
| static int intersect_rect(drm_clip_rect_t *out, |
| drm_clip_rect_t *a, |
| drm_clip_rect_t *b) |
| { |
| *out = *a; |
| |
| if (0) |
| fprintf(stderr, "intersect %d,%d %d,%d and %d,%d %d,%d\n", |
| a->x1, a->y1, a->x2, a->y2, |
| b->x1, b->y1, b->x2, b->y2); |
| |
| if (b->x1 > out->x1) out->x1 = b->x1; |
| if (b->x2 < out->x2) out->x2 = b->x2; |
| if (out->x1 >= out->x2) return 0; |
| |
| if (b->y1 > out->y1) out->y1 = b->y1; |
| if (b->y2 < out->y2) out->y2 = b->y2; |
| if (out->y1 >= out->y2) return 0; |
| |
| return 1; |
| } |
| |
| void viaFlushDmaLocked(struct via_context *vmesa, GLuint flags) |
| { |
| int i; |
| RING_VARS; |
| |
| if (VIA_DEBUG & (DEBUG_IOCTL|DEBUG_DMA)) |
| fprintf(stderr, "%s\n", __FUNCTION__); |
| |
| if (*(GLuint *)vmesa->driHwLock != (DRM_LOCK_HELD|vmesa->hHWContext) && |
| *(GLuint *)vmesa->driHwLock != |
| (DRM_LOCK_HELD|DRM_LOCK_CONT|vmesa->hHWContext)) { |
| fprintf(stderr, "%s called without lock held\n", __FUNCTION__); |
| abort(); |
| } |
| |
| if (vmesa->dmaLow == 0) { |
| return; |
| } |
| |
| assert(vmesa->dmaLastPrim == 0); |
| |
| /* viaFinishPrimitive can add up to 8 bytes beyond VIA_DMA_HIGHWATER: |
| */ |
| if (vmesa->dmaLow > VIA_DMA_HIGHWATER + 8) { |
| fprintf(stderr, "buffer overflow in Flush Prims = %d\n",vmesa->dmaLow); |
| abort(); |
| } |
| |
| switch (vmesa->dmaLow & 0x1F) { |
| case 8: |
| BEGIN_RING_NOCHECK( 6 ); |
| OUT_RING( HC_HEADER2 ); |
| OUT_RING( (HC_ParaType_NotTex << 16) ); |
| OUT_RING( HC_DUMMY ); |
| OUT_RING( HC_DUMMY ); |
| OUT_RING( HC_DUMMY ); |
| OUT_RING( HC_DUMMY ); |
| ADVANCE_RING(); |
| break; |
| case 16: |
| BEGIN_RING_NOCHECK( 4 ); |
| OUT_RING( HC_HEADER2 ); |
| OUT_RING( (HC_ParaType_NotTex << 16) ); |
| OUT_RING( HC_DUMMY ); |
| OUT_RING( HC_DUMMY ); |
| ADVANCE_RING(); |
| break; |
| case 24: |
| BEGIN_RING_NOCHECK( 10 ); |
| OUT_RING( HC_HEADER2 ); |
| OUT_RING( (HC_ParaType_NotTex << 16) ); |
| OUT_RING( HC_DUMMY ); |
| OUT_RING( HC_DUMMY ); |
| OUT_RING( HC_DUMMY ); |
| OUT_RING( HC_DUMMY ); |
| OUT_RING( HC_DUMMY ); |
| OUT_RING( HC_DUMMY ); |
| OUT_RING( HC_DUMMY ); |
| OUT_RING( HC_DUMMY ); |
| ADVANCE_RING(); |
| break; |
| case 0: |
| break; |
| default: |
| if (VIA_DEBUG & DEBUG_IOCTL) |
| fprintf(stderr, "%s: unaligned value for vmesa->dmaLow: %x\n", |
| __FUNCTION__, vmesa->dmaLow); |
| } |
| |
| vmesa->lastDma = vmesa->lastBreadcrumbWrite; |
| |
| if (VIA_DEBUG & DEBUG_DMA) |
| dump_dma( vmesa ); |
| |
| if (flags & VIA_NO_CLIPRECTS) { |
| if (0) fprintf(stderr, "%s VIA_NO_CLIPRECTS\n", __FUNCTION__); |
| assert(vmesa->dmaCliprectAddr == ~0); |
| fire_buffer( vmesa ); |
| } |
| else if (vmesa->dmaCliprectAddr == ~0) { |
| /* Contains only state. Could just dump the packet? |
| */ |
| if (0) fprintf(stderr, "%s: no dmaCliprectAddr\n", __FUNCTION__); |
| if (0) fire_buffer( vmesa ); |
| } |
| else if (vmesa->numClipRects) { |
| drm_clip_rect_t *pbox = vmesa->pClipRects; |
| __DRIdrawablePrivate *dPriv = vmesa->driDrawable; |
| struct via_renderbuffer *const vrb = |
| (struct via_renderbuffer *) dPriv->driverPrivate; |
| |
| for (i = 0; i < vmesa->numClipRects; i++) { |
| drm_clip_rect_t b; |
| |
| b.x1 = pbox[i].x1; |
| b.x2 = pbox[i].x2; |
| b.y1 = pbox[i].y1; |
| b.y2 = pbox[i].y2; |
| |
| if (vmesa->scissor && |
| !intersect_rect(&b, &b, &vmesa->scissorRect)) |
| continue; |
| |
| via_emit_cliprect(vmesa, &b); |
| |
| if (fire_buffer(vmesa) != 0) { |
| dump_dma( vmesa ); |
| goto done; |
| } |
| } |
| } else { |
| if (0) fprintf(stderr, "%s: no cliprects\n", __FUNCTION__); |
| UNLOCK_HARDWARE(vmesa); |
| sched_yield(); |
| LOCK_HARDWARE(vmesa); |
| } |
| |
| done: |
| /* Reset vmesa vars: |
| */ |
| vmesa->dmaLow = 0; |
| vmesa->dmaCliprectAddr = ~0; |
| vmesa->newEmitState = ~0; |
| } |
| |
| void viaWrapPrimitive( struct via_context *vmesa ) |
| { |
| GLenum renderPrimitive = vmesa->renderPrimitive; |
| GLenum hwPrimitive = vmesa->hwPrimitive; |
| |
| if (VIA_DEBUG & DEBUG_PRIMS) fprintf(stderr, "%s\n", __FUNCTION__); |
| |
| if (vmesa->dmaLastPrim) |
| viaFinishPrimitive( vmesa ); |
| |
| viaFlushDma(vmesa); |
| |
| if (renderPrimitive != GL_POLYGON + 1) |
| viaRasterPrimitive( vmesa->glCtx, |
| renderPrimitive, |
| hwPrimitive ); |
| |
| } |
| |
| void viaFlushDma(struct via_context *vmesa) |
| { |
| if (vmesa->dmaLow) { |
| assert(!vmesa->dmaLastPrim); |
| |
| LOCK_HARDWARE(vmesa); |
| viaFlushDmaLocked(vmesa, 0); |
| UNLOCK_HARDWARE(vmesa); |
| } |
| } |
| |
| static void viaFlush(GLcontext *ctx) |
| { |
| struct via_context *vmesa = VIA_CONTEXT(ctx); |
| VIA_FLUSH_DMA(vmesa); |
| } |
| |
| static void viaFinish(GLcontext *ctx) |
| { |
| struct via_context *vmesa = VIA_CONTEXT(ctx); |
| VIA_FLUSH_DMA(vmesa); |
| viaWaitIdle(vmesa, GL_FALSE); |
| } |
| |
| static void viaClearStencil(GLcontext *ctx, int s) |
| { |
| return; |
| } |
| |
| void viaInitIoctlFuncs(GLcontext *ctx) |
| { |
| ctx->Driver.Flush = viaFlush; |
| ctx->Driver.Clear = viaClear; |
| ctx->Driver.Finish = viaFinish; |
| ctx->Driver.ClearStencil = viaClearStencil; |
| } |
| |
| |
| |