1 /* $NetBSD: xboxfb.c,v 1.11 2007/12/11 12:00:55 lukem Exp $ */
4 * Copyright (c) 2007 Jared D. McNeill <jmcneill@invisible.ca>
5 * Copyright (c) 2006 Andrew Gillham
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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.
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>
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
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
90 #define CHAR_HEIGHT 16
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
;
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
= {
146 const struct wsscreen_descr
*_xboxfb_scrlist
[] = {
147 &xboxfb_defaultscreen
,
150 struct wsscreen_list xboxfb_screenlist
= {
151 sizeof(_xboxfb_scrlist
) / sizeof(struct wsscreen_descr
*),
155 static int xboxfb_ioctl(void *, void *, u_long
, void *, int,
157 static paddr_t
xboxfb_mmap(void *, void *, off_t
, int);
158 static void xboxfb_init_screen(void *, struct vcons_screen
*, int,
161 struct wsdisplay_accessops xboxfb_accessops
= {
164 NULL
, /* alloc_screen */
165 NULL
, /* free_screen */
166 NULL
, /* show_screen */
167 NULL
, /* load_font */
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
) {
182 if (PCI_CLASS(pa
->pa_class
) != PCI_CLASS_DISPLAY
||
183 PCI_SUBCLASS(pa
->pa_class
) != PCI_SUBCLASS_DISPLAY_VGA
)
185 if ((PCI_VENDOR(pa
->pa_id
) == PCI_VENDOR_NVIDIA
) &&
186 (PCI_PRODUCT(pa
->pa_id
) == PCI_PRODUCT_NVIDIA_XBOXFB
))
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
;
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 */
217 vcons_init_screen(&sc
->vd
, &xboxfb_console_screen
, 1,
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
238 xboxfb_ioctl(void *v
, void*vs
, u_long cmd
, void *data
, int flag
,
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
;
247 case WSDISPLAYIO_GTYPE
:
248 *(u_int
*)data
= WSDISPLAY_TYPE_PCIMISC
;
251 case WSDISPLAYIO_GINFO
:
253 wdf
->height
= xboxfb_console_height
;
254 wdf
->width
= xboxfb_console_width
;
255 wdf
->depth
= ms
->scr_ri
.ri_depth
;
259 case WSDISPLAYIO_GETCMAP
:
262 case WSDISPLAYIO_PUTCMAP
:
265 case WSDISPLAYIO_LINEBYTES
:
266 *(u_int
*)data
= ms
->scr_ri
.ri_stride
;
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
) {
276 vcons_redraw_screen(vd
->active
);
286 xboxfb_mmap(void *v
, void *vs
, off_t offset
, int prot
)
288 struct vcons_data
*vd
;
289 struct xboxfb_softc
*sc
;
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
);
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
)
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
;
322 ri
->ri_hwbits
= console_ri
->ri_hwbits
;
323 ri
->ri_bits
= console_ri
->ri_bits
;
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
);
338 xboxfb_clear_fb(struct xboxfb_softc
*sc
)
340 struct rasops_info
*ri
;
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
359 xboxfb_smbus_read(bus_space_tag_t t
, bus_space_handle_t h
, uint8_t addr
,
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))
372 return bus_space_read_1(t
, h
, 0x06);
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
390 xboxfb_get_encoder(void)
392 bus_space_tag_t t
= X86_BUS_SPACE_IO
;
393 bus_space_handle_t h
;
396 rv
= bus_space_map(t
, XBOX_SMBUS_BA
, 16, 0, &h
);
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
;
405 rv
= XBOXFB_ENCODER_XCALIBUR
;
407 bus_space_unmap(t
, h
, 16);
413 * Detect widescreen settings from the EEPROM
416 xboxfb_is_widescreen(void)
418 bus_space_tag_t t
= X86_BUS_SPACE_IO
;
419 bus_space_handle_t h
;
422 rv
= bus_space_map(t
, XBOX_SMBUS_BA
, 16, 0, &h
);
426 rv
= xboxfb_smbus_read(t
, h
, 0x54, 0x96) & 1;
428 bus_space_unmap(t
, h
, 16);
434 xboxfb_get_avpack(void)
437 bus_space_handle_t h
;
440 t
= X86_BUS_SPACE_IO
;
441 rv
= bus_space_map(t
, XBOX_SMBUS_BA
, 16, 0, &h
);
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);
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
;
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
)
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.
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();
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
;
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;
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
;
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
;
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");
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
;
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
;
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 */
554 ri
->ri_hwbits
+= (sa_left
* 4);
555 ri
->ri_width
-= (sa_left
* 2);
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
;
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
);