Subject: New wsdisplay for playstation2
To: None <port-playstation2@netbsd.org, tech-kern@netbsd.org,>
From: Jorge Acereda =?iso-8859-1?q?Maci=E1?= <al004046@anubis.uji.es>
List: tech-kern
Date: 10/28/2003 18:24:16
Hi,

I am writing a gsfb replacement for the playstation2 port. It will
provide an mmap()able wsdisplay, unlike current implementation. My
goal is to run an X server on top of it.

This is my first attempt at writing a NetBSD device driver, so I would
appreciate any comments on style and errors.

Right now, I got the console working using rasops. But the mmap() part
is failing, don't know why.

I can mmap() from user space, but it seems to map the wrong page and I
can't find out why. Disassembling the mmap()ed memory reveals what
appears to be valid code (and used code, since writing to it hangs the
machine :-).

I faced another problem. I need some interrupt code to upload the
screen memory to the framebuffer, but that fails miserably when
entering ddb. Is there any way to leave an interrupt running even when
ddb is entered?

Finally, I don't know if the attach/cninit code is right.

The code is only a first attempt to get it working, it should really
support resolutions higher than 640x400.

TIA for any hints/suggestions,
  Jorge Acereda


----->8----->8----->8----->8----->8----->8----->8----->8----->8----->8


#include "debug_playstation2.h"

#include <sys/param.h>
#include <sys/device.h>
#include <sys/systm.h>

#include <machine/autoconf.h>

#include <dev/cons.h> 

#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wsdisplayvar.h>
#include <dev/wscons/wscons_callbacks.h>

#include <dev/rasops/rasops.h>

#include <playstation2/ee/dmacvar.h>
#include <playstation2/ee/dmacreg.h>
#include <playstation2/ee/eevar.h>
#include <playstation2/ee/gsvar.h>
#include <playstation2/ee/gsreg.h>
#include <playstation2/ee/intcvar.h>



/* internal */
void gsfb_do_redraw(void); /* XXX should be static, called from ddb */
static void gsfb_hwinit(void);
static void gsfb_init(void);
static void gsfb_dma_kick_phys(paddr_t, size_t);
static void gsfb_dma_kick_kseg0(void *, size_t);
static void gsfb_dma_kick_kseg1(void *, size_t);
static int vblank_intr(void *);

#define GSFB_BUFFER_DEPTH 32
#define CLEAR 1


#if GSFB_BUFFER_DEPTH == 32
#  define GSFB_PIXEL_SIZE 4
#  define GSFB_BUFFER_HEIGHT 200
#elif GSFB_BUFFER_DEPTH == 15 || GSFB_BUFFER_DEPTH == 16
#  define GSFB_PIXEL_SIZE 2
#  define GSFB_BUFFER_HEIGHT 400
#else 
#  error
#endif
#define GSFB_BUFFER_WIDTH 640
#define GSFB_BUFFER_SIZE \
    (GSFB_BUFFER_WIDTH * GSFB_BUFFER_HEIGHT * GSFB_PIXEL_SIZE)

/* 
 *  Documentation can be found at:
 * What:		Where:		Pages
 * GIFtag		EEUSER_E.pdf	151
 * BITBLTBUF		GSUSER_E.pdf	76, 101
 * TRXPOS		GSUSER_E.pdf	76
 * TRXREG		GSUSER_E.pdf	77
 * TRXDIR		GSUSER_E.pdf	77
 * FRAME_1              GSUSER_E.pdf    110
 * SCISSOR_1            GSUSER_E.pdf    121
 * ZBUF_1               GSUSER_E.pdf    176
 */

#define GSFB_PACKETS(name) \
  static u_int32_t name __attribute__((__aligned__(16)))


