Sync usage with man page.
[netbsd-mini2440.git] / sys / arch / i386 / xbox / xboxfb.c
blobb2f8c9059b6fb9bd743a6e6a0ca06c2ecda13ae1
1 /* $NetBSD: xboxfb.c,v 1.11 2007/12/11 12:00:55 lukem Exp $ */
3 /*
4 * Copyright (c) 2007 Jared D. McNeill <jmcneill@invisible.ca>
5 * Copyright (c) 2006 Andrew Gillham
6 * All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 /*
32 * A console driver for the Xbox.
35 #include <sys/cdefs.h>
36 __KERNEL_RCSID(0, "$NetBSD: xboxfb.c,v 1.11 2007/12/11 12:00:55 lukem Exp $");
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/kernel.h>
41 #include <sys/device.h>
42 #include <sys/malloc.h>
43 #include <sys/callout.h>
44 #include <sys/lwp.h>
45 #include <sys/kauth.h>
47 #include <uvm/uvm_extern.h>
48 /* #include <machine/autoconf.h> */
49 #include <machine/bus.h>
50 #include <machine/xbox.h>
52 #include <dev/pci/pcivar.h>
53 #include <dev/pci/pcireg.h>
54 #include <dev/pci/pcidevs.h>
55 #include <dev/pci/pciio.h>
57 #include <dev/wscons/wsdisplayvar.h>
58 #include <dev/wscons/wsconsio.h>
59 #include <dev/wsfont/wsfont.h>
60 #include <dev/rasops/rasops.h>
61 #include <dev/wscons/wsdisplay_vconsvar.h>
63 #include <dev/i2c/pic16lcreg.h>
65 #include "opt_wsemul.h"
67 MALLOC_DEFINE(M_XBOXFB, "xboxfb", "xboxfb shadow framebuffer");
69 #define SCREEN_WIDTH_SDTV 640
70 #define SCREEN_HEIGHT_SDTV 480
71 #define SCREEN_WIDTH_SDTVWS_CONEXANT 1024
72 #define SCREEN_HEIGHT_SDTVWS_CONEXANT 576
73 #define SCREEN_WIDTH_HDTV 720
74 #define SCREEN_HEIGHT_HDTV 480
75 #define SCREEN_WIDTH_VGA 800
76 #define SCREEN_HEIGHT_VGA 600
77 #define SCREEN_BPP 32
80 * Define a safe area border for TV displays.
82 #define XBOXFB_SAFE_AREA_SDTV_LEFT 40
83 #define XBOXFB_SAFE_AREA_SDTV_TOP 40
84 #define XBOXFB_SAFE_AREA_HDTV_LEFT 72
85 #define XBOXFB_SAFE_AREA_HDTV_TOP 16
87 #define FONT_HEIGHT 16
88 #define FONT_WIDTH 8
90 #define CHAR_HEIGHT 16
91 #define CHAR_WIDTH 10
94 #define XBOX_RAM_SIZE (arch_i386_xbox_memsize * 1024 * 1024)
95 #define XBOX_FB_SIZE (0x400000)
96 #define XBOX_FB_START (0xF0000000 | (XBOX_RAM_SIZE - XBOX_FB_SIZE))
97 #define XBOX_FB_START_PTR (0xFD600800)
100 struct xboxfb_softc {
101 struct vcons_data vd;
103 bus_space_tag_t sc_memt;
104 bus_space_handle_t sc_memh;
106 void *sc_ih;
108 size_t memsize;
110 int sc_mode;
111 uint32_t sc_bg;
114 static bus_space_handle_t xboxfb_console_memh;
115 static struct vcons_screen xboxfb_console_screen;
116 static uint8_t *xboxfb_console_bits;
117 static int xboxfb_console_width;
118 static int xboxfb_console_height;
120 static int xboxfb_match(device_t, cfdata_t, void *);
121 static void xboxfb_attach(device_t, device_t, void *);
123 static uint8_t xboxfb_get_avpack(void);
124 static void xboxfb_clear_fb(struct xboxfb_softc *);
126 CFATTACH_DECL_NEW(xboxfb, sizeof(struct xboxfb_softc), xboxfb_match,
127 xboxfb_attach, NULL, NULL);
129 /* static void xboxfb_init(struct xboxfb_softc *); */
131 /* static void xboxfb_cursor(void *, int, int, int); */
132 /* static void xboxfb_copycols(void *, int, int, int, int); */
133 /* static void xboxfb_erasecols(void *, int, int, int, long); */
134 /* static void xboxfb_copyrows(void *, int, int, int); */
135 /* static void xboxfb_eraserows(void *, int, int, long); */
137 struct wsscreen_descr xboxfb_defaultscreen = {
138 "default",
139 0, 0,
140 NULL,
141 8, 16,
142 WSSCREEN_WSCOLORS,
143 NULL,
146 const struct wsscreen_descr *_xboxfb_scrlist[] = {
147 &xboxfb_defaultscreen,
150 struct wsscreen_list xboxfb_screenlist = {
151 sizeof(_xboxfb_scrlist) / sizeof(struct wsscreen_descr *),
152 _xboxfb_scrlist
155 static int xboxfb_ioctl(void *, void *, u_long, void *, int,
156 struct lwp *);
157 static paddr_t xboxfb_mmap(void *, void *, off_t, int);
158 static void xboxfb_init_screen(void *, struct vcons_screen *, int,
159 long *);
161 struct wsdisplay_accessops xboxfb_accessops = {
162 xboxfb_ioctl,
163 xboxfb_mmap,
164 NULL, /* alloc_screen */
165 NULL, /* free_screen */
166 NULL, /* show_screen */
167 NULL, /* load_font */
168 NULL, /* pollc */
169 NULL, /* scroll */
172 static int
173 xboxfb_match(device_t parent, cfdata_t match, void *aux)
175 struct pci_attach_args *pa = (struct pci_attach_args *)aux;
177 /* Don't match on non-Xbox i386 */
178 if (!arch_i386_is_xbox) {
179 return (0);
182 if (PCI_CLASS(pa->pa_class) != PCI_CLASS_DISPLAY ||
183 PCI_SUBCLASS(pa->pa_class) != PCI_SUBCLASS_DISPLAY_VGA)
184 return 0;
185 if ((PCI_VENDOR(pa->pa_id) == PCI_VENDOR_NVIDIA) &&
186 (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_NVIDIA_XBOXFB))
187 return 100;
188 return 0;
191 static void
192 xboxfb_attach(device_t parent, device_t self, void *aux)
194 struct xboxfb_softc *sc = device_private(self);
195 struct wsemuldisplaydev_attach_args aa;
196 struct rasops_info *ri;
197 int console;
198 ulong defattr = 0;
199 uint32_t bg, fg, ul;
201 ri = &xboxfb_console_screen.scr_ri;
203 sc->sc_memt = X86_BUS_SPACE_MEM;
204 sc->sc_memh = xboxfb_console_memh;
205 sc->sc_mode = WSDISPLAYIO_MODE_EMUL;
207 aprint_normal(": %dx%d, %d bit framebuffer console\n",
208 xboxfb_console_width, xboxfb_console_height, ri->ri_depth);
210 vcons_init(&sc->vd, sc, &xboxfb_defaultscreen, &xboxfb_accessops);
211 sc->vd.init_screen = xboxfb_init_screen;
213 /* yes, we're the console */
214 console = 1;
216 if (console) {
217 vcons_init_screen(&sc->vd, &xboxfb_console_screen, 1,
218 &defattr);
219 xboxfb_console_screen.scr_flags |= VCONS_SCREEN_IS_STATIC;
222 rasops_unpack_attr(defattr, &fg, &bg, &ul);
223 sc->sc_bg = ri->ri_devcmap[bg];
225 aa.console = console;
226 aa.scrdata = &xboxfb_screenlist;
227 aa.accessops = &xboxfb_accessops;
228 aa.accesscookie = &sc->vd;
230 config_found(self, &aa, wsemuldisplaydevprint);
234 * respond to ioctl requests
237 static int
238 xboxfb_ioctl(void *v, void*vs, u_long cmd, void *data, int flag,
239 struct lwp *l)
241 struct vcons_data *vd = v;
242 struct xboxfb_softc *sc = vd->cookie;
243 struct wsdisplay_fbinfo *wdf;
244 struct vcons_screen *ms = vd->active;
246 switch (cmd) {
247 case WSDISPLAYIO_GTYPE:
248 *(u_int *)data = WSDISPLAY_TYPE_PCIMISC;
249 return 0;
251 case WSDISPLAYIO_GINFO:
252 wdf = (void *)data;
253 wdf->height = xboxfb_console_height;
254 wdf->width = xboxfb_console_width;
255 wdf->depth = ms->scr_ri.ri_depth;
256 wdf->cmsize = 256;
257 return 0;
259 case WSDISPLAYIO_GETCMAP:
260 return EINVAL;
262 case WSDISPLAYIO_PUTCMAP:
263 return EINVAL;
265 case WSDISPLAYIO_LINEBYTES:
266 *(u_int *)data = ms->scr_ri.ri_stride;
267 return 0;
269 case WSDISPLAYIO_SMODE:
271 int new_mode = *(int *)data;
272 if (new_mode != sc->sc_mode) {
273 sc->sc_mode = new_mode;
274 if (new_mode == WSDISPLAYIO_MODE_EMUL) {
275 xboxfb_clear_fb(sc);
276 vcons_redraw_screen(vd->active);
280 return 0;
282 return EPASSTHROUGH;
285 static paddr_t
286 xboxfb_mmap(void *v, void *vs, off_t offset, int prot)
288 struct vcons_data *vd;
289 struct xboxfb_softc *sc;
290 paddr_t pa;
292 vd = (struct vcons_data *)v;
293 sc = (struct xboxfb_softc *)vd->cookie;
295 if (offset >= 0 && offset < XBOX_FB_SIZE) {
296 pa = bus_space_mmap(X86_BUS_SPACE_MEM, XBOX_FB_START,
297 offset, prot, BUS_SPACE_MAP_LINEAR);
298 return pa;
301 return (-1);
304 static void
305 xboxfb_init_screen(void *cookie, struct vcons_screen *scr,
306 int existing, long *defattr)
308 /*struct xboxfb_softc *sc = cookie;*/
309 struct rasops_info *ri = &scr->scr_ri;
310 struct rasops_info *console_ri;
312 if (scr == &xboxfb_console_screen)
313 return;
314 console_ri = &xboxfb_console_screen.scr_ri;
316 ri->ri_depth = console_ri->ri_depth;
317 ri->ri_width = console_ri->ri_width;
318 ri->ri_height = console_ri->ri_height;
319 ri->ri_stride = console_ri->ri_stride;
320 ri->ri_flg = 0;
322 ri->ri_hwbits = console_ri->ri_hwbits;
323 ri->ri_bits = console_ri->ri_bits;
325 if (existing)
326 ri->ri_flg |= RI_CLEAR;
328 rasops_init(ri, ri->ri_height / 8, ri->ri_width / 8);
329 ri->ri_caps = WSSCREEN_WSCOLORS;
331 rasops_reconfig(ri, ri->ri_height / ri->ri_font->fontheight,
332 ri->ri_width / ri->ri_font->fontwidth);
334 ri->ri_hw = scr;
337 static void
338 xboxfb_clear_fb(struct xboxfb_softc *sc)
340 struct rasops_info *ri;
341 uint32_t fbsize;
343 ri = &xboxfb_console_screen.scr_ri;
344 fbsize = ri->ri_height * ri->ri_stride;
346 memset(xboxfb_console_bits, 0, fbsize);
350 * Gross hack to determine the display resolution based on the type of
351 * AV cable attached at boot. Since we don't have the capability of changing
352 * the display resolution yet, we need to rely on Cromwell/Xromwell to
353 * set this up for us.
355 #define XBOX_SMBUS_BA 0xc000
356 #define XBOX_PIC_ADDR 0x10
358 static uint8_t
359 xboxfb_smbus_read(bus_space_tag_t t, bus_space_handle_t h, uint8_t addr,
360 uint8_t cmd)
362 uint8_t val;
364 bus_space_write_1(t, h, 0x04, (addr << 1) | 1);
365 bus_space_write_1(t, h, 0x08, cmd);
366 bus_space_write_2(t, h, 0x00, bus_space_read_2(t, h, 0x00));
367 bus_space_write_1(t, h, 0x02, 0x0a);
368 while (((val = bus_space_read_1(t, h, 0x00)) & 0x36) == 0)
370 if (((val & 0x10) == 0) || (val & 0x24))
371 return 0xff;
372 return bus_space_read_1(t, h, 0x06);
375 static uint8_t
376 xboxfb_smbus_pic_read(bus_space_tag_t t, bus_space_handle_t h, uint8_t cmd)
378 return xboxfb_smbus_read(t, h, XBOX_PIC_ADDR, cmd);
382 * Detect the TV encoder type; used to help determine which mode has been
383 * setup by the bootloader.
385 #define XBOXFB_ENCODER_CONEXANT 1
386 #define XBOXFB_ENCODER_FOCUS 2
387 #define XBOXFB_ENCODER_XCALIBUR 3
389 static uint8_t
390 xboxfb_get_encoder(void)
392 bus_space_tag_t t = X86_BUS_SPACE_IO;
393 bus_space_handle_t h;
394 uint8_t rv;
396 rv = bus_space_map(t, XBOX_SMBUS_BA, 16, 0, &h);
397 if (rv)
398 return XBOXFB_ENCODER_XCALIBUR; /* shouldn't happen */
400 if (xboxfb_smbus_read(t, h, 0x45, 0x00) != 0xff)
401 rv = XBOXFB_ENCODER_CONEXANT;
402 else if (xboxfb_smbus_read(t, h, 0x6a, 0x00) != 0xff)
403 rv = XBOXFB_ENCODER_FOCUS;
404 else
405 rv = XBOXFB_ENCODER_XCALIBUR;
407 bus_space_unmap(t, h, 16);
409 return rv;
413 * Detect widescreen settings from the EEPROM
415 static uint8_t
416 xboxfb_is_widescreen(void)
418 bus_space_tag_t t = X86_BUS_SPACE_IO;
419 bus_space_handle_t h;
420 uint8_t rv;
422 rv = bus_space_map(t, XBOX_SMBUS_BA, 16, 0, &h);
423 if (rv)
424 return 0;
426 rv = xboxfb_smbus_read(t, h, 0x54, 0x96) & 1;
428 bus_space_unmap(t, h, 16);
430 return rv;
433 static uint8_t
434 xboxfb_get_avpack(void)
436 bus_space_tag_t t;
437 bus_space_handle_t h;
438 uint8_t rv;
440 t = X86_BUS_SPACE_IO;
441 rv = bus_space_map(t, XBOX_SMBUS_BA, 16, 0, &h);
442 if (rv)
443 return PIC16LC_REG_AVPACK_COMPOSITE; /* shouldn't happen */
445 rv = xboxfb_smbus_pic_read(t, h, PIC16LC_REG_AVPACK);
447 bus_space_unmap(t, h, 16);
449 return rv;
453 xboxfb_cnattach(void)
455 static int ncalls = 0;
456 struct rasops_info *ri = &xboxfb_console_screen.scr_ri;
457 uint8_t *xboxfb_console_shadowbits = NULL;
458 long defattr;
459 uint8_t avpack;
460 uint32_t fbsize;
461 uint32_t sa_top, sa_left;
463 /* We can't attach if we're not running on an Xbox... */
464 if (!arch_i386_is_xbox)
465 return 1;
467 /* XXX jmcneill
468 * Console initialization is called multiple times on i386; the first
469 * two being too early for us to use malloc or bus_space_map. Defer
470 * initialization under these subsystems are ready.
472 ++ncalls;
473 if (ncalls < 3)
474 return -1;
476 wsfont_init();
479 * We need to ask the pic16lc for the avpack type to determine
480 * which video mode the loader has setup for us
482 avpack = xboxfb_get_avpack();
483 switch (avpack) {
484 case PIC16LC_REG_AVPACK_HDTV:
485 ri->ri_width = SCREEN_WIDTH_HDTV;
486 ri->ri_height = SCREEN_HEIGHT_HDTV;
487 sa_top = XBOXFB_SAFE_AREA_HDTV_TOP;
488 sa_left = XBOXFB_SAFE_AREA_HDTV_LEFT;
489 break;
490 case PIC16LC_REG_AVPACK_VGA:
491 case PIC16LC_REG_AVPACK_VGA_SOG:
492 ri->ri_width = SCREEN_WIDTH_VGA;
493 ri->ri_height = SCREEN_HEIGHT_VGA;
494 sa_top = sa_left = 0;
495 break;
496 default:
497 /* Ugh, Cromwell puts Xboxes w/ Conexant encoders that are
498 * configured for widescreen mode into a different resolution.
499 * compensate for that here.
501 if (xboxfb_is_widescreen() &&
502 xboxfb_get_encoder() == XBOXFB_ENCODER_CONEXANT) {
503 ri->ri_width = SCREEN_WIDTH_SDTVWS_CONEXANT;
504 ri->ri_height = SCREEN_HEIGHT_SDTVWS_CONEXANT;
505 } else {
506 ri->ri_width = SCREEN_WIDTH_SDTV;
507 ri->ri_height = SCREEN_HEIGHT_SDTV;
509 sa_top = XBOXFB_SAFE_AREA_SDTV_TOP;
510 sa_left = XBOXFB_SAFE_AREA_SDTV_LEFT;
511 break;
514 fbsize = ri->ri_width * ri->ri_height * 4;
516 xboxfb_console_shadowbits = malloc(fbsize, M_XBOXFB, M_NOWAIT);
518 if (xboxfb_console_shadowbits == NULL)
519 aprint_error("xboxfb_cnattach: failed to allocate shadowfb\n");
521 if (bus_space_map(X86_BUS_SPACE_MEM, XBOX_FB_START, fbsize,
522 BUS_SPACE_MAP_LINEAR, &xboxfb_console_memh)) {
523 aprint_error("xboxfb_cnattach: failed to map memory.\n");
524 return 1;
527 ri->ri_depth = SCREEN_BPP;
528 ri->ri_stride = ri->ri_width * ri->ri_depth / 8;
529 ri->ri_flg = 0; /* RI_CENTER does not work with shadowfb */
530 if (xboxfb_console_shadowbits) {
531 ri->ri_bits = xboxfb_console_shadowbits;
532 ri->ri_hwbits = bus_space_vaddr(X86_BUS_SPACE_MEM,
533 xboxfb_console_memh);
534 xboxfb_console_bits = ri->ri_hwbits;
535 } else {
536 ri->ri_bits = bus_space_vaddr(X86_BUS_SPACE_MEM,
537 xboxfb_console_memh);
538 ri->ri_hwbits = NULL;
539 xboxfb_console_bits = ri->ri_bits;
542 /* clear screen */
543 if (ri->ri_bits != NULL)
544 memset(ri->ri_bits, 0, fbsize);
545 if (ri->ri_hwbits != NULL)
546 memset(ri->ri_hwbits, 0, fbsize);
549 xboxfb_console_width = ri->ri_width;
550 xboxfb_console_height = ri->ri_height;
552 /* Define a TV safe area where applicable */
553 if (sa_left > 0) {
554 ri->ri_hwbits += (sa_left * 4);
555 ri->ri_width -= (sa_left * 2);
557 if (sa_top > 0) {
558 ri->ri_hwbits += (ri->ri_stride * sa_top);
559 ri->ri_height -= (sa_top * 2);
562 rasops_init(ri, ri->ri_height / 8, ri->ri_width / 8);
563 ri->ri_caps = WSSCREEN_WSCOLORS;
564 rasops_reconfig(ri,
565 ri->ri_height / ri->ri_font->fontheight,
566 ri->ri_width / ri->ri_font->fontwidth);
568 xboxfb_defaultscreen.nrows = ri->ri_rows;
569 xboxfb_defaultscreen.ncols = ri->ri_cols;
570 xboxfb_defaultscreen.textops = &ri->ri_ops;
571 xboxfb_defaultscreen.capabilities = ri->ri_caps;
572 ri->ri_ops.allocattr(ri, 0, 0, 0, &defattr);
574 wsdisplay_preattach(&xboxfb_defaultscreen, ri, 0, 0, defattr);
576 return 0;