/* 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 #ifdef __LINUX__ #include #elif __FreeBSD__ #include #include #endif #include "radeon_reg.h" int debug; int skip; /* *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 long radeon_get(unsigned long offset,char *name) { unsigned long value; if(debug) printf("reading %s (%lx) is ",name,offset); if(radeon_cntl_mem == NULL) { printf("internal error\n"); exit(-2); }; value = *(unsigned long * volatile)(radeon_cntl_mem+offset); if(debug) printf("%08lx\n",value); return value; } static void radeon_set(unsigned long offset,char *name,unsigned long value) { if(debug) printf("writing %s (%lx) -> %08lx\n",name,offset,value); if(radeon_cntl_mem == NULL) { printf("internal error\n"); exit(-2); }; *(unsigned long * 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(" dac [on|off] - power down the external video outputs (%s)\n", (radeon_get(RADEON_DAC_CNTL,"RADEON_DAC_CNTL")&RADEON_DAC_PDWN)?"off":"on"); printf(" light [on|off] - power down the backlight (%s)\n", (radeon_get(RADEON_LVDS_GEN_CNTL,"RADEON_LVDS_GEN_CNTL")&RADEON_LVDS_ON)?"on":"off"); printf(" stretch [on|off|vert|horiz|auto|manual] - stretching for resolution mismatch \n"); //(radeon_get(RADEON_FP_HORZ_STRETCH,"RADEON_FP_HORZ_STRETCH")&RADEON_HORZ_STRETCH_ENABLE)?"on":"off"); printf(" regs - show a listing of some random registers\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; unsigned char *device_mem; /* 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 + (PAGE_SIZE-1))) == NULL) { fatal("allocation error \n"); } if ((unsigned long)device_mem % PAGE_SIZE) device_mem += PAGE_SIZE - ((unsigned long)device_mem % PAGE_SIZE); device_mem = (unsigned char *)mmap( (caddr_t)device_mem, length, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED, mem_fd, base ); if ((long)device_mem < 0) { if(debug) fprintf(stderr,"mmap returned %d\n",(int)device_mem); fatal("mmap error \n"); } return device_mem; } void radeon_cmd_regs(void) { printf("RADEON_DAC_CNTL=%08lx\n",radeon_get(RADEON_DAC_CNTL,"RADEON_DAC_CNTL")); printf("RADEON_DAC_CNTL2=%08lx\n",radeon_get(RADEON_DAC_CNTL2,"RADEON_DAC_CNTL2")); printf("RADEON_TV_DAC_CNTL=%08lx\n",radeon_get(RADEON_TV_DAC_CNTL,"RADEON_TV_DAC_CNTL")); printf("RADEON_DISP_OUTPUT_CNTL=%08lx\n",radeon_get(RADEON_DISP_OUTPUT_CNTL,"RADEON_DISP_OUTPUT_CNTL")); printf("RADEON_CONFIG_MEMSIZE=%08lx\n",radeon_get(RADEON_CONFIG_MEMSIZE,"RADEON_CONFIG_MEMSIZE")); printf("RADEON_AUX_SC_CNTL=%08lx\n",radeon_get(RADEON_AUX_SC_CNTL,"RADEON_AUX_SC_CNTL")); printf("RADEON_CRTC_EXT_CNTL=%08lx\n",radeon_get(RADEON_CRTC_EXT_CNTL,"RADEON_CRTC_EXT_CNTL")); printf("RADEON_CRTC_GEN_CNTL=%08lx\n",radeon_get(RADEON_CRTC_GEN_CNTL,"RADEON_CRTC_GEN_CNTL")); printf("RADEON_CRTC2_GEN_CNTL=%08lx\n",radeon_get(RADEON_CRTC2_GEN_CNTL,"RADEON_CRTC2_GEN_CNTL")); printf("RADEON_DEVICE_ID=%08lx\n",radeon_get(RADEON_DEVICE_ID,"RADEON_DEVICE_ID")); printf("RADEON_DISP_MISC_CNTL=%08lx\n",radeon_get(RADEON_DISP_MISC_CNTL,"RADEON_DISP_MISC_CNTL")); printf("RADEON_GPIO_MONID=%08lx\n",radeon_get(RADEON_GPIO_MONID,"RADEON_GPIO_MONID")); printf("RADEON_GPIO_MONIDB=%08lx\n",radeon_get(RADEON_GPIO_MONIDB,"RADEON_GPIO_MONIDB")); printf("RADEON_GPIO_CRT2_DDC=%08lx\n",radeon_get(RADEON_GPIO_CRT2_DDC,"RADEON_GPIO_CRT2_DDC")); printf("RADEON_GPIO_DVI_DDC=%08lx\n",radeon_get(RADEON_GPIO_DVI_DDC,"RADEON_GPIO_DVI_DDC")); printf("RADEON_GPIO_VGA_DDC=%08lx\n",radeon_get(RADEON_GPIO_VGA_DDC,"RADEON_GPIO_VGA_DDC")); printf("RADEON_LVDS_GEN_CNTL=%08lx\n",radeon_get(RADEON_LVDS_GEN_CNTL,"RADEON_LVDS_GEN_CNTL")); } void radeon_cmd_bits(void) { unsigned long dac_cntl; dac_cntl = radeon_get(RADEON_DAC_CNTL,"RADEON_DAC_CNTL"); printf("RADEON_DAC_CNTL=%08lx (",dac_cntl); if(dac_cntl & RADEON_DAC_RANGE_CNTL) printf("range_cntl "); if(dac_cntl & RADEON_DAC_BLANKING) printf("blanking "); if(dac_cntl & RADEON_DAC_8BIT_EN) printf("8bit_en "); if(dac_cntl & RADEON_DAC_VGA_ADR_EN) printf("vga_adr_en "); if(dac_cntl & RADEON_DAC_PDWN) printf("pdwn "); printf(")\n"); } void radeon_cmd_dac(char *param) { unsigned long dac_cntl; dac_cntl = radeon_get(RADEON_DAC_CNTL,"RADEON_DAC_CNTL"); if(param == NULL) { printf("The radeon external DAC looks %s\n",(dac_cntl&(RADEON_DAC_PDWN))?"off":"on"); exit (-1); } else if(strcmp(param,"off") == 0) { dac_cntl |= RADEON_DAC_PDWN; } else if(strcmp(param,"on") == 0) { dac_cntl &= ~ RADEON_DAC_PDWN; } else { usage(); }; radeon_set(RADEON_DAC_CNTL,"RADEON_DAC_CNTL",dac_cntl); } void radeon_cmd_light(char *param) { unsigned long lvds_gen_cntl; lvds_gen_cntl = radeon_get(RADEON_LVDS_GEN_CNTL,"RADEON_LVDS_GEN_CNTL"); if(param == NULL) { printf("The radeon backlight looks %s\n",(lvds_gen_cntl&(RADEON_LVDS_ON))?"on":"off"); exit (-1); } else if(strcmp(param,"on") == 0) { lvds_gen_cntl |= RADEON_LVDS_ON; } else if(strcmp(param,"off") == 0) { lvds_gen_cntl &= ~ RADEON_LVDS_ON; } else { usage(); }; radeon_set(RADEON_LVDS_GEN_CNTL,"RADEON_LVDS_GEN_CNTL",lvds_gen_cntl); } void radeon_cmd_stretch(char *param) { unsigned long fp_vert_stretch,fp_horz_stretch; fp_vert_stretch = radeon_get(RADEON_FP_VERT_STRETCH,"RADEON_FP_VERT_STRETCH"); fp_horz_stretch = radeon_get(RADEON_FP_HORZ_STRETCH,"RADEON_FP_HORZ_STRETCH"); if(param == NULL) { printf("The horizontal stretching looks %s\n",(fp_horz_stretch&(RADEON_HORZ_STRETCH_ENABLE))?"on":"off"); printf("The vertical stretching looks %s\n",(fp_vert_stretch&(RADEON_VERT_STRETCH_ENABLE))?"on":"off"); exit (-1); } else if(strncmp(param,"ver",3) == 0) { fp_horz_stretch &= ~ RADEON_HORZ_STRETCH_ENABLE; fp_vert_stretch |= RADEON_VERT_STRETCH_ENABLE; } else if(strncmp(param,"hor",3) == 0) { fp_horz_stretch |= RADEON_HORZ_STRETCH_ENABLE; fp_vert_stretch &= ~ RADEON_VERT_STRETCH_ENABLE; } else if(strcmp(param,"on") == 0) { fp_horz_stretch |= RADEON_HORZ_STRETCH_ENABLE; fp_vert_stretch |= RADEON_VERT_STRETCH_ENABLE; } else if(strcmp(param,"auto") == 0) { fp_horz_stretch |= RADEON_HORZ_AUTO_RATIO; fp_horz_stretch |= RADEON_HORZ_AUTO_RATIO_INC; fp_horz_stretch |= RADEON_HORZ_STRETCH_ENABLE; fp_vert_stretch |= RADEON_VERT_AUTO_RATIO_EN; fp_vert_stretch |= RADEON_VERT_STRETCH_ENABLE; } else if(strcmp(param,"manual") == 0) { fp_horz_stretch &= ~ RADEON_HORZ_AUTO_RATIO; fp_horz_stretch &= ~ RADEON_HORZ_AUTO_RATIO_INC; fp_horz_stretch |= RADEON_HORZ_STRETCH_ENABLE; fp_vert_stretch &= ~ RADEON_VERT_AUTO_RATIO_EN; fp_vert_stretch |= RADEON_VERT_STRETCH_ENABLE; } else if(strcmp(param,"off") == 0) { fp_horz_stretch &= ~ RADEON_HORZ_STRETCH_ENABLE; fp_vert_stretch &= ~ RADEON_VERT_STRETCH_ENABLE; } else { usage(); }; radeon_set(RADEON_FP_HORZ_STRETCH,"RADEON_FP_HORZ_STRETCH",fp_horz_stretch); radeon_set(RADEON_FP_VERT_STRETCH,"RADEON_FP_VERT_STRETCH",fp_vert_stretch); } #ifdef __LINUX__ /* Here we fork() and exec() the lspci command to look for the Radeon hardware address. */ static void map_radeon_cntl_mem(void) { int pipefd[2]; int forkrc; FILE *fp; char line[1000]; int base; if(pipe(pipefd)) { fatal("pipe failure\n"); } forkrc = fork(); if(forkrc == -1) { fatal("fork failure\n"); } else if(forkrc == 0) { /* if child */ close(pipefd[0]); dup2(pipefd[1],1); /* stdout */ setenv("PATH","/sbin:/usr/sbin:/bin:/usr/bin",1); execlp("lspci","lspci","-v",NULL); fatal("exec lspci failure\n"); } close(pipefd[1]); fp = fdopen(pipefd[0],"r"); if(fp == NULL) { fatal("fdopen error\n"); } #if 0 This is an example output of "lspci -v" ... 00:1f.6 Modem: Intel Corp. 82801CA/CAM AC 97 Modem (rev 01) (prog-if 00 [Generic]) Subsystem: PCTel Inc: Unknown device 4c21 Flags: bus master, medium devsel, latency 0, IRQ 11 I/O ports at d400 [size=256] I/O ports at dc00 [size=128] 01:00.0 VGA compatible controller: ATI Technologies Inc Radeon Mobility M6 LY (prog-if 00 [VGA]) Subsystem: Dell Computer Corporation: Unknown device 00e3 Flags: bus master, VGA palette snoop, stepping, 66Mhz, medium devsel, latency 32, IRQ 11 Memory at e0000000 (32-bit, prefetchable) [size=128M] I/O ports at c000 [size=256] Memory at fcff0000 (32-bit, non-prefetchable) [size=64K] Expansion ROM at [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] Memory 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,"Radeon") || strstr(line,"ATI Tech")) { /* if line contains a "radeon" string */ if(skip-- < 1) { break; } } }; if(debug) printf("%s",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,"K")) { /* 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("Radeon found. Base control address is %x.\n",base); radeon_cntl_mem = map_devince_memory(base,0x2000); } #endif #ifdef __FreeBSD__ #define MAX_RADEON_CARDS 10 static void map_radeon_cntl_mem(void) { int fd ; struct pci_conf_io pci; struct pci_match_conf pmc; struct pci_conf pc[MAX_RADEON_CARDS]; struct pci_io pi; pmc.pc_vendor = 0x1002;/*ATI*/ pmc.pc_class = 0x3; /*Display*/ pci.offset =0; pmc.flags = PCI_GETCONF_MATCH_VENDOR|PCI_GETCONF_MATCH_CLASS; pci.pat_buf_len = sizeof(pmc); pci.num_patterns = 1; pci.patterns =&pmc; pci.match_buf_len = sizeof(pc); pci.num_matches=0; pci.matches = pc; if(skip >= MAX_RADEON_CARDS ){ fprintf(stderr,"I cannot handle cards so much\n"); exit(-1); } fd = open("/dev/pci", O_RDWR); if(fd == -1){ perror("open"); exit(-1); } if(ioctl(fd, PCIOCGETCONF, &pci) == -1){ perror("PCIOCGETCONF"); exit(-1); } if(pci.num_matches == 0){ fprintf(stderr, "Cannot found devices\n"); exit(-1); } if(pci.num_matches < skip ){ fprintf(stderr, "There are not so much ATI cards\n"); exit(-1); } pi.pi_sel = pc[skip].pc_sel; pi.pi_reg = 0x18; pi.pi_width = 4; if(ioctl(fd, PCIOCREAD, &pi) == -1){ perror("PCIOCREAD"); exit(-1); } if(pi.pi_data &1){ fprintf(stderr, "Not memory mapped\n"); exit(-1); } radeon_cntl_mem = map_devince_memory(pi.pi_data&0xfffffff0,0x2000); } #endif int main(int argc,char *argv[]) { #if 0 if(strcmp(argv[1],"--debug") == 0) { debug=1; argv++; argc--; }; if(strcmp(argv[1],"--skip=") == 0) { skip=atoi(argv[1]+7); argv++; argc--; }; #else int ch; static struct option longopts [] = { {"debug", no_argument, NULL, 'd'}, {"skip", required_argument, NULL, 's'}, {NULL, 0, NULL, 0} }; while((ch = getopt_long(argc, argv, "ds:", longopts, NULL)) != -1){ switch(ch){ case 'd': debug = 1; break; case 's': skip=atoi(optarg); break; default: usage(); } } optind --; argc -= optind; argv += optind; #endif if(argc == 1) { map_radeon_cntl_mem(); usage(); } map_radeon_cntl_mem(); if(argc == 2) { if(strcmp(argv[1],"regs") == 0) { radeon_cmd_regs(); return 0; } else if(strcmp(argv[1],"bits") == 0) { radeon_cmd_bits(); return 0; } else if(strcmp(argv[1],"dac") == 0) { radeon_cmd_dac(NULL); return 0; } else if(strcmp(argv[1],"light") == 0) { radeon_cmd_light(NULL); return 0; } else if(strcmp(argv[1],"stretch") == 0) { radeon_cmd_stretch(NULL); return 0; }; } else if(argc == 3) { if(strcmp(argv[1],"dac") == 0) { radeon_cmd_dac(argv[2]); return 0; } else if(strcmp(argv[1],"light") == 0) { radeon_cmd_light(argv[2]); return 0; } else if(strcmp(argv[1],"stretch") == 0) { radeon_cmd_stretch(argv[2]); return 0; }; }; usage(); return 1; }