xserver-multidpi/hw/kdrive/ati/ati_draw.c
Eric Anholt c8eb20a08e - Add new Composite hook for kdrive drivers, which only ensures that the
pixmaps are offscreen and don't have alpha maps. It is the last case
    checked before going to software fallback
- Use the new Composite hook in the ati driver to implement acceleration of
    most Composites that get done in an xcompmgr environment on r100 series
    cards. It is only available when using the DRM. There are still some
    corruption issues, but the DRI is still non-default and I need to get
    this into version control.
2003-12-30 08:23:56 +00:00

556 lines
14 KiB
C

/*
* $Id$
*
* Copyright © 2003 Eric Anholt
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of Eric Anholt not be used in
* advertising or publicity pertaining to distribution of the software without
* specific, written prior permission. Eric Anholt makes no
* representations about the suitability of this software for any purpose. It
* is provided "as is" without express or implied warranty.
*
* ERIC ANHOLT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL ERIC ANHOLT BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Header$ */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "ati.h"
#include "ati_reg.h"
#include "ati_draw.h"
#ifdef USE_DRI
#include "radeon_common.h"
#include "r128_common.h"
#include "ati_sarea.h"
#endif /* USE_DRI */
CARD8 ATISolidRop[16] = {
/* GXclear */ 0x00, /* 0 */
/* GXand */ 0xa0, /* src AND dst */
/* GXandReverse */ 0x50, /* src AND NOT dst */
/* GXcopy */ 0xf0, /* src */
/* GXandInverted*/ 0x0a, /* NOT src AND dst */
/* GXnoop */ 0xaa, /* dst */
/* GXxor */ 0x5a, /* src XOR dst */
/* GXor */ 0xfa, /* src OR dst */
/* GXnor */ 0x05, /* NOT src AND NOT dst */
/* GXequiv */ 0xa5, /* NOT src XOR dst */
/* GXinvert */ 0x55, /* NOT dst */
/* GXorReverse */ 0xf5, /* src OR NOT dst */
/* GXcopyInverted*/ 0x0f, /* NOT src */
/* GXorInverted */ 0xaf, /* NOT src OR dst */
/* GXnand */ 0x5f, /* NOT src OR NOT dst */
/* GXset */ 0xff, /* 1 */
};
CARD8 ATIBltRop[16] = {
/* GXclear */ 0x00, /* 0 */
/* GXand */ 0x88, /* src AND dst */
/* GXandReverse */ 0x44, /* src AND NOT dst */
/* GXcopy */ 0xcc, /* src */
/* GXandInverted*/ 0x22, /* NOT src AND dst */
/* GXnoop */ 0xaa, /* dst */
/* GXxor */ 0x66, /* src XOR dst */
/* GXor */ 0xee, /* src OR dst */
/* GXnor */ 0x11, /* NOT src AND NOT dst */
/* GXequiv */ 0x99, /* NOT src XOR dst */
/* GXinvert */ 0x55, /* NOT dst */
/* GXorReverse */ 0xdd, /* src OR NOT dst */
/* GXcopyInverted*/ 0x33, /* NOT src */
/* GXorInverted */ 0xbb, /* NOT src OR dst */
/* GXnand */ 0x77, /* NOT src OR NOT dst */
/* GXset */ 0xff, /* 1 */
};
static CARD32 R128BlendOp[] = {
/* Clear */
R128_ALPHA_BLEND_SRC_ZERO | R128_ALPHA_BLEND_DST_ZERO,
/* Src */
R128_ALPHA_BLEND_SRC_ONE | R128_ALPHA_BLEND_DST_ZERO,
/* Dst */
R128_ALPHA_BLEND_SRC_ZERO | R128_ALPHA_BLEND_DST_ONE,
/* Over */
R128_ALPHA_BLEND_SRC_ONE | R128_ALPHA_BLEND_DST_INVSRCALPHA,
/* OverReverse */
R128_ALPHA_BLEND_SRC_INVDSTALPHA | R128_ALPHA_BLEND_DST_ONE,
/* In */
R128_ALPHA_BLEND_SRC_DSTALPHA | R128_ALPHA_BLEND_DST_ZERO,
/* InReverse */
R128_ALPHA_BLEND_SRC_ZERO | R128_ALPHA_BLEND_DST_SRCALPHA,
/* Out */
R128_ALPHA_BLEND_SRC_INVDSTALPHA | R128_ALPHA_BLEND_DST_ZERO,
/* OutReverse */
R128_ALPHA_BLEND_SRC_ZERO | R128_ALPHA_BLEND_DST_INVSRCALPHA,
/* Atop */
R128_ALPHA_BLEND_SRC_DSTALPHA | R128_ALPHA_BLEND_DST_INVSRCALPHA,
/* AtopReverse */
R128_ALPHA_BLEND_SRC_INVDSTALPHA | R128_ALPHA_BLEND_DST_SRCALPHA,
/* Xor */
R128_ALPHA_BLEND_SRC_INVDSTALPHA | R128_ALPHA_BLEND_DST_INVSRCALPHA,
/* Add */
R128_ALPHA_BLEND_SRC_ONE | R128_ALPHA_BLEND_DST_ONE,
};
int copydx, copydy;
int fifo_size;
ATIScreenInfo *accel_atis;
int src_pitch;
int src_offset;
int src_bpp;
/* If is_24bpp is set, then we are using the accelerator in 8-bit mode due
* to it being broken for 24bpp, so coordinates have to be multiplied by 3.
*/
int is_24bpp;
static void
ATIWaitAvailMMIO(int n)
{
ATICardInfo *atic = accel_atis->atic;
char *mmio = atic->reg_base;
if (fifo_size >= n) {
fifo_size -= n;
return;
}
if (atic->is_radeon) {
do {
fifo_size = MMIO_IN32(mmio, RADEON_REG_RBBM_STATUS) &
RADEON_RBBM_FIFOCNT_MASK;
} while (fifo_size < n);
} else {
do {
fifo_size = MMIO_IN32(mmio, R128_REG_GUI_STAT) & 0xfff;
} while (fifo_size < n);
}
fifo_size -= n;
}
static void
RadeonWaitIdle(void)
{
ATIScreenInfo *atis = accel_atis;
ATICardInfo *atic = atis->atic;
char *mmio = atic->reg_base;
CARD32 temp;
#ifdef USE_DRI
if (atis->using_dma) {
int ret;
do {
ret = drmCommandNone(atic->drmFd, DRM_RADEON_CP_IDLE);
} while (ret == -EBUSY);
if (ret != 0)
ErrorF("Failed to idle DMA, returned %d\n", ret);
}
#endif /* USE_DRI */
/* Wait for the engine to go idle */
ATIWaitAvailMMIO(64);
while ((MMIO_IN32(mmio, RADEON_REG_RBBM_STATUS) &
RADEON_RBBM_ACTIVE) != 0)
;
/* Flush pixel cache */
temp = MMIO_IN32(mmio, RADEON_REG_RB2D_DSTCACHE_CTLSTAT);
temp |= RADEON_RB2D_DC_FLUSH_ALL;
MMIO_OUT32(mmio, RADEON_REG_RB2D_DSTCACHE_CTLSTAT, temp);
while ((MMIO_IN32(mmio, RADEON_REG_RB2D_DSTCACHE_CTLSTAT) &
RADEON_RB2D_DC_BUSY) != 0)
;
}
static void
R128WaitIdle(void)
{
ATIScreenInfo *atis = accel_atis;
ATICardInfo *atic = atis->atic;
char *mmio = atic->reg_base;
CARD32 temp;
int tries;
#ifdef USE_DRI
if (atis->using_dma) {
int ret;
do {
ret = drmCommandNone(atic->drmFd, DRM_R128_CCE_IDLE);
} while (ret == -EBUSY);
if (ret != 0)
ErrorF("Failed to idle DMA, returned %d\n", ret);
}
#endif /* USE_DRI */
ATIWaitAvailMMIO(64);
tries = 1000000;
while (tries--) {
if ((MMIO_IN32(mmio, R128_REG_GUI_STAT) & R128_GUI_ACTIVE) == 0)
break;
}
temp = MMIO_IN32(mmio, R128_REG_PC_NGUI_CTLSTAT);
MMIO_OUT32(mmio, R128_REG_PC_NGUI_CTLSTAT, temp | 0xff);
tries = 1000000;
while (tries--) {
if ((MMIO_IN32(mmio, R128_REG_PC_NGUI_CTLSTAT) & R128_PC_BUSY) !=
R128_PC_BUSY)
break;
}
}
void
ATIWaitIdle(void)
{
ATIScreenInfo *atis = accel_atis;
ATICardInfo *atic = atis->atic;
#ifdef USE_DRI
/* Dispatch any accumulated commands first. */
if (atis->using_dma && atis->indirectBuffer != NULL)
ATIDMAFlushIndirect(0);
#endif /* USE_DRI */
if (atic->is_radeon)
RadeonWaitIdle();
else
R128WaitIdle();
}
#ifdef USE_DRI
void ATIDMAStart(ScreenPtr pScreen)
{
KdScreenPriv(pScreen);
ATICardInfo(pScreenPriv);
ATIScreenInfo(pScreenPriv);
int ret;
if (atic->is_radeon)
ret = drmCommandNone(atic->drmFd, DRM_RADEON_CP_START);
else
ret = drmCommandNone(atic->drmFd, DRM_R128_CCE_START);
if (ret == 0)
atis->using_dma = TRUE;
else
ErrorF("%s: DMA start returned %d\n", __FUNCTION__, ret);
}
/* Attempts to idle the DMA engine, and stops it. Note that the ioctl is the
* same for both R128 and Radeon, so we can just use the name of one of them.
*/
void ATIDMAStop(ScreenPtr pScreen)
{
KdScreenPriv(pScreen);
ATICardInfo(pScreenPriv);
ATIScreenInfo(pScreenPriv);
drmRadeonCPStop stop;
int ret;
stop.flush = 1;
stop.idle = 1;
ret = drmCommandWrite(atic->drmFd, DRM_RADEON_CP_STOP, &stop,
sizeof(drmRadeonCPStop));
if (ret != 0 && errno == EBUSY) {
ErrorF("Failed to idle the DMA engine\n");
stop.idle = 0;
ret = drmCommandWrite(atic->drmFd, DRM_RADEON_CP_STOP, &stop,
sizeof(drmRadeonCPStop));
}
atis->using_dma = FALSE;
}
/* The R128 and Radeon Indirect ioctls differ only in the ioctl number */
void ATIDMADispatchIndirect(Bool discard)
{
ATIScreenInfo *atis = accel_atis;
ATICardInfo *atic = atis->atic;
drmBufPtr buffer = atis->indirectBuffer;
drmR128Indirect indirect;
int cmd;
indirect.idx = buffer->idx;
indirect.start = atis->indirectStart;
indirect.end = buffer->used;
indirect.discard = discard;
cmd = atic->is_radeon ? DRM_RADEON_INDIRECT : DRM_R128_INDIRECT;
drmCommandWriteRead(atic->drmFd, cmd, &indirect,
sizeof(drmR128Indirect));
}
/* Flush the indirect buffer to the kernel for submission to the card */
void ATIDMAFlushIndirect(Bool discard)
{
ATIScreenInfo *atis = accel_atis;
drmBufPtr buffer = atis->indirectBuffer;
if (buffer == NULL)
return;
if ((atis->indirectStart == buffer->used) && !discard)
return;
ATIDMADispatchIndirect(discard);
if (discard) {
atis->indirectBuffer = ATIDMAGetBuffer();
atis->indirectStart = 0;
} else {
/* Start on a double word boundary */
atis->indirectStart = buffer->used = (buffer->used + 7) & ~7;
}
}
/* Get an indirect buffer for the DMA 2D acceleration commands */
drmBufPtr ATIDMAGetBuffer()
{
ATIScreenInfo *atis = accel_atis;
ATICardInfo *atic = atis->atic;
drmDMAReq dma;
drmBufPtr buf = NULL;
int indx = 0;
int size = 0;
int ret;
dma.context = atis->serverContext;
dma.send_count = 0;
dma.send_list = NULL;
dma.send_sizes = NULL;
dma.flags = 0;
dma.request_count = 1;
if (atis->atic->is_radeon)
dma.request_size = RADEON_BUFFER_SIZE;
else
dma.request_size = R128_BUFFER_SIZE;
dma.request_list = &indx;
dma.request_sizes = &size;
dma.granted_count = 0;
do {
ret = drmDMA(atic->drmFd, &dma);
} while (ret != 0);
buf = &atis->buffers->list[indx];
buf->used = 0;
return buf;
}
/* The hardware has a cache on the memory controller for writes to the
* destination, which I guess is separate for 2d and 3d. So, when switching
* between 2d and 3d you need to wait for idle and for the cache to clean.
*/
void
RadeonSwitchTo2D(void)
{
ATIScreenInfo *atis = accel_atis;
RING_LOCALS;
BEGIN_RING(2);
OUT_RING(DMA_PACKET0(RADEON_REG_WAIT_UNTIL, 0));
OUT_RING(RADEON_WAIT_HOST_IDLECLEAN | RADEON_WAIT_3D_IDLECLEAN);
ADVANCE_RING();
}
void
RadeonSwitchTo3D(void)
{
ATIScreenInfo *atis = accel_atis;
RING_LOCALS;
BEGIN_RING(2);
OUT_RING(DMA_PACKET0(RADEON_REG_WAIT_UNTIL, 0));
OUT_RING(RADEON_WAIT_HOST_IDLECLEAN | RADEON_WAIT_2D_IDLECLEAN);
ADVANCE_RING();
}
#endif /* USE_DRI */
static Bool
R128GetDatatypePict(CARD32 format, CARD32 *type)
{
switch (format) {
case PICT_a8r8g8b8:
*type = R128_DATATYPE_ARGB_8888;
return TRUE;
case PICT_r5g6b5:
*type = R128_DATATYPE_RGB_565;
return TRUE;
}
ErrorF ("Unsupported format: %x\n", format);
return FALSE;
}
/* Assumes that depth 15 and 16 can be used as depth 16, which is okay since we
* require src and dest datatypes to be equal.
*/
static Bool
ATIGetDatatypeBpp(int bpp, CARD32 *type)
{
is_24bpp = FALSE;
switch (bpp) {
case 8:
*type = R128_DATATYPE_C8;
return TRUE;
case 16:
*type = R128_DATATYPE_RGB_565;
return TRUE;
case 24:
*type = R128_DATATYPE_C8;
is_24bpp = TRUE;
return TRUE;
case 32:
*type = R128_DATATYPE_ARGB_8888;
return TRUE;
default:
ErrorF("Unsupported bpp: %x\n", bpp);
return FALSE;
}
}
#ifdef USE_DRI
#define USE_DMA
#include "ati_drawtmp.h"
#include "r128_blendtmp.h"
#endif /* USE_DRI */
#undef USE_DMA
#include "ati_drawtmp.h"
#include "r128_blendtmp.h"
static void
ATIDoneSolid(void)
{
}
static void
ATIDoneCopy(void)
{
}
Bool
ATIDrawInit(ScreenPtr pScreen)
{
KdScreenPriv(pScreen);
ATIScreenInfo(pScreenPriv);
ATICardInfo(pScreenPriv);
ErrorF("Screen: %d/%d depth/bpp\n", pScreenPriv->screen->fb[0].depth,
pScreenPriv->screen->fb[0].bitsPerPixel);
#ifdef USE_DRI
if (atis->using_dri)
ATIDMAStart(pScreen);
else {
if (ATIDRIScreenInit(pScreen))
atis->using_dri = TRUE;
}
#endif /* USE_DRI */
memset(&atis->kaa, 0, sizeof(KaaScreenInfoRec));
#ifdef USE_DRI
if (atis->using_dma) {
atis->kaa.PrepareSolid = ATIPrepareSolidDMA;
atis->kaa.Solid = ATISolidDMA;
atis->kaa.PrepareCopy = ATIPrepareCopyDMA;
atis->kaa.Copy = ATICopyDMA;
if (!atic->is_radeon) {
atis->kaa.PrepareBlend = R128PrepareBlendDMA;
atis->kaa.Blend = R128BlendDMA;
atis->kaa.DoneBlend = R128DoneBlendDMA;
} else if (!atic->is_r200) {
atis->kaa.PrepareBlend = RadeonPrepareBlend;
atis->kaa.Blend = RadeonBlend;
atis->kaa.DoneBlend = RadeonDoneBlend;
atis->kaa.PrepareComposite = RadeonPrepareComposite;
atis->kaa.Composite = RadeonComposite;
atis->kaa.DoneComposite = RadeonDoneComposite;
}
} else {
#else
{
#endif /* USE_DRI */
atis->kaa.PrepareSolid = ATIPrepareSolidMMIO;
atis->kaa.Solid = ATISolidMMIO;
atis->kaa.PrepareCopy = ATIPrepareCopyMMIO;
atis->kaa.Copy = ATICopyMMIO;
if (!atic->is_radeon) {
atis->kaa.PrepareBlend = R128PrepareBlendMMIO;
atis->kaa.Blend = R128BlendMMIO;
atis->kaa.DoneBlend = R128DoneBlendMMIO;
}
}
atis->kaa.DoneSolid = ATIDoneSolid;
atis->kaa.DoneCopy = ATIDoneCopy;
atis->kaa.flags = KAA_OFFSCREEN_PIXMAPS;
if (atic->is_radeon) {
atis->kaa.offscreenByteAlign = 1024;
atis->kaa.offscreenPitch = 1024;
} else {
atis->kaa.offscreenByteAlign = 32;
/* Pitch alignment is in sets of 8 pixels, and we need to cover
* 32bpp, so 32 bytes.
*/
atis->kaa.offscreenPitch = 32;
}
if (!kaaDrawInit(pScreen, &atis->kaa))
return FALSE;
return TRUE;
}
void
ATIDrawEnable(ScreenPtr pScreen)
{
KdMarkSync(pScreen);
}
void
ATIDrawDisable(ScreenPtr pScreen)
{
}
void
ATIDrawFini(ScreenPtr pScreen)
{
#ifdef USE_DRI
KdScreenPriv(pScreen);
ATIScreenInfo(pScreenPriv);
if (atis->using_dma)
ATIDMAStop(pScreen);
if (atis->using_dri)
ATIDRICloseScreen(pScreen);
#endif /* USE_DRI */
kaaDrawFini(pScreen);
}
void
ATIDrawSync(ScreenPtr pScreen)
{
KdScreenPriv(pScreen);
ATIScreenInfo(pScreenPriv);
accel_atis = atis;
ATIWaitIdle();
}