1/* 2 * SiS 300/540/630[S]/730[S], 3 * SiS 315[E|PRO]/550/[M]650/651/[M]661[F|M]X/740/[M]741[GX]/330/[M]760[GX], 4 * XGI V3XT/V5/V8, Z7 5 * frame buffer driver for Linux kernels >= 2.4.14 and >=2.6.3 6 * 7 * 2D acceleration part 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2 of the named License, 12 * or any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program; if not, write to the Free Software 21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA 22 * 23 * Based on the XFree86/X.org driver which is 24 * Copyright (C) 2001-2005 by Thomas Winischhofer, Vienna, Austria 25 * 26 * Author: Thomas Winischhofer <thomas@winischhofer.net> 27 * (see http://www.winischhofer.net/ 28 * for more information and updates) 29 */ 30 31#include <linux/version.h> 32#include <linux/module.h> 33#include <linux/kernel.h> 34#include <linux/fb.h> 35#include <linux/ioport.h> 36#include <linux/types.h> 37#include <asm/io.h> 38 39#include "sis.h" 40#include "sis_accel.h" 41 42static const u8 sisALUConv[] = 43{ 44 0x00, /* dest = 0; 0, GXclear, 0 */ 45 0x88, /* dest &= src; DSa, GXand, 0x1 */ 46 0x44, /* dest = src & ~dest; SDna, GXandReverse, 0x2 */ 47 0xCC, /* dest = src; S, GXcopy, 0x3 */ 48 0x22, /* dest &= ~src; DSna, GXandInverted, 0x4 */ 49 0xAA, /* dest = dest; D, GXnoop, 0x5 */ 50 0x66, /* dest = ^src; DSx, GXxor, 0x6 */ 51 0xEE, /* dest |= src; DSo, GXor, 0x7 */ 52 0x11, /* dest = ~src & ~dest; DSon, GXnor, 0x8 */ 53 0x99, /* dest ^= ~src ; DSxn, GXequiv, 0x9 */ 54 0x55, /* dest = ~dest; Dn, GXInvert, 0xA */ 55 0xDD, /* dest = src|~dest ; SDno, GXorReverse, 0xB */ 56 0x33, /* dest = ~src; Sn, GXcopyInverted, 0xC */ 57 0xBB, /* dest |= ~src; DSno, GXorInverted, 0xD */ 58 0x77, /* dest = ~src|~dest; DSan, GXnand, 0xE */ 59 0xFF, /* dest = 0xFF; 1, GXset, 0xF */ 60}; 61/* same ROP but with Pattern as Source */ 62static const u8 sisPatALUConv[] = 63{ 64 0x00, /* dest = 0; 0, GXclear, 0 */ 65 0xA0, /* dest &= src; DPa, GXand, 0x1 */ 66 0x50, /* dest = src & ~dest; PDna, GXandReverse, 0x2 */ 67 0xF0, /* dest = src; P, GXcopy, 0x3 */ 68 0x0A, /* dest &= ~src; DPna, GXandInverted, 0x4 */ 69 0xAA, /* dest = dest; D, GXnoop, 0x5 */ 70 0x5A, /* dest = ^src; DPx, GXxor, 0x6 */ 71 0xFA, /* dest |= src; DPo, GXor, 0x7 */ 72 0x05, /* dest = ~src & ~dest; DPon, GXnor, 0x8 */ 73 0xA5, /* dest ^= ~src ; DPxn, GXequiv, 0x9 */ 74 0x55, /* dest = ~dest; Dn, GXInvert, 0xA */ 75 0xF5, /* dest = src|~dest ; PDno, GXorReverse, 0xB */ 76 0x0F, /* dest = ~src; Pn, GXcopyInverted, 0xC */ 77 0xAF, /* dest |= ~src; DPno, GXorInverted, 0xD */ 78 0x5F, /* dest = ~src|~dest; DPan, GXnand, 0xE */ 79 0xFF, /* dest = 0xFF; 1, GXset, 0xF */ 80}; 81 82static const int myrops[] = { 83 3, 10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 84}; 85 86/* 300 series ----------------------------------------------------- */ 87#ifdef CONFIG_FB_SIS_300 88static void 89SiS300Sync(struct sis_video_info *ivideo) 90{ 91 SiS300Idle 92} 93 94static void 95SiS300SetupForScreenToScreenCopy(struct sis_video_info *ivideo, int xdir, int ydir, 96 int rop, int trans_color) 97{ 98 SiS300SetupDSTColorDepth(ivideo->DstColor); 99 SiS300SetupSRCPitch(ivideo->video_linelength) 100 SiS300SetupDSTRect(ivideo->video_linelength, 0xffff) 101 102 if(trans_color != -1) { 103 SiS300SetupROP(0x0A) 104 SiS300SetupSRCTrans(trans_color) 105 SiS300SetupCMDFlag(TRANSPARENT_BITBLT) 106 } else { 107 SiS300SetupROP(sisALUConv[rop]) 108 } 109 if(xdir > 0) { 110 SiS300SetupCMDFlag(X_INC) 111 } 112 if(ydir > 0) { 113 SiS300SetupCMDFlag(Y_INC) 114 } 115} 116 117static void 118SiS300SubsequentScreenToScreenCopy(struct sis_video_info *ivideo, int src_x, 119 int src_y, int dst_x, int dst_y, int width, int height) 120{ 121 u32 srcbase = 0, dstbase = 0; 122 123 if(src_y >= 2048) { 124 srcbase = ivideo->video_linelength * src_y; 125 src_y = 0; 126 } 127 if(dst_y >= 2048) { 128 dstbase = ivideo->video_linelength * dst_y; 129 dst_y = 0; 130 } 131 132 SiS300SetupSRCBase(srcbase); 133 SiS300SetupDSTBase(dstbase); 134 135 if(!(ivideo->CommandReg & X_INC)) { 136 src_x += width-1; 137 dst_x += width-1; 138 } 139 if(!(ivideo->CommandReg & Y_INC)) { 140 src_y += height-1; 141 dst_y += height-1; 142 } 143 SiS300SetupRect(width, height) 144 SiS300SetupSRCXY(src_x, src_y) 145 SiS300SetupDSTXY(dst_x, dst_y) 146 SiS300DoCMD 147} 148 149static void 150SiS300SetupForSolidFill(struct sis_video_info *ivideo, u32 color, int rop) 151{ 152 SiS300SetupPATFG(color) 153 SiS300SetupDSTRect(ivideo->video_linelength, 0xffff) 154 SiS300SetupDSTColorDepth(ivideo->DstColor); 155 SiS300SetupROP(sisPatALUConv[rop]) 156 SiS300SetupCMDFlag(PATFG) 157} 158 159static void 160SiS300SubsequentSolidFillRect(struct sis_video_info *ivideo, int x, int y, int w, int h) 161{ 162 u32 dstbase = 0; 163 164 if(y >= 2048) { 165 dstbase = ivideo->video_linelength * y; 166 y = 0; 167 } 168 SiS300SetupDSTBase(dstbase) 169 SiS300SetupDSTXY(x,y) 170 SiS300SetupRect(w,h) 171 SiS300SetupCMDFlag(X_INC | Y_INC | BITBLT) 172 SiS300DoCMD 173} 174#endif 175 176/* 315/330/340 series ---------------------------------------------- */ 177 178#ifdef CONFIG_FB_SIS_315 179static void 180SiS310Sync(struct sis_video_info *ivideo) 181{ 182 SiS310Idle 183} 184 185static void 186SiS310SetupForScreenToScreenCopy(struct sis_video_info *ivideo, int rop, int trans_color) 187{ 188 SiS310SetupDSTColorDepth(ivideo->DstColor); 189 SiS310SetupSRCPitch(ivideo->video_linelength) 190 SiS310SetupDSTRect(ivideo->video_linelength, 0x0fff) 191 if(trans_color != -1) { 192 SiS310SetupROP(0x0A) 193 SiS310SetupSRCTrans(trans_color) 194 SiS310SetupCMDFlag(TRANSPARENT_BITBLT) 195 } else { 196 SiS310SetupROP(sisALUConv[rop]) 197 /* Set command - not needed, both 0 */ 198 /* SiSSetupCMDFlag(BITBLT | SRCVIDEO) */ 199 } 200 SiS310SetupCMDFlag(ivideo->SiS310_AccelDepth) 201 /* The chip is smart enough to know the direction */ 202} 203 204static void 205SiS310SubsequentScreenToScreenCopy(struct sis_video_info *ivideo, int src_x, int src_y, 206 int dst_x, int dst_y, int width, int height) 207{ 208 u32 srcbase = 0, dstbase = 0; 209 int mymin = min(src_y, dst_y); 210 int mymax = max(src_y, dst_y); 211 212 /* Although the chip knows the direction to use 213 * if the source and destination areas overlap, 214 * that logic fails if we fiddle with the bitmap 215 * addresses. Therefore, we check if the source 216 * and destination blitting areas overlap and 217 * adapt the bitmap addresses synchronously 218 * if the coordinates exceed the valid range. 219 * The the areas do not overlap, we do our 220 * normal check. 221 */ 222 if((mymax - mymin) < height) { 223 if((src_y >= 2048) || (dst_y >= 2048)) { 224 srcbase = ivideo->video_linelength * mymin; 225 dstbase = ivideo->video_linelength * mymin; 226 src_y -= mymin; 227 dst_y -= mymin; 228 } 229 } else { 230 if(src_y >= 2048) { 231 srcbase = ivideo->video_linelength * src_y; 232 src_y = 0; 233 } 234 if(dst_y >= 2048) { 235 dstbase = ivideo->video_linelength * dst_y; 236 dst_y = 0; 237 } 238 } 239 240 srcbase += ivideo->video_offset; 241 dstbase += ivideo->video_offset; 242 243 SiS310SetupSRCBase(srcbase); 244 SiS310SetupDSTBase(dstbase); 245 SiS310SetupRect(width, height) 246 SiS310SetupSRCXY(src_x, src_y) 247 SiS310SetupDSTXY(dst_x, dst_y) 248 SiS310DoCMD 249} 250 251static void 252SiS310SetupForSolidFill(struct sis_video_info *ivideo, u32 color, int rop) 253{ 254 SiS310SetupPATFG(color) 255 SiS310SetupDSTRect(ivideo->video_linelength, 0x0fff) 256 SiS310SetupDSTColorDepth(ivideo->DstColor); 257 SiS310SetupROP(sisPatALUConv[rop]) 258 SiS310SetupCMDFlag(PATFG | ivideo->SiS310_AccelDepth) 259} 260 261static void 262SiS310SubsequentSolidFillRect(struct sis_video_info *ivideo, int x, int y, int w, int h) 263{ 264 u32 dstbase = 0; 265 266 if(y >= 2048) { 267 dstbase = ivideo->video_linelength * y; 268 y = 0; 269 } 270 dstbase += ivideo->video_offset; 271 SiS310SetupDSTBase(dstbase) 272 SiS310SetupDSTXY(x,y) 273 SiS310SetupRect(w,h) 274 SiS310SetupCMDFlag(BITBLT) 275 SiS310DoCMD 276} 277#endif 278 279/* --------------------------------------------------------------------- */ 280 281/* The exported routines */ 282 283int sisfb_initaccel(struct sis_video_info *ivideo) 284{ 285#ifdef SISFB_USE_SPINLOCKS 286 spin_lock_init(&ivideo->lockaccel); 287#endif 288 return 0; 289} 290 291void sisfb_syncaccel(struct sis_video_info *ivideo) 292{ 293 if(ivideo->sisvga_engine == SIS_300_VGA) { 294#ifdef CONFIG_FB_SIS_300 295 SiS300Sync(ivideo); 296#endif 297 } else { 298#ifdef CONFIG_FB_SIS_315 299 SiS310Sync(ivideo); 300#endif 301 } 302} 303 304int fbcon_sis_sync(struct fb_info *info) 305{ 306 struct sis_video_info *ivideo = (struct sis_video_info *)info->par; 307 CRITFLAGS 308 309 if((!ivideo->accel) || (!ivideo->engineok)) 310 return 0; 311 312 CRITBEGIN 313 sisfb_syncaccel(ivideo); 314 CRITEND 315 316 return 0; 317} 318 319void fbcon_sis_fillrect(struct fb_info *info, const struct fb_fillrect *rect) 320{ 321 struct sis_video_info *ivideo = (struct sis_video_info *)info->par; 322 u32 col = 0; 323 u32 vxres = info->var.xres_virtual; 324 u32 vyres = info->var.yres_virtual; 325 int width, height; 326 CRITFLAGS 327 328 if(info->state != FBINFO_STATE_RUNNING) 329 return; 330 331 if((!ivideo->accel) || (!ivideo->engineok)) { 332 cfb_fillrect(info, rect); 333 return; 334 } 335 336 if(!rect->width || !rect->height || rect->dx >= vxres || rect->dy >= vyres) 337 return; 338 339 /* Clipping */ 340 width = ((rect->dx + rect->width) > vxres) ? (vxres - rect->dx) : rect->width; 341 height = ((rect->dy + rect->height) > vyres) ? (vyres - rect->dy) : rect->height; 342 343 switch(info->var.bits_per_pixel) { 344 case 8: col = rect->color; 345 break; 346 case 16: 347 case 32: col = ((u32 *)(info->pseudo_palette))[rect->color]; 348 break; 349 } 350 351 if(ivideo->sisvga_engine == SIS_300_VGA) { 352#ifdef CONFIG_FB_SIS_300 353 CRITBEGIN 354 SiS300SetupForSolidFill(ivideo, col, myrops[rect->rop]); 355 SiS300SubsequentSolidFillRect(ivideo, rect->dx, rect->dy, width, height); 356 CRITEND 357#endif 358 } else { 359#ifdef CONFIG_FB_SIS_315 360 CRITBEGIN 361 SiS310SetupForSolidFill(ivideo, col, myrops[rect->rop]); 362 SiS310SubsequentSolidFillRect(ivideo, rect->dx, rect->dy, width, height); 363 CRITEND 364#endif 365 } 366 367 sisfb_syncaccel(ivideo); 368} 369 370void fbcon_sis_copyarea(struct fb_info *info, const struct fb_copyarea *area) 371{ 372 struct sis_video_info *ivideo = (struct sis_video_info *)info->par; 373 u32 vxres = info->var.xres_virtual; 374 u32 vyres = info->var.yres_virtual; 375 int width = area->width; 376 int height = area->height; 377 CRITFLAGS 378 379 if(info->state != FBINFO_STATE_RUNNING) 380 return; 381 382 if((!ivideo->accel) || (!ivideo->engineok)) { 383 cfb_copyarea(info, area); 384 return; 385 } 386 387 if(!width || !height || 388 area->sx >= vxres || area->sy >= vyres || 389 area->dx >= vxres || area->dy >= vyres) 390 return; 391 392 /* Clipping */ 393 if((area->sx + width) > vxres) width = vxres - area->sx; 394 if((area->dx + width) > vxres) width = vxres - area->dx; 395 if((area->sy + height) > vyres) height = vyres - area->sy; 396 if((area->dy + height) > vyres) height = vyres - area->dy; 397 398 if(ivideo->sisvga_engine == SIS_300_VGA) { 399#ifdef CONFIG_FB_SIS_300 400 int xdir, ydir; 401 402 if(area->sx < area->dx) xdir = 0; 403 else xdir = 1; 404 if(area->sy < area->dy) ydir = 0; 405 else ydir = 1; 406 407 CRITBEGIN 408 SiS300SetupForScreenToScreenCopy(ivideo, xdir, ydir, 3, -1); 409 SiS300SubsequentScreenToScreenCopy(ivideo, area->sx, area->sy, 410 area->dx, area->dy, width, height); 411 CRITEND 412#endif 413 } else { 414#ifdef CONFIG_FB_SIS_315 415 CRITBEGIN 416 SiS310SetupForScreenToScreenCopy(ivideo, 3, -1); 417 SiS310SubsequentScreenToScreenCopy(ivideo, area->sx, area->sy, 418 area->dx, area->dy, width, height); 419 CRITEND 420#endif 421 } 422 423 sisfb_syncaccel(ivideo); 424} 425