/* Radeontool v1.4
* by Frederick Dean <software@fdd.com>
* 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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <getopt.h>
#ifdef __LINUX__
#include <asm/page.h>
#elif __FreeBSD__
#include <machine/param.h>
#include <sys/pciio.h>
#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 <unassigned> [disabled] [size=128K]
Capabilities: <available only to root>
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: <available only to root>
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;
}
syntax highlighted by Code2HTML, v. 0.9.1