GSFB_PACKETS(gsfb_init_cmd_640x480x32[]) = {
#if CLEAR
        0x00008008, 0x10000000, 0x0000000e, 0x00000000, /* GIFtag */
#else
        0x00008003, 0x10000000, 0x0000000e, 0x00000000, /* GIFtag */
#endif
#if GSFB_PIXEL_SIZE == 4
	0x000a0000, 0x00000000, 0x0000004c, 0x00000000, /* FRAME_1 */
	0x00000096, 0x00000000, 0x0000004e, 0x00000000, /* ZBUF_1 */ 
#elif GSFB_PIXEL_SIZE == 2
	0x020a0000, 0x00000000, 0x0000004c, 0x00000000, /* FRAME_1 */
	0x02000096, 0x00000000, 0x0000004e, 0x00000000, /* ZBUF_1 */ 
#else
#error
#endif      
	0x02800000, 0x01e00000, 0x00000040, 0x00000000, /* SCISSOR_1 */
#if CLEAR
	0x00000006, 0x00000000, 0x00000000, 0x00000000, /* PRIM */ 
	0x80000000, 0x00000000, 0x00000001, 0x00000000,
	0x00000000, 0x00000000, 0x0000000d, 0x00000000,
	0x80000000, 0x00000000, 0x00000001, 0x00000000,
	0x1e002800, 0x00000000, 0x00000005, 0x00000000,
#endif
};

GSFB_PACKETS(gsfb_load_screen[]) = {
#if GSFB_PIXEL_SIZE == 4 && GSFB_BUFFER_HEIGHT == 200
	0x00000004, 0x10000000, 0x0000000e, 0x00000000, /* GIFtag */
	0x00000000, 0x000a0000, 0x00000050, 0x00000000, /* BITBLTBUF */
	0x00000000, 0x00000000, 0x00000051, 0x00000000, /* TRXPOS */
        0x00000280, 0x000000c8, 0x00000052, 0x00000000, /* TRXREG */
        0x00000000, 0x00000000, 0x00000053, 0x00000000, /* TRXDIR */
        0x0000fd00, 0x08000000, 0x00000000, 0x00000000, /* GIFtag */
#elif GSFB_PIXEL_SIZE == 2 && GSFB_BUFFER_HEIGHT == 400
	0x00000004, 0x10000000, 0x0000000e, 0x00000000, /* GIFtag */
	0x00000000, 0x020a0000, 0x00000050, 0x00000000, /* BITBLTBUF */
	0x00000000, 0x00000000, 0x00000051, 0x00000000, /* TRXPOS */
        0x00000280, 0x00000190, 0x00000052, 0x00000000, /* TRXREG */
        0x00000000, 0x00000000, 0x00000053, 0x00000000, /* TRXDIR */
        0x0000fd00, 0x08000000, 0x00000000, 0x00000000, /* GIFtag */
#else
#error
#endif 
};

static int gsfb_nscreens = 0;
static int gsfb_attached = 0;
static int gsfb_console = 0;
static char * gsfb_screen_base = 0;
char gsfb_screen_mem[GSFB_BUFFER_SIZE + sizeof gsfb_load_screen + NBPG]; 
static struct rasops_info gsfb_rinfo;

static int gsfb_match(struct device *, struct cfdata *, void *);
static void gsfb_attach(struct device *, struct device *, void *);

const struct cfattach gsfb_ca = {
        sizeof (struct device), gsfb_match, gsfb_attach, 0 , 0 
};

/* console (exported) */
void gsfbcnprobe(struct consdev *);
void gsfbcninit(struct consdev *);

/* access ops */
static int gsfb_ioctl(void *, u_long, caddr_t, int, struct proc *);
static paddr_t gsfb_mmap(void *, off_t, int);
static int gsfb_alloc_screen(void *, const struct wsscreen_descr *, void **,
			     int *, int *, long *);
static void gsfb_free_screen(void *, void *);
static int gsfb_show_screen(void *, void *, int, void (*)(void *, int, int),
			     void *);

static const struct wsdisplay_accessops gsfb_accessops = {
	.ioctl		= gsfb_ioctl,
	.mmap		= gsfb_mmap,
	.alloc_screen	= gsfb_alloc_screen,
	.free_screen	= gsfb_free_screen,
	.show_screen	= gsfb_show_screen,
	.load_font	= 0,
};


