2005-04-17 02:20:36 +04:00
/*
*
* Hardware accelerated Matrox Millennium I , II , Mystique , G100 , G200 and G400
*
* ( c ) 1998 - 2002 Petr Vandrovec < vandrove @ vc . cvut . cz >
*
* Version : 1.65 2002 / 08 / 14
*
* MTRR stuff : 1998 Tom Rini < trini @ kernel . crashing . org >
*
* Contributors : " menion? " < menion @ mindless . com >
* Betatesting , fixes , ideas
*
* " Kurt Garloff " < garloff @ suse . de >
* Betatesting , fixes , ideas , videomodes , videomodes timmings
*
* " Tom Rini " < trini @ kernel . crashing . org >
* MTRR stuff , PPC cleanups , betatesting , fixes , ideas
*
* " Bibek Sahu " < scorpio @ dodds . net >
* Access device through readb | w | l and write b | w | l
* Extensive debugging stuff
*
* " Daniel Haun " < haund @ usa . net >
* Testing , hardware cursor fixes
*
* " Scott Wood " < sawst46 + @ pitt . edu >
* Fixes
*
* " Gerd Knorr " < kraxel @ goldbach . isdn . cs . tu - berlin . de >
* Betatesting
*
* " Kelly French " < targon @ hazmat . com >
* " Fernando Herrera " < fherrera @ eurielec . etsit . upm . es >
* Betatesting , bug reporting
*
* " Pablo Bianucci " < pbian @ pccp . com . ar >
* Fixes , ideas , betatesting
*
* " Inaky Perez Gonzalez " < inaky @ peloncho . fis . ucm . es >
* Fixes , enhandcements , ideas , betatesting
*
* " Ryuichi Oikawa " < roikawa @ rr . iiij4u . or . jp >
* PPC betatesting , PPC support , backward compatibility
*
* " Paul Womar " < Paul @ pwomar . demon . co . uk >
* " Owen Waller " < O . Waller @ ee . qub . ac . uk >
* PPC betatesting
*
* " Thomas Pornin " < pornin @ bolet . ens . fr >
* Alpha betatesting
*
* " Pieter van Leuven " < pvl @ iae . nl >
* " Ulf Jaenicke-Roessler " < ujr @ physik . phy . tu - dresden . de >
* G100 testing
*
* " H. Peter Arvin " < hpa @ transmeta . com >
* Ideas
*
* " Cort Dougan " < cort @ cs . nmt . edu >
* CHRP fixes and PReP cleanup
*
* " Mark Vojkovich " < mvojkovi @ ucsd . edu >
* G400 support
*
* ( following author is not in any relation with this code , but his code
* is included in this driver )
*
* Based on framebuffer driver for VBE 2.0 compliant graphic boards
* ( c ) 1998 Gerd Knorr < kraxel @ cs . tu - berlin . de >
*
* ( following author is not in any relation with this code , but his ideas
2007-05-09 09:14:03 +04:00
* were used when writing this driver )
2005-04-17 02:20:36 +04:00
*
* FreeVBE / AF ( Matrox ) , " Shawn Hargreaves " < shawn @ talula . demon . co . uk >
*
*/
# include "matroxfb_accel.h"
# include "matroxfb_DAC1064.h"
# include "matroxfb_Ti3026.h"
# include "matroxfb_misc.h"
2009-09-23 03:47:48 +04:00
# define curr_ydstorg(x) ((x)->curr.ydstorg.pixels)
2005-04-17 02:20:36 +04:00
# define mga_ydstlen(y,l) mga_outl(M_YDSTLEN | M_EXEC, ((y) << 16) | (l))
static inline void matrox_cfb4_pal ( u_int32_t * pal ) {
unsigned int i ;
for ( i = 0 ; i < 16 ; i + + ) {
pal [ i ] = i * 0x11111111U ;
}
}
static inline void matrox_cfb8_pal ( u_int32_t * pal ) {
unsigned int i ;
for ( i = 0 ; i < 16 ; i + + ) {
pal [ i ] = i * 0x01010101U ;
}
}
static void matroxfb_copyarea ( struct fb_info * info , const struct fb_copyarea * area ) ;
static void matroxfb_fillrect ( struct fb_info * info , const struct fb_fillrect * rect ) ;
static void matroxfb_imageblit ( struct fb_info * info , const struct fb_image * image ) ;
static void matroxfb_cfb4_fillrect ( struct fb_info * info , const struct fb_fillrect * rect ) ;
static void matroxfb_cfb4_copyarea ( struct fb_info * info , const struct fb_copyarea * area ) ;
2009-09-23 03:47:49 +04:00
void matrox_cfbX_init ( struct matrox_fb_info * minfo )
{
2005-04-17 02:20:36 +04:00
u_int32_t maccess ;
u_int32_t mpitch ;
u_int32_t mopmode ;
int accel ;
2008-04-28 13:15:47 +04:00
DBG ( __func__ )
2005-04-17 02:20:36 +04:00
2009-09-23 03:47:48 +04:00
mpitch = minfo - > fbcon . var . xres_virtual ;
2005-04-17 02:20:36 +04:00
2009-09-23 03:47:48 +04:00
minfo - > fbops . fb_copyarea = cfb_copyarea ;
minfo - > fbops . fb_fillrect = cfb_fillrect ;
minfo - > fbops . fb_imageblit = cfb_imageblit ;
minfo - > fbops . fb_cursor = NULL ;
2005-04-17 02:20:36 +04:00
2009-09-23 03:47:48 +04:00
accel = ( minfo - > fbcon . var . accel_flags & FB_ACCELF_TEXT ) = = FB_ACCELF_TEXT ;
2005-04-17 02:20:36 +04:00
2009-09-23 03:47:48 +04:00
switch ( minfo - > fbcon . var . bits_per_pixel ) {
2005-04-17 02:20:36 +04:00
case 4 : maccess = 0x00000000 ; /* accelerate as 8bpp video */
mpitch = ( mpitch > > 1 ) | 0x8000 ; /* disable linearization */
mopmode = M_OPMODE_4BPP ;
2009-09-23 03:47:48 +04:00
matrox_cfb4_pal ( minfo - > cmap ) ;
2005-04-17 02:20:36 +04:00
if ( accel & & ! ( mpitch & 1 ) ) {
2009-09-23 03:47:48 +04:00
minfo - > fbops . fb_copyarea = matroxfb_cfb4_copyarea ;
minfo - > fbops . fb_fillrect = matroxfb_cfb4_fillrect ;
2005-04-17 02:20:36 +04:00
}
break ;
case 8 : maccess = 0x00000000 ;
mopmode = M_OPMODE_8BPP ;
2009-09-23 03:47:48 +04:00
matrox_cfb8_pal ( minfo - > cmap ) ;
2005-04-17 02:20:36 +04:00
if ( accel ) {
2009-09-23 03:47:48 +04:00
minfo - > fbops . fb_copyarea = matroxfb_copyarea ;
minfo - > fbops . fb_fillrect = matroxfb_fillrect ;
minfo - > fbops . fb_imageblit = matroxfb_imageblit ;
2005-04-17 02:20:36 +04:00
}
break ;
2009-09-23 03:47:48 +04:00
case 16 : if ( minfo - > fbcon . var . green . length = = 5 )
2005-04-17 02:20:36 +04:00
maccess = 0xC0000001 ;
2007-07-17 15:05:45 +04:00
else
2005-04-17 02:20:36 +04:00
maccess = 0x40000001 ;
mopmode = M_OPMODE_16BPP ;
if ( accel ) {
2009-09-23 03:47:48 +04:00
minfo - > fbops . fb_copyarea = matroxfb_copyarea ;
minfo - > fbops . fb_fillrect = matroxfb_fillrect ;
minfo - > fbops . fb_imageblit = matroxfb_imageblit ;
2005-04-17 02:20:36 +04:00
}
break ;
case 24 : maccess = 0x00000003 ;
mopmode = M_OPMODE_24BPP ;
if ( accel ) {
2009-09-23 03:47:48 +04:00
minfo - > fbops . fb_copyarea = matroxfb_copyarea ;
minfo - > fbops . fb_fillrect = matroxfb_fillrect ;
minfo - > fbops . fb_imageblit = matroxfb_imageblit ;
2005-04-17 02:20:36 +04:00
}
break ;
case 32 : maccess = 0x00000002 ;
mopmode = M_OPMODE_32BPP ;
if ( accel ) {
2009-09-23 03:47:48 +04:00
minfo - > fbops . fb_copyarea = matroxfb_copyarea ;
minfo - > fbops . fb_fillrect = matroxfb_fillrect ;
minfo - > fbops . fb_imageblit = matroxfb_imageblit ;
2005-04-17 02:20:36 +04:00
}
break ;
default : maccess = 0x00000000 ;
mopmode = 0x00000000 ;
break ; /* turn off acceleration!!! */
}
mga_fifo ( 8 ) ;
mga_outl ( M_PITCH , mpitch ) ;
2009-09-23 03:47:48 +04:00
mga_outl ( M_YDSTORG , curr_ydstorg ( minfo ) ) ;
if ( minfo - > capable . plnwt )
2005-04-17 02:20:36 +04:00
mga_outl ( M_PLNWT , - 1 ) ;
2009-09-23 03:47:48 +04:00
if ( minfo - > capable . srcorg ) {
2005-04-17 02:20:36 +04:00
mga_outl ( M_SRCORG , 0 ) ;
mga_outl ( M_DSTORG , 0 ) ;
}
mga_outl ( M_OPMODE , mopmode ) ;
mga_outl ( M_CXBNDRY , 0xFFFF0000 ) ;
mga_outl ( M_YTOP , 0 ) ;
mga_outl ( M_YBOT , 0x01FFFFFF ) ;
mga_outl ( M_MACCESS , maccess ) ;
2009-09-23 03:47:48 +04:00
minfo - > accel . m_dwg_rect = M_DWG_TRAP | M_DWG_SOLID | M_DWG_ARZERO | M_DWG_SGNZERO | M_DWG_SHIFTZERO ;
if ( isMilleniumII ( minfo ) ) minfo - > accel . m_dwg_rect | = M_DWG_TRANSC ;
minfo - > accel . m_opmode = mopmode ;
2005-04-17 02:20:36 +04:00
}
EXPORT_SYMBOL ( matrox_cfbX_init ) ;
2009-09-23 03:47:49 +04:00
static void matrox_accel_bmove ( struct matrox_fb_info * minfo , int vxres , int sy ,
int sx , int dy , int dx , int height , int width )
{
2005-04-17 02:20:36 +04:00
int start , end ;
CRITFLAGS
2008-04-28 13:15:47 +04:00
DBG ( __func__ )
2005-04-17 02:20:36 +04:00
CRITBEGIN
if ( ( dy < sy ) | | ( ( dy = = sy ) & & ( dx < = sx ) ) ) {
mga_fifo ( 2 ) ;
mga_outl ( M_DWGCTL , M_DWG_BITBLT | M_DWG_SHIFTZERO | M_DWG_SGNZERO |
M_DWG_BFCOL | M_DWG_REPLACE ) ;
mga_outl ( M_AR5 , vxres ) ;
width - - ;
2009-09-23 03:47:48 +04:00
start = sy * vxres + sx + curr_ydstorg ( minfo ) ;
2005-04-17 02:20:36 +04:00
end = start + width ;
} else {
mga_fifo ( 3 ) ;
mga_outl ( M_DWGCTL , M_DWG_BITBLT | M_DWG_SHIFTZERO | M_DWG_BFCOL | M_DWG_REPLACE ) ;
mga_outl ( M_SGN , 5 ) ;
mga_outl ( M_AR5 , - vxres ) ;
width - - ;
2009-09-23 03:47:48 +04:00
end = ( sy + height - 1 ) * vxres + sx + curr_ydstorg ( minfo ) ;
2005-04-17 02:20:36 +04:00
start = end + width ;
dy + = height - 1 ;
}
mga_fifo ( 4 ) ;
mga_outl ( M_AR0 , end ) ;
mga_outl ( M_AR3 , start ) ;
mga_outl ( M_FXBNDRY , ( ( dx + width ) < < 16 ) | dx ) ;
mga_ydstlen ( dy , height ) ;
WaitTillIdle ( ) ;
CRITEND
}
2009-09-23 03:47:49 +04:00
static void matrox_accel_bmove_lin ( struct matrox_fb_info * minfo , int vxres ,
int sy , int sx , int dy , int dx , int height ,
int width )
{
2005-04-17 02:20:36 +04:00
int start , end ;
CRITFLAGS
2008-04-28 13:15:47 +04:00
DBG ( __func__ )
2005-04-17 02:20:36 +04:00
CRITBEGIN
if ( ( dy < sy ) | | ( ( dy = = sy ) & & ( dx < = sx ) ) ) {
mga_fifo ( 2 ) ;
mga_outl ( M_DWGCTL , M_DWG_BITBLT | M_DWG_SHIFTZERO | M_DWG_SGNZERO |
M_DWG_BFCOL | M_DWG_REPLACE ) ;
mga_outl ( M_AR5 , vxres ) ;
width - - ;
2009-09-23 03:47:48 +04:00
start = sy * vxres + sx + curr_ydstorg ( minfo ) ;
2005-04-17 02:20:36 +04:00
end = start + width ;
} else {
mga_fifo ( 3 ) ;
mga_outl ( M_DWGCTL , M_DWG_BITBLT | M_DWG_SHIFTZERO | M_DWG_BFCOL | M_DWG_REPLACE ) ;
mga_outl ( M_SGN , 5 ) ;
mga_outl ( M_AR5 , - vxres ) ;
width - - ;
2009-09-23 03:47:48 +04:00
end = ( sy + height - 1 ) * vxres + sx + curr_ydstorg ( minfo ) ;
2005-04-17 02:20:36 +04:00
start = end + width ;
dy + = height - 1 ;
}
mga_fifo ( 5 ) ;
mga_outl ( M_AR0 , end ) ;
mga_outl ( M_AR3 , start ) ;
mga_outl ( M_FXBNDRY , ( ( dx + width ) < < 16 ) | dx ) ;
mga_outl ( M_YDST , dy * vxres > > 5 ) ;
mga_outl ( M_LEN | M_EXEC , height ) ;
WaitTillIdle ( ) ;
CRITEND
}
static void matroxfb_cfb4_copyarea ( struct fb_info * info , const struct fb_copyarea * area ) {
2009-09-23 03:47:50 +04:00
struct matrox_fb_info * minfo = info2minfo ( info ) ;
2005-04-17 02:20:36 +04:00
if ( ( area - > sx | area - > dx | area - > width ) & 1 )
cfb_copyarea ( info , area ) ;
else
2009-09-23 03:47:49 +04:00
matrox_accel_bmove_lin ( minfo , minfo - > fbcon . var . xres_virtual > > 1 , area - > sy , area - > sx > > 1 , area - > dy , area - > dx > > 1 , area - > height , area - > width > > 1 ) ;
2005-04-17 02:20:36 +04:00
}
static void matroxfb_copyarea ( struct fb_info * info , const struct fb_copyarea * area ) {
2009-09-23 03:47:50 +04:00
struct matrox_fb_info * minfo = info2minfo ( info ) ;
2005-04-17 02:20:36 +04:00
2009-09-23 03:47:49 +04:00
matrox_accel_bmove ( minfo , minfo - > fbcon . var . xres_virtual , area - > sy , area - > sx , area - > dy , area - > dx , area - > height , area - > width ) ;
2005-04-17 02:20:36 +04:00
}
2009-09-23 03:47:49 +04:00
static void matroxfb_accel_clear ( struct matrox_fb_info * minfo , u_int32_t color ,
int sy , int sx , int height , int width )
{
2005-04-17 02:20:36 +04:00
CRITFLAGS
2008-04-28 13:15:47 +04:00
DBG ( __func__ )
2005-04-17 02:20:36 +04:00
CRITBEGIN
mga_fifo ( 5 ) ;
2009-09-23 03:47:48 +04:00
mga_outl ( M_DWGCTL , minfo - > accel . m_dwg_rect | M_DWG_REPLACE ) ;
2005-04-17 02:20:36 +04:00
mga_outl ( M_FCOL , color ) ;
mga_outl ( M_FXBNDRY , ( ( sx + width ) < < 16 ) | sx ) ;
mga_ydstlen ( sy , height ) ;
WaitTillIdle ( ) ;
CRITEND
}
static void matroxfb_fillrect ( struct fb_info * info , const struct fb_fillrect * rect ) {
2009-09-23 03:47:50 +04:00
struct matrox_fb_info * minfo = info2minfo ( info ) ;
2005-04-17 02:20:36 +04:00
switch ( rect - > rop ) {
case ROP_COPY :
2009-09-23 03:47:49 +04:00
matroxfb_accel_clear ( minfo , ( ( u_int32_t * ) info - > pseudo_palette ) [ rect - > color ] , rect - > dy , rect - > dx , rect - > height , rect - > width ) ;
2005-04-17 02:20:36 +04:00
break ;
}
}
2009-09-23 03:47:49 +04:00
static void matroxfb_cfb4_clear ( struct matrox_fb_info * minfo , u_int32_t bgx ,
int sy , int sx , int height , int width )
{
2005-04-17 02:20:36 +04:00
int whattodo ;
CRITFLAGS
2008-04-28 13:15:47 +04:00
DBG ( __func__ )
2005-04-17 02:20:36 +04:00
CRITBEGIN
whattodo = 0 ;
if ( sx & 1 ) {
sx + + ;
if ( ! width ) return ;
width - - ;
whattodo = 1 ;
}
if ( width & 1 ) {
whattodo | = 2 ;
}
width > > = 1 ;
sx > > = 1 ;
if ( width ) {
mga_fifo ( 5 ) ;
2009-09-23 03:47:48 +04:00
mga_outl ( M_DWGCTL , minfo - > accel . m_dwg_rect | M_DWG_REPLACE2 ) ;
2005-04-17 02:20:36 +04:00
mga_outl ( M_FCOL , bgx ) ;
mga_outl ( M_FXBNDRY , ( ( sx + width ) < < 16 ) | sx ) ;
2009-09-23 03:47:48 +04:00
mga_outl ( M_YDST , sy * minfo - > fbcon . var . xres_virtual > > 6 ) ;
2005-04-17 02:20:36 +04:00
mga_outl ( M_LEN | M_EXEC , height ) ;
WaitTillIdle ( ) ;
}
if ( whattodo ) {
2009-09-23 03:47:48 +04:00
u_int32_t step = minfo - > fbcon . var . xres_virtual > > 1 ;
vaddr_t vbase = minfo - > video . vbase ;
2005-04-17 02:20:36 +04:00
if ( whattodo & 1 ) {
unsigned int uaddr = sy * step + sx - 1 ;
u_int32_t loop ;
u_int8_t bgx2 = bgx & 0xF0 ;
for ( loop = height ; loop > 0 ; loop - - ) {
mga_writeb ( vbase , uaddr , ( mga_readb ( vbase , uaddr ) & 0x0F ) | bgx2 ) ;
uaddr + = step ;
}
}
if ( whattodo & 2 ) {
unsigned int uaddr = sy * step + sx + width ;
u_int32_t loop ;
u_int8_t bgx2 = bgx & 0x0F ;
for ( loop = height ; loop > 0 ; loop - - ) {
mga_writeb ( vbase , uaddr , ( mga_readb ( vbase , uaddr ) & 0xF0 ) | bgx2 ) ;
uaddr + = step ;
}
}
}
CRITEND
}
static void matroxfb_cfb4_fillrect ( struct fb_info * info , const struct fb_fillrect * rect ) {
2009-09-23 03:47:50 +04:00
struct matrox_fb_info * minfo = info2minfo ( info ) ;
2005-04-17 02:20:36 +04:00
switch ( rect - > rop ) {
case ROP_COPY :
2009-09-23 03:47:49 +04:00
matroxfb_cfb4_clear ( minfo , ( ( u_int32_t * ) info - > pseudo_palette ) [ rect - > color ] , rect - > dy , rect - > dx , rect - > height , rect - > width ) ;
2005-04-17 02:20:36 +04:00
break ;
}
}
2009-09-23 03:47:49 +04:00
static void matroxfb_1bpp_imageblit ( struct matrox_fb_info * minfo , u_int32_t fgx ,
u_int32_t bgx , const u_int8_t * chardata ,
int width , int height , int yy , int xx )
{
2005-04-17 02:20:36 +04:00
u_int32_t step ;
u_int32_t ydstlen ;
u_int32_t xlen ;
u_int32_t ar0 ;
u_int32_t charcell ;
u_int32_t fxbndry ;
vaddr_t mmio ;
int easy ;
CRITFLAGS
2008-04-28 13:15:47 +04:00
DBG_HEAVY ( __func__ ) ;
2005-04-17 02:20:36 +04:00
step = ( width + 7 ) > > 3 ;
charcell = height * step ;
xlen = ( charcell + 3 ) & ~ 3 ;
ydstlen = ( yy < < 16 ) | height ;
if ( width = = step < < 3 ) {
ar0 = height * width - 1 ;
easy = 1 ;
} else {
ar0 = width - 1 ;
easy = 0 ;
}
CRITBEGIN
mga_fifo ( 3 ) ;
if ( easy )
mga_outl ( M_DWGCTL , M_DWG_ILOAD | M_DWG_SGNZERO | M_DWG_SHIFTZERO | M_DWG_BMONOWF | M_DWG_LINEAR | M_DWG_REPLACE ) ;
else
mga_outl ( M_DWGCTL , M_DWG_ILOAD | M_DWG_SGNZERO | M_DWG_SHIFTZERO | M_DWG_BMONOWF | M_DWG_REPLACE ) ;
mga_outl ( M_FCOL , fgx ) ;
mga_outl ( M_BCOL , bgx ) ;
fxbndry = ( ( xx + width - 1 ) < < 16 ) | xx ;
2009-09-23 03:47:48 +04:00
mmio = minfo - > mmio . vbase ;
2005-04-17 02:20:36 +04:00
mga_fifo ( 6 ) ;
mga_writel ( mmio , M_FXBNDRY , fxbndry ) ;
mga_writel ( mmio , M_AR0 , ar0 ) ;
mga_writel ( mmio , M_AR3 , 0 ) ;
if ( easy ) {
mga_writel ( mmio , M_YDSTLEN | M_EXEC , ydstlen ) ;
mga_memcpy_toio ( mmio , chardata , xlen ) ;
} else {
mga_writel ( mmio , M_AR5 , 0 ) ;
mga_writel ( mmio , M_YDSTLEN | M_EXEC , ydstlen ) ;
if ( ( step & 3 ) = = 0 ) {
/* Great. Source has 32bit aligned lines, so we can feed them
directly to the accelerator . */
mga_memcpy_toio ( mmio , chardata , charcell ) ;
} else if ( step = = 1 ) {
/* Special case for 1..8bit widths */
while ( height - - ) {
# if defined(__BIG_ENDIAN)
fb_writel ( ( * chardata ) < < 24 , mmio . vaddr ) ;
# else
fb_writel ( * chardata , mmio . vaddr ) ;
# endif
chardata + + ;
}
} else if ( step = = 2 ) {
/* Special case for 9..15bit widths */
while ( height - - ) {
# if defined(__BIG_ENDIAN)
fb_writel ( ( * ( u_int16_t * ) chardata ) < < 16 , mmio . vaddr ) ;
# else
fb_writel ( * ( u_int16_t * ) chardata , mmio . vaddr ) ;
# endif
chardata + = 2 ;
}
} else {
/* Tell... well, why bother... */
while ( height - - ) {
size_t i ;
for ( i = 0 ; i < step ; i + = 4 ) {
/* Hope that there are at least three readable bytes beyond the end of bitmap */
fb_writel ( get_unaligned ( ( u_int32_t * ) ( chardata + i ) ) , mmio . vaddr ) ;
}
chardata + = step ;
}
}
}
WaitTillIdle ( ) ;
CRITEND
}
static void matroxfb_imageblit ( struct fb_info * info , const struct fb_image * image ) {
2009-09-23 03:47:50 +04:00
struct matrox_fb_info * minfo = info2minfo ( info ) ;
2005-04-17 02:20:36 +04:00
2008-04-28 13:15:47 +04:00
DBG_HEAVY ( __func__ ) ;
2005-04-17 02:20:36 +04:00
if ( image - > depth = = 1 ) {
u_int32_t fgx , bgx ;
fgx = ( ( u_int32_t * ) info - > pseudo_palette ) [ image - > fg_color ] ;
bgx = ( ( u_int32_t * ) info - > pseudo_palette ) [ image - > bg_color ] ;
2009-09-23 03:47:49 +04:00
matroxfb_1bpp_imageblit ( minfo , fgx , bgx , image - > data , image - > width , image - > height , image - > dy , image - > dx ) ;
2005-04-17 02:20:36 +04:00
} else {
/* Danger! image->depth is useless: logo painting code always
passes framebuffer color depth here , although logo data are
always 8 bpp and info - > pseudo_palette is changed to contain
logo palette to be used ( but only for true / direct - color . . . sic . . . ) .
So do it completely in software . . . */
cfb_imageblit ( info , image ) ;
}
}
MODULE_LICENSE ( " GPL " ) ;