#include <sys/types.h>
#include <sys/visual_io.h>
#include <sys/fbio.h>
#include <sys/ddi.h>
#include <sys/kd.h>
#include <sys/sunddi.h>
#include <sys/rgb.h>
#include <sys/gfx_private.h>
#include "gfxp_fb.h"
#define MYNAME "gfxp_bitmap"
static ddi_device_acc_attr_t dev_attr = {
DDI_DEVICE_ATTR_V0,
DDI_NEVERSWAP_ACC,
DDI_MERGING_OK_ACC
};
static struct fbgattr bitmap_attr = {
FBTYPE_MEMCOLOR, 0,
{ FBTYPE_MEMCOLOR, 0, 0, 0, 0, 0 },
{ 0, FBTYPE_MEMCOLOR, { 0 } },
{ -1 }
};
static struct vis_identifier gfxp_bitmap_ident = { "illumos_fb" };
static void bitmap_copy_fb(struct gfxp_fb_softc *, uint8_t *, uint8_t *);
static int bitmap_kdsetmode(struct gfxp_fb_softc *, int);
static int bitmap_devinit(struct gfxp_fb_softc *, struct vis_devinit *);
static void bitmap_cons_copy(struct gfxp_fb_softc *, struct vis_conscopy *);
static void bitmap_cons_display(struct gfxp_fb_softc *,
struct vis_consdisplay *);
static int bitmap_cons_clear(struct gfxp_fb_softc *,
struct vis_consclear *);
static void bitmap_cons_cursor(struct gfxp_fb_softc *,
struct vis_conscursor *);
static void bitmap_polled_copy(struct vis_polledio_arg *,
struct vis_conscopy *);
static void bitmap_polled_display(struct vis_polledio_arg *,
struct vis_consdisplay *);
static void bitmap_polled_cursor(struct vis_polledio_arg *,
struct vis_conscursor *);
static int bitmap_suspend(struct gfxp_fb_softc *softc);
static void bitmap_resume(struct gfxp_fb_softc *softc);
static int bitmap_devmap(dev_t dev, devmap_cookie_t dhp, offset_t off,
size_t len, size_t *maplen, uint_t model, void *ptr);
static struct gfxp_ops gfxp_bitmap_ops = {
.ident = &gfxp_bitmap_ident,
.kdsetmode = bitmap_kdsetmode,
.devinit = bitmap_devinit,
.cons_copy = bitmap_cons_copy,
.cons_display = bitmap_cons_display,
.cons_cursor = bitmap_cons_cursor,
.cons_clear = bitmap_cons_clear,
.suspend = bitmap_suspend,
.resume = bitmap_resume,
.devmap = bitmap_devmap
};
void
gfxp_bm_register_fbops(gfxp_fb_softc_ptr_t ptr, struct gfxp_blt_ops *ops)
{
struct gfxp_fb_softc *softc = (struct gfxp_fb_softc *)ptr;
if (softc != NULL) {
softc->blt_ops.blt = ops->blt;
softc->blt_ops.copy = ops->copy;
softc->blt_ops.clear = ops->clear;
softc->blt_ops.setmode = ops->setmode;
}
}
void
gfxp_bm_getfb_info(gfxp_fb_softc_ptr_t ptr, struct gfxp_bm_fb_info *fbip)
{
struct gfxp_fb_softc *softc = (struct gfxp_fb_softc *)ptr;
switch (softc->fb_type) {
case GFXP_BITMAP:
fbip->xres = softc->console->fb.screen.x;
fbip->yres = softc->console->fb.screen.y;
fbip->bpp = softc->console->fb.bpp;
fbip->depth = softc->console->fb.depth;
break;
case GFXP_VGATEXT:
fbip->xres = 0;
fbip->yres = 0;
fbip->bpp = 0;
fbip->depth = 0;
break;
}
}
int
gfxp_bm_attach(dev_info_t *devi __unused, struct gfxp_fb_softc *softc)
{
softc->polledio.display = bitmap_polled_display;
softc->polledio.copy = bitmap_polled_copy;
softc->polledio.cursor = bitmap_polled_cursor;
softc->gfxp_ops = &gfxp_bitmap_ops;
softc->fbgattr = &bitmap_attr;
softc->silent = 0;
return (DDI_SUCCESS);
}
int
gfxp_bm_detach(dev_info_t *devi __unused, struct gfxp_fb_softc *softc)
{
if (softc == NULL || softc->console == NULL)
return (DDI_SUCCESS);
if (softc->console->fb.fb_size != 0) {
gfxp_unmap_kernel_space((gfxp_kva_t)softc->console->fb.fb,
softc->console->fb.fb_size);
fb_info.fb = NULL;
kmem_free(softc->console->fb.shadow_fb,
softc->console->fb.fb_size);
softc->console->fb.shadow_fb = NULL;
}
return (DDI_SUCCESS);
}
static void
bitmap_kdsettext(struct gfxp_fb_softc *softc)
{
bitmap_copy_fb(softc, softc->console->fb.shadow_fb,
softc->console->fb.fb);
}
static void
bitmap_kdsetgraphics(struct gfxp_fb_softc *softc __unused)
{
}
static int
bitmap_suspend(struct gfxp_fb_softc *softc __unused)
{
return (DDI_SUCCESS);
}
static void
bitmap_resume(struct gfxp_fb_softc *softc)
{
bitmap_kdsettext(softc);
}
static int
bitmap_kdsetmode(struct gfxp_fb_softc *softc, int mode)
{
switch (mode) {
case KD_TEXT:
if (softc->blt_ops.setmode != NULL)
if (softc->blt_ops.setmode(KD_TEXT) != 0)
return (EIO);
bitmap_kdsettext(softc);
break;
case KD_GRAPHICS:
bitmap_kdsetgraphics(softc);
if (softc->blt_ops.setmode != NULL)
if (softc->blt_ops.setmode(KD_GRAPHICS) != 0) {
bitmap_kdsettext(softc);
return (EIO);
}
break;
case KD_RESETTEXT:
if (softc->mode == KD_TEXT && softc->silent == 1) {
bitmap_kdsettext(softc);
}
mode = KD_TEXT;
break;
default:
return (EINVAL);
}
softc->mode = mode;
return (0);
}
static int
bitmap_setup_fb(struct gfxp_fb_softc *softc)
{
size_t size;
struct gfxfb_info *gfxfb_info;
softc->console = (union gfx_console *)&fb_info;
size = ptob(btopr(fb_info.fb_size));
softc->console->fb.fb_size = size;
softc->console->fb.fb = (uint8_t *)gfxp_map_kernel_space(fb_info.paddr,
size, GFXP_MEMORY_WRITECOMBINED);
if (softc->console->fb.fb == NULL)
return (DDI_FAILURE);
softc->console->fb.shadow_fb = kmem_zalloc(size, KM_SLEEP);
bitmap_attr.fbtype.fb_height = fb_info.screen.y;
bitmap_attr.fbtype.fb_width = fb_info.screen.x;
bitmap_attr.fbtype.fb_depth = fb_info.depth;
bitmap_attr.fbtype.fb_size = size;
if (fb_info.depth == 32)
bitmap_attr.fbtype.fb_cmsize = 1 << 24;
else
bitmap_attr.fbtype.fb_cmsize = 1 << fb_info.depth;
gfxfb_info = (struct gfxfb_info *)bitmap_attr.sattr.dev_specific;
gfxfb_info->terminal_origin_x = fb_info.terminal_origin.x;
gfxfb_info->terminal_origin_y = fb_info.terminal_origin.y;
gfxfb_info->pitch = fb_info.pitch;
gfxfb_info->font_width = fb_info.font_width;
gfxfb_info->font_height = fb_info.font_height;
gfxfb_info->red_mask_size = fb_info.rgb.red.size;
gfxfb_info->red_field_position = fb_info.rgb.red.pos;
gfxfb_info->green_mask_size = fb_info.rgb.green.size;
gfxfb_info->green_field_position = fb_info.rgb.green.pos;
gfxfb_info->blue_mask_size = fb_info.rgb.blue.size;
gfxfb_info->blue_field_position = fb_info.rgb.blue.pos;
return (DDI_SUCCESS);
}
static int
bitmap_devinit(struct gfxp_fb_softc *softc, struct vis_devinit *data)
{
union gfx_console *console;
if (bitmap_setup_fb(softc) == DDI_FAILURE)
return (1);
console = softc->console;
bitmap_copy_fb(softc, console->fb.fb, console->fb.shadow_fb);
data->version = VIS_CONS_REV;
data->width = console->fb.screen.x;
data->height = console->fb.screen.y;
data->linebytes = console->fb.pitch;
data->color_map = boot_color_map;
data->depth = console->fb.depth;
data->mode = VIS_PIXEL;
data->polledio = &softc->polledio;
#if 0
data->modechg_cb;
data->modechg_arg;
#endif
return (0);
}
static void
bitmap_copy_fb(struct gfxp_fb_softc *softc, uint8_t *src, uint8_t *dst)
{
uint32_t i, pitch, height;
pitch = softc->console->fb.pitch;
height = softc->console->fb.screen.y;
for (i = 0; i < height; i++) {
(void) memmove(dst + i * pitch, src + i * pitch, pitch);
}
}
static void
bitmap_cons_copy(struct gfxp_fb_softc *softc, struct vis_conscopy *ma)
{
union gfx_console *console;
uint32_t soffset, toffset;
uint32_t width, height, pitch;
uint8_t *src, *dst, *sdst;
int i;
console = softc->console;
soffset = ma->s_col * console->fb.bpp + ma->s_row * console->fb.pitch;
toffset = ma->t_col * console->fb.bpp + ma->t_row * console->fb.pitch;
src = console->fb.shadow_fb + soffset;
dst = console->fb.fb + toffset;
sdst = console->fb.shadow_fb + toffset;
width = (ma->e_col - ma->s_col + 1) * console->fb.bpp;
height = ma->e_row - ma->s_row + 1;
pitch = console->fb.pitch;
if (toffset <= soffset) {
for (i = 0; i < height; i++) {
uint32_t increment = i * pitch;
if (softc->mode == KD_TEXT) {
(void) memmove(dst + increment,
src + increment, width);
}
(void) memmove(sdst + increment, src + increment,
width);
}
} else {
for (i = height - 1; i >= 0; i--) {
uint32_t increment = i * pitch;
if (softc->mode == KD_TEXT) {
(void) memmove(dst + increment,
src + increment, width);
}
(void) memmove(sdst + increment, src + increment,
width);
}
}
}
static uint8_t
alpha_blend(uint8_t fg, uint8_t bg, uint8_t alpha)
{
uint16_t blend, h, l;
if (alpha == 0)
return (bg);
if (alpha == 0xFF)
return (fg);
blend = (alpha * fg + (0xFF - alpha) * bg);
h = blend >> 8;
l = blend & 0xFF;
if (h + l >= 0xFF)
h++;
return (h);
}
static void
bitmap_cpy(uint8_t *dst, uint8_t *src, uint32_t len, int bpp)
{
uint32_t i;
uint8_t a;
switch (bpp) {
case 4:
for (i = 0; i < len; i += bpp) {
a = src[i+3];
dst[i] = alpha_blend(src[i], dst[i], a);
dst[i+1] = alpha_blend(src[i+1], dst[i+1], a);
dst[i+2] = alpha_blend(src[i+2], dst[i+2], a);
dst[i+3] = a;
}
break;
default:
(void) memcpy(dst, src, len);
break;
}
}
static void
bitmap_cons_display(struct gfxp_fb_softc *softc, struct vis_consdisplay *da)
{
union gfx_console *console;
uint32_t size;
uint8_t *fbp, *sfbp;
int i;
console = softc->console;
if (da->col >= console->fb.screen.x ||
da->row >= console->fb.screen.y ||
da->col + da->width > console->fb.screen.x ||
da->row + da->height > console->fb.screen.y)
return;
size = da->width * console->fb.bpp;
fbp = console->fb.fb + da->col * console->fb.bpp +
da->row * console->fb.pitch;
sfbp = console->fb.shadow_fb + da->col * console->fb.bpp +
da->row * console->fb.pitch;
for (i = 0; i < da->height; i++) {
uint8_t *dest = sfbp + i * console->fb.pitch;
uint8_t *src = da->data + i * size;
bitmap_cpy(dest, src, size, console->fb.bpp);
if (softc->mode == KD_TEXT) {
src = dest;
dest = fbp + i * console->fb.pitch;
(void) memcpy(dest, src, size);
}
}
}
static int
bitmap_cons_clear(struct gfxp_fb_softc *softc, struct vis_consclear *ca)
{
union gfx_console *console;
uint8_t *fb, *sfb;
uint16_t *fb16, *sfb16;
uint32_t data, *fb32, *sfb32;
int i, j, pitch;
console = softc->console;
pitch = console->fb.pitch;
switch (console->fb.depth) {
case 8:
data = ca->bg_color.eight;
for (i = 0; i < console->fb.screen.y; i++) {
if (softc->mode == KD_TEXT) {
fb = console->fb.fb + i * pitch;
(void) memset(fb, data, pitch);
}
fb = console->fb.shadow_fb + i * pitch;
(void) memset(fb, data, pitch);
}
break;
case 15:
case 16:
data = (ca->bg_color.sixteen[0] << 8) |
ca->bg_color.sixteen[1];
for (i = 0; i < console->fb.screen.y; i++) {
fb16 = (uint16_t *)(console->fb.fb + i * pitch);
sfb16 = (uint16_t *)(console->fb.shadow_fb + i * pitch);
for (j = 0; j < console->fb.screen.x; j++) {
if (softc->mode == KD_TEXT)
fb16[j] = (uint16_t)data & 0xffff;
sfb16[j] = (uint16_t)data & 0xffff;
}
}
break;
case 24:
data = ca->bg_color.twentyfour[0] << 16;
data |= ca->bg_color.twentyfour[1] << 8;
data |= ca->bg_color.twentyfour[2];
for (i = 0; i < console->fb.screen.y; i++) {
fb = console->fb.fb + i * pitch;
sfb = console->fb.shadow_fb + i * pitch;
for (j = 0; j < pitch; j += 3) {
if (softc->mode == KD_TEXT) {
fb[j] = (data >> 16) & 0xff;
fb[j+1] = (data >> 8) & 0xff;
fb[j+2] = data & 0xff;
}
sfb[j] = (data >> 16) & 0xff;
sfb[j+1] = (data >> 8) & 0xff;
sfb[j+2] = data & 0xff;
}
}
break;
case 32:
data = *(uint32_t *)&ca->bg_color;
for (i = 0; i < console->fb.screen.y; i++) {
fb32 = (uint32_t *)(console->fb.fb + i * pitch);
sfb32 = (uint32_t *)(console->fb.shadow_fb + i * pitch);
for (j = 0; j < console->fb.screen.x; j++) {
if (softc->mode == KD_TEXT)
fb32[j] = data;
sfb32[j] = data;
}
}
break;
}
return (0);
}
static void
bitmap_display_cursor(struct gfxp_fb_softc *softc, struct vis_conscursor *ca)
{
union gfx_console *console;
uint32_t fg, bg, offset, size;
uint32_t *fb32, *sfb32;
uint16_t *fb16, *sfb16;
uint8_t *fb8, *sfb8;
int i, j, bpp, pitch;
console = softc->console;
pitch = console->fb.pitch;
bpp = console->fb.bpp;
size = ca->width * bpp;
offset = ca->col * bpp + ca->row * pitch;
switch (console->fb.depth) {
case 8:
fg = ca->fg_color.eight;
bg = ca->bg_color.eight;
for (i = 0; i < ca->height; i++) {
fb8 = console->fb.fb + offset + i * pitch;
sfb8 = console->fb.shadow_fb + offset + i * pitch;
for (j = 0; j < size; j += 1) {
sfb8[j] = (sfb8[j] ^ (fg & 0xff)) ^ (bg & 0xff);
if (softc->mode == KD_TEXT) {
fb8[j] = sfb8[j];
}
}
}
break;
case 15:
case 16:
fg = ca->fg_color.sixteen[0] << 8;
fg |= ca->fg_color.sixteen[1];
bg = ca->bg_color.sixteen[0] << 8;
bg |= ca->bg_color.sixteen[1];
for (i = 0; i < ca->height; i++) {
fb16 = (uint16_t *)
(console->fb.fb + offset + i * pitch);
sfb16 = (uint16_t *)
(console->fb.shadow_fb + offset + i * pitch);
for (j = 0; j < ca->width; j++) {
sfb16[j] = (sfb16[j] ^ (fg & 0xffff)) ^
(bg & 0xffff);
if (softc->mode == KD_TEXT) {
fb16[j] = sfb16[j];
}
}
}
break;
case 24:
fg = ca->fg_color.twentyfour[0] << 16;
fg |= ca->fg_color.twentyfour[1] << 8;
fg |= ca->fg_color.twentyfour[2];
bg = ca->bg_color.twentyfour[0] << 16;
bg |= ca->bg_color.twentyfour[1] << 8;
bg |= ca->bg_color.twentyfour[2];
for (i = 0; i < ca->height; i++) {
fb8 = console->fb.fb + offset + i * pitch;
sfb8 = console->fb.shadow_fb + offset + i * pitch;
for (j = 0; j < size; j += 3) {
sfb8[j] = (sfb8[j] ^ ((fg >> 16) & 0xff)) ^
((bg >> 16) & 0xff);
sfb8[j+1] = (sfb8[j+1] ^ ((fg >> 8) & 0xff)) ^
((bg >> 8) & 0xff);
sfb8[j+2] = (sfb8[j+2] ^ (fg & 0xff)) ^
(bg & 0xff);
if (softc->mode == KD_TEXT) {
fb8[j] = sfb8[j];
fb8[j+1] = sfb8[j+1];
fb8[j+2] = sfb8[j+2];
}
}
}
break;
case 32:
fg = *(uint32_t *)&ca->fg_color;
bg = *(uint32_t *)&ca->bg_color;
for (i = 0; i < ca->height; i++) {
fb32 = (uint32_t *)
(console->fb.fb + offset + i * pitch);
sfb32 = (uint32_t *)
(console->fb.shadow_fb + offset + i * pitch);
for (j = 0; j < ca->width; j++) {
sfb32[j] = (sfb32[j] ^ fg) ^ bg;
if (softc->mode == KD_TEXT)
fb32[j] = sfb32[j];
}
}
break;
}
}
static void
bitmap_cons_cursor(struct gfxp_fb_softc *softc, struct vis_conscursor *ca)
{
union gfx_console *console = softc->console;
switch (ca->action) {
case VIS_HIDE_CURSOR:
bitmap_display_cursor(softc, ca);
console->fb.cursor.visible = B_FALSE;
break;
case VIS_DISPLAY_CURSOR:
console->fb.cursor.pos.x =
(ca->col - console->fb.terminal_origin.x) /
console->fb.font_width;
console->fb.cursor.pos.y =
(ca->row - console->fb.terminal_origin.y) /
console->fb.font_height;
console->fb.cursor.origin.x = ca->col;
console->fb.cursor.origin.y = ca->row;
bitmap_display_cursor(softc, ca);
console->fb.cursor.visible = B_TRUE;
break;
case VIS_GET_CURSOR:
ca->row = console->fb.cursor.origin.y;
ca->col = console->fb.cursor.origin.x;
break;
}
}
static void
bitmap_polled_copy(struct vis_polledio_arg *arg, struct vis_conscopy *ca)
{
struct gfxp_fb_softc *softc = (struct gfxp_fb_softc *)arg;
bitmap_cons_copy(softc, ca);
}
static void
bitmap_polled_display(struct vis_polledio_arg *arg, struct vis_consdisplay *da)
{
struct gfxp_fb_softc *softc = (struct gfxp_fb_softc *)arg;
bitmap_cons_display(softc, da);
}
static void
bitmap_polled_cursor(struct vis_polledio_arg *arg, struct vis_conscursor *ca)
{
struct gfxp_fb_softc *softc = (struct gfxp_fb_softc *)arg;
bitmap_cons_cursor(softc, ca);
}
static int
bitmap_devmap(dev_t dev, devmap_cookie_t dhp, offset_t off,
size_t len, size_t *maplen, uint_t model, void *ptr)
{
struct gfxp_fb_softc *softc = (struct gfxp_fb_softc *)ptr;
union gfx_console *console;
size_t length;
if (softc == NULL) {
cmn_err(CE_WARN, "bitmap: Can't find softstate");
return (ENXIO);
}
console = softc->console;
if (off >= console->fb.fb_size) {
cmn_err(CE_WARN, "bitmap: Can't map offset 0x%llx", off);
return (ENXIO);
}
if (off + len > console->fb.fb_size)
length = console->fb.fb_size - off;
else
length = len;
gfxp_map_devmem(dhp, console->fb.paddr, length, &dev_attr);
*maplen = length;
return (0);
}