446 lines
13 KiB
C
446 lines
13 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 "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 < 4096; i++) {
|
|
if (voodoo2->status & STATUS__CHUCK_BUSY) {
|
|
cnt = 0;
|
|
} else {
|
|
cnt += 1;
|
|
}
|
|
if (cnt >= 3)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#define wait_graphics_busy(voodoo2) assert(_wait_graphics_busy(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);
|
|
}
|
|
|
|
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;
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// 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__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
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
voodoo2->fbzMode
|
|
= FBZMODE__DEPTH_BUFFER_FUNCTION__ALWAYS
|
|
| FBZMODE__RGB_BUFFER_WRITE_MASK
|
|
| FBZMODE__DEPTH_ALPHA_BUFFER_WRITE_MASK
|
|
| FBZMODE__DRAW_BUFFER__FRONT_BUFFER
|
|
;
|
|
|
|
voodoo2->lfbMode
|
|
= LFBMODE__LINEAR_FRAME_BUFFER__WRITE_FORMAT__RGB565
|
|
| LFBMODE__LINEAR_FRAME_BUFFER__WRITE_BUFFER_SELECT__FRONT_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
|
|
;
|
|
|
|
voodoo2->sSetupMode
|
|
= SSETUPMODE__SETUP_RED_GREEN_BLUE
|
|
;
|
|
wait_graphics_busy(voodoo2);
|
|
|
|
voodoo2->sBeginTriCMD = 1;
|
|
wait_graphics_busy(voodoo2);
|
|
|
|
voodoo2->sVx = 10;
|
|
voodoo2->sVy = 10;
|
|
voodoo2->sARGB = 0xffff00ff;
|
|
voodoo2->sDrawTriCMD = 1;
|
|
wait_graphics_busy(voodoo2);
|
|
|
|
voodoo2->sVx = 10;
|
|
voodoo2->sVy = 100;
|
|
voodoo2->sARGB = 0xffff00ff;
|
|
voodoo2->sDrawTriCMD = 1;
|
|
wait_graphics_busy(voodoo2);
|
|
|
|
voodoo2->sVx = 100;
|
|
voodoo2->sVy = 100;
|
|
voodoo2->sARGB = 0xffff00ff;
|
|
voodoo2->sDrawTriCMD = 1;
|
|
wait_graphics_busy(voodoo2);
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// cleanup
|
|
////////////////////////////////////////////////////////////////////////
|
|
close(config_fd);
|
|
close(resource0_fd);
|
|
|
|
return 0;
|
|
}
|