NetBSD/sys/dev/ic/igsfb.c
jmmv 7a51d4dddc Add an extra cookie to the ioctl and mmap wsdisplay accessops that points
to the screen on which they are being called.  The driver cannot guess
this by itself but it is needed to implement, at least, the getwschar and
putwschar functions in the correct place.  There are no functional changes
yet.

Tested on i386 (vga, vga_raster, machfb, vesafb), macppc and sparc64.
Suggested and reviewed by macallan@.
2006-04-12 19:38:22 +00:00

1359 lines
34 KiB
C

/* $NetBSD: igsfb.c,v 1.38 2006/04/12 19:38:23 jmmv Exp $ */
/*
* 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.38 2006/04/12 19:38:23 jmmv Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/ioctl.h>
#include <sys/buf.h>
#include <uvm/uvm_extern.h>
#include <machine/bus.h>
#include <dev/wscons/wsdisplayvar.h>
#include <dev/wscons/wsconsio.h>
#include <dev/wsfont/wsfont.h>
#include <dev/rasops/rasops.h>
#include <dev/wscons/wsdisplay_vconsvar.h>
#include <dev/ic/igsfbreg.h>
#include <dev/ic/igsfbvar.h>
#include "opt_wsemul.h"
struct igsfb_devconfig igsfb_console_dc;
/*
* wsscreen
*/
/* filled from rasops_info in igsfb_init_wsdisplay */
static struct wsscreen_descr igsfb_stdscreen = {
.name = "std",
};
static const struct wsscreen_descr *_igsfb_scrlist[] = {
&igsfb_stdscreen,
};
static const struct wsscreen_list igsfb_screenlist = {
.nscreens = sizeof(_igsfb_scrlist) / sizeof(_igsfb_scrlist[0]),
.screens = _igsfb_scrlist,
};
/*
* wsdisplay_accessops
*/
static int igsfb_ioctl(void *, void *, u_long, caddr_t, int, struct lwp *);
static paddr_t igsfb_mmap(void *, void *, off_t, int);
static struct wsdisplay_accessops igsfb_accessops = {
.ioctl = igsfb_ioctl,
.mmap = igsfb_mmap,
};
/*
* acceleration
*/
static int igsfb_make_text_cursor(struct igsfb_devconfig *,
struct vcons_screen *);
static void igsfb_accel_cursor(void *, int, int, int);
static int igsfb_accel_wait(struct igsfb_devconfig *);
static void igsfb_accel_fill(struct igsfb_devconfig *,
uint32_t, uint32_t, uint16_t, uint16_t);
static void igsfb_accel_copy(struct igsfb_devconfig *,
uint32_t, uint32_t, uint16_t, uint16_t);
static void igsfb_accel_copycols(void *, int, int, int, int);
static void igsfb_accel_erasecols(void *, int, int, int, long);
static void igsfb_accel_copyrows(void *, int, int, int);
static void igsfb_accel_eraserows(void *, int, int, long);
static void igsfb_accel_putchar(void *, int, int, u_int, long);
/*
* internal functions
*/
static int igsfb_init_video(struct igsfb_devconfig *);
static void igsfb_init_cmap(struct igsfb_devconfig *);
static uint16_t igsfb_spread_bits_8(uint8_t);
static void igsfb_init_bit_table(struct igsfb_devconfig *);
static void igsfb_init_wsdisplay(void *, struct vcons_screen *, int,
long *);
static void igsfb_blank_screen(struct igsfb_devconfig *, int);
static int igsfb_get_cmap(struct igsfb_devconfig *,
struct wsdisplay_cmap *);
static int igsfb_set_cmap(struct igsfb_devconfig *,
const struct wsdisplay_cmap *);
static void igsfb_update_cmap(struct igsfb_devconfig *, u_int, u_int);
static void igsfb_set_curpos(struct igsfb_devconfig *,
const struct wsdisplay_curpos *);
static void igsfb_update_curpos(struct igsfb_devconfig *);
static int igsfb_get_cursor(struct igsfb_devconfig *,
struct wsdisplay_cursor *);
static int igsfb_set_cursor(struct igsfb_devconfig *,
const struct wsdisplay_cursor *);
static void igsfb_update_cursor(struct igsfb_devconfig *, u_int);
static void igsfb_convert_cursor_data(struct igsfb_devconfig *,
u_int, u_int);
int
igsfb_cnattach_subr(struct igsfb_devconfig *dc)
{
struct rasops_info *ri;
long defattr;
KASSERT(dc == &igsfb_console_dc);
igsfb_init_video(dc);
dc->dc_vd.active = NULL;
igsfb_init_wsdisplay(dc, &dc->dc_console, 1, &defattr);
ri = &dc->dc_console.scr_ri;
ri->ri_hw = &dc->dc_console;
dc->dc_console.scr_cookie = dc;
(*ri->ri_ops.allocattr)(ri,
WS_DEFAULT_FG, /* fg */
WS_DEFAULT_BG, /* bg */
0, /* wsattrs */
&defattr);
wsdisplay_cnattach(&igsfb_stdscreen,
ri, /* emulcookie */
0, 0, /* cursor position */
defattr);
return 0;
}
/*
* 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;
KASSERT(dc != NULL);
if (!isconsole) {
igsfb_init_video(dc);
}
vcons_init(&dc->dc_vd, dc, &igsfb_stdscreen, &igsfb_accessops);
dc->dc_vd.init_screen = igsfb_init_wsdisplay;
vcons_init_screen(&dc->dc_vd, &dc->dc_console, 1, &defattr);
dc->dc_console.scr_flags |= VCONS_SCREEN_IS_STATIC;
printf("%s: %dMB, %s%dx%d, %dbpp\n",
sc->sc_dev.dv_xname,
(uint32_t)(dc->dc_vmemsz >> 20),
(dc->dc_hwflags & IGSFB_HW_BSWAP)
? (dc->dc_hwflags & IGSFB_HW_BE_SELECT)
? "hardware bswap, " : "software bswap, "
: "",
dc->dc_width, dc->dc_height, dc->dc_depth);
ri = &dc->dc_console.scr_ri;
ri->ri_ops.eraserows(ri, 0, ri->ri_rows, defattr);
/* attach wsdisplay */
waa.console = isconsole;
waa.scrdata = &igsfb_screenlist;
waa.accessops = &igsfb_accessops;
waa.accesscookie = &dc->dc_vd;
config_found(&sc->sc_dev, &waa, wsemuldisplaydevprint);
}
static int
igsfb_init_video(struct igsfb_devconfig *dc)
{
bus_space_handle_t tmph;
uint8_t *p;
int need_bswap;
bus_addr_t fbaddr, craddr;
off_t croffset;
uint8_t busctl, curctl;
/* 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;
}
p = bus_space_vaddr(dc->dc_memt, tmph);
#if BYTE_ORDER == BIG_ENDIAN
*((uint32_t *)p) = 0x12345678;
#else
*((uint32_t *)p) = 0x78563412;
#endif
if (p[0] == 0x12 && p[1] == 0x34 && p[2] == 0x56 && p[3] == 0x78)
need_bswap = 0;
else
need_bswap = 1;
bus_space_unmap(dc->dc_memt, tmph, sizeof(uint32_t));
/*
* 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;
}
}
/*
* XXX: TODO: make it possible to select the desired video mode.
* For now - hardcode to 1024x768/8bpp. This is what Krups OFW uses.
*/
igsfb_hw_setup(dc);
dc->dc_width = 1024;
dc->dc_height = 768;
dc->dc_depth = 8;
dc->dc_stride = dc->dc_width;
/*
* Don't map in all N megs, just the amount we need for the wsscreen.
*/
dc->dc_fbsz = dc->dc_width * dc->dc_height; /* XXX: 8bpp specific */
if (bus_space_map(dc->dc_memt, fbaddr, dc->dc_fbsz,
dc->dc_memflags | BUS_SPACE_MAP_LINEAR,
&dc->dc_fbh) != 0)
{
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)
{
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));
memset(bus_space_vaddr(dc->dc_memt, dc->dc_crh),
/* transparent */ 0xaa, IGS_CURSOR_DATA_SIZE);
curctl = igs_ext_read(dc->dc_iot, dc->dc_ioh, IGS_EXT_SPRITE_CTL);
curctl |= IGS_EXT_SPRITE_64x64;
curctl &= ~IGS_EXT_SPRITE_VISIBLE;
igs_ext_write(dc->dc_iot, dc->dc_ioh, IGS_EXT_SPRITE_CTL, curctl);
dc->dc_curenb = 0;
/*
* Map and init graphic coprocessor for 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;
}
/* XXX: hardcoded 8bpp */
bus_space_write_2(dc->dc_iot, dc->dc_coph,
IGS_COP_SRC_MAP_WIDTH_REG,
dc->dc_width - 1);
bus_space_write_2(dc->dc_iot, dc->dc_coph,
IGS_COP_DST_MAP_WIDTH_REG,
dc->dc_width - 1);
bus_space_write_1(dc->dc_iot, dc->dc_coph,
IGS_COP_MAP_FMT_REG,
IGS_COP_MAP_8BPP);
}
/* make sure screen is not blanked */
dc->dc_blanked = 0;
igsfb_blank_screen(dc, dc->dc_blanked);
return 0;
}
static void
igsfb_init_cmap(struct igsfb_devconfig *dc)
{
bus_space_tag_t iot = dc->dc_iot;
bus_space_handle_t ioh = dc->dc_ioh;
const uint8_t *p;
int i;
p = rasops_cmap; /* "ANSI" color map */
/* 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 (XXX: use defattr's background?) */
igs_ext_write(iot, ioh, IGS_EXT_OVERSCAN_RED, 0);
igs_ext_write(iot, ioh, IGS_EXT_OVERSCAN_GREEN, 0);
igs_ext_write(iot, ioh, IGS_EXT_OVERSCAN_BLUE, 0);
}
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) && (dc->dc_vd.active != NULL))
return;
ri->ri_flg = RI_CENTER | RI_FULLCLEAR;
if (IGSFB_HW_SOFT_BSWAP(dc))
ri->ri_flg |= RI_BSWAP;
ri->ri_depth = dc->dc_depth;
ri->ri_width = dc->dc_width;
ri->ri_height = dc->dc_height;
ri->ri_stride = dc->dc_stride;
ri->ri_bits = bus_space_vaddr(dc->dc_memt, dc->dc_fbh);
/*
* 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);
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);
}
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, 34, 80);
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;
if (dc->dc_id >= 0x2000) { /* XXX */
/* accelerated erase/copy */
ri->ri_ops.copycols = igsfb_accel_copycols;
ri->ri_ops.erasecols = igsfb_accel_erasecols;
ri->ri_ops.copyrows = igsfb_accel_copyrows;
ri->ri_ops.eraserows = igsfb_accel_eraserows;
/* putchar hook to sync with the cop */
dc->dc_ri_putchar = ri->ri_ops.putchar;
ri->ri_ops.putchar = igsfb_accel_putchar;
}
igsfb_stdscreen.nrows = ri->ri_rows;
igsfb_stdscreen.ncols = ri->ri_cols;
igsfb_stdscreen.textops = &ri->ri_ops;
igsfb_stdscreen.capabilities = ri->ri_caps;
}
/*
* Init cursor data in dc_cursor for the accelerated text cursor.
*/
static int
igsfb_make_text_cursor(struct igsfb_devconfig *dc, struct vcons_screen *scr)
{
struct rasops_info *ri = &scr->scr_ri;
struct wsdisplay_font *f = ri->ri_font;
uint16_t cc_scan[8]; /* one sprite scanline */
uint16_t s;
int w, i;
KASSERT(f->fontwidth <= IGS_CURSOR_MAX_SIZE);
KASSERT(f->fontheight <= IGS_CURSOR_MAX_SIZE);
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;
}
dc->dc_cursor.cc_size.x = f->fontwidth;
dc->dc_cursor.cc_size.y = f->fontheight;
dc->dc_cursor.cc_hot.x = 0;
dc->dc_cursor.cc_hot.y = 0;
/* 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);
}
}
/*
* wsdisplay_accessops: mmap()
* XXX: allow mmapping i/o mapped i/o regs if INSECURE???
*/
static paddr_t
igsfb_mmap(void *v, void *vs, off_t offset, int prot)
{
struct vcons_data *vd = v;
struct igsfb_devconfig *dc = vd->cookie;
if (offset >= dc->dc_memsz || offset < 0)
return -1;
return bus_space_mmap(dc->dc_memt, dc->dc_memaddr, offset, prot,
dc->dc_memflags | BUS_SPACE_MAP_LINEAR);
}
/*
* wsdisplay_accessops: ioctl()
*/
static int
igsfb_ioctl(void *v, void *vs, u_long cmd, caddr_t data, int flag,
struct lwp *l)
{
struct vcons_data *vd = v;
struct igsfb_devconfig *dc = vd->cookie;
int cursor_busy;
int turnoff;
/* don't permit cursor ioctls if we use sprite for text cursor */
cursor_busy = !dc->dc_mapped
&& (dc->dc_hwflags & IGSFB_HW_TEXT_CURSOR);
switch (cmd) {
case WSDISPLAYIO_GTYPE:
*(u_int *)data = WSDISPLAY_TYPE_PCIMISC;
return 0;
case WSDISPLAYIO_GINFO:
#define wsd_fbip ((struct wsdisplay_fbinfo *)data)
wsd_fbip->height = dc->dc_height;
wsd_fbip->width = dc->dc_width;
wsd_fbip->depth = dc->dc_depth;
wsd_fbip->cmsize = IGS_CMAP_SIZE;
#undef wsd_fbip
return 0;
case WSDISPLAYIO_LINEBYTES:
*(int *)data = dc->dc_stride;
return 0;
case WSDISPLAYIO_SMODE:
#define d (*(int *)data)
if (d != WSDISPLAYIO_MODE_EMUL) {
dc->dc_mapped = 1;
/* turn off hardware cursor */
if (dc->dc_hwflags & IGSFB_HW_TEXT_CURSOR) {
dc->dc_curenb = 0;
igsfb_update_cursor(dc,
WSDISPLAY_CURSOR_DOCUR);
}
} else {
dc->dc_mapped = 0;
/* reinit sprite for text cursor */
if (dc->dc_hwflags & IGSFB_HW_TEXT_CURSOR) {
igsfb_make_text_cursor(dc, dc->dc_vd.active);
dc->dc_curenb = 0;
igsfb_update_cursor(dc,
WSDISPLAY_CURSOR_DOSHAPE
| WSDISPLAY_CURSOR_DOCUR);
}
vcons_redraw_screen(vd->active);
}
return 0;
case WSDISPLAYIO_GVIDEO:
*(u_int *)data = dc->dc_blanked ?
WSDISPLAYIO_VIDEO_OFF : WSDISPLAYIO_VIDEO_ON;
return 0;
case WSDISPLAYIO_SVIDEO:
turnoff = (*(u_int *)data == WSDISPLAYIO_VIDEO_OFF);
if (dc->dc_blanked != turnoff) {
dc->dc_blanked = turnoff;
igsfb_blank_screen(dc, dc->dc_blanked);
}
return 0;
case WSDISPLAYIO_GETCMAP:
return igsfb_get_cmap(dc, (struct wsdisplay_cmap *)data);
case WSDISPLAYIO_PUTCMAP:
return igsfb_set_cmap(dc, (struct wsdisplay_cmap *)data);
case WSDISPLAYIO_GCURMAX:
((struct wsdisplay_curpos *)data)->x = IGS_CURSOR_MAX_SIZE;
((struct wsdisplay_curpos *)data)->y = IGS_CURSOR_MAX_SIZE;
return 0;
case WSDISPLAYIO_GCURPOS:
if (cursor_busy)
return EBUSY;
*(struct wsdisplay_curpos *)data = dc->dc_cursor.cc_pos;
return 0;
case WSDISPLAYIO_SCURPOS:
if (cursor_busy)
return EBUSY;
igsfb_set_curpos(dc, (struct wsdisplay_curpos *)data);
return 0;
case WSDISPLAYIO_GCURSOR:
if (cursor_busy)
return EBUSY;
return igsfb_get_cursor(dc, (struct wsdisplay_cursor *)data);
case WSDISPLAYIO_SCURSOR:
if (cursor_busy)
return EBUSY;
return igsfb_set_cursor(dc, (struct wsdisplay_cursor *)data);
}
return EPASSTHROUGH;
}
/*
* wsdisplay_accessops: ioctl(WSDISPLAYIO_SVIDEO)
*/
static void
igsfb_blank_screen(struct igsfb_devconfig *dc, int blank)
{
igs_ext_write(dc->dc_iot, dc->dc_ioh,
IGS_EXT_SYNC_CTL,
blank ? IGS_EXT_SYNC_H0 | IGS_EXT_SYNC_V0
: 0);
}
/*
* wsdisplay_accessops: ioctl(WSDISPLAYIO_GETCMAP)
* Served from the software cmap copy.
*/
static int
igsfb_get_cmap(struct igsfb_devconfig *dc, struct wsdisplay_cmap *p)
{
u_int index, count;
int err;
index = p->index;
count = p->count;
if (index >= IGS_CMAP_SIZE || count > IGS_CMAP_SIZE - index)
return EINVAL;
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;
memcpy(&dc->dc_cmap.r[index], &r[index], count);
memcpy(&dc->dc_cmap.g[index], &g[index], count);
memcpy(&dc->dc_cmap.b[index], &b[index], count);
/* propagate changes to the device */
igsfb_update_cmap(dc, index, count);
return 0;
}
/*
* Propagate specified part of the software cmap copy to the device.
*/
static void
igsfb_update_cmap(struct igsfb_devconfig *dc, u_int index, u_int count)
{
bus_space_tag_t t;
bus_space_handle_t h;
u_int last, i;
if (index >= IGS_CMAP_SIZE)
return;
last = index + count;
if (last > IGS_CMAP_SIZE)
last = IGS_CMAP_SIZE;
t = dc->dc_iot;
h = dc->dc_ioh;
/* start palette writing, index is autoincremented by hardware */
bus_space_write_1(t, h, IGS_DAC_PEL_WRITE_IDX, index);
for (i = index; i < last; ++i) {
bus_space_write_1(t, h, IGS_DAC_PEL_DATA, dc->dc_cmap.r[i]);
bus_space_write_1(t, h, IGS_DAC_PEL_DATA, dc->dc_cmap.g[i]);
bus_space_write_1(t, h, IGS_DAC_PEL_DATA, dc->dc_cmap.b[i]);
}
}
/*
* wsdisplay_accessops: ioctl(WSDISPLAYIO_SCURPOS)
*/
static void
igsfb_set_curpos(struct igsfb_devconfig *dc,
const struct wsdisplay_curpos *curpos)
{
struct rasops_info *ri = &dc->dc_vd.active->scr_ri;
u_int x = curpos->x, y = curpos->y;
if (x >= ri->ri_width)
x = ri->ri_width - 1;
if (y >= ri->ri_height)
y = ri->ri_height - 1;
dc->dc_cursor.cc_pos.x = x;
dc->dc_cursor.cc_pos.y = y;
/* propagate changes to the device */
igsfb_update_curpos(dc);
}
static void
igsfb_update_curpos(struct igsfb_devconfig *dc)
{
bus_space_tag_t t;
bus_space_handle_t h;
int x, xoff, y, yoff;
xoff = 0;
x = dc->dc_cursor.cc_pos.x - dc->dc_cursor.cc_hot.x;
if (x < 0) {
xoff = -x;
x = 0;
}
yoff = 0;
y = dc->dc_cursor.cc_pos.y - dc->dc_cursor.cc_hot.y;
if (y < 0) {
yoff = -y;
y = 0;
}
t = dc->dc_iot;
h = dc->dc_ioh;
igs_ext_write(t, h, IGS_EXT_SPRITE_HSTART_LO, x & 0xff);
igs_ext_write(t, h, IGS_EXT_SPRITE_HSTART_HI, (x >> 8) & 0x07);
igs_ext_write(t, h, IGS_EXT_SPRITE_HPRESET, xoff & 0x3f);
igs_ext_write(t, h, IGS_EXT_SPRITE_VSTART_LO, y & 0xff);
igs_ext_write(t, h, IGS_EXT_SPRITE_VSTART_HI, (y >> 8) & 0x07);
igs_ext_write(t, h, IGS_EXT_SPRITE_VPRESET, yoff & 0x3f);
}
/*
* wsdisplay_accessops: ioctl(WSDISPLAYIO_GCURSOR)
*/
static int
igsfb_get_cursor(struct igsfb_devconfig *dc, struct wsdisplay_cursor *p)
{
/* XXX: TODO */
return 0;
}
/*
* wsdisplay_accessops: ioctl(WSDISPLAYIO_SCURSOR)
*/
static int
igsfb_set_cursor(struct igsfb_devconfig *dc, const struct wsdisplay_cursor *p)
{
struct igs_hwcursor *cc;
struct wsdisplay_curpos pos, hot;
u_int v, index, count, icount, iwidth;
uint8_t r[2], g[2], b[2], image[512], mask[512];
int error;
cc = &dc->dc_cursor;
v = p->which;
index = count = icount = iwidth = 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;
memcpy(cc->cc_image, image, icount);
memcpy(cc->cc_mask, mask, icount);
cc->cc_size = p->size;
/* clear trailing bits in the "partial" mask bytes */
trailing_bits = p->size.x & 0x07;
if (trailing_bits != 0) {
const u_int cutmask = ~((~0) << trailing_bits);
u_char *mp;
u_int i;
mp = cc->cc_mask + iwidth - 1;
for (i = 0; i < p->size.y; ++i) {
*mp &= cutmask;
mp += iwidth;
}
}
igsfb_convert_cursor_data(dc, iwidth, p->size.y);
}
/* propagate changes to the device */
igsfb_update_cursor(dc, v);
return 0;
}
/*
* Convert incoming 1bpp cursor image/mask into native 2bpp format.
*/
static void
igsfb_convert_cursor_data(struct igsfb_devconfig *dc,
u_int width, u_int height)
{
struct igs_hwcursor *cc = &dc->dc_cursor;
uint16_t *expand = dc->dc_bexpand;
uint8_t *ip, *mp;
uint16_t is, ms, *dp;
u_int line, i;
/* 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);
p = dc->dc_cursor.cc_color;
bus_space_write_1(iot, ioh, IGS_DAC_PEL_WRITE_IDX, 0);
bus_space_write_1(iot, ioh, IGS_DAC_PEL_DATA, p[0]);
bus_space_write_1(iot, ioh, IGS_DAC_PEL_DATA, p[2]);
bus_space_write_1(iot, ioh, IGS_DAC_PEL_DATA, p[4]);
bus_space_write_1(iot, ioh, IGS_DAC_PEL_WRITE_IDX, 1);
bus_space_write_1(iot, ioh, IGS_DAC_PEL_DATA, p[1]);
bus_space_write_1(iot, ioh, IGS_DAC_PEL_DATA, p[3]);
bus_space_write_1(iot, ioh, IGS_DAC_PEL_DATA, p[5]);
/* restore access to the normal palette */
igs_ext_write(iot, ioh, IGS_EXT_SPRITE_CTL, curctl);
}
if (which & WSDISPLAY_CURSOR_DOCUR) {
if ((curctl & IGS_EXT_SPRITE_VISIBLE) == 0
&& dc->dc_curenb)
igs_ext_write(iot, ioh, IGS_EXT_SPRITE_CTL,
curctl | IGS_EXT_SPRITE_VISIBLE);
else if ((curctl & IGS_EXT_SPRITE_VISIBLE) != 0
&& !dc->dc_curenb)
igs_ext_write(iot, ioh, IGS_EXT_SPRITE_CTL,
curctl & ~IGS_EXT_SPRITE_VISIBLE);
}
}
/*
* 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;
while (timo--) {
reg = bus_space_read_1(t, h, IGS_COP_CTL_REG);
if ((reg & IGS_COP_CTL_BUSY) == 0)
return 0;
}
return 1;
}
static void
igsfb_accel_copy(struct igsfb_devconfig *dc, uint32_t src, uint32_t dst,
uint16_t width, uint16_t height)
{
bus_space_tag_t t = dc->dc_iot;
bus_space_handle_t h = dc->dc_coph;
uint32_t toend;
uint8_t drawcmd;
drawcmd = IGS_COP_DRAW_ALL;
if (dst > src) {
toend = dc->dc_vd.active->scr_ri.ri_width * (height - 1) + (width - 1);
src += toend;
dst += toend;
drawcmd |= IGS_COP_OCTANT_X_NEG | IGS_COP_OCTANT_Y_NEG;
}
igsfb_accel_wait(dc);
bus_space_write_1(t, h, IGS_COP_CTL_REG, 0);
bus_space_write_1(t, h, IGS_COP_FG_MIX_REG, IGS_COP_MIX_S);
bus_space_write_2(t, h, IGS_COP_WIDTH_REG, width - 1);
bus_space_write_2(t, h, IGS_COP_HEIGHT_REG, height - 1);
bus_space_write_4(t, h, IGS_COP_SRC_START_REG, src);
bus_space_write_4(t, h, IGS_COP_DST_START_REG, dst);
bus_space_write_1(t, h, IGS_COP_PIXEL_OP_0_REG, drawcmd);
bus_space_write_1(t, h, IGS_COP_PIXEL_OP_1_REG, IGS_COP_PPM_FIXED_FG);
bus_space_write_1(t, h, IGS_COP_PIXEL_OP_2_REG, 0);
bus_space_write_1(t, h, IGS_COP_PIXEL_OP_3_REG,
IGS_COP_OP_PXBLT | IGS_COP_OP_FG_FROM_SRC);
}
static void
igsfb_accel_fill(struct igsfb_devconfig *dc, uint32_t color, uint32_t dst,
uint16_t width, uint16_t height)
{
bus_space_tag_t t = dc->dc_iot;
bus_space_handle_t h = dc->dc_coph;
igsfb_accel_wait(dc);
bus_space_write_1(t, h, IGS_COP_CTL_REG, 0);
bus_space_write_1(t, h, IGS_COP_FG_MIX_REG, IGS_COP_MIX_S);
bus_space_write_2(t, h, IGS_COP_WIDTH_REG, width - 1);
bus_space_write_2(t, h, IGS_COP_HEIGHT_REG, height - 1);
bus_space_write_4(t, h, IGS_COP_DST_START_REG, dst);
bus_space_write_4(t, h, IGS_COP_FG_REG, color);
bus_space_write_1(t, h, IGS_COP_PIXEL_OP_0_REG, IGS_COP_DRAW_ALL);
bus_space_write_1(t, h, IGS_COP_PIXEL_OP_1_REG, IGS_COP_PPM_FIXED_FG);
bus_space_write_1(t, h, IGS_COP_PIXEL_OP_2_REG, 0);
bus_space_write_1(t, h, IGS_COP_PIXEL_OP_3_REG, IGS_COP_OP_PXBLT);
}
static void
igsfb_accel_copyrows(void *cookie, int src, int dst, int num)
{
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;
uint32_t srp, dsp;
uint16_t width, height;
width = ri->ri_emuwidth;
height = num * ri->ri_font->fontheight;
srp = ri->ri_xorigin
+ ri->ri_width * (ri->ri_yorigin
+ src * ri->ri_font->fontheight);
dsp = ri->ri_xorigin
+ ri->ri_width * (ri->ri_yorigin
+ dst * ri->ri_font->fontheight);
igsfb_accel_copy(dc, srp, dsp, width, height);
}
static void
igsfb_accel_copycols(void *cookie, int row, int src, int dst, int num)
{
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;
uint32_t rowp, srp, dsp;
uint16_t width, height;
width = num * ri->ri_font->fontwidth;
height = ri->ri_font->fontheight;
rowp = ri->ri_xorigin
+ ri->ri_width * (ri->ri_yorigin
+ row * ri->ri_font->fontheight);
srp = rowp + src * ri->ri_font->fontwidth;
dsp = rowp + dst * ri->ri_font->fontwidth;
igsfb_accel_copy(dc, srp, dsp, width, height);
}
static void
igsfb_accel_eraserows(void *cookie, int row, int num, 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;
uint32_t color;
uint32_t dsp;
uint16_t width, height;
if (num == ri->ri_rows && (ri->ri_flg & RI_FULLCLEAR) != 0) {
width = ri->ri_width;
height = ri->ri_height;
dsp = 0;
} else {
width = ri->ri_emuwidth;
height = num * ri->ri_font->fontheight;
dsp = ri->ri_xorigin
+ ri->ri_width * (ri->ri_yorigin
+ row * ri->ri_font->fontheight);
}
/* XXX: we "know" the encoding that rasops' allocattr uses */
color = (attr >> 16) & 0xff;
igsfb_accel_fill(dc, color, dsp, width, height);
}
static void
igsfb_accel_erasecols(void *cookie, int row, int col, int num, 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;
uint32_t color;
uint32_t rowp, dsp;
uint16_t width, height;
width = num * ri->ri_font->fontwidth;
height = ri->ri_font->fontheight;
rowp = ri->ri_xorigin
+ ri->ri_width * (ri->ri_yorigin
+ row * ri->ri_font->fontheight);
dsp = rowp + col * ri->ri_font->fontwidth;
/* XXX: we "know" the encoding that rasops' allocattr uses */
color = (attr >> 16) & 0xff;
igsfb_accel_fill(dc, color, dsp, width, height);
}
/*
* 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;
igsfb_accel_wait(dc);
(*dc->dc_ri_putchar)(cookie, row, col, uc, attr);
}