File: [cvs.NetBSD.org] / src / sys / arch / i386 / xbox / Attic / xboxfb.c (download)
Revision 1.9, Sun Jan 7 19:40:50 2007 UTC (17 years, 2 months ago) by jmcneill
Branch: MAIN
CVS Tags: post-newlock2-merge, newlock2-nbase, newlock2-base, ad-audiomp-base, ad-audiomp Branch point for: yamt-lazymbuf, yamt-idlelwp, newlock2
Changes since 1.8: +87 -13
lines
Another annoyance with Cromwell/Xromwell; apparently if your Xbox has a
Conexant TV encoder and the Microsoft dashboard is set to widescreen AND
you are using a standard definition AV pack, the loader configures the
framebuffer to 1024x576. Reported by riz@
|
/* $NetBSD: xboxfb.c,v 1.9 2007/01/07 19:40:50 jmcneill Exp $ */
/*
* Copyright (c) 2007 Jared D. McNeill <jmcneill@invisible.ca>
* Copyright (c) 2006 Andrew Gillham
* 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.
*/
/*
* A console driver for the Xbox.
*/
#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/callout.h>
#include <sys/lwp.h>
#include <sys/kauth.h>
#include <uvm/uvm_extern.h>
/* #include <machine/autoconf.h> */
#include <machine/bus.h>
#include <machine/xbox.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcidevs.h>
#include <dev/pci/pciio.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/i2c/pic16lcreg.h>
#include "opt_wsemul.h"
MALLOC_DEFINE(M_XBOXFB, "xboxfb", "xboxfb shadow framebuffer");
#define SCREEN_WIDTH_SDTV 640
#define SCREEN_HEIGHT_SDTV 480
#define SCREEN_WIDTH_SDTVWS_CONEXANT 1024
#define SCREEN_HEIGHT_SDTVWS_CONEXANT 576
#define SCREEN_WIDTH_HDTV 720
#define SCREEN_HEIGHT_HDTV 480
#define SCREEN_WIDTH_VGA 800
#define SCREEN_HEIGHT_VGA 600
#define SCREEN_BPP 32
/*
* Define a safe area border for TV displays.
*/
#define XBOXFB_SAFE_AREA_SDTV_LEFT 40
#define XBOXFB_SAFE_AREA_SDTV_TOP 40
#define XBOXFB_SAFE_AREA_HDTV_LEFT 72
#define XBOXFB_SAFE_AREA_HDTV_TOP 16
#define FONT_HEIGHT 16
#define FONT_WIDTH 8
#define CHAR_HEIGHT 16
#define CHAR_WIDTH 10
/*
#define XBOX_RAM_SIZE (arch_i386_xbox_memsize * 1024 * 1024)
#define XBOX_FB_SIZE (0x400000)
#define XBOX_FB_START (0xF0000000 | (XBOX_RAM_SIZE - XBOX_FB_SIZE))
#define XBOX_FB_START_PTR (0xFD600800)
*/
struct xboxfb_softc {
struct device sc_dev;
struct vcons_data vd;
bus_space_tag_t sc_memt;
bus_space_handle_t sc_memh;
void *sc_ih;
size_t memsize;
int sc_mode;
uint32_t sc_bg;
};
static bus_space_handle_t xboxfb_console_memh;
static struct vcons_screen xboxfb_console_screen;
static uint8_t *xboxfb_console_bits;
static int xboxfb_console_width;
static int xboxfb_console_height;
static int xboxfb_match(struct device *, struct cfdata *, void *);
static void xboxfb_attach(struct device *, struct device *, void *);
static uint8_t xboxfb_get_avpack(void);
static void xboxfb_clear_fb(struct xboxfb_softc *);
CFATTACH_DECL(xboxfb, sizeof(struct xboxfb_softc), xboxfb_match,
xboxfb_attach, NULL, NULL);
/* static void xboxfb_init(struct xboxfb_softc *); */
/* static void xboxfb_cursor(void *, int, int, int); */
/* static void xboxfb_copycols(void *, int, int, int, int); */
/* static void xboxfb_erasecols(void *, int, int, int, long); */
/* static void xboxfb_copyrows(void *, int, int, int); */
/* static void xboxfb_eraserows(void *, int, int, long); */
struct wsscreen_descr xboxfb_defaultscreen = {
"default",
0, 0,
NULL,
8, 16,
WSSCREEN_WSCOLORS,
NULL,
};
const struct wsscreen_descr *_xboxfb_scrlist[] = {
&xboxfb_defaultscreen,
};
struct wsscreen_list xboxfb_screenlist = {
sizeof(_xboxfb_scrlist) / sizeof(struct wsscreen_descr *),
_xboxfb_scrlist
};
static int xboxfb_ioctl(void *, void *, u_long, caddr_t, int,
struct lwp *);
static paddr_t xboxfb_mmap(void *, void *, off_t, int);
static void xboxfb_init_screen(void *, struct vcons_screen *, int,
long *);
struct wsdisplay_accessops xboxfb_accessops = {
xboxfb_ioctl,
xboxfb_mmap,
NULL, /* alloc_screen */
NULL, /* free_screen */
NULL, /* show_screen */
NULL, /* load_font */
NULL, /* pollc */
NULL, /* scroll */
};
static int
xboxfb_match(struct device *parent, struct cfdata *match, void *aux)
{
struct pci_attach_args *pa = (struct pci_attach_args *)aux;
/* Don't match on non-Xbox i386 */
if (!arch_i386_is_xbox) {
return (0);
}
if (PCI_CLASS(pa->pa_class) != PCI_CLASS_DISPLAY ||
PCI_SUBCLASS(pa->pa_class) != PCI_SUBCLASS_DISPLAY_VGA)
return 0;
if ((PCI_VENDOR(pa->pa_id) == PCI_VENDOR_NVIDIA) &&
(PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_NVIDIA_XBOXFB))
return 100;
return 0;
};
static void
xboxfb_attach(struct device *parent, struct device *self, void *aux)
{
struct xboxfb_softc *sc = (void *)self;
struct wsemuldisplaydev_attach_args aa;
struct rasops_info *ri;
int console;
ulong defattr = 0;
uint32_t bg, fg, ul;
ri = &xboxfb_console_screen.scr_ri;
sc->sc_memt = X86_BUS_SPACE_MEM;
sc->sc_memh = xboxfb_console_memh;
sc->sc_mode = WSDISPLAYIO_MODE_EMUL;
aprint_normal(": %dx%d, %d bit framebuffer console\n",
xboxfb_console_width, xboxfb_console_height, ri->ri_depth);
vcons_init(&sc->vd, sc, &xboxfb_defaultscreen, &xboxfb_accessops);
sc->vd.init_screen = xboxfb_init_screen;
/* yes, we're the console */
console = 1;
if (console) {
vcons_init_screen(&sc->vd, &xboxfb_console_screen, 1,
&defattr);
xboxfb_console_screen.scr_flags |= VCONS_SCREEN_IS_STATIC;
}
rasops_unpack_attr(defattr, &fg, &bg, &ul);
sc->sc_bg = ri->ri_devcmap[bg];
aa.console = console;
aa.scrdata = &xboxfb_screenlist;
aa.accessops = &xboxfb_accessops;
aa.accesscookie = &sc->vd;
config_found(self, &aa, wsemuldisplaydevprint);
}
/*
* respond to ioctl requests
*/
static int
xboxfb_ioctl(void *v, void*vs, u_long cmd, caddr_t data, int flag,
struct lwp *l)
{
struct vcons_data *vd = v;
struct xboxfb_softc *sc = vd->cookie;
struct wsdisplay_fbinfo *wdf;
struct vcons_screen *ms = vd->active;
switch (cmd) {
case WSDISPLAYIO_GTYPE:
*(u_int *)data = WSDISPLAY_TYPE_PCIMISC;
return 0;
case WSDISPLAYIO_GINFO:
wdf = (void *)data;
wdf->height = xboxfb_console_height;
wdf->width = xboxfb_console_width;
wdf->depth = ms->scr_ri.ri_depth;
wdf->cmsize = 256;
return 0;
case WSDISPLAYIO_GETCMAP:
return EINVAL;
case WSDISPLAYIO_PUTCMAP:
return EINVAL;
case WSDISPLAYIO_LINEBYTES:
*(u_int *)data = ms->scr_ri.ri_stride;
return 0;
case WSDISPLAYIO_SMODE:
{
int new_mode = *(int *)data;
if (new_mode != sc->sc_mode) {
sc->sc_mode = new_mode;
if (new_mode == WSDISPLAYIO_MODE_EMUL) {
xboxfb_clear_fb(sc);
vcons_redraw_screen(vd->active);
}
}
}
return 0;
}
return EPASSTHROUGH;
}
static paddr_t
xboxfb_mmap(void *v, void *vs, off_t offset, int prot)
{
struct vcons_data *vd;
struct xboxfb_softc *sc;
paddr_t pa;
vd = (struct vcons_data *)v;
sc = (struct xboxfb_softc *)vd->cookie;
if (offset >= 0 && offset < XBOX_FB_SIZE) {
pa = bus_space_mmap(X86_BUS_SPACE_MEM, XBOX_FB_START,
offset, prot, BUS_SPACE_MAP_LINEAR);
return pa;
}
return (-1);
}
static void
xboxfb_init_screen(void *cookie, struct vcons_screen *scr,
int existing, long *defattr)
{
/*struct xboxfb_softc *sc = cookie;*/
struct rasops_info *ri = &scr->scr_ri;
struct rasops_info *console_ri;
if (scr == &xboxfb_console_screen)
return;
console_ri = &xboxfb_console_screen.scr_ri;
ri->ri_depth = console_ri->ri_depth;
ri->ri_width = console_ri->ri_width;
ri->ri_height = console_ri->ri_height;
ri->ri_stride = console_ri->ri_stride;
ri->ri_flg = 0;
ri->ri_hwbits = console_ri->ri_hwbits;
ri->ri_bits = console_ri->ri_bits;
if (existing)
ri->ri_flg |= RI_CLEAR;
rasops_init(ri, ri->ri_height / 8, ri->ri_width / 8);
ri->ri_caps = WSSCREEN_WSCOLORS;
rasops_reconfig(ri, ri->ri_height / ri->ri_font->fontheight,
ri->ri_width / ri->ri_font->fontwidth);
ri->ri_hw = scr;
}
static void
xboxfb_clear_fb(struct xboxfb_softc *sc)
{
struct rasops_info *ri;
uint32_t fbsize;
ri = &xboxfb_console_screen.scr_ri;
fbsize = ri->ri_height * ri->ri_stride;
memset(xboxfb_console_bits, 0, fbsize);
}
/*
* Gross hack to determine the display resolution based on the type of
* AV cable attached at boot. Since we don't have the capability of changing
* the display resolution yet, we need to rely on Cromwell/Xromwell to
* set this up for us.
*/
#define XBOX_SMBUS_BA 0xc000
#define XBOX_PIC_ADDR 0x10
static uint8_t
xboxfb_smbus_read(bus_space_tag_t t, bus_space_handle_t h, uint8_t addr,
uint8_t cmd)
{
uint8_t val;
bus_space_write_1(t, h, 0x04, (addr << 1) | 1);
bus_space_write_1(t, h, 0x08, cmd);
bus_space_write_2(t, h, 0x00, bus_space_read_2(t, h, 0x00));
bus_space_write_1(t, h, 0x02, 0x0a);
while (((val = bus_space_read_1(t, h, 0x00)) & 0x36) == 0)
;
if (((val & 0x10) == 0) || (val & 0x24))
return 0xff;
return bus_space_read_1(t, h, 0x06);
}
static uint8_t
xboxfb_smbus_pic_read(bus_space_tag_t t, bus_space_handle_t h, uint8_t cmd)
{
return xboxfb_smbus_read(t, h, XBOX_PIC_ADDR, cmd);
}
/*
* Detect the TV encoder type; used to help determine which mode has been
* setup by the bootloader.
*/
#define XBOXFB_ENCODER_CONEXANT 1
#define XBOXFB_ENCODER_FOCUS 2
#define XBOXFB_ENCODER_XCALIBUR 3
static uint8_t
xboxfb_get_encoder(void)
{
bus_space_tag_t t = X86_BUS_SPACE_IO;
bus_space_handle_t h;
uint8_t rv;
rv = bus_space_map(t, XBOX_SMBUS_BA, 16, 0, &h);
if (rv)
return XBOXFB_ENCODER_XCALIBUR; /* shouldn't happen */
if (xboxfb_smbus_read(t, h, 0x45, 0x00) != 0xff)
rv = XBOXFB_ENCODER_CONEXANT;
else if (xboxfb_smbus_read(t, h, 0x6a, 0x00) != 0xff)
rv = XBOXFB_ENCODER_FOCUS;
else
rv = XBOXFB_ENCODER_XCALIBUR;
bus_space_unmap(t, h, 16);
return rv;
}
/*
* Detect widescreen settings from the EEPROM
*/
static uint8_t
xboxfb_is_widescreen(void)
{
bus_space_tag_t t = X86_BUS_SPACE_IO;
bus_space_handle_t h;
uint8_t rv;
rv = bus_space_map(t, XBOX_SMBUS_BA, 16, 0, &h);
if (rv)
return 0;
rv = xboxfb_smbus_read(t, h, 0x54, 0x96) & 1;
bus_space_unmap(t, h, 16);
return rv;
}
static uint8_t
xboxfb_get_avpack(void)
{
bus_space_tag_t t;
bus_space_handle_t h;
uint8_t rv;
t = X86_BUS_SPACE_IO;
rv = bus_space_map(t, XBOX_SMBUS_BA, 16, 0, &h);
if (rv)
return PIC16LC_REG_AVPACK_COMPOSITE; /* shouldn't happen */
rv = xboxfb_smbus_pic_read(t, h, PIC16LC_REG_AVPACK);
bus_space_unmap(t, h, 16);
return rv;
}
int
xboxfb_cnattach(void)
{
static int ncalls = 0;
struct rasops_info *ri = &xboxfb_console_screen.scr_ri;
uint8_t *xboxfb_console_shadowbits = NULL;
long defattr;
uint8_t avpack;
uint32_t fbsize;
uint32_t sa_top, sa_left;
/* We can't attach if we're not running on an Xbox... */
if (!arch_i386_is_xbox)
return 1;
/* XXX jmcneill
* Console initialization is called multiple times on i386; the first
* two being too early for us to use malloc or bus_space_map. Defer
* initialization under these subsystems are ready.
*/
++ncalls;
if (ncalls < 3)
return -1;
wsfont_init();
/*
* We need to ask the pic16lc for the avpack type to determine
* which video mode the loader has setup for us
*/
avpack = xboxfb_get_avpack();
switch (avpack) {
case PIC16LC_REG_AVPACK_HDTV:
ri->ri_width = SCREEN_WIDTH_HDTV;
ri->ri_height = SCREEN_HEIGHT_HDTV;
sa_top = XBOXFB_SAFE_AREA_HDTV_TOP;
sa_left = XBOXFB_SAFE_AREA_HDTV_LEFT;
break;
case PIC16LC_REG_AVPACK_VGA:
case PIC16LC_REG_AVPACK_VGA_SOG:
ri->ri_width = SCREEN_WIDTH_VGA;
ri->ri_height = SCREEN_HEIGHT_VGA;
sa_top = sa_left = 0;
break;
default:
/* Ugh, Cromwell puts Xboxes w/ Conexant encoders that are
* configured for widescreen mode into a different resolution.
* compensate for that here.
*/
if (xboxfb_is_widescreen() &&
xboxfb_get_encoder() == XBOXFB_ENCODER_CONEXANT) {
ri->ri_width = SCREEN_WIDTH_SDTVWS_CONEXANT;
ri->ri_height = SCREEN_HEIGHT_SDTVWS_CONEXANT;
} else {
ri->ri_width = SCREEN_WIDTH_SDTV;
ri->ri_height = SCREEN_HEIGHT_SDTV;
}
sa_top = XBOXFB_SAFE_AREA_SDTV_TOP;
sa_left = XBOXFB_SAFE_AREA_SDTV_LEFT;
break;
}
fbsize = ri->ri_width * ri->ri_height * 4;
xboxfb_console_shadowbits = malloc(fbsize, M_XBOXFB, M_NOWAIT);
if (xboxfb_console_shadowbits == NULL)
aprint_error("xboxfb_cnattach: failed to allocate shadowfb\n");
if (bus_space_map(X86_BUS_SPACE_MEM, XBOX_FB_START, fbsize,
BUS_SPACE_MAP_LINEAR, &xboxfb_console_memh)) {
aprint_error("xboxfb_cnattach: failed to map memory.\n");
return 1;
}
ri->ri_depth = SCREEN_BPP;
ri->ri_stride = ri->ri_width * ri->ri_depth / 8;
ri->ri_flg = 0; /* RI_CENTER does not work with shadowfb */
if (xboxfb_console_shadowbits) {
ri->ri_bits = xboxfb_console_shadowbits;
ri->ri_hwbits = bus_space_vaddr(X86_BUS_SPACE_MEM,
xboxfb_console_memh);
xboxfb_console_bits = ri->ri_hwbits;
} else {
ri->ri_bits = bus_space_vaddr(X86_BUS_SPACE_MEM,
xboxfb_console_memh);
ri->ri_hwbits = NULL;
xboxfb_console_bits = ri->ri_bits;
}
/* clear screen */
if (ri->ri_bits != NULL)
memset(ri->ri_bits, 0, fbsize);
if (ri->ri_hwbits != NULL)
memset(ri->ri_hwbits, 0, fbsize);
xboxfb_console_width = ri->ri_width;
xboxfb_console_height = ri->ri_height;
/* Define a TV safe area where applicable */
if (sa_left > 0) {
ri->ri_hwbits += (sa_left * 4);
ri->ri_width -= (sa_left * 2);
}
if (sa_top > 0) {
ri->ri_hwbits += (ri->ri_stride * sa_top);
ri->ri_height -= (sa_top * 2);
}
rasops_init(ri, ri->ri_height / 8, ri->ri_width / 8);
ri->ri_caps = WSSCREEN_WSCOLORS;
rasops_reconfig(ri,
ri->ri_height / ri->ri_font->fontheight,
ri->ri_width / ri->ri_font->fontwidth);
xboxfb_defaultscreen.nrows = ri->ri_rows;
xboxfb_defaultscreen.ncols = ri->ri_cols;
xboxfb_defaultscreen.textops = &ri->ri_ops;
xboxfb_defaultscreen.capabilities = ri->ri_caps;
ri->ri_ops.allocattr(ri, 0, 0, 0, &defattr);
wsdisplay_preattach(&xboxfb_defaultscreen, ri, 0, 0, defattr);
return 0;
}