/* $NetBSD: vesafb.c,v 1.31 2008/07/09 20:41:02 joerg Exp $ */ /*- * Copyright (c) 2006 Jared D. McNeill <jmcneill@invisible.ca> * All rights reserved. * * Hardware scrolling added in 2007 by Reinoud Zandijk <reinoud@NetBSD.org> * * 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 following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * 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. */ #include <sys/cdefs.h> __KERNEL_RCSID(0, "$NetBSD: vesafb.c,v 1.31 2008/07/09 20:41:02 joerg Exp $"); #include <sys/param.h> #include <sys/systm.h> #include <sys/device.h> #include <sys/malloc.h> #include <uvm/uvm_extern.h> #include <machine/frame.h> #include <machine/kvm86.h> #include <machine/bus.h> #if notyet #include <machine/bioscall.h> #endif #include "opt_vesafb.h" #include <arch/i386/bios/vesabios.h> #include <arch/i386/bios/vesabiosreg.h> #include <arch/i386/bios/vesafbvar.h> #ifdef VESAFB_DISABLE_TEXT #include <sys/reboot.h> #define DISABLESPLASH (boothowto & (RB_SINGLE | RB_USERCONF | RB_ASKNAME | \ AB_VERBOSE | AB_DEBUG) ) #endif MALLOC_DEFINE(M_VESAFB, "vesafb", "vesafb shadow framebuffer"); static int vesafb_match(device_t, cfdata_t, void *); static void vesafb_attach(device_t, device_t, void *); struct wsscreen_descr vesafb_stdscreen = { "fb", 0, 0, NULL, 8, 16, 0, /* capabilities */ NULL, /* modecookie */ }; static int vesafb_ioctl(void *, void *, u_long, void *, int, struct lwp *); static paddr_t vesafb_mmap(void *, void *, off_t, int); static void vesafb_show_screen_cb(struct vcons_screen *); static void vesafb_init_screen(void *, struct vcons_screen *, int, long *); static void vesafb_init(struct vesafb_softc *, int); static int vesafb_svideo(struct vesafb_softc *, int); static int vesafb_gvideo(struct vesafb_softc *, u_int *); static int vesafb_putcmap(struct vesafb_softc *, struct wsdisplay_cmap *); static int vesafb_getcmap(struct vesafb_softc *, struct wsdisplay_cmap *); static void vesafb_set_palette(struct vesafb_softc *, int, struct paletteentry); static void vesafb_display_on(device_t); static void vesafb_display_standby(device_t); static void vesafb_display_suspend(device_t); static void vesafb_display_off(device_t); static void vesafb_display_reduced(device_t); struct wsdisplay_accessops vesafb_accessops = { vesafb_ioctl, vesafb_mmap, NULL, /* alloc_screen */ NULL, /* free_screen */ NULL, /* show_screen */ NULL, /* load_font */ NULL, /* pollc */ NULL, /* scroll */ }; static struct vcons_screen vesafb_console_screen; const struct wsscreen_descr *_vesafb_scrlist[] = { &vesafb_stdscreen, }; struct wsscreen_list vesafb_screenlist = { sizeof(_vesafb_scrlist) / sizeof(struct wsscreen_descr *), _vesafb_scrlist }; CFATTACH_DECL_NEW(vesafb, sizeof(struct vesafb_softc), vesafb_match, vesafb_attach, NULL, NULL); static int vesafb_match(device_t parent, cfdata_t match, void *aux) { struct vesabiosdev_attach_args *vaa = aux; if (strcmp(vaa->vbaa_type, "raster")) return (0); return (1); } static void vesafb_attach(device_t parent, device_t self, void *aux) { struct vesafb_softc *sc = device_private(self); struct vesabiosdev_attach_args *vaa = aux; unsigned char *buf; struct trapframe tf; int res, i, j; long defattr; struct modeinfoblock *mi; struct rasops_info *ri; struct wsemuldisplaydev_attach_args aa; bus_space_handle_t h; sc->sc_dev = self; aprint_naive("\n"); aprint_normal(": VESA frame buffer\n"); buf = kvm86_bios_addpage(0x2000); if (!buf) { aprint_error_dev(self, "kvm86_bios_addpage(0x2000) failed\n"); return; } sc->sc_buf = buf; if (vaa->vbaa_nmodes == 0) goto out; sc->sc_nscreens = 1; /* XXX console */ sc->sc_wsmode = WSDISPLAYIO_MODE_EMUL; sc->sc_mode = vaa->vbaa_modes[0]; /* XXX */ sc->sc_pm = 0; sc->sc_pmstates = 0; sc->sc_isconsole = 0; mi = NULL; j = 0; /* XXX */ for (i = 0; i < vaa->vbaa_nmodes; i++) { memset(&tf, 0, sizeof(struct trapframe)); tf.tf_eax = 0x4f01; /* function code */ tf.tf_ecx = vaa->vbaa_modes[i]; tf.tf_vm86_es = 0; tf.tf_edi = 0x2000; /* buf ptr */ res = kvm86_bioscall(0x10, &tf); if (res || (tf.tf_eax & 0xff) != 0x4f) { aprint_error_dev(self, "vbecall: res=%d, ax=%x\n", res, tf.tf_eax); goto out; } mi = (struct modeinfoblock *)buf; if (mi->XResolution == VESAFB_WIDTH && mi->YResolution == VESAFB_HEIGHT && mi->BitsPerPixel == VESAFB_DEPTH) { sc->sc_mode = vaa->vbaa_modes[i]; break; } } if (i == vaa->vbaa_nmodes) { aprint_error_dev(self, "no supported mode found\n"); goto out; } sc->sc_mi = *mi; /* Check for power management support */ memset(&tf, 0, sizeof(struct trapframe)); tf.tf_eax = 0x4f10; /* function code */ tf.tf_ebx = 0; tf.tf_edi = 0x2000; /* buf ptr */ res = kvm86_bioscall(0x10, &tf); if (res || (tf.tf_eax & 0xff) != 0x4f) sc->sc_pm = 0; /* power management not supported */ else { sc->sc_pm = 1; /* power management is supported */ sc->sc_pmver = tf.tf_ebx & 0xff; sc->sc_pmstates = (tf.tf_ebx >> 8); } ri = &vesafb_console_screen.scr_ri; memset(ri, 0, sizeof(struct rasops_info)); vcons_init(&sc->sc_vd, sc, &vesafb_stdscreen, &vesafb_accessops); sc->sc_vd.init_screen = vesafb_init_screen; sc->sc_vd.show_screen_cb = vesafb_show_screen_cb; aprint_normal_dev(self, "fb %dx%dx%d @0x%x\n", mi->XResolution, mi->YResolution, mi->BitsPerPixel, mi->PhysBasePtr); if (vaa->vbaa_vbeversion >= 0x300) { sc->sc_scrollscreens = mi->LinNumberOfImagePages; } else { sc->sc_scrollscreens = mi->NumberOfImagePages; } if (sc->sc_scrollscreens == 0) sc->sc_scrollscreens = 1; /* XXX disable hardware scrolling for now; kvm86_call() can trap */ sc->sc_scrollscreens = 1; sc->sc_screensize = mi->YResolution * mi->BytesPerScanLine; sc->sc_fbsize = sc->sc_scrollscreens * sc->sc_screensize; aprint_normal_dev(self, "%d Kb memory reported, %d screens possible\n", sc->sc_fbsize / 1024, sc->sc_scrollscreens); if (sc->sc_scrollscreens == 1) aprint_normal_dev(self, "one screen, so hardware scrolling not " "possible\n"); if (sc->sc_pm) { aprint_normal_dev(self, "VBE/PM %d.%d\n", (sc->sc_pmver >> 4), sc->sc_pmver & 0xf); } res = _x86_memio_map(X86_BUS_SPACE_MEM, mi->PhysBasePtr, sc->sc_fbsize, /* was sc_screensize */ BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_PREFETCHABLE, &h); if (res) { aprint_error_dev(self, "framebuffer mapping failed\n"); goto out; } sc->sc_fbstart = bus_space_vaddr(X86_BUS_SPACE_MEM, h); sc->sc_bits = sc->sc_fbstart; #ifdef VESAFB_SHADOW_FB sc->sc_shadowbits = malloc(sc->sc_screensize, M_VESAFB, M_NOWAIT); if (sc->sc_shadowbits == NULL) { aprint_error_dev(self, "unable to allocate %d bytes for shadowfb\n", sc->sc_screensize); /* Not fatal; we'll just have to continue without shadowfb */ } #endif /* initialise our display wide settings */ #ifdef VESAFB_SHADOW_FB sc->sc_displ_bits = sc->sc_shadowbits; sc->sc_displ_hwbits = sc->sc_bits; #else sc->sc_displ_bits = sc->sc_bits; sc->sc_displ_hwbits = NULL; #endif /* !VESAFB_SHADOW_FB */ vesafb_init(sc, 1); vesafb_console_screen.scr_flags |= VCONS_SCREEN_IS_STATIC; vcons_init_screen(&sc->sc_vd, &vesafb_console_screen, 1, &defattr); #ifdef SPLASHSCREEN /* * If system isn't going to go multiuser, or user has requested to see * boot text, don't render splash screen immediately */ if (DISABLESPLASH) #endif vcons_redraw_screen(&vesafb_console_screen); vesafb_stdscreen.ncols = ri->ri_cols; vesafb_stdscreen.nrows = ri->ri_rows; vesafb_stdscreen.textops = &ri->ri_ops; vesafb_stdscreen.capabilities = ri->ri_caps; vesafb_stdscreen.modecookie = NULL; wsdisplay_cnattach(&vesafb_stdscreen, ri, 0, 0, defattr); #ifdef SPLASHSCREEN sc->sc_si.si_depth = ri->ri_depth; sc->sc_si.si_bits = ri->ri_bits; sc->sc_si.si_hwbits = ri->ri_hwbits; sc->sc_si.si_width = ri->ri_width; sc->sc_si.si_height = ri->ri_height; sc->sc_si.si_stride = ri->ri_stride; sc->sc_si.si_fillrect = NULL; if (!DISABLESPLASH) splash_render(&sc->sc_si, SPLASH_F_CENTER|SPLASH_F_FILL); #endif #ifdef SPLASHSCREEN_PROGRESS sc->sc_sp.sp_top = (mi->YResolution / 8) * 7; sc->sc_sp.sp_width = (mi->XResolution / 4) * 3; sc->sc_sp.sp_left = (mi->XResolution - sc->sc_sp.sp_width) / 2; sc->sc_sp.sp_height = 20; sc->sc_sp.sp_state = -1; sc->sc_sp.sp_si = &sc->sc_si; splash_progress_init(&sc->sc_sp); #endif aa.console = 1; /* XXX */ aa.scrdata = &vesafb_screenlist; aa.accessops = &vesafb_accessops; aa.accesscookie = &sc->sc_vd; #ifdef VESAFB_DISABLE_TEXT if (!DISABLESPLASH) SCREEN_DISABLE_DRAWING(&vesafb_console_screen); #endif /* !VESAFB_DISABLE_TEXT */ sc->sc_isconsole = 1; if (!pmf_device_register(self, NULL, NULL)) aprint_error_dev(self, "couldn't establish power handler\n"); else if (!pmf_class_display_register(self)) aprint_error_dev(self, "couldn't set display class\n"); if (!pmf_event_register(self, PMFE_DISPLAY_ON, vesafb_display_on, true)) aprint_error_dev(self, "couldn't register DISPLAY ON event\n"); if ((sc->sc_pmstates & 1) != 0 && !pmf_event_register(self, PMFE_DISPLAY_STANDBY, vesafb_display_standby, true)) aprint_error_dev(self, "couldn't register DISPLAY STANDBY event\n"); if ((sc->sc_pmstates & 2) != 0 && !pmf_event_register(self, PMFE_DISPLAY_SUSPEND, vesafb_display_suspend, true)) aprint_error_dev(self, "couldn't register DISPLAY SUSPEND event\n"); if ((sc->sc_pmstates & 4) != 0 && !pmf_event_register(self, PMFE_DISPLAY_OFF, vesafb_display_off, true)) aprint_error_dev(self, "couldn't register DISPLAY OFF event\n"); if ((sc->sc_pmstates & 8) != 0 && !pmf_event_register(self, PMFE_DISPLAY_REDUCED, vesafb_display_reduced, true)) aprint_error_dev(self, "couldn't register DISPLAY REDUCED event\n"); config_found(self, &aa, wsemuldisplaydevprint); out: return; } static int vesafb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, struct lwp *l) { struct vcons_data *vd; struct vesafb_softc *sc; struct wsdisplay_fbinfo *fb; vd = (struct vcons_data *)v; sc = (struct vesafb_softc *)vd->cookie; switch (cmd) { case WSDISPLAYIO_GTYPE: *(u_int *)data = WSDISPLAY_TYPE_VESA; return 0; case WSDISPLAYIO_GINFO: if (vd->active != NULL) { fb = (struct wsdisplay_fbinfo *)data; fb->width = sc->sc_mi.XResolution; fb->height = sc->sc_mi.YResolution; fb->depth = sc->sc_mi.BitsPerPixel; fb->cmsize = 256; return 0; } else return ENODEV; case WSDISPLAYIO_GVIDEO: if (sc->sc_pm) return vesafb_gvideo(sc, (u_int *)data); else return ENODEV; case WSDISPLAYIO_SVIDEO: if (sc->sc_pm) { u_int on = *(u_int *)data; if (on == WSDISPLAYIO_VIDEO_OFF) return vesafb_svideo(sc, 4); else return vesafb_svideo(sc, 0); } else return ENODEV; case WSDISPLAYIO_GETCMAP: if (sc->sc_mi.BitsPerPixel == 8) return vesafb_getcmap(sc, (struct wsdisplay_cmap *)data); else return EINVAL; case WSDISPLAYIO_PUTCMAP: if (sc->sc_mi.BitsPerPixel == 8) return vesafb_putcmap(sc, (struct wsdisplay_cmap *)data); else return EINVAL; case WSDISPLAYIO_LINEBYTES: *(u_int *)data = sc->sc_mi.BytesPerScanLine; return 0; case WSDISPLAYIO_SMODE: { int new_mode = *(int *)data; if (new_mode != sc->sc_wsmode) { sc->sc_wsmode = new_mode; if (new_mode == WSDISPLAYIO_MODE_EMUL) { /* vesafb_init(sc, 0); */ vcons_redraw_screen(vd->active); } } } return 0; case WSDISPLAYIO_SSPLASH: #if defined(SPLASHSCREEN) if (*(int *)data == 1) { SCREEN_DISABLE_DRAWING(&vesafb_console_screen); splash_render(&sc->sc_si, SPLASH_F_CENTER|SPLASH_F_FILL); } else SCREEN_ENABLE_DRAWING(&vesafb_console_screen); return 0; #else return ENODEV; #endif case WSDISPLAYIO_SPROGRESS: #if defined(SPLASHSCREEN) && defined(SPLASHSCREEN_PROGRESS) sc->sc_sp.sp_force = 1; splash_progress_update(&sc->sc_sp); sc->sc_sp.sp_force = 0; return 0; #else return ENODEV; #endif } return EPASSTHROUGH; } static paddr_t vesafb_mmap(void *v, void *vs, off_t offset, int prot) { struct vcons_data *vd; struct vesafb_softc *sc; paddr_t pa; vd = (struct vcons_data *)v; sc = (struct vesafb_softc *)vd->cookie; /* XXX */ if (offset >= 0 && offset < sc->sc_mi.YResolution*sc->sc_mi.BytesPerScanLine) { pa = bus_space_mmap(X86_BUS_SPACE_MEM, sc->sc_mi.PhysBasePtr, offset, prot, BUS_SPACE_MAP_LINEAR); return pa; } return -1; } /* called back by vcons on screen change; needed for VT display sharing */ static void vesafb_show_screen_cb(struct vcons_screen *scr) { struct vesafb_softc *sc; struct rasops_info *ri; sc = (struct vesafb_softc *) scr->scr_cookie; ri = &scr->scr_ri; /* protect against roque values) */ if ((sc == NULL) || (ri == NULL)) return; /* set our rasops info to match the display's global state (VTs!) */ ri->ri_bits = sc->sc_displ_bits; ri->ri_hwbits = sc->sc_displ_hwbits; } static void vv_copyrows(void *id, int srcrow, int dstrow, int nrows) { static int working = 1; struct trapframe tf; struct rasops_info *ri = id; struct vcons_screen *scr = (struct vcons_screen *) ri->ri_hw; struct vesafb_softc *sc = (struct vesafb_softc *) scr->scr_cookie; uint32_t displ_offset; uint8_t *src, *dst, *hwbits, *cur_hwbits, *hiwater, *lowater; int fontheight, offset, linesz, size, height; int scrollup, scrolldown, res; /* set our rasops info to match the display's global state (VTs!) */ ri->ri_bits = sc->sc_displ_bits; ri->ri_hwbits = sc->sc_displ_hwbits; /* fontheight = ri->ri_font->fontheight; */ fontheight = 16; /* All movements are done in multiples of character heights */ height = fontheight * nrows; size = height * ri->ri_stride; linesz = fontheight * ri->ri_stride; offset = (srcrow - dstrow) * linesz; /* check if we are full screen scrolling */ scrollup = (srcrow + nrows >= ri->ri_rows); scrolldown = (dstrow + nrows >= ri->ri_rows); if (working && (scrollup || scrolldown)) { lowater = sc->sc_fbstart; hiwater = lowater + sc->sc_fbsize - sc->sc_screensize; #ifdef VESAFB_SHADOW_FB hwbits = ri->ri_hwbits; #else hwbits = ri->ri_bits; #endif cur_hwbits = hwbits; hwbits += offset; if (hwbits > hiwater) { /* offset is positive */ memmove(lowater, ri->ri_bits + offset, sc->sc_screensize - offset); hwbits = lowater; } if (hwbits < lowater) { /* offset is negative */ memmove(hiwater - offset, ri->ri_bits, sc->sc_screensize + offset); hwbits = hiwater; } /* program VESA frame buffer start */ displ_offset = (hwbits - sc->sc_fbstart); memset(&tf, 0, sizeof(struct trapframe)); tf.tf_eax = 0x4f07; /* function code */ tf.tf_ebx = 0x00; /* set display immediately */ tf.tf_ecx = 0; /* hpixels */ tf.tf_edx = displ_offset / ri->ri_stride; /* lineno */ tf.tf_vm86_es = 0; res = kvm86_bioscall(0x10, &tf); if (res || (tf.tf_eax & 0xff) != 0x4f) { working = 0; aprint_error_dev(sc->sc_dev, "vbecall: res=%d, ax=%x\n", res, tf.tf_eax); hwbits = cur_hwbits; goto out; } #ifdef VESAFB_SHADOW_FB ri->ri_hwbits = hwbits; src = ri->ri_bits + srcrow * fontheight * ri->ri_stride; dst = ri->ri_bits + dstrow * fontheight * ri->ri_stride; memmove(dst, src, size); #else ri->ri_bits = hwbits; #endif #if 0 /* wipe out remains of the screen if nessisary */ if (ri->ri_emuheight != ri->ri_height) vv_eraserows(id, ri->ri_rows, 1, 0); #endif /* remember display's global state (VTs!) */ sc->sc_displ_bits = ri->ri_bits; sc->sc_displ_hwbits = ri->ri_hwbits; return; } out: /* just deligate to the origional routine */ sc->sc_orig_copyrows(id, srcrow, dstrow, nrows); } static void vesafb_init_screen(void *c, struct vcons_screen *scr, int existing, long *defattr) { struct vesafb_softc *sc; struct rasops_info *ri; struct modeinfoblock *mi; sc = (struct vesafb_softc *)c; mi = &sc->sc_mi; ri = &scr->scr_ri; ri->ri_flg = RI_CENTER; ri->ri_depth = mi->BitsPerPixel; ri->ri_width = mi->XResolution; ri->ri_height = mi->YResolution; ri->ri_emuheight = ri->ri_height; /* XXX always ? */ ri->ri_stride = mi->BytesPerScanLine; /* set our rasops info to match the display's global state (VTs!) */ ri->ri_bits = sc->sc_displ_bits; ri->ri_hwbits = sc->sc_displ_hwbits; ri->ri_caps = WSSCREEN_WSCOLORS; ri->ri_rnum = mi->RedMaskSize; ri->ri_gnum = mi->GreenMaskSize; ri->ri_bnum = mi->BlueMaskSize; ri->ri_rpos = mi->RedFieldPosition; ri->ri_gpos = mi->GreenFieldPosition; ri->ri_bpos = mi->BlueFieldPosition; rasops_init(ri, mi->YResolution / 16, mi->XResolution / 8); if (sc->sc_scrollscreens > 1) { /* override copyrows but remember old one for delegation */ sc->sc_orig_copyrows = ri->ri_ops.copyrows; ri->ri_ops.copyrows = vv_copyrows; } #ifdef VESA_DISABLE_TEXT if (scr == &vesafb_console_screen && !DISABLESPLASH) SCREEN_DISABLE_DRAWING(&vesafb_console_screen); #endif } static void vesafb_init(struct vesafb_softc *sc, int setmode) { struct bioscallregs regs; struct modeinfoblock *mi; struct paletteentry pe; int i, j, s; mi = &sc->sc_mi; s = splsched(); if (setmode) { /* set mode */ regs.EAX = 0x4f02; regs.EBX = sc->sc_mode | 0x4000; kvm86_bioscall_simple(0x10, ®s); if ((regs.EAX & 0xff) != 0x4f) { aprint_error_dev(sc->sc_dev, "bioscall failed\n"); goto out; } } /* setup palette, not required in direct colour mode */ if (mi->BitsPerPixel == 8) { memset(®s, 0, sizeof(struct bioscallregs)); regs.EAX = 0x4f08; /* function code */ regs.EBX = 0x0600; /* we want a 6-bit palette */ kvm86_bioscall_simple(0x10, ®s); if ((regs.EAX & 0xff) != 0x4f) { aprint_error_dev(sc->sc_dev, "vbecall failed: ax=%x\n", regs.EAX); goto out; } j = 0; /* setup ansi / rasops palette */ for (i = 0; i < 256; i++) { #ifndef SPLASHSCREEN pe.Blue = rasops_cmap[(i * 3) + 2]; pe.Green = rasops_cmap[(i * 3) + 1]; pe.Red = rasops_cmap[(i * 3) + 0]; pe.Alignment = 0; vesafb_set_palette(sc, i, pe); #else if (i >= SPLASH_CMAP_OFFSET && i < SPLASH_CMAP_OFFSET + SPLASH_CMAP_SIZE) { pe.Blue = _splash_header_data_cmap[j][2]; pe.Green = _splash_header_data_cmap[j][1]; pe.Red = _splash_header_data_cmap[j][0]; pe.Alignment = 0; j++; vesafb_set_palette(sc, i, pe); } else { pe.Blue = rasops_cmap[(i * 3) + 2]; pe.Green = rasops_cmap[(i * 3) + 1]; pe.Red = rasops_cmap[(i * 3) + 0]; pe.Alignment = 0; vesafb_set_palette(sc, i, pe); } #endif /* Fill in the softc colourmap arrays */ sc->sc_cmap_red[i / 3] = rasops_cmap[i + 0]; sc->sc_cmap_green[i / 3] = rasops_cmap[i + 1]; sc->sc_cmap_blue[i / 3] = rasops_cmap[i + 2]; } } out: splx(s); return; } static void vesafb_display_on(device_t dv) { struct vesafb_softc *sc = device_private(dv); if (sc->sc_wsmode == WSDISPLAYIO_MODE_EMUL) vesafb_svideo(sc, 0); } static void vesafb_display_standby(device_t dv) { struct vesafb_softc *sc = device_private(dv); if (sc->sc_wsmode == WSDISPLAYIO_MODE_EMUL) vesafb_svideo(sc, 1); } static void vesafb_display_suspend(device_t dv) { struct vesafb_softc *sc = device_private(dv); if (sc->sc_wsmode == WSDISPLAYIO_MODE_EMUL) vesafb_svideo(sc, 2); } static void vesafb_display_off(device_t dv) { struct vesafb_softc *sc = device_private(dv); if (sc->sc_wsmode == WSDISPLAYIO_MODE_EMUL) vesafb_svideo(sc, 4); } static void vesafb_display_reduced(device_t dv) { struct vesafb_softc *sc = device_private(dv); if (sc->sc_wsmode == WSDISPLAYIO_MODE_EMUL) vesafb_svideo(sc, 8); } static void vesafb_set_palette(struct vesafb_softc *sc, int reg, struct paletteentry pe) { struct trapframe tf; int res; char *buf; buf = sc->sc_buf; /* * this function takes 8 bit per palette as input, but we're * working in 6 bit mode here */ pe.Red >>= 2; pe.Green >>= 2; pe.Blue >>= 2; memcpy(buf, &pe, sizeof(struct paletteentry)); /* set palette */ memset(&tf, 0, sizeof(struct trapframe)); tf.tf_eax = 0x4f09; /* function code */ tf.tf_ebx = 0x0600; /* 6 bit per primary, set format */ tf.tf_ecx = 1; tf.tf_edx = reg; tf.tf_vm86_es = 0; tf.tf_edi = 0x2000; res = kvm86_bioscall(0x10, &tf); if (res || (tf.tf_eax & 0xff) != 0x4f) aprint_error_dev(sc->sc_dev, "vbecall: res=%d, ax=%x\n", res, tf.tf_eax); return; } static int vesafb_svideo(struct vesafb_softc *sc, int bh) { #ifdef VESAFB_PM struct bioscallregs regs; int s; /* If the power management state is not supported, bail */ if ((sc->sc_pmstates & bh) != bh) return ENODEV; memset(®s, 0, sizeof(struct bioscallregs)); regs.EAX = 0x4f10; regs.EBX = (bh << 8) | 1; s = splsched(); kvm86_bioscall_simple(0x10, ®s); splx(s); return 0; #else return ENODEV; #endif } static int vesafb_gvideo(struct vesafb_softc *sc, u_int *on) { #ifdef VESAFB_PM struct trapframe tf; int res, bh; /* get video pm state */ memset(&tf, 0, sizeof(struct trapframe)); tf.tf_eax = 0x4f10; /* function code */ tf.tf_ebx = 2; res = kvm86_bioscall(0x10, &tf); if (res || (tf.tf_eax & 0xff) != 0x4f) { aprint_error_dev(sc->sc_dev, "unable to get power state (0x%04x)\n", (tf.tf_eax & 0xffff)); return ENODEV; } bh = (tf.tf_ebx & 0xff00) >> 8; switch (bh) { case 0: /* 0:on */ *on = WSDISPLAYIO_VIDEO_ON; break; default: /* 1:standby, 2:suspend, 4:off, 8:reduced_on */ *on = WSDISPLAYIO_VIDEO_OFF; break; } return 0; #else return ENODEV; #endif } static int vesafb_putcmap(struct vesafb_softc *sc, struct wsdisplay_cmap *cm) { struct paletteentry pe; u_int idx, cnt; u_char r[256], g[256], b[256]; u_char *rp, *gp, *bp; int rv, i; idx = cm->index; cnt = cm->count; if (idx >= 255 || cnt > 256 || idx + cnt > 256) return EINVAL; rv = copyin(cm->red, &r[idx], cnt); if (rv) return rv; rv = copyin(cm->green, &g[idx], cnt); if (rv) return rv; rv = copyin(cm->blue, &b[idx], cnt); if (rv) return rv; memcpy(&sc->sc_cmap_red[idx], &r[idx], cnt); memcpy(&sc->sc_cmap_green[idx], &g[idx], cnt); memcpy(&sc->sc_cmap_blue[idx], &b[idx], cnt); rp = &sc->sc_cmap_red[idx]; gp = &sc->sc_cmap_green[idx]; bp = &sc->sc_cmap_blue[idx]; for (i = 0; i < cnt; i++) { pe.Blue = *bp; pe.Green = *gp; pe.Red = *rp; pe.Alignment = 0; vesafb_set_palette(sc, idx, pe); idx++; rp++, gp++, bp++; } return 0; } static int vesafb_getcmap(struct vesafb_softc *sc, struct wsdisplay_cmap *cm) { u_int idx, cnt; int rv; idx = cm->index; cnt = cm->count; if (idx >= 255 || cnt > 256 || idx + cnt > 256) return EINVAL; rv = copyout(&sc->sc_cmap_red[idx], cm->red, cnt); if (rv) return rv; rv = copyout(&sc->sc_cmap_green[idx], cm->green, cnt); if (rv) return rv; rv = copyout(&sc->sc_cmap_blue[idx], cm->blue, cnt); if (rv) return rv; return 0; } int vesafb_cnattach(void) { /* XXX i386 calls consinit too early for us to use * kvm86; assume that we've attached */ return 0; }