Created
September 17, 2016 21:24
-
-
Save rkujawa/2bf31e5cbe261a9df5011985c35641e3 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Preliminary driver for an Indivision ECS graphics core. | |
* | |
* The amiga bus_space(9) implementation does not have bus_space_poke/peek | |
* functions implemented. These would be useful, as there's no way to detect | |
* Indivision ECS without writing to bus space. | |
* | |
* TODO: | |
* - everything | |
* - man page | |
* | |
* TODO for second revision of this driver: | |
* - add secondary Indivision ECS support | |
* - add support for resolutions other than 1024x768 | |
* - add 15/16 bit modes support | |
* - introduce non-naive algorithm for SDRAM updates (current | |
* performance sucks) | |
* - create user-space configuration utility | |
* - mmap'ing frame buffer if possible at all | |
*/ | |
#include <sys/cdefs.h> | |
#include <sys/param.h> | |
#include <sys/systm.h> | |
#include <sys/kernel.h> | |
#include <sys/device.h> | |
#include <sys/proc.h> | |
#include <sys/mutex.h> | |
#include <sys/ioctl.h> | |
#include <sys/kernel.h> | |
#include <sys/systm.h> | |
#include <sys/kauth.h> | |
#include <sys/kmem.h> | |
#include <machine/bus.h> | |
#include <machine/cpu.h> | |
#include <amiga/amiga/cc_registers.h> | |
#include <amiga/amiga/custom.h> | |
#include <amiga/amiga/device.h> | |
#include <amiga/dev/zbusvar.h> | |
#include <dev/wscons/wsdisplayvar.h> | |
#include <dev/wscons/wsconsio.h> | |
#include <dev/wsfont/wsfont.h> | |
#include <dev/rasops/rasops.h> | |
#include <dev/wscons/wsdisplay_vconsvar.h> | |
#include "opt_wsfb.h" | |
#include "opt_wsdisplay_compat.h" | |
#define DEBUG | |
#define GFXCORE_LOAD_SIMPLE 0 | |
#define GFXCORE_LOAD_ADOOMSTYLE 1 | |
#define IVECS_FPGA_BASE 0xdff0ac | |
#define IVECS_FPGA_REGSEL 0x0 /* select FPGA register */ | |
#define IVECS_FPGA_VAL 0x4 /* 0x4 * 4 = offset 0x10, FPGA register value */ | |
#define IVECS_CPLD_BASE 0xdff1f0 | |
/* CPLD registers */ | |
#define CPLD_REG0_UNLOCK1 0x5000 /* ?? unlock CPLD */ | |
#define CPLD_REG0_UNLOCK2 0xB000 /* ?? unlock CPLD continued */ | |
#define CPLD_REG0_LOCK 0xF000 /* ?? lock CPLD */ | |
#define CPLD_REG1_GFXCORE 0x03C0 /* core 3C0 == Indivision GFX */ | |
#define CPLD_REG1_SDCORE 0xE400 /* ?? scandoubler mode */ | |
#define CPLD_REG0_DSFLASH 0x0 /* ?? deselect flash */ | |
#define CPLD_REG0_RSTFLASH 0x2000 /* ?? reset flash */ | |
#define CPLD_REG1_RSTFPGA 0x0100 /* ?? reset FPGA */ | |
/* FPGA registers */ | |
#define FPGA_REG_LOCK 0x00 /* FPGA reg 00: lock/unlock */ | |
#define UNLOCK 0xFA50 | |
#define LOCK 0x00 | |
#define FPGA_REG_UNLOAD 0x03 /* FPGA reg 03: disable GFX core */ | |
#define UNLOAD 0x01 | |
#define FPGA_REG_STATUS 0x07 /* FPGA reg 07: status */ | |
#define STATUS_GFXCORE 0x3000 | |
#define FPGA_REG_PIX_CLOCK 0xC0 /* FPGA reg C0: pixel clock */ | |
struct ivecs_modesetup { /* FPGA registers C0 - C2 */ | |
uint16_t pix_clock; | |
#define PIXDIV1 0 | |
uint16_t pix_cmode; | |
#define CMODE_CLUT 0 | |
uint16_t clut_mode; | |
#define CLUT_MODE_16RGB 2 | |
}; | |
struct ivecs_display { /* FPGA registers D0 - DA */ | |
uint16_t dcr; | |
uint16_t htotal; | |
uint16_t vtotal; | |
uint16_t hsync_start; | |
uint16_t hsync_stop; | |
uint16_t vsync_start; | |
uint16_t vsync_stop; | |
uint16_t screen_hstart; | |
uint16_t screen_width; | |
uint16_t screen_vstart; | |
uint16_t screen_height; | |
}; | |
struct ivecs_mode { | |
struct ivecs_modesetup modesetup; | |
struct ivecs_display display; | |
}; | |
struct ivecs_softc { | |
device_t ivecs_dev; | |
struct bus_space_tag cpld_bst; | |
bus_space_tag_t cpld_iot; | |
bus_space_handle_t cpld_ioh; | |
struct bus_space_tag fpga_bst; | |
bus_space_tag_t fpga_iot; | |
bus_space_handle_t fpga_ioh; | |
uint16_t hw_cmap[256]; | |
#define HWCMAP_ELEMENTS 256 | |
void (*copycols)(void *, int, int, int, int); | |
void (*erasecols)(void *, int, int, int, long); | |
void (*copyrows)(void *, int, int, int); | |
void (*eraserows)(void *, int, int, long); | |
void (*putchar)(void *, int, int, u_int, long); | |
void (*cursor)(void *, int, int, int); | |
}; | |
struct wsdisplay_accessops ivecs_accessops = { | |
ivecs_ioctl, | |
ivecs_mmap, | |
NULL, /* alloc_screen */ | |
NULL, /* free_screen */ | |
NULL, /* show_screen */ | |
NULL, /* load_font */ | |
NULL, /* pollc */ | |
NULL /* scroll */ | |
}; | |
/* basic driver functions */ | |
static int ivecs_match(device_t, cfdata_t, void *); | |
static void ivecs_attach(device_t, device_t, void *); | |
static int ivecs_ioctl(void *, void *, u_long, void *, int, | |
struct lwp *); | |
static paddr_t ivecs_mmap(void *, void *, off_t, int); | |
/* internal functions */ | |
static int ivecs_init_display(struct ivecs_softc *sc); | |
static int ivecs_load_gfxcore(struct ivecs_softc *sc); | |
static uint16_t ivecs_fpga_readreg(struct ivecs_softc *sc, uint16_t fpgareg); | |
static void ivecs_fpga_writereg(struct ivecs_softc *sc, uint16_t fpgareg, uint16_t value); | |
static void ivecs_cpld_writereg(struct ivecs_softc *sc, size_t offset, uint16_t val); | |
static void ivecs_delay(unsigned int us); | |
#ifdef DEBUG | |
static void ivecs_dumpstate(void); | |
#endif /* DEBUG */ | |
/* wsdisplay functions */ | |
/* | |
static void ivecs_putchar(void *, int, int, u_int, long); | |
static void ivecs_cursor(void *, int, int, int); | |
static void ivecs_copycols(void *, int, int, int, int); | |
static void ivecs_erasecols(void *, int, int, int, long); | |
static void ivecs_copyrows(void *, int, int, int); | |
static void ivecs_eraserows(void *, int, int, long); | |
*/ | |
CFATTACH_DECL_NEW(ivecs, sizeof(struct ivecs_softc), | |
ivecs_match, ivecs_attach, NULL, NULL); | |
static int | |
ivecs_match(device_t parent, cfdata_t match, void *aux) | |
{ | |
//uint16_t *fpgareg_p, *fpgaval_p, *cpld_p; | |
static int pri_present = 0; | |
if(!matchname("ivecs", aux)) | |
return(0); | |
/* check for any default register values if possible */ | |
//aprint_normal("0xdff0ac: %x 0xdff0bc: %x 0xdff1f0: %x \n", | |
// *fpgareg_p, *fpgaval_p, *cpld_p); | |
pri_present = 1; | |
return 1; | |
/* default should be | |
return 0; */ | |
} | |
static void | |
ivecs_attach(device_t parent, device_t self, void *aux) | |
{ | |
struct ivecs_softc *sc; | |
sc = device_private(self); | |
sc->cpld_bst.base = (u_long) ztwomap(IVECS_CPLD_BASE); | |
sc->cpld_bst.absm = &amiga_bus_stride_4; | |
sc->fpga_bst.base = (u_long) ztwomap(IVECS_FPGA_BASE); | |
sc->fpga_bst.absm = &amiga_bus_stride_4; | |
#ifdef DEBUG | |
printf("ivecs attach: cpld va = %x, fpga va = %x\n", | |
sc->cpld_bst.base, sc->fpga_bst.base); | |
#endif /* DEBUG */ | |
sc->cpld_iot = &sc->cpld_bst; | |
sc->fpga_iot = &sc->fpga_bst; | |
if(bus_space_map(sc->cpld_iot, 0, 3, 0, | |
&sc->cpld_ioh)) | |
{ | |
aprint_error_dev(self, "couldn't map CPLD registers\n"); | |
return; | |
} | |
if(bus_space_map(sc->fpga_iot, 0, 4, 0, | |
&sc->fpga_ioh)) | |
{ | |
aprint_error_dev(self, "couldn't map FPGA registers\n"); | |
return; | |
} | |
if(!ivecs_load_gfxcore(sc)) | |
{ | |
aprint_error_dev(self, "couldn't load graphics core\n"); | |
return; | |
} | |
if(!ivecs_init_display(sc)) | |
{ | |
aprint_error_dev(self, "couldn't initialize display\n"); | |
return; | |
} | |
/* insert wsdisplay glue here */ | |
aprint_normal("\n"); | |
} | |
/* Initialize graphics core, based on ADoom source */ | |
static void | |
ivecs_load_gfxcore_adoom(struct ivecs_softc *sc) | |
{ | |
uint16_t tmp; int i; | |
volatile uint16_t *color0; | |
core = 0; | |
color0 = (volatile uint16_t *)ztwomap(CUSTOMBASE + R_COLOR00);*/ | |
/* unlock CPLD */ | |
ivecs_cpld_writereg(sc, 0, CPLD_REG0_UNLOCK1); | |
ivecs_cpld_writereg(sc, 0, CPLD_REG0_UNLOCK2); | |
tmp = *color0; /* delay 1 CCK */ | |
ivecs_cpld_writereg(sc, 0, CPLD_REG0_DSFLASH); | |
ivecs_cpld_writereg(sc, 0, CPLD_REG0_RSTFLASH); | |
ivecs_cpld_writereg(sc, 1, CPLD_REG1_RSTFPGA); | |
/* delay minimum 4096 CCK */ | |
for(i = 0; i < 4096; i++) { | |
tmp = *color0; | |
} | |
/* load graphics core */ | |
ivecs_cpld_writereg(sc, 1, CPLD_REG1_GFXCORE); | |
for(i = 0; i < 16384; i++) | |
{ | |
if(i%2) | |
*color0 = 0xA70; | |
else | |
*color0 = 0xA50; | |
if( (bus_space_read_2(sc->cpld_iot, sc->cpld_ioh, 2)) == 0x4000) | |
{ | |
aprint_normal(": breaking news\n"); | |
break; | |
} | |
} | |
ivecs_delay(3000000); | |
core = bus_space_read_2(sc->cpld_iot, sc->cpld_ioh, 2); | |
aprint_normal(": FPGA config %x\n", core); | |
ivecs_cpld_writereg(sc, 0, CPLD_REG0_DSFLASH); | |
ivecs_cpld_writereg(sc, 0, CPLD_REG0_LOCK); | |
ivecs_delay(3000000); | |
ivecs_delay(3000000); | |
} | |
/* Initialize graphics core, based on official documentation */ | |
static void | |
ivecs_load_gfxcore_simple(struct ivecs_softc *sc) | |
{ | |
/* unlock CPLD */ | |
ivecs_cpld_writereg(sc, 0, CPLD_REG0_UNLOCK1); | |
ivecs_cpld_writereg(sc, 0, CPLD_REG0_UNLOCK2); | |
/* load graphics core */ | |
ivecs_cpld_writereg(sc, 1, CPLD_REG1_GFXCORE); | |
/* lock CPLD */ | |
ivecs_cpld_writereg(sc, 0, CPLD_REG0_LOCK); | |
ivecs_delay(3000000); | |
} | |
/* Switch FPGA to gfx core and check if it's loaded */ | |
static int | |
ivecs_load_gfxcore(struct ivecs_softc *sc) | |
{ | |
uint16_t core; | |
int loadmethod = GFXCORE_LOAD_SIMPLE; /* XXX: make this selectable */ | |
if(loadmethod == GFXCORE_LOAD_SIMPLE) | |
ivecs_load_gfxcore_simple(); | |
else | |
ivecs_load_gfxcore_adoom(); | |
/* Check core ID */ | |
ivecs_fpga_writereg(sc, FPGA_REG_LOCK, UNLOCK); | |
core = ivecs_fpga_readreg(sc, FPGA_REG_STATUS); | |
ivecs_delay(3000000); | |
core = ivecs_fpga_readreg(sc, FPGA_REG_STATUS); | |
ivecs_delay(3000000); | |
core = ivecs_fpga_readreg(sc, FPGA_REG_STATUS); | |
ivecs_fpga_writereg(sc, FPGA_REG_LOCK, LOCK); | |
ivecs_delay(3000000); | |
core = ivecs_fpga_readreg(sc, FPGA_REG_STATUS); | |
if(core != STATUS_GFXCORE) { | |
aprint_error("couldn't switch to graphics core (status reg = %x)\n", core); | |
ivecs_fpga_writereg(sc, FPGA_REG_LOCK, LOCK); | |
ivecs_unload_gfxcore(); /* be sure that we are not left with black screen */ | |
return 0; | |
} | |
ivecs_fpga_writereg(sc, FPGA_REG_LOCK, LOCK); | |
return 1; | |
} | |
/* Unload graphics core from FPGA, ECS display should be back */ | |
ivecs_unload_gfxcore(struct ivecs_softc *sc) { | |
#ifdef DEBUG | |
printf("ivecs_unload_gfxcore: unloading...\n"); | |
#endif DEBUG | |
ivecs_fpga_writereg(sc, FPGA_REG_LOCK, UNLOCK); | |
ivecs_fpga_writereg(sc, FPGA_REG_UNLOAD, UNLOAD); | |
ivecs_delay(3000000); | |
ivecs_cpld_writereg(sc, 0, CPLD_REG0_UNLOCK); | |
ivecs_cpld_writereg(sc, 0, CPLD_REG0_UNLOCK2); | |
ivecs_cpld_writereg(sc, 1, CPLD_REG1_SDCORE); | |
ivecs_cpld_writereg(sc, 0, CPLD_REG0_LOCK); | |
} | |
static void | |
ivecs_delay(unsigned int us) { | |
extern uint32_t delaydivisor; | |
#ifdef DEBUG | |
printf("ivecs_delay: delay for %d us (%d seconds) (delaydivisor %d)\n", | |
us, us/1000000, delaydivisor); | |
#endif /* DEBUG */ | |
DELAY(us); | |
} | |
/* Initialize display */ | |
static int | |
ivecs_init_display(struct ivecs_softc *sc) | |
{ | |
int i; | |
struct ivecs_modesetup *msp; | |
struct ivecs_display *dp; | |
/* let's hardcode 1024x768 8-bit for now */ | |
struct ivecs_mode m = | |
{ { PIXDIV1, CMODE_CLUT, CLUT_MODE_16RGB }, | |
{ 0x0001, 0x0530, 0x0326, 0x0018, 0x00a0, 0x0003, | |
0x0009, 0x012e, 0x03ff, 0x0025, 0x02ff } }; | |
msp = &m.modesetup; | |
dp = &m.display; | |
/* write raw data from structs to FPGA regs */ | |
for(i=0; i<3; i++) { | |
bus_space_write_2(sc->fpga_iot, sc->fpga_ioh, | |
IVECS_FPGA_REGSEL, FPGA_REG_PIX_CLOCK+i); | |
/* bus_space_write_2(sc->fpga_iot, sc->fpga_ioh, | |
IVECS_FPGA_VAL, ??? );*/ | |
} | |
/* for ... dp */ | |
return 1; | |
} | |
/* write CPLD register */ | |
static void | |
ivecs_cpld_writereg(struct ivecs_softc *sc, size_t offset, uint16_t val) { | |
bus_space_write_2(sc->cpld_iot, sc->cpld_ioh, offset, val); | |
#ifdef DEBUG | |
printf("ivecs_cpld_writereg: write value %x at pa %x\n", | |
val, IVECS_CPLD_BASE+(offset*4) ); | |
#endif /* DEBUG */ | |
} | |
/* read FPGA register */ | |
static uint16_t | |
ivecs_fpga_readreg(struct ivecs_softc *sc, uint16_t fpgareg) { | |
uint16_t tmpread; | |
bus_space_write_2(sc->fpga_iot, sc->fpga_ioh, IVECS_FPGA_REGSEL, | |
fpgareg); | |
tmpread = bus_space_read_2(sc->fpga_iot, sc->fpga_ioh, | |
IVECS_FPGA_VAL); | |
#ifdef DEBUG | |
printf("ivecs_fpga_readreg: select reg %x at pa %x\n", fpgareg, | |
IVECS_FPGA_BASE+(IVECS_FPGA_REGSEL*4) ); | |
printf("ivecs_fpga_readreg: read reg %x at pa %x got %x\n", fpgareg, | |
IVECS_FPGA_BASE+(IVECS_FPGA_VAL*4), tmpread); | |
#endif /* DEBUG */ | |
return tmpread; | |
} | |
/* write FPGA register */ | |
static void | |
ivecs_fpga_writereg(struct ivecs_softc *sc, uint16_t fpgareg, uint16_t value) { | |
bus_space_write_2(sc->fpga_iot, sc->fpga_ioh, IVECS_FPGA_REGSEL, | |
fpgareg); | |
bus_space_write_2(sc->fpga_iot, sc->fpga_ioh, IVECS_FPGA_VAL, value); | |
#ifdef DEBUG | |
printf("ivecs_fpga_writereg: select reg %x at pa %x\n", fpgareg, | |
IVECS_FPGA_BASE+(IVECS_FPGA_REGSEL*4) ); | |
printf("ivecs_fpga_writereg: write value %x into reg %x at pa %x \n", | |
value, fpgareg, IVECS_FPGA_BASE+(IVECS_FPGA_VAL*4)); | |
#endif /* DEBUG */ | |
} | |
/* write Amiga custom chip register | |
static void | |
ivecs_cc_writereg(struct ivecs_softc *sc, size_t offset, uint16_t val) { | |
bus_space_write_2(sc->cc_iot, sc->cc_ioh, offset, val); | |
#ifdef DEBUG | |
printf("ivecs_cc_writereg: write value %x at pa %x (va %lx)\n", | |
val, CC_BASE+(offset*1), sc->cc_ioh); | |
#endif | |
} | |
*/ | |
/* | |
static int | |
ivecs_get_cmap(struct ivecs_softc *sc, struct wsdisplay_cmap *wscmap) | |
{ | |
u_int index = wscmap->index; | |
u_int count = wscmap->count; | |
if (index >= HWCMAP_ELEMENTS || count > HWCMAP_ELEMENTS - index) | |
return EINVAL; | |
... | |
} | |
static void | |
ivecs_cmap_internal2ws(uint16_t *hw_cmap, struct wsdisplay_cmap *wscmap) | |
{ | |
blue[0] = palette16[0] & 0x1F; | |
green[0] = (palette16[0] & 0x7E0) >> 5; | |
red[0] = (palette16[0] & 0xF800) >>11; | |
... | |
} | |
*/ | |
/* data is transferred to SDRAM after writing 128 words (64 bytes), | |
so len should always be 64*n */ | |
/* write 64 bytes using longword transfer */ | |
/* | |
static void | |
ivecs_redrawfb(struct ivecs_softc *sc, uint8_t *srcbuf, size_t len) { | |
int i; | |
bus_space_write_2(sc->fpga_iot, sc->fpga_ioh, IVECS_FPGA_REGSEL, | |
FPGA_REG_FBADDR); | |
bus_space_write_2(sc->fpga_iot, sc->fpga_ioh, IVECS_FPGA_VAL, 0); | |
if(len % 64 != 0) { | |
} | |
bus_space_write_2(sc->fpga_iot, sc->fpga_ioh, IVECS_FPGA_REGSEL, | |
FPGA_REG_FBFIFO); | |
while(i<len) { | |
bus_space_write_multi_4(sc->fpga_iot, sc->fpga_ioh, IVECS_FPGA_VAL, | |
srcbuf[i], 16); | |
i+=64 | |
} | |
... | |
} | |
*/ | |
/* | |
static void | |
ivecs_set_palette(struct ivecs_softc *sc, struct ivecs_cmap) { | |
} | |
*/ | |
static int | |
ivecs_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, | |
struct lwp *l) | |
{ | |
switch (cmd) | |
{ | |
case WSDISPLAYIO_GTYPE: | |
*(u_int *)data = WSDISPLAY_TYPE_AMIGACC; | |
#ifdef DEBUG | |
/* use this ioctl to dump debug info */ | |
ivecs_dumpstate(); | |
#endif /* DEBUG */ | |
return (0); | |
/* case: ... */ | |
} | |
return (EPASSTHROUGH); | |
} | |
/* Indivision ECS memory is not directly accessible by the CPU. */ | |
static paddr_t | |
ivecs_mmap(void *v, void *vs, off_t offset, int prot) | |
{ | |
return -1; | |
} | |
#ifdef DEBUG | |
static void | |
ivecs_dumpstate(void) | |
{ | |
/* | |
uint16_t core; | |
ivecs_fpga_writereg(sc, FPGA_REG_LOCK, UNLOCK); | |
core = ivecs_fpga_readreg(sc, FPGA_REG_STATUS); | |
ivecs_fpga_writereg(sc, FPGA_REG_LOCK, LOCK); | |
printf("ivecs DEBUG: core: %x\n", core); | |
printf("driver state: ... \n"); | |
*/ | |
} | |
#endif /* DEBUG */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment