Skip to content

Instantly share code, notes, and snippets.

@rkujawa
Created September 17, 2016 21:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rkujawa/2bf31e5cbe261a9df5011985c35641e3 to your computer and use it in GitHub Desktop.
Save rkujawa/2bf31e5cbe261a9df5011985c35641e3 to your computer and use it in GitHub Desktop.
/*
* 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