voodoo/main.c

629 lines
17 KiB
C

#include <sys/types.h>
#include <sys/mman.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <assert.h>
#include <fcntl.h>
#include <math.h>
#include "voodoo2.h"
#include "voodoo2_bits.h"
#include "voodoo2_config.h"
#include "voodoo2_config_bits.h"
#include "ics5342.h"
static inline bool _wait_graphics_busy(voodoo2_reg * voodoo2)
{
int cnt = 0;
for (int i = 0; i < 128; i++) {
if (voodoo2->status & STATUS__CHUCK_BUSY) {
//if (voodoo2->status & STATUS__PCI_FIFO_FREESPACE(0x3f)) {
cnt = 0;
} else {
cnt += 1;
}
if (cnt >= 5)
return true;
}
//fprintf(stderr, "voodoo2 status %08x\n", voodoo2->status);
//return false;
return true;
}
#define wait_graphics_busy(voodoo2) assert(_wait_graphics_busy(voodoo2));
static inline bool _wait_graphics_busy2(voodoo2_reg * voodoo2)
{
int cnt = 0;
while (true) {
if (voodoo2->status & STATUS__CHUCK_BUSY) {
cnt = 0;
} else {
cnt += 1;
}
if (cnt >= 3)
return true;
}
return false;
}
#define wait_graphics_busy2(voodoo2) assert(_wait_graphics_busy2(voodoo2));
static inline void dac_data_read(voodoo2_reg * voodoo2,
int data,
int rs)
{
voodoo2->dacData
= DACDATA__WRITE_DATA(data)
| DACDATA__ADDRESS_2_0(rs >> 0)
| DACDATA__READ_COMMAND
| DACDATA__ADDRESS_4_3(rs >> 3)
;
}
static inline void dac_data_write(voodoo2_reg * voodoo2,
int data,
int rs)
{
voodoo2->dacData
= DACDATA__WRITE_DATA(data)
| DACDATA__ADDRESS_2_0(rs >> 0)
| DACDATA__ADDRESS_4_3(rs >> 3)
;
}
static inline void dac_write_pll_8(voodoo2_reg * voodoo2,
int address,
int parameter_m)
{
dac_data_write(voodoo2, address, DAC__RS__PLL_ADDRESS_WRITE);
wait_graphics_busy(voodoo2);
dac_data_write(voodoo2, parameter_m, DAC__RS__PLL_PARAMETER);
wait_graphics_busy(voodoo2);
}
static inline void dac_write_pll_16(voodoo2_reg * voodoo2,
int address,
int parameter_m,
int parameter_n)
{
dac_data_write(voodoo2, address, DAC__RS__PLL_ADDRESS_WRITE);
wait_graphics_busy(voodoo2);
dac_data_write(voodoo2, parameter_m, DAC__RS__PLL_PARAMETER);
wait_graphics_busy(voodoo2);
dac_data_write(voodoo2, parameter_n, DAC__RS__PLL_PARAMETER);
wait_graphics_busy(voodoo2);
}
typedef struct mn {
uint8_t m;
uint8_t n;
} mn_t;
static inline mn_t dac_read_pll_16(voodoo2_reg * voodoo2,
int address)
{
dac_data_write(voodoo2, address, DAC__RS__PLL_ADDRESS_READ);
wait_graphics_busy(voodoo2);
dac_data_read(voodoo2, 0, DAC__RS__PLL_PARAMETER);
wait_graphics_busy(voodoo2);
int m = voodoo2->fbiInit2 & 0xff;
dac_data_read(voodoo2, 0, DAC__RS__PLL_PARAMETER);
wait_graphics_busy(voodoo2);
int n = voodoo2->fbiInit2 & 0xff;
return (mn_t){ m, n };
}
uint8_t read_fd_u8(int fd, uint32_t offset)
{
off_t ret = lseek(fd, offset, SEEK_SET);
assert(ret == offset);
uint8_t value;
ssize_t len = read(fd, &value, 1);
assert(len == 1);
return value;
}
uint16_t read_fd_u16(int fd, uint32_t offset)
{
off_t ret = lseek(fd, offset, SEEK_SET);
assert(ret == offset);
uint16_t value;
ssize_t len = read(fd, &value, 2);
assert(len == 2);
return value;
}
void write_fd_u8(int fd, uint32_t offset, uint8_t value)
{
off_t ret = lseek(fd, offset, SEEK_SET);
assert(ret == offset);
ssize_t len = write(fd, &value, 1);
assert(len == 1);
}
void write_fd_u32(int fd, uint32_t offset, uint32_t value)
{
off_t ret = lseek(fd, offset, SEEK_SET);
assert(ret == offset);
ssize_t len = write(fd, &value, 4);
assert(len == 4);
}
typedef struct vec3 {
float x;
float y;
float z;
} vec3;
static const vec3 cube_vertex_position[] = {
{ 1.0f, 1.0f, -1.0f },
{ 1.0f, -1.0f, -1.0f },
{ 1.0f, 1.0f, 1.0f },
{ 1.0f, -1.0f, 1.0f },
{ -1.0f, 1.0f, -1.0f },
{ -1.0f, -1.0f, -1.0f },
{ -1.0f, 1.0f, 1.0f },
{ -1.0f, -1.0f, 1.0f },
};
static const uint32_t cube_vertex_color[] = {
0xff0000, // red
0x00ff00, // green
0x0000ff, // blue
0xffff00, // yellow
0x00ffff, // cyan
0xff00ff, // magenta
0xff7f00, // orange
0x7f00ff, // violet
};
typedef struct face {
int a;
int b;
int c;
} face;
static const face cube_faces[] = {
{4, 2, 0},
{2, 7, 3},
{6, 5, 7},
{1, 7, 5},
{0, 3, 1},
{4, 1, 5},
{4, 6, 2},
{2, 6, 7},
{6, 4, 5},
{1, 3, 7},
{0, 2, 3},
{4, 0, 1},
};
static const int cube_faces_length = (sizeof (cube_faces)) / (sizeof (cube_faces[0]));
static float theta = 0;
static inline vec3 vertex_rotate(vec3 v)
{
// to make the cube's appearance more interesting, rotate the vertex on two
// axes
float x0 = v.x;
float y0 = v.y;
float z0 = v.z;
float x1 = x0 * cos(theta) - z0 * sin(theta);
float y1 = y0;
float z1 = x0 * sin(theta) + z0 * cos(theta);
float x2 = x1;
float y2 = y1 * cos(theta) - z1 * sin(theta);
float z2 = y1 * sin(theta) + z1 * cos(theta);
return (vec3){x2, y2, z2};
}
static inline vec3 vertex_perspective_divide(vec3 v)
{
float w = 1.0f / (v.z + 3.0f);
return (vec3){v.x * w, v.y * w, w};
}
static inline vec3 vertex_screen_space(vec3 v)
{
return (vec3){
v.x * 300.f + 400.f,
v.y * 300.f + 300.f,
v.z,
};
}
int main()
{
const char * config_path = "/sys/bus/pci/devices/0000:02:0a.0/config";
int config_fd = open(config_path, O_RDWR | O_SYNC);
assert(config_fd >= 0);
uint16_t vendor_id = read_fd_u16(config_fd, PCI__CONFIG__VENDOR_ID);
uint16_t device_id = read_fd_u16(config_fd, PCI__CONFIG__DEVICE_ID);
printf("Vendor ID: %04x\n", vendor_id);
printf("Device ID: %04x\n", device_id);
assert(vendor_id == 0x121a);
assert(device_id == 0x0002);
////////////////////////////////////////////////////////////////////////
// PCI enable
////////////////////////////////////////////////////////////////////////
{
const char * enable_path = "/sys/bus/pci/devices/0000:02:0a.0/enable";
int enable_fd = open(enable_path, O_RDWR | O_SYNC);
assert(enable_fd >= 0);
if (read_fd_u8(enable_fd, 0) == '0') {
write_fd_u8(enable_fd, 0, '1'); // enable
assert(read_fd_u8(enable_fd, 0) != '0');
}
close(enable_fd);
}
////////////////////////////////////////////////////////////////////////
// PCI resource0
////////////////////////////////////////////////////////////////////////
const char * resource0_path = "/sys/bus/pci/devices/0000:02:0a.0/resource0";
int resource0_fd = open(resource0_path, O_RDWR | O_SYNC);
assert(resource0_fd >= 0);
uint32_t resource0_size = 16 * 1024 * 1024;
void * resource0_base = mmap(0, resource0_size, PROT_READ | PROT_WRITE, MAP_SHARED, resource0_fd, 0);
assert(resource0_base != MAP_FAILED);
voodoo2_reg * voodoo2 = (voodoo2_reg *)resource0_base;
int init_enable;
goto draw;
////////////////////////////////////////////////////////////////////////
// reset
////////////////////////////////////////////////////////////////////////
init_enable
= INIT_ENABLE__ENABLE_WRITES_TO_HARDWARE_REGISTERS;
write_fd_u32(config_fd, PCI__CONFIG__INIT_ENABLE, init_enable);
wait_graphics_busy(voodoo2);
voodoo2->fbiInit0
= FBIINIT0__CHUCK_GRAPHICS_RESET
| FBIINIT0__CHUCK_FIFO_RESET
;
wait_graphics_busy(voodoo2);
voodoo2->fbiInit1
= FBIINIT1__VIDEO_TIMING_RESET
;
wait_graphics_busy(voodoo2);
voodoo2->fbiInit2
= 0; // disable DRAM refresh
wait_graphics_busy(voodoo2);
////////////////////////////////////////////////////////////////////////
// DAC initialization
////////////////////////////////////////////////////////////////////////
// enable fbiInit2/dacRead remap
init_enable
= INIT_ENABLE__ENABLE_WRITES_TO_HARDWARE_REGISTERS
| INIT_ENABLE__REMAP_FBIINIT2_FBIINIT3_TO_DACREAD_VIDEOCHECKSUM;
write_fd_u32(config_fd, PCI__CONFIG__INIT_ENABLE, init_enable);
wait_graphics_busy(voodoo2);
int command = DAC__COMMAND__COLOR_MODE__16BIT_DIRECT_COLOR_WITH_BYPASS;
dac_data_write(voodoo2, command, DAC__RS__COMMAND);
wait_graphics_busy(voodoo2);
dac_data_read(voodoo2, command, DAC__RS__COMMAND);
wait_graphics_busy(voodoo2);
int command_read = voodoo2->fbiInit2 & 0xff;
printf("dac read: DAC__RS__COMMAND: m: %02x\n", command_read);
/*
pixel clock: 40MHz
graphics/memory clock: 44.6MHz
*/
int m = 54;
int n = 67;
dac_write_pll_16(voodoo2, DAC__PLL_PARAMETER__CLK0_f0_PLL, m, n);
mn_t clk0_f0_res = dac_read_pll_16(voodoo2, DAC__PLL_PARAMETER__CLK0_f0_PLL);
printf("dac read: DAC__PLL_PARAMETER__CLK0_f0_PLL: m: %02x n: %02x\n",
clk0_f0_res.m, clk0_f0_res.n);
int pll_control
= DAC__PLL_CONTROL__CLK0_SELECT(0) // f0
| DAC__PLL_CONTROL__CLK1_SELECT(0) // fA
| DAC__PLL_CONTROL__ENABLE_INTERNAL_CLOCK_SELECT;
dac_write_pll_8(voodoo2, DAC__PLL_PARAMETER__PLL_CONTROL, pll_control);
mn_t pll_control_res = dac_read_pll_16(voodoo2, DAC__PLL_PARAMETER__PLL_CONTROL);
printf("dac read: DAC__PLL_PARAMETER__PLL_CONTROL: m: %02x\n",
pll_control_res.m);
// disable fbiInit2/dacRead remap
init_enable
= INIT_ENABLE__ENABLE_WRITES_TO_HARDWARE_REGISTERS;
write_fd_u32(config_fd, PCI__CONFIG__INIT_ENABLE, init_enable);
wait_graphics_busy(voodoo2);
////////////////////////////////////////////////////////////////////////
// framebuffer initialization
////////////////////////////////////////////////////////////////////////
voodoo2->fbiInit0
= FBIINIT0__VGA_PASSTHROUGH // disable VGA passthrough
| FBIINIT0__STALL_PCI_ENABLE_FOR_HIGH_WATER_MARK
| FBIINIT0__PCI_FIFO_EMPTY_ENTRIES_LOW_WATER_MARK(0x10)
;
wait_graphics_busy(voodoo2);
int x_tiles = 832 / 32;
int x_tiles_0 = (x_tiles >> 0) & 0b1;
int x_tiles_4_1 = (x_tiles >> 1) & 0b1111;
int x_tiles_5 = (x_tiles >> 5) & 0b1;
voodoo2->fbiInit1
= FBIINIT1__ENABLE_LINEAR_FRAME_BUFFER_READS
| FBIINIT1__NUMBER_OF_32X32_VIDEO_TILES_IN_HORIZONTAL_DIMENSION_4_1(x_tiles_4_1)
| FBIINIT1__DRIVE_VIDEO_TIMING_DATA_OUTPUTS
| FBIINIT1__DRIVE_VIDEO_TIMING_BLANK_OUTPUTS
| FBIINIT1__DRIVE_VIDEO_TIMING_HSYNC_VSYNC_OUTPUTS
| FBIINIT1__DRIVE_VIDEO_TIMING_DCLK_OUTPUT
| FBIINIT1__VIDEO_TIMING_VCLK_INPUT_SELECT_0(0) // vid_clk_2x
| FBIINIT1__VIDEO_TIMING_VCLK_SOURCE_SELECT__VID_CLK_2X_SEL
| FBIINIT1__NUMBER_OF_32X32_VIDEO_TILES_IN_HORIZONTAL_DIMENSION_5(x_tiles_5)
;
wait_graphics_busy(voodoo2);
voodoo2->fbiInit2
= FBIINIT2__DRAM_BANKING_CONFIGURATION
| FBIINIT2__ENABLE_FAST_RAS_READ_CYCLES
| FBIINIT2__ENABLE_GENERATED_DRAM_OE_SIGNAL
| FBIINIT2__VIDEO_BUFFER_OFFSET(247)
| FBIINIT2__ENABLE_DRAM_READ_AHEAD_FIFO
| FBIINIT2__ENABLE_DRAM_BANKING
| FBIINIT2__REFRESH_ENABLE
| FBIINIT2__REFRESH_LOAD_VALUE(0x30)
;
wait_graphics_busy(voodoo2);
voodoo2->fbiInit3
= FBIINIT3__DISABLE_TEXTURE_MAPPING
;
wait_graphics_busy(voodoo2);
voodoo2->fbiInit4
= FBIINIT4__ENABLE_READ_AHEAD_LOGIC_FOR_LINEAR_FRAME_BUFFER_READS
;
wait_graphics_busy(voodoo2);
voodoo2->fbiInit5
= FBIINIT5__VIDEO_TIMING_VCLK_INPUT_SELECT_1(0) // vid_clk_2x
| FBIINIT5__INVERT_DAC_HSYNC_OUTPUT_TO_DAC
| FBIINIT5__INVERT_DAC_VSYNC_OUTPUT_TO_DAC
;
wait_graphics_busy(voodoo2);
voodoo2->fbiInit6
= FBIINIT6__NUMBER_OF_32X32_VIDEO_TILES_IN_HORIZONTAL_DIMENSION_0(x_tiles_0)
;
wait_graphics_busy(voodoo2);
////////////////////////////////////////////////////////////////////////
// video timing initialization
////////////////////////////////////////////////////////////////////////
int y_height = 600;
int x_width = 800;
int v_back_porch = 628 - 605;
int h_back_porch = 1056 - 968;
int v_sync_on = 605 - 601;
int h_sync_on = 968 - 840;
int v_sync_off = 628 - v_sync_on;
int h_sync_off = 1056 - h_sync_on;
voodoo2->hSync
= HSYNC__H_SYNC_ON(h_sync_on - 1)
| HSYNC__H_SYNC_OFF(h_sync_off)
;
wait_graphics_busy(voodoo2);
voodoo2->vSync
= VSYNC__V_SYNC_ON(v_sync_on)
| VSYNC__V_SYNC_OFF(v_sync_off)
;
wait_graphics_busy(voodoo2);
voodoo2->backPorch
= BACKPORCH__H_BACK_PORCH(h_back_porch - 2)
| BACKPORCH__V_BACK_PORCH(v_back_porch)
;
wait_graphics_busy(voodoo2);
voodoo2->videoDimensions
= VIDEODIMENSIONS__X_WIDTH(x_width - 1)
| VIDEODIMENSIONS__Y_HEIGHT(y_height)
;
wait_graphics_busy(voodoo2);
////////////////////////////////////////////////////////////////////////
// panda
////////////////////////////////////////////////////////////////////////
init_enable
= INIT_ENABLE__ENABLE_WRITES_TO_PCI_FIFO;
write_fd_u32(config_fd, PCI__CONFIG__INIT_ENABLE, init_enable);
wait_graphics_busy(voodoo2);
if (0) {
/*
static const uint16_t panda[] __attribute__((aligned(4))) = {
#include "panda.rgb565.inc"
};
static const uint16_t bear[] __attribute__((aligned(4))) = {
#include "bear.rgb565.inc"
};
while (true) {
volatile uint16_t * framebuffer
= (volatile uint16_t * )(((ptrdiff_t)resource0_base) + 0x0400000);
printf("panda\n");
for (int y = 0; y < 600; y++) {
for (int x = 0; x < 800; x++) {
framebuffer[y * 1024 + x] = panda[y * 800 + x];
wait_graphics_busy(voodoo2);
}
}
sleep(1);
printf("bear\n");
for (int y = 0; y < 600; y++) {
for (int x = 0; x < 800; x++) {
framebuffer[y * 1024 + x] = bear[y * 800 + x];
wait_graphics_busy(voodoo2);
}
}
sleep(1);
}
*/
}
////////////////////////////////////////////////////////////////////////
// triangle
////////////////////////////////////////////////////////////////////////
draw:
voodoo2->fbzMode
= FBZMODE__DEPTH_BUFFER_FUNCTION__LESS_THAN
| FBZMODE__ENABLE_DEPTH_BUFFERING
| FBZMODE__FLOATING_POINT_DEPTH_BUFFER
| FBZMODE__RGB_BUFFER_WRITE_MASK
| FBZMODE__DEPTH_ALPHA_BUFFER_WRITE_MASK
| FBZMODE__DRAW_BUFFER__BACK_BUFFER
;
wait_graphics_busy(voodoo2);
voodoo2->lfbMode
= LFBMODE__LINEAR_FRAME_BUFFER__WRITE_FORMAT__RGB565
| LFBMODE__LINEAR_FRAME_BUFFER__WRITE_BUFFER_SELECT__BACK_BUFFER
| LFBMODE__LINEAR_FRAME_BUFFER__READ_BUFFER_SELECT__FRONT_BUFFER
| LFBMODE__LINEAR_FRAME_BUFFER__ENABLE_PIXEL_PIPELINE_WRITES
| LFBMODE__LINEAR_FRAME_BUFFER__RGBA_LANES__ARGB
;
wait_graphics_busy(voodoo2);
voodoo2->sSetupMode
= SSETUPMODE__SETUP_RED_GREEN_BLUE
| SSETUPMODE__SETUP_WB
;
wait_graphics_busy(voodoo2);
//
voodoo2->clipLeftRight
= CLIPLEFTRIGHT__RIGHT(800)
| CLIPLEFTRIGHT__LEFT(0);
wait_graphics_busy(voodoo2);
voodoo2->clipLowYHighY
= CLIPLOWYHIGHY__HIGH_Y(600)
| CLIPLOWYHIGHY__LOW_Y(0);
wait_graphics_busy(voodoo2);
voodoo2->color1 = 0xff008080;
wait_graphics_busy(voodoo2);
voodoo2->zaColor
= ZACOLOR__DEPTH(0xffff)
| ZACOLOR__ALPHA(0xff)
;
for (int i = 0; i < 601; i++) {
/*
volatile uint16_t * framebuffer
= (volatile uint16_t * )(((ptrdiff_t)resource0_base) + 0x0400000);
for (int i = 0; i < 1024 * 800 * 2 / 2; i++) {
framebuffer[i] = 0;
}
wait_graphics_busy(voodoo2);
*/
voodoo2->fastfillCMD = 0;
wait_graphics_busy2(voodoo2);
for (int face_ix = 0; face_ix < cube_faces_length; face_ix++) {
int ia = cube_faces[face_ix].a;
int ib = cube_faces[face_ix].b;
int ic = cube_faces[face_ix].c;
vec3 va = vertex_screen_space(
vertex_perspective_divide(
vertex_rotate(cube_vertex_position[ia])));
vec3 vb = vertex_screen_space(
vertex_perspective_divide(
vertex_rotate(cube_vertex_position[ib])));
vec3 vc = vertex_screen_space(
vertex_perspective_divide(
vertex_rotate(cube_vertex_position[ic])));
uint32_t va_color = cube_vertex_color[ia];
uint32_t vb_color = cube_vertex_color[ib];
uint32_t vc_color = cube_vertex_color[ic];
voodoo2->sVx = va.x;
voodoo2->sVy = va.y;
voodoo2->sWb = va.z;
voodoo2->sARGB = va_color;
voodoo2->sBeginTriCMD = 1;
wait_graphics_busy(voodoo2);
voodoo2->sVx = vb.x;
voodoo2->sVy = vb.y;
voodoo2->sWb = vb.z;
voodoo2->sARGB = vb_color;
voodoo2->sDrawTriCMD = 1;
wait_graphics_busy(voodoo2);
voodoo2->sVx = vc.x;
voodoo2->sVy = vc.y;
voodoo2->sWb = vc.z;
voodoo2->sARGB = vc_color;
voodoo2->sDrawTriCMD = 1;
wait_graphics_busy(voodoo2);
}
// swap front/back buffers
voodoo2->swapbufferCMD = 1;
//wait_graphics_busy(voodoo2);
//asdf
while (voodoo2->status & STATUS__SWAP_BUFFERS_PENDING(0x7));
theta += 0.01f;
}
////////////////////////////////////////////////////////////////////////
// cleanup
////////////////////////////////////////////////////////////////////////
close(config_fd);
close(resource0_fd);
return 0;
}