vgatext.c revision d1e631af56641f21cde1f1efe3a9623ff9d6ee7c
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */
/* Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T */
/* All Rights Reserved */
#include <sys/visual_io.h>
#include <sys/ddi_impldefs.h>
#define MYNAME "vgatext"
/*
* Each instance of this driver has 2 minor nodes:
* 0: for common graphics operations
* 1: for agpmaster operations
*/
#define GFX_MINOR 0
#define AGPMASTER_MINOR 1
#define MY_NBITSMINOR 1
/* I don't know exactly where these should be defined, but this is a */
/* heck of a lot better than constants in the code. */
#define TEXT_ROWS 25
#define TEXT_COLS 80
#define VGA_BRIGHT_WHITE 0x0f
#define VGA_BLACK 0x00
#define VGA_REG_ADDR 0x3c0
#define VGA_REG_SIZE 0x20
#define VGA_MEM_ADDR 0xa0000
#define VGA_MEM_SIZE 0x20000
#define VGA_MMAP_FB_BASE VGA_MEM_ADDR
/*
* This variable allows for this driver to suspend even if it
* shouldn't. Note that by setting it, the framebuffer will probably
* not come back. So use it with a serial console, or with serial
* line debugging (say, for example, if this driver is being modified
* to support _some_ hardware doing suspend and resume).
*/
int vgatext_force_suspend = 0;
static struct cb_ops cb_vgatext_ops = {
vgatext_open, /* cb_open */
vgatext_close, /* cb_close */
nodev, /* cb_strategy */
nodev, /* cb_print */
nodev, /* cb_dump */
nodev, /* cb_read */
nodev, /* cb_write */
vgatext_ioctl, /* cb_ioctl */
vgatext_devmap, /* cb_devmap */
nodev, /* cb_mmap */
ddi_devmap_segmap, /* cb_segmap */
nochpoll, /* cb_chpoll */
ddi_prop_op, /* cb_prop_op */
0, /* cb_stream */
};
void **result);
static struct dev_ops vgatext_ops = {
DEVO_REV, /* devo_rev */
0, /* devo_refcnt */
vgatext_info, /* devo_getinfo */
nulldev, /* devo_identify */
nulldev, /* devo_probe */
vgatext_attach, /* devo_attach */
vgatext_detach, /* devo_detach */
nodev, /* devo_reset */
&cb_vgatext_ops, /* devo_cb_ops */
NULL, /* power */
ddi_quiesce_not_needed, /* quiesce */
};
struct vgatext_softc {
int fb_regno;
int mode; /* KD_TEXT or KD_GRAPHICS */
struct {
int row;
int col;
} cursor;
struct vis_polledio polledio;
struct {
unsigned char red;
unsigned char green;
unsigned char blue;
unsigned char attrib_palette[VGA_ATR_NUM_PLT];
unsigned int flags;
};
#define VGATEXT_FLAG_CONSOLE 0x00000001
static void vgatext_cons_copy(struct vgatext_softc *,
struct vis_conscopy *);
static void vgatext_cons_display(struct vgatext_softc *,
struct vis_consdisplay *);
static void vgatext_cons_cursor(struct vgatext_softc *,
struct vis_conscursor *);
static void vgatext_polled_copy(struct vis_polledio_arg *,
struct vis_conscopy *);
static void vgatext_polled_display(struct vis_polledio_arg *,
struct vis_consdisplay *);
static void vgatext_polled_cursor(struct vis_polledio_arg *,
struct vis_conscursor *);
static void vgatext_init(struct vgatext_softc *);
static void vgatext_set_text(struct vgatext_softc *);
#if defined(USE_BORDERS)
static void vgatext_init_graphics(struct vgatext_softc *);
#endif
static void *vgatext_softc_head;
static char vgatext_silent;
static char happyface_boot;
/* Loadable Driver stuff */
&mod_driverops, /* Type of module. This one is a driver */
"VGA text driver", /* Name of the module. */
&vgatext_ops, /* driver ops */
};
static struct modlinkage modlinkage = {
};
typedef enum pc_colors {
pc_black = 0,
pc_blue = 1,
pc_green = 2,
pc_cyan = 3,
pc_red = 4,
pc_magenta = 5,
pc_brown = 6,
pc_white = 7,
pc_grey = 8,
pc_brt_blue = 9,
pc_brt_green = 10,
pc_brt_cyan = 11,
pc_brt_red = 12,
pc_brt_magenta = 13,
pc_yellow = 14,
pc_brt_white = 15
} pc_colors_t;
static const unsigned char solaris_color_to_pc_color[16] = {
pc_brt_white, /* 0 - brt_white */
pc_black, /* 1 - black */
pc_blue, /* 2 - blue */
pc_green, /* 3 - green */
pc_cyan, /* 4 - cyan */
pc_red, /* 5 - red */
pc_magenta, /* 6 - magenta */
pc_brown, /* 7 - brown */
pc_white, /* 8 - white */
pc_grey, /* 9 - gery */
pc_brt_blue, /* 10 - brt_blue */
pc_brt_green, /* 11 - brt_green */
pc_brt_cyan, /* 12 - brt_cyan */
pc_brt_red, /* 13 - brt_red */
pc_brt_magenta, /* 14 - brt_magenta */
pc_yellow /* 15 - yellow */
};
static ddi_device_acc_attr_t i8xx_dev_access = {
};
static ddi_device_acc_attr_t dev_attr = {
};
int
_init(void)
{
int e;
if ((e = ddi_soft_state_init(&vgatext_softc_head,
sizeof (struct vgatext_softc), 1)) != 0) {
return (e);
}
e = mod_install(&modlinkage);
if (e) {
}
return (e);
}
int
_fini(void)
{
int e;
if ((e = mod_remove(&modlinkage)) != 0)
return (e);
return (0);
}
int
{
}
/* default structure for FBIOGATTR ioctl */
static struct fbgattr vgatext_attr = {
/* real_type owner */
/* fbtype: type h w depth cms size */
/* fbsattr: flags emu_type dev_specific */
{ 0, FBTYPE_SUN4COLOR, { 0 } },
/* emu_types */
{ -1 }
};
/*
* handy macros
*/
/*
* NOTE: this function is duplicated here and in gfx_private/vgatext while
* we work on a set of commitable interfaces to sunpci.c.
*
* Use the class code to determine if the device is a PCI-to-PCI bridge.
* Returns: B_TRUE if the device is a bridge.
* B_FALSE if the device is not a bridge or the property cannot be
* retrieved.
*/
static boolean_t
{
return (B_FALSE);
class_code &= 0x00ffff00;
return (B_TRUE);
return (B_FALSE);
}
static void
int pci_pcie_bus)
{
/*
* Based on Section 11.3, "PCI Display Subsystem Initialization",
* of the 1.1 PCI-to-PCI Bridge Architecture Specification
* determine if this is the boot console device. First, see
* if the SBIOS has turned on PCI I/O for this device. Then if
*/
MYNAME ": can't get PCI conf handle");
return;
}
if (data16 & PCI_COMM_IO)
return;
/*
* Check for VGA Enable in the Bridge Control register for all
* this cannot be the boot console.
*/
int error;
char *parent_type = NULL;
if (error != DDI_SUCCESS) {
return;
}
return;
}
parent_type = NULL;
/* VGAEnable is set only for PCI-to-PCI bridges. */
continue;
continue;
if (!(data16 & PCI_BCNF_BCNTRL_VGA_ENABLE)) {
return;
}
}
}
static int
{
struct vgatext_softc *softc;
int error;
char *parent_type = NULL;
int reg_rnumber;
int agpm = 0;
int pci_pcie_bus = 0;
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
/*
* Though vgatext doesn't really know how to resume
* on a generic framebuffer, we should succeed, as
* it is far better to have no console, than potentiall
* have no machine.
*/
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
/* DDI_ATTACH */
/* Allocate softc struct */
return (DDI_FAILURE);
}
/* link it in */
if (error != DDI_SUCCESS) {
goto fail;
}
®_offset);
if (reg_rnumber < 0) {
MYNAME ": can't find reg entry for registers");
error = DDI_FAILURE;
goto fail;
}
MYNAME ": can't find reg entry for memory");
error = DDI_FAILURE;
goto fail;
}
pci_pcie_bus = 1;
®_offset);
if (reg_rnumber < 0) {
MYNAME ": can't find reg entry for registers");
error = DDI_FAILURE;
goto fail;
}
&mem_offset);
MYNAME ": can't find reg entry for memory");
error = DDI_FAILURE;
goto fail;
}
} else {
error = DDI_FAILURE;
goto fail;
}
parent_type = NULL;
if (error != DDI_SUCCESS)
goto fail;
if (error != DDI_SUCCESS)
goto fail;
else
happyface_boot = 1;
vgatext_silent = 1;
} else {
}
} else {
}
if (error != DDI_SUCCESS)
goto fail;
if (error != DDI_SUCCESS)
goto fail;
/* only do this if not in graphics mode */
}
if (agpm != 0) { /* try AGP master attach */
/* setup mapping for PCI config space access */
if (error != DDI_SUCCESS) {
"PCI configuration space setup failed");
goto fail;
}
}
return (DDI_SUCCESS);
fail:
if (parent_type != NULL)
return (error);
}
static int
{
switch (cmd) {
case DDI_DETACH:
}
return (DDI_SUCCESS);
case DDI_SUSPEND:
/*
* This is a generic VGA file, and therefore, cannot
* understand how to deal with suspend and resume on
* a generic interface. So we fail any attempt to
* suspend. At some point in the future, we might use
* this as an entrypoint for display drivers and this
* assumption may change.
*
* However, from a platform development perspective,
* it is important that this driver suspend if a
* on a framebuffer driver that will support suspend
* and resume. Therefore, we have this module tunable
* (purposely using a long name) that will allow for
* suspend it it is set. Otherwise we fail.
*/
if (vgatext_force_suspend != 0)
return (DDI_SUCCESS);
else
return (DDI_FAILURE);
default:
return (DDI_FAILURE);
}
}
/*ARGSUSED*/
static int
{
int error;
int instance;
struct vgatext_softc *softc;
error = DDI_SUCCESS;
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
error = DDI_FAILURE;
} else {
error = DDI_SUCCESS;
}
break;
case DDI_INFO_DEVT2INSTANCE:
error = DDI_SUCCESS;
break;
default:
error = DDI_FAILURE;
break;
}
return (error);
}
/*ARGSUSED*/
static int
{
return (ENXIO);
return (0);
}
/*ARGSUSED*/
static int
{
return (0);
}
static int
{
static char kernel_only[] =
"do_gfx_ioctl: %s is a kernel only ioctl";
int err;
int kd_mode;
switch (cmd) {
case KDSETMODE:
case KDGETMODE:
return (EFAULT);
break;
case VIS_GETIDENTIFIER:
sizeof (struct vis_identifier), mode))
return (EFAULT);
break;
case VIS_DEVINIT:
return (ENXIO);
}
if (err != 0) {
"vgatext_ioctl: could not initialize console");
return (err);
}
break;
case VIS_CONSCOPY: /* move */
{
struct vis_conscopy pma;
sizeof (struct vis_conscopy), mode))
return (EFAULT);
break;
}
case VIS_CONSDISPLAY: /* display */
{
struct vis_consdisplay display_request;
sizeof (display_request), mode))
return (EFAULT);
break;
}
case VIS_CONSCURSOR:
{
struct vis_conscursor cursor_request;
sizeof (cursor_request), mode))
return (EFAULT);
sizeof (cursor_request), mode))
return (EFAULT);
break;
}
case VIS_GETCMAP:
case VIS_PUTCMAP:
case FBIOPUTCMAP:
case FBIOGETCMAP:
/*
* At the moment, text mode is not considered to have
* a color map.
*/
return (EINVAL);
case FBIOGATTR:
sizeof (struct fbgattr)))
return (EFAULT);
break;
case FBIOGTYPE:
sizeof (struct fbtype)))
return (EFAULT);
break;
default:
return (ENXIO);
}
return (0);
}
/*ARGSUSED*/
static int
int cmd,
int mode,
int *rval)
{
int err;
case GFX_MINOR:
break;
case AGPMASTER_MINOR:
softc->agp_master);
break;
default:
/* not a valid minor node */
return (EBADF);
}
return (err);
}
static void
{
unsigned i;
}
static void
{
extern void progressbar_stop(void);
if (vgatext_silent == 1) {
vgatext_silent = 0;
}
}
static void
{
int i;
}
}
}
static void
{
#if defined(USE_BORDERS)
#endif
}
static int
{
return (0);
switch (mode) {
case KD_TEXT:
break;
case KD_GRAPHICS:
break;
case KD_RESETTEXT:
/*
* In order to avoid racing with a starting X server,
* this needs to be a test and set that is performed in
* a single (softc->lock protected) ioctl into this driver.
*/
}
break;
default:
return (EINVAL);
}
return (0);
}
/*ARGSUSED*/
static int
{
struct vgatext_softc *softc;
int err;
return (-1);
}
if (!(off >= VGA_MMAP_FB_BASE &&
return (-1);
}
else
return (err);
}
return (0);
}
static int
{
/* initialize console instance */
return (0);
}
/*
* display a string on the screen at (row, col)
* assume it has been cropped to fit.
*/
static void
{
unsigned char *string;
int i;
unsigned char attr;
struct cgatext {
unsigned char ch;
unsigned char attr;
};
/*
* Sanity checks. This is a last-ditch effort to avoid damage
* from brokenness or maliciousness above.
*/
return;
/*
* To be fully general, we should copyin the data. This is not
* really relevant for this text-only driver, but a graphical driver
* should support these ioctls from userland to enable simple
* system startup graphics.
*/
addr++;
}
}
static void
struct vis_polledio_arg *arg,
struct vis_consdisplay *da)
{
}
/*
* screen-to-screen copy
*/
static void
{
unsigned short *from;
unsigned short *to;
int cnt;
unsigned short *to_row_start;
unsigned short *from_row_start;
unsigned short *base;
/*
* Sanity checks. Note that this is a last-ditch effort to avoid
* damage caused by broken-ness or maliciousness above.
*/
return;
/*
* Remember we're going to copy shorts because each
*/
/* More sanity checks. */
return;
if (to_row_start < from_row_start) {
while (rows_to_move-- > 0) {
to = to_row_start;
}
} else {
/*
* Offset to the end of the region and copy backwards.
*/
to_row_start += cnt;
from_row_start += cnt;
while (rows_to_move-- > 0) {
to = to_row_start;
}
}
}
static void
struct vis_polledio_arg *arg,
struct vis_conscopy *ca)
{
}
static void
{
if (vgatext_silent)
return;
case VIS_HIDE_CURSOR:
break;
case VIS_DISPLAY_CURSOR:
/*
* Sanity check. This is a last-ditch effort to avoid
* damage from brokenness or maliciousness above.
*/
return;
break;
case VIS_GET_CURSOR:
}
break;
}
}
static void
struct vis_polledio_arg *arg,
struct vis_conscursor *ca)
{
}
/*ARGSUSED*/
static void
{
/* Nothing at present */
}
static void
{
short addr;
if (vgatext_silent)
return;
}
static void
{
short addr;
}
/*
* This code is experimental. It's only enabled if console is
* set to graphics, a preliminary implementation of happyface boot.
*/
static void
{
int i;
if (happyface_boot == 0)
return;
/* we are in graphics mode, set to text 80X25 mode */
/* set misc registers */
/* set sequencer registers */
for (i = 1; i < NUM_SEQ_REG; i++) {
}
/* set crt controller registers */
for (i = 0; i < NUM_CRTC_REG; i++) {
}
/* set graphics controller registers */
for (i = 0; i < NUM_GRC_REG; i++) {
}
/* set attribute registers */
for (i = 0; i < NUM_ATR_REG; i++) {
}
/* set palette */
for (i = 0; i < VGA_TEXT_CMAP_ENTRIES; i++) {
}
for (i = VGA_TEXT_CMAP_ENTRIES; i < VGA8_CMAP_ENTRIES; i++) {
}
}
static void
{
unsigned char atr_mode;
if (atr_mode & VGA_ATR_MODE_GRAPH)
#if defined(USE_BORDERS)
#else
#endif
}
#if defined(USE_BORDERS)
static void
{
}
#endif
static char vga_fontslot = 0;
static void
{
extern bitmap_data_t font_data_8x16;
int i, j, s;
/* Sync-reset the sequencer registers */
/*
* enable write to plane2, since fonts
* could only be loaded into plane2
*/
/*
* sequentially access data in the bit map being
* selected by MapMask register (index 0x02)
*/
/* Sync-reset ended, and allow the sequencer to operate */
/*
* select plane 2 on Read Mode 0
*/
/*
* system addresses sequentially access data, follow
* Memory Mode register bit 2 in the sequencer
*/
/*
* set range of host memory addresses decoded by VGA
* hardware -- A0000h-BFFFFh (128K region)
*/
/*
* This assumes 8x16 characters, which yield the traditional 80x25
* screen. It really should support other character heights.
*/
bpc = 16;
s = vga_fontslot;
for (i = 0; i < 256; i++) {
for (j = 0; j < bpc; j++)
}
/* Sync-reset the sequencer registers */
/* enable write to plane 0 and 1 */
/*
* enable character map selection
*/
/*
* select font map
*/
/* Sync-reset ended, and allow the sequencer to operate */
/* restore graphic registers */
/* select plane 0 */
/*
* range of host memory addresses decoded by VGA
* hardware -- B8000h-BFFFFh (32K region)
*/
/* enable all color plane */
}
static void
{
int i;
for (i = 0; i < VGA_ATR_NUM_PLT; i++) {
}
for (i = 0; i < VGA8_CMAP_ENTRIES; i++) {
}
}
static void
{
int i;
for (i = 0; i < VGA_ATR_NUM_PLT; i++) {
}
for (i = 0; i < VGA8_CMAP_ENTRIES; i++) {
}
}
/*
* search the entries of the "reg" property for one which has the desired
* combination of phys_hi bits and contains the desired address.
*
* This version searches a PCI-style "reg" property. It was prompted by
* issues surrounding the presence or absence of an entry for the ROM:
* (a) a transition problem with PowerPC Virtual Open Firmware
* (b) uncertainty as to whether an entry will be included on a device
* with ROM support (and so an "active" ROM base address register),
* but no ROM actually installed.
*
* See the note below on vgatext_get_isa_reg_index for the reasons for
* returning the offset.
*
* Note that this routine may not be fully general; it is intended for the
* specific purpose of finding a couple of particular VGA reg entries and
* may not be suitable for all reg-searching purposes.
*/
static int
dev_info_t *const devi,
unsigned long himask,
unsigned long hival,
unsigned long addr,
{
return (-1);
}
continue;
continue;
continue;
continue;
continue;
return (index);
}
return (-1);
}
/*
* search the entries of the "reg" property for one which has the desired
* combination of phys_hi bits and contains the desired address.
*
* This version searches a ISA-style "reg" property. It was prompted by
* issues surrounding 8514/A support. By IEEE 1275 compatibility conventions,
* 8514/A registers should have been added after all standard VGA registers.
* (a) lists the 8514/A registers before the video memory, and then
* (b) also sorts the entries so that I/O entries come before memory
* entries.
*
* It returns the "reg" index and offset into that register set.
* The offset is needed because there exist (broken?) BIOSes that
* report larger ranges enclosing the standard ranges. One reports
* 0x3bf for 0x21 instead of 0x3c0 for 0x20, for instance. Using the
* offset adjusts for this difference in the base of the register set.
*
* Note that this routine may not be fully general; it is intended for the
* specific purpose of finding a couple of particular VGA reg entries and
* may not be suitable for all reg-searching purposes.
*/
static int
dev_info_t *const devi,
unsigned long hival,
unsigned long addr,
{
return (-1);
}
continue;
continue;
continue;
return (index);
}
return (-1);
}