NetBSD/sys/arch/pmax/dev/px.c

2004 lines
46 KiB
C

/* $NetBSD: px.c,v 1.47 2003/04/02 04:19:49 thorpej Exp $ */
/*-
* Copyright (c) 1999 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Andrew Doran.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the NetBSD
* Foundation, Inc. and its contributors.
* 4. Neither the name of The NetBSD Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "px.h"
#if NPX > 1
#error Multiboard px support not in-tree right now.
#endif
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: px.c,v 1.47 2003/04/02 04:19:49 thorpej Exp $");
/*
* px.c: driver for the DEC TURBOchannel 2D and 3D accelerated framebuffers
* with PixelStamp blitter asics (and i860 accelerators, on the higher end
* cards).
*/
#include <sys/param.h>
#include <sys/conf.h>
#include <sys/device.h>
#include <sys/errno.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/poll.h>
#include <sys/proc.h>
#include <sys/resourcevar.h>
#include <sys/systm.h>
#include <sys/vnode.h>
#include <uvm/uvm_extern.h>
#include <miscfs/specfs/specdev.h>
#include <dev/cons.h>
#include <dev/ic/bt459reg.h>
#include <dev/tc/tcvar.h>
#include <dev/rcons/rcons.h>
#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wsdisplayvar.h>
#include <dev/wsfont/wsfont.h>
#include <machine/autoconf.h>
#include <dev/sun/fbio.h>
#include <machine/fbvar.h>
#include <machine/pmioctl.h>
#include <pmax/dev/fbreg.h>
#include <pmax/dev/pxreg.h>
#include <pmax/dev/pxvar.h>
#include <pmax/dev/qvssvar.h>
#include <pmax/dev/rconsvar.h>
struct px_softc {
struct device px_dv;
struct px_info *px_info;
};
static int px_match __P((struct device *, struct cfdata *, void *));
static void px_attach __P((struct device *, struct device *, void *));
static int px_init __P((struct fbinfo *, caddr_t, int, int));
static int px_intr __P((void *xxx_sc));
static int32_t *px_alloc_pbuf __P((struct px_info *));
static int px_send_packet __P((struct px_info *, int *buf));
static void px_init_stic __P((struct px_info *, int));
static void px_bt459_init __P((struct px_info *));
static int px_probe_planes __P((struct px_info *, int));
static void px_load_cursor __P((struct px_info *));
static void px_conv_cursor __P((struct px_info *, u_short *));
static void px_load_cmap __P((struct px_info *, int, int));
static void px_load_cursor_data __P((struct px_info *, int, int));
static void px_make_cursor __P((struct px_info *));
static int px_rect __P((struct px_info *, int, int, int, int, int));
static void px_qvss_init __P((struct px_info *));
static int px_mmap_info __P((struct proc *, dev_t, vaddr_t *));
static void px_cursor_hack __P((struct fbinfo *, int, int));
static int px_probe_sram __P((struct px_info *));
static void px_bt459_flush __P((struct px_info *));
CFATTACH_DECL(px, sizeof(struct px_softc),
px_match, px_attach, NULL, NULL);
dev_type_open(pxopen);
dev_type_close(pxclose);
dev_type_ioctl(pxioctl);
dev_type_poll(pxpoll);
dev_type_mmap(pxmmap);
dev_type_kqfilter(pxkqfilter);
const struct cdevsw px_cdevsw = {
pxopen, pxclose, noread, nowrite, pxioctl,
nostop, notty, pxpoll, pxmmap, pxkqfilter,
};
/* The different types of card that we support, for px_match(). */
static const char *px_types[] = {
"PMAG-CA ", /* 2DA */
"PMAG-DA ", /* LM-3DA */
"PMAG-FA ", /* HE-3DA */
"PMAG-FB ", /* HE+3DA */
"PMAGB-FA", /* HE+3DA */
"PMAGB-FB", /* HE+3DA */
};
#define NUM_PX_TYPES (sizeof(px_types) / sizeof(px_types[0]))
/* wscons emulator operations */
static void px_cursor __P((void *, int, int, int));
static void px_putchar __P((void *, int, int, u_int, long));
static void px_copycols __P((void *, int, int, int, int));
static void px_copyrows __P((void *, int, int, int num));
static void px_erasecols __P((void *, int, int, int num, long));
static void px_eraserows __P((void *, int, int, long));
static int px_allocattr __P((void *, int, int, int, long *));
static int px_mapchar __P((void *, int, unsigned int *));
static struct wsdisplay_emulops px_emulops = {
px_cursor,
px_mapchar,
px_putchar,
px_copycols,
px_erasecols,
px_copyrows,
px_eraserows,
px_allocattr
};
/* Colormap for wscons, matching WSCOL_*. Upper 8 are high-intensity */
static const u_char px_cmap[16*3] = {
0x00, 0x00, 0x00, /* black */
0x7f, 0x00, 0x00, /* red */
0x00, 0x7f, 0x00, /* green */
0x7f, 0x7f, 0x00, /* brown */
0x00, 0x00, 0x7f, /* blue */
0x7f, 0x00, 0x7f, /* magenta */
0x00, 0x7f, 0x7f, /* cyan */
0xc7, 0xc7, 0xc7, /* white - XXX too dim? */
0x7f, 0x7f, 0x7f, /* black */
0xff, 0x00, 0x00, /* red */
0x00, 0xff, 0x00, /* green */
0xff, 0xff, 0x00, /* brown */
0x00, 0x00, 0xff, /* blue */
0xff, 0x00, 0xff, /* magenta */
0x00, 0xff, 0xff, /* cyan */
0xff, 0xff, 0xff, /* white */
};
/*
* Compose 2 bit/pixel cursor image. Bit order will be reversed.
* M M M M I I I I M I M I M I M I
* [ before ] [ after ]
* 3 2 1 0 3 2 1 0 0 0 1 1 2 2 3 3
* 7 6 5 4 7 6 5 4 4 4 5 5 6 6 7 7
*
* XXX duplicated in sfb.c, cfb.c, mfb.c
*/
static const u_char px_shuffle[256] = {
0x00, 0x40, 0x10, 0x50, 0x04, 0x44, 0x14, 0x54,
0x01, 0x41, 0x11, 0x51, 0x05, 0x45, 0x15, 0x55,
0x80, 0xc0, 0x90, 0xd0, 0x84, 0xc4, 0x94, 0xd4,
0x81, 0xc1, 0x91, 0xd1, 0x85, 0xc5, 0x95, 0xd5,
0x20, 0x60, 0x30, 0x70, 0x24, 0x64, 0x34, 0x74,
0x21, 0x61, 0x31, 0x71, 0x25, 0x65, 0x35, 0x75,
0xa0, 0xe0, 0xb0, 0xf0, 0xa4, 0xe4, 0xb4, 0xf4,
0xa1, 0xe1, 0xb1, 0xf1, 0xa5, 0xe5, 0xb5, 0xf5,
0x08, 0x48, 0x18, 0x58, 0x0c, 0x4c, 0x1c, 0x5c,
0x09, 0x49, 0x19, 0x59, 0x0d, 0x4d, 0x1d, 0x5d,
0x88, 0xc8, 0x98, 0xd8, 0x8c, 0xcc, 0x9c, 0xdc,
0x89, 0xc9, 0x99, 0xd9, 0x8d, 0xcd, 0x9d, 0xdd,
0x28, 0x68, 0x38, 0x78, 0x2c, 0x6c, 0x3c, 0x7c,
0x29, 0x69, 0x39, 0x79, 0x2d, 0x6d, 0x3d, 0x7d,
0xa8, 0xe8, 0xb8, 0xf8, 0xac, 0xec, 0xbc, 0xfc,
0xa9, 0xe9, 0xb9, 0xf9, 0xad, 0xed, 0xbd, 0xfd,
0x02, 0x42, 0x12, 0x52, 0x06, 0x46, 0x16, 0x56,
0x03, 0x43, 0x13, 0x53, 0x07, 0x47, 0x17, 0x57,
0x82, 0xc2, 0x92, 0xd2, 0x86, 0xc6, 0x96, 0xd6,
0x83, 0xc3, 0x93, 0xd3, 0x87, 0xc7, 0x97, 0xd7,
0x22, 0x62, 0x32, 0x72, 0x26, 0x66, 0x36, 0x76,
0x23, 0x63, 0x33, 0x73, 0x27, 0x67, 0x37, 0x77,
0xa2, 0xe2, 0xb2, 0xf2, 0xa6, 0xe6, 0xb6, 0xf6,
0xa3, 0xe3, 0xb3, 0xf3, 0xa7, 0xe7, 0xb7, 0xf7,
0x0a, 0x4a, 0x1a, 0x5a, 0x0e, 0x4e, 0x1e, 0x5e,
0x0b, 0x4b, 0x1b, 0x5b, 0x0f, 0x4f, 0x1f, 0x5f,
0x8a, 0xca, 0x9a, 0xda, 0x8e, 0xce, 0x9e, 0xde,
0x8b, 0xcb, 0x9b, 0xdb, 0x8f, 0xcf, 0x9f, 0xdf,
0x2a, 0x6a, 0x3a, 0x7a, 0x2e, 0x6e, 0x3e, 0x7e,
0x2b, 0x6b, 0x3b, 0x7b, 0x2f, 0x6f, 0x3f, 0x7f,
0xaa, 0xea, 0xba, 0xfa, 0xae, 0xee, 0xbe, 0xfe,
0xab, 0xeb, 0xbb, 0xfb, 0xaf, 0xef, 0xbf, 0xff,
};
#define PXMAP_INFO_SIZE (PAGE_SIZE)
#define PXMAP_RBUF_SIZE (4096 * 16 + 8192 * 2)
/* Need alignment to 8KB here... */
static u_char px_cons_rbuf[PXMAP_INFO_SIZE + PXMAP_RBUF_SIZE + 8192];
static u_int px_cons_rbuf_use;
static struct px_info *px_cons_info;
static struct px_info *px_unit[NPX];
/* bt459 gunk. XXX should be done with driver independant interface */
struct bt459_regs {
volatile int32_t lo;
volatile int32_t hi;
volatile int32_t reg;
volatile int32_t cmap;
};
#define BT459_SELECT(vdac, regno) do { \
(vdac)->lo = DUPBYTE0(regno); \
(vdac)->hi = DUPBYTE1(regno); \
tc_wmb(); \
} while(0);
#define BT459_WRITE_REG(vdac, data) \
((vdac)->reg = (data)), tc_wmb();
#define BT459_WRITE_CMAP(vdac, data) \
((vdac)->cmap = (data)), tc_wmb();
#define BT459_READ_REG(vdac) ((vdac)->reg)
/* We have to support 3 VDACs on the 24-bit cards */
#define DUPBYTE0(x) ((((x)&0xff)<<16) | (((x)&0xff)<<8) | ((x)&0xff))
#define DUPBYTE1(x) ((((x)<<8)&0xff0000) | ((x)&0xff00) | (((x)>>8)&0xff))
#define DUPBYTE2(x) (((x)&0xff0000) | (((x)>>8)&0xff00) | (((x)>>16)&0xff))
#define PACK_WORD(p, o) ((p)[(o)] | ((p)[(o)+1] << 16))
int
px_cnattach(addr)
paddr_t addr;
{
caddr_t base;
base = (caddr_t)TC_PHYS_TO_UNCACHED(addr);
if (px_init((struct fbinfo *)1, base, 0, 1) != 1)
return (0);
return (1);
}
/*
* Match a supported board.
*/
static int
px_match(parent, match, aux)
struct device *parent;
struct cfdata *match;
void *aux;
{
struct tc_attach_args *ta = (struct tc_attach_args *)aux;
int i;
for (i = 0; i < NUM_PX_TYPES; i++)
if (strncmp(px_types[i], ta->ta_modname, TC_ROM_LLEN) == 0)
return (1);
return (0);
}
/*
* Attach the graphics board.
*/
static void
px_attach(parent, self, aux)
struct device *parent, *self;
void *aux;
{
struct px_softc *sc;
struct px_info *pxi;
struct tc_attach_args *ta;
caddr_t slotbase;
char *p;
int i;
sc = (struct px_softc *)self;
ta = (struct tc_attach_args *)aux;
slotbase = (caddr_t)TC_PHYS_TO_UNCACHED(ta->ta_addr);
/* Init the card only if it hasn't been done before... */
if (!px_cons_info || slotbase != (caddr_t)px_cons_info->pxi_slotbase)
px_init((struct fbinfo *)1, slotbase, sc->px_dv.dv_unit, 0);
/* px_init() fills in px_unit[#] */
pxi = px_unit[sc->px_dv.dv_unit];
sc->px_info = pxi;
/* Now grab the interrupt */
tc_intr_establish(parent, ta->ta_cookie, TC_IPL_TTY, px_intr, sc);
for (i = 0, p = ta->ta_modname; *p != ' ' && i < TC_ROM_LLEN; i++)
pxi->pxi_type[i] = *p++;
pxi->pxi_type[i] = '\0';
/* Set ISR driven packet-buffer polling addresses */
for (i = 0; i < 16; i++) {
caddr_t addr = (caddr_t)pxi->pxi_rbuf + (i << 12);
pxi->pxi_qpoll[i] = px_poll_addr(slotbase, addr);
}
/* The following values are filled in by px_init_stic. */
printf(": %cD, %dx%d stamp, %d plane", (pxi->pxi_option ? '3' : '2'),
pxi->pxi_stampw, pxi->pxi_stamph, pxi->pxi_nplanes);
if (pxi->pxi_option)
printf(", %dKB SRAM", px_probe_sram(pxi) >> 10);
printf("\n");
}
/*
* Initialize the graphics board. This can be called from tc_findcons and
* the pmax console trickery, in which case ``fi'' will be null.
*
* XXX use magic number to make sure fi isn't a real struct fbinfo?
*/
static int
px_init(fi, slotbase, unit, console)
struct fbinfo *fi;
caddr_t slotbase;
int unit, console;
{
struct px_info *pxi;
u_long bufpa;
int i;
#if NPX > 1
if (px_cons_rbuf_use)
/* XXX allocate buffers */;
else
#endif
{
bufpa = MIPS_KSEG0_TO_PHYS(px_cons_rbuf);
px_cons_rbuf_use = 1;
}
/* Align to 8KB. px_info struct gets the first 4KB */
bufpa = (bufpa + 8191) & ~8191;
pxi = (struct px_info *)MIPS_PHYS_TO_KSEG1(bufpa);
px_unit[unit] = pxi;
bufpa += PXMAP_INFO_SIZE;
if (bufpa + PXMAP_RBUF_SIZE > 8192*1024) {
printf("px%d: ring buffer outside first 8MB of RAM\n", unit);
return 0;
}
pxi->pxi_slotbase = TC_PHYS_TO_UNCACHED(slotbase);
pxi->pxi_unit = unit;
pxi->pxi_stamp = (caddr_t)(pxi->pxi_slotbase + PX_STAMP_OFFSET);
pxi->pxi_poll = (int32_t *)(pxi->pxi_slotbase + PX_STIC_POLL_OFFSET);
pxi->pxi_stic = (struct stic_regs *)(pxi->pxi_slotbase + PX_STIC_OFFSET);
pxi->pxi_rbuf = (int *)MIPS_PHYS_TO_KSEG1(bufpa);
pxi->pxi_rbuf_phys = bufpa;
pxi->pxi_rbuf_size = PXMAP_RBUF_SIZE;
pxi->pxi_pbuf_select = 0;
pxi->pxi_flg = PX_ENABLE;
/* We need to do this ASAP so we can disable the co-processor */
px_init_stic(pxi, 1);
/*
* If this is a PXG, use the SRAM and not kernel bss.
* XXX this is a big fat waste of memory.
*/
if (pxi->pxi_option) {
bufpa = MIPS_KSEG0_TO_PHYS(slotbase + PXG_SRAM_OFFSET);
pxi->pxi_rbuf = (int *)MIPS_PHYS_TO_KSEG1(bufpa);
pxi->pxi_rbuf_phys = bufpa;
pxi->pxi_rbuf_size = px_probe_sram(pxi);
}
/* Get a font and lock. If we're not the console, we don't care */
if (console) {
wsfont_init();
i = wsfont_find(NULL, 0, 0, 2, WSDISPLAY_FONTORDER_R2L,
WSDISPLAY_FONTORDER_L2R);
if (i <= 0)
panic("px_init: unable to get font");
if (wsfont_lock(i, &pxi->pxi_font))
panic("px_init: unable to lock font");
pxi->pxi_wsfcookie = i;
} else
pxi->pxi_wsfcookie = -1;
/* Only now can we init the bt459... */
px_bt459_init(pxi);
/* Clear ringbuffer and then the screen */
memset(pxi->pxi_rbuf, 0, pxi->pxi_rbuf_size);
px_rect(pxi, 0, 0, 1280, 1024, 0);
/* Connect to rcons if this is the console device */
if (console) {
pxi->pxi_fontscale = pxi->pxi_font->fontheight *
pxi->pxi_font->stride;
px_cons_info = pxi;
/* XXX no multiscreen X support yet */
px_qvss_init(pxi);
rcons_connect_native(&px_emulops, pxi, 1280, 1024,
1280 / pxi->pxi_font->fontwidth,
1024 / pxi->pxi_font->fontheight);
}
return 1;
}
/*
* Initialize our DISGUSTING little hack so we can use the qvss event-buffer
* stuff for the X server.
*/
static void
px_qvss_init(pxi)
struct px_info *pxi;
{
struct fbinfo *fi;
static struct pmax_fbtty pxfb;
static struct fbdriver fbdriver = {
NULL, NULL, NULL, NULL,
NULL, px_cursor_hack, NULL, NULL
};
fi = &pxi->pxi_fbinfo;
fi->fi_driver = &fbdriver;
fi->fi_base = (caddr_t)pxi;
fi->fi_fbu = &pxi->pxi_fbuaccess;
fi->fi_type.fb_width = 1280;
fi->fi_type.fb_height = 1024;
fi->fi_type.fb_type = PMAX_FBTYPE_PX;
fi->fi_type.fb_cmsize = 256;
fi->fi_type.fb_depth = 8;
fi->fi_fbu->scrInfo.max_row = 1024 / 8;
fi->fi_fbu->scrInfo.max_col = 80;
fi->fi_glasstty = &pxfb;
tb_kbdmouseconfig(fi);
init_pmaxfbu(fi);
}
/*
* Initialize the Brooktree 459 VDAC.
*/
static void
px_bt459_init(pxi)
struct px_info *pxi;
{
struct bt459_regs *vdac = pxi->pxi_vdac;
int i;
/* Hit it... */
BT459_SELECT(vdac, BT459_IREG_COMMAND_0);
BT459_WRITE_REG(vdac, 0xc0c0c0);
/* Now reset the VDAC */
if (pxi->pxi_option)
*(int32_t *)(pxi->pxi_slotbase + PXG_VDAC_RESET_OFFSET) = 0;
else
*(int32_t *)(pxi->pxi_slotbase + PX_VDAC_RESET_OFFSET) = 0;
tc_wmb();
/* Finish the initialization */
BT459_SELECT(vdac, BT459_IREG_COMMAND_1);
BT459_WRITE_REG(vdac, 0x000000);
BT459_WRITE_REG(vdac, 0xc2c2c2);
BT459_WRITE_REG(vdac, 0xffffff);
for (i = 0; i < 7; i++)
BT459_WRITE_REG(vdac, 0);
/* Set cursor colormap */
BT459_SELECT(vdac, BT459_IREG_CCOLOR_1);
BT459_WRITE_REG(vdac, 0xffffff);
BT459_WRITE_REG(vdac, 0xffffff);
BT459_WRITE_REG(vdac, 0xffffff);
BT459_WRITE_REG(vdac, 0x00);
BT459_WRITE_REG(vdac, 0x00);
BT459_WRITE_REG(vdac, 0x00);
BT459_WRITE_REG(vdac, 0xffffff);
BT459_WRITE_REG(vdac, 0xffffff);
BT459_WRITE_REG(vdac, 0xffffff);
/* Build and load a sane colormap */
memset(pxi->pxi_cmap, 0, sizeof(pxi->pxi_cmap));
memcpy(pxi->pxi_cmap, px_cmap, 16 * 3);
pxi->pxi_cmap[255*3+0] = 0xff;
pxi->pxi_cmap[255*3+1] = 0xff;
pxi->pxi_cmap[255*3+2] = 0xff;
px_load_cmap(pxi, 0, 256);
/* Make a sane cursor and load it */
if (pxi->pxi_font != NULL) {
px_make_cursor(pxi);
px_load_cursor(pxi);
/* Enable cursor */
BT459_SELECT(vdac, BT459_IREG_CCR);
BT459_WRITE_REG(vdac, 0x1c1c1c1);
pxi->pxi_flg |= PX_CURSOR_ENABLE;
} else {
BT459_SELECT(vdac, BT459_IREG_CCR);
BT459_WRITE_REG(vdac, 0);
}
}
/*
* Determine the number of planes supported by the given buffer.
* XXX should know this from module name
*/
static int
px_probe_planes(pxi, buf)
struct px_info *pxi;
int buf;
{
int i;
if (buf == 0) {
/*
* For the real framebuffer (# 0), we can cheat and use the
* VDAC ID. One color is active at level 0x4a for 8 bits, all
* colors are active at 0x4a on the 24 bit cards.
*/
BT459_SELECT(pxi->pxi_vdac, BT459_IREG_ID);
i = pxi->pxi_vdac->reg & 0x00ffffff;
/* 3 VDACs */
if (i == 0x4a4a4a)
return 24;
/* 1 VDAC */
if ((i & 0xff0000) == 0x4a0000 ||
(i & 0x00ff00) == 0x004a00 ||
(i & 0x0000ff) == 0x00004a)
return 8;
/* Whoops... Assume 8 planes? */
printf("\n");
printf("px%d: invalid VDAC ID 0x%08x", pxi->pxi_unit, i);
return 8;
}
/* Don't give a damn about Z-buffers... */
panic("px_probe_planes: (buf != 0) was un-implemented (bloat)");
}
/*
* Figure out how much SRAM the PXG has: 128KB or 256KB.
*/
static int
px_probe_sram(pxi)
struct px_info *pxi;
{
volatile int32_t *a, *b;
a = (int32_t *)(pxi->pxi_slotbase + PXG_SRAM_OFFSET);
b = a + (0x20000 >> 1);
*a = 4321;
*b = 1234;
tc_wmb();
return ((*a == *b) ? 0x20000 : 0x40000);
}
/*
* Initialize the STIC (STamp Interface Chip) and stamp
*/
static void
px_init_stic(pxi, probe)
struct px_info *pxi;
int probe;
{
int modtype, xconfig, yconfig, config;
struct stic_regs *stic;
volatile int32_t *slot;
caddr_t stamp;
int i;
stic = pxi->pxi_stic;
stamp = pxi->pxi_stamp;
/* If this is a 3D board, disable the i860 co-processor. */
if (((stic->modcl >> 12) & 3) != 0) {
slot = (volatile int32_t *)pxi->pxi_slotbase;
slot[PXG_N10_RESET_OFFSET >> 2] = 0;
tc_wmb();
slot[PXG_HOST_INTR_OFFSET >> 2] = 0;
tc_wmb();
DELAY(40000); /* paranoia */
}
/*
* Initialize STIC interface chip registers. Magic sequence from
* logic analyser.
*/
stic->sticsr = 0x00000030; /* Get the STIC's attention. */
tc_wmb();
DELAY(4000); /* wait 4ms for STIC to respond. */
stic->sticsr = 0x00000000; /* Hit the STIC's csr again... */
tc_wmb();
stic->buscsr = 0xffffffff; /* and bash its bus-acess csr. */
tc_wmb(); /* Blam! */
DELAY(20000); /* wait until the stic recovers... */
/* Initialize Stamp config register for model 0. */
modtype = stic->modcl;
xconfig = (modtype & 0x800) >> 11;
yconfig = (modtype & 0x600) >> 9;
config = (yconfig << 1) | xconfig;
*(int32_t *) (stamp + __PXS(0x000b0)) = config;
*(int32_t *) (stamp + __PXS(0x000b4)) = 0x0;
if (yconfig > 0) {
/* pixelstamp 1 configuration */
*(int32_t *) (stamp + __PXS(0x100b0)) = config | 8;
*(int32_t *) (stamp + __PXS(0x100b4)) = 0x0;
}
/*
* Remember the size of the stamp, and card revision. Also
* figure out the number of planes.
*/
if (probe) {
pxi->pxi_stampw = (xconfig ? 5 : 4);
pxi->pxi_stamph = (1 << yconfig);
pxi->pxi_revision = (char)(modtype >> 24);
pxi->pxi_option = (char)((modtype >> 12) & 3);
/* PXG upwards has it's VDAC at a different location */
i = (pxi->pxi_option ? PXG_VDAC_OFFSET : PX_VDAC_OFFSET);
pxi->pxi_vdac = (struct bt459_regs *) (pxi->pxi_slotbase + i);
if (pxi->pxi_option == 0) {
/* 2D board */
pxi->pxi_nplanes = 8;
pxi->pxi_planemask = 0xff;
} else {
if (pxi->pxi_stamph > 1) {
/* high-end 3D */
pxi->pxi_nplanes = 24;
pxi->pxi_planemask = 0xffffff;
} else {
/* low/mid 3D */
pxi->pxi_nplanes = px_probe_planes(pxi, 0);
/* XXX is this right? */
if (pxi->pxi_nplanes == 8)
pxi->pxi_planemask = 0xff0000;
else
pxi->pxi_planemask = 0xffffff;
}
}
}
/*
* Initialize STIC video registers. (if we knew what we were doing,
* we might be able to frob this to work at different montior
* frequencies.)
*/
stic->vblank = (1024 << 16) | 1063;
stic->vsync = (1027 << 16) | 1030;
stic->hblank = (255 << 16) | 340;
stic->hsync2 = 245;
stic->hsync = (261 << 16) | 293;
stic->ipdvint = STIC_INT_CLR | STIC_INT_WE;
stic->sticsr = 0x00000008;
tc_wmb();
#ifdef notdef
/* Now enable the i860 and STIC interrupts (PXG only) */
if (pxi->pxi_option) {
slot = (volatile int32_t *)pxi->pxi_slotbase;
slot[PXG_N10_START_OFFSET >> 2] = 1;
tc_wmb();
DELAY(2000);
stic->sticsr = STIC_INT_WE | STIC_INT_CLR;
tc_wmb();
}
#endif
}
/*
* Make a cursor matching the current font dimensions.
*/
static void
px_make_cursor(pxi)
struct px_info *pxi;
{
u_int8_t *ip, *mp;
int r, c, o, b;
ip = pxi->pxi_cursor;
mp = pxi->pxi_cursor + (sizeof(pxi->pxi_cursor) >> 1);
memset(pxi->pxi_cursor, 0, sizeof(pxi->pxi_cursor));
for (r = 0; r < pxi->pxi_font->fontheight; r++) {
for (c = 0; c < pxi->pxi_font->fontwidth; c++) {
o = c >> 3;
b = 1 << (c & 7);
ip[o] |= b;
mp[o] |= b;
}
ip += 8;
mp += 8;
}
}
/*
* Convert a 16x16 cursor from the Xserver.
*/
static void
px_conv_cursor(pxi, sip)
struct px_info *pxi;
u_short *sip;
{
u_short *ip, *mp;
int r;
ip = (u_short *)pxi->pxi_cursor;
mp = (u_short *)(pxi->pxi_cursor + (sizeof(pxi->pxi_cursor) >> 1));
memset(pxi->pxi_cursor, 0, sizeof(pxi->pxi_cursor));
for (r = 0; r < 16; r++) {
*ip = sip[0];
*mp = sip[16];
sip++;
ip += 4;
mp += 4;
}
}
/*
* Load a single byte into the cursor map.
*/
static void
px_load_cursor_data(pxi, pos, val)
struct px_info *pxi;
int pos, val;
{
struct bt459_regs *vdac;
int cnt;
vdac = pxi->pxi_vdac;
val = DUPBYTE0(val);
for (cnt = 10; cnt; cnt--) {
BT459_SELECT(vdac, BT459_IREG_CRAM_BASE + pos);
BT459_WRITE_REG(vdac, val);
if ((BT459_READ_REG(vdac) & pxi->pxi_planemask) == val)
break;
}
}
/*
* Load the cursor image.
*/
static void
px_load_cursor(pxi)
struct px_info *pxi;
{
struct bt459_regs *vdac;
u_int8_t *ip, *mp, img, msk, u;
int bcnt;
vdac = pxi->pxi_vdac;
ip = pxi->pxi_cursor;
mp = pxi->pxi_cursor + (sizeof(pxi->pxi_cursor) >> 1);
bcnt = 0;
BT459_SELECT(vdac, BT459_IREG_CRAM_BASE + 0);
/* 64 pixel scan line is made with 8 bytes of cursor RAM */
while (bcnt < sizeof(pxi->pxi_cursor)) {
img = *ip++;
msk = *mp++;
img &= msk; /* cookie off image */
u = (msk & 0x0f) << 4 | (img & 0x0f);
px_load_cursor_data(pxi, bcnt++, px_shuffle[u]);
u = (msk & 0xf0) | (img & 0xf0) >> 4;
px_load_cursor_data(pxi, bcnt++, px_shuffle[u]);
}
}
/*
* Load the colormap.
*/
static void
px_load_cmap(pxi, index, num)
struct px_info *pxi;
int index, num;
{
struct bt459_regs *vdac;
u_char *p;
if (index < 0 || num <= 0 || index + num > 256)
return;
vdac = pxi->pxi_vdac;
num = (num << 1) + num; /* multiply by 3 */
p = pxi->pxi_cmap + (index << 1) + index;
BT459_SELECT(vdac, index);
BT459_SELECT(vdac, index);
DELAY(20);
for (; num--; p++)
BT459_WRITE_CMAP(vdac, DUPBYTE0(*p));
}
/*
* Flush any pending updates to the bt459. This gets called during vblank
* on the PX to prevent shearing/snow. The PXG always has to flush.
*/
static void
px_bt459_flush(pxi)
struct px_info *pxi;
{
struct bt459_regs *vdac;
u_char *cp;
int i;
vdac = pxi->pxi_vdac;
if (pxi->pxi_dirty & PX_DIRTY_CURSOR_POS) {
BT459_SELECT(vdac, BT459_IREG_CURSOR_X_LOW);
BT459_WRITE_REG(vdac, DUPBYTE0(pxi->pxi_curx));
BT459_WRITE_REG(vdac, DUPBYTE1(pxi->pxi_curx));
BT459_WRITE_REG(vdac, DUPBYTE0(pxi->pxi_cury));
BT459_WRITE_REG(vdac, DUPBYTE1(pxi->pxi_cury));
}
if (pxi->pxi_dirty & PX_DIRTY_CURSOR)
px_load_cursor(pxi);
if (pxi->pxi_dirty & PX_DIRTY_CURSOR_CMAP) {
cp = pxi->pxi_curcmap;
BT459_SELECT(vdac, BT459_IREG_CCOLOR_1);
BT459_WRITE_REG(vdac, DUPBYTE0(cp[3]));
BT459_WRITE_REG(vdac, DUPBYTE0(cp[4]));
BT459_WRITE_REG(vdac, DUPBYTE0(cp[5]));
BT459_WRITE_REG(vdac, DUPBYTE0(cp[0]));
BT459_WRITE_REG(vdac, DUPBYTE0(cp[1]));
BT459_WRITE_REG(vdac, DUPBYTE0(cp[2]));
BT459_WRITE_REG(vdac, DUPBYTE0(cp[3]));
BT459_WRITE_REG(vdac, DUPBYTE0(cp[4]));
BT459_WRITE_REG(vdac, DUPBYTE0(cp[5]));
}
if (pxi->pxi_dirty & PX_DIRTY_ENABLE) {
if (pxi->pxi_flg & PX_ENABLE) {
BT459_SELECT(vdac, BT459_IREG_PRM);
BT459_WRITE_REG(vdac, 0xffffff);
px_load_cmap(pxi, 0, 1);
pxi->pxi_dirty |= PX_DIRTY_CURSOR_ENABLE;
} else {
BT459_SELECT(vdac, BT459_IREG_PRM);
BT459_WRITE_REG(vdac, 0);
BT459_SELECT(vdac, 0);
BT459_WRITE_CMAP(vdac, 0);
BT459_WRITE_CMAP(vdac, 0);
BT459_WRITE_CMAP(vdac, 0);
BT459_SELECT(vdac, BT459_IREG_CCR);
BT459_WRITE_REG(vdac, 0);
}
}
if (pxi->pxi_flg & PX_ENABLE) {
if (pxi->pxi_dirty & PX_DIRTY_CMAP)
px_load_cmap(pxi, pxi->pxi_cmap_idx,
pxi->pxi_cmap_cnt);
if (pxi->pxi_dirty & PX_DIRTY_CURSOR_ENABLE) {
if (pxi->pxi_flg & PX_CURSOR_ENABLE) {
/* No flashing cursor for X */
if (pxi->pxi_flg & PX_OPEN)
i = 0x00c0c0c0;
else
i = 0x01c1c1c1;
} else
i = 0;
BT459_SELECT(vdac, BT459_IREG_CCR);
BT459_WRITE_REG(vdac, i);
}
}
pxi->pxi_dirty = 0;
}
/*
* PixelStamp board interrupt handler. We can get more than one interrupt
* at a time (i.e. packet+vertical retrace) so we don't return after
* handling each case.
*/
static int
px_intr(xxx_sc)
void *xxx_sc;
{
struct px_cliplist *cl;
struct stic_regs *stic;
struct px_info *pxi;
int caught, state;
pxi = (struct px_info *)
MIPS_PHYS_TO_KSEG1(((struct px_softc *)xxx_sc)->px_info);
stic = pxi->pxi_stic;
caught = 0;
state = stic->ipdvint;
#ifdef notdef
/* Getting this from the i860? */
if (pxi->pxi_option) {
hi = (int32_t *)pxi->pxi_slotbase + (PXG_HOST_INTR_OFFSET>>2);
/* Clear the interrupt condition */
i = hi[0] & 15;
hi[0] = 0;
tc_wmb();
hi[2] = 0;
tc_wmb();
if (i == 3) /* 3 == vblank */
state |= STIC_INT_V;
}
#endif
/* Vertical retrace interrupt. */
if (state & STIC_INT_V) {
stic->ipdvint = STIC_INT_V_WE | (stic->ipdvint & STIC_INT_V_EN);
tc_wmb();
caught = 1;
if (pxi->pxi_dirty)
px_bt459_flush(pxi);
}
/* Packet interrupt. Clear packet done flag. */
if (state & STIC_INT_P) {
stic->ipdvint = STIC_INT_P_WE | (stic->ipdvint & STIC_INT_P_EN);
tc_wmb();
caught = 1;
}
#ifdef notyet
/* Error/stray interrupt */
if ((stic->ipdvint & STIC_INT_E) || !caught) {
printf("px%d: %s intr, %x %x %x %x %x", pxi->pxi_unit,
(caught ? "error" : "stray"), stic->ipdvint, stic->sticsr,
stic->buscsr, stic->busadr, stic->busdat);
}
#endif
/* Abort if no packets are in the queue */
if (pxi->pxi_lpr == pxi->pxi_lpw) {
pxi->pxi_flg &= ~PX_ISR_ACTIVE;
return (0);
}
/* Abort if we're awaiting reload of cliplist */
if (pxi->pxi_flg & PX_ISR_LOAD_CLIP)
return (0);
cl = &pxi->pxi_cliplist;
/* Does this new packet need to be clipped? */
if (cl->cl_loaded != 0 && (pxi->pxi_flg & PX_ISR_PASS_CLIP) == 0) {
int32_t *buf;
buf = (int32_t *)pxi->pxi_rbuf + (pxi->pxi_lpr & 15) * 1024;
if (*buf & STAMP_CLIPRECT) {
pxi->pxi_flg |= PX_ISR_PASS_CLIP;
/* Figure out where in the packet fixup should occur */
cl->cl_fixup = buf + 4;
/* XXX this computation is wrong... */
if (*buf & STAMP_XY_PERPACKET)
cl->cl_fixup++;
if (*buf & STAMP_LW_PERPACKET)
cl->cl_fixup++;
cl->cl_cur = 0;
}
}
/* Need to fix up packet with next cliprect? */
if (pxi->pxi_flg & PX_ISR_PASS_CLIP) {
cl->cl_fixup[0] = cl->cl_minval[cl->cl_cur];
cl->cl_fixup[1] = cl->cl_maxval[cl->cl_cur];
/* Need more cliprects/finished? */
if ((cl->cl_cur + 1) >= cl->cl_loaded) {
if (cl->cl_notloaded != 0)
pxi->pxi_flg |= PX_ISR_LOAD_CLIP;
else
pxi->pxi_flg &= ~PX_ISR_PASS_CLIP;
}
}
/* Fire off the packet and move to next cliprect OR next packet */
if (*pxi->pxi_qpoll[pxi->pxi_lpr & 15] == STAMP_OK) {
if (pxi->pxi_flg & PX_ISR_PASS_CLIP)
cl->cl_cur++;
else
pxi->pxi_lpr++;
}
return (0);
}
/*
* Allocate a PixelStamp packet buffer.
*/
static inline int32_t *
px_alloc_pbuf(pxi)
struct px_info *pxi;
{
volatile int32_t *poll;
int32_t *buf;
int i, j;
/* Use queueing if the ISR is enabled */
if (pxi->pxi_flg & PX_ISR_ENABLE) {
/* Wait until we have a free buffer */
for (i = STAMP_RETRIES; i; i--) {
if (pxi->pxi_lpw - pxi->pxi_lpr < 15)
break;
DELAY(STAMP_DELAY);
}
/*
* Dequeue stalled packets manually. This should only
* ever happen during prelonged periods at splhigh().
* Autoconfiguration time is an example.
*/
if (i == 0) {
for (i = pxi->pxi_lpr; i < pxi->pxi_lpw; i++) {
poll = pxi->pxi_qpoll[i & 15];
for (j = STAMP_RETRIES; j; j--) {
if (*poll == STAMP_OK)
break;
DELAY(STAMP_DELAY);
}
}
pxi->pxi_lpr = pxi->pxi_lpw;
}
return (int32_t *)((caddr_t)pxi->pxi_rbuf +
((pxi->pxi_lpw & 15) << 12));
}
#ifdef notdef
/* If this is a PXG, ask the damn i860 which buffer to use */
if (pxi->pxi_option) {
poll = (volatile int32_t *)pxi->pxi_slotbase;
poll += PXG_COPROC_INTR_OFFSET >> 2;
/*
* XXX these should be defined as constants. 0x30 is
* "pause coprocessor and interrupt."
*/
*poll = 0x30;
tc_wmb();
for (i = 1000000; i; i--) {
DELAY(4);
switch(j = *poll) {
case 2:
pxi->pxi_pbuf_select = 4096;
break;
case 1:
pxi->pxi_pbuf_select = 0;
break;
default:
if (j == 0x30)
continue;
break;
}
break;
}
if (j != 1 || j != 2) {
/* i860 has gone mad, punish it */
px_init_stic(pxi, 0);
pxi->pxi_pbuf_select = 0;
}
}
#endif
buf = (int32_t *)((u_long)pxi->pxi_rbuf + pxi->pxi_pbuf_select);
pxi->pxi_pbuf_select ^= 4096;
return buf;
}
/*
* Send a PixelStamp packet.
*/
static int
px_send_packet(pxi, buf)
struct px_info *pxi;
int32_t *buf;
{
volatile int32_t *poll;
int c;
if (pxi->pxi_flg & PX_ISR_ENABLE) {
struct stic_regs *stic;
pxi->pxi_lpw++;
stic = pxi->pxi_stic;
/* Generate a packet-done interrupt */
c = stic->ipdvint;
c |= (STIC_INT_P_EN | STIC_INT_P | STIC_INT_P_WE);
c &= ~(STIC_INT_E_WE | STIC_INT_V_WE);
stic->ipdvint = c;
tc_wmb();
return (0);
}
/* Convert buffer address to i860 physical address for PXG */
if (pxi->pxi_option)
buf = (int32_t *)(((long)buf-(long)pxi->pxi_rbuf) & ~0x40000);
/* Get address of poll register for this buffer */
poll = px_poll_addr((caddr_t)pxi->pxi_slotbase, buf);
/*
* Now check the poll register and make sure the stamp wants to
* accept our packet. This read will initiate the DMA. Don't
* wait for ever, just in case something's wrong.
*/
tc_wmb();
for (c = STAMP_RETRIES; c; c--) {
if (*poll == STAMP_OK) {
#ifdef notdef
/* Tell the i860 we are done */
if (pxi->pxi_option) {
poll = (volatile int32_t*)pxi->pxi_slotbase +
(PXG_HOST_INTR_OFFSET >> 2);
poll[0] = 0;
tc_wmb();
poll[2] = 0;
tc_wmb();
}
#endif
return (0);
}
DELAY(STAMP_DELAY);
}
/*
* Oops... The STIC seems to have died. Reinitialize it and hope
* for the best. This is a non destructive operation anyhow, except
* for the packet that we just dropped on the floor.
*/
px_init_stic(pxi, 0);
tc_wmb();
return (-*poll);
}
/*
* Draw a 'flat' rectangle
*/
static int
px_rect(pxi, x, y, w, h, color)
struct px_info *pxi;
int x, y, w, h, color;
{
int *pb, linewidth;
pb = px_alloc_pbuf(pxi);
linewidth = (h << 2) - 1;
y = (y << 3) + linewidth;
pb[0] = STAMP_CMD_LINES | STAMP_RGB_CONST | STAMP_LW_PERPACKET;
pb[1] = 0x01ffffff;
pb[2] = 0;
pb[3] = STAMP_UPDATE_ENABLE | STAMP_METHOD_COPY;
pb[4] = linewidth;
pb[5] = color;
pb[6] = (x << 19) | y;
pb[7] = ((((x + w) << 3) - 1) << 16) | y;
return px_send_packet(pxi, pb);
}
/*
* Allocate attribute. We just pack these into an integer.
*/
static int
px_allocattr(cookie, fg, bg, flags, attr)
void *cookie;
int fg, bg, flags;
long *attr;
{
int swap;
if (flags & (WSATTR_BLINK | WSATTR_UNDERLINE))
return (EINVAL);
if (flags & WSATTR_HILIT)
fg += 8;
if (flags & WSATTR_REVERSE) {
swap = fg;
fg = bg;
bg = swap;
}
*attr = fg | (bg << 8) | (flags << 16);
return 0;
}
/*
* Erase columns
*/
static void
px_erasecols(cookie, row, col, num, attr)
void *cookie;
int row, col, num;
long attr;
{
struct px_info *pxi = (struct px_info *)cookie;
int32_t *pb;
u_int linewidth;
col = (col * pxi->pxi_font->fontwidth) << 19;
num = (num * pxi->pxi_font->fontwidth) << 19;
row = row * pxi->pxi_font->fontheight;
pb = px_alloc_pbuf(pxi);
linewidth = (pxi->pxi_font->fontheight << 2) - 1;
row = (row << 3) + linewidth;
pb[0] = STAMP_CMD_LINES | STAMP_RGB_CONST | STAMP_LW_PERPACKET;
pb[1] = 0x01ffffff;
pb[2] = 0;
pb[3] = STAMP_UPDATE_ENABLE | STAMP_METHOD_COPY;
pb[4] = linewidth;
pb[5] = DUPBYTE1(attr);
pb[6] = col | row;
pb[7] = (col + num) | row;
px_send_packet(pxi, pb);
}
/*
* Erase rows
*/
static void
px_eraserows(cookie, row, num, attr)
void *cookie;
int row, num;
long attr;
{
struct px_info *pxi = (struct px_info *)cookie;
int32_t *pb;
int linewidth;
row *= pxi->pxi_font->fontheight;
num *= pxi->pxi_font->fontheight;
pb = px_alloc_pbuf(pxi);
linewidth = (num << 2) - 1;
row = (row << 3) + linewidth;
pb[0] = STAMP_CMD_LINES | STAMP_RGB_CONST | STAMP_LW_PERPACKET;
pb[1] = 0x01ffffff;
pb[2] = 0;
pb[3] = STAMP_UPDATE_ENABLE | STAMP_METHOD_COPY;
pb[4] = linewidth;
pb[5] = DUPBYTE1(attr);
pb[6] = row;
pb[7] = (1280 << 19) | row;
px_send_packet(pxi, pb);
}
/*
* Copy rows.
*/
static void
px_copyrows(cookie, src, dst, height)
void *cookie;
int src, dst, height;
{
struct px_info *pxi;
int32_t *pb, *pbs;
int num, inc, adj;
pxi = (struct px_info *)cookie;
/*
* We need to do this in reverse if the destination row is below
* the source.
*/
if (dst > src) {
src += height;
dst += height;
inc = -8;
adj = -1;
} else {
inc = 8;
adj = 0;
}
src = (src * pxi->pxi_font->fontheight + adj) << 3;
dst = (dst * pxi->pxi_font->fontheight + adj) << 3;
height *= pxi->pxi_font->fontheight;
while (height > 0) {
num = (height < 255 ? height : 255);
height -= num;
pb = pbs = px_alloc_pbuf(pxi);
/*
* We can use COPYSPAN_ALIGNED here to improve performance
* since the source and destination X co-ordinates are
* identical.
*/
*pb++ = STAMP_CMD_COPYSPANS | STAMP_LW_PERPACKET;
*pb++ = (num << 24) | 0xffffff;
*pb++ = 0x0;
*pb++ = STAMP_UPDATE_ENABLE |
STAMP_METHOD_COPY |
STAMP_SPAN |
STAMP_COPYSPAN_ALIGNED;
*pb++ = 1; /* linewidth */
for ( ; num > 0; num--, src += inc, dst += inc) {
*pb++ = 1280 << 3;
*pb++ = src;
*pb++ = dst;
}
px_send_packet(pxi, pbs);
}
}
/*
* Copy columns.
*/
static void
px_copycols(cookie, row, src, dst, num)
void *cookie;
int row, src, dst, num;
{
struct px_info *pxi = (struct px_info *)cookie;
int32_t *pb, *pbs;
int height, updword;
/*
* Due the fact that the stamp reads and writes left->right only,
* we need to "half-buffer" if the source and destination regions
* overlap, and the source is left of the destination. This is
* slower than just a simple copy.
*/
updword = STAMP_UPDATE_ENABLE | STAMP_METHOD_COPY | STAMP_SPAN;
if (src < dst && src + num > dst)
updword |= STAMP_HALF_BUFF;
row = (row * pxi->pxi_font->fontheight) << 3;
num = (num * pxi->pxi_font->fontwidth) << 3;
src = row | ((src * pxi->pxi_font->fontwidth) << 19);
dst = row | ((dst * pxi->pxi_font->fontwidth) << 19);
height = pxi->pxi_font->fontheight;
pb = pbs = px_alloc_pbuf(pxi);
*pb++ = STAMP_CMD_COPYSPANS | STAMP_LW_PERPACKET;
*pb++ = (height << 24) | 0xffffff;
*pb++ = 0x0;
*pb++ = updword;
*pb++ = 1; /* linewidth */
for ( ; height; height--, src += 8, dst += 8) {
*pb++ = num;
*pb++ = src;
*pb++ = dst;
}
px_send_packet(pxi, pbs);
}
/*
* Blit a character at the specified co-ordinates.
*/
static void
px_putchar(cookie, r, c, uc, attr)
void *cookie;
int r, c;
u_int uc;
long attr;
{
struct wsdisplay_font *font;
struct px_info *pxi;
int i, bgcolor, fgcolor;
unsigned short *fr; /* font row data */
int *pb; /* packet buffer ptr */
int v1; /* mask co-ords */
int v2; /* mask co-ords */
int xya; /* saved XY position */
/* Don't bother blitting the space character */
if (uc == ' ') {
px_erasecols(cookie, r, c, 1, attr);
return;
}
pxi = (struct px_info *)cookie;
font = pxi->pxi_font;
pb = px_alloc_pbuf(pxi);
pb[0] = STAMP_CMD_LINES |
STAMP_RGB_FLAT |
STAMP_XY_PERPRIMATIVE |
STAMP_LW_PERPRIMATIVE;
pb[2] = 0x0;
pb[3] = STAMP_UPDATE_ENABLE | STAMP_WE_XYMASK | STAMP_METHOD_COPY;
if (font->fontheight > 16)
pb[1] = 0x04ffffff;
else
pb[1] = 0x02ffffff;
r *= font->fontheight;
c *= font->fontwidth;
uc = (uc - font->firstchar) * pxi->pxi_fontscale;
fr = (u_short *)((caddr_t)font->data + uc);
bgcolor = DUPBYTE1(attr);
fgcolor = DUPBYTE0(attr);
i = (16 << 2) - 1;
v1 = (c << 19) | ((r << 3) + i);
v2 = ((c + font->fontwidth) << 19) | (v1 & 0xffff);
xya = XYMASKADDR(c, r, 0, 0);
pb[4] = PACK_WORD(fr, 0);
pb[5] = PACK_WORD(fr, 2);
pb[6] = PACK_WORD(fr, 4);
pb[7] = PACK_WORD(fr, 6);
pb[8] = PACK_WORD(fr, 8);
pb[9] = PACK_WORD(fr, 10);
pb[10] = PACK_WORD(fr, 12);
pb[11] = PACK_WORD(fr, 14);
pb[12] = xya;
pb[13] = v1;
pb[14] = v2;
pb[15] = i;
pb[16] = fgcolor;
/* opaque background mask */
pb[17] = ~pb[4];
pb[18] = ~pb[5];
pb[19] = ~pb[6];
pb[20] = ~pb[7];
pb[21] = ~pb[8];
pb[22] = ~pb[9];
pb[23] = ~pb[10];
pb[24] = ~pb[11];
pb[25] = xya;
pb[26] = v1;
pb[27] = v2;
pb[28] = i;
pb[29] = bgcolor;
if (font->fontheight > 16) {
i = ((font->fontheight - 16) << 2) - 1;
r += 16;
v1 = (c << 19) | ((r << 3) + i);
v2 = ((c + font->fontwidth) << 19) | (v1 & 0xffff);
/* lower part of fg character */
pb[30] = PACK_WORD(fr, 16);
pb[31] = PACK_WORD(fr, 18);
pb[32] = PACK_WORD(fr, 20);
pb[33] = PACK_WORD(fr, 22);
pb[34] = PACK_WORD(fr, 24);
pb[35] = PACK_WORD(fr, 26);
pb[36] = PACK_WORD(fr, 28);
pb[37] = PACK_WORD(fr, 30);
pb[38] = xya;
pb[39] = v1;
pb[40] = v2;
pb[41] = i;
pb[42] = fgcolor;
/* opaque background */
pb[43] = ~pb[30];
pb[44] = ~pb[31];
pb[45] = ~pb[32];
pb[46] = ~pb[33];
pb[47] = ~pb[34];
pb[48] = ~pb[35];
pb[49] = ~pb[36];
pb[50] = ~pb[37];
pb[51] = xya;
pb[52] = v1;
pb[53] = v2;
pb[54] = i;
pb[55] = bgcolor;
}
px_send_packet(pxi, pb);
}
/*
* Map a character.
*/
static int
px_mapchar(cookie, c, cp)
void *cookie;
int c;
u_int *cp;
{
struct px_info *pxi;
pxi = (struct px_info *)cookie;
if (c < pxi->pxi_font->firstchar) {
*cp = ' ';
return (0);
}
if (c - pxi->pxi_font->firstchar >= pxi->pxi_font->numchars) {
*cp = ' ';
return (0);
}
*cp = c;
return (5);
}
/*
* Position|{enable|disable} the cursor at the specified location.
*/
static void
px_cursor(cookie, on, row, col)
void *cookie;
int on, row, col;
{
struct px_info *pxi;
pxi = (struct px_info *)cookie;
#if 0
if (row < 0)
row = 0;
else if (row > pxi->pxi_max_row)
row = pxi->pxi_max_row;
if (col < 0)
col = 0;
else if (col > pxi->pxi_max_col)
col = pxi->pxi_max_col;
#endif
/*
* These magic offsets (370, 37) were obtained by looking at sfb.c
* and then bumping them until they worked right.
*/
pxi->pxi_curx = col * pxi->pxi_font->fontwidth + 370;
pxi->pxi_cury = row * pxi->pxi_font->fontheight + 37;
if (on)
pxi->pxi_flg |= PX_CURSOR_ENABLE;
else
pxi->pxi_flg &= ~PX_CURSOR_ENABLE;
pxi->pxi_dirty |= (PX_DIRTY_CURSOR_ENABLE | PX_DIRTY_CURSOR_POS);
if (pxi->pxi_option)
px_bt459_flush(pxi);
}
/*
* Move the cursor for qvss. This is disgusting.
*/
static void
px_cursor_hack(fi, x, y)
struct fbinfo *fi;
int x, y;
{
struct px_info *pxi;
pxi = (struct px_info *)fi->fi_base;
pxi->pxi_curx = x + 370;
pxi->pxi_cury = y + 37;
pxi->pxi_dirty |= PX_DIRTY_CURSOR_POS;
if (pxi->pxi_option)
px_bt459_flush(pxi);
}
int
pxopen(dev, flag, mode, p)
dev_t dev;
int flag, mode;
struct proc *p;
{
extern struct fbinfo *firstfi; /* XXX */
struct stic_regs *stic;
struct px_info *pxi;
int s;
if (minor(dev) >= NPX || px_unit[minor(dev)] == NULL)
return (ENXIO);
pxi = px_unit[minor(dev)];
if (pxi->pxi_flg & PX_OPEN)
return (EBUSY);
pxi->pxi_flg = (pxi->pxi_flg | PX_OPEN) & ~PX_CURSOR_ENABLE;
pxi->pxi_flg &= ~PX_ISR_MASK;
pxi->pxi_dirty |= PX_DIRTY_CURSOR_ENABLE;
pxi->pxi_fbinfo.fi_open = 1;
/*
* Set up event queue for later
*/
firstfi = &pxi->pxi_fbinfo; /* XXX */
pmEventQueueInit(&pxi->pxi_fbuaccess.scrInfo.qe);
genConfigMouse();
/* Turn packet-done interrupts on */
stic = pxi->pxi_stic;
s = stic->ipdvint | STIC_INT_P_WE | STIC_INT_P_EN;
s &= ~(STIC_INT_E_WE | STIC_INT_V_WE | STIC_INT_P);
stic->ipdvint = s;
tc_wmb();
return (0);
}
int
pxclose(dev, flag, mode, p)
dev_t dev;
int flag, mode;
struct proc *p;
{
extern struct fbinfo *firstfi; /* XXX */
struct stic_regs *stic;
struct px_info *pxi;
int s;
if (minor(dev) >= NPX || px_unit[minor(dev)] == NULL)
return (EBADF);
pxi = px_unit[minor(dev)];
if ((pxi->pxi_flg & PX_OPEN) == 0)
return (EBADF);
pxi->pxi_flg = (pxi->pxi_flg & ~PX_OPEN) | PX_ENABLE;
pxi->pxi_fbinfo.fi_open = 0;
genDeconfigMouse();
firstfi = NULL;
/* Disable interrupt driven operation */
s = splhigh();
pxi->pxi_lpw = 0;
pxi->pxi_lpr = 0;
pxi->pxi_flg &= ~PX_ISR_MASK;
splx(s);
/* Turn packet-done interrupts off */
stic = pxi->pxi_stic;
s = stic->ipdvint | STIC_INT_P_WE;
s &= ~(STIC_INT_E_WE | STIC_INT_V_WE | STIC_INT_P_EN);
stic->ipdvint = s;
tc_wmb();
px_init_stic(pxi, 0);
px_bt459_init(pxi);
px_rect(pxi, 0, 0, 1280, 1024, 0);
pxi->pxi_dirty |= PX_DIRTY_ENABLE; /* make sure video is enabled */
if (pxi->pxi_option)
px_bt459_flush(pxi);
return (0);
}
int
pxioctl(dev, cmd, data, flag, p)
dev_t dev;
u_long cmd;
caddr_t data;
int flag;
struct proc *p;
{
struct pmax_fbtty *fbtty;
struct px_info *pxi;
pmKpCmd *kpCmdPtr;
u_char *cp;
u_int *ptr;
int i;
if (minor(dev) >= NPX || px_unit[minor(dev)] == NULL)
return (EBADF);
pxi = px_unit[minor(dev)];
fbtty = pxi->pxi_fbinfo.fi_glasstty;
if ((pxi->pxi_flg & PX_OPEN) == 0)
return (EBADF);
switch (cmd) {
case QIOCGINFO:
/*
* Map card info.
*/
return (px_mmap_info(p, dev, (vaddr_t *)data));
case QIOCPMSTATE:
/*
* Set mouse state.
*/
pxi->pxi_curx = ((pmCursor *)data)->x + 370;
pxi->pxi_cury = ((pmCursor *)data)->y + 37;
pxi->pxi_dirty |= PX_DIRTY_CURSOR_POS;
break;
case QIOCINIT:
px_rect(pxi, 0, 0, 1280, 1024, 0);
break;
case QIOVIDEOON:
pxi->pxi_flg |= PX_ENABLE;
pxi->pxi_dirty |= PX_DIRTY_ENABLE;
break;
case QIOVIDEOOFF:
pxi->pxi_flg &= ~PX_ENABLE;
pxi->pxi_dirty |= PX_DIRTY_ENABLE;
break;
case QIOWCURSOR:
px_conv_cursor(pxi, (u_short *)data);
pxi->pxi_flg |= PX_CURSOR_ENABLE;
pxi->pxi_dirty |= PX_DIRTY_CURSOR | PX_DIRTY_CURSOR_ENABLE;
break;
case QIOWCURSORCOLOR:
ptr = (u_int *)data;
for (i = 0; i < 6; i++)
pxi->pxi_curcmap[i] = *ptr++ >> 8;
pxi->pxi_dirty |= PX_DIRTY_CURSOR_CMAP;
break;
case QIOSETCMAP:
i = ((ColorMap *)data)->index;
if (i < 0 || i > 255)
return (-1);
pxi->pxi_cmap_idx = i;
pxi->pxi_cmap_cnt = 1;
i = (i << 1) + i;
pxi->pxi_cmap[i+0] = ((ColorMap *)data)->Entry.red;
pxi->pxi_cmap[i+1] = ((ColorMap *)data)->Entry.green;
pxi->pxi_cmap[i+2] = ((ColorMap *)data)->Entry.blue;
pxi->pxi_dirty |= PX_DIRTY_CMAP;
break;
case QIOCKPCMD:
/*
* Keyboard command.
*/
kpCmdPtr = (pmKpCmd *)data;
if (kpCmdPtr->nbytes == 0)
kpCmdPtr->cmd |= 0x80;
(*fbtty->KBDPutc)(fbtty->kbddev, (int)kpCmdPtr->cmd);
cp = &kpCmdPtr->par[0];
for (; kpCmdPtr->nbytes > 0; cp++, kpCmdPtr->nbytes--) {
if (kpCmdPtr->nbytes == 1)
*cp |= 0x80;
(*fbtty->KBDPutc)(fbtty->kbddev, (int)*cp);
}
break;
case QIOKERNLOOP:
genConfigMouse();
break;
case QIOKERNUNLOOP:
genDeconfigMouse();
break;
default:
printf("px%d: unknown ioctl %lx\n", minor(dev), cmd);
return (EINVAL);
}
return (0);
}
int
pxpoll(dev, events, p)
dev_t dev;
int events;
struct proc *p;
{
struct fbinfo *fi = &px_unit[minor(dev)]->pxi_fbinfo;
int revents = 0;
if (events & (POLLIN | POLLRDNORM)) {
if (fi->fi_fbu->scrInfo.qe.eHead !=
fi->fi_fbu->scrInfo.qe.eTail)
revents |= (events & (POLLIN | POLLRDNORM));
else
selrecord(p, &fi->fi_selp);
}
return (revents);
}
static void
filt_pxrdetach(struct knote *kn)
{
struct fbinfo *fi = kn->kn_hook;
int s;
s = spltty();
SLIST_REMOVE(&fi->fi_selp.sel_klist, kn, knote, kn_selnext);
splx(s);
}
static int
filt_pxread(struct knote *kn, long hint)
{
struct fbinfo *fi = kn->kn_hook;
if (fi->fi_fbu->scrInfo.qe.eHead == fi->fi_fbu->scrInfo.qe.eTail)
return (0);
kn->kn_data = 0; /* XXXLUKEM (thorpej): what to put here? */
return (1);
}
static const struct filterops pxread_filtops =
{ 1, NULL, filt_pxrdetach, filt_pxread };
int
pxkqfilter(dev_t dev, struct knote *kn)
{
struct fbinfo *fi = &px_unit[minor(dev)]->pxi_fbinfo;
struct klist *klist;
int s;
switch (kn->kn_filter) {
case EVFILT_READ:
klist = &fi->fi_selp.sel_klist;
kn->kn_fop = &pxread_filtops;
break;
default:
return (1);
}
kn->kn_hook = fi;
s = spltty();
SLIST_INSERT_HEAD(klist, kn, kn_selnext);
splx(s);
return (0);
}
paddr_t
pxmmap(dev, off, prot)
dev_t dev;
off_t off;
int prot;
{
struct px_info *pxi;
if (minor(dev) >= NPX || px_unit[minor(dev)] == NULL)
return (EBADF);
pxi = px_unit[minor(dev)];
if ((pxi->pxi_flg & PX_OPEN) == 0)
return (EBADF);
/*
* STIC control registers
*/
if (off < PAGE_SIZE)
return mips_btop(MIPS_KSEG1_TO_PHYS(pxi->pxi_stic) + off);
off -= PAGE_SIZE;
/*
* STIC poll registers
*/
if (off < sizeof(int32_t) * 4096)
return mips_btop(MIPS_KSEG1_TO_PHYS(pxi->pxi_poll) + off);
off -= sizeof(int32_t) * 4096;
/*
* 'struct px_info' and ringbuffer
*/
if (off < PXMAP_INFO_SIZE + PXMAP_RBUF_SIZE)
return mips_btop(MIPS_KSEG1_TO_PHYS(pxi) + off);
off -= (PXMAP_INFO_SIZE + PXMAP_RBUF_SIZE);
return (-1);
}
/*
* mmap info struct for this card into userspace.
*/
static int
px_mmap_info (p, dev, va)
struct proc *p;
dev_t dev;
vaddr_t *va;
{
struct specinfo si;
struct vnode vn;
vm_prot_t prot;
vsize_t size;
int flags;
vn.v_type = VCHR; /* XXX */
vn.v_specinfo = &si; /* XXX */
vn.v_rdev = dev; /* XXX */
size = sizeof(struct px_map);
prot = VM_PROT_READ | VM_PROT_WRITE;
flags = MAP_SHARED | MAP_FILE;
*va = VM_DEFAULT_ADDRESS(p->p_vmspace->vm_daddr, size);
return uvm_mmap(&p->p_vmspace->vm_map, va, size, prot,
VM_PROT_ALL, flags, &vn, 0, p->p_rlimit[RLIMIT_MEMLOCK].rlim_cur);
}