/*
* Copyright (c) 2002, 2003 Valeriy E. Ushakov
* All rights reserved.
*
* 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. 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.
*/
/*
* Integraphics Systems IGA 168x and CyberPro series.
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: igsfb.c,v 1.60 2021/08/07 16:19:12 thorpej Exp $");
/*
* Finish off the attach. Bus specific attach method should have
* enabled io and memory accesses and mapped io (and cop?) registers.
*/
void
igsfb_attach_subr(struct igsfb_softc *sc, int isconsole)
{
struct igsfb_devconfig *dc = sc->sc_dc;
struct wsemuldisplaydev_attach_args waa;
struct rasops_info *ri;
long defattr;
/* Total amount of video memory. */
busctl = igs_ext_read(dc->dc_iot, dc->dc_ioh, IGS_EXT_BUS_CTL);
if (busctl & 0x2)
dc->dc_vmemsz = 4;
else if (busctl & 0x1)
dc->dc_vmemsz = 2;
else
dc->dc_vmemsz = 1;
dc->dc_vmemsz <<= 20; /* megabytes -> bytes */
/*
* Check for endianness mismatch by writing a word at the end of
* the video memory (off-screen) and reading it back byte-by-byte.
*/
if (bus_space_map(dc->dc_memt,
dc->dc_memaddr + dc->dc_vmemsz - sizeof(uint32_t),
sizeof(uint32_t),
dc->dc_memflags | BUS_SPACE_MAP_LINEAR,
&tmph) != 0)
{
printf("unable to map video memory for endianness test\n");
return 1;
}
/*
* On CyberPro we can use magic bswap bit in linear address.
*/
fbaddr = dc->dc_memaddr;
if (need_bswap) {
dc->dc_hwflags |= IGSFB_HW_BSWAP;
if (dc->dc_id >= 0x2000) {
dc->dc_hwflags |= IGSFB_HW_BE_SELECT;
fbaddr |= IGS_MEM_BE_SELECT;
}
}
/*
* Map graphic coprocessor for mode setting and accelerated rasops.
*/
if (dc->dc_id >= 0x2000) { /* XXX */
if (bus_space_map(dc->dc_iot,
dc->dc_iobase + IGS_COP_BASE_B, IGS_COP_SIZE,
dc->dc_ioflags,
&dc->dc_coph) != 0)
{
printf("unable to map COP registers\n");
return 1;
}
}
igsfb_hw_setup(dc);
/*
* Don't map in all N megs, just the amount we need for the wsscreen.
*/
dc->dc_fbsz = dc->dc_stride * dc->dc_height;
if (bus_space_map(dc->dc_memt, fbaddr, dc->dc_fbsz,
dc->dc_memflags | BUS_SPACE_MAP_LINEAR,
&dc->dc_fbh) != 0)
{
if (dc->dc_id >= 0x2000) { /* XXX */
bus_space_unmap(dc->dc_iot, dc->dc_coph, IGS_COP_SIZE);
}
bus_space_unmap(dc->dc_iot, dc->dc_ioh, IGS_REG_SIZE);
printf("unable to map framebuffer\n");
return 1;
}
igsfb_init_cmap(dc);
/*
* 1KB for cursor sprite data at the very end of the video memory.
*/
croffset = dc->dc_vmemsz - IGS_CURSOR_DATA_SIZE;
craddr = fbaddr + croffset;
if (bus_space_map(dc->dc_memt, craddr, IGS_CURSOR_DATA_SIZE,
dc->dc_memflags | BUS_SPACE_MAP_LINEAR,
&dc->dc_crh) != 0)
{
if (dc->dc_id >= 0x2000) { /* XXX */
bus_space_unmap(dc->dc_iot, dc->dc_coph, IGS_COP_SIZE);
}
bus_space_unmap(dc->dc_iot, dc->dc_ioh, IGS_REG_SIZE);
bus_space_unmap(dc->dc_memt, dc->dc_fbh, dc->dc_fbsz);
printf("unable to map cursor sprite region\n");
return 1;
}
/*
* Tell the device where cursor sprite data are located in the
* linear space (it takes data offset in 1KB units).
*/
croffset >>= 10; /* bytes -> kilobytes */
igs_ext_write(dc->dc_iot, dc->dc_ioh,
IGS_EXT_SPRITE_DATA_LO, croffset & 0xff);
igs_ext_write(dc->dc_iot, dc->dc_ioh,
IGS_EXT_SPRITE_DATA_HI, (croffset >> 8) & 0xf);
/* init the bit expansion table for cursor sprite data conversion */
igsfb_init_bit_table(dc);
/* XXX: fill dc_cursor and use igsfb_update_cursor() instead? */
memset(&dc->dc_cursor, 0, sizeof(struct igs_hwcursor));
va = bus_space_vaddr(dc->dc_memt, dc->dc_crh);
memset(va, /* transparent */ 0xaa, IGS_CURSOR_DATA_SIZE);
/* init software copy */
for (i = 0; i < IGS_CMAP_SIZE; ++i, p += 3) {
dc->dc_cmap.r[i] = p[0];
dc->dc_cmap.g[i] = p[1];
dc->dc_cmap.b[i] = p[2];
}
/* propagate to the device */
igsfb_update_cmap(dc, 0, IGS_CMAP_SIZE);
/* set overscan color */
p = &rasops_cmap[WSDISPLAY_BORDER_COLOR * 3];
igs_ext_write(iot, ioh, IGS_EXT_OVERSCAN_RED, p[0]);
igs_ext_write(iot, ioh, IGS_EXT_OVERSCAN_GREEN, p[1]);
igs_ext_write(iot, ioh, IGS_EXT_OVERSCAN_BLUE, p[2]);
}
static void
igsfb_init_wsdisplay(void *cookie, struct vcons_screen *scr, int existing,
long *defattr)
{
struct igsfb_devconfig *dc = cookie;
struct rasops_info *ri = &scr->scr_ri;
int wsfcookie;
if (scr == &dc->dc_console) {
if (ri->ri_flg == 0) {
/* first time, need to set RI_NO_AUTO */
ri->ri_flg |= RI_NO_AUTO;
} else {
/* clear it on 2nd run */
ri->ri_flg &= ~RI_NO_AUTO;
}
}
ri->ri_flg |= RI_CENTER | RI_FULLCLEAR;
if (IGSFB_HW_SOFT_BSWAP(dc))
ri->ri_flg |= RI_BSWAP;
/*
* Initialize wsfont related stuff.
*/
wsfont_init();
/* prefer gallant that is identical to the one the prom uses */
wsfcookie = wsfont_find("Gallant", 12, 22, 0,
WSDISPLAY_FONTORDER_L2R,
WSDISPLAY_FONTORDER_L2R, WSFONT_FIND_BITMAP);
if (wsfcookie <= 0) {
#ifdef DIAGNOSTIC
printf("unable to find font Gallant 12x22\n");
#endif
wsfcookie = wsfont_find(NULL, 0, 0, 0, /* any font at all? */
WSDISPLAY_FONTORDER_L2R,
WSDISPLAY_FONTORDER_L2R,
WSFONT_FIND_BITMAP);
}
if (wsfcookie <= 0) {
printf("unable to find any fonts\n");
return;
}
if (wsfont_lock(wsfcookie, &ri->ri_font) != 0) {
printf("unable to lock font\n");
return;
}
ri->ri_wsfcookie = wsfcookie;
/* XXX: TODO: compute term size based on font dimensions? */
rasops_init(ri, 0, 0);
rasops_reconfig(ri, ri->ri_height / ri->ri_font->fontheight,
ri->ri_width / ri->ri_font->fontwidth);
/* use the sprite for the text mode cursor */
igsfb_make_text_cursor(dc, scr);
/* the cursor is "busy" while we are in the text mode */
dc->dc_hwflags |= IGSFB_HW_TEXT_CURSOR;
/* propagate sprite data to the device */
igsfb_update_cursor(dc, WSDISPLAY_CURSOR_DOSHAPE);
/* accelerated text cursor */
ri->ri_ops.cursor = igsfb_accel_cursor;
w = f->fontwidth;
for (i = 0; i < f->stride; ++i) {
if (w >= 8) {
s = 0xffff; /* all inverted */
w -= 8;
} else {
/* first w pixels inverted, the rest is transparent */
s = ~(0x5555 << (w * 2));
s = htole16(s);
w = 0;
}
cc_scan[i] = s;
}
/* init sprite array to be all transparent */
memset(dc->dc_cursor.cc_sprite, 0xaa, IGS_CURSOR_DATA_SIZE);
for (i = 0; i < f->fontheight; ++i)
memcpy(&dc->dc_cursor.cc_sprite[i * 8],
cc_scan, f->stride * sizeof(uint16_t));
return 0;
}
/*
* Spread a byte (abcd.efgh) into two (0a0b.0c0d 0e0f.0g0h).
* Helper function for igsfb_init_bit_table().
*/
static uint16_t
igsfb_spread_bits_8(uint8_t b)
{
uint16_t s = b;
s = ((s & 0x00f0) << 4) | (s & 0x000f);
s = ((s & 0x0c0c) << 2) | (s & 0x0303);
s = ((s & 0x2222) << 1) | (s & 0x1111);
return s;
}
/*
* Cursor sprite data are in 2bpp. Incoming image/mask are in 1bpp.
* Prebuild the table to expand 1bpp->2bpp, with bswapping if necessary.
*/
static void
igsfb_init_bit_table(struct igsfb_devconfig *dc)
{
uint16_t *expand = dc->dc_bexpand;
uint16_t s;
u_int i;
for (i = 0; i < 256; ++i) {
s = igsfb_spread_bits_8(i);
expand[i] = htole16(s);
}
}
err = copyout(&dc->dc_cmap.r[index], p->red, count);
if (err)
return err;
err = copyout(&dc->dc_cmap.g[index], p->green, count);
if (err)
return err;
err = copyout(&dc->dc_cmap.b[index], p->blue, count);
if (err)
return err;
return 0;
}
/*
* wsdisplay_accessops: ioctl(WSDISPLAYIO_PUTCMAP)
* Set the software cmap copy and propagate changed range to the device.
*/
static int
igsfb_set_cmap(struct igsfb_devconfig *dc, const struct wsdisplay_cmap *p)
{
u_int index, count;
uint8_t r[IGS_CMAP_SIZE];
uint8_t g[IGS_CMAP_SIZE];
uint8_t b[IGS_CMAP_SIZE];
int error;
index = p->index;
count = p->count;
if (index >= IGS_CMAP_SIZE || count > IGS_CMAP_SIZE - index)
return EINVAL;
error = copyin(p->red, &r[index], count);
if (error)
return error;
error = copyin(p->green, &g[index], count);
if (error)
return error;
error = copyin(p->blue, &b[index], count);
if (error)
return error;
cc = &dc->dc_cursor;
v = p->which;
index = count = icount = iwidth = 0; /* XXX: gcc */
pos.x = pos.y = 0; /* XXX: gcc */
/* copy in the new cursor colormap */
if (v & WSDISPLAY_CURSOR_DOCMAP) {
index = p->cmap.index;
count = p->cmap.count;
if (index >= 2 || (index + count) > 2)
return EINVAL;
error = copyin(p->cmap.red, &r[index], count);
if (error)
return error;
error = copyin(p->cmap.green, &g[index], count);
if (error)
return error;
error = copyin(p->cmap.blue, &b[index], count);
if (error)
return error;
}
/* verify that the new cursor data are valid */
if (v & WSDISPLAY_CURSOR_DOSHAPE) {
if (p->size.x > IGS_CURSOR_MAX_SIZE
|| p->size.y > IGS_CURSOR_MAX_SIZE)
return EINVAL;
iwidth = (p->size.x + 7) >> 3; /* bytes per scan line */
icount = iwidth * p->size.y;
error = copyin(p->image, image, icount);
if (error)
return error;
error = copyin(p->mask, mask, icount);
if (error)
return error;
}
/* enforce that the position is within screen bounds */
if (v & WSDISPLAY_CURSOR_DOPOS) {
struct rasops_info *ri = &dc->dc_vd.active->scr_ri;
pos = p->pos; /* local copy we can write to */
if (pos.x >= ri->ri_width)
pos.x = ri->ri_width - 1;
if (pos.y >= ri->ri_height)
pos.y = ri->ri_height - 1;
}
/* enforce that the hot spot is within sprite bounds */
if (v & WSDISPLAY_CURSOR_DOHOT)
hot = p->hot; /* local copy we can write to */
if (v & (WSDISPLAY_CURSOR_DOHOT | WSDISPLAY_CURSOR_DOSHAPE)) {
const struct wsdisplay_curpos *nsize;
struct wsdisplay_curpos *nhot;
nsize = (v & WSDISPLAY_CURSOR_DOSHAPE) ?
&p->size : &cc->cc_size;
nhot = (v & WSDISPLAY_CURSOR_DOHOT) ? &hot : &cc->cc_hot;
if (nhot->x >= nsize->x)
nhot->x = nsize->x - 1;
if (nhot->y >= nsize->y)
nhot->y = nsize->y - 1;
}
/* copy data to the driver's cursor info */
if (v & WSDISPLAY_CURSOR_DOCUR)
dc->dc_curenb = p->enable;
if (v & WSDISPLAY_CURSOR_DOPOS)
cc->cc_pos = pos; /* local copy, possibly corrected */
if (v & WSDISPLAY_CURSOR_DOHOT)
cc->cc_hot = hot; /* local copy, possibly corrected */
if (v & WSDISPLAY_CURSOR_DOCMAP) {
memcpy(&cc->cc_color[index], &r[index], count);
memcpy(&cc->cc_color[index + 2], &g[index], count);
memcpy(&cc->cc_color[index + 4], &b[index], count);
}
if (v & WSDISPLAY_CURSOR_DOSHAPE) {
u_int trailing_bits;
/* init sprite to be all transparent */
memset(cc->cc_sprite, 0xaa, IGS_CURSOR_DATA_SIZE);
/* first scanline */
ip = cc->cc_image;
mp = cc->cc_mask;
dp = cc->cc_sprite;
for (line = 0; line < height; ++line) {
for (i = 0; i < width; ++i) {
is = expand[ip[i]]; /* image: 0 -> 00, 1 -> 01 */
ms = expand[mp[i]]; /* mask: 0 -> 00, 1 -> 11 */
ms |= (ms << 1);
dp[i] = (0xaaaa & ~ms) | (is & ms);
}
/* next scanline */
ip += width;
mp += width;
dp += 8; /* 64 pixels, 2bpp, 8 pixels per short = 8 shorts */
}
}
/*
* Propagate cursor changes to the device.
* "which" is composed of WSDISPLAY_CURSOR_DO* bits.
*/
static void
igsfb_update_cursor(struct igsfb_devconfig *dc, u_int which)
{
bus_space_tag_t iot = dc->dc_iot;
bus_space_handle_t ioh = dc->dc_ioh;
uint8_t curctl;
curctl = 0; /* XXX: gcc */
/*
* We will need to tweak sprite control register for cursor
* visibility and cursor color map manipulation.
*/
if (which & (WSDISPLAY_CURSOR_DOCUR | WSDISPLAY_CURSOR_DOCMAP)) {
curctl = igs_ext_read(iot, ioh, IGS_EXT_SPRITE_CTL);
}
if (which & WSDISPLAY_CURSOR_DOSHAPE) {
uint8_t *dst = bus_space_vaddr(dc->dc_memt, dc->dc_crh);
/*
* memcpy between spaces of different endianness would
* be ... special. We cannot be sure if memcpy gonna
* push data in 4-byte chunks, we can't pre-bswap it,
* so do it byte-by-byte to preserve byte ordering.
*/
if (IGSFB_HW_SOFT_BSWAP(dc)) {
uint8_t *src = (uint8_t *)dc->dc_cursor.cc_sprite;
int i;
for (i = 0; i < IGS_CURSOR_DATA_SIZE; ++i)
*dst++ = *src++;
} else {
memcpy(dst, dc->dc_cursor.cc_sprite,
IGS_CURSOR_DATA_SIZE);
}
}
if (which & (WSDISPLAY_CURSOR_DOPOS | WSDISPLAY_CURSOR_DOHOT)) {
/* code shared with WSDISPLAYIO_SCURPOS */
igsfb_update_curpos(dc);
}
if (which & WSDISPLAY_CURSOR_DOCMAP) {
uint8_t *p;
/* tell DAC we want access to the cursor palette */
igs_ext_write(iot, ioh, IGS_EXT_SPRITE_CTL,
curctl | IGS_EXT_SPRITE_DAC_PEL);
/*
* Accelerated text mode cursor that uses hardware sprite.
*/
static void
igsfb_accel_cursor(void *cookie, int on, int row, int col)
{
struct rasops_info *ri = (struct rasops_info *)cookie;
struct vcons_screen *scr = ri->ri_hw;
struct igsfb_devconfig *dc = scr->scr_cookie;
struct igs_hwcursor *cc = &dc->dc_cursor;
u_int which;
ri->ri_crow = row;
ri->ri_ccol = col;
which = 0;
if (on) {
ri->ri_flg |= RI_CURSOR;
/* only bother to move the cursor if it's visible */
cc->cc_pos.x = ri->ri_xorigin
+ ri->ri_ccol * ri->ri_font->fontwidth;
cc->cc_pos.y = ri->ri_yorigin
+ ri->ri_crow * ri->ri_font->fontheight;
which |= WSDISPLAY_CURSOR_DOPOS;
} else
ri->ri_flg &= ~RI_CURSOR;
if (dc->dc_curenb != on) {
dc->dc_curenb = on;
which |= WSDISPLAY_CURSOR_DOCUR;
}
/* propagate changes to the device */
igsfb_update_cursor(dc, which);
}
/*
* Accelerated raster ops that use graphic coprocessor.
*/
static int
igsfb_accel_wait(struct igsfb_devconfig *dc)
{
bus_space_tag_t t = dc->dc_iot;
bus_space_handle_t h = dc->dc_coph;
int timo = 100000;
uint8_t reg;
/*
* Not really implemented here, but we need to hook into the one
* supplied by rasops so that we can synchronize with the COP.
*/
static void
igsfb_accel_putchar(void *cookie, int row, int col, u_int uc, long attr)
{
struct rasops_info *ri = (struct rasops_info *)cookie;
struct vcons_screen *scr = ri->ri_hw;
struct igsfb_devconfig *dc = (struct igsfb_devconfig *)scr->scr_cookie;