/* Radeontool v1.4 * by Frederick Dean * Copyright 2002-2004 Frederick Dean * Use hereby granted under the zlib license. * * Warning: I do not have the Radeon documents, so this was engineered from * the radeon_reg.h header file. * * USE RADEONTOOL AT YOUR OWN RISK * * Thanks to Deepak Chawla, Erno Kuusela, Rolf Offermanns, and Soos Peter * for patches. */ #include #include #include #include #include #include #include #include #include #include #include "nvreg.h" int debug=0; int skip; int arch; /* *radeon_cntl_mem is mapped to the actual device's memory mapped control area. */ /* Not the address but what it points to is volatile. */ unsigned char * volatile radeon_cntl_mem; static void fatal(char *why) { fprintf(stderr,why); exit (-1); } static unsigned int radeon_get32(unsigned long offset, const char *name) { unsigned int value; if(debug) printf("reading %s (%lx) is ",name,offset); if(radeon_cntl_mem == NULL) { printf("internal error\n"); exit(-2); }; #ifdef __powerpc__ value = *(unsigned int * volatile)(radeon_cntl_mem+offset); #if 0 __asm__ __volatile__ ("lwbrx %0,%1,%2\n\t" "eieio" : "=r" (value) : "b" (radeon_cntl_mem), "r"(offset), "m" (*((volatile unsigned int *)radeon_cntl_mem+offset))); #endif #else value = *(unsigned int * volatile)(radeon_cntl_mem+offset); #endif if(debug) printf("%08x\n",value); return value; } static void radeon_set32(unsigned long offset, const char *name, unsigned int value) { if(debug) printf("writing %s (%lx) -> %08x\n",name,offset,value); if(radeon_cntl_mem == NULL) { printf("internal error\n"); exit(-2); }; #if 0 //def __powerpc__ __asm__ __volatile__ ("stwbrx %1,%2,%3\n\t" "eieio" : "=m" (*((volatile unsigned int *)radeon_cntl_mem+offset)) : "r"(value), "b"(radeon_cntl_mem), "r"(offset)); #else *(unsigned int * volatile)(radeon_cntl_mem+offset) = value; #endif } static unsigned int radeon_get8(unsigned long offset, const char *name) { unsigned char value; if(debug) printf("reading %s (%lx) is ",name,offset); if(radeon_cntl_mem == NULL) { printf("internal error\n"); exit(-2); }; #ifdef __powerpc__ __asm__ __volatile__ ("lwbrx %0,%1,%2\n\t" "eieio" : "=r" (value) : "b" (radeon_cntl_mem), "r"(offset), "m" (*((volatile unsigned int *)radeon_cntl_mem+offset))); #else value = *(unsigned char * volatile)(radeon_cntl_mem+offset); #endif if(debug) printf("%02x\n",value); return value; } static void radeon_set8(unsigned long offset, const char *name, unsigned char value) { if(debug) printf("writing %s (%lx) -> %08x\n",name,offset,value); if(radeon_cntl_mem == NULL) { printf("internal error\n"); exit(-2); }; *(unsigned char * volatile)(radeon_cntl_mem+offset) = value; } static void usage(void) { printf("usage: radeontool [options] [command]\n"); printf(" --debug - show a little debug info\n"); printf(" --skip=1 - use the second radeon card\n"); printf("\n"); printf(" regs - show a listing of some random registers\n"); printf(" 8regmatch - show 8 bit registers matching wildcard pattern\n"); printf(" 8regset - set 8 bit registers matching wildcard pattern\n"); printf(" regmatch - show registers matching wildcard pattern\n"); printf(" regset - set registers matching wildcard pattern\n"); printf("\n"); printf(" owner - set cr44 to some value (0 (head a), 3 (head b), or 4 (both) suggested)\n"); printf(" unlock - unlock vga crtcs for r/w access\n"); exit(-1); } /* Ohh, life would be good if we could simply address all memory addresses */ /* with /dev/mem, then I could write this whole program in perl, */ /* but sadly this is only the size of physical RAM. If you */ /* want to be truely bad and poke into device memory you have to mmap() */ static unsigned char * map_devince_memory(unsigned int base,unsigned int length) { int mem_fd; void *device_mem = NULL; /* open /dev/mem */ if ((mem_fd = open("/dev/mem", O_RDWR) ) < 0) { fatal("can't open /dev/mem\nAre you root?\n"); } /* mmap graphics memory */ // if ((device_mem = malloc(length + (getpagesize()-1))) == NULL) { // fatal("allocation error \n"); // } // if ((unsigned long)device_mem % getpagesize()) // device_mem += getpagesize() - ((unsigned long)device_mem % getpagesize()); device_mem = (void *)mmap( (caddr_t)device_mem, length, PROT_READ|PROT_WRITE, MAP_SHARED, mem_fd, base ); if (device_mem == MAP_FAILED) { if(debug) fprintf(stderr,"mmap returned %d\n",(int)device_mem); fatal("mmap error \n"); } return device_mem; } #define CRTC_INDEX 0x3d4 #define CRTC_DATA 0x3d5 uint8_t get_vga_crtc_reg(int crtc, int reg) { int offset; uint8_t retval; if (crtc==0) offset = NV_PRMCIO0_OFFSET; else offset = NV_PRMCIO1_OFFSET; radeon_set8(offset+CRTC_INDEX, "CRTC", reg); retval = radeon_get8(offset+CRTC_DATA, "CRTC") & 0xff; return retval; } void set_vga_crtc_reg(int crtc, int reg, uint8_t val) { int offset; if (crtc==0) offset = NV_PRMCIO0_OFFSET; else offset = NV_PRMCIO1_OFFSET; radeon_set8(offset+CRTC_INDEX, "CRTC", reg); radeon_set8(offset+CRTC_DATA, "CRTC", val); } uint32_t get_tmds_index_reg(int index, int dl, int reg) { uint32_t offset = dl ? 8 : 0; uint32_t retval; if (index) offset += NV_PRAMDAC0_SIZE; radeon_set32(NV_PRAMDAC_FP_TMDS_CONTROL + offset, "FPTMDS", 0x10000 | reg); retval = radeon_get32(NV_PRAMDAC_FP_TMDS_DATA + offset, "FPTMDS"); return retval; } void set_crtc_owner(int owner) { radeon_set8(NV_PRMCIO_CRX__COLOR, "CRTC", NV_CIO_CRE_44); radeon_set8(NV_PRMCIO_CR__COLOR, "CRTC", owner); if (arch == 0x11) { radeon_set8(NV_PRMCIO_CRX__COLOR, "CRTC", NV_CIO_CRE_2E); radeon_set8(NV_PRMCIO_CR__COLOR, "CRTC", owner); radeon_set8(NV_PRMCIO_CRX__COLOR, "CRTC", NV_CIO_CRE_2E); radeon_set8(NV_PRMCIO_CR__COLOR, "CRTC", owner); } } void lock_vga_crtc(int lock) { int lockval = lock ? 0x99 : 0x57; set_vga_crtc_reg(0, NV_CIO_SR_LOCK_INDEX, lockval); if (arch == 0x11 && !(radeon_get32(NV_PBUS_DEBUG_1, "NV_PBUS_DEBUG_1") & (1 << 28))) set_vga_crtc_reg(1, NV_CIO_SR_LOCK_INDEX, lockval); } void dump_vga_regs(int crtc) { int i; for (i = 0; i<0x9f; i+=4) printf("CRTC%2d %02X\t%02X %02X %02X %02X\n", crtc, i, get_vga_crtc_reg(crtc, i), get_vga_crtc_reg(crtc, i+1), get_vga_crtc_reg(crtc, i+2), get_vga_crtc_reg(crtc, i+3)); #define SHOW_VGA_CRTC_REG(r) printf("%s-%s\t%02x\n", #r, crtc ? "HB" : "HA", get_vga_crtc_reg(crtc, r)) SHOW_VGA_CRTC_REG(NV_CIO_CRE_RPC0_INDEX); SHOW_VGA_CRTC_REG(NV_CIO_CRE_RPC1_INDEX); SHOW_VGA_CRTC_REG(NV_CIO_CRE_FF_INDEX); SHOW_VGA_CRTC_REG(NV_CIO_CRE_ENH_INDEX); SHOW_VGA_CRTC_REG(NV_CIO_SR_LOCK_INDEX); SHOW_VGA_CRTC_REG(NV_CIO_CRE_FFLWM__INDEX); SHOW_VGA_CRTC_REG(NV_CIO_CRE_21); SHOW_VGA_CRTC_REG(NV_CIO_CRE_LSR_INDEX); SHOW_VGA_CRTC_REG(NV_CIO_CRE_PIXEL_INDEX); SHOW_VGA_CRTC_REG(NV_CIO_CRE_HEB__INDEX); SHOW_VGA_CRTC_REG(NV_CIO_CRE_HCUR_ADDR0_INDEX); SHOW_VGA_CRTC_REG(NV_CIO_CRE_HCUR_ADDR1_INDEX); SHOW_VGA_CRTC_REG(NV_CIO_CRE_HCUR_ADDR2_INDEX); SHOW_VGA_CRTC_REG(NV_CIO_CRE_LCD__INDEX); SHOW_VGA_CRTC_REG(NV_CIO_CRE_ILACE__INDEX); SHOW_VGA_CRTC_REG(NV_CIO_CRE_EBR_INDEX); SHOW_VGA_CRTC_REG(NV_CIO_CRE_44); SHOW_VGA_CRTC_REG(NV_CIO_CRE_RCR); SHOW_VGA_CRTC_REG(NV_CIO_CRE_47); SHOW_VGA_CRTC_REG(NV_CIO_CRE_53); SHOW_VGA_CRTC_REG(NV_CIO_CRE_54); SHOW_VGA_CRTC_REG(NV_CIO_CRE_59); } void dump_tmds_regs(int index) { int i; for ( i = 0; i < 0x43; i++) printf("TMDS%d: %02X:\t%08X\n", index, i, get_tmds_index_reg(index, 0, i)); } void radeon_cmd_regs(void) { int init_head = -1; unsigned int fp_debug_0 = radeon_get32(NV_PRAMDAC_FP_DEBUG_0, "NV_PRAMDAC_FP_DEBUG_0"); if (arch >= 0x17 && arch != 0x1a && arch != 0x20) { init_head = get_vga_crtc_reg(0, NV_CIO_CRE_44); fp_debug_0 = 0; } else if (arch == 0x11) // just assume crtc 0 init_head = 0; #define SHOW_REG(r) printf("%s\t%08x\n", #r, radeon_get32(r, #r)) SHOW_REG(NV_PMC_BOOT_0); SHOW_REG(NV_PMC_ENABLE); SHOW_REG(NV_PBUS_DEBUG_1); SHOW_REG(NV_PFB_CSTATUS); SHOW_REG(NV_PEXTDEV_BOOT_0); #define SHOW_CRTC_REG(r) do { printf("%08X: %s-HA\t%08x\n", r, #r, radeon_get32(r, #r)); \ printf("%08X: %s-HB\t%08x\n", 0x2000+r, #r, radeon_get32(0x2000+r, #r)); \ } while(0) SHOW_CRTC_REG(NV_PCRTC_START); SHOW_CRTC_REG(NV_PCRTC_CURSOR_CONFIG); SHOW_CRTC_REG(NV_PCRTC_830); SHOW_CRTC_REG(NV_PCRTC_834); SHOW_CRTC_REG(NV_PCRTC_ENGINE_CTRL); #define SHOW_RAMDAC0_REG(r) do { printf("%08X: %s\t%08x\n", r, #r, radeon_get32(r, #r)); } while(0) #define SHOW_RAMDAC_REG(r) do { printf("%08X: %s-HA\t%08x\n", r, #r, radeon_get32(r, #r)); \ printf("%08X: %s-HB\t%08x\n", 0x2000+r, #r, radeon_get32(0x2000+r, #r)); \ } while(0) SHOW_RAMDAC0_REG(NV_PRAMDAC_NVPLL_COEFF); SHOW_RAMDAC0_REG(NV_PRAMDAC_MPLL_COEFF); SHOW_RAMDAC0_REG(NV_PRAMDAC_VPLL_COEFF); SHOW_RAMDAC0_REG(NV_PRAMDAC_PLL_COEFF_SELECT); SHOW_RAMDAC0_REG(NV_RAMDAC_VPLL2); SHOW_RAMDAC0_REG(NV_PRAMDAC_SEL_CLK); SHOW_RAMDAC0_REG(NV_RAMDAC_NVPLL_B); SHOW_RAMDAC0_REG(NV_RAMDAC_MPLL_B); SHOW_RAMDAC0_REG(NV_RAMDAC_VPLL_B); SHOW_RAMDAC0_REG(NV_RAMDAC_VPLL2_B); SHOW_RAMDAC0_REG(NV_PRAMDAC_580); SHOW_RAMDAC_REG(NV_PRAMDAC_DACCLK); SHOW_RAMDAC_REG(0x680594); SHOW_RAMDAC_REG(NV_PRAMDAC_GENERAL_CONTROL); SHOW_RAMDAC_REG(NV_PRAMDAC_FP_VDISPLAY_END); SHOW_RAMDAC_REG(NV_PRAMDAC_FP_VTOTAL); SHOW_RAMDAC_REG(NV_PRAMDAC_FP_VCRTC); SHOW_RAMDAC_REG(NV_PRAMDAC_FP_VSYNC_START); SHOW_RAMDAC_REG(NV_PRAMDAC_FP_VSYNC_END); SHOW_RAMDAC_REG(NV_PRAMDAC_FP_VVALID_START); SHOW_RAMDAC_REG(NV_PRAMDAC_FP_VVALID_END); SHOW_RAMDAC_REG(NV_PRAMDAC_FP_HDISPLAY_END); SHOW_RAMDAC_REG(NV_PRAMDAC_FP_HTOTAL); SHOW_RAMDAC_REG(NV_PRAMDAC_FP_HCRTC); SHOW_RAMDAC_REG(NV_PRAMDAC_FP_HSYNC_START); SHOW_RAMDAC_REG(NV_PRAMDAC_FP_HSYNC_END); SHOW_RAMDAC_REG(NV_PRAMDAC_FP_HVALID_START); SHOW_RAMDAC_REG(NV_PRAMDAC_FP_HVALID_END); SHOW_RAMDAC_REG(NV_RAMDAC_FP_DITHER); SHOW_RAMDAC_REG(NV_PRAMDAC_FP_TG_CONTROL); SHOW_RAMDAC_REG(NV_PRAMDAC_FP_DEBUG_0); SHOW_RAMDAC_REG(NV_PRAMDAC_FP_DEBUG_1); if (fp_debug_0 & NV_PRAMDAC_FP_DEBUG_0_PWRDOWN_FPCLK) { printf("Temporarily changing NV_PRAMDAC_FP_DEBUG_0 to avoid lockup\n"); radeon_set32(NV_PRAMDAC_FP_DEBUG_0, "NV_PRAMDAC_FP_DEBUG_0", fp_debug_0 & ~NV_PRAMDAC_FP_DEBUG_0_PWRDOWN_FPCLK); } SHOW_RAMDAC_REG(NV_PRAMDAC_FP_TMDS_CONTROL); SHOW_RAMDAC_REG(NV_PRAMDAC_FP_TMDS_DATA); SHOW_RAMDAC_REG(0x6808b8); SHOW_RAMDAC_REG(0x6808bc); set_crtc_owner(0); dump_vga_regs(0); dump_vga_regs(1); if (init_head >= 0) set_crtc_owner(init_head); dump_tmds_regs(0); printf("TMDS0(dl): 04:\t%08X\n", get_tmds_index_reg(0, 1, 4)); dump_tmds_regs(1); printf("TMDS1(dl): 04:\t%08X\n", get_tmds_index_reg(1, 1, 4)); if (fp_debug_0 & NV_PRAMDAC_FP_DEBUG_0_PWRDOWN_FPCLK) radeon_set32(NV_PRAMDAC_FP_DEBUG_0, "NV_PRAMDAC_FP_DEBUG_0", fp_debug_0); } #define REGLIST(r) { #r, r } static struct { const char *name; unsigned address; } reg_list[] = { }; void radeon_reg_match(const char *pattern, int byte) { int i; unsigned long address; unsigned int value; if (pattern[0] == '0' && pattern[1] == 'x') { address = strtol(&(pattern[2]), NULL, 16); if (byte) { value = radeon_get8(address, pattern); printf("%s\t0x%02x (%d)\n", pattern, value, value); } else { value = radeon_get32(address, pattern); printf("%s\t0x%08x (%d)\n", pattern, value, value); } return; } if (pattern[0] == 'C' && pattern[2] == ':') { uint8_t crtc = (pattern[1] == '0') ? 0 : 1; address = strtol(&(pattern[3]), NULL, 16); value = get_vga_crtc_reg(crtc, address); printf("%s\tCRTC%d: 0x%02x (%d)\n", pattern, crtc, value, value); return; } for (i=0;i [disabled] [size=128K] Capabilities: 02:00.0 Ethernet controller: 3Com Corporation 3c905C-TX/TX-M [Tornado] (rev 78) Subsystem: Dell Computer Corporation: Unknown device 00e3 Flags: bus master, medium devsel, latency 32, IRQ 11 I/O ports at ec80 [size=128] t emory at f8fffc00 (32-bit, non-prefetchable) [size=128] Expansion ROM at f9000000 [disabled] [size=128K] Capabilities: We need to look through it to find the smaller region base address f8fffc00. #endif while(1) { /* for every line up to the "Radeon" string */ if(fgets(line,sizeof(line),fp) == NULL) { /* if end of file */ fatal("Radeon hardware not found in lspci output.\n"); } if(strstr(line,"VGA compatible controller") && strstr(line,"nVidia")) { /* if line contains a "radeon" string */ if(skip-- < 1) { break; } } }; // if(debug) printf("%s\n",line); while(1) { /* for every line up till memory statement */ if(fgets(line,sizeof(line),fp) == NULL || line[0] != '\t') { /* if end of file */ fatal("Radeon control memory not found.\n"); } if(debug) printf("%s",line); if(strstr(line,"emory") && strstr(line,"M")) { /* if line contains a "Memory" and "K" string */ break; } }; if(sscanf(line,"%*s%*s%x",&base) == 0) { /* third token as hex number */ fatal("parse error of lspci output (control memory not found)\n"); } if(debug) printf("nVidia card found. Base control address is %x.\n",base); radeon_cntl_mem = map_devince_memory(base,0x800000); } #ifdef __powerpc__ #define __swab16(x) \ ({ \ unsigned short __x = (x); \ ((unsigned short)( \ (((unsigned short)(__x) & (unsigned short)0x00ffU) << 8) | \ (((unsigned short)(__x) & (unsigned short)0xff00U) >> 8) )); \ }) #define __swab32(x) \ ({ \ unsigned int __x = (x); \ ((unsigned int)( \ (((unsigned int)(__x) & (unsigned int)0x000000ffUL) << 24) | \ (((unsigned int)(__x) & (unsigned int)0x0000ff00UL) << 8) | \ (((unsigned int)(__x) & (unsigned int)0x00ff0000UL) >> 8) | \ (((unsigned int)(__x) & (unsigned int)0xff000000UL) >> 24) )); \ }) #define BIOS16(offset) __swab16(*((unsigned short *)(bios + (offset)))) #define BIOS32(offset) __swab32(*((unsigned int *)(bios + (offset)))) #else #define BIOS16(offset) (*((unsigned short *)(bios + (offset)))) #define BIOS32(offset) (*((unsigned int *)(bios + (offset)))) #endif #define BIOS8(offset) (*((unsigned char *)(bios + (offset)))) struct nametable_entry { unsigned int value; const char *name; }; const char *radeon_valname(const struct nametable_entry *table, unsigned int value) { static char ret_buf[256]; while(table->name) { if (table->value == value) return table->name; table++; } sprintf(ret_buf, "", value); return ret_buf; } int main(int argc,char *argv[]) { if(argc == 1) { map_radeon_cntl_mem(); usage(); } if(strcmp(argv[1],"--debug") == 0) { debug=1; argv++; argc--; }; if(argc && (strcmp(argv[1],"--skip=") == 0)) { skip=atoi(argv[1]+7); argv++; argc--; }; map_radeon_cntl_mem(); arch = (radeon_get32(NV_PMC_BOOT_0, "NV_PMC_BOOT_0") >> 20) & 0xff; if(argc == 2) { if(strcmp(argv[1],"regs") == 0) { radeon_cmd_regs(); return 0; }; if(strcmp(argv[1],"unlock") == 0) { lock_vga_crtc(0); return 0; }; } else if(argc == 3) { if(strcmp(argv[1],"regmatch") == 0) { radeon_reg_match(argv[2], 0); return 0; }; if(strcmp(argv[1],"8regmatch") == 0) { radeon_reg_match(argv[2], 1); return 0; }; if(strcmp(argv[1],"owner") == 0) { set_crtc_owner(strtoul(argv[2], NULL, 0)); return 0; }; } else if(argc == 4) { if(strcmp(argv[1],"regset") == 0) { radeon_reg_set(argv[2], strtoul(argv[3], NULL, 0), 0); return 0; } if(strcmp(argv[1],"8regset") == 0) { radeon_reg_set(argv[2], strtoul(argv[3], NULL, 0), 1); return 0; } } usage(); return 1; }