Subject: Accelerated LCSPX driver
To: None <port-vax@NetBSD.org>
From: Blaz Antonic <blaz.antonic@siol.net>
List: port-vax
Date: 08/16/2004 08:57:15
This is a multi-part message in MIME format.
--------------1E262080FE2
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Hello,
Attached is HW accelerated LCSPX driver (for Vaxstation 4000/9x class
machines). It is a drop-in replacement for existing dumb framebuffer
driver, offers the same functionality and much more (full palette
support, hardware cursor, HW acceleration, everything needed to get X
running in unaccelerated mode, user-defined font support, etc.). It has
been tested under NetBSD 2.0_beta.
While this driver is complete as far as console operation goes it still
has few chunks of debugging code inside (which is commented out) and it
hasn't been optimised yet. It is also missing HW acceleration support
for X which will be added before i finish working on it so there's at
least one more release coming up sometime in the future.
I would really appreciate it if people could try it out. Please try out
different font types to see which one suits you best (and to flush out
any bugs that might still be lurking in the driver, possibly with cursor
which is the least tested part). Despite being "beta" this driver can
cause no harm to your hardware, there's no risk involved in trying it
out, it's considerably faster than existing driver so there's no reason
for you not to try it if you have suitable hardware :-)
Note that since LCSPX doesn't seem to suypport any kind of mechanism
that would allow asynchronous requests to be issued to hardware (at
least the way i understand it, there are no docs avaliable on the
subject) the kernel must sit idle while HW operation is in progress -
this means that heavy screen output with lots of font rendering, the
slowest part of the driver that could use some hand optimisations, will
clog the machine somewhat, but much less than the existing driver would.
The slowdown is still noticable and unfortunately there is no way to
avoid it. Pre-rendering font glyphs with intention to copy them in as
needed doesn't seem like a good option since block copy operation
requires quite some setup and takes about as many I/O cycles as
rendering of one glyph does.
Last, but by no means least important: *many* thanks go to Michael Hitch
who's been weeding out the bugs i carefully planted inside :-) I don't
think i would ever make it this far without your help mate.
Blaz Antonic
--
Hi! I'm a signature virus!
Copy me into your signature to help me spread!
--------------1E262080FE2
Content-Type: text/plain; charset=us-ascii; name="LCSPX.C"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="LCSPX.C"
/* $NetBSD: lcspx.c,v 1.2 2004/16/08 09:49:00 Blaz Antonic Exp $ */
/*
* LCSPX accelerated framebuffer driver
* Copyright (c) 2004 Blaz Antonic
* All rights reserved.
*
* This software contains code written by Michael L. Hitch.
*
* 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 abovementioned copyrights
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: lcspx.c,v 1.2 2004/16/08 09:49:00 Blaz Antonic Exp $");
#include <sys/param.h>
#include <sys/device.h>
#include <sys/systm.h>
#include <sys/callout.h>
#include <sys/time.h>
#include <sys/malloc.h>
#include <sys/conf.h>
#include <sys/kernel.h>
#include <uvm/uvm.h>
#include <machine/vsbus.h>
#include <machine/sid.h>
#include <machine/cpu.h>
#include <dev/cons.h>
#include <dev/dec/dzreg.h>
#include <dev/dec/dzvar.h>
#include <dev/dec/dzkbdvar.h>
#include <dev/wscons/wsdisplayvar.h>
#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wscons_callbacks.h>
#include <dev/wsfont/wsfont.h>
#include "machine/scb.h"
#include "dzkbd.h"
/* Screen hardware defs */
#define LCSPX_FB_ADDR 0x38000000 /* Frame buffer */
/* register space defines */
#define LCSPX_REG_ADDR 0x39302000 /* 1st set of LCSPX registers */
#define LCSPX_REG_SIZE 0x00002000
#define LCSPX_REG(reg) regaddr[((reg - 0x2000) / 4)]
#define LCSPX_REG1_ADDR 0x39b00000 /* 2nd set of LCSPX registers */
#define LCSPX_REG1_SIZE 0x00001000
#define LCSPX_REG1(reg) regaddr1[(reg / 4)]
/* bt459 locations */
#define LCSPX_BT459_ADDRL 0x39b10000
#define LCSPX_BT459_ADDRH 0x39b14000
#define LCSPX_BT459_REG 0x39b18000
#define LCSPX_BT459_CMAP 0x39b1c000
#define LCSPX_BG_COLOR WSCOL_BLUE
#define LCSPX_FG_COLOR WSCOL_WHITE
/* these are NOT palette color but 4 pixels of cursor sprite, fg/bg respectively */
#define CURSOR_FG 0xff
#define CURSOR_BG 0x00
#define CUR_XBIAS 360
#define CUR_YBIAS 35
/* few BT459 indirect register defines */
#define BT459_REG_CCOLOR_1 0x181
#define BT459_REG_CCOLOR_2 0x182
#define BT459_REG_CCOLOR_3 0x183
#define BT459_REG_ID 0x200
#define BT459_REG_CMD0 0x201
#define BT459_REG_CMD1 0x202
#define BT459_REG_CMD2 0x203
#define BT459_REG_PRM 0x204
#define BT459_REG_CCR 0x300
#define BT459_REG_CXLO 0x301
#define BT459_REG_CXHI 0x302
#define BT459_REG_CYLO 0x303
#define BT459_REG_CYHI 0x304
#define BT459_REG_WXLO 0x305
#define BT459_REG_WXHI 0x306
#define BT459_REG_WYLO 0x307
#define BT459_REG_WYHI 0x308
#define BT459_REG_WWLO 0x309
#define BT459_REG_WWHI 0x30a
#define BT459_REG_WHLO 0x30b
#define BT459_REG_WHHI 0x30c
#define BT459_REG_CRAM_BASE 0x400
/* used to access top/bottom 8 bits of a 16-bit argument for BT459 split addressing */
#define HI(x) (((x) >> 8) & 0xff)
#define LO(x) ((x) & 0xff)
/* implement sanity checks at certain points to ensure safer operation */
#define LCSPX_SAFE
static int lcspx_match(struct device *, struct cfdata *, void *);
static void lcspx_attach(struct device *, struct device *, void *);
struct lcspx_softc {
struct device ss_dev;
};
CFATTACH_DECL(lcspx, sizeof(struct lcspx_softc),
lcspx_match, lcspx_attach, NULL, NULL);
static void lcspx_cursor(void *, int, int, int);
static int lcspx_mapchar(void *, int, unsigned int *);
static void lcspx_putchar(void *, int, int, u_int, long);
static void lcspx_copycols(void *, int, int, int,int);
static void lcspx_erasecols(void *, int, int, int, long);
static void lcspx_copyrows(void *, int, int, int);
static void lcspx_eraserows(void *, int, int, long);
static int lcspx_allocattr(void *, int, int, int, long *);
static int lcspx_get_cmap(struct wsdisplay_cmap *);
static int lcspx_set_cmap(struct wsdisplay_cmap *);
static void lcspx_init_common(struct device *, struct vsbus_attach_args *);
#if 0
static void showmem(long addr, u_int xsize, u_int ysize);
#endif
const struct wsdisplay_emulops lcspx_emulops = {
lcspx_cursor,
lcspx_mapchar,
lcspx_putchar,
lcspx_copycols,
lcspx_erasecols,
lcspx_copyrows,
lcspx_eraserows,
lcspx_allocattr
};
static char lcspx_stdscreen_name[10] = "160x68";
struct wsscreen_descr lcspx_stdscreen = {
lcspx_stdscreen_name, 160, 68, /* dynamically set */
&lcspx_emulops,
8, 15, /* dynamically set */
WSSCREEN_UNDERLINE|WSSCREEN_REVERSE|WSSCREEN_WSCOLORS,
};
const struct wsscreen_descr *_lcspx_scrlist[] = {
&lcspx_stdscreen,
};
const struct wsscreen_list lcspx_screenlist = {
sizeof(_lcspx_scrlist) / sizeof(struct wsscreen_descr *),
_lcspx_scrlist,
};
static char *lcspxaddr;
static volatile long *regaddr;
static volatile long *regaddr1;
static char *bt_addrl;
static char *bt_addrh;
static char *bt_reg;
static char *bt_cmap;
static int lcspx_xsize;
static int lcspx_ysize;
static int lcspx_depth;
static int lcspx_cols;
static int lcspx_rows;
static int lcspx_onerow;
static long lcspx_fb_size;
/* Our current hardware colormap */
static struct hwcmap256 {
#define CMAP_SIZE 256 /* 256 R/G/B entries */
u_int8_t r[CMAP_SIZE];
u_int8_t g[CMAP_SIZE];
u_int8_t b[CMAP_SIZE];
} lcspx_cmap;
/* The default colormap */
static struct {
u_int8_t r[8];
u_int8_t g[8];
u_int8_t b[8];
} lcspx_default_cmap = {
{ 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
{ 0x00, 0x00, 0xff, 0x7f, 0x00, 0x00, 0xff, 0xff },
{ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff }
};
static char cons_curmap[1024];
struct wsdisplay_font lcspx_font;
static u_char *qf;
static u_short *qf2;
#define QCHAR(c) (c < lcspx_font.firstchar ? 0 : \
(c >= (lcspx_font.firstchar + lcspx_font.numchars) ? 0 : c - lcspx_font.firstchar))
#define QFONT(c,line) ((lcspx_font.stride == 2 ? \
qf2[QCHAR(c) * lcspx_font.fontheight + line] : \
qf[QCHAR(c) * lcspx_font.fontheight + line]))
#define LCSPX_ADDR(row, col, line, dot) \
lcspxaddr[((col) * lcspx_font.fontwidth) + ((row) * lcspx_font.fontheight * lcspx_xsize) + \
(line) * lcspx_xsize + dot]
static int lcspx_ioctl(void *, u_long, caddr_t, int, struct proc *);
static paddr_t lcspx_mmap(void *, off_t, int);
static int lcspx_alloc_screen(void *, const struct wsscreen_descr *,
void **, int *, int *, long *);
static void lcspx_free_screen(void *, void *);
static int lcspx_show_screen(void *, void *, int,
void (*) (void *, int, int), void *);
static void lcspx_crsr_blink(void *);
static void lcspx_update_cmap(int entry, char red, char green, char blue);
static void set_btreg(u_int addr, u_char value);
static u_char get_btreg(u_int addr);
/* LCSPX HW accel functions */
static void blkcpy(u_int sxpos, u_int sypos, u_int dxpos, u_int dypos, u_int xdim, u_int ydim);
static void blkset(u_int xpos, u_int ypos, u_int xdim, u_int ydim, char color);
const struct wsdisplay_accessops lcspx_accessops = {
lcspx_ioctl,
lcspx_mmap,
lcspx_alloc_screen,
lcspx_free_screen,
lcspx_show_screen,
0 /* load_font */
};
/* TODO allocate ss_image dynamically for consoles beyond first one */
struct lcspx_screen {
int ss_curx;
int ss_cury;
int ss_cur_fg;
int ss_cur_bg;
struct {
u_char data; /* Image character */
u_char attr; /* Attribute: 80/70/08/07 */
} ss_image[160 * 128]; /* allow for maximum possible cell matrix */
};
#define LCSPX_ATTR_UNDERLINE 0x80
#define LCSPX_BG_MASK 0x70
#define LCSPX_ATTR_REVERSE 0x08
#define LCSPX_FG_MASK 0x07
static struct lcspx_screen lcspx_conscreen;
static struct lcspx_screen *curscr;
static struct lcspx_screen *savescr;
static struct callout lcspx_cursor_ch = CALLOUT_INITIALIZER;
static int cur_fg, cur_bg;
void lcspx_update_cmap(int entry, char red, char green, char blue)
{
bt_addrl[0] = LO(entry);
bt_addrh[0] = HI(entry);
if ((entry >= 0x181) || (entry <= 0x183))
{
bt_reg[0] = red;
bt_reg[0] = green;
bt_reg[0] = blue;
} else {
bt_cmap[0] = red;
bt_cmap[0] = green;
bt_cmap[0] = blue;
}
}
void set_btreg(u_int addr, u_char value)
{
bt_addrl[0] = LO(addr);
bt_addrh[0] = HI(addr);
bt_reg[0] = value;
}
u_char get_btreg(u_int addr)
{
bt_addrl[0] = LO(addr);
bt_addrh[0] = HI(addr);
return bt_reg[0] & 0xff;
}
void blkcpy(u_int sxpos, u_int sypos, u_int dxpos, u_int dypos, u_int xdim, u_int ydim)
{
u_int counter = 0xfffe;
long temp = 0;
LCSPX_REG(0x21ec) = 0x84884648;
// LCSPX_REG(0x2260) = 0xffffffff;
// LCSPX_REG(0x219c) = 0xffaa5501;
LCSPX_REG(0x21d0) = dxpos << 16;
LCSPX_REG(0x21d4) = dypos << 16;
LCSPX_REG(0x21d8) = (dxpos + xdim) << 16;
LCSPX_REG(0x21dc) = (dypos + ydim) << 16;
temp = ((LCSPX_REG1(0x10) & 0xc0) << (30 - 6)) | ((LCSPX_REG1(0x10) & 0x3f) << 24);
LCSPX_REG(0x21e0) = (temp + (dypos * lcspx_xsize) + dxpos);
// LCSPX_REG(0x20e0) = 0xff000000;
LCSPX_REG(0x21e4) = (temp + (sypos * lcspx_xsize) + sxpos);
LCSPX_REG(0x20e4) = 0;
// LCSPX_REG(0x21e8) = (lcspx_xsize << 16) | lcspx_xsize;
LCSPX_REG(0x21f0) = 0xff;
LCSPX_REG(0x21f4) = 0xff;
if ((LCSPX_REG(0x2100) >> 24) == 0) {
printf("blkcpy: going the long way !!!\n");
LCSPX_REG(0x2268) = 0x002223b4;
LCSPX_REG(0x226c) = 0x00b620ac;
LCSPX_REG(0x2270) = 0x00272003;
LCSPX_REG(0x2274) = 0x601020af;
LCSPX_REG(0x227c) = 0x2010e010;
LCSPX_REG(0x23c8) = 0x012c0000;
LCSPX_REG(0x21fc) = 0xa9;
} else {
LCSPX_REG(0x2270) = 0x00112003; // CHECKME !!!
// LCSPX_REG(0x2270) = ((0x2014 << 16) + 0x2003);
LCSPX_REG(0x21fc) = 0x201a;
}
LCSPX_REG1(0x18) = 0xffffffff;
while ((counter) && ((LCSPX_REG1(0x18) & 2) == 0))
counter--;
}
void blkset(u_int xpos, u_int ypos, u_int xdim, u_int ydim, char color)
{
u_int counter = 0xfffe;
long temp = 0;
LCSPX_REG(0x21ec) = 0x84884608;
LCSPX_REG(0x21d0) = xpos << 16;
LCSPX_REG(0x21d4) = ypos << 16;
LCSPX_REG(0x21d8) = (xpos + xdim) << 16;
LCSPX_REG(0x21dc) = (ypos + ydim) << 16;
// LCSPX_REG(0x21e8) = (lcspx_xsize << 16) | lcspx_xsize;
LCSPX_REG(0x2270) = 0x20102003;
temp = ((LCSPX_REG1(0x10) & 0xc0) << (30 - 6)) | ((LCSPX_REG1(0x10) & 0x3f) << 24);
temp += (xpos + (ypos * lcspx_xsize));
LCSPX_REG(0x21e0) = temp;
// LCSPX_REG(0x20e0) = 0xff000000;
/* CHECKME: do we need datapath width case at all ? */
LCSPX_REG(0x2260) = (color << 24) | (color << 16) | (color << 8) | color;
LCSPX_REG(0x21fc) = 0x00002019;
LCSPX_REG1(0x18) = 0xffffffff;
while ((counter) && ((LCSPX_REG1(0x18) & 2) == 0))
counter--;
}
int
lcspx_match(struct device *parent, struct cfdata *match, void *aux)
{
struct vsbus_softc *sc = (void *)parent;
#if 0
struct vsbus_attach_args *va = aux;
char *ch = (char *)va->va_addr;
#endif
if (vax_boardtype != VAX_BTYP_49)
return 0;
#if 0 /* FIXME are memory addresses besides va->va_addr off limits at this time ? */
/* BT459 ID register test, should always read 0x4a */
if (get_btreg(BT459_REG_ID) != 0x4a)
return 0;
#endif
#if 0
*ch = 1;
if ((*ch & 1) == 0)
return 0;
*ch = 0;
if ((*ch & 1) != 0)
return 0;
#endif
sc->sc_mask = 0x04; /* XXX - should be generated */
scb_fake(0x120, 0x15);
return 20;
}
#if 0
void showmem(long addr, u_int xsize, u_int ysize)
{
u_int i, j;
for (i = 0; i < ysize; i++) {
printf("0x%x: ", (u_int)(LCSPX_FB_ADDR + addr + i * lcspx_xsize));
for (j = 0; j < xsize; j++)
printf("%02x ", (lcspxaddr[addr + (i * lcspx_xsize) + j] & 0xff));
printf("\n");
}
}
#endif
void
lcspx_attach(struct device *parent, struct device *self, void *aux)
{
struct vsbus_attach_args *va = aux;
struct wsemuldisplaydev_attach_args aa;
#if 0
u_int i, j, k;
#endif
printf("\n");
aa.console = lcspxaddr != NULL;
lcspx_init_common(self, va);
curscr = &lcspx_conscreen;
aa.scrdata = &lcspx_screenlist;
aa.accessops = &lcspx_accessops;
/* enable cursor */
callout_reset(&lcspx_cursor_ch, hz / 2, lcspx_crsr_blink, NULL);
config_found(self, &aa, wsemuldisplaydevprint);
#if 0
/* display colorbar */
for (i = 0; i <256; i++)
for (j = 400; j < 600; j++)
for (k = 0; k < 5; k++)
if (k != 4)
lcspxaddr[(j * lcspx_xsize) + (i * 5) + k] = i;
// delay(3 * 1000 * 1000);
for (i = 1; i < 256; i++)
lcspx_update_cmap(i, 255 - i, 255 - i, 255 - i);
#endif
#if 0
// LCSPX_REG(0x316c) = LCSPX_REG(0x316c) | 0x20000;
for (i = 0; i < 16; i++)
for (j = 0; j < 16; j++)
lcspxaddr[((i + 50) * lcspx_xsize) + j + 100] = (i << 4) | j;
showmem((50 * lcspx_xsize) + 100, 16, 16);
k = 0;
// delay(3 * 1000 * 1000);
#endif
// LCSPX_REG(0x316c) = LCSPX_REG(0x316c) | 0xfffdffff;
#if 0
showmem(0, 10, 10);
blkcpy(1, 1, 5, 5, 4, 4);
printf("\n");
showmem(0, 10, 10);
#endif
}
static char *cursor;
static int cur_on;
static int cur_status = 0;
static void
lcspx_crsr_blink(void *arg)
{
#ifdef SOFTCURSOR
int dot;
for (dot = 0; dot < lcspx_font.fontwidth; dot++)
cursor[dot] = ((cursor[dot] & 0x0f) == cur_fg) ? cur_bg : cur_fg;
#else
if (cur_on && curscr != NULL) {
set_btreg(BT459_REG_CCR, (cur_status == 0) ? 0xc0 : 0x00);
cur_status = !cur_status;
}
#endif
callout_reset(&lcspx_cursor_ch, hz / 2, lcspx_crsr_blink, NULL);
}
void
lcspx_cursor(void *id, int on, int row, int col)
{
struct lcspx_screen *ss = id;
int attr;
#ifdef SOFTCURSOR
int dot;
#endif
attr = ss->ss_image[row * lcspx_cols + col].attr;
if (ss == curscr) {
if (cursor != NULL) {
#ifdef SOFTCURSOR
int ch = QFONT(ss->ss_image[ss->ss_cury * lcspx_cols +
ss->ss_curx].data, lcspx_font.fontheight - 1);
#endif
int attr = ss->ss_image[ss->ss_cury * lcspx_cols +
ss->ss_curx].attr;
if (attr & LCSPX_ATTR_REVERSE) {
cur_bg = attr & LCSPX_FG_MASK;
cur_fg = (attr & LCSPX_BG_MASK) >> 4;
} else {
cur_fg = attr & LCSPX_FG_MASK;
cur_bg = (attr & LCSPX_BG_MASK) >> 4;
}
#ifdef SOFTCURSOR
for (dot = 0; dot < lcspx_font.fontwidth; dot++)
cursor[dot] = (ch & (1 << dot)) ?
cur_fg : cur_bg;
#else
/* change cursor foreground color colormap entry */
lcspx_update_cmap(BT459_REG_CCOLOR_3,
lcspx_cmap.r[cur_fg],
lcspx_cmap.g[cur_fg],
lcspx_cmap.b[cur_fg]);
#if 0
set_btreg(BT459_REG_CCOLOR_3, lcspx_cmap.r[cur_fg]);
bt_reg[0] = lcspx_cmap.g[cur_fg];
bt_reg[0] = lcspx_cmap.b[cur_fg];
#endif
/* update cursor position registers */
set_btreg(BT459_REG_CXLO, LO(col * lcspx_font.fontwidth + CUR_XBIAS));
set_btreg(BT459_REG_CXHI, HI(col * lcspx_font.fontwidth + CUR_XBIAS));
set_btreg(BT459_REG_CYLO, LO(row * lcspx_font.fontheight + CUR_YBIAS));
set_btreg(BT459_REG_CYHI, HI(row * lcspx_font.fontheight + CUR_YBIAS));
#endif
}
cursor = &LCSPX_ADDR(row, col, lcspx_font.fontheight - 1, 0);
cur_on = on;
if (attr & LCSPX_ATTR_REVERSE) {
cur_bg = attr & LCSPX_FG_MASK;
cur_fg = (attr & LCSPX_BG_MASK) >> 4;
} else {
cur_fg = attr & LCSPX_FG_MASK;
cur_bg = (attr & LCSPX_BG_MASK) >> 4;
}
}
ss->ss_curx = col;
ss->ss_cury = row;
if (attr & LCSPX_ATTR_REVERSE) {
ss->ss_cur_bg = attr & LCSPX_FG_MASK;
ss->ss_cur_fg = (attr & LCSPX_BG_MASK) >> 4;
} else {
ss->ss_cur_fg = attr & LCSPX_FG_MASK;
ss->ss_cur_bg = (attr & LCSPX_BG_MASK) >> 4;
}
}
int
lcspx_mapchar(void *id, int uni, unsigned int *index)
{
if (uni < 256) {
*index = uni;
return (5);
}
*index = ' ';
return (0);
}
static void
lcspx_putchar(void *id, int row, int col, u_int c, long attr)
{
struct lcspx_screen *ss = id;
u_int i, j;
char dot_fg, dot_bg;
c &= 0xff;
ss->ss_image[row * lcspx_cols + col].data = c;
ss->ss_image[row * lcspx_cols + col].attr = attr;
if (ss != curscr)
return;
dot_fg = attr & LCSPX_FG_MASK;
dot_bg = (attr & LCSPX_BG_MASK) >> 4;
if (attr & LCSPX_ATTR_REVERSE) {
dot_fg = (attr & LCSPX_BG_MASK) >> 4;
dot_bg = attr & LCSPX_FG_MASK;
}
for (i = 0; i < lcspx_font.fontheight; i++) {
int ch = QFONT(c, i);
char *p = &LCSPX_ADDR(row, col, i, 0);
for (j = 0; j < lcspx_font.fontwidth; ++j) {
*p++ = ch & (1 << j) ? dot_fg : dot_bg;
}
}
if (attr & LCSPX_ATTR_UNDERLINE) {
char *p = &LCSPX_ADDR(row, col, lcspx_font.fontheight - 1, 0);
for (i = 0; i < lcspx_font.fontwidth; i++)
p[i] = ~p[i];
}
}
/*
* copies columns inside a row.
*/
static void
lcspx_copycols(void *id, int row, int srccol, int dstcol, int ncols)
{
struct lcspx_screen *ss = id;
bcopy(&ss->ss_image[row * lcspx_cols + srccol], &ss->ss_image[row *
lcspx_cols + dstcol], ncols * sizeof(ss->ss_image[0]));
if (ss != curscr)
return;
blkcpy((srccol * lcspx_font.fontwidth), (row * lcspx_font.fontheight),
(dstcol * lcspx_font.fontwidth), (row * lcspx_font.fontheight),
(ncols * lcspx_font.fontwidth), lcspx_font.fontheight);
}
/*
* Erases a bunch of chars inside one row.
*/
static void
lcspx_erasecols(void *id, int row, int startcol, int ncols, long fillattr)
{
struct lcspx_screen *ss = id;
int i;
bzero(&ss->ss_image[row * lcspx_cols + startcol], ncols * sizeof(ss->ss_image[0]));
for (i = row * lcspx_cols + startcol; i < row * lcspx_cols + startcol + ncols; ++i)
ss->ss_image[i].attr = fillattr;
if (ss != curscr)
return;
blkset((startcol * lcspx_font.fontwidth), (row * lcspx_font.fontheight),
(ncols * lcspx_font.fontwidth), lcspx_font.fontheight, ((fillattr & LCSPX_BG_MASK) >> 4));
}
static void
lcspx_copyrows(void *id, int srcrow, int dstrow, int nrows)
{
struct lcspx_screen *ss = id;
bcopy(&ss->ss_image[srcrow * lcspx_cols], &ss->ss_image[dstrow * lcspx_cols],
nrows * lcspx_cols * sizeof(ss->ss_image[0]));
if (ss != curscr)
return;
blkcpy(0, (srcrow * lcspx_font.fontheight),
0, (dstrow * lcspx_font.fontheight),
lcspx_xsize, (nrows * lcspx_font.fontheight));
}
static void
lcspx_eraserows(void *id, int startrow, int nrows, long fillattr)
{
struct lcspx_screen *ss = id;
int i;
bzero(&ss->ss_image[startrow * lcspx_cols], nrows * lcspx_cols *
sizeof(ss->ss_image[0]));
for (i = startrow * lcspx_cols; i < (startrow + nrows) * lcspx_cols; ++i)
ss->ss_image[i].attr = fillattr;
if (ss != curscr)
return;
blkset(0, (startrow * lcspx_font.fontheight), lcspx_xsize,
(nrows * lcspx_font.fontheight), ((fillattr & LCSPX_BG_MASK) >> 4));
}
static int
lcspx_allocattr(void *id, int fg, int bg, int flags, long *attrp)
{
long myattr;
if (flags & WSATTR_WSCOLORS)
myattr = (fg & LCSPX_FG_MASK) | ((bg << 4) & LCSPX_BG_MASK);
else
myattr = WSCOL_WHITE << 4; /* XXXX */
if (flags & WSATTR_REVERSE)
myattr |= LCSPX_ATTR_REVERSE;
if (flags & WSATTR_UNDERLINE)
myattr |= LCSPX_ATTR_UNDERLINE;
*attrp = myattr;
return 0;
}
int
lcspx_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
{
struct wsdisplay_fbinfo *fb = (void *)data;
int i;
switch (cmd) {
case WSDISPLAYIO_GTYPE:
*(u_int *)data = WSDISPLAY_TYPE_SPX;
break;
case WSDISPLAYIO_GINFO:
fb->height = lcspx_ysize;
fb->width = lcspx_xsize;
fb->depth = lcspx_depth;
fb->cmsize = 1 << lcspx_depth;
break;
case WSDISPLAYIO_GETCMAP:
return lcspx_get_cmap((struct wsdisplay_cmap *)data);
case WSDISPLAYIO_PUTCMAP:
return lcspx_set_cmap((struct wsdisplay_cmap *)data);
case WSDISPLAYIO_GMODE:
return EPASSTHROUGH;
case WSDISPLAYIO_SMODE:
/* if setting WSDISPLAYIO_MODE_EMUL, restore console cmap, current screen */
if (*(u_int *)data == WSDISPLAYIO_MODE_EMUL) {
for (i = 0; i < 8; i++) {
lcspx_cmap.r[i] = lcspx_default_cmap.r[i];
lcspx_cmap.g[i] = lcspx_default_cmap.g[i];
lcspx_cmap.b[i] = lcspx_default_cmap.b[i];
lcspx_update_cmap(i,
lcspx_cmap.r[i],
lcspx_cmap.g[i],
lcspx_cmap.b[i]);
}
if (savescr != NULL)
lcspx_show_screen(NULL, savescr, 0, NULL, NULL);
savescr = NULL;
} else { /* WSDISPLAYIO_MODE_MAPPED */
savescr = curscr;
curscr = NULL;
/* clear screen? */
}
return EPASSTHROUGH;
#if 0
/* FIXME use bt459 to disable video ? */
case WSDISPLAYIO_SVIDEO:
if (*(u_int *)data == WSDISPLAYIO_VIDEO_ON) {
LCSPX_REG(LCSPX_REG_VIDEO_CONFIG) = LCSPX_REG(LCSPX_REG_VIDEO_CONFIG) | 0x1000;
} else {
LCSPX_REG(LCSPX_REG_VIDEO_CONFIG) = LCSPX_REG(LCSPX_REG_VIDEO_CONFIG) & ~0x1000;
}
break;
/* FIXME ??? */
case WSDISPLAYIO_GVIDEO:
*(u_int *)data = (LCSPX_REG(LCSPX_REG_VIDEO_CONFIG) & 0x1000) ?
WSDISPLAYIO_VIDEO_ON : WSDISPLAYIO_VIDEO_OFF;
break;
#endif
default:
return EPASSTHROUGH;
}
return 0;
}
static paddr_t
lcspx_mmap(void *v, off_t offset, int prot)
{
if (offset >= lcspx_fb_size || offset < 0)
return -1;
return (LCSPX_FB_ADDR + offset) >> PGSHIFT;
}
int
lcspx_alloc_screen(void *v, const struct wsscreen_descr *type, void **cookiep,
int *curxp, int *curyp, long *defattrp)
{
int i;
struct lcspx_screen *ss;
*cookiep = malloc(sizeof(struct lcspx_screen), M_DEVBUF, M_WAITOK);
bzero(*cookiep, sizeof(struct lcspx_screen));
*curxp = *curyp = 0;
*defattrp = (LCSPX_BG_COLOR << 4) | LCSPX_FG_COLOR;
ss = *cookiep;
for (i = 0; i < lcspx_cols * lcspx_rows; ++i)
ss->ss_image[i].attr = (LCSPX_BG_COLOR << 4) | LCSPX_FG_COLOR;
return 0;
}
void
lcspx_free_screen(void *v, void *cookie)
{
/* FIXME add something to actually free malloc()ed screen ? */
}
int
lcspx_show_screen(void *v, void *cookie, int waitok,
void (*cb)(void *, int, int), void *cbarg)
{
struct lcspx_screen *ss = cookie;
u_int row, col, dot_fg, dot_bg, i, j, attr;
u_char c;
if (ss == curscr)
return (0);
for (row = 0; row < lcspx_rows; row++)
for (col = 0; col < lcspx_cols; col++) {
attr = ss->ss_image[row * lcspx_cols + col].attr;
if (attr & LCSPX_ATTR_REVERSE) {
dot_fg = (attr & LCSPX_BG_MASK) >> 4;
dot_bg = attr & LCSPX_FG_MASK;
} else {
dot_fg = attr & LCSPX_FG_MASK;
dot_bg = (attr & LCSPX_BG_MASK) >> 4;
}
c = ss->ss_image[row * lcspx_cols + col].data;
if (c < 32)
c = 32;
for (i = 0; i < lcspx_font.fontheight; i++) {
int ch = QFONT(c, i);
char *p = &LCSPX_ADDR(row, col, i, 0);
for (j = 0; j < lcspx_font.fontwidth; ++j) {
*p++ = ch & (1 << j) ? dot_fg : dot_bg;
}
}
if (attr & LCSPX_ATTR_UNDERLINE)
for (j = 0; j < lcspx_font.fontwidth; j++)
LCSPX_ADDR(row, col,
lcspx_font.fontheight - 1, j) = dot_fg;
}
cursor = &lcspxaddr[(ss->ss_cury * lcspx_onerow) +
((lcspx_font.fontheight - 1) * lcspx_xsize) +
(ss->ss_curx * lcspx_font.fontwidth)];
cur_fg = ss->ss_cur_fg;
cur_bg = ss->ss_cur_bg;
curscr = ss;
return (0);
}
static int
lcspx_get_cmap(p)
struct wsdisplay_cmap *p;
{
u_int index = p->index, count = p->count;
int error;
if (index >= (1 << lcspx_depth) || count > (1 << lcspx_depth) - index)
return (EINVAL);
error = copyout(&lcspx_cmap.r[index], p->red, count);
if (error)
return error;
error = copyout(&lcspx_cmap.g[index], p->green, count);
if (error)
return error;
error = copyout(&lcspx_cmap.b[index], p->blue, count);
return error;
}
static int
lcspx_set_cmap(p)
struct wsdisplay_cmap *p;
{
u_int index = p->index, count = p->count;
int error, s;
if (index >= (1 << lcspx_depth) || count > (1 << lcspx_depth) - index)
return (EINVAL);
error = copyin(p->red, &lcspx_cmap.r[index], count);
if (error)
return error;
error = copyin(p->green, &lcspx_cmap.g[index], count);
if (error)
return error;
error = copyin(p->blue, &lcspx_cmap.b[index], count);
if (error)
return error;
s = spltty();
while (count-- > 0) {
lcspx_update_cmap(index,
lcspx_cmap.r[index],
lcspx_cmap.g[index],
lcspx_cmap.b[index]);
index++;
}
splx(s);
return (0);
}
cons_decl(lcspx);
void
lcspxcninit(struct consdev *cndev)
{
int i;
/* Clear screen */
printf("Clear screen\n");
memset(lcspxaddr, LCSPX_BG_COLOR, lcspx_xsize * lcspx_ysize);
curscr = &lcspx_conscreen;
for (i = 0; i < lcspx_cols * lcspx_rows; i++)
lcspx_conscreen.ss_image[i].attr =
(LCSPX_BG_COLOR << 4) | LCSPX_FG_COLOR;
wsdisplay_cnattach(&lcspx_stdscreen, &lcspx_conscreen, 0, 0,
(LCSPX_BG_COLOR << 4) | LCSPX_FG_COLOR);
cn_tab->cn_pri = CN_INTERNAL;
#if NDZKBD > 0
dzkbd_cnattach(0); /* Connect keyboard and screen together */
#endif
}
/*
* Called very early to setup the glass tty as console.
* Because it's called before the VM system is inited, virtual memory
* for the framebuffer can be stolen directly without disturbing anything.
*/
void
lcspxcnprobe(struct consdev *cndev)
{
extern const struct cdevsw wsdisplay_cdevsw;
if (vax_boardtype != VAX_BTYP_49)
return; /* Only for VS 4000/90, 90A and 96 */
if (vax_confdata & 0x100)
return; /* Diagnostic console */
lcspx_init_common(NULL, NULL);
cndev->cn_pri = CN_INTERNAL;
cndev->cn_dev = makedev(cdevsw_lookup_major(&wsdisplay_cdevsw), 0);
}
void
lcspx_init_common(struct device *self, struct vsbus_attach_args *va)
{
int i, pixel;
extern vaddr_t virtual_avail;
int cookie;
struct wsdisplay_font *wf;
#if 0
if (regaddr != NULL)
return;
#endif
/* map LCSPX registers first */
if (self != NULL) {
regaddr = (long*)vax_map_physmem(LCSPX_REG_ADDR, (LCSPX_REG_SIZE/VAX_NBPG));
if (regaddr == 0) {
printf("%s: unable to allocate register memory (1).\n", self->dv_xname);
return;
}
regaddr1 = (long*)vax_map_physmem(LCSPX_REG1_ADDR, (LCSPX_REG1_SIZE/VAX_NBPG));
if (regaddr1 == 0) {
printf("%s: unable to allocate register memory (2).\n", self->dv_xname);
return;
}
} else {
regaddr = (long*)virtual_avail;
virtual_avail += LCSPX_REG_SIZE;
ioaccess((vaddr_t)regaddr, LCSPX_REG_ADDR, (LCSPX_REG_SIZE/VAX_NBPG));
regaddr1 = (long*)virtual_avail;
virtual_avail += LCSPX_REG1_SIZE;
ioaccess((vaddr_t)regaddr1, LCSPX_REG1_ADDR, (LCSPX_REG1_SIZE/VAX_NBPG));
}
/* any autodetection stuff comes here */
lcspx_depth = 8;
lcspx_xsize = 1280;
lcspx_ysize = 1024;
wsfont_init();
cookie = wsfont_find(NULL, 12, 22, 0, WSDISPLAY_FONTORDER_R2L,
WSDISPLAY_FONTORDER_L2R);
if (cookie == -1)
cookie = wsfont_find(NULL, 8, 0, 0, WSDISPLAY_FONTORDER_R2L, 0);
if (cookie == -1)
cookie = wsfont_find(NULL, 0, 0, 0, WSDISPLAY_FONTORDER_R2L,
WSDISPLAY_FONTORDER_L2R);
if (cookie == -1 || wsfont_lock(cookie, &wf))
panic("lcspx_common_init: can't load console font");
lcspx_font = *wf;
#if 0
if (self != NULL) {
printf("%s: Using font %s\n", self->dv_xname, wf->name);
}
#endif
lcspx_cols = lcspx_xsize / lcspx_font.fontwidth;
lcspx_rows = lcspx_ysize / lcspx_font.fontheight;
lcspx_onerow = lcspx_xsize * lcspx_font.fontheight;
lcspx_fb_size = lcspx_xsize * lcspx_ysize;
lcspx_stdscreen.ncols = lcspx_cols;
lcspx_stdscreen.nrows = lcspx_rows;
lcspx_stdscreen.fontwidth = lcspx_font.fontwidth;
lcspx_stdscreen.fontheight = lcspx_font.fontheight;
sprintf(lcspx_stdscreen_name, "%dx%d", lcspx_cols, lcspx_rows);
qf = lcspx_font.data;
qf2 = (u_short *)lcspx_font.data;
if (self != NULL) {
lcspxaddr = (caddr_t)vax_map_physmem(va->va_paddr,
(lcspx_fb_size/VAX_NBPG));
if (lcspxaddr == 0) {
printf("%s: unable to map framebuffer memory.\n", self->dv_xname);
return;
}
/* map 4 BT459 registers */
bt_addrl = (caddr_t)vax_map_physmem(LCSPX_BT459_ADDRL, 1);
if (bt_addrl == 0) {
printf("%s: unable to map BT459 Address register (Low).\n", self->dv_xname);
return;
}
bt_addrh = (caddr_t)vax_map_physmem(LCSPX_BT459_ADDRH, 1);
if (bt_addrh == 0) {
printf("%s: unable to map BT459 Address reister (High).\n", self->dv_xname);
return;
}
bt_reg = (caddr_t)vax_map_physmem(LCSPX_BT459_REG, 1);
if (bt_reg == 0) {
printf("%s: unable to map BT459 I/O Register.\n", self->dv_xname);
return;
}
bt_cmap = (caddr_t)vax_map_physmem(LCSPX_BT459_CMAP, 1);
if (bt_cmap == 0) {
printf("%s: unable to map BT459 Color Map register.\n", self->dv_xname);
return;
}
} else {
lcspxaddr = (caddr_t)virtual_avail;
virtual_avail += lcspx_fb_size;
ioaccess((vaddr_t)lcspxaddr, LCSPX_FB_ADDR, (lcspx_fb_size/VAX_NBPG));
bt_addrl = (caddr_t)virtual_avail;
virtual_avail += VAX_NBPG;
ioaccess((vaddr_t)bt_addrl, LCSPX_BT459_ADDRL, 1);
bt_addrh = (caddr_t)virtual_avail;
virtual_avail += VAX_NBPG;
ioaccess((vaddr_t)bt_addrh, LCSPX_BT459_ADDRH, 1);
bt_reg = (caddr_t)virtual_avail;
virtual_avail += VAX_NBPG;
ioaccess((vaddr_t)bt_reg, LCSPX_BT459_REG, 1);
bt_cmap = (caddr_t)virtual_avail;
virtual_avail += VAX_NBPG;
ioaccess((vaddr_t)bt_cmap, LCSPX_BT459_CMAP, 1);
}
/* BT459 ID register test, should always read 0x4a */
if ((get_btreg(BT459_REG_ID) & 0xff) != 0x4a)
printf("%s: BT459 test register read error, value = %x.\n",
self->dv_xname, get_btreg(BT459_REG_ID) & 0xff);
for (i = 0; i < 8; i++) {
lcspx_cmap.r[i] = lcspx_default_cmap.r[i];
lcspx_cmap.g[i] = lcspx_default_cmap.g[i];
lcspx_cmap.b[i] = lcspx_default_cmap.b[i];
lcspx_update_cmap(i,
lcspx_cmap.r[i],
lcspx_cmap.g[i],
lcspx_cmap.b[i]);
}
/* build console cursor bitmap based on font dimensions */
for (i = 0; i < 1024; i++) {
if ((i / 16) != (lcspx_font.fontheight - 1)) {
/* this is not the cursor line */
cons_curmap[i] = CURSOR_BG;
} else {
/* only enable lcspx_font.fontwidth of pixels */
if ((i % 16) < (lcspx_font.fontwidth / 4))
cons_curmap[i] = CURSOR_FG;
if ((i % 16) == (lcspx_font.fontwidth / 4)) {
cons_curmap[i] = 0;
for (pixel = 0; pixel < 4; pixel++)
if (pixel < (lcspx_font.fontwidth % 4))
cons_curmap[i] += ((CURSOR_FG % 4) << (pixel * 2));
else
cons_curmap[i] += ((CURSOR_BG % 4) << (pixel * 2));
}
if ((i % 16) > (lcspx_font.fontwidth / 4))
cons_curmap[i] = CURSOR_BG;
}
#if 0
/* REMOVE verify rendering of cursor sprite */
printf("%x ", cons_curmap[i] & 0xff);
if ((i > 0) && (((i + 1) % 16) == 0))
printf("\n");
#endif
/* set BT459 cursor sprite */
set_btreg(BT459_REG_CRAM_BASE + i, cons_curmap[i]);
#if 0
/* REMOVE verify cursor sprite */
printf("%x ", get_btreg(BT459_REG_CRAM_BASE + i) & 0xff);
if ((i > 0) && (((i + 1) % 16) == 0))
printf("\n");
#endif
}
#if 0
/* for testing only, should be removed/replaced */
lcspx_update_cmap(BT459_REG_CCOLOR_1,
lcspx_cmap.r[7],
lcspx_cmap.g[7],
lcspx_cmap.b[7]);
lcspx_update_cmap(BT459_REG_CCOLOR_2,
lcspx_cmap.r[5],
lcspx_cmap.g[5],
lcspx_cmap.b[5]);
lcspx_update_cmap(BT459_REG_CCOLOR_3,
lcspx_cmap.r[6],
lcspx_cmap.g[6],
lcspx_cmap.b[6]);
#endif
/* set LCSPX write/read plane masks */
LCSPX_REG(0x3170) = 0xffffffff;
LCSPX_REG(0x3174) = 0xffffffff;
/* set various BT459 registers */
set_btreg(BT459_REG_CMD0, 0x40);
set_btreg(BT459_REG_CMD1, 0x00);
set_btreg(BT459_REG_CMD2, 0xc0);
set_btreg(BT459_REG_PRM, 0xff);
set_btreg(BT459_REG_CXLO, 0);
set_btreg(BT459_REG_CXHI, 0);
set_btreg(BT459_REG_CYLO, 0);
set_btreg(BT459_REG_CYHI, 0);
set_btreg(BT459_REG_WXLO, 0);
set_btreg(BT459_REG_WXHI, 0);
set_btreg(BT459_REG_WYLO, 0);
set_btreg(BT459_REG_WYHI, 0);
#if 1
set_btreg(BT459_REG_WWLO, LO(lcspx_xsize));
set_btreg(BT459_REG_WWHI, HI(lcspx_xsize));
set_btreg(BT459_REG_WHLO, LO(lcspx_ysize));
set_btreg(BT459_REG_WHHI, HI(lcspx_ysize));
#else
set_btreg(BT459_REG_WWLO, 0);
set_btreg(BT459_REG_WWHI, 0);
set_btreg(BT459_REG_WHLO, 0);
set_btreg(BT459_REG_WHHI, 0);
#endif
}
--------------1E262080FE2--