/*
* Copyright (c) 1996, 1998, 2009 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Paul Kranenburg and Michael Lorenz.
*
* 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.
*
* 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.
*/
/*
* color display (TCX) driver.
*
* Does not handle interrupts, even though they can occur.
*
* XXX should defer colormap updates to vertical retrace interrupts
*/
short sc_8bit; /* true if 8-bit hardware */
short sc_blanked; /* true if blanked */
uint32_t sc_fbsize; /* size of the 8bit fb */
u_char sc_cmap_red[256];
u_char sc_cmap_green[256];
u_char sc_cmap_blue[256];
int sc_mode, sc_bg;
int sc_cursor_x, sc_cursor_y;
int sc_hotspot_x, sc_hotspot_y;
struct vcons_data vd;
};
fb->fb_driver = &tcx_fbdriver;
fb->fb_device = sc->sc_dev;
/* Mask out invalid flags from the user. */
fb->fb_flags = device_cfdata(sc->sc_dev)->cf_flags & FB_USERMASK;
/*
* The onboard framebuffer on the SS4 supports only 8-bit mode;
* it can be distinguished from the S24 card for the SS5 by the
* presence of the "tcx-8-bit" attribute on the SS4 version.
*/
sc->sc_8bit = node_has_property(node, "tcx-8-bit");
fb->fb_type.fb_depth = 8;
fb_setsize_obp(fb, fb->fb_type.fb_depth, 1152, 900, node);
/*
* actual FB size ( of the 8bit region )
* no reason to restrict userland mappings to the visible VRAM
*/
if (sc->sc_8bit) {
aprint_normal(" (8-bit only TCX)\n");
/* at least the SS4 can have 2MB with a VSIMM */
sc->sc_fbsize = 0x100000 * prom_getpropint(node, "vram", 1);
} else {
aprint_normal(" (S24)\n");
/* all S24 I know of have 4MB, non-expandable */
sc->sc_fbsize = 0x100000;
}
fb->fb_type.fb_cmsize = 256;
fb->fb_type.fb_size = sc->sc_fbsize; /* later code assumes 8bit */
aprint_normal_dev(self, "%s, %d x %d\n", OBPNAME,
fb->fb_type.fb_width,
fb->fb_type.fb_height);
fb->fb_type.fb_type = FBTYPE_TCXCOLOR;
if (sa->sa_nreg != TCX_NREG) {
aprint_error("\n");
aprint_error_dev(self, "only %d register sets\n",
sa->sa_nreg);
return;
}
if (sa->sa_reg[TCX_REG_STIP].oa_size < 0x1000) {
aprint_error("\n");
aprint_error_dev(self, "STIP register too small (0x%x)\n",
sa->sa_reg[TCX_REG_STIP].oa_size);
return;
}
/* map the 8bit dumb FB for the console */
if (sbus_bus_map(sa->sa_bustag,
sc->sc_physaddr[TCX_REG_DFB8].oa_space,
sc->sc_physaddr[TCX_REG_DFB8].oa_base,
sc->sc_fbsize,
BUS_SPACE_MAP_LINEAR,
&bh) != 0) {
aprint_error_dev(self, "tcxattach: cannot map framebuffer\n");
return;
}
sc->sc_fbaddr = bus_space_vaddr(sa->sa_bustag, bh);
/*
* 8bit tcx has the RSTIP and RBLIT ranges set to size 0.
* On Real Hardware they work anyway ( on my SS4 at least ) but
* emulators may not be so forgiving.
*/
if (sc->sc_8bit) {
/* BLIT space */
if (sbus_bus_map(sa->sa_bustag,
sc->sc_physaddr[TCX_REG_BLIT].oa_space,
sc->sc_physaddr[TCX_REG_BLIT].oa_base,
sc->sc_fbsize << 3,
BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_LARGE,
&bh) != 0) {
aprint_error_dev(self,
"tcxattach: cannot map BLIT space\n");
return;
}
sc->sc_rblit = bus_space_vaddr(sa->sa_bustag, bh);
config_found(self, &aa, wsemuldisplaydevprint, CFARGS_NONE);
/*
* we need to do this again - something overwrites a handful
* palette registers and we end up with white in reg. 0
*/
tcx_loadcmap(sc, 0, 256);
}
int
tcxopen(dev_t dev, int flags, int mode, struct lwp *l)
{
return (0);
}
int
tcxclose(dev_t dev, int flags, int mode, struct lwp *l)
{
struct tcx_softc *sc = device_lookup_private(&tcx_cd, minor(dev));
tcx_reset(sc);
/* we may want to clear and redraw the console here */
return (0);
}
int
tcxioctl(dev_t dev, u_long cmd, void *data, int flags, struct lwp *l)
{
struct tcx_softc *sc = device_lookup_private(&tcx_cd, minor(dev));
switch (cmd) {
case FBIOGTYPE:
*(struct fbtype *)data = sc->sc_fb.fb_type;
break;
/*
* Base addresses at which users can mmap() the various pieces of a tcx.
*/
#define TCX_USER_RAM 0x00000000
#define TCX_USER_RAM24 0x01000000
#define TCX_USER_RAM_COMPAT 0x04000000 /* cg3 emulation */
#define TCX_USER_STIP 0x10000000
#define TCX_USER_BLIT 0x20000000
#define TCX_USER_RDFB32 0x28000000
#define TCX_USER_RSTIP 0x30000000
#define TCX_USER_RBLIT 0x38000000
#define TCX_USER_TEC 0x70001000
#define TCX_USER_BTREGS 0x70002000
#define TCX_USER_THC 0x70004000
#define TCX_USER_DHC 0x70008000
#define TCX_USER_ALT 0x7000a000
#define TCX_USER_UART 0x7000c000
#define TCX_USER_VRT 0x7000e000
#define TCX_USER_ROM 0x70010000
struct mmo {
u_int mo_uaddr; /* user (virtual) address */
u_int mo_size; /* size, or 0 for video ram size */
u_int mo_bank; /* register bank number */
};
/*
* Return the address that would map the given device at the given
* offset, allowing for the given protection, or return -1 for error.
*
* XXX needs testing against `demanding' applications (e.g., aviator)
*/
paddr_t
tcxmmap(dev_t dev, off_t off, int prot)
{
struct tcx_softc *sc = device_lookup_private(&tcx_cd, minor(dev));
struct openprom_addr *rr = sc->sc_physaddr;
struct mmo *mo, *mo_end;
u_int u, sz;
static struct mmo mmo[] = {
{ TCX_USER_RAM, 0, TCX_REG_DFB8 },
{ TCX_USER_RAM24, 0, TCX_REG_DFB24 },
{ TCX_USER_RAM_COMPAT, 0, TCX_REG_DFB8 },
/*
* Entries with size 0 map video RAM (i.e., the size in fb data).
* Entries that map 32-bit deep regions are adjusted for their
* depth (fb_size gives the size of the 8-bit-deep region).
*
* Since we work in pages, the fact that the map offset table's
* sizes are sometimes bizarre (e.g., 1) is effectively ignored:
* one byte is as good as one page.
*/
mo = mmo;
mo_end = &mmo[NMMO];
for (; mo < mo_end; mo++) {
if ((u_int)off < mo->mo_uaddr)
continue;
u = off - mo->mo_uaddr;
sz = mo->mo_size;
if (sz == 0) {
sz = sc->sc_fb.fb_type.fb_size;
/*
* check for the 32-bit-deep regions and adjust
* accordingly
*/
if (mo->mo_uaddr == TCX_USER_RAM24 ||
mo->mo_uaddr == TCX_USER_RDFB32) {
if (sc->sc_8bit) {
/*
* not present on 8-bit hardware
*/
continue;
}
sz *= 4;
}
}
if (sz == 1)
sz = rr[mo->mo_bank].oa_size;
if (u < sz) {
return (bus_space_mmap(sc->sc_bustag,
BUS_ADDR(rr[mo->mo_bank].oa_space,
rr[mo->mo_bank].oa_base),
u,
prot,
BUS_SPACE_MAP_LINEAR));
}
}
return (-1);
}
/* 'regular' framebuffer mmap()ing */
if (sc->sc_8bit) {
/* on 8Bit boards hand over the 8 bit aperture */
if (offset > sc->sc_fbsize)
return -1;
return bus_space_mmap(sc->sc_bustag,
sc->sc_physaddr[TCX_REG_DFB8].oa_base + offset, 0, prot,
BUS_SPACE_MAP_LINEAR);
} else {
/* ... but if we have a 24bit aperture we use it */
if (offset > sc->sc_fbsize * 4)
return -1;
return bus_space_mmap(sc->sc_bustag,
sc->sc_physaddr[TCX_REG_DFB24].oa_base + offset, 0, prot,
BUS_SPACE_MAP_LINEAR);
}
return -1;
}
static void
tcx_clearscreen(struct tcx_softc *sc, int spc)
{
/* ROP in the upper 4bit is necessary, tcx actually uses it */
volatile uint64_t bg = 0x30000000ffffffffLL;
volatile uint64_t spc64;
int i, len;
spc64 = ((spc & 3) << 24);
bg |= (spc64 << 32);
len = sc->sc_fb.fb_type.fb_width * sc->sc_fb.fb_type.fb_height;
for (i = 0; i < len; i += 32)
sc->sc_rstip[i] = bg;
}
static void
tcx_copyrows(void *cookie, int srcrow, int dstrow, int nrows)
{
struct rasops_info *ri = cookie;
struct vcons_screen *scr = ri->ri_hw;
struct tcx_softc *sc = scr->scr_cookie;
int i, last, first, len, dest, leftover;
i = ri->ri_width * ri->ri_font->fontheight * nrows;
len = i & 0xffffe0;
leftover = i & 0x1f;
if (srcrow < dstrow) {
/* we must go bottom to top */
first = ri->ri_width *
(ri->ri_font->fontheight * srcrow + ri->ri_yorigin);
last = first + len;
dest = ri->ri_width *
(ri->ri_font->fontheight * dstrow + ri->ri_yorigin) + len;
if (leftover > 0) {
sc->sc_rblit[dest + 32] =
(uint64_t)((leftover - 1) << 24) |
(uint64_t)(i + 32);
}
for (i = last; i >= first; i -= 32) {
sc->sc_rblit[dest] = 0x300000001f000000LL | (uint64_t)i;
dest -= 32;
}
} else {
/* top to bottom */
first = ri->ri_width *
(ri->ri_font->fontheight * srcrow + ri->ri_yorigin);
dest = ri->ri_width *
(ri->ri_font->fontheight * dstrow + ri->ri_yorigin);
last = first + len;
for (i = first; i <= last; i+= 32) {
sc->sc_rblit[dest] = 0x300000001f000000LL | (uint64_t)i;
dest += 32;
}
if (leftover > 0) {
sc->sc_rblit[dest] =
(uint64_t)((leftover - 1) << 24) | (uint64_t)i;
}
}
}
static void
tcx_eraserows(void *cookie, int start, int nrows, long attr)
{
struct rasops_info *ri = cookie;
struct vcons_screen *scr = ri->ri_hw;
struct tcx_softc *sc = scr->scr_cookie;
volatile uint64_t temp;
int i, last, first, len, leftover;
i = ri->ri_width * ri->ri_font->fontheight * nrows;
len = i & 0xffffe0;
leftover = i & 0x1f;
first = ri->ri_width *
(ri->ri_font->fontheight * start + ri->ri_yorigin);
last = first + len;
temp = 0x30000000ffffffffLL |
((uint64_t)((ri->ri_devcmap[(attr >> 16) & 0xff]) & 0xff) << 32);
for (i = first; i < last; i+= 32)
sc->sc_rstip[i] = temp;
if (leftover > 0) {
temp &= 0xffffffffffffffffLL << (32 - leftover);
sc->sc_rstip[i] = temp;
}
}
/*
* The stipple engine is 100% retarded. All drawing operations have to start
* at 32 pixel boundaries so we'll have to deal with characters being split.
*/
if (font->fontwidth < 9) {
/* byte by byte */
for (i = 0; i < font->fontheight; i++) {
sc->sc_rstip[addr] = bg;
sc->sc_rstip[addr + 32] = bgr;
bork = *cdata;
if (bork != 0) {
fg = (uint64_t)bork >> (shift - 24);
sc->sc_rstip[addr] = fg | temp;
fgr = (uint64_t)(bork << (52 - shift));
sc->sc_rstip[addr] = fgr | temp;
}
cdata++;
addr += ri->ri_width;
}
} else if (font->fontwidth < 17) {
/* short by short */
wdata = (uint16_t *)cdata;
for (i = 0; i < font->fontheight; i++) {
sc->sc_rstip[addr] = bg;
sc->sc_rstip[addr + 32] = bgr;
bork = *wdata;
if (bork != 0) {
fg = (uint64_t)bork >> (shift - 16);
sc->sc_rstip[addr] = fg | temp;
fgr = (uint64_t)(bork << (48 - shift));
sc->sc_rstip[addr + 32] = fgr | temp;
}
wdata++;
addr += ri->ri_width;
}
}
}
}
static int
tcx_do_cursor(struct tcx_softc *sc, struct wsdisplay_cursor *cur)
{
if (sc->sc_8bit) {
/* hw cursor is not implemented on tcx */
return -1;
}
if (cur->which & WSDISPLAY_CURSOR_DOCUR) {
if (cur->enable) {
tcx_set_cursor(sc);
} else {
/* move the cursor out of sight */
bus_space_write_4(sc->sc_bustag, sc->sc_thc,
THC_CURSOR_POS, 0x7fff7fff);
}
}
if (cur->which & WSDISPLAY_CURSOR_DOHOT) {
sc->sc_cursor_x = cur->pos.x;
sc->sc_cursor_y = cur->pos.y;
tcx_set_cursor(sc);
}
if (cur->which & WSDISPLAY_CURSOR_DOCMAP) {
#if 0
/*
* apparently we're not writing in the right register here - if we do
* this the screen goes all funky
*/
int i;
for (i = 0; i < cur->cmap.count; i++) {
bus_space_write_4(sc->sc_bustag, sc->sc_bt, DAC_ADDRESS,
(cur->cmap.index + i + 2) << 24);
bus_space_write_4(sc->sc_bustag, sc->sc_bt,
DAC_CURSOR_LUT, cur->cmap.red[i] << 24);
bus_space_write_4(sc->sc_bustag, sc->sc_bt,
DAC_CURSOR_LUT, cur->cmap.green[i] << 24);
bus_space_write_4(sc->sc_bustag, sc->sc_bt,
DAC_CURSOR_LUT, cur->cmap.blue[i] << 24);
}
#endif
}
if (cur->which & WSDISPLAY_CURSOR_DOSHAPE) {
int i;
uint32_t temp, poof;