/* * Copyright 2000 ATI Technologies Inc., Markham, Ontario, and * VA Linux Systems Inc., Fremont, California. * * 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 on the rights to use, copy, modify, merge, * publish, distribute, sublicense, 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 ATI, VA LINUX SYSTEMS AND/OR * THEIR 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. */ /* * Authors: * Kevin E. Martin * Rickard E. Faith * Alan Hourihane * * Credits: * * Thanks to Ani Joshi for providing source * code to his Radeon driver. Portions of this file are based on the * initialization code for that driver. * * References: * * !!!! FIXME !!!! * RAGE 128 VR/ RAGE 128 GL Register Reference Manual (Technical * Reference Manual P/N RRG-G04100-C Rev. 0.04), ATI Technologies: April * 1999. * * RAGE 128 Software Development Manual (Technical Reference Manual P/N * SDK-G04000 Rev. 0.01), ATI Technologies: June 1999. * * Notes on unimplemented XAA optimizations: * * SetClipping: This has been removed as XAA expects 16bit registers * for full clipping. * TwoPointLine: The Radeon supports this. Not Bresenham. * DashedLine with non-power-of-two pattern length: Apparently, there is * no way to set the length of the pattern -- it is always * assumed to be 8 or 32 (or 1024?). * ScreenToScreenColorExpandFill: See p. 4-17 of the Technical Reference * Manual where it states that monochrome expansion of frame * buffer data is not supported. * Color8x8PatternFill: Apparently, an 8x8 color brush cannot take an 8x8 * pattern from frame buffer memory. * */ #include "fb.h" #include "radeonfb.h" static struct { int rop; int pattern; } RADEON_ROP[] = { { ROP3_ZERO, ROP3_ZERO }, /* GXclear */ { ROP3_DSa, ROP3_DPa }, /* Gxand */ { ROP3_SDna, ROP3_PDna }, /* GXandReverse */ { ROP3_S, ROP3_P }, /* GXcopy */ { ROP3_DSna, ROP3_DPna }, /* GXandInverted */ { ROP3_D, ROP3_D }, /* GXnoop */ { ROP3_DSx, ROP3_DPx }, /* GXxor */ { ROP3_DSo, ROP3_DPo }, /* GXor */ { ROP3_DSon, ROP3_DPon }, /* GXnor */ { ROP3_DSxn, ROP3_PDxn }, /* GXequiv */ { ROP3_Dn, ROP3_Dn }, /* GXinvert */ { ROP3_SDno, ROP3_PDno }, /* GXorReverse */ { ROP3_Sn, ROP3_Pn }, /* GXcopyInverted */ { ROP3_DSno, ROP3_DPno }, /* GXorInverted */ { ROP3_DSan, ROP3_DPan }, /* GXnand */ { ROP3_ONE, ROP3_ONE } /* GXset */ }; #define ACCEL_MMIO #define ACCEL_PREAMBLE() #define BEGIN_ACCEL(n) radeon_wait_for_fifo(rinfo, (n)) #define OUT_ACCEL_REG(reg, val) OUTREG(reg, val) #define FINISH_ACCEL() /* MMIO: * * Wait for the graphics engine to be completely idle: the FIFO has * drained, the Pixel Cache is flushed, and the engine is idle. This is * a standard "sync" function that will make the hardware "quiescent". */ void radeon_wait_for_idle_mmio(struct radeonfb_info *rinfo) { int i = 0; /* Wait for the engine to go idle */ radeon_wait_for_fifo_function(rinfo, 64); while(1) { for(i = 0; i < RADEON_TIMEOUT; i++) { if (!(INREG(RBBM_STATUS) & RBBM_ACTIVE)) { radeon_engine_flush(rinfo); return; } } radeon_engine_reset(rinfo); radeon_engine_restore(rinfo); } } #if 0 /* This callback is required for multiheader cards using XAA */ void RADEONRestoreAccelStateMMIO(struct fb_info *info) { struct radeonfb_info *rinfo = info->par; // unsigned long pitch64 = ((info->var.xres * (rinfo->bpp / 8) + 0x3f)) >> 6; OUTREG(DEFAULT_OFFSET, (((INREG(DISPLAY_BASE_ADDR) + rinfo->fb_local_base) >> 10) | (rinfo->pitch << 22))); /* FIXME: May need to restore other things, like BKGD_CLK FG_CLK... */ RADEONWaitForIdleMMIO(rinfo); } #endif /* Setup for XAA SolidFill */ void radeon_setup_for_solid_fill(struct fb_info *info, int color, int rop, unsigned int planemask) { struct radeonfb_info *rinfo = info->par; ACCEL_PREAMBLE(); /* Save for later clipping */ rinfo->dp_gui_master_cntl_clip = (rinfo->dp_gui_master_cntl | GMC_BRUSH_SOLID_COLOR | GMC_SRC_DATATYPE_COLOR | RADEON_ROP[rop].pattern); BEGIN_ACCEL(4); OUT_ACCEL_REG(DP_GUI_MASTER_CNTL, rinfo->dp_gui_master_cntl_clip); OUT_ACCEL_REG(DP_BRUSH_FRGD_CLR, color); OUT_ACCEL_REG(DP_WRITE_MSK, planemask); OUT_ACCEL_REG(DP_CNTL, (DST_X_LEFT_TO_RIGHT | DST_Y_TOP_TO_BOTTOM)); FINISH_ACCEL(); } /* Subsequent XAA SolidFillRect */ void radeon_subsequent_solid_fill_rect_mmio(struct fb_info *info, int x, int y, int w, int h) { struct radeonfb_info *rinfo = info->par; ACCEL_PREAMBLE(); #ifdef RADEON_TILING BEGIN_ACCEL(3); OUT_ACCEL_REG(DST_PITCH_OFFSET, rinfo->dst_pitch_offset | ((rinfo->tilingEnabled && (y <= info->var.yres_virtual)) ? DST_TILE_MACRO : 0)); #else BEGIN_ACCEL(2); #endif OUT_ACCEL_REG(DST_Y_X, (y << 16) | x); OUT_ACCEL_REG(DST_WIDTH_HEIGHT, (w << 16) | h); FINISH_ACCEL(); } /* Setup for XAA solid lines */ void radeon_setup_for_solid_line_mmio(struct fb_info *info, int color, int rop, unsigned int planemask) { struct radeonfb_info *rinfo = info->par; ACCEL_PREAMBLE(); /* Save for later clipping */ rinfo->dp_gui_master_cntl_clip = (rinfo->dp_gui_master_cntl | GMC_BRUSH_SOLID_COLOR | GMC_SRC_DATATYPE_COLOR | RADEON_ROP[rop].pattern); if (rinfo->family >= CHIP_FAMILY_RV200) { BEGIN_ACCEL(1); OUT_ACCEL_REG(DST_LINE_PATCOUNT, 0x55 << BRES_CNTL_SHIFT); } BEGIN_ACCEL(3); OUT_ACCEL_REG(DP_GUI_MASTER_CNTL, rinfo->dp_gui_master_cntl_clip); OUT_ACCEL_REG(DP_BRUSH_FRGD_CLR, color); OUT_ACCEL_REG(DP_WRITE_MSK, planemask); FINISH_ACCEL(); } /* Subsequent XAA solid horizontal and vertical lines */ void radeon_subsequent_solid_hor_vert_line_mmio(struct fb_info *info, int x, int y, int len, int dir) { struct radeonfb_info *rinfo = info->par; int w = 1; int h = 1; ACCEL_PREAMBLE(); if (dir == DEGREES_0) w = len; else h = len; #ifdef RADEON_TILING BEGIN_ACCEL(4); OUT_ACCEL_REG(DP_CNTL, (DST_X_LEFT_TO_RIGHT | DST_Y_TOP_TO_BOTTOM)); OUT_ACCEL_REG(DST_PITCH_OFFSET, rinfo->dst_pitch_offset | ((rinfo->tilingEnabled && (y <= info->var.xres_virtual)) ? DST_TILE_MACRO : 0)); #else BEGIN_ACCEL(3); OUT_ACCEL_REG(DP_CNTL, (DST_X_LEFT_TO_RIGHT | DST_Y_TOP_TO_BOTTOM)); #endif OUT_ACCEL_REG(DST_Y_X, (y << 16) | x); OUT_ACCEL_REG(DST_WIDTH_HEIGHT, (w << 16) | h); FINISH_ACCEL(); } /* Subsequent XAA solid TwoPointLine line */ void radeon_subsequent_solid_two_point_line_mmio(struct fb_info *info, int xa, int ya, int xb, int yb, int flags) { struct radeonfb_info *rinfo = info->par; ACCEL_PREAMBLE(); /* TODO: Check bounds -- RADEON only has 14 bits */ if (!(flags & OMIT_LAST)) radeon_subsequent_solid_hor_vert_line_mmio(info, xb, yb, 1, DEGREES_0); #ifdef RADEON_TILING BEGIN_ACCEL(3); OUT_ACCEL_REG(DST_PITCH_OFFSET, rinfo->dst_pitch_offset | ((rinfo->tilingEnabled && (ya <= info->var.yres_virtual)) ? DST_TILE_MACRO : 0)); #else BEGIN_ACCEL(2); #endif OUT_ACCEL_REG(DST_LINE_START, (ya << 16) | xa); OUT_ACCEL_REG(DST_LINE_END, (yb << 16) | xb); FINISH_ACCEL(); } /* Setup for XAA dashed lines * NOTE: Since we can only accelerate lines with power-of-2 patterns of * length <= 32 */ void radeon_setup_for_dashed_line_mmio(struct fb_info *info, int fg, int bg, int rop, unsigned int planemask, int length, unsigned char *pattern) { struct radeonfb_info *rinfo = info->par; unsigned long pat = *(unsigned long *) pattern; ACCEL_PREAMBLE(); /* Save for determining whether or not to draw last pixel */ rinfo->dashLen = length; rinfo->dashPattern = pat; if (rinfo->big_endian) { switch (length) { case 2: pat |= (pat >> 2); /* fall through */ case 4: pat |= (pat >> 4); /* fall through */ case 8: pat |= (pat >> 8); /* fall through */ case 16: pat |= (pat >> 16); } } else { switch (length) { case 2: pat |= (pat << 2); /* fall through */ case 4: pat |= (pat << 4); /* fall through */ case 8: pat |= (pat << 8); /* fall through */ case 16: pat |= (pat << 16); } } /* Save for later clipping */ rinfo->dp_gui_master_cntl_clip = (rinfo->dp_gui_master_cntl | (bg == -1 ? GMC_BRUSH_32X1_MONO_FG_LA : GMC_BRUSH_32X1_MONO_FG_BG) | RADEON_ROP[rop].pattern | GMC_BYTE_LSB_TO_MSB); rinfo->dash_fg = fg; rinfo->dash_bg = bg; BEGIN_ACCEL((bg == -1) ? 4 : 5); OUT_ACCEL_REG(DP_GUI_MASTER_CNTL, rinfo->dp_gui_master_cntl_clip); OUT_ACCEL_REG(DP_WRITE_MSK, planemask); OUT_ACCEL_REG(DP_BRUSH_FRGD_CLR, fg); if (bg != -1) OUT_ACCEL_REG(DP_BRUSH_BKGD_CLR, bg); OUT_ACCEL_REG(BRUSH_DATA0, pat); FINISH_ACCEL(); } /* Helper function to draw last point for dashed lines */ static void RADEONDashedLastPelMMIO(struct fb_info *info, int x, int y, int fg) { struct radeonfb_info *rinfo = info->par; unsigned long dp_gui_master_cntl = rinfo->dp_gui_master_cntl_clip; ACCEL_PREAMBLE(); dp_gui_master_cntl &= ~GMC_BRUSH_DATATYPE_MASK; dp_gui_master_cntl |= GMC_BRUSH_SOLID_COLOR; dp_gui_master_cntl &= ~GMC_SRC_DATATYPE_MASK; dp_gui_master_cntl |= GMC_SRC_DATATYPE_COLOR; #ifdef RADEON_TILING BEGIN_ACCEL(8); OUT_ACCEL_REG(DP_GUI_MASTER_CNTL, dp_gui_master_cntl); OUT_ACCEL_REG(DP_CNTL, (DST_X_LEFT_TO_RIGHT | DST_Y_TOP_TO_BOTTOM)); OUT_ACCEL_REG(DST_PITCH_OFFSET, rinfo->dst_pitch_offset | ((rinfo->tilingEnabled && (y <= info->var.yres_virtual)) ? DST_TILE_MACRO : 0)); #else BEGIN_ACCEL(7); OUT_ACCEL_REG(DP_GUI_MASTER_CNTL, dp_gui_master_cntl); OUT_ACCEL_REG(DP_CNTL, (DST_X_LEFT_TO_RIGHT | DST_Y_TOP_TO_BOTTOM)); #endif OUT_ACCEL_REG(DP_BRUSH_FRGD_CLR, fg); OUT_ACCEL_REG(DST_Y_X, (y << 16) | x); OUT_ACCEL_REG(DST_WIDTH_HEIGHT, (1 << 16) | 1); /* Restore old values */ OUT_ACCEL_REG(DP_GUI_MASTER_CNTL, rinfo->dp_gui_master_cntl_clip); OUT_ACCEL_REG(DP_BRUSH_FRGD_CLR, rinfo->dash_fg); FINISH_ACCEL(); } /* Subsequent XAA dashed line */ void radeon_subsequent_dashed_two_point_line_mmio(struct fb_info *info, int xa, int ya, int xb, int yb, int flags, int phase) { struct radeonfb_info *rinfo = info->par; ACCEL_PREAMBLE(); /* TODO: Check bounds -- RADEON only has 14 bits */ if (!(flags & OMIT_LAST)) { int deltax = xa - xb; int deltay = ya - yb; int shift; if (deltax < 0) deltax = -deltax; if (deltay < 0) deltay = -deltay; if (deltax > deltay) shift = deltax; else shift = deltay; shift += phase; shift %= rinfo->dashLen; if ((rinfo->dashPattern >> shift) & 1) RADEONDashedLastPelMMIO(info, xb, yb, rinfo->dash_fg); else if (rinfo->dash_bg != -1) RADEONDashedLastPelMMIO(info, xb, yb, rinfo->dash_bg); } #ifdef RADEON_TILING BEGIN_ACCEL(4); OUT_ACCEL_REG(DST_PITCH_OFFSET, rinfo->dst_pitch_offset | ((rinfo->tilingEnabled && (ya <= info->var.yres_virtual)) ? DST_TILE_MACRO : 0)); #else BEGIN_ACCEL(3); #endif OUT_ACCEL_REG(DST_LINE_START, (ya << 16) | xa); OUT_ACCEL_REG(DST_LINE_PATCOUNT, phase); OUT_ACCEL_REG(DST_LINE_END, (yb << 16) | xb); FINISH_ACCEL(); } /* Set up for transparency * * Mmmm, Seems as though the transparency compare is opposite to r128. * It should only draw when source != trans_color, this is the opposite * of that. */ static void radeon_set_transparency_mmio(struct radeonfb_info *rinfo, int trans_color) { if (trans_color != -1) { ACCEL_PREAMBLE(); BEGIN_ACCEL(3); OUT_ACCEL_REG(CLR_CMP_CLR_SRC, trans_color); OUT_ACCEL_REG(CLR_CMP_MSK, 0xffffffff); OUT_ACCEL_REG(CLR_CMP_CNTL, (SRC_CMP_EQ_COLOR | CLR_CMP_SRC_SOURCE)); FINISH_ACCEL(); } } /* Setup for XAA screen-to-screen copy */ void radeon_setup_for_screen_to_screen_copy_mmio(struct fb_info *info, int xdir, int ydir, int rop, unsigned int planemask, int trans_color) { struct radeonfb_info *rinfo = info->par; ACCEL_PREAMBLE(); rinfo->xdir = xdir; rinfo->ydir = ydir; /* Save for later clipping */ #ifdef RADEON_TILING rinfo->dp_gui_master_cntl_clip = (rinfo->dp_gui_master_cntl | GMC_BRUSH_NONE | GMC_SRC_DATATYPE_COLOR | RADEON_ROP[rop].rop | DP_SRC_SOURCE_MEMORY | GMC_SRC_PITCH_OFFSET_CNTL); #else rinfo->dp_gui_master_cntl_clip = (rinfo->dp_gui_master_cntl | GMC_BRUSH_NONE | GMC_SRC_DATATYPE_COLOR | RADEON_ROP[rop].rop | DP_SRC_SOURCE_MEMORY); #endif BEGIN_ACCEL(3); OUT_ACCEL_REG(DP_GUI_MASTER_CNTL, rinfo->dp_gui_master_cntl_clip); OUT_ACCEL_REG(DP_WRITE_MSK, planemask); OUT_ACCEL_REG(DP_CNTL, ((xdir >= 0 ? DST_X_LEFT_TO_RIGHT : 0) | (ydir >= 0 ? DST_Y_TOP_TO_BOTTOM : 0))); FINISH_ACCEL(); rinfo->trans_color = trans_color; if (trans_color != -1) radeon_set_transparency_mmio(rinfo, trans_color); } /* Subsequent XAA screen-to-screen copy */ void radeon_subsequent_screen_to_screen_copy_mmio(struct fb_info *info, int xa, int ya, int xb, int yb, int w, int h) { struct radeonfb_info *rinfo = info->par; ACCEL_PREAMBLE(); if (rinfo->xdir < 0) xa += w - 1, xb += w - 1; if (rinfo->ydir < 0) ya += h - 1, yb += h - 1; #ifdef RADEON_TILING BEGIN_ACCEL(5); OUT_ACCEL_REG(SRC_PITCH_OFFSET, rinfo->dst_pitch_offset | ((rinfo->tilingEnabled && (ya <= info->var.yres_virtual)) ? DST_TILE_MACRO : 0)); OUT_ACCEL_REG(DST_PITCH_OFFSET, rinfo->dst_pitch_offset | ((rinfo->tilingEnabled && (yb <= info->var.yres_virtual)) ? DST_TILE_MACRO : 0)); #else BEGIN_ACCEL(3); #endif OUT_ACCEL_REG(SRC_Y_X, (ya << 16) | xa); OUT_ACCEL_REG(DST_Y_X, (yb << 16) | xb); OUT_ACCEL_REG(DST_HEIGHT_WIDTH, (h << 16) | w); FINISH_ACCEL(); } /* XAA screen-to-screen copy */ void radeon_screen_to_screen_copy_mmio(struct fb_info *info, int xa, int ya, int xb, int yb, int w, int h, int rop) { struct radeonfb_info *rinfo = info->par; int xdir = xa - xb; int ydir = ya - yb; ACCEL_PREAMBLE(); if (xdir < 0) xa += w - 1, xb += w - 1; if (ydir < 0) ya += h - 1, yb += h - 1; #ifdef RADEON_TILING /* Save for later clipping */ rinfo->dp_gui_master_cntl_clip = (rinfo->dp_gui_master_cntl | GMC_BRUSH_NONE | GMC_SRC_DATATYPE_COLOR | RADEON_ROP[rop].rop | DP_SRC_SOURCE_MEMORY | GMC_SRC_PITCH_OFFSET_CNTL); BEGIN_ACCEL(8); OUT_ACCEL_REG(SRC_PITCH_OFFSET, rinfo->dst_pitch_offset | ((rinfo->tilingEnabled && (ya <= info->var.yres_virtual)) ? DST_TILE_MACRO : 0)); OUT_ACCEL_REG(DST_PITCH_OFFSET, rinfo->dst_pitch_offset | ((rinfo->tilingEnabled && (yb <= info->var.yres_virtual)) ? DST_TILE_MACRO : 0)); #else /* Save for later clipping */ rinfo->dp_gui_master_cntl_clip = (rinfo->dp_gui_master_cntl | GMC_BRUSH_NONE | GMC_SRC_DATATYPE_COLOR | RADEON_ROP[rop].rop | DP_SRC_SOURCE_MEMORY); BEGIN_ACCEL(6); #endif OUT_ACCEL_REG(DP_GUI_MASTER_CNTL, rinfo->dp_gui_master_cntl_clip); OUT_ACCEL_REG(DP_WRITE_MSK, -1); OUT_ACCEL_REG(DP_CNTL, ((xdir >= 0 ? DST_X_LEFT_TO_RIGHT : 0) | (ydir >= 0 ? DST_Y_TOP_TO_BOTTOM : 0))); OUT_ACCEL_REG(SRC_Y_X, (ya << 16) | xa); OUT_ACCEL_REG(DST_Y_X, (yb << 16) | xb); OUT_ACCEL_REG(DST_HEIGHT_WIDTH, (h << 16) | w); FINISH_ACCEL(); } /* Setup for XAA mono 8x8 pattern color expansion. Patterns with * transparency use `bg == -1'. This routine is only used if the XAA * pixmap cache is turned on. */ void radeon_setup_for_mono_8x8_pattern_fill_mmio(struct fb_info *info, int patternx, int patterny, int fg, int bg, int rop, unsigned int planemask) { struct radeonfb_info *rinfo = info->par; unsigned char pattern[8]; ACCEL_PREAMBLE(); if (rinfo->big_endian) { /* Take care of endianness */ pattern[0] = (patternx & 0x000000ff); pattern[1] = (patternx & 0x0000ff00) >> 8; pattern[2] = (patternx & 0x00ff0000) >> 16; pattern[3] = (patternx & 0xff000000) >> 24; pattern[4] = (patterny & 0x000000ff); pattern[5] = (patterny & 0x0000ff00) >> 8; pattern[6] = (patterny & 0x00ff0000) >> 16; pattern[7] = (patterny & 0xff000000) >> 24; /* Save for later clipping */ rinfo->dp_gui_master_cntl_clip = (rinfo->dp_gui_master_cntl | (bg == -1 ? GMC_BRUSH_8X8_MONO_FG_LA : GMC_BRUSH_8X8_MONO_FG_BG) | RADEON_ROP[rop].pattern | GMC_BYTE_MSB_TO_LSB); } else /* Save for later clipping */ rinfo->dp_gui_master_cntl_clip = (rinfo->dp_gui_master_cntl | (bg == -1 ? GMC_BRUSH_8X8_MONO_FG_LA : GMC_BRUSH_8X8_MONO_FG_BG) | RADEON_ROP[rop].pattern); BEGIN_ACCEL((bg == -1) ? 5 : 6); OUT_ACCEL_REG(DP_GUI_MASTER_CNTL, rinfo->dp_gui_master_cntl_clip); OUT_ACCEL_REG(DP_WRITE_MSK, planemask); OUT_ACCEL_REG(DP_BRUSH_FRGD_CLR, fg); if (bg != -1) OUT_ACCEL_REG(DP_BRUSH_BKGD_CLR, bg); if (rinfo->big_endian) { OUT_ACCEL_REG(BRUSH_DATA0, *(unsigned long *) &pattern[0]); OUT_ACCEL_REG(BRUSH_DATA1, *(unsigned long *) &pattern[4]); } else { OUT_ACCEL_REG(BRUSH_DATA0, patternx); OUT_ACCEL_REG(BRUSH_DATA1, patterny); } FINISH_ACCEL(); } /* Subsequent XAA 8x8 pattern color expansion. Because they are used in * the setup function, `patternx' and `patterny' are not used here. */ void radeon_subsequent_mono_8x8_pattern_fill_rect_mmio(struct fb_info *info, int patternx, int patterny, int x, int y, int w, int h) { struct radeonfb_info *rinfo = info->par; ACCEL_PREAMBLE(); #ifdef RADEON_TILING BEGIN_ACCEL(4); OUT_ACCEL_REG(DST_PITCH_OFFSET, rinfo->dst_pitch_offset | ((rinfo->tilingEnabled && (y <= info->var.yres_virtual)) ? DST_TILE_MACRO : 0)); #else BEGIN_ACCEL(3); #endif OUT_ACCEL_REG(BRUSH_Y_X, (patterny << 8) | patternx); OUT_ACCEL_REG(DST_Y_X, (y << 16) | x); OUT_ACCEL_REG(DST_HEIGHT_WIDTH, (h << 16) | w); FINISH_ACCEL(); } /* Setup for XAA indirect CPU-to-screen color expansion (indirect). * Because of how the scratch buffer is initialized, this is really a * mainstore-to-screen color expansion. Transparency is supported when * `bg == -1'. */ void radeon_setup_for_scanline_cpu_to_screen_color_expand_fill_mmio(struct fb_info *info, int fg, int bg, int rop, unsigned int planemask) { struct radeonfb_info *rinfo = info->par; ACCEL_PREAMBLE(); /* Save for later clipping */ if (rinfo->big_endian) rinfo->dp_gui_master_cntl_clip = (rinfo->dp_gui_master_cntl | GMC_DST_CLIPPING | GMC_BRUSH_NONE | (bg == -1 ? GMC_SRC_DATATYPE_MONO_FG_LA : GMC_SRC_DATATYPE_MONO_FG_BG) | RADEON_ROP[rop].rop | GMC_BYTE_MSB_TO_LSB | DP_SRC_SOURCE_HOST_DATA); else rinfo->dp_gui_master_cntl_clip = (rinfo->dp_gui_master_cntl | GMC_DST_CLIPPING | GMC_BRUSH_NONE | (bg == -1 ? GMC_SRC_DATATYPE_MONO_FG_LA : GMC_SRC_DATATYPE_MONO_FG_BG) | RADEON_ROP[rop].rop | GMC_BYTE_LSB_TO_MSB | DP_SRC_SOURCE_HOST_DATA); if (rinfo->big_endian) { BEGIN_ACCEL(5); OUT_ACCEL_REG(RBBM_GUICNTL, HOST_DATA_SWAP_NONE); } else BEGIN_ACCEL(4); OUT_ACCEL_REG(DP_GUI_MASTER_CNTL, rinfo->dp_gui_master_cntl_clip); OUT_ACCEL_REG(DP_WRITE_MSK, planemask); OUT_ACCEL_REG(DP_SRC_FRGD_CLR, fg); OUT_ACCEL_REG(DP_SRC_BKGD_CLR, bg); FINISH_ACCEL(); } /* Subsequent XAA indirect CPU-to-screen color expansion. This is only * called once for each rectangle. */ void radeon_subsequent_scanline_cpu_to_screen_color_expand_fill_mmio(struct fb_info *info, int x, int y, int w, int h, int skipleft) { struct radeonfb_info *rinfo = info->par; ACCEL_PREAMBLE(); rinfo->scanline_h = h; rinfo->scanline_words = (w + 31) >> 5; #ifdef RADEON_TILING BEGIN_ACCEL(5); OUT_ACCEL_REG(DST_PITCH_OFFSET, rinfo->dst_pitch_offset | ((rinfo->tilingEnabled && (y <= info->var.yres_virtual)) ? DST_TILE_MACRO : 0)); #else BEGIN_ACCEL(4); #endif OUT_ACCEL_REG(SC_TOP_LEFT, (y << 16) | ((x+skipleft) & 0xffff)); OUT_ACCEL_REG(SC_BOTTOM_RIGHT, ((y+h) << 16) | ((x+w) & 0xffff)); OUT_ACCEL_REG(DST_Y_X, (y << 16) | (x & 0xffff)); /* Have to pad the width here and use clipping engine */ OUT_ACCEL_REG(DST_HEIGHT_WIDTH, (h << 16) | ((w + 31) & ~31)); FINISH_ACCEL(); } /* Subsequent XAA indirect CPU-to-screen color expansion and indirect * image write. This is called once for each scanline. */ void radeon_subsequent_scanline_mmio(struct fb_info *info, unsigned long *src) { struct radeonfb_info *rinfo = info->par; int left = rinfo->scanline_words; volatile unsigned long *d; ACCEL_PREAMBLE(); --rinfo->scanline_h; while(left) { if (left <= 8) { /* Last scanline - finish write to DATA_LAST */ if (rinfo->scanline_h == 0) { BEGIN_ACCEL(left); /* Unrolling doesn't improve performance */ for(d = ADDRREG(HOST_DATA_LAST) - (left - 1); left; --left) *d++ = *src++; return; } else { BEGIN_ACCEL(left); /* Unrolling doesn't improve performance */ for(d = ADDRREG(HOST_DATA7) - (left - 1); left; --left) *d++ = *src++; } } else { BEGIN_ACCEL(8); /* Unrolling doesn't improve performance */ d = ADDRREG(HOST_DATA0); *d++ = *src++; *d++ = *src++; *d++ = *src++; *d++ = *src++; *d++ = *src++; *d++ = *src++; *d++ = *src++; *d++ = *src++; left -= 8; } } FINISH_ACCEL(); } /* Setup for XAA indirect image write */ void radeon_setup_for_scanline_image_write_mmio(struct fb_info *info, int rop, unsigned int planemask, int trans_color, int bpp) { struct radeonfb_info *rinfo = info->par; ACCEL_PREAMBLE(); rinfo->scanline_bpp = bpp; /* Save for later clipping */ rinfo->dp_gui_master_cntl_clip = (rinfo->dp_gui_master_cntl | GMC_DST_CLIPPING | GMC_BRUSH_NONE | GMC_SRC_DATATYPE_COLOR | RADEON_ROP[rop].rop | GMC_BYTE_MSB_TO_LSB | DP_SRC_SOURCE_HOST_DATA); if (rinfo->big_endian) { BEGIN_ACCEL(3); if (bpp == 16) OUT_ACCEL_REG(RBBM_GUICNTL, HOST_DATA_SWAP_16BIT); else if (bpp == 32) OUT_ACCEL_REG(RBBM_GUICNTL, HOST_DATA_SWAP_32BIT); else OUT_ACCEL_REG(RBBM_GUICNTL, HOST_DATA_SWAP_NONE); } else BEGIN_ACCEL(2); OUT_ACCEL_REG(DP_GUI_MASTER_CNTL, rinfo->dp_gui_master_cntl_clip); OUT_ACCEL_REG(DP_WRITE_MSK, planemask); FINISH_ACCEL(); rinfo->trans_color = trans_color; if (trans_color != -1) radeon_set_transparency_mmio(rinfo, trans_color); } /* Subsequent XAA indirect image write. This is only called once for each rectangle. */ void radeon_subsequent_scanline_image_write_rect_mmio(struct fb_info *info, int x, int y, int w, int h, int skipleft) { struct radeonfb_info *rinfo = info->par; int shift = 0; /* 32bpp */ ACCEL_PREAMBLE(); if (rinfo->bpp == 8) shift = 3; else if (rinfo->bpp == 16) shift = 1; rinfo->scanline_h = h; rinfo->scanline_words = (w * rinfo->scanline_bpp + 31) >> 5; #ifdef RADEON_TILING BEGIN_ACCEL(5); OUT_ACCEL_REG(DST_PITCH_OFFSET, rinfo->dst_pitch_offset | ((rinfo->tilingEnabled && (y <= info->var.yres_virtual)) ? DST_TILE_MACRO : 0)); #else BEGIN_ACCEL(4); #endif OUT_ACCEL_REG(SC_TOP_LEFT, (y << 16) | ((x+skipleft) & 0xffff)); OUT_ACCEL_REG(SC_BOTTOM_RIGHT, ((y+h) << 16) | ((x+w) & 0xffff)); OUT_ACCEL_REG(DST_Y_X, (y << 16) | (x & 0xffff)); /* Have to pad the width here and use clipping engine */ OUT_ACCEL_REG(DST_HEIGHT_WIDTH, (h << 16) | ((w + shift) & ~shift)); FINISH_ACCEL(); } /* Set up the clipping rectangle */ void radeon_set_clipping_rectangle_mmio(struct fb_info *info, int xa, int ya, int xb, int yb) { struct radeonfb_info *rinfo = info->par; unsigned long tmp1 = 0; unsigned long tmp2 = 0; ACCEL_PREAMBLE(); if (xa < 0) { tmp1 = (-xa) & 0x3fff; tmp1 |= SC_SIGN_MASK_LO; } else tmp1 = xa; if (ya < 0) { tmp1 |= (((-ya) & 0x3fff) << 16); tmp1 |= SC_SIGN_MASK_HI; } else tmp1 |= (ya << 16); xb++; yb++; if (xb < 0) { tmp2 = (-xb) & 0x3fff; tmp2 |= SC_SIGN_MASK_LO; } else tmp2 = xb; if (yb < 0) { tmp2 |= (((-yb) & 0x3fff) << 16); tmp2 |= SC_SIGN_MASK_HI; } else tmp2 |= (yb << 16); BEGIN_ACCEL(3); OUT_ACCEL_REG(DP_GUI_MASTER_CNTL, (rinfo->dp_gui_master_cntl_clip | GMC_DST_CLIPPING)); OUT_ACCEL_REG(SC_TOP_LEFT, tmp1); OUT_ACCEL_REG(SC_BOTTOM_RIGHT, tmp2); FINISH_ACCEL(); if (rinfo->trans_color != -1) radeon_set_transparency_mmio(rinfo, rinfo->trans_color); } /* Disable the clipping rectangle */ void radeon_disable_clipping_mmio(struct fb_info *info) { struct radeonfb_info *rinfo = info->par; ACCEL_PREAMBLE(); BEGIN_ACCEL(3); OUT_ACCEL_REG(DP_GUI_MASTER_CNTL, rinfo->dp_gui_master_cntl_clip); OUT_ACCEL_REG(SC_TOP_LEFT, 0); OUT_ACCEL_REG(SC_BOTTOM_RIGHT, (DEFAULT_SC_RIGHT_MAX | DEFAULT_SC_BOTTOM_MAX)); FINISH_ACCEL(); if (rinfo->trans_color != -1) radeon_set_transparency_mmio(rinfo, rinfo->trans_color); } #if 0 /* Change surfaces The idea here is to only set up front buffer as tiled, and back/depth buffer when needed. Everything else is left as untiled. This means we need to use eplicit src/dst pitch control when blitting, based on the src/target address, and can no longer use a default offset. But OTOH we don't need to dynamically change surfaces (for xv for instance), and some ugly offset / fb reservation (cursor) is gone. And as a bonus, everything actually works... All surface addresses are relative to MC_FB_LOCATION */ void RADEONChangeSurfaces(struct fb_info *info) { struct radeonfb_info *rinfo = info->par; int cpp = rinfo->bpp >> 3; /* depth/front/back pitch must be identical (and the same as displayWidth) */ int width_bytes = info->var.xres_virtual * cpp; int bufferSize = (((((info->var.yres_virtual + 15) & ~15) * width_bytes) + RADEON_BUFFER_ALIGN) & ~RADEON_BUFFER_ALIGN); unsigned int depth_pattern, color_pattern, swap_pattern, surf_info; if (rinfo->big_endian) { switch(rinfo->bpp) { case 16: swap_pattern = SURF_AP0_SWP_16BPP | SURF_AP1_SWP_16BPP; break; case 32: swap_pattern = SURF_AP0_SWP_32BPP | SURF_AP1_SWP_32BPP; break; default: swap_pattern = 0; } } else swap_pattern = 0; if (rinfo->family < CHIP_FAMILY_R200) { color_pattern = SURF_TILE_COLOR_MACRO; if (cpp == 2) depth_pattern = SURF_TILE_DEPTH_16BPP; else depth_pattern = SURF_TILE_DEPTH_32BPP; } else if (rinfo->family >= CHIP_FAMILY_R300) { color_pattern = R300_SURF_TILE_COLOR_MACRO; if (cpp == 2) depth_pattern = R300_SURF_TILE_COLOR_MACRO; else depth_pattern = R300_SURF_TILE_COLOR_MACRO | R300_SURF_TILE_DEPTH_32BPP; } else { color_pattern = R200_SURF_TILE_COLOR_MACRO; if (cpp == 2) depth_pattern = R200_SURF_TILE_DEPTH_16BPP; else depth_pattern = R200_SURF_TILE_DEPTH_32BPP; } /* we don't need anything like WaitForFifo, no? */ #ifdef RADEON_TILING if (rinfo->tilingEnabled) { if (rinfo->family >= CHIP_FAMILY_R300) surf_info = swap_pattern | (width_bytes / 8) | color_pattern; else surf_info = swap_pattern | (width_bytes / 16) | color_pattern; } else #endif surf_info = 0; OUTREG(SURFACE0_INFO, surf_info); OUTREG(SURFACE0_LOWER_BOUND, 0); OUTREG(SURFACE0_UPPER_BOUND, bufferSize - 1); } #endif /* The FIFO has 64 slots. This routines waits until at least `entries' * of these slots are empty. */ void radeon_wait_for_fifo_function(struct radeonfb_info *rinfo, int entries) { int i; while(1) { for(i = 0; i < RADEON_TIMEOUT; i++) { rinfo->fifo_slots = INREG(RBBM_STATUS) & RBBM_FIFOCNT_MASK; if (rinfo->fifo_slots >= entries) return; } radeon_engine_reset(rinfo); radeon_engine_restore(rinfo); } } /* Flush all dirty data in the Pixel Cache to memory */ void radeon_engine_flush(struct radeonfb_info *rinfo) { int i; OUTREGP(RB2D_DSTCACHE_CTLSTAT, RB2D_DC_FLUSH_ALL, ~RB2D_DC_FLUSH_ALL); for(i = 0; i < RADEON_TIMEOUT; i++) { if (!(INREG(RB2D_DSTCACHE_CTLSTAT) & RB2D_DC_BUSY)) break; } } /* Reset graphics card to known state */ void radeon_engine_reset(struct radeonfb_info *rinfo) { unsigned long clock_cntl_index; unsigned long mclk_cntl; unsigned long rbbm_soft_reset; unsigned long host_path_cntl; radeon_engine_flush(rinfo); clock_cntl_index = INREG(CLOCK_CNTL_INDEX); /* Some ASICs have bugs with dynamic-on feature, which are * ASIC-version dependent, so we force all blocks on for now */ if (rinfo->has_CRTC2) { unsigned long tmp; tmp = INPLL(SCLK_CNTL); OUTPLL(SCLK_CNTL, ((tmp & ~DYN_STOP_LAT_MASK) | CP_MAX_DYN_STOP_LAT | SCLK_FORCEON_MASK)); if (rinfo->family == CHIP_FAMILY_RV200) { tmp = INPLL(SCLK_MORE_CNTL); OUTPLL(SCLK_MORE_CNTL, tmp | SCLK_MORE_FORCEON); } } mclk_cntl = INPLL(MCLK_CNTL); OUTPLL(MCLK_CNTL, (mclk_cntl | FORCEON_MCLKA | FORCEON_MCLKB | FORCEON_YCLKA | FORCEON_YCLKB | FORCEON_MC | FORCEON_AIC)); /* Soft resetting HDP thru RBBM_SOFT_RESET register can cause some * unexpected behaviour on some machines. Here we use * HOST_PATH_CNTL to reset it. */ host_path_cntl = INREG(HOST_PATH_CNTL); rbbm_soft_reset = INREG(RBBM_SOFT_RESET); if ((rinfo->family == CHIP_FAMILY_R300) || (rinfo->family == CHIP_FAMILY_R350) || (rinfo->family == CHIP_FAMILY_RV350)) { unsigned long tmp; OUTREG(RBBM_SOFT_RESET, (rbbm_soft_reset | SOFT_RESET_CP | SOFT_RESET_HI | SOFT_RESET_E2)); INREG(RBBM_SOFT_RESET); OUTREG(RBBM_SOFT_RESET, 0); tmp = INREG(RB2D_DSTCACHE_MODE); OUTREG(RB2D_DSTCACHE_MODE, tmp | (1 << 17)); /* FIXME */ } else { OUTREG(RBBM_SOFT_RESET, (rbbm_soft_reset | SOFT_RESET_CP | SOFT_RESET_HI | SOFT_RESET_SE | SOFT_RESET_RE | SOFT_RESET_PP | SOFT_RESET_E2 | SOFT_RESET_RB)); INREG(RBBM_SOFT_RESET); OUTREG(RBBM_SOFT_RESET, (rbbm_soft_reset & (unsigned long) ~(SOFT_RESET_CP | SOFT_RESET_HI | SOFT_RESET_SE | SOFT_RESET_RE | SOFT_RESET_PP | SOFT_RESET_E2 | SOFT_RESET_RB))); INREG(RBBM_SOFT_RESET); } OUTREG(HOST_PATH_CNTL, host_path_cntl | HDP_SOFT_RESET); INREG(HOST_PATH_CNTL); OUTREG(HOST_PATH_CNTL, host_path_cntl); if ((rinfo->family != CHIP_FAMILY_R300) && (rinfo->family != CHIP_FAMILY_R350) && (rinfo->family != CHIP_FAMILY_RV350)) OUTREG(RBBM_SOFT_RESET, rbbm_soft_reset); OUTREG(CLOCK_CNTL_INDEX, clock_cntl_index); OUTPLL(MCLK_CNTL, mclk_cntl); } /* Restore the acceleration hardware to its previous state */ void radeon_engine_restore(struct radeonfb_info *rinfo) { BEGIN_ACCEL(1); /* NOTE: The following RB2D_DSTCACHE_MODE setting will cause the * R300 to hang. ATI does not see a reason to change it from the * default BIOS settings (even on non-R300 cards). This setting * might be removed in future versions of the Radeon driver. */ /* Turn of all automatic flushing - we'll do it all */ if ((rinfo->family != CHIP_FAMILY_R300) && (rinfo->family != CHIP_FAMILY_R350) && (rinfo->family != CHIP_FAMILY_RV350)) OUTREG(RB2D_DSTCACHE_MODE, 0); rinfo->fb_local_base = INREG(MC_FB_LOCATION) << 16; BEGIN_ACCEL(3); OUTREG(DEFAULT_PITCH_OFFSET, rinfo->dst_pitch_offset); OUTREG(DST_PITCH_OFFSET, rinfo->dst_pitch_offset); OUTREG(SRC_PITCH_OFFSET, rinfo->dst_pitch_offset); BEGIN_ACCEL(1); if (rinfo->big_endian) OUTREGP(DP_DATATYPE, HOST_BIG_ENDIAN_EN, ~HOST_BIG_ENDIAN_EN); else OUTREGP(DP_DATATYPE, 0, ~HOST_BIG_ENDIAN_EN); /* Restore SURFACE_CNTL - only the first head contains valid data */ OUTREG(SURFACE_CNTL, rinfo->state.surface_cntl); BEGIN_ACCEL(2); OUTREG(DEFAULT_SC_TOP_LEFT, 0); OUTREG(DEFAULT_SC_BOTTOM_RIGHT, (DEFAULT_SC_RIGHT_MAX | DEFAULT_SC_BOTTOM_MAX)); BEGIN_ACCEL(1); OUTREG(DP_GUI_MASTER_CNTL, (rinfo->dp_gui_master_cntl | GMC_BRUSH_SOLID_COLOR | GMC_SRC_DATATYPE_COLOR)); BEGIN_ACCEL(7); OUTREG(DST_LINE_START, 0); OUTREG(DST_LINE_END, 0); OUTREG(DP_BRUSH_FRGD_CLR, 0xffffffff); OUTREG(DP_BRUSH_BKGD_CLR, 0x00000000); OUTREG(DP_SRC_FRGD_CLR, 0xffffffff); OUTREG(DP_SRC_BKGD_CLR, 0x00000000); OUTREG(DP_WRITE_MSK, 0xffffffff); radeon_wait_for_idle_mmio(rinfo); } /* Initialize the acceleration hardware */ void radeon_engine_init(struct radeonfb_info *rinfo) { unsigned long temp; OUTREG(RB3D_CNTL, 0); radeon_engine_reset(rinfo); temp = radeon_get_dstbpp(rinfo->depth); #ifdef RADEON_TILING rinfo->dp_gui_master_cntl = ((temp << GMC_DST_DATATYPE_SHIFT) | GMC_CLR_CMP_CNTL_DIS | GMC_DST_PITCH_OFFSET_CNTL); #else rinfo->dp_gui_master_cntl = ((temp << GMC_DST_DATATYPE_SHIFT) | GMC_CLR_CMP_CNTL_DIS); #endif radeon_engine_restore(rinfo); }