1*b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 2f7018c21STomi Valkeinen #include "radeonfb.h" 3f7018c21STomi Valkeinen 4f7018c21STomi Valkeinen /* the accelerated functions here are patterned after the 5f7018c21STomi Valkeinen * "ACCEL_MMIO" ifdef branches in XFree86 6f7018c21STomi Valkeinen * --dte 7f7018c21STomi Valkeinen */ 8f7018c21STomi Valkeinen 9f7018c21STomi Valkeinen static void radeon_fixup_offset(struct radeonfb_info *rinfo) 10f7018c21STomi Valkeinen { 11f7018c21STomi Valkeinen u32 local_base; 12f7018c21STomi Valkeinen 13f7018c21STomi Valkeinen /* *** Ugly workaround *** */ 14f7018c21STomi Valkeinen /* 15f7018c21STomi Valkeinen * On some platforms, the video memory is mapped at 0 in radeon chip space 16f7018c21STomi Valkeinen * (like PPCs) by the firmware. X will always move it up so that it's seen 17f7018c21STomi Valkeinen * by the chip to be at the same address as the PCI BAR. 18f7018c21STomi Valkeinen * That means that when switching back from X, there is a mismatch between 19f7018c21STomi Valkeinen * the offsets programmed into the engine. This means that potentially, 20f7018c21STomi Valkeinen * accel operations done before radeonfb has a chance to re-init the engine 21f7018c21STomi Valkeinen * will have incorrect offsets, and potentially trash system memory ! 22f7018c21STomi Valkeinen * 23f7018c21STomi Valkeinen * The correct fix is for fbcon to never call any accel op before the engine 24f7018c21STomi Valkeinen * has properly been re-initialized (by a call to set_var), but this is a 25f7018c21STomi Valkeinen * complex fix. This workaround in the meantime, called before every accel 26f7018c21STomi Valkeinen * operation, makes sure the offsets are in sync. 27f7018c21STomi Valkeinen */ 28f7018c21STomi Valkeinen 29f7018c21STomi Valkeinen radeon_fifo_wait (1); 30f7018c21STomi Valkeinen local_base = INREG(MC_FB_LOCATION) << 16; 31f7018c21STomi Valkeinen if (local_base == rinfo->fb_local_base) 32f7018c21STomi Valkeinen return; 33f7018c21STomi Valkeinen 34f7018c21STomi Valkeinen rinfo->fb_local_base = local_base; 35f7018c21STomi Valkeinen 36f7018c21STomi Valkeinen radeon_fifo_wait (3); 37f7018c21STomi Valkeinen OUTREG(DEFAULT_PITCH_OFFSET, (rinfo->pitch << 0x16) | 38f7018c21STomi Valkeinen (rinfo->fb_local_base >> 10)); 39f7018c21STomi Valkeinen OUTREG(DST_PITCH_OFFSET, (rinfo->pitch << 0x16) | (rinfo->fb_local_base >> 10)); 40f7018c21STomi Valkeinen OUTREG(SRC_PITCH_OFFSET, (rinfo->pitch << 0x16) | (rinfo->fb_local_base >> 10)); 41f7018c21STomi Valkeinen } 42f7018c21STomi Valkeinen 43f7018c21STomi Valkeinen static void radeonfb_prim_fillrect(struct radeonfb_info *rinfo, 44f7018c21STomi Valkeinen const struct fb_fillrect *region) 45f7018c21STomi Valkeinen { 46f7018c21STomi Valkeinen radeon_fifo_wait(4); 47f7018c21STomi Valkeinen 48f7018c21STomi Valkeinen OUTREG(DP_GUI_MASTER_CNTL, 49f7018c21STomi Valkeinen rinfo->dp_gui_master_cntl /* contains, like GMC_DST_32BPP */ 50f7018c21STomi Valkeinen | GMC_BRUSH_SOLID_COLOR 51f7018c21STomi Valkeinen | ROP3_P); 52f7018c21STomi Valkeinen if (radeon_get_dstbpp(rinfo->depth) != DST_8BPP) 53f7018c21STomi Valkeinen OUTREG(DP_BRUSH_FRGD_CLR, rinfo->pseudo_palette[region->color]); 54f7018c21STomi Valkeinen else 55f7018c21STomi Valkeinen OUTREG(DP_BRUSH_FRGD_CLR, region->color); 56f7018c21STomi Valkeinen OUTREG(DP_WRITE_MSK, 0xffffffff); 57f7018c21STomi Valkeinen OUTREG(DP_CNTL, (DST_X_LEFT_TO_RIGHT | DST_Y_TOP_TO_BOTTOM)); 58f7018c21STomi Valkeinen 59f7018c21STomi Valkeinen radeon_fifo_wait(2); 60f7018c21STomi Valkeinen OUTREG(DSTCACHE_CTLSTAT, RB2D_DC_FLUSH_ALL); 61f7018c21STomi Valkeinen OUTREG(WAIT_UNTIL, (WAIT_2D_IDLECLEAN | WAIT_DMA_GUI_IDLE)); 62f7018c21STomi Valkeinen 63f7018c21STomi Valkeinen radeon_fifo_wait(2); 64f7018c21STomi Valkeinen OUTREG(DST_Y_X, (region->dy << 16) | region->dx); 65f7018c21STomi Valkeinen OUTREG(DST_WIDTH_HEIGHT, (region->width << 16) | region->height); 66f7018c21STomi Valkeinen } 67f7018c21STomi Valkeinen 68f7018c21STomi Valkeinen void radeonfb_fillrect(struct fb_info *info, const struct fb_fillrect *region) 69f7018c21STomi Valkeinen { 70f7018c21STomi Valkeinen struct radeonfb_info *rinfo = info->par; 71f7018c21STomi Valkeinen struct fb_fillrect modded; 72f7018c21STomi Valkeinen int vxres, vyres; 73f7018c21STomi Valkeinen 74f7018c21STomi Valkeinen if (info->state != FBINFO_STATE_RUNNING) 75f7018c21STomi Valkeinen return; 76f7018c21STomi Valkeinen if (info->flags & FBINFO_HWACCEL_DISABLED) { 77f7018c21STomi Valkeinen cfb_fillrect(info, region); 78f7018c21STomi Valkeinen return; 79f7018c21STomi Valkeinen } 80f7018c21STomi Valkeinen 81f7018c21STomi Valkeinen radeon_fixup_offset(rinfo); 82f7018c21STomi Valkeinen 83f7018c21STomi Valkeinen vxres = info->var.xres_virtual; 84f7018c21STomi Valkeinen vyres = info->var.yres_virtual; 85f7018c21STomi Valkeinen 86f7018c21STomi Valkeinen memcpy(&modded, region, sizeof(struct fb_fillrect)); 87f7018c21STomi Valkeinen 88f7018c21STomi Valkeinen if(!modded.width || !modded.height || 89f7018c21STomi Valkeinen modded.dx >= vxres || modded.dy >= vyres) 90f7018c21STomi Valkeinen return; 91f7018c21STomi Valkeinen 92f7018c21STomi Valkeinen if(modded.dx + modded.width > vxres) modded.width = vxres - modded.dx; 93f7018c21STomi Valkeinen if(modded.dy + modded.height > vyres) modded.height = vyres - modded.dy; 94f7018c21STomi Valkeinen 95f7018c21STomi Valkeinen radeonfb_prim_fillrect(rinfo, &modded); 96f7018c21STomi Valkeinen } 97f7018c21STomi Valkeinen 98f7018c21STomi Valkeinen static void radeonfb_prim_copyarea(struct radeonfb_info *rinfo, 99f7018c21STomi Valkeinen const struct fb_copyarea *area) 100f7018c21STomi Valkeinen { 101f7018c21STomi Valkeinen int xdir, ydir; 102f7018c21STomi Valkeinen u32 sx, sy, dx, dy, w, h; 103f7018c21STomi Valkeinen 104f7018c21STomi Valkeinen w = area->width; h = area->height; 105f7018c21STomi Valkeinen dx = area->dx; dy = area->dy; 106f7018c21STomi Valkeinen sx = area->sx; sy = area->sy; 107f7018c21STomi Valkeinen xdir = sx - dx; 108f7018c21STomi Valkeinen ydir = sy - dy; 109f7018c21STomi Valkeinen 110f7018c21STomi Valkeinen if ( xdir < 0 ) { sx += w-1; dx += w-1; } 111f7018c21STomi Valkeinen if ( ydir < 0 ) { sy += h-1; dy += h-1; } 112f7018c21STomi Valkeinen 113f7018c21STomi Valkeinen radeon_fifo_wait(3); 114f7018c21STomi Valkeinen OUTREG(DP_GUI_MASTER_CNTL, 115f7018c21STomi Valkeinen rinfo->dp_gui_master_cntl /* i.e. GMC_DST_32BPP */ 116f7018c21STomi Valkeinen | GMC_BRUSH_NONE 117f7018c21STomi Valkeinen | GMC_SRC_DSTCOLOR 118f7018c21STomi Valkeinen | ROP3_S 119f7018c21STomi Valkeinen | DP_SRC_SOURCE_MEMORY ); 120f7018c21STomi Valkeinen OUTREG(DP_WRITE_MSK, 0xffffffff); 121f7018c21STomi Valkeinen OUTREG(DP_CNTL, (xdir>=0 ? DST_X_LEFT_TO_RIGHT : 0) 122f7018c21STomi Valkeinen | (ydir>=0 ? DST_Y_TOP_TO_BOTTOM : 0)); 123f7018c21STomi Valkeinen 124f7018c21STomi Valkeinen radeon_fifo_wait(2); 125f7018c21STomi Valkeinen OUTREG(DSTCACHE_CTLSTAT, RB2D_DC_FLUSH_ALL); 126f7018c21STomi Valkeinen OUTREG(WAIT_UNTIL, (WAIT_2D_IDLECLEAN | WAIT_DMA_GUI_IDLE)); 127f7018c21STomi Valkeinen 128f7018c21STomi Valkeinen radeon_fifo_wait(3); 129f7018c21STomi Valkeinen OUTREG(SRC_Y_X, (sy << 16) | sx); 130f7018c21STomi Valkeinen OUTREG(DST_Y_X, (dy << 16) | dx); 131f7018c21STomi Valkeinen OUTREG(DST_HEIGHT_WIDTH, (h << 16) | w); 132f7018c21STomi Valkeinen } 133f7018c21STomi Valkeinen 134f7018c21STomi Valkeinen 135f7018c21STomi Valkeinen void radeonfb_copyarea(struct fb_info *info, const struct fb_copyarea *area) 136f7018c21STomi Valkeinen { 137f7018c21STomi Valkeinen struct radeonfb_info *rinfo = info->par; 138f7018c21STomi Valkeinen struct fb_copyarea modded; 139f7018c21STomi Valkeinen u32 vxres, vyres; 140f7018c21STomi Valkeinen modded.sx = area->sx; 141f7018c21STomi Valkeinen modded.sy = area->sy; 142f7018c21STomi Valkeinen modded.dx = area->dx; 143f7018c21STomi Valkeinen modded.dy = area->dy; 144f7018c21STomi Valkeinen modded.width = area->width; 145f7018c21STomi Valkeinen modded.height = area->height; 146f7018c21STomi Valkeinen 147f7018c21STomi Valkeinen if (info->state != FBINFO_STATE_RUNNING) 148f7018c21STomi Valkeinen return; 149f7018c21STomi Valkeinen if (info->flags & FBINFO_HWACCEL_DISABLED) { 150f7018c21STomi Valkeinen cfb_copyarea(info, area); 151f7018c21STomi Valkeinen return; 152f7018c21STomi Valkeinen } 153f7018c21STomi Valkeinen 154f7018c21STomi Valkeinen radeon_fixup_offset(rinfo); 155f7018c21STomi Valkeinen 156f7018c21STomi Valkeinen vxres = info->var.xres_virtual; 157f7018c21STomi Valkeinen vyres = info->var.yres_virtual; 158f7018c21STomi Valkeinen 159f7018c21STomi Valkeinen if(!modded.width || !modded.height || 160f7018c21STomi Valkeinen modded.sx >= vxres || modded.sy >= vyres || 161f7018c21STomi Valkeinen modded.dx >= vxres || modded.dy >= vyres) 162f7018c21STomi Valkeinen return; 163f7018c21STomi Valkeinen 164f7018c21STomi Valkeinen if(modded.sx + modded.width > vxres) modded.width = vxres - modded.sx; 165f7018c21STomi Valkeinen if(modded.dx + modded.width > vxres) modded.width = vxres - modded.dx; 166f7018c21STomi Valkeinen if(modded.sy + modded.height > vyres) modded.height = vyres - modded.sy; 167f7018c21STomi Valkeinen if(modded.dy + modded.height > vyres) modded.height = vyres - modded.dy; 168f7018c21STomi Valkeinen 169f7018c21STomi Valkeinen radeonfb_prim_copyarea(rinfo, &modded); 170f7018c21STomi Valkeinen } 171f7018c21STomi Valkeinen 172f7018c21STomi Valkeinen void radeonfb_imageblit(struct fb_info *info, const struct fb_image *image) 173f7018c21STomi Valkeinen { 174f7018c21STomi Valkeinen struct radeonfb_info *rinfo = info->par; 175f7018c21STomi Valkeinen 176f7018c21STomi Valkeinen if (info->state != FBINFO_STATE_RUNNING) 177f7018c21STomi Valkeinen return; 178f7018c21STomi Valkeinen radeon_engine_idle(); 179f7018c21STomi Valkeinen 180f7018c21STomi Valkeinen cfb_imageblit(info, image); 181f7018c21STomi Valkeinen } 182f7018c21STomi Valkeinen 183f7018c21STomi Valkeinen int radeonfb_sync(struct fb_info *info) 184f7018c21STomi Valkeinen { 185f7018c21STomi Valkeinen struct radeonfb_info *rinfo = info->par; 186f7018c21STomi Valkeinen 187f7018c21STomi Valkeinen if (info->state != FBINFO_STATE_RUNNING) 188f7018c21STomi Valkeinen return 0; 189f7018c21STomi Valkeinen radeon_engine_idle(); 190f7018c21STomi Valkeinen 191f7018c21STomi Valkeinen return 0; 192f7018c21STomi Valkeinen } 193f7018c21STomi Valkeinen 194f7018c21STomi Valkeinen void radeonfb_engine_reset(struct radeonfb_info *rinfo) 195f7018c21STomi Valkeinen { 196f7018c21STomi Valkeinen u32 clock_cntl_index, mclk_cntl, rbbm_soft_reset; 197f7018c21STomi Valkeinen u32 host_path_cntl; 198f7018c21STomi Valkeinen 199f7018c21STomi Valkeinen radeon_engine_flush (rinfo); 200f7018c21STomi Valkeinen 201f7018c21STomi Valkeinen clock_cntl_index = INREG(CLOCK_CNTL_INDEX); 202f7018c21STomi Valkeinen mclk_cntl = INPLL(MCLK_CNTL); 203f7018c21STomi Valkeinen 204f7018c21STomi Valkeinen OUTPLL(MCLK_CNTL, (mclk_cntl | 205f7018c21STomi Valkeinen FORCEON_MCLKA | 206f7018c21STomi Valkeinen FORCEON_MCLKB | 207f7018c21STomi Valkeinen FORCEON_YCLKA | 208f7018c21STomi Valkeinen FORCEON_YCLKB | 209f7018c21STomi Valkeinen FORCEON_MC | 210f7018c21STomi Valkeinen FORCEON_AIC)); 211f7018c21STomi Valkeinen 212f7018c21STomi Valkeinen host_path_cntl = INREG(HOST_PATH_CNTL); 213f7018c21STomi Valkeinen rbbm_soft_reset = INREG(RBBM_SOFT_RESET); 214f7018c21STomi Valkeinen 215f7018c21STomi Valkeinen if (IS_R300_VARIANT(rinfo)) { 216f7018c21STomi Valkeinen u32 tmp; 217f7018c21STomi Valkeinen 218f7018c21STomi Valkeinen OUTREG(RBBM_SOFT_RESET, (rbbm_soft_reset | 219f7018c21STomi Valkeinen SOFT_RESET_CP | 220f7018c21STomi Valkeinen SOFT_RESET_HI | 221f7018c21STomi Valkeinen SOFT_RESET_E2)); 222f7018c21STomi Valkeinen INREG(RBBM_SOFT_RESET); 223f7018c21STomi Valkeinen OUTREG(RBBM_SOFT_RESET, 0); 224f7018c21STomi Valkeinen tmp = INREG(RB2D_DSTCACHE_MODE); 225f7018c21STomi Valkeinen OUTREG(RB2D_DSTCACHE_MODE, tmp | (1 << 17)); /* FIXME */ 226f7018c21STomi Valkeinen } else { 227f7018c21STomi Valkeinen OUTREG(RBBM_SOFT_RESET, rbbm_soft_reset | 228f7018c21STomi Valkeinen SOFT_RESET_CP | 229f7018c21STomi Valkeinen SOFT_RESET_HI | 230f7018c21STomi Valkeinen SOFT_RESET_SE | 231f7018c21STomi Valkeinen SOFT_RESET_RE | 232f7018c21STomi Valkeinen SOFT_RESET_PP | 233f7018c21STomi Valkeinen SOFT_RESET_E2 | 234f7018c21STomi Valkeinen SOFT_RESET_RB); 235f7018c21STomi Valkeinen INREG(RBBM_SOFT_RESET); 236f7018c21STomi Valkeinen OUTREG(RBBM_SOFT_RESET, rbbm_soft_reset & (u32) 237f7018c21STomi Valkeinen ~(SOFT_RESET_CP | 238f7018c21STomi Valkeinen SOFT_RESET_HI | 239f7018c21STomi Valkeinen SOFT_RESET_SE | 240f7018c21STomi Valkeinen SOFT_RESET_RE | 241f7018c21STomi Valkeinen SOFT_RESET_PP | 242f7018c21STomi Valkeinen SOFT_RESET_E2 | 243f7018c21STomi Valkeinen SOFT_RESET_RB)); 244f7018c21STomi Valkeinen INREG(RBBM_SOFT_RESET); 245f7018c21STomi Valkeinen } 246f7018c21STomi Valkeinen 247f7018c21STomi Valkeinen OUTREG(HOST_PATH_CNTL, host_path_cntl | HDP_SOFT_RESET); 248f7018c21STomi Valkeinen INREG(HOST_PATH_CNTL); 249f7018c21STomi Valkeinen OUTREG(HOST_PATH_CNTL, host_path_cntl); 250f7018c21STomi Valkeinen 251f7018c21STomi Valkeinen if (!IS_R300_VARIANT(rinfo)) 252f7018c21STomi Valkeinen OUTREG(RBBM_SOFT_RESET, rbbm_soft_reset); 253f7018c21STomi Valkeinen 254f7018c21STomi Valkeinen OUTREG(CLOCK_CNTL_INDEX, clock_cntl_index); 255f7018c21STomi Valkeinen OUTPLL(MCLK_CNTL, mclk_cntl); 256f7018c21STomi Valkeinen } 257f7018c21STomi Valkeinen 258f7018c21STomi Valkeinen void radeonfb_engine_init (struct radeonfb_info *rinfo) 259f7018c21STomi Valkeinen { 260f7018c21STomi Valkeinen unsigned long temp; 261f7018c21STomi Valkeinen 262f7018c21STomi Valkeinen /* disable 3D engine */ 263f7018c21STomi Valkeinen OUTREG(RB3D_CNTL, 0); 264f7018c21STomi Valkeinen 265f7018c21STomi Valkeinen radeonfb_engine_reset(rinfo); 266f7018c21STomi Valkeinen 267f7018c21STomi Valkeinen radeon_fifo_wait (1); 268f7018c21STomi Valkeinen if (IS_R300_VARIANT(rinfo)) { 269f7018c21STomi Valkeinen OUTREG(RB2D_DSTCACHE_MODE, INREG(RB2D_DSTCACHE_MODE) | 270f7018c21STomi Valkeinen RB2D_DC_AUTOFLUSH_ENABLE | 271f7018c21STomi Valkeinen RB2D_DC_DC_DISABLE_IGNORE_PE); 272f7018c21STomi Valkeinen } else { 273f7018c21STomi Valkeinen /* This needs to be double checked with ATI. Latest X driver 274f7018c21STomi Valkeinen * completely "forgets" to set this register on < r3xx, and 275f7018c21STomi Valkeinen * we used to just write 0 there... I'll keep the 0 and update 276f7018c21STomi Valkeinen * that when we have sorted things out on X side. 277f7018c21STomi Valkeinen */ 278f7018c21STomi Valkeinen OUTREG(RB2D_DSTCACHE_MODE, 0); 279f7018c21STomi Valkeinen } 280f7018c21STomi Valkeinen 281f7018c21STomi Valkeinen radeon_fifo_wait (3); 282f7018c21STomi Valkeinen /* We re-read MC_FB_LOCATION from card as it can have been 283f7018c21STomi Valkeinen * modified by XFree drivers (ouch !) 284f7018c21STomi Valkeinen */ 285f7018c21STomi Valkeinen rinfo->fb_local_base = INREG(MC_FB_LOCATION) << 16; 286f7018c21STomi Valkeinen 287f7018c21STomi Valkeinen OUTREG(DEFAULT_PITCH_OFFSET, (rinfo->pitch << 0x16) | 288f7018c21STomi Valkeinen (rinfo->fb_local_base >> 10)); 289f7018c21STomi Valkeinen OUTREG(DST_PITCH_OFFSET, (rinfo->pitch << 0x16) | (rinfo->fb_local_base >> 10)); 290f7018c21STomi Valkeinen OUTREG(SRC_PITCH_OFFSET, (rinfo->pitch << 0x16) | (rinfo->fb_local_base >> 10)); 291f7018c21STomi Valkeinen 292f7018c21STomi Valkeinen radeon_fifo_wait (1); 293f7018c21STomi Valkeinen #if defined(__BIG_ENDIAN) 294f7018c21STomi Valkeinen OUTREGP(DP_DATATYPE, HOST_BIG_ENDIAN_EN, ~HOST_BIG_ENDIAN_EN); 295f7018c21STomi Valkeinen #else 296f7018c21STomi Valkeinen OUTREGP(DP_DATATYPE, 0, ~HOST_BIG_ENDIAN_EN); 297f7018c21STomi Valkeinen #endif 298f7018c21STomi Valkeinen radeon_fifo_wait (2); 299f7018c21STomi Valkeinen OUTREG(DEFAULT_SC_TOP_LEFT, 0); 300f7018c21STomi Valkeinen OUTREG(DEFAULT_SC_BOTTOM_RIGHT, (DEFAULT_SC_RIGHT_MAX | 301f7018c21STomi Valkeinen DEFAULT_SC_BOTTOM_MAX)); 302f7018c21STomi Valkeinen 303f7018c21STomi Valkeinen temp = radeon_get_dstbpp(rinfo->depth); 304f7018c21STomi Valkeinen rinfo->dp_gui_master_cntl = ((temp << 8) | GMC_CLR_CMP_CNTL_DIS); 305f7018c21STomi Valkeinen 306f7018c21STomi Valkeinen radeon_fifo_wait (1); 307f7018c21STomi Valkeinen OUTREG(DP_GUI_MASTER_CNTL, (rinfo->dp_gui_master_cntl | 308f7018c21STomi Valkeinen GMC_BRUSH_SOLID_COLOR | 309f7018c21STomi Valkeinen GMC_SRC_DATATYPE_COLOR)); 310f7018c21STomi Valkeinen 311f7018c21STomi Valkeinen radeon_fifo_wait (7); 312f7018c21STomi Valkeinen 313f7018c21STomi Valkeinen /* clear line drawing regs */ 314f7018c21STomi Valkeinen OUTREG(DST_LINE_START, 0); 315f7018c21STomi Valkeinen OUTREG(DST_LINE_END, 0); 316f7018c21STomi Valkeinen 317f7018c21STomi Valkeinen /* set brush color regs */ 318f7018c21STomi Valkeinen OUTREG(DP_BRUSH_FRGD_CLR, 0xffffffff); 319f7018c21STomi Valkeinen OUTREG(DP_BRUSH_BKGD_CLR, 0x00000000); 320f7018c21STomi Valkeinen 321f7018c21STomi Valkeinen /* set source color regs */ 322f7018c21STomi Valkeinen OUTREG(DP_SRC_FRGD_CLR, 0xffffffff); 323f7018c21STomi Valkeinen OUTREG(DP_SRC_BKGD_CLR, 0x00000000); 324f7018c21STomi Valkeinen 325f7018c21STomi Valkeinen /* default write mask */ 326f7018c21STomi Valkeinen OUTREG(DP_WRITE_MSK, 0xffffffff); 327f7018c21STomi Valkeinen 328f7018c21STomi Valkeinen radeon_engine_idle (); 329f7018c21STomi Valkeinen } 330