wip
This commit is contained in:
parent
6413c2ef1d
commit
b668f34e53
9
.gitignore
vendored
9
.gitignore
vendored
@ -3,3 +3,12 @@
|
||||
*.pyc
|
||||
__pycache__
|
||||
*.gch
|
||||
*.o
|
||||
*.mod
|
||||
*.ko
|
||||
*~
|
||||
*.symvers
|
||||
.*
|
||||
*.cmd
|
||||
*.order
|
||||
*.mod.c
|
58
main.c
Normal file
58
main.c
Normal file
@ -0,0 +1,58 @@
|
||||
#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"
|
||||
|
||||
struct name_int {
|
||||
const char * name;
|
||||
uint32_t value;
|
||||
};
|
||||
|
||||
void print_registers(struct voodoo2_reg * reg)
|
||||
{
|
||||
const struct name_int registers[] = {
|
||||
{"status", reg->status},
|
||||
{"fbiInit0", reg->fbiInit0},
|
||||
{"fbiInit1", reg->fbiInit1},
|
||||
{"fbiInit2", reg->fbiInit2},
|
||||
{"fbiInit3", reg->fbiInit3},
|
||||
{"fbiInit4", reg->fbiInit4},
|
||||
{"fbiInit5", reg->fbiInit5},
|
||||
{"fbiInit6", reg->fbiInit6},
|
||||
{"fbiInit7", reg->fbiInit7},
|
||||
//
|
||||
{"backPorch", reg->backPorch},
|
||||
{"videoDimensions", reg->videoDimensions},
|
||||
{"hSync", reg->hSync},
|
||||
{"vSync", reg->vSync},
|
||||
};
|
||||
|
||||
for (int i = 0; i < (sizeof (registers)) / (sizeof (registers[0])); i++) {
|
||||
printf("%s %08x\n", registers[i].name, registers[i].value);
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
const char * filename = "/sys/bus/pci/devices/0000:02:0a.0/resource0";
|
||||
|
||||
int fd = open(filename, O_RDWR | O_SYNC);
|
||||
assert(fd >= 0);
|
||||
|
||||
uint32_t map_size = 0x4000;
|
||||
|
||||
off_t target_base = 0;
|
||||
void * map_base = mmap(0, map_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, target_base);
|
||||
assert(map_base != MAP_FAILED);
|
||||
|
||||
struct voodoo2_reg * reg = (struct voodoo2_reg *)map_base;
|
||||
print_registers(reg);
|
||||
|
||||
close(fd);
|
||||
}
|
19
pci/Makefile
Normal file
19
pci/Makefile
Normal file
@ -0,0 +1,19 @@
|
||||
BINARY := test_pci_module
|
||||
KERNEL := /lib/modules/$(shell uname -r)/build
|
||||
ARCH := x86
|
||||
C_FLAGS := -Wall
|
||||
KMOD_DIR := $(shell pwd)
|
||||
|
||||
OBJECTS := main.o
|
||||
|
||||
ccflags-y += $(C_FLAGS)
|
||||
|
||||
obj-m += $(BINARY).o
|
||||
|
||||
$(BINARY)-y := $(OBJECTS)
|
||||
|
||||
$(BINARY).ko:
|
||||
make -C $(KERNEL) M=$(KMOD_DIR) modules
|
||||
|
||||
clean:
|
||||
rm -f $(BINARY).ko
|
548
pci/main.c
Normal file
548
pci/main.c
Normal file
@ -0,0 +1,548 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#define VOODOO2 "voodoo2"
|
||||
|
||||
static struct pci_device_id voodoo2_id_table[] = {
|
||||
{ PCI_DEVICE(0x121a, 0x0002) },
|
||||
{ 0,}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(pci, voodoo2_id_table);
|
||||
|
||||
static int voodoo2_probe(struct pci_dev *pdev, const struct pci_device_id *ent);
|
||||
static void voodoo2_remove(struct pci_dev *pdev);
|
||||
|
||||
/* Module registration structure */
|
||||
static struct pci_driver voodoo2 = {
|
||||
.name = VOODOO2,
|
||||
.id_table = voodoo2_id_table,
|
||||
.probe = voodoo2_probe,
|
||||
.remove = voodoo2_remove
|
||||
};
|
||||
|
||||
/* This is a "private" data structure */
|
||||
/* You can store there any data that should be passed between module's functions */
|
||||
struct voodoo2_priv {
|
||||
volatile u32 __iomem *hwmem;
|
||||
};
|
||||
|
||||
/* */
|
||||
|
||||
static int __init voodoo2_module_init(void)
|
||||
{
|
||||
return pci_register_driver(&voodoo2);
|
||||
}
|
||||
|
||||
static void __exit voodoo2_module_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&voodoo2);
|
||||
}
|
||||
|
||||
void release_device(struct pci_dev *pdev);
|
||||
|
||||
void release_device(struct pci_dev *pdev)
|
||||
{
|
||||
/* Free memory region */
|
||||
pci_release_region(pdev, pci_select_bars(pdev, IORESOURCE_MEM));
|
||||
/* And disable device */
|
||||
pci_disable_device(pdev);
|
||||
}
|
||||
|
||||
struct name_int {
|
||||
const char * name;
|
||||
int offset;
|
||||
};
|
||||
|
||||
#define STATUS 0x000
|
||||
#define FBIINIT0 0x210
|
||||
#define FBIINIT1 0x214
|
||||
#define FBIINIT2 0x218
|
||||
#define FBIINIT3 0x21c
|
||||
#define FBIINIT4 0x200
|
||||
#define FBIINIT5 0x244
|
||||
#define FBIINIT6 0x248
|
||||
#define FBIINIT7 0x24c
|
||||
#define DACDATA 0x22c
|
||||
|
||||
#define LFBMODE 0x114
|
||||
|
||||
#define BACKPORCH 0x208
|
||||
#define VIDEODIMENSIONS 0x20c
|
||||
#define HSYNC 0x220
|
||||
#define VSYNC 0x224
|
||||
|
||||
const struct name_int registers[] = {
|
||||
{"status", STATUS},
|
||||
{"fbiInit0", FBIINIT0},
|
||||
{"fbiInit1", FBIINIT1},
|
||||
{"fbiInit2", FBIINIT2},
|
||||
{"fbiInit3", FBIINIT3},
|
||||
{"fbiInit4", FBIINIT4},
|
||||
{"fbiInit5", FBIINIT5},
|
||||
{"fbiInit6", FBIINIT6},
|
||||
{"fbiInit7", FBIINIT7},
|
||||
//
|
||||
{"backPorch", BACKPORCH},
|
||||
{"videoDimensions", VIDEODIMENSIONS},
|
||||
{"hSync", HSYNC},
|
||||
{"vSync", VSYNC},
|
||||
};
|
||||
|
||||
static inline void dac_data(volatile uint32_t * base, int data, int rs, int read)
|
||||
{
|
||||
int value = 0
|
||||
| ((data & 0xff) << 0)
|
||||
| ((rs & 0b111) << 8)
|
||||
| ((read & 0b1) << 11);
|
||||
|
||||
//printk(KERN_INFO "DACDATA = %08x\n", value);
|
||||
base[DACDATA / 4] = value;
|
||||
}
|
||||
|
||||
#define DAC__RS__PIXEL_ADDRESS_WRITE 0b000 // TDV_GENDAC_WR
|
||||
#define DAC__RS__PIXEL_ADDRESS_READ 0b011 // TDV_GENDAC_RD
|
||||
#define DAC__RS__COLOR_VALUE 0b001 // TDV_GENDAC_LUT
|
||||
#define DAC__RS__PIXEL_MASK 0b010 // TDV_GENDAC_PIXMASK
|
||||
#define DAC__RS__PLL_ADDRESS_WRITE 0b100 // TDV_GENDAC_PLLWR
|
||||
#define DAC__RS__PLL_PARAMETER 0b101 // TDV_GENDAC_PLLDATA
|
||||
#define DAC__RS__COMMAND 0b110 // TDV_GENDAC_CMD
|
||||
#define DAC__RS__PLL_ADDRESS_READ 0b111 // TDV_GENDAC_PLLRD
|
||||
|
||||
#define CLK0_f0_PLL 0x00
|
||||
#define CLK0_f1_PLL 0x01
|
||||
#define CLK0_f2_PLL 0x02
|
||||
#define CLK0_f3_PLL 0x03
|
||||
#define CLK0_f4_PLL 0x04
|
||||
#define CLK0_f5_PLL 0x05
|
||||
#define CLK0_f6_PLL 0x06
|
||||
#define CLK0_f7_PLL 0x07
|
||||
#define CLK1_fA_PLL 0x0A
|
||||
#define CLK1_fB_PLL 0x0B
|
||||
#define PLL_CONTROL 0x0E
|
||||
|
||||
const struct name_int dac_registers[] = {
|
||||
{"CLK0_f0_PLL", CLK0_f0_PLL},
|
||||
{"CLK0_f1_PLL", CLK0_f1_PLL},
|
||||
{"CLK0_f2_PLL", CLK0_f2_PLL},
|
||||
{"CLK0_f3_PLL", CLK0_f3_PLL},
|
||||
{"CLK0_f4_PLL", CLK0_f4_PLL},
|
||||
{"CLK0_f5_PLL", CLK0_f5_PLL},
|
||||
{"CLK0_f6_PLL", CLK0_f6_PLL},
|
||||
{"CLK0_f7_PLL", CLK0_f7_PLL},
|
||||
{"CLK1_fA_PLL", CLK1_fA_PLL},
|
||||
{"CLK1_fB_PLL", CLK1_fB_PLL},
|
||||
{"PLL_CONTROL", PLL_CONTROL},
|
||||
};
|
||||
|
||||
#define MAXLOOP 4096
|
||||
|
||||
static void
|
||||
busy_wait(volatile uint32_t * base)
|
||||
{
|
||||
uint32_t x, cnt;
|
||||
cnt = 0;
|
||||
for (x = 0; x < MAXLOOP; x++) {
|
||||
if (base[STATUS / 4] & (1 << 7)) // busy
|
||||
cnt = 0;
|
||||
else
|
||||
cnt++;
|
||||
|
||||
if (cnt >= 5) /* Voodoo2 specs suggest at least 3 */
|
||||
break;
|
||||
}
|
||||
|
||||
if (x == MAXLOOP)
|
||||
printk(KERN_INFO "wait timeout\n");
|
||||
}
|
||||
|
||||
static inline void dac_write_8(volatile uint32_t * base, int param_address, int m)
|
||||
{
|
||||
dac_data(base, param_address, DAC__RS__PLL_ADDRESS_WRITE, 0);
|
||||
busy_wait(base);
|
||||
|
||||
dac_data(base, m, DAC__RS__PLL_PARAMETER, 0);
|
||||
busy_wait(base);
|
||||
}
|
||||
|
||||
static inline void dac_write_16(volatile uint32_t * base, int param_address, int m, int n)
|
||||
{
|
||||
dac_data(base, param_address, DAC__RS__PLL_ADDRESS_WRITE, 0);
|
||||
busy_wait(base);
|
||||
|
||||
dac_data(base, m, DAC__RS__PLL_PARAMETER, 0);
|
||||
busy_wait(base);
|
||||
|
||||
dac_data(base, n, DAC__RS__PLL_PARAMETER, 0);
|
||||
busy_wait(base);
|
||||
}
|
||||
|
||||
|
||||
struct m_n {
|
||||
uint8_t m;
|
||||
uint8_t n;
|
||||
};
|
||||
|
||||
static inline struct m_n dac_read_16(volatile uint32_t * base, int param_address)
|
||||
{
|
||||
dac_data(base, param_address, DAC__RS__PLL_ADDRESS_READ, 0);
|
||||
busy_wait(base);
|
||||
|
||||
dac_data(base, 0, DAC__RS__PLL_PARAMETER, 1);
|
||||
busy_wait(base);
|
||||
uint32_t m = base[FBIINIT2 / 4] & 0xff;
|
||||
|
||||
dac_data(base, 0, DAC__RS__PLL_PARAMETER, 1);
|
||||
busy_wait(base);
|
||||
uint32_t n = base[FBIINIT2 / 4] & 0xff;
|
||||
|
||||
return (struct m_n){ m, n };
|
||||
}
|
||||
|
||||
/* This function is called by the kernel */
|
||||
static int voodoo2_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||
{
|
||||
int bar, err;
|
||||
u16 vendor, device;
|
||||
unsigned long mmio_start, mmio_len;
|
||||
struct voodoo2_priv *drv_priv;
|
||||
|
||||
/* Let's read data from the PCI device configuration registers */
|
||||
pci_read_config_word(pdev, PCI_VENDOR_ID, &vendor);
|
||||
pci_read_config_word(pdev, PCI_DEVICE_ID, &device);
|
||||
|
||||
printk(KERN_INFO "vid: %04x pid: %04x\n", vendor, device);
|
||||
|
||||
uint32_t init_enable;
|
||||
init_enable = 0
|
||||
| (1 << 2) // enable writes to hardware initialization registers
|
||||
| (1 << 0); // remap fbiinit2, fbiinit3 to dacread, videochecksum
|
||||
pci_write_config_dword(pdev, 0x40, init_enable);
|
||||
pci_read_config_dword(pdev, 0x40, &init_enable);
|
||||
printk(KERN_INFO "init_enable: %08x\n", init_enable);
|
||||
|
||||
/* Request IO BAR */
|
||||
bar = pci_select_bars(pdev, IORESOURCE_MEM);
|
||||
|
||||
/* Enable device memory */
|
||||
err = pci_enable_device_mem(pdev);
|
||||
if (err) {
|
||||
printk(KERN_INFO "pci_enable_device_mem error\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Request memory region for the BAR */
|
||||
err = pci_request_region(pdev, bar, VOODOO2);
|
||||
if (err) {
|
||||
printk(KERN_INFO "pci_request_region error\n");
|
||||
pci_disable_device(pdev);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Get start and stop memory offsets */
|
||||
mmio_start = pci_resource_start(pdev, 0);
|
||||
mmio_len = pci_resource_len(pdev, 0);
|
||||
printk(KERN_INFO "mmio_start %p mmio_len %p\n", (void*)mmio_start, (void*)mmio_len);
|
||||
|
||||
/* Allocate memory for the module private data */
|
||||
drv_priv = kzalloc(sizeof(struct voodoo2_priv), GFP_KERNEL);
|
||||
|
||||
if (!drv_priv) {
|
||||
release_device(pdev);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Remap BAR to the local pointer */
|
||||
drv_priv->hwmem = ioremap(mmio_start, mmio_len);
|
||||
if (!drv_priv->hwmem) {
|
||||
release_device(pdev);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Set module private data */
|
||||
/* Now we can access mapped "hwmem" from the any module's function */
|
||||
pci_set_drvdata(pdev, drv_priv);
|
||||
|
||||
for (int i = 0; i < (sizeof (registers)) / (sizeof (registers[0])); i++) {
|
||||
printk(KERN_INFO "%s %08x\n",
|
||||
registers[i].name,
|
||||
drv_priv->hwmem[registers[i].offset / 4]);
|
||||
}
|
||||
|
||||
//return 0;
|
||||
|
||||
init_enable = 0
|
||||
| (1 << 0); // enable writes to hardware initialization registers
|
||||
pci_write_config_dword(pdev, 0x40, init_enable);
|
||||
busy_wait(drv_priv->hwmem);
|
||||
|
||||
drv_priv->hwmem[FBIINIT1 / 4] = (1 << 8); // 8: Video Timing Reset (0=run, 1=reset).
|
||||
busy_wait(drv_priv->hwmem);
|
||||
drv_priv->hwmem[FBIINIT0 / 4] = (1 << 1) // 1: Chuck Graphics Reset
|
||||
| (1 << 2); // 2: Chuck FIFO Reset
|
||||
busy_wait(drv_priv->hwmem);
|
||||
|
||||
drv_priv->hwmem[FBIINIT2 / 4] &= (1 << 22); // 22: dram refresh disable
|
||||
busy_wait(drv_priv->hwmem);
|
||||
|
||||
init_enable = 0
|
||||
| (1 << 2) // remap fbiinit2, fbiinit3 to dacread, videochecksum
|
||||
| (1 << 0); // enable writes to hardware initialization registers
|
||||
pci_write_config_dword(pdev, 0x40, init_enable);
|
||||
busy_wait(drv_priv->hwmem);
|
||||
|
||||
for (int i = 0; i < (sizeof (dac_registers)) / (sizeof (dac_registers[0])); i++) {
|
||||
struct m_n res = dac_read_16(drv_priv->hwmem, dac_registers[i].offset);
|
||||
printk(KERN_INFO "dac read1: %s: m: %02x n: %02x\n", dac_registers[i].name, res.m, res.n);
|
||||
}
|
||||
|
||||
/* pixel clock: 40MHz
|
||||
graphics/memory clock: 44.6MHz
|
||||
*/
|
||||
dac_data(drv_priv->hwmem, (0b0101 << 4), DAC__RS__COMMAND, 0); //write
|
||||
busy_wait(drv_priv->hwmem);
|
||||
dac_data(drv_priv->hwmem, 0, DAC__RS__COMMAND, 1); //read
|
||||
busy_wait(drv_priv->hwmem);
|
||||
uint32_t command = drv_priv->hwmem[FBIINIT2 / 4] & 0xff;
|
||||
printk(KERN_INFO "dac read2: RS__COMMAND: m: %02x\n", command);
|
||||
|
||||
int m = 54;
|
||||
int n = 67;
|
||||
dac_write_16(drv_priv->hwmem, CLK0_f0_PLL, m, n);
|
||||
struct m_n res0 = dac_read_16(drv_priv->hwmem, CLK0_f0_PLL);
|
||||
printk(KERN_INFO "dac read2: CLK0_f0_PLL: m: %02x n: %02x\n", res0.m, res0.n);
|
||||
|
||||
uint8_t pll_control = 0
|
||||
| (0 << 0) // internal select: f0
|
||||
| (0 << 4) // clk1 sel: fA
|
||||
| (1 << 5); // enable incs: 1
|
||||
dac_write_8(drv_priv->hwmem, PLL_CONTROL, pll_control);
|
||||
struct m_n res = dac_read_16(drv_priv->hwmem, PLL_CONTROL);
|
||||
printk(KERN_INFO "dac read2: PLL_CONTROL: m: %02x n: %02x\n", res.m, res.n);
|
||||
|
||||
/*
|
||||
fbiInit0 00000410
|
||||
4: Stall PCI enable for High Water Mark
|
||||
6: (0x10) PCI FIFO Empty Entries Low Water Mark
|
||||
fbiInit1 00201102
|
||||
1: One wait state
|
||||
8: Video Timing Reset
|
||||
12: Software Blanking Enable
|
||||
20: (0x2) vid_clk_2x_sel
|
||||
fbiInit2 80000002
|
||||
1: DRAM banking configuration (0=128Kx16 banking, 1=256Kx16 banking)
|
||||
23: 0x100 Refresh_Load value
|
||||
fbiInit3 00000000
|
||||
fbiInit4 00000001
|
||||
0: Wait state cycles for PCI read accesses (0=1 ws, 1=2 ws).
|
||||
fbiInit5 00000128
|
||||
|
||||
fbiInit6 001e6000
|
||||
fbiInit7 000000aa
|
||||
*/
|
||||
|
||||
int x_tiles = 832 / 32;
|
||||
|
||||
// fbinit0
|
||||
int fbiinit0 = 0
|
||||
| (1 << 0) // no VGA passthrough
|
||||
| (0 << 1) // no chuck graphics reset
|
||||
| (0 << 2) // no chuck fifo reset
|
||||
| (0 << 3) // no byte swizzle
|
||||
| (1 << 4) // Stall PCI enable for High Water Mark
|
||||
| (0x10 << 6) // PCI FIFO Empty Entries Low Water Mark
|
||||
;
|
||||
|
||||
// fbiinit1
|
||||
int fbiinit1 = 0
|
||||
//| (1 << 1) // one wait state
|
||||
| (1 << 3) // enable linear framebuffer reads
|
||||
| (((x_tiles >> 1) & 0b1111) << 4) // tiles x [4:1]
|
||||
| (1 << 13) // drive data output
|
||||
| (1 << 14) // drive blank output
|
||||
| (1 << 15) // drive hsync/vsync output
|
||||
| (1 << 16) // drive dclk output
|
||||
| (0 << 17) // Video timing vclk input select : vid_clk_2x
|
||||
| (0x2 << 20) // Video timing vclk source select : vid_clk_2x
|
||||
| (((x_tiles >> 5) & 0b1) << 24) // tiles x [5]
|
||||
;
|
||||
|
||||
int fbiinit2 = 0
|
||||
| (1 << 1) // DRAM banking configuration (0=128Kx16 banking, 1=256Kx16 banking)
|
||||
| (1 << 5) // fast RAS
|
||||
| (1 << 6) // DRAM_OE
|
||||
| (247 << 11) // video buffer offset
|
||||
| (1 << 21) // FIFO RDA
|
||||
| (1 << 22) // Refresh Enable (0=disable, 1=enable). Default is 0.
|
||||
| (0x30 << 23) // Refresh_Load Value.
|
||||
;
|
||||
|
||||
int fbiinit3 = 0
|
||||
| (1 << 6) // trex dis
|
||||
;
|
||||
|
||||
int fbiinit4 = 0
|
||||
| (1 << 1) // lfb rda
|
||||
;
|
||||
|
||||
int fbiinit5 = 0
|
||||
| 0x00000128
|
||||
| (1 << 23)
|
||||
| (1 << 24)
|
||||
;
|
||||
|
||||
int fbiinit6 = 0
|
||||
| (x_tiles & 1) << 30; // tiles x [0]
|
||||
|
||||
int yHeight = 600;
|
||||
int xWidth = 800;
|
||||
|
||||
// sc->sc_videomode->vtotal - sc->sc_videomode->vsync_end
|
||||
int vBackPorch = 628 - 605;
|
||||
// sc->sc_videomode->htotal - sc->sc_videomode->hsync_end
|
||||
int hBackPorch = 1056 - 968;
|
||||
|
||||
// sc->sc_videomode->vsync_end - sc->sc_videomode->vsync_start
|
||||
int vSyncOn = 605 - 601;
|
||||
// sc->sc_videomode->hsync_end - sc->sc_videomode->hsync_start
|
||||
int hSyncOn = 968 - 840;
|
||||
|
||||
// sc->sc_videomode->vtotal - vsyncon
|
||||
int vSyncOff = 628 - vSyncOn;
|
||||
// sc->sc_videomode->htotal - hsyncon
|
||||
int hSyncOff = 1056 - hSyncOn;
|
||||
|
||||
int hSync = 0
|
||||
| (((hSyncOn - 1) & 0x1ff) << 0)
|
||||
| ((hSyncOff & 0x3ff) << 16)
|
||||
;
|
||||
|
||||
int vSync = 0
|
||||
| ((vSyncOn & 0x1fff) << 0)
|
||||
| ((vSyncOff & 0x1fff) << 16)
|
||||
;
|
||||
|
||||
int backPorch = 0
|
||||
| (((hBackPorch - 2) & 0x1ff) << 0)
|
||||
| ((vBackPorch & 0x1ff) << 16)
|
||||
;
|
||||
|
||||
int videoDimensions = 0
|
||||
| (((xWidth - 1) & 0x7ff) << 0)
|
||||
| ((yHeight & 0x7ff) << 16)
|
||||
;
|
||||
|
||||
drv_priv->hwmem[HSYNC / 4] = hSync;
|
||||
busy_wait(drv_priv->hwmem);
|
||||
drv_priv->hwmem[VSYNC / 4] = vSync;
|
||||
busy_wait(drv_priv->hwmem);
|
||||
drv_priv->hwmem[BACKPORCH / 4] = backPorch;
|
||||
busy_wait(drv_priv->hwmem);
|
||||
drv_priv->hwmem[VIDEODIMENSIONS / 4] = videoDimensions;
|
||||
busy_wait(drv_priv->hwmem);
|
||||
|
||||
int lfbMode = 0;
|
||||
|
||||
drv_priv->hwmem[LFBMODE / 4] = lfbMode;
|
||||
busy_wait(drv_priv->hwmem);
|
||||
|
||||
init_enable = 0
|
||||
| (1 << 0); // enable writes to hardware initialization registers
|
||||
pci_write_config_dword(pdev, 0x40, init_enable);
|
||||
busy_wait(drv_priv->hwmem);
|
||||
|
||||
drv_priv->hwmem[FBIINIT0 / 4] = fbiinit0;
|
||||
drv_priv->hwmem[FBIINIT1 / 4] = fbiinit1;
|
||||
drv_priv->hwmem[FBIINIT2 / 4] = fbiinit2;
|
||||
drv_priv->hwmem[FBIINIT3 / 4] = fbiinit3;
|
||||
drv_priv->hwmem[FBIINIT4 / 4] = fbiinit4;
|
||||
drv_priv->hwmem[FBIINIT5 / 4] = fbiinit5;
|
||||
drv_priv->hwmem[FBIINIT6 / 4] = fbiinit6;
|
||||
busy_wait(drv_priv->hwmem);
|
||||
|
||||
init_enable = 0
|
||||
| (1 << 1); // Enable writes to PCI FIFO
|
||||
pci_write_config_dword(pdev, 0x40, init_enable);
|
||||
busy_wait(drv_priv->hwmem);
|
||||
pci_write_config_dword(pdev, 0xC0, 0); // VCLK_ENABLE_REG
|
||||
|
||||
for (int i = 0; i < (sizeof (registers)) / (sizeof (registers[0])); i++) {
|
||||
printk(KERN_INFO "read2: %s %08x\n",
|
||||
registers[i].name,
|
||||
drv_priv->hwmem[registers[i].offset / 4]);
|
||||
}
|
||||
printk(KERN_INFO "hsync: %08x vsync: %08x\n", hSync, vSync);
|
||||
|
||||
#define rgb(r, g, b) (((r) << 11) | ((g) << 5) | ((b) << 0))
|
||||
static const uint16_t panda[] __attribute__((aligned(4))) = {
|
||||
//#embed "panda.rgb565.txt"
|
||||
#include "panda.txt"
|
||||
};
|
||||
|
||||
const int colors[] = {
|
||||
rgb(31, 0, 0),
|
||||
rgb(0, 63, 0),
|
||||
rgb(0, 0, 31),
|
||||
rgb(31, 63, 0), // yellow
|
||||
rgb(31, 0, 31), // magenta
|
||||
rgb(0, 63, 31), // cyan
|
||||
rgb(31, 31, 0), // orange
|
||||
rgb(31, 0, 15), // pink
|
||||
rgb(0, 31, 31), // light blue
|
||||
rgb(0, 63, 15), // light green
|
||||
};
|
||||
|
||||
if (0) {
|
||||
for (int y = 0; y < 600; y++) {
|
||||
for (int x = 0; x < 1024; x++) {
|
||||
((volatile uint16_t *)drv_priv->hwmem)[(0x0400000 / 2) + y * 1024 + x] = 0
|
||||
| colors[(y / 32) % 10];
|
||||
|
||||
if (0) {
|
||||
((volatile uint16_t *)drv_priv->hwmem)[(0x0400000 / 2) + y * 1024 + x] = 0
|
||||
| rgb(0, 63, 0);
|
||||
}
|
||||
}
|
||||
busy_wait(drv_priv->hwmem);
|
||||
}
|
||||
for (int y = 0; y < 600; y++) {
|
||||
((volatile uint16_t *)drv_priv->hwmem)[(0x0400000 / 2) + y * 1024 + 10] = 0
|
||||
| rgb(0, 0, 31);
|
||||
((volatile uint16_t *)drv_priv->hwmem)[(0x0400000 / 2) + y * 1024 + 790] = 0
|
||||
| rgb(0, 63, 0);
|
||||
}
|
||||
}
|
||||
|
||||
for (int y = 0; y < 600; y++) {
|
||||
for (int x = 0; x < 800; x++) {
|
||||
((volatile uint16_t *)drv_priv->hwmem)[(0x0400000 / 2) + y * 1024 + x] = ((uint16_t*)panda)[y * 800 + x];
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Clean up */
|
||||
static void voodoo2_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct voodoo2_priv *drv_priv = pci_get_drvdata(pdev);
|
||||
|
||||
if (drv_priv) {
|
||||
if (drv_priv->hwmem) {
|
||||
iounmap(drv_priv->hwmem);
|
||||
}
|
||||
|
||||
kfree(drv_priv);
|
||||
}
|
||||
|
||||
release_device(pdev);
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Zachary Buhman <zack@buhman.org>");
|
||||
MODULE_DESCRIPTION("Voodoo2 module");
|
||||
MODULE_VERSION("0.1");
|
||||
|
||||
module_init(voodoo2_module_init);
|
||||
module_exit(voodoo2_module_exit);
|
46
pci/mode.c
Normal file
46
pci/mode.c
Normal file
@ -0,0 +1,46 @@
|
||||
int dot_clock; /* Dot clock frequency in kHz. */
|
||||
int hdisplay; // hr
|
||||
int hsync_start; // hs
|
||||
int hsync_end; // he
|
||||
int htotal; // ht
|
||||
int vdisplay; // vr
|
||||
int vsync_start; // vs
|
||||
int vsync_end; // ve
|
||||
int vtotal; // vt
|
||||
int flags; // f /* Video mode flags; see below. */
|
||||
const char *name;
|
||||
|
||||
// name hr vr clk hs he ht vs ve vt f
|
||||
// M("800x600x60",800,600,40000,840,968,1056,601,605,628,HP|VP),
|
||||
|
||||
/* dac write */
|
||||
// 0x04 0x0a
|
||||
//tdvfb_cvg_dac_write(sc, TDV_GENDAC_PLLWR, TDV_GENDAC_PLL_0);
|
||||
|
||||
{
|
||||
// TDV_GENDAC_ADDRMASK 0x07
|
||||
wreg = ((reg & TDV_GENDAC_ADDRMASK) << 8) | val;
|
||||
// TDV_OFF_DAC_DATA 0x22c
|
||||
tdvfb_cvg_write(sc, TDV_OFF_DAC_DATA, wreg);
|
||||
tdvfb_wait(sc);
|
||||
}
|
||||
|
||||
/* dac read */
|
||||
{
|
||||
// reg = TDV_GENDAC_PLLWR 0x4 "PLL Address (write mode)"
|
||||
// reg = TDV_GENDAC_PLLDATA 0x5 "PLL Parameter"
|
||||
// reg = TDV_GENDAC_CMD 0x6 "Command"
|
||||
// reg = TDV_GENDAC_PLLRD 0x7 "PLL Address (read mode)"
|
||||
// TDV_DAC_DATA_READ (1 << 11) 0x800
|
||||
tdvfb_cvg_dac_write(sc, reg, TDV_DAC_DATA_READ);
|
||||
|
||||
/* When fbiinit2/fbiinit3 address remapping is enabled (PCI Configuration
|
||||
register initEnable bit(2)=1), reading from fbiinit2 bits (7:0) returns the last
|
||||
value read from the external DAC (fbiinit2 bits(31:8) are undefined when address
|
||||
remapping is enabled).
|
||||
*/
|
||||
|
||||
// TDV_OFF_DAC_READ 0x0218 fbiInit2
|
||||
rv = tdvfb_cvg_read(sc, TDV_OFF_DAC_READ);
|
||||
return rv & 0xFF;
|
||||
}
|
14
pci/panda.py
Normal file
14
pci/panda.py
Normal file
@ -0,0 +1,14 @@
|
||||
import sys
|
||||
import struct
|
||||
|
||||
with open(sys.argv[1], 'rb') as f:
|
||||
buf = f.read()
|
||||
|
||||
mem = memoryview(buf)
|
||||
|
||||
for i in range(len(mem) // 2):
|
||||
n, = struct.unpack("<H", mem[i * 2:i*2+2])
|
||||
print(n, end=", ")
|
||||
if i % 16 == 15:
|
||||
print()
|
||||
|
BIN
pci/panda.rgb565
Normal file
BIN
pci/panda.rgb565
Normal file
Binary file not shown.
30000
pci/panda.txt
Normal file
30000
pci/panda.txt
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user