static struct wsscreen_descr gsfb_stdscreen = {
	.name = "std", 
	.ncols = 0, 
	.nrows = 0, 
	.textops = 0, 
	.fontwidth = 0,
	.fontheight = 0,
	.capabilities = WSSCREEN_WSCOLORS
};

static const struct wsscreen_descr * gsfb_screen_table[] = {
	&gsfb_stdscreen,
};

static const struct wsscreen_list gsfb_screen_list = {
	.nscreens	= sizeof gsfb_screen_table /
	                      sizeof gsfb_screen_table[0],
	.screens	= gsfb_screen_table
};


static inline void * 
gsfb_uncached(void * addr)
{
	return (void *) MIPS_PHYS_TO_KSEG1(MIPS_KSEG0_TO_PHYS(addr));
}

int
gsfb_match(struct device *parent, struct cfdata *cf, void *aux)
{
#if 0	
	struct mainbus_attach_args *ma;
        ma = aux;
	return strcmp(ma->ma_name, cf->cf_driver->cd_name) ?
		0 : !gsfb_attached;
#endif
	return !gsfb_attached;
}

void
gsfb_attach(struct device *parent, struct device *self, void *aux)
{
	struct wsemuldisplaydev_attach_args wa;
	
	gsfb_attached = 1;
	gsfb_init();
	printf("\n");
	wa.console	= gsfb_console;
	wa.scrdata	= &gsfb_screen_list;
	wa.accessops	= &gsfb_accessops;
	wa.accesscookie	= NULL; 
	
	config_found(self, &wa, wsdisplaydevprint);
}

/*
 * console
 */

void
gsfbcnprobe(struct consdev *cndev)
{
	cndev->cn_dev = NODEV;
	cndev->cn_pri = CN_INTERNAL;
#if 0 && NWSDISPLAY > 0
	{
		int maj = nchrdev;
		while (maj--)
			if (cdevsw[maj].d_open == wsdisplayopen)
				break;
		if (maj >= 0)
			cndev->cn_dev = makedev(maj,0);
	}
#endif
}

void
gsfbcninit(struct consdev *cndev)
{
	long defattr;
	gsfb_init();
	(*gsfb_rinfo.ri_ops.alloc_attr)(&gsfb_rinfo, 0, 0, 0, &defattr);
	wsdisplay_cnattach(&gsfb_stdscreen, &gsfb_rinfo, 0, 0, defattr);
	gsfb_console = 1;
	cn_tab->cn_pri = CN_INTERNAL;
}

/*
 * wsdisplay
 */

int
gsfb_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
{
        switch (cmd) {
	case WSDISPLAYIO_GTYPE:
                *(u_int *)data = WSDISPLAY_TYPE_PS2GS;
		return 0;
        case WSDISPLAYIO_GINFO:
#define fbi ((struct wsdisplay_fbinfo *)data)
                fbi->width = GSFB_BUFFER_WIDTH;
                fbi->height = GSFB_BUFFER_HEIGHT;
                fbi->depth = GSFB_BUFFER_DEPTH;
                fbi->cmsize = 0;
                return 0;
#undef fbi
        }
	return EPASSTHROUGH;
}

paddr_t
gsfb_mmap(void *v, off_t offset, int prot)
{
	if (GSFB_BUFFER_SIZE >= (unsigned) offset)
	    return NULL;
	return mips_btop(offset + gsfb_screen_base);
}

int
gsfb_alloc_screen(void *v, const struct wsscreen_descr *type, void **cookiep,
		  int *curxp, int *curyp, long *attrp)
{
	if (gsfb_nscreens > 0)
	    return ENOMEM;
	*cookiep = &gsfb_rinfo; 
	*curxp = 0;
	*curyp = 0;
	(*gsfb_rinfo.ri_ops.alloc_attr)(&gsfb_rinfo, 0, 0, 0, attrp);
	gsfb_nscreens++;
	return (0);
}

