/*-
* Copyright (c) 2007 Michael Lorenz
* 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.
*
* 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.
*/
struct genfb_private {
struct genfb_ops sc_ops;
struct vcons_screen sc_console_screen;
struct wsscreen_descr sc_defaultscreen_descr;
const struct wsscreen_descr *sc_screens[1];
struct wsscreen_list sc_screenlist;
struct genfb_colormap_callback *sc_cmcb;
struct genfb_parameter_callback *sc_backlight;
struct genfb_parameter_callback *sc_brightness;
struct genfb_mode_callback *sc_modecb;
uint32_t *sc_devcmap;
int sc_backlight_level, sc_backlight_on;
void *sc_shadowfb;
bool sc_enable_shadowfb;
int sc_mode;
u_char sc_cmap_red[256];
u_char sc_cmap_green[256];
u_char sc_cmap_blue[256];
bool sc_want_clear;
#ifdef SPLASHSCREEN
struct splash_info sc_splash;
#endif
struct wsdisplay_accessops sc_accessops;
#if GENFB_GLYPHCACHE > 0
/*
* The generic glyphcache code makes a bunch of assumptions that are
* true for most graphics hardware with a directly supported blitter.
* For example it assume that
* - VRAM access from the host is expensive
* - copying data around in VRAM is cheap and can happen in parallel
* to the host CPU
* -> therefore we draw glyphs normally if we have to, so the ( assumed
* to be hardware assisted ) driver supplied putchar() method doesn't
* need to be glyphcache aware, then copy them away for later use
* for genfb things are a bit different. On most hardware:
* - VRAM access from the host is still expensive
* - copying data around in VRAM is also expensive since we don't have
* a blitter and VRAM is mapped uncached
* - VRAM reads are usually slower than writes ( write combining and
* such help writes but not reads, and VRAM might be behind an
* asymmetric bus like AGP ) and must be avoided, both are much
* slower than main memory
* -> therefore we cache glyphs in main memory, no reason to map it
* uncached, we draw into the cache first and then copy the glyph
* into video memory to avoid framebuffer reads and to allow more
* efficient write accesses than putchar() would offer
* Because of this we can't use the generic code but we can recycle a
* few data structures.
*/
uint8_t *sc_cache;
struct rasops_info sc_cache_ri;
void (*sc_putchar)(void *, int, int, u_int, long);
int sc_cache_cells;
int sc_nbuckets; /* buckets allocated */
gc_bucket *sc_buckets; /* we allocate as many as we can get into ram */
int sc_attrmap[256]; /* mapping a colour attribute to a bucket */
#endif
};
/*
* deal with a bug in the Raptor firmware which always sets
* stride = width even when depth != 8
*/
if (sc->sc_stride < sc->sc_width * (sc->sc_depth >> 3))
sc->sc_stride = sc->sc_width * (sc->sc_depth >> 3);
#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(&scp->sc_console_screen);
case WSDISPLAYIO_SETPARAM:
param = (struct wsdisplay_param *)data;
switch (param->param) {
case WSDISPLAYIO_PARAM_BRIGHTNESS:
if (scp->sc_brightness == NULL)
return EPASSTHROUGH;
val = param->curval;
if (val < 0)
val = 0;
if (val > 255)
val = 255;
return scp->sc_brightness->gpc_set_parameter(
scp->sc_brightness->gpc_cookie, val);
case WSDISPLAYIO_PARAM_BACKLIGHT:
if (scp->sc_backlight == NULL)
return EPASSTHROUGH;
val = param->curval;
if (val < 0)
val = 0;
if (val > 1)
val = 1;
return scp->sc_backlight->gpc_set_parameter(
scp->sc_backlight->gpc_cookie, val);
}
return EPASSTHROUGH;
}
ret = EPASSTHROUGH;
if (scp->sc_ops.genfb_ioctl)
ret = scp->sc_ops.genfb_ioctl(sc, vs, cmd, data, flag, l);
if (ret != EPASSTHROUGH)
return ret;
/*
* XXX
* handle these only if there either is no ioctl() handler or it didn't
* know how to deal with them. This allows bus frontends to override
* them but still provides fallback implementations
*/
switch (cmd) {
case WSDISPLAYIO_GET_EDID:
{
struct wsdisplayio_edid_info *d = data;
return wsdisplayio_get_edid(sc->sc_dev, d);
}
case WSDISPLAYIO_GET_FBINFO:
{
struct wsdisplayio_fbinfo *fbi = data;
return wsdisplayio_get_fbinfo(&ms->scr_ri, fbi);
}
}
return EPASSTHROUGH;
}
/* Returns the width of the display in millimeters, or 0 if not known. */
static int
genfb_calc_hsize(struct genfb_softc *sc)
{
device_t dev = sc->sc_dev;
prop_dictionary_t dict = device_properties(dev);
prop_data_t edid_data;
struct edid_info *edid;
const char *edid_ptr;
int hsize;
/* Return the minimum number of character columns based on DPI */
static int
genfb_calc_cols(struct genfb_softc *sc, struct rasops_info *ri)
{
const int hsize = genfb_calc_hsize(sc);
if (hsize != 0) {
ri->ri_flg |= RI_PREFER_WIDEFONT;
}
if (sc->sc_depth <= 8) {
for (i = 0; i < (1 << sc->sc_depth); i++) {
genfb_putpalreg(sc, i, scp->sc_cmap_red[i],
scp->sc_cmap_green[i], scp->sc_cmap_blue[i]);
}
}
}
static void
genfb_init_palette(struct genfb_softc *sc)
{
struct genfb_private *scp = sc->sc_private;
int i, j, tmp;
if (sc->sc_depth == 8) {
/* generate an r3g3b2 colour map */
for (i = 0; i < 256; i++) {
tmp = i & 0xe0;
/*
* replicate bits so 0xe0 maps to a red value of 0xff
* in order to make white look actually white
*/
tmp |= (tmp >> 3) | (tmp >> 6);
scp->sc_cmap_red[i] = tmp;
if (scp->sc_console_screen.scr_vd) {
if (scp->sc_ops.genfb_disable_polling)
(*scp->sc_ops.genfb_disable_polling)(sc);
vcons_disable_polling(&sc->vd);
}
}
/*
* now we build a mutant rasops_info for the cache - same pixel type
* and such as the real fb, but only one character per line for
* simplicity and locality
*/
memcpy(cri, ri, sizeof(struct rasops_info));
cri->ri_ops.putchar = scp->sc_putchar;
cri->ri_width = ri->ri_font->fontwidth;
cri->ri_stride = ri->ri_xscale;
cri->ri_bits = scp->sc_cache;
cri->ri_hwbits = NULL;
cri->ri_origbits = scp->sc_cache;
cri->ri_cols = 1;
cri->ri_rows = GLYPHCACHESIZE /
(cri->ri_stride * cri->ri_font->fontheight);
cri->ri_xorigin = 0;
cri->ri_yorigin = 0;
cri->ri_xscale = ri->ri_xscale;
cri->ri_yscale = ri->ri_font->fontheight * ri->ri_xscale;
static void
genfb_putchar(void *cookie, int row, int col, u_int c, long attr)
{
struct rasops_info *ri = cookie;
struct vcons_screen *scr = ri->ri_hw;
struct genfb_softc *sc = scr->scr_cookie;
struct genfb_private *scp = sc->sc_private;
uint8_t *src, *dst, *hwdst;
gc_bucket *b;
int i, idx, bi, cell;
attr &= ~WSATTR_USERMASK;
idx = attr2idx(attr);
if (c < 33 || c > 255 || idx < 0) goto nope;
/* look for a bucket with the right attribute */
bi = scp->sc_attrmap[idx];
if (bi == -1) {
/* nope, see if there's an empty one left */
bi = 1;
while ((bi < scp->sc_nbuckets) &&
(scp->sc_buckets[bi].gb_index != -1)) {
bi++;
}
if (bi < scp->sc_nbuckets) {
/* found one -> grab it */
scp->sc_attrmap[idx] = bi;
b = &scp->sc_buckets[bi];
b->gb_index = idx;
b->gb_usedcells = 0;
/* make sure this doesn't get evicted right away */
b->gb_lastread = time_uptime;
} else {
/*
* still nothing
* steal the least recently read bucket
*/
time_t moo = time_uptime;
int oldest = 1;
for (i = 1; i < scp->sc_nbuckets; i++) {
if (scp->sc_buckets[i].gb_lastread < moo) {
oldest = i;
moo = scp->sc_buckets[i].gb_lastread;
}
}
/* if we end up here all buckets must be in use */
b = &scp->sc_buckets[oldest];
scp->sc_attrmap[b->gb_index] = -1;
b->gb_index = idx;
b->gb_usedcells = 0;
scp->sc_attrmap[idx] = oldest;
/* now scrub it */
for (i = 0; i < 223; i++)
b->gb_map[i] = -1;
/* and set the time stamp */
b->gb_lastread = time_uptime;
}
} else {
/* found one */
b = &scp->sc_buckets[bi];
}
/* see if there's room in the bucket */
if (b->gb_usedcells >= b->gb_numcells) goto nope;