You can compile it by typing this command :
Code: Select all
gcc -O2 -o pflip-nvidia pflip-nvidia.c
Code: Select all
./pflip-nvidia 512
Here is the code (pflip-nvidia.c) :
Code: Select all
/*
NAME
pflip-nvidia - test hardware page flipping for NVIDIA video cards
SYNOPSIS
pflip-nvidia <offset>
DESCRIPTION
pflip-nvidia is used to test hardware page flipping on graphic cards
supporting standard VGA registers while running an X Server.
To obtain meaningful results, a virtual screen must be active with an
horizontal resolution set to the double of the viewport horizontal
resolution. For example, a 1024x768 viewport with a 2048x768 virtual
screen.
The application is controlled using these interactive commands:
q quit the application
1 set the CRTC Start Address Register to 0
2 set the CRTC Start Address Register to <offset>
OPTIONS
<offset>
Set the offset value as a number of pixel columns that should
correspond to the width of the current viewport. For example
1920 for a 3840x1080 virtual screen.
AUTHOR
Frédéric Lopez <http://askyl.blog.free.fr/>
21 May 2012
*/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <inttypes.h>
#include <sys/mman.h>
// Define the "address" and "data" registers for VGA CRTC access
#define VGA_CRTC_ADDR_REG 0x3D4
#define VGA_CRTC_DATA_REG 0x3D5
#define PCI_GET_BUS(devbusfn) ((devbusfn >> 8) & 0xff)
#define PCI_GET_DEVICE(devbusfn) ((devbusfn & 0xff) >> 3)
#define PCI_GET_FUNCTION(devbusfn) (devbusfn & 0x7)
// Set the CRTC Start Adress Register
static void set_vga_start(volatile uint8_t *mem, int offset)
{
// Set the VGA CRTC Start Address Low Register
mem[VGA_CRTC_ADDR_REG] = 0x0D;
mem[VGA_CRTC_DATA_REG] = offset & 0xFF;
// Set the VGA CRTC Start Address High Register
mem[VGA_CRTC_ADDR_REG] = 0x0C;
mem[VGA_CRTC_DATA_REG] = offset >> 8;
}
int32_t pciReadLong(unsigned short devbusfn, long offset)
{
char file[25];
FILE *device;
short bus = PCI_GET_BUS(devbusfn);
short dev = PCI_GET_DEVICE(devbusfn);
short function = PCI_GET_FUNCTION(devbusfn);
snprintf(file, sizeof(file), "/proc/bus/pci/%02x/%02x.%x", bus, dev, function);
if ((device = fopen(file, "r")) != NULL)
{
int32_t buffer;
fseek(device, offset, SEEK_SET);
fread(&buffer, sizeof(int32_t), 1, device);
fclose(device);
return buffer;
}
return -1;
}
/* Check if the device is a videocard */
int is_video_card(unsigned short devbusfn)
{
int32_t pci_class = pciReadLong(devbusfn, 0x9);
/* The device is a VGA card */
if (((htonl(pci_class) >> 8) & 0xf) == 0x03)
return 1;
else
return 0;
}
/* Get the register offset */
int get_reg_addr()
{
int dev, dum, reg_addr, i = 0;
unsigned short devbusfn;
char buf[256];
FILE *proc;
proc = fopen("/proc/bus/pci/devices", "r");
if (!proc)
{
perror("Can't open /proc/bus/pci/devices !");
return -1;
}
while (fgets(buf, sizeof(buf) - 1, proc))
{
if (sscanf(buf,"%hx %x %x %x",&devbusfn, &dev, &dum, ®_addr) != 4)
continue;
/* The card contains an Nvidia chipset */
if ((dev >> 16) == 0x10de)
{
if (!is_video_card(devbusfn))
continue;
i++;
}
}
fclose(proc);
if (i == 0)
return -1;
else return reg_addr;
}
int main(int argc, char *argv[])
{
int key, offset;
char *p;
int fd;
void *map;
uint64_t res;
char buf[32];
volatile uint8_t *m;
int resfd;
// Test the number of command-line arguments
if (argc != 2)
{
printf("Usage: pflip-nvidia <offset>\n");
exit(EXIT_FAILURE);
}
// Test if the argument is an integer
errno = 0;
offset = strtol(argv[1], &p, 10);
if (errno != 0 || *p != 0 || p == argv[1])
{
printf("Error: offset value must be an integer!\n");
exit(EXIT_FAILURE);
}
// Show interactive commands help
printf("Quit with 'q'.\n");
printf("Page 1 with '1'.\n");
printf("Page 2 with '2'.\n");
// Find video card register offset
res = get_reg_addr();
if (res == -1)
{
printf("Error: no NVIDIA video card found!\n");
exit(EXIT_FAILURE);
}
// Open /dev/mem to map NVIDIA registers
if ((fd = open("/dev/mem", O_RDWR)) < 0) {
perror("Fail open :(");
return -1;
}
// Map NVIDIA VGA CRTC registers
m = map = mmap(0, 0x2000, PROT_READ|PROT_WRITE, MAP_SHARED, fd, res + 0x601000);
if (map == MAP_FAILED) {
perror("FAIL mmap :(");
return -1;
}
// Main loop
while ((key = getchar()) != 'q')
{
switch (key)
{
case '1' :
set_vga_start(m, 0);
printf("Page 1.\n");
break;
case '2' :
set_vga_start(m, offset);
printf("Page 2.\n");
break;
}
}
printf("Done.\n");
return EXIT_SUCCESS;
}
If it does work on a good range of NVIDIA GPUs I may be tempted to continue my work on my Genlock modification and my glstereo driver to implement a functional S3D driver for 120Hz displays under Linux.
Feel free to test it and to report back if it does work for you, giving your CPU, GPU and driver (NVIDIA or Nouveau) references.
Thank you.