xboxfb.c revision 152219
11558Srgrimes/*-
21558Srgrimes * Copyright (c) 2005 Rink Springer
31558Srgrimes * All rights reserved.
41558Srgrimes *
51558Srgrimes * Redistribution and use in source and binary forms, with or without
61558Srgrimes * modification, are permitted provided that the following conditions
71558Srgrimes * are met:
81558Srgrimes * 1. Redistributions of source code must retain the above copyright
91558Srgrimes *    notice, this list of conditions and the following disclaimer.
101558Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111558Srgrimes *    notice, this list of conditions and the following disclaimer in the
121558Srgrimes *    documentation and/or other materials provided with the distribution.
131558Srgrimes * 3. The name of the author may not be used to endorse or promote products
141558Srgrimes *    derived from this software without specific prior written permission
151558Srgrimes *
161558Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
171558Srgrimes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
181558Srgrimes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
191558Srgrimes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
201558Srgrimes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
211558Srgrimes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
221558Srgrimes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
231558Srgrimes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
241558Srgrimes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
251558Srgrimes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
261558Srgrimes *
271558Srgrimes * $FreeBSD: head/sys/i386/xbox/xboxfb.c 152219 2005-11-09 03:55:40Z imp $
281558Srgrimes */
291558Srgrimes
30114589Sobrien/*
311558Srgrimes * This will handles video output using the XBOX' frame buffer. It assumes
3238036Scharnier * the graphics have been set up by Cromwell. This driver uses all video memory
331558Srgrimes * to avoid expensive memcpy()'s.
341558Srgrimes *
351558Srgrimes * It is usuable as console (to see the initial boot) as well as for interactive
361558Srgrimes * use. The latter is handeled using kbd_*() functionality. Keyboard hotplug is
371558Srgrimes * fully supported, the console will periodically rescan if no keyboard was
3841684Sbde * found.
39114589Sobrien *
4038036Scharnier */
41114589Sobrien#include <sys/param.h>
42114589Sobrien#include <sys/systm.h>
431558Srgrimes#include <vm/vm_param.h>
441558Srgrimes#include <sys/kernel.h>
451558Srgrimes#include <sys/cons.h>
461558Srgrimes#include <sys/conf.h>
471558Srgrimes#include <sys/consio.h>
481558Srgrimes#include <sys/tty.h>
491558Srgrimes#include <sys/kbio.h>
5038036Scharnier#include <sys/fbio.h>
511558Srgrimes#include <dev/kbd/kbdreg.h>
52114763Sobrien#include <vm/vm.h>
531558Srgrimes#include <vm/pmap.h>
541558Srgrimes#include <machine/bus.h>
551558Srgrimes#include <machine/xbox.h>
561558Srgrimes#include <dev/fb/fbreg.h>
571558Srgrimes#include <dev/fb/gfb.h>
581558Srgrimes
591558Srgrimes#define SCREEN_WIDTH	640
601558Srgrimes#define SCREEN_HEIGHT	480
611558Srgrimes#define SCREEN_BPP	4
621558Srgrimes#define SCREEN_SIZE	(SCREEN_WIDTH*SCREEN_HEIGHT*SCREEN_BPP)
631558Srgrimes
641558Srgrimes/* FONT_xxx declares the dimensions of the charachter structure, CHAR_xxx is how
651558Srgrimes * they appear on-screen. Having slightly more spacing improves readability.  */
661558Srgrimes#define FONT_HEIGHT	16
671558Srgrimes#define FONT_WIDTH	8
681558Srgrimes
691558Srgrimes#define CHAR_HEIGHT	16
701558Srgrimes#define CHAR_WIDTH	10
711558Srgrimes
721558Srgrimes#define RAM_SIZE	(arch_i386_xbox_memsize * 1024 * 1024)
7332399Salex#define FB_SIZE		(0x400000)
7432399Salex#define FB_START	(0xf0000000 | (RAM_SIZE - FB_SIZE))
7532399Salex#define FB_START_PTR	(0xFD600800)
7632399Salex
7732399Salex/* colours */
7832399Salex#define CONSOLE_COL	0xFF88FF88	/* greenish */
7932399Salex#define NORM_COL	0xFFAAAAAA	/* grayish */
8032399Salex#define BLACK_COL	0x00000000	/* black */
8132399Salex
8232399Salexstatic int xcon_x     = 0;
8332399Salexstatic int xcon_y     = 0;
8432399Salexstatic int xcon_yoffs = 0;
851558Srgrimes
861558Srgrimesextern struct gfb_font bold8x16;
871558Srgrimes
881558Srgrimesstatic char* xcon_map;
891558Srgrimesstatic int* xcon_memstartptr;
901558Srgrimes
9148078Srustatic struct tty* xboxfb_tp = NULL;
9279749Sddstatic struct keyboard* xbfb_kbd = NULL;
9379749Sddstatic int xbfb_keyboard = -1;
941558Srgrimesstatic d_open_t xboxfb_dev_open;
9592883Simpstatic d_close_t xboxfb_dev_close;
9692883Simpstatic int xboxfb_kbdevent(keyboard_t* thiskbd, int event, void* arg);
9792883Simp
9892883Simpstatic struct cdevsw xboxfb_cdevsw = {
9992883Simp	.d_version = D_VERSION,
10092883Simp	.d_open    = xboxfb_dev_open,
10192883Simp	.d_close   = xboxfb_dev_close,
10292883Simp	.d_name    = "xboxfb",
10392883Simp	.d_flags   = D_TTY | D_NEEDGIANT,
1041558Srgrimes};
105140796Sdelphij
106140796Sdelphijstatic void
1071558Srgrimesxcon_probe(struct consdev* cp)
108140796Sdelphij{
1091558Srgrimes	if (arch_i386_is_xbox)
11079749Sdd		cp->cn_pri = CN_REMOTE;
1111558Srgrimes	else
1121558Srgrimes		cp->cn_pri = CN_DEAD;
1131558Srgrimes}
1141558Srgrimes
11526737Scharnierstatic int
11626737Scharnierxcon_getc(struct consdev* cp)
1171558Srgrimes{
1181558Srgrimes	return 0;
1191558Srgrimes}
12048078Sru
1211558Srgrimesstatic int
1221558Srgrimesxcon_checkc(struct consdev* cp)
1231558Srgrimes{
1241558Srgrimes	return 0;
1251558Srgrimes}
1261558Srgrimes
1271558Srgrimesstatic void
1281558Srgrimesxcon_real_putc(int basecol, int c)
1291558Srgrimes{
1301558Srgrimes	int i, j, ch = c, col;
1311558Srgrimes	char mask;
1321558Srgrimes	int* ptri = (int*)xcon_map;
1331558Srgrimes
13448078Sru	/* special control chars */
13548078Sru	switch (ch) {
13648078Sru	case '\r': /* carriage return */
13741666Smsmith		xcon_x = 0;
13841666Smsmith		return;
13941666Smsmith	case '\n': /* newline */
1401558Srgrimes		xcon_y += CHAR_HEIGHT;
1411558Srgrimes		goto scroll;
1421558Srgrimes	case 7: /* beep */
1431558Srgrimes		return;
1441558Srgrimes	case 8: /* backspace */
14548078Sru		if (xcon_x > 0) {
1461558Srgrimes			xcon_x -= CHAR_WIDTH;
1471558Srgrimes		} else {
1481558Srgrimes			if (xcon_y > CHAR_HEIGHT) {
1491558Srgrimes				xcon_y -= CHAR_HEIGHT;
1501558Srgrimes				xcon_x = (SCREEN_WIDTH - CHAR_WIDTH);
15148078Sru			}
1521558Srgrimes		}
15348078Sru		return;
15448078Sru	case 9: /* tab */
15548062Sjkoshy		xcon_real_putc (basecol, ' ');
15648078Sru		while ((xcon_x % (8 * CHAR_WIDTH)) != 0) {
15748078Sru			xcon_real_putc (basecol, ' ');
15848062Sjkoshy		}
15948078Sru		return;
16048078Sru	}
16148078Sru	ptri += (xcon_y * SCREEN_WIDTH) + xcon_x;
1621558Srgrimes
1631558Srgrimes	/* we plot the font pixel-by-pixel. bit 7 is skipped as it renders the
1641558Srgrimes	 * console unreadable ... */
1651558Srgrimes	for (i = 0; i < FONT_HEIGHT; i++) {
1661558Srgrimes		mask = 0x40;
1671558Srgrimes		for (j = 0; j < FONT_WIDTH; j++) {
1681558Srgrimes			col = (bold8x16.data[(ch * FONT_HEIGHT) + i] & mask) ? basecol : BLACK_COL;
1691558Srgrimes			*ptri++ = col;
1701558Srgrimes			mask >>= 1;
17141684Sbde		}
1721558Srgrimes		ptri += (SCREEN_WIDTH - FONT_WIDTH);
1731558Srgrimes	}
1741558Srgrimes
1751558Srgrimes	xcon_x += CHAR_WIDTH;
1761558Srgrimes	if (xcon_x >= SCREEN_WIDTH) {
1771558Srgrimes		xcon_x = 0;
1781558Srgrimes		xcon_y += CHAR_HEIGHT;
1791558Srgrimes	}
1801558Srgrimes
1811558Srgrimesscroll:
1821558Srgrimes	if (((xcon_yoffs + CHAR_HEIGHT) * SCREEN_WIDTH * SCREEN_BPP) > (FB_SIZE - SCREEN_SIZE)) {
1831558Srgrimes		/* we are about to run out of video memory, so move everything
1841558Srgrimes		 * back to the beginning of the video memory */
1851558Srgrimes		memcpy ((char*)xcon_map,
1861558Srgrimes		        (char*)(xcon_map + (xcon_yoffs * SCREEN_WIDTH * SCREEN_BPP)),
1871558Srgrimes		        SCREEN_SIZE);
1881558Srgrimes		xcon_y -= xcon_yoffs; xcon_yoffs = 0;
1891558Srgrimes		*xcon_memstartptr = FB_START;
1901558Srgrimes	}
1911558Srgrimes
1921558Srgrimes	/* we achieve much faster scrolling by just altering the video memory
1931558Srgrimes	 * address base. once all memory is used, we return to the beginning
1941558Srgrimes	 * again */
1951558Srgrimes	while ((xcon_y - xcon_yoffs) >= SCREEN_HEIGHT) {
1961558Srgrimes		xcon_yoffs += CHAR_HEIGHT;
1971558Srgrimes		memset ((char*)(xcon_map + (xcon_y * SCREEN_WIDTH * SCREEN_BPP)), 0, CHAR_HEIGHT * SCREEN_WIDTH * SCREEN_BPP);
1981558Srgrimes		*xcon_memstartptr = FB_START + (xcon_yoffs * SCREEN_WIDTH * SCREEN_BPP);
1991558Srgrimes	}
2001558Srgrimes}
2011558Srgrimes
2021558Srgrimesstatic void
2031558Srgrimesxcon_putc(struct consdev* cp, int c)
2041558Srgrimes{
2051558Srgrimes	xcon_real_putc (CONSOLE_COL, c);
2061558Srgrimes}
2071558Srgrimes
2081558Srgrimesstatic void
2091558Srgrimesxcon_init(struct consdev* cp)
21026737Scharnier{
21126737Scharnier	int i;
21226737Scharnier	int* iptr;
21326737Scharnier
2141558Srgrimes	/* Don't init the framebuffer on non-XBOX-es */
21528613Sjoerg	if (!arch_i386_is_xbox)
2161558Srgrimes		return;
2171558Srgrimes
2181558Srgrimes	/*
21938036Scharnier	 * We must make a mapping from video framebuffer memory to real. This is
2201558Srgrimes	 * very crude:  we map the entire videomemory to PAGE_SIZE! Since our
2211558Srgrimes	 * kernel lives at it's relocated address range (0xc0xxxxxx), it won't
2221558Srgrimes	 * care.
2231558Srgrimes	 *
2241558Srgrimes	 * We use address PAGE_SIZE and up so we can still trap NULL pointers.
2251558Srgrimes	 * Once xboxfb_drvinit() is called, the mapping will be done via the OS
2261558Srgrimes	 * and stored in a more sensible location ... but since we're not fully
2271558Srgrimes	 * initialized, this is our only way to go :-(
2281558Srgrimes	 */
2291558Srgrimes	for (i = 0; i < (FB_SIZE / PAGE_SIZE); i++) {
2301558Srgrimes		pmap_kenter (((i + 1) * PAGE_SIZE), FB_START + (i * PAGE_SIZE));
2311558Srgrimes	}
2321558Srgrimes	pmap_kenter ((i + 1) * PAGE_SIZE, FB_START_PTR - FB_START_PTR % PAGE_SIZE);
2331558Srgrimes	xcon_map = (char*)PAGE_SIZE;
2341558Srgrimes	xcon_memstartptr = (int*)((i + 1) * PAGE_SIZE + FB_START_PTR % PAGE_SIZE);
2351558Srgrimes
2361558Srgrimes	/* clear the screen */
2371558Srgrimes	iptr = (int*)xcon_map;
2381558Srgrimes	for (i = 0; i < SCREEN_HEIGHT * SCREEN_WIDTH; i++)
23948004Sru		*iptr++ = BLACK_COL;
2401558Srgrimes
2411558Srgrimes	sprintf(cp->cn_name, "xboxfb");
2421558Srgrimes	cp->cn_tp = xboxfb_tp;
2431558Srgrimes}
2441558Srgrimes
24532399Salexstatic void
24679749Sddxboxfb_timer(void* arg)
2471558Srgrimes{
2481558Srgrimes	int i;
2491558Srgrimes
2501558Srgrimes	if (xbfb_kbd != NULL)
2511558Srgrimes		return;
2521558Srgrimes
2531558Srgrimes	i = kbd_allocate ("*", 0, (void*)&xbfb_keyboard, xboxfb_kbdevent, NULL);
2541558Srgrimes	if (i != -1) {
2551558Srgrimes		/* allocation was successfull; xboxfb_kbdevent() is called to
2561558Srgrimes		 * feed the keystrokes to the tty driver */
2571558Srgrimes		xbfb_kbd = kbd_get_keyboard (i);
2581558Srgrimes		xbfb_keyboard = i;
2591558Srgrimes		return;
2601558Srgrimes	}
2611558Srgrimes
2621558Srgrimes	/* probe again in a few */
2631558Srgrimes	timeout (xboxfb_timer, NULL, hz / 10);
2641558Srgrimes}
2651558Srgrimes
26679749Sddstatic int
26732399Salexxboxfb_kbdevent(keyboard_t* thiskbd, int event, void* arg)
26832399Salex{
26932399Salex	int c;
27032399Salex
2711558Srgrimes	if (event == KBDIO_UNLOADING) {
272140797Sdelphij		/* keyboard was unplugged; clean up and enable probing */
2731558Srgrimes		xbfb_kbd = NULL;
2741558Srgrimes		xbfb_keyboard = -1;
2751558Srgrimes		kbd_release (thiskbd, (void*)&xbfb_keyboard);
2761558Srgrimes		timeout (xboxfb_timer, NULL, hz / 10);
2771558Srgrimes		return 0;
2781558Srgrimes	}
2791558Srgrimes
2801558Srgrimes	for (;;) {
2811558Srgrimes		c = (kbdsw[xbfb_kbd->kb_index])->read_char (xbfb_kbd, 0);
2821558Srgrimes		if (c == NOKEY)
2831558Srgrimes			return 0;
28432399Salex
2851558Srgrimes		/* only feed non-special keys to an open console */
2861558Srgrimes		if (c != ERRKEY) {
2871558Srgrimes			if ((KEYFLAGS(c)) == 0x0)
2881558Srgrimes				if (xboxfb_tp->t_state & TS_ISOPEN)
2891558Srgrimes					ttyld_rint (xboxfb_tp, KEYCHAR(c));
2901558Srgrimes		}
2911558Srgrimes	}
2921558Srgrimes
2931558Srgrimes	return 0;
2941558Srgrimes}
2951558Srgrimes
2961558Srgrimesstatic void
2971558Srgrimesxboxfb_drvinit (void* unused)
2981558Srgrimes{
2991558Srgrimes	struct cdev* dev;
3001558Srgrimes	int i;
3011558Srgrimes
3021558Srgrimes	/* Don't init the framebuffer on non-XBOX-es */
3031558Srgrimes	if (!arch_i386_is_xbox)
3041558Srgrimes		return;
3051558Srgrimes
3061558Srgrimes	/*
3071558Srgrimes	 * When this function is called, the OS is capable of doing
3081558Srgrimes	 * device-memory mappings using pmap_mapdev(). Therefore, we ditch the
3091558Srgrimes	 * ugly PAGE_SIZE-based mapping and ask the OS to create a decent
31038036Scharnier	 * mapping for us.
3111558Srgrimes	 */
3121558Srgrimes	dev = make_dev (&xboxfb_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "%s", "xboxfb");
3131558Srgrimes	xcon_map = pmap_mapdev (FB_START, FB_SIZE);
3141558Srgrimes	xcon_memstartptr = (int*)pmap_mapdev (FB_START_PTR, PAGE_SIZE);
3151558Srgrimes	*xcon_memstartptr = FB_START;
3161558Srgrimes
3171558Srgrimes	/* ditch all ugly previous mappings */
3181558Srgrimes	for (i = 0; i < (FB_SIZE / PAGE_SIZE); i++) {
3191558Srgrimes		pmap_kremove (((i + 1) * PAGE_SIZE));
3201558Srgrimes	}
3211558Srgrimes	pmap_kremove (PAGE_SIZE + FB_SIZE);
322140797Sdelphij
3231558Srgrimes	/* probe for a keyboard */
3241558Srgrimes	xboxfb_timer (NULL);
3251558Srgrimes}
3261558Srgrimes
3271558Srgrimesstatic void
3281558Srgrimesxboxfb_tty_start(struct tty* tp)
3291558Srgrimes{
33032399Salex	struct clist* cl;
3311558Srgrimes	int len, i;
3321558Srgrimes	u_char buf[128];
33341666Smsmith
33441666Smsmith	if (tp->t_state & TS_BUSY)
3351558Srgrimes		return;
3361558Srgrimes
3371558Srgrimes	/* simply feed all outstanding tty data to real_putc() */
3381558Srgrimes	tp->t_state |= TS_BUSY;
3391558Srgrimes	cl = &tp->t_outq;
3404844Sats	len = q_to_b(cl, buf, 128);
3411558Srgrimes	for (i = 0; i < len; i++)
3421558Srgrimes		xcon_real_putc(NORM_COL, buf[i]);
3431558Srgrimes	tp->t_state &= ~TS_BUSY;
3441558Srgrimes}
3451558Srgrimes
3461558Srgrimesstatic void
34741666Smsmithxboxfb_tty_stop(struct tty* tp, int flag) {
34841666Smsmith	if (tp->t_state & TS_BUSY)
34948078Sru		if ((tp->t_state & TS_TTSTOP) == 0)
3501558Srgrimes			tp->t_state |= TS_FLUSH;
3511558Srgrimes}
3521558Srgrimes
35348078Srustatic int
35448078Sruxboxfb_tty_param(struct tty* tp, struct termios* t)
35548078Sru{
35648078Sru	return 0;
35748078Sru}
35848078Sru
35948078Srustatic int
36048078Sruxboxfb_dev_open(struct cdev* dev, int flag, int mode, struct thread* td)
36148078Sru{
36248078Sru	struct tty* tp;
36348078Sru
36448078Sru	tp = xboxfb_tp = dev->si_tty = ttymalloc (xboxfb_tp);
36548078Sru
36648078Sru	tp->t_oproc = xboxfb_tty_start;
36748078Sru	tp->t_param = xboxfb_tty_param;
36848078Sru	tp->t_stop = xboxfb_tty_stop;
36948078Sru	tp->t_dev = dev;
37048078Sru
37148078Sru	if ((tp->t_state & TS_ISOPEN) == 0) {
37248078Sru		tp->t_state |= TS_CARR_ON;
37348078Sru		ttychars(tp);
37448078Sru		tp->t_iflag = TTYDEF_IFLAG;
37548078Sru		tp->t_oflag = TTYDEF_OFLAG;
37648078Sru		tp->t_cflag = TTYDEF_CFLAG | CLOCAL;
37748078Sru		tp->t_lflag = TTYDEF_LFLAG;
37848078Sru		tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
37948078Sru		ttsetwater(tp);
38048078Sru	}
3811558Srgrimes
3821558Srgrimes	return ttyld_open (tp, dev);
3831558Srgrimes}
3841558Srgrimes
3851558Srgrimesstatic int
3861558Srgrimesxboxfb_dev_close(struct cdev* dev, int flag, int mode, struct thread* td)
3871558Srgrimes{
3881558Srgrimes	struct tty* tp;
389140797Sdelphij
3901558Srgrimes	tp = xboxfb_tp;
39179749Sdd	ttyld_close (tp, flag);
39279749Sdd	tty_close(tp);
3931558Srgrimes	return 0;
39432328Salex}
3951558Srgrimes
39648062SjkoshySYSINIT(xboxfbdev, SI_SUB_CONFIGURE, SI_ORDER_MIDDLE, xboxfb_drvinit, NULL)
39748062Sjkoshy
3981558SrgrimesCONS_DRIVER(xcon, xcon_probe, xcon_init, NULL, xcon_getc, xcon_checkc, xcon_putc, NULL);
3991558Srgrimes