diff options
author | Alex Deucher <alex@botch2.com> | 2007-06-06 00:08:45 -0400 |
---|---|---|
committer | Alex Deucher <alex@botch2.com> | 2007-06-06 00:08:45 -0400 |
commit | 2527f2b69aa7dffa3ba4359c45955e3185bdf0e6 (patch) | |
tree | 3027070a87a63382df048892c091c45120f8fb5d | |
parent | e67d1420bf65055ecb6fdfe6b1b1f53aae83854a (diff) |
RADEON: implement support for posting cards based on x86 bios tables (untested)
This is based on the netbsd radeonfb driver by Garrett D'Amore.
The code is not hooked up yet, but should allow you to post cards
without needing to execute bios code. This should be useful on
non-x86 platforms, for posting secndary cards, and for suspend/resume.
Works on legacy bioses only (no ATOM support).
-rw-r--r-- | src/radeon.h | 13 | ||||
-rw-r--r-- | src/radeon_bios.c | 462 | ||||
-rw-r--r-- | src/radeon_reg.h | 30 |
3 files changed, 497 insertions, 8 deletions
diff --git a/src/radeon.h b/src/radeon.h index 260c413c..f1b2fa3e 100644 --- a/src/radeon.h +++ b/src/radeon.h @@ -174,6 +174,18 @@ typedef enum { #define RADEON_ALIGN(x,bytes) (((x) + ((bytes) - 1)) & ~((bytes) - 1)) #define RADEONPTR(pScrn) ((RADEONInfoPtr)(pScrn)->driverPrivate) +typedef struct { + int revision; + CARD16 rr1_offset; + CARD16 rr2_offset; + CARD16 dyn_clk_offset; + CARD16 pll_offset; + CARD16 mem_config_offset; + CARD16 mem_reset_offset; + CARD16 short_mem_offset; + CARD16 rr3_offset; + CARD16 rr4_offset; +} RADEONBIOSInitTable; typedef struct { /* Common registers */ @@ -734,6 +746,7 @@ typedef struct { Bool want_vblank_interrupts; RADEONBIOSConnector BiosConnector[RADEON_MAX_BIOS_CONNECTOR]; + RADEONBIOSInitTable BiosTable; Rotation rotation; void (*PointerMoved)(int, int, int); diff --git a/src/radeon_bios.c b/src/radeon_bios.c index 76b0a728..543ccb9b 100644 --- a/src/radeon_bios.c +++ b/src/radeon_bios.c @@ -612,3 +612,465 @@ Bool RADEONGetTMDSInfoFromBIOS (xf86OutputPtr output) } return FALSE; } + +/* support for init from bios tables + * + * Based heavily on the netbsd radeonfb driver + * Written by Garrett D'Amore + * Copyright (c) 2006 Itronix Inc. + * + */ + +/* bios table defines */ + +#define RADEON_TABLE_ENTRY_FLAG_MASK 0xe000 +#define RADEON_TABLE_ENTRY_INDEX_MASK 0x1fff +#define RADEON_TABLE_ENTRY_COMMAND_MASK 0x00ff + +#define RADEON_TABLE_FLAG_WRITE_INDEXED 0x0000 +#define RADEON_TABLE_FLAG_WRITE_DIRECT 0x2000 +#define RADEON_TABLE_FLAG_MASK_INDEXED 0x4000 +#define RADEON_TABLE_FLAG_MASK_DIRECT 0x6000 +#define RADEON_TABLE_FLAG_DELAY 0x8000 +#define RADEON_TABLE_FLAG_SCOMMAND 0xa000 + +#define RADEON_TABLE_SCOMMAND_WAIT_MC_BUSY_MASK 0x03 +#define RADEON_TABLE_SCOMMAND_WAIT_MEM_PWRUP_COMPLETE 0x08 + +#define RADEON_PLL_FLAG_MASK 0xc0 +#define RADEON_PLL_INDEX_MASK 0x3f + +#define RADEON_PLL_FLAG_WRITE 0x00 +#define RADEON_PLL_FLAG_MASK_BYTE 0x40 +#define RADEON_PLL_FLAG_WAIT 0x80 + +#define RADEON_PLL_WAIT_150MKS 1 +#define RADEON_PLL_WAIT_5MS 2 +#define RADEON_PLL_WAIT_MC_BUSY_MASK 3 +#define RADEON_PLL_WAIT_DLL_READY_MASK 4 +#define RADEON_PLL_WAIT_CHK_SET_CLK_PWRMGT_CNTL24 5 + +static CARD16 +RADEONValidateBIOSOffset(ScrnInfoPtr pScrn, CARD16 offset) +{ + RADEONInfoPtr info = RADEONPTR (pScrn); + CARD8 revision = RADEON_BIOS8(offset - 1); + + if (revision > 0x10) { + xf86DrvMsg(pScrn->scrnIndex, X_WARNING, + "Bad revision %d for BIOS table\n", revision); + return 0; + } + + if (offset < 0x60) { + xf86DrvMsg(pScrn->scrnIndex, X_WARNING, + "Bad offset 0x%x for BIOS Table\n", offset); + return 0; + } + + return offset; +} + +Bool +RADEONGetBIOSInitTableOffsets(ScrnInfoPtr pScrn) +{ + RADEONInfoPtr info = RADEONPTR (pScrn); + CARD8 val; + + if (!info->VBIOS) { + return FALSE; + } else { + if (info->IsAtomBios) { + return FALSE; + } else { + info->BiosTable.revision = RADEON_BIOS8(info->ROMHeaderStart + 4); + info->BiosTable.rr1_offset = RADEON_BIOS16(info->ROMHeaderStart + 0x0c); + if (info->BiosTable.rr1_offset) { + info->BiosTable.rr1_offset = + RADEONValidateBIOSOffset(pScrn, info->BiosTable.rr1_offset); + } + if (info->BiosTable.revision > 0x09) + return TRUE; + info->BiosTable.rr2_offset = RADEON_BIOS16(info->ROMHeaderStart + 0x4e); + if (info->BiosTable.rr2_offset) { + info->BiosTable.rr2_offset = + RADEONValidateBIOSOffset(pScrn, info->BiosTable.rr2_offset); + } + info->BiosTable.dyn_clk_offset = RADEON_BIOS16(info->ROMHeaderStart + 0x52); + if (info->BiosTable.dyn_clk_offset) { + info->BiosTable.dyn_clk_offset = + RADEONValidateBIOSOffset(pScrn, info->BiosTable.dyn_clk_offset); + } + info->BiosTable.pll_offset = RADEON_BIOS16(info->ROMHeaderStart + 0x46); + if (info->BiosTable.pll_offset) { + info->BiosTable.pll_offset = + RADEONValidateBIOSOffset(pScrn, info->BiosTable.pll_offset); + } + info->BiosTable.mem_config_offset = RADEON_BIOS16(info->ROMHeaderStart + 0x48); + if (info->BiosTable.mem_config_offset) { + info->BiosTable.mem_config_offset = + RADEONValidateBIOSOffset(pScrn, info->BiosTable.mem_config_offset); + } + if (info->BiosTable.mem_config_offset) { + info->BiosTable.mem_reset_offset = RADEON_BIOS16(info->BiosTable.mem_config_offset); + if (info->BiosTable.mem_reset_offset) { + while (RADEON_BIOS8(info->BiosTable.mem_reset_offset)) + info->BiosTable.mem_reset_offset++; + info->BiosTable.mem_reset_offset++; + info->BiosTable.mem_reset_offset += 2; + } + } + if (info->BiosTable.mem_config_offset) { + info->BiosTable.short_mem_offset = RADEON_BIOS16(info->BiosTable.mem_config_offset); + if ((info->BiosTable.short_mem_offset != 0) && + (RADEON_BIOS8(info->BiosTable.short_mem_offset - 2) <= 64)) + info->BiosTable.short_mem_offset += + RADEON_BIOS8(info->BiosTable.short_mem_offset - 3); + } + if (info->BiosTable.rr2_offset) { + info->BiosTable.rr3_offset = RADEON_BIOS16(info->BiosTable.rr2_offset); + if (info->BiosTable.rr3_offset) { + while ((val = RADEON_BIOS8(info->BiosTable.rr3_offset + 1)) != 0) { + if (val & 0x40) + info->BiosTable.rr3_offset += 10; + else if (val & 0x80) + info->BiosTable.rr3_offset += 4; + else + info->BiosTable.rr3_offset += 6; + } + info->BiosTable.rr3_offset += 2; + } + } + + if (info->BiosTable.rr3_offset) { + info->BiosTable.rr4_offset = RADEON_BIOS16(info->BiosTable.rr3_offset); + if (info->BiosTable.rr4_offset) { + while ((val = RADEON_BIOS8(info->BiosTable.rr4_offset + 1)) != 0) { + if (val & 0x40) + info->BiosTable.rr4_offset += 10; + else if (val & 0x80) + info->BiosTable.rr4_offset += 4; + else + info->BiosTable.rr4_offset += 6; + } + info->BiosTable.rr4_offset += 2; + } + } + + if (info->BiosTable.rr3_offset + 1 == info->BiosTable.pll_offset) { + info->BiosTable.rr3_offset = 0; + info->BiosTable.rr4_offset = 0; + } + + return TRUE; + + } + } +} + +static void +RADEONRestoreBIOSRegBlock(ScrnInfoPtr pScrn, CARD16 table_offset) +{ + RADEONInfoPtr info = RADEONPTR (pScrn); + unsigned char *RADEONMMIO = info->MMIO; + CARD16 offset = table_offset; + CARD16 value, flag, index, count; + CARD32 andmask, ormask, val, channel_complete_mask; + CARD8 command; + + if (offset == 0) + return; + + while ((value = RADEON_BIOS16(offset)) != 0) { + flag = value & RADEON_TABLE_ENTRY_FLAG_MASK; + index = value & RADEON_TABLE_ENTRY_INDEX_MASK; + command = value & RADEON_TABLE_ENTRY_COMMAND_MASK; + + offset += 2; + + switch (flag) { + case RADEON_TABLE_FLAG_WRITE_INDEXED: + val = RADEON_BIOS32(offset); + ErrorF("WRITE INDEXED: 0x%x 0x%x\n", + index, val); + OUTREG(RADEON_MM_INDEX, index); + OUTREG(RADEON_MM_DATA, val); + offset += 4; + break; + + case RADEON_TABLE_FLAG_WRITE_DIRECT: + val = RADEON_BIOS32(offset); + ErrorF("WRITE DIRECT: 0x%x 0x%x\n", index, val); + OUTREG(index, val); + offset += 4; + break; + + case RADEON_TABLE_FLAG_MASK_INDEXED: + andmask = RADEON_BIOS32(offset); + offset += 4; + ormask = RADEON_BIOS32(offset); + offset += 4; + ErrorF("MASK INDEXED: 0x%x 0x%x 0x%x\n", + index, andmask, ormask); + OUTREG(RADEON_MM_INDEX, index); + val = INREG(RADEON_MM_DATA); + val = (val & andmask) | ormask; + OUTREG(RADEON_MM_DATA, val); + break; + + case RADEON_TABLE_FLAG_MASK_DIRECT: + andmask = RADEON_BIOS32(offset); + offset += 4; + ormask = RADEON_BIOS32(offset); + offset += 4; + ErrorF("MASK DIRECT: 0x%x 0x%x 0x%x\n", + index, andmask, ormask); + val = INREG(index); + val = (val & andmask) | ormask; + OUTREG(index, val); + break; + + case RADEON_TABLE_FLAG_DELAY: + count = RADEON_BIOS16(offset); + ErrorF("delay: %d\n", count); + usleep(count); + offset += 2; + break; + + case RADEON_TABLE_FLAG_SCOMMAND: + ErrorF("SCOMMAND 0x%x\n", command); + switch (command) { + case RADEON_TABLE_SCOMMAND_WAIT_MC_BUSY_MASK: + count = RADEON_BIOS16(offset); + ErrorF("SCOMMAND_WAIT_MC_BUSY_MASK %d\n", count); + while (count--) { + if (!(INPLL(pScrn, RADEON_CLK_PWRMGT_CNTL) & + RADEON_MC_BUSY)) + break; + } + break; + + case RADEON_TABLE_SCOMMAND_WAIT_MEM_PWRUP_COMPLETE: + count = RADEON_BIOS16(offset); + ErrorF("SCOMMAND_WAIT_MEM_PWRUP_COMPLETE %d\n", count); + while (count--) { + /* XXX: may need indexed access */ + /* may need to take into account how many memory channels + * each card has + */ + if (IS_R300_VARIANT) + channel_complete_mask = R300_MEM_PWRUP_COMPLETE; + else + channel_complete_mask = RADEON_MEM_PWRUP_COMPLETE; + if ((INREG(RADEON_MEM_STR_CNTL) & + channel_complete_mask) == + channel_complete_mask) + break; + } + break; + + } + offset += 2; + break; + } + } +} + +static void +RADEONRestoreBIOSMemBlock(ScrnInfoPtr pScrn, CARD16 table_offset) +{ + RADEONInfoPtr info = RADEONPTR (pScrn); + unsigned char *RADEONMMIO = info->MMIO; + CARD16 offset = table_offset; + CARD16 count; + CARD32 ormask, val, channel_complete_mask; + CARD8 index; + + if (offset == 0) + return; + + while ((index = RADEON_BIOS8(offset)) != 0xff) { + offset++; + if (index == 0x0f) { + count = 20000; + ErrorF("MEM_WAIT_MEM_PWRUP_COMPLETE %d\n", count); + while (count--) { + /* XXX: may need indexed access */ + /* may need to take into account how many memory channels + * each card has + */ + if (IS_R300_VARIANT) + channel_complete_mask = R300_MEM_PWRUP_COMPLETE; + else + channel_complete_mask = RADEON_MEM_PWRUP_COMPLETE; + if ((INREG(RADEON_MEM_STR_CNTL) & + channel_complete_mask) == + channel_complete_mask) + break; + } + } else { + ormask = RADEON_BIOS16(offset); + offset += 2; + + ErrorF("INDEX RADEON_MEM_SDRAM_MODE_REG %x %x\n", + RADEON_SDRAM_MODE_MASK, ormask); + + /* can this use direct access? */ + OUTREG(RADEON_MM_INDEX, RADEON_MEM_SDRAM_MODE_REG); + val = INREG(RADEON_MM_DATA); + val = (val & RADEON_SDRAM_MODE_MASK) | ormask; + OUTREG(RADEON_MM_DATA, val); + + ormask = (CARD32)index << 24; + + ErrorF("INDEX RADEON_MEM_SDRAM_MODE_REG %x %x\n", + RADEON_B3MEM_RESET_MASK, ormask); + + /* can this use direct access? */ + OUTREG(RADEON_MM_INDEX, RADEON_MEM_SDRAM_MODE_REG); + val = INREG(RADEON_MM_DATA); + val = (val & RADEON_B3MEM_RESET_MASK) | ormask; + OUTREG(RADEON_MM_DATA, val); + } + } +} + +static void +RADEONRestoreBIOSPllBlock(ScrnInfoPtr pScrn, CARD16 table_offset) +{ + RADEONInfoPtr info = RADEONPTR (pScrn); + unsigned char *RADEONMMIO = info->MMIO; + CARD16 offset = table_offset; + CARD8 index, shift; + CARD32 andmask, ormask, val, clk_pwrmgt_cntl; + CARD16 count; + + if (offset == 0) + return; + + while ((index = BIOS8(offset)) != 0) { + offset++; + + switch (index & RADEON_PLL_FLAG_MASK) { + case RADEON_PLL_FLAG_WAIT: + switch (index & RADEON_PLL_INDEX_MASK) { + case RADEON_PLL_WAIT_150MKS: + ErrorF("delay: 150 us\n"); + usleep(150); + break; + case RADEON_PLL_WAIT_5MS: + usleep(5000); + ErrorF("delay: 5 ms\n"); + break; + + case RADEON_PLL_WAIT_MC_BUSY_MASK: + count = 1000; + ErrorF("PLL_WAIT_MC_BUSY_MASK %d\n", count); + while (count--) { + if (!(INPLL(pScrn, RADEON_CLK_PWRMGT_CNTL) & + RADEON_MC_BUSY)) + break; + } + break; + + case RADEON_PLL_WAIT_DLL_READY_MASK: + count = 1000; + ErrorF("PLL_WAIT_DLL_READY_MASK %d\n", count); + while (count--) { + if (INPLL(pScrn, RADEON_CLK_PWRMGT_CNTL) & + RADEON_DLL_READY) + break; + } + break; + + case RADEON_PLL_WAIT_CHK_SET_CLK_PWRMGT_CNTL24: + ErrorF("PLL_WAIT_CHK_SET_CLK_PWRMGT_CNTL24\n"); + clk_pwrmgt_cntl = INPLL(pScrn, RADEON_CLK_PWRMGT_CNTL); + if (clk_pwrmgt_cntl & RADEON_CG_NO1_DEBUG_0) { + val = INPLL(pScrn, RADEON_MCLK_CNTL); + /* is this right? */ + val = (val & 0xFFFF0000) | 0x1111; /* seems like we should clear these... */ + OUTPLL(pScrn, RADEON_MCLK_CNTL, val); + usleep(10000); + OUTPLL(pScrn, RADEON_CLK_PWRMGT_CNTL, + clk_pwrmgt_cntl & ~RADEON_CG_NO1_DEBUG_0); + usleep(10000); + } + break; + } + break; + + case RADEON_PLL_FLAG_MASK_BYTE: + shift = BIOS8(offset) * 8; + offset++; + + andmask = + (((CARD32)BIOS8(offset)) << shift) | + ~((CARD32)0xff << shift); + offset++; + + ormask = ((CARD32)BIOS8(offset)) << shift; + offset++; + + ErrorF("PLL_MASK_BYTE 0x%x 0x%x 0x%x 0x%x\n", + index, shift, andmask, ormask); + val = INPLL(pScrn, index); + val = (val & andmask) | ormask; + OUTPLL(pScrn, index, val); + break; + + case RADEON_PLL_FLAG_WRITE: + val = RADEON_BIOS32(offset); + ErrorF("PLL_WRITE 0x%x 0x%x\n", index, val); + OUTPLL(pScrn, index, val); + offset += 4; + break; + } + } +} + +Bool +RADEONPostCardFromBIOSTables(ScrnInfoPtr pScrn) +{ + RADEONInfoPtr info = RADEONPTR (pScrn); + + if (!info->VBIOS) { + return FALSE; + } else { + if (info->IsAtomBios) { + return FALSE; + } else { + if (info->BiosTable.rr1_offset) { + ErrorF("rr1 restore\n"); + RADEONRestoreBIOSRegBlock(pScrn, info->BiosTable.rr1_offset); + } + if (info->BiosTable.revision < 0x09) { + if (info->BiosTable.pll_offset) { + ErrorF("pll restore\n"); + RADEONRestoreBIOSPllBlock(pScrn, info->BiosTable.pll_offset); + } + if (info->BiosTable.rr2_offset) { + ErrorF("rr2 restore\n"); + RADEONRestoreBIOSRegBlock(pScrn, info->BiosTable.rr2_offset); + } + if (info->BiosTable.rr4_offset) { + ErrorF("rr4 restore\n"); + RADEONRestoreBIOSRegBlock(pScrn, info->BiosTable.rr4_offset); + } + if (info->BiosTable.mem_reset_offset) { + ErrorF("mem reset restore\n"); + RADEONRestoreBIOSMemBlock(pScrn, info->BiosTable.mem_reset_offset); + } + if (info->BiosTable.rr3_offset) { + ErrorF("rr3 restore\n"); + RADEONRestoreBIOSRegBlock(pScrn, info->BiosTable.rr3_offset); + } + if (info->BiosTable.dyn_clk_offset) { + ErrorF("dyn_clk restore\n"); + RADEONRestoreBIOSPllBlock(pScrn, info->BiosTable.dyn_clk_offset); + } + } + } + } + return TRUE; +} diff --git a/src/radeon_reg.h b/src/radeon_reg.h index 72470a66..9423bca5 100644 --- a/src/radeon_reg.h +++ b/src/radeon_reg.h @@ -231,7 +231,13 @@ # define RADEON_ACTIVE_HILO_LAT_MASK (3 << 13) # define RADEON_ACTIVE_HILO_LAT_SHIFT 13 # define RADEON_DISP_DYN_STOP_LAT_MASK (1 << 12) +# define RADEON_MC_BUSY (1 << 16) +# define RADEON_DLL_READY (1 << 19) +# define RADEON_CG_NO1_DEBUG_0 (1 << 24) +# define RADEON_CG_NO1_DEBUG_MASK (0x1f << 24) # define RADEON_DYN_STOP_MODE_MASK (7 << 21) +# define RADEON_TVPLL_PWRMGT_OFF (1 << 30) +# define RADEON_TVCLK_TURNOFF (1 << 31) #define RADEON_PLL_PWRMGT_CNTL 0x0015 # define RADEON_TCL_BYPASS_DISABLE (1 << 20) #define RADEON_CLR_CMP_CLR_3D 0x1a24 @@ -889,9 +895,6 @@ #define RADEON_MAX_LATENCY 0x0f3f /* PCI */ #define RADEON_MC_AGP_LOCATION 0x014c #define RADEON_MC_FB_LOCATION 0x0148 -#define RADEON_MC_STATUS 0x0150 -# define RADEON_MC_IDLE (1 << 2) -# define R300_MC_IDLE (1 << 4) #define RADEON_DISPLAY_BASE_ADDR 0x23c #define RADEON_DISPLAY2_BASE_ADDR 0x33c #define RADEON_OV0_BASE_ADDR 0x43c @@ -907,8 +910,8 @@ # define R300_DISABLE_MC_MCLKA (1 << 21) # define R300_DISABLE_MC_MCLKB (1 << 21) #define RADEON_MCLK_MISC 0x001f /* PLL */ -# define RADEON_MC_MCLK_MAX_DYN_STOP_LAT (1<<12) -# define RADEON_IO_MCLK_MAX_DYN_STOP_LAT (1<<13) +# define RADEON_MC_MCLK_MAX_DYN_STOP_LAT (1 << 12) +# define RADEON_IO_MCLK_MAX_DYN_STOP_LAT (1 << 13) # define RADEON_MC_MCLK_DYN_ENABLE (1 << 14) # define RADEON_IO_MCLK_DYN_ENABLE (1 << 15) #define RADEON_LCD_GPIO_MASK 0x01a0 @@ -921,15 +924,26 @@ #define RADEON_MEM_BASE 0x0f10 /* PCI */ #define RADEON_MEM_CNTL 0x0140 # define RADEON_MEM_NUM_CHANNELS_MASK 0x01 -# define RADEON_MEM_USE_B_CH_ONLY (1<<1) -# define RV100_HALF_MODE (1<<3) +# define RADEON_MEM_USE_B_CH_ONLY (1 << 1) +# define RV100_HALF_MODE (1 << 3) # define R300_MEM_NUM_CHANNELS_MASK 0x03 -# define R300_MEM_USE_CD_CH_ONLY (1<<2) +# define R300_MEM_USE_CD_CH_ONLY (1 << 2) #define RADEON_MEM_TIMING_CNTL 0x0144 /* EXT_MEM_CNTL */ #define RADEON_MEM_INIT_LAT_TIMER 0x0154 #define RADEON_MEM_INTF_CNTL 0x014c #define RADEON_MEM_SDRAM_MODE_REG 0x0158 +# define RADEON_SDRAM_MODE_MASK 0xffff0000 +# define RADEON_B3MEM_RESET_MASK 0x6fffffff #define RADEON_MEM_STR_CNTL 0x0150 +# define RADEON_MEM_PWRUP_COMPL_A (1 << 0) +# define RADEON_MEM_PWRUP_COMPL_B (1 << 1) +# define R300_MEM_PWRUP_COMPL_C (1 << 2) +# define R300_MEM_PWRUP_COMPL_D (1 << 3) +# define RADEON_MEM_PWRUP_COMPLETE 0x03 +# define R300_MEM_PWRUP_COMPLETE 0x0f +#define RADEON_MC_STATUS 0x0150 +# define RADEON_MC_IDLE (1 << 2) +# define R300_MC_IDLE (1 << 4) #define RADEON_MEM_VGA_RP_SEL 0x003c #define RADEON_MEM_VGA_WP_SEL 0x0038 #define RADEON_MIN_GRANT 0x0f3e /* PCI */ |