Home | History | Annotate | Line # | Download | only in src
      1 /*
      2  * Copyright 1999, 2000 ATI Technologies Inc., Markham, Ontario,
      3  *                      Precision Insight, Inc., Cedar Park, Texas, and
      4  *                      VA Linux Systems Inc., Fremont, California.
      5  *
      6  * All Rights Reserved.
      7  *
      8  * Permission is hereby granted, free of charge, to any person obtaining
      9  * a copy of this software and associated documentation files (the
     10  * "Software"), to deal in the Software without restriction, including
     11  * without limitation on the rights to use, copy, modify, merge,
     12  * publish, distribute, sublicense, and/or sell copies of the Software,
     13  * and to permit persons to whom the Software is furnished to do so,
     14  * subject to the following conditions:
     15  *
     16  * The above copyright notice and this permission notice (including the
     17  * next paragraph) shall be included in all copies or substantial
     18  * portions of the Software.
     19  *
     20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     21  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     22  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     23  * NON-INFRINGEMENT.  IN NO EVENT SHALL ATI, PRECISION INSIGHT, VA LINUX
     24  * SYSTEMS AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
     25  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
     26  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     27  * OTHER DEALINGS IN THE SOFTWARE.
     28  */
     29 
     30 #ifdef HAVE_CONFIG_H
     31 #include "config.h"
     32 #endif
     33 
     34 /*
     35  * Authors:
     36  *   Rickard E. Faith <faith (at) valinux.com>
     37  *   Kevin E. Martin <martin (at) valinux.com>
     38  *   Alan Hourihane <alanh (at) fairlite.demon.co.uk>
     39  *
     40  * Credits:
     41  *
     42  *   Thanks to Alan Hourihane <alanh (at) fairlite.demon..co.uk> and SuSE for
     43  *   providing source code to their 3.3.x Rage 128 driver.  Portions of
     44  *   this file are based on the acceleration code for that driver.
     45  *
     46  * References:
     47  *
     48  *   RAGE 128 VR/ RAGE 128 GL Register Reference Manual (Technical
     49  *   Reference Manual P/N RRG-G04100-C Rev. 0.04), ATI Technologies: April
     50  *   1999.
     51  *
     52  *   RAGE 128 Software Development Manual (Technical Reference Manual P/N
     53  *   SDK-G04000 Rev. 0.01), ATI Technologies: June 1999.
     54  *
     55  * Notes on unimplemented XAA optimizations:
     56  *
     57  *   SetClipping:   The Rage128 doesn't support the full 16bit registers needed
     58  *                  for XAA clip rect support.
     59  *   SolidFillTrap: This will probably work if we can compute the correct
     60  *                  Bresenham error values.
     61  *   TwoPointLine:  The Rage 128 supports Bresenham lines instead.
     62  *   DashedLine with non-power-of-two pattern length: Apparently, there is
     63  *                  no way to set the length of the pattern -- it is always
     64  *                  assumed to be 8 or 32 (or 1024?).
     65  *   ScreenToScreenColorExpandFill: See p. 4-17 of the Technical Reference
     66  *                  Manual where it states that monochrome expansion of frame
     67  *                  buffer data is not supported.
     68  *   CPUToScreenColorExpandFill, direct: The implementation here uses a hybrid
     69  *                  direct/indirect method.  If we had more data registers,
     70  *                  then we could do better.  If XAA supported a trigger write
     71  *                  address, the code would be simpler.
     72  * (Alan Hourihane) Update. We now use purely indirect and clip the full
     73  *                  rectangle. Seems as the direct method has some problems
     74  *                  with this, although this indirect method is much faster
     75  *                  than the old method of setting up the engine per scanline.
     76  *                  This code was the basis of the Radeon work we did.
     77  *   Color8x8PatternFill: Apparently, an 8x8 color brush cannot take an 8x8
     78  *                  pattern from frame buffer memory.
     79  *   ImageWrites:   See CPUToScreenColorExpandFill.
     80  *
     81  */
     82 
     83 #define R128_TRAPEZOIDS 0       /* Trapezoids don't work               */
     84 
     85 				/* Driver data structures */
     86 #include <errno.h>
     87 
     88 #include "r128.h"
     89 #include "r128_reg.h"
     90 #include "r128_probe.h"
     91 #ifdef R128DRI
     92 #include "r128_sarea.h"
     93 #define _XF86DRI_SERVER_
     94 #include "r128_dri.h"
     95 #include "r128_common.h"
     96 #endif
     97 
     98 				/* Line support */
     99 #include "miline.h"
    100 
    101 				/* X and server generic header files */
    102 #include "xf86.h"
    103 
    104 
    105 /* Flush all dirty data in the Pixel Cache to memory. */
    106 void R128EngineFlush(ScrnInfoPtr pScrn)
    107 {
    108     R128InfoPtr   info      = R128PTR(pScrn);
    109     unsigned char *R128MMIO = info->MMIO;
    110     int           i;
    111 
    112     OUTREGP(R128_PC_NGUI_CTLSTAT, R128_PC_FLUSH_ALL, ~R128_PC_FLUSH_ALL);
    113     for (i = 0; i < R128_TIMEOUT; i++) {
    114 	if (!(INREG(R128_PC_NGUI_CTLSTAT) & R128_PC_BUSY)) break;
    115     }
    116 }
    117 
    118 /* Reset graphics card to known state. */
    119 void R128EngineReset(ScrnInfoPtr pScrn)
    120 {
    121     R128InfoPtr   info      = R128PTR(pScrn);
    122     unsigned char *R128MMIO = info->MMIO;
    123     uint32_t      clock_cntl_index;
    124     uint32_t      mclk_cntl;
    125     uint32_t      gen_reset_cntl;
    126 
    127     R128EngineFlush(pScrn);
    128 
    129     clock_cntl_index = INREG(R128_CLOCK_CNTL_INDEX);
    130     mclk_cntl        = INPLL(pScrn, R128_MCLK_CNTL);
    131 
    132     OUTPLL(R128_MCLK_CNTL, mclk_cntl | R128_FORCE_GCP | R128_FORCE_PIPE3D_CP);
    133 
    134     gen_reset_cntl   = INREG(R128_GEN_RESET_CNTL);
    135 
    136     OUTREG(R128_GEN_RESET_CNTL, gen_reset_cntl | R128_SOFT_RESET_GUI);
    137     INREG(R128_GEN_RESET_CNTL);
    138     OUTREG(R128_GEN_RESET_CNTL,
    139 	gen_reset_cntl & (uint32_t)(~R128_SOFT_RESET_GUI));
    140     INREG(R128_GEN_RESET_CNTL);
    141 
    142     OUTPLL(R128_MCLK_CNTL,        mclk_cntl);
    143     OUTREG(R128_CLOCK_CNTL_INDEX, clock_cntl_index);
    144     OUTREG(R128_GEN_RESET_CNTL,   gen_reset_cntl);
    145 }
    146 
    147 /* The FIFO has 64 slots.  This routines waits until at least `entries' of
    148    these slots are empty. */
    149 void R128WaitForFifoFunction(ScrnInfoPtr pScrn, int entries)
    150 {
    151     R128InfoPtr   info      = R128PTR(pScrn);
    152     unsigned char *R128MMIO = info->MMIO;
    153     int           i;
    154 
    155     for (;;) {
    156 	for (i = 0; i < R128_TIMEOUT; i++) {
    157 	    info->fifo_slots = INREG(R128_GUI_STAT) & R128_GUI_FIFOCNT_MASK;
    158 	    if (info->fifo_slots >= entries) return;
    159 	}
    160 
    161     DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
    162                     "FIFO timed out: %lu entries, "
    163                     "stat = 0x%08lx, probe = 0x%08lx\n",
    164                     INREG(R128_GUI_STAT) & R128_GUI_FIFOCNT_MASK,
    165                     INREG(R128_GUI_STAT),
    166                     INREG(R128_GUI_PROBE)));
    167 	xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
    168 		   "FIFO timed out, resetting engine...\n");
    169 	R128EngineReset(pScrn);
    170 #ifdef R128DRI
    171 	R128CCE_RESET(pScrn, info);
    172 	if (info->directRenderingEnabled) {
    173 	    R128CCE_START(pScrn, info);
    174 	}
    175 #endif
    176     }
    177 }
    178 
    179 /* Wait for the graphics engine to be completely idle: the FIFO has
    180    drained, the Pixel Cache is flushed, and the engine is idle.  This is a
    181    standard "sync" function that will make the hardware "quiescent". */
    182 void R128WaitForIdle(ScrnInfoPtr pScrn)
    183 {
    184     R128InfoPtr   info      = R128PTR(pScrn);
    185     unsigned char *R128MMIO = info->MMIO;
    186     int           i;
    187 
    188     R128WaitForFifoFunction(pScrn, 64);
    189 
    190     for (;;) {
    191 	for (i = 0; i < R128_TIMEOUT; i++) {
    192 	    if (!(INREG(R128_GUI_STAT) & R128_GUI_ACTIVE)) {
    193 		R128EngineFlush(pScrn);
    194 		return;
    195 	    }
    196 	}
    197 
    198     DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
    199                         "Idle timed out: %lu entries, "
    200                         "stat = 0x%08lx, probe = 0x%08lx\n",
    201                         INREG(R128_GUI_STAT) & R128_GUI_FIFOCNT_MASK,
    202                         INREG(R128_GUI_STAT),
    203                         INREG(R128_GUI_PROBE)));
    204 	xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
    205 		   "Idle timed out, resetting engine...\n");
    206 #ifdef R128DRI
    207         R128CCE_STOP(pScrn, info);
    208 #endif
    209 	R128EngineReset(pScrn);
    210 #ifdef R128DRI
    211 	R128CCE_RESET(pScrn, info);
    212 	if (info->directRenderingEnabled) {
    213 	    R128CCE_START(pScrn, info);
    214 	}
    215 #endif
    216     }
    217 }
    218 
    219 #ifdef R128DRI
    220 /* Wait until the CCE is completely idle: the FIFO has drained and the
    221  * CCE is idle.
    222  */
    223 void R128CCEWaitForIdle(ScrnInfoPtr pScrn)
    224 {
    225     R128InfoPtr info = R128PTR(pScrn);
    226     int         ret, i;
    227 
    228     FLUSH_RING();
    229 
    230     for (;;) {
    231         i = 0;
    232         do {
    233             ret = drmCommandNone(info->drmFD, DRM_R128_CCE_IDLE);
    234         } while ( ret && errno == EBUSY && i++ < (R128_IDLE_RETRY * R128_IDLE_RETRY) );
    235 
    236 	if (ret && ret != -EBUSY) {
    237 	    xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
    238 		       "%s: CCE idle %d\n", __FUNCTION__, ret);
    239 	}
    240 
    241 	if (i > R128_IDLE_RETRY) {
    242 	    xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
    243 		       "%s: (DEBUG) CCE idle took i = %d\n", __FUNCTION__, i);
    244 	}
    245 
    246 	if (ret == 0) return;
    247 
    248 	xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
    249 		   "Idle timed out, resetting engine...\n");
    250 	R128CCE_STOP(pScrn, info);
    251 	R128EngineReset(pScrn);
    252 
    253 	/* Always restart the engine when doing CCE 2D acceleration */
    254 	R128CCE_RESET(pScrn, info);
    255 	R128CCE_START(pScrn, info);
    256     }
    257 }
    258 
    259 int R128CCEStop(ScrnInfoPtr pScrn)
    260 {
    261     R128InfoPtr    info = R128PTR(pScrn);
    262     drmR128CCEStop stop;
    263     int            ret, i;
    264 
    265     stop.flush = 1;
    266     stop.idle  = 1;
    267 
    268     ret = drmCommandWrite( info->drmFD, DRM_R128_CCE_STOP,
    269                            &stop, sizeof(drmR128CCEStop) );
    270 
    271     if ( ret == 0 ) {
    272         return 0;
    273     } else if ( errno != EBUSY ) {
    274         return -errno;
    275     }
    276 
    277     stop.flush = 0;
    278 
    279     i = 0;
    280     do {
    281         ret = drmCommandWrite( info->drmFD, DRM_R128_CCE_STOP,
    282                                &stop, sizeof(drmR128CCEStop) );
    283     } while ( ret && errno == EBUSY && i++ < R128_IDLE_RETRY );
    284 
    285     if ( ret == 0 ) {
    286         return 0;
    287     } else if ( errno != EBUSY ) {
    288         return -errno;
    289     }
    290 
    291     stop.idle = 0;
    292 
    293     if ( drmCommandWrite( info->drmFD, DRM_R128_CCE_STOP,
    294                           &stop, sizeof(drmR128CCEStop) )) {
    295         return -errno;
    296     } else {
    297         return 0;
    298     }
    299 }
    300 
    301 #endif
    302 
    303 
    304 /* Initialize the acceleration hardware. */
    305 void R128EngineInit(ScrnInfoPtr pScrn)
    306 {
    307     R128InfoPtr   info      = R128PTR(pScrn);
    308     unsigned char *R128MMIO = info->MMIO;
    309 
    310     DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
    311                         "EngineInit (%d/%d)\n",
    312                         info->CurrentLayout.pixel_code,
    313                         info->CurrentLayout.bitsPerPixel));
    314 
    315     OUTREG(R128_SCALE_3D_CNTL, 0);
    316     R128EngineReset(pScrn);
    317 
    318     switch (info->CurrentLayout.pixel_code) {
    319     case 8:  info->datatype = 2; break;
    320     case 15: info->datatype = 3; break;
    321     case 16: info->datatype = 4; break;
    322     case 24: info->datatype = 5; break;
    323     case 32: info->datatype = 6; break;
    324     default:
    325     DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
    326                         "Unknown depth/bpp = %d/%d (code = %d)\n",
    327                         info->CurrentLayout.depth,
    328                         info->CurrentLayout.bitsPerPixel,
    329                         info->CurrentLayout.pixel_code));
    330     }
    331     info->pitch = (info->CurrentLayout.displayWidth / 8) * (info->CurrentLayout.pixel_bytes == 3 ? 3 : 1);
    332 
    333     DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
    334                         "Pitch for acceleration = %d\n", info->pitch));
    335 
    336     R128WaitForFifo(pScrn, 2);
    337     OUTREG(R128_DEFAULT_OFFSET, pScrn->fbOffset);
    338     OUTREG(R128_DEFAULT_PITCH,  info->pitch);
    339 
    340     R128WaitForFifo(pScrn, 4);
    341     OUTREG(R128_AUX_SC_CNTL,             0);
    342     OUTREG(R128_DEFAULT_SC_BOTTOM_RIGHT, (R128_DEFAULT_SC_RIGHT_MAX
    343 					  | R128_DEFAULT_SC_BOTTOM_MAX));
    344     OUTREG(R128_SC_TOP_LEFT,             0);
    345     OUTREG(R128_SC_BOTTOM_RIGHT,         (R128_DEFAULT_SC_RIGHT_MAX
    346 					  | R128_DEFAULT_SC_BOTTOM_MAX));
    347 
    348     info->dp_gui_master_cntl = ((info->datatype << R128_GMC_DST_DATATYPE_SHIFT)
    349 				| R128_GMC_CLR_CMP_CNTL_DIS
    350 				| R128_GMC_AUX_CLIP_DIS);
    351     R128WaitForFifo(pScrn, 1);
    352     OUTREG(R128_DP_GUI_MASTER_CNTL, (info->dp_gui_master_cntl
    353 				     | R128_GMC_BRUSH_SOLID_COLOR
    354 				     | R128_GMC_SRC_DATATYPE_COLOR));
    355 
    356     R128WaitForFifo(pScrn, 8);
    357     OUTREG(R128_DST_BRES_ERR,      0);
    358     OUTREG(R128_DST_BRES_INC,      0);
    359     OUTREG(R128_DST_BRES_DEC,      0);
    360     OUTREG(R128_DP_BRUSH_FRGD_CLR, 0xffffffff);
    361     OUTREG(R128_DP_BRUSH_BKGD_CLR, 0x00000000);
    362     OUTREG(R128_DP_SRC_FRGD_CLR,   0xffffffff);
    363     OUTREG(R128_DP_SRC_BKGD_CLR,   0x00000000);
    364     OUTREG(R128_DP_WRITE_MASK,     0xffffffff);
    365 
    366     R128WaitForFifo(pScrn, 1);
    367 
    368 #if X_BYTE_ORDER == X_BIG_ENDIAN
    369     /* FIXME: this is a kludge for texture uploads in the 3D driver. Look at
    370      * how the radeon driver handles HOST_DATA_SWAP if you want to implement
    371      * CCE ImageWrite acceleration or anything needing this bit */
    372 #ifdef R128DRI
    373     if (info->directRenderingEnabled)
    374 	OUTREGP(R128_DP_DATATYPE, 0, ~R128_HOST_BIG_ENDIAN_EN);
    375     else
    376 #endif
    377 	OUTREGP(R128_DP_DATATYPE,
    378 		R128_HOST_BIG_ENDIAN_EN, ~R128_HOST_BIG_ENDIAN_EN);
    379 #else /* X_LITTLE_ENDIAN */
    380     OUTREGP(R128_DP_DATATYPE, 0, ~R128_HOST_BIG_ENDIAN_EN);
    381 #endif
    382 
    383 #ifdef R128DRI
    384     info->sc_left         = 0x00000000;
    385     info->sc_right        = R128_DEFAULT_SC_RIGHT_MAX;
    386     info->sc_top          = 0x00000000;
    387     info->sc_bottom       = R128_DEFAULT_SC_BOTTOM_MAX;
    388 
    389     info->re_top_left     = 0x00000000;
    390     info->re_width_height = ((0x7ff << R128_RE_WIDTH_SHIFT) |
    391 			     (0x7ff << R128_RE_HEIGHT_SHIFT));
    392 
    393     info->aux_sc_cntl     = 0x00000000;
    394 #endif
    395 
    396     R128WaitForIdle(pScrn);
    397 }
    398 
    399 #ifdef R128DRI
    400 
    401 /* Get an indirect buffer for the CCE 2D acceleration commands.
    402  */
    403 drmBufPtr R128CCEGetBuffer( ScrnInfoPtr pScrn )
    404 {
    405     R128InfoPtr   info = R128PTR(pScrn);
    406     drmDMAReq dma;
    407     drmBufPtr buf = NULL;
    408     int indx = 0;
    409     int size = 0;
    410     int ret, i = 0;
    411 
    412 #if 0
    413     /* FIXME: pScrn->pScreen has not been initialized when this is first
    414        called from RADEONSelectBuffer via RADEONDRICPInit.  We could use
    415        the screen index from pScrn, which is initialized, and then get
    416        the screen from screenInfo.screens[index], but that is a hack. */
    417     dma.context = DRIGetContext(pScrn->pScreen);
    418 #else
    419     dma.context = 0x00000001; /* This is the X server's context */
    420 #endif
    421     dma.send_count = 0;
    422     dma.send_list = NULL;
    423     dma.send_sizes = NULL;
    424     dma.flags = 0;
    425     dma.request_count = 1;
    426     dma.request_size = R128_BUFFER_SIZE;
    427     dma.request_list = &indx;
    428     dma.request_sizes = &size;
    429     dma.granted_count = 0;
    430 
    431     while ( 1 ) {
    432 	do {
    433 	    ret = drmDMA( info->drmFD, &dma );
    434 	    if ( ret && ret != -EAGAIN ) {
    435 		xf86DrvMsg( pScrn->scrnIndex, X_ERROR,
    436 			    "%s: CCE GetBuffer %d\n", __FUNCTION__, ret );
    437 	    }
    438 	} while ( ( ret == -EAGAIN ) && ( i++ < R128_TIMEOUT ) );
    439 
    440 	if ( ret == 0 ) {
    441 	    buf = &info->buffers->list[indx];
    442 	    buf->used = 0;
    443 	    if ( R128_VERBOSE ) {
    444 		xf86DrvMsg( pScrn->scrnIndex, X_INFO,
    445 			    "   GetBuffer returning %d\n", buf->idx );
    446 	    }
    447 	    return buf;
    448 	}
    449 
    450 	xf86DrvMsg( pScrn->scrnIndex, X_ERROR,
    451 		    "GetBuffer timed out, resetting engine...\n");
    452 	R128EngineReset( pScrn );
    453 	/* R128EngineRestore( pScrn ); FIXME ??? */
    454 
    455 	/* Always restart the engine when doing CCE 2D acceleration */
    456 	R128CCE_RESET( pScrn, info );
    457 	R128CCE_START( pScrn, info );
    458     }
    459 }
    460 
    461 /* Flush the indirect buffer to the kernel for submission to the card.
    462  */
    463 void R128CCEFlushIndirect( ScrnInfoPtr pScrn, int discard )
    464 {
    465     R128InfoPtr   info = R128PTR(pScrn);
    466     drmBufPtr buffer = info->indirectBuffer;
    467     int start = info->indirectStart;
    468     drmR128Indirect indirect;
    469 
    470     if ( !buffer )
    471 	return;
    472 
    473     if ( (start == buffer->used) && !discard )
    474         return;
    475 
    476     indirect.idx = buffer->idx;
    477     indirect.start = start;
    478     indirect.end = buffer->used;
    479     indirect.discard = discard;
    480 
    481     drmCommandWriteRead( info->drmFD, DRM_R128_INDIRECT,
    482                          &indirect, sizeof(drmR128Indirect));
    483 
    484     if ( discard )
    485         buffer = info->indirectBuffer = R128CCEGetBuffer( pScrn );
    486 
    487     /* pad to an even number of dwords */
    488     if (buffer->used & 7)
    489         buffer->used = ( buffer->used+7 ) & ~7;
    490 
    491     info->indirectStart = buffer->used;
    492 }
    493 
    494 /* Flush and release the indirect buffer.
    495  */
    496 void R128CCEReleaseIndirect( ScrnInfoPtr pScrn )
    497 {
    498     R128InfoPtr   info = R128PTR(pScrn);
    499     drmBufPtr buffer = info->indirectBuffer;
    500     int start = info->indirectStart;
    501     drmR128Indirect indirect;
    502 
    503     info->indirectBuffer = NULL;
    504     info->indirectStart = 0;
    505 
    506     if ( !buffer )
    507 	return;
    508 
    509     indirect.idx = buffer->idx;
    510     indirect.start = start;
    511     indirect.end = buffer->used;
    512     indirect.discard = 1;
    513 
    514     drmCommandWriteRead( info->drmFD, DRM_R128_INDIRECT,
    515                          &indirect, sizeof(drmR128Indirect));
    516 }
    517 
    518 #endif
    519 
    520 void R128CopySwap(uint8_t *dst, uint8_t *src, unsigned int size, int swap)
    521 {
    522     switch(swap) {
    523     case APER_0_BIG_ENDIAN_32BPP_SWAP:
    524 	{
    525 	    unsigned int *d = (unsigned int *)dst;
    526 	    unsigned int *s = (unsigned int *)src;
    527 	    unsigned int nwords = size >> 2;
    528 
    529 	    for (; nwords > 0; --nwords, ++d, ++s)
    530 #ifdef __powerpc__
    531 		asm volatile("stwbrx %0,0,%1" : : "r" (*s), "r" (d));
    532 #else
    533 		*d = ((*s >> 24) & 0xff) | ((*s >> 8) & 0xff00)
    534 			| ((*s & 0xff00) << 8) | ((*s & 0xff) << 24);
    535 #endif
    536 	    return;
    537 	}
    538     case APER_0_BIG_ENDIAN_16BPP_SWAP:
    539 	{
    540 	    unsigned short *d = (unsigned short *)dst;
    541 	    unsigned short *s = (unsigned short *)src;
    542 	    unsigned int nwords = size >> 1;
    543 
    544 	    for (; nwords > 0; --nwords, ++d, ++s)
    545 #ifdef __powerpc__
    546 		asm volatile("sthbrx %0,0,%1" : : "r" (*s), "r" (d));
    547 #else
    548 	        *d = (*s >> 8) | (*s << 8);
    549 #endif
    550 	    return;
    551 	}
    552     }
    553     if (src != dst)
    554 	memcpy(dst, src, size);
    555 }
    556