void
gsfb_free_screen(void *v, void *cookie)
{
 	panic("gsfb_free_screen: console");
}

int
gsfb_show_screen(void *v, void *cookie, int waitok,
		 void (*cb)(void *, int, int), void *cbarg)
{
	return (0);
}

/* 
 * Internal
 */

void
gsfb_hwinit()
{
	gs_init(VESA_1A);
	dmac_init();

	/* reset GIF channel DMA */
	_reg_write_4(D2_QWC_REG, 0);
	_reg_write_4(D2_MADR_REG, 0);
	_reg_write_4(D2_TADR_REG, 0);
	_reg_write_4(D2_CHCR_REG, 0);

	gsfb_dma_kick_kseg0(gsfb_init_cmd_640x480x32, 
			    sizeof gsfb_init_cmd_640x480x32);
}

void
gsfb_init()
{
	static int gsfb_initialized = 0;
	char * base;
	if (gsfb_initialized)
		return;
	gsfb_initialized = 1;

	base = (char *)
		mips_round_page(gsfb_screen_mem + sizeof gsfb_load_screen);
	base = gsfb_uncached(base);
	memcpy(base - sizeof gsfb_load_screen,
	       gsfb_load_screen, sizeof gsfb_load_screen);
	
	gsfb_hwinit();
	
	memset(&gsfb_rinfo, 0, sizeof gsfb_rinfo);
	gsfb_rinfo.ri_depth = GSFB_BUFFER_DEPTH;
	gsfb_rinfo.ri_bits = base;
	gsfb_rinfo.ri_width = GSFB_BUFFER_WIDTH;
	gsfb_rinfo.ri_height = GSFB_BUFFER_HEIGHT;
	gsfb_rinfo.ri_stride = GSFB_BUFFER_WIDTH * GSFB_PIXEL_SIZE;

	rasops_init(&gsfb_rinfo, 1000, 1000);

	gsfb_stdscreen.nrows = gsfb_rinfo.ri_rows;
	gsfb_stdscreen.ncols = gsfb_rinfo.ri_cols;
	gsfb_stdscreen.textops = &gsfb_rinfo.ri_ops;
	gsfb_stdscreen.capabilities = gsfb_rinfo.ri_caps;

	gsfb_screen_base = base;

	/* install vblank handler */
	intc_intr_establish(I_CH2_VB_ON, IPL_TTY, vblank_intr, 0);
}

void 
gsfb_do_redraw()
{
	gsfb_dma_kick_kseg1(gsfb_screen_base - sizeof gsfb_load_screen,
			    GSFB_BUFFER_SIZE + sizeof gsfb_load_screen);
}

void 
gsfb_dma_kick_phys(paddr_t addr, size_t size)
{
	/* Wait for previous DMA request complete */
	while (_reg_read_4(D2_QWC_REG))
		;

	/* Wait until GS FIFO empty */
	while ((_reg_read_8(GS_S_CSR_REG) & (3 << 14)) != (1 << 14))
		;

	/* wait for DMA complete */
	dmac_bus_poll(D_CH2_GIF);

	/* transfer addr */
	_reg_write_4(D2_MADR_REG, addr);
	/* transfer data size (unit qword) */
	_reg_write_4(D2_QWC_REG, bytetoqwc(size));
	
	/* kick DMA (normal-mode) */
	dmac_chcr_write(D_CH2_GIF, D_CHCR_STR);
}

void
gsfb_dma_kick_kseg0(void * addr, size_t size)
{
	gsfb_dma_kick_phys(MIPS_KSEG0_TO_PHYS(addr), size);
}

void
gsfb_dma_kick_kseg1(void * addr, size_t size)
{
	gsfb_dma_kick_phys(MIPS_KSEG1_TO_PHYS(addr), size);
}

int 
vblank_intr(void * unused)
{
	static int skipcnt = 0;
	if (!skipcnt--) {
		skipcnt = 0;
		gsfb_do_redraw();
	}
	return 1;
}