xboxfb.c revision 152219
1/*-
2 * Copyright (c) 2005 Rink Springer
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 *    derived from this software without specific prior written permission
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 *
27 * $FreeBSD: head/sys/i386/xbox/xboxfb.c 152219 2005-11-09 03:55:40Z imp $
28 */
29
30/*
31 * This will handles video output using the XBOX' frame buffer. It assumes
32 * the graphics have been set up by Cromwell. This driver uses all video memory
33 * to avoid expensive memcpy()'s.
34 *
35 * It is usuable as console (to see the initial boot) as well as for interactive
36 * use. The latter is handeled using kbd_*() functionality. Keyboard hotplug is
37 * fully supported, the console will periodically rescan if no keyboard was
38 * found.
39 *
40 */
41#include <sys/param.h>
42#include <sys/systm.h>
43#include <vm/vm_param.h>
44#include <sys/kernel.h>
45#include <sys/cons.h>
46#include <sys/conf.h>
47#include <sys/consio.h>
48#include <sys/tty.h>
49#include <sys/kbio.h>
50#include <sys/fbio.h>
51#include <dev/kbd/kbdreg.h>
52#include <vm/vm.h>
53#include <vm/pmap.h>
54#include <machine/bus.h>
55#include <machine/xbox.h>
56#include <dev/fb/fbreg.h>
57#include <dev/fb/gfb.h>
58
59#define SCREEN_WIDTH	640
60#define SCREEN_HEIGHT	480
61#define SCREEN_BPP	4
62#define SCREEN_SIZE	(SCREEN_WIDTH*SCREEN_HEIGHT*SCREEN_BPP)
63
64/* FONT_xxx declares the dimensions of the charachter structure, CHAR_xxx is how
65 * they appear on-screen. Having slightly more spacing improves readability.  */
66#define FONT_HEIGHT	16
67#define FONT_WIDTH	8
68
69#define CHAR_HEIGHT	16
70#define CHAR_WIDTH	10
71
72#define RAM_SIZE	(arch_i386_xbox_memsize * 1024 * 1024)
73#define FB_SIZE		(0x400000)
74#define FB_START	(0xf0000000 | (RAM_SIZE - FB_SIZE))
75#define FB_START_PTR	(0xFD600800)
76
77/* colours */
78#define CONSOLE_COL	0xFF88FF88	/* greenish */
79#define NORM_COL	0xFFAAAAAA	/* grayish */
80#define BLACK_COL	0x00000000	/* black */
81
82static int xcon_x     = 0;
83static int xcon_y     = 0;
84static int xcon_yoffs = 0;
85
86extern struct gfb_font bold8x16;
87
88static char* xcon_map;
89static int* xcon_memstartptr;
90
91static struct tty* xboxfb_tp = NULL;
92static struct keyboard* xbfb_kbd = NULL;
93static int xbfb_keyboard = -1;
94static d_open_t xboxfb_dev_open;
95static d_close_t xboxfb_dev_close;
96static int xboxfb_kbdevent(keyboard_t* thiskbd, int event, void* arg);
97
98static struct cdevsw xboxfb_cdevsw = {
99	.d_version = D_VERSION,
100	.d_open    = xboxfb_dev_open,
101	.d_close   = xboxfb_dev_close,
102	.d_name    = "xboxfb",
103	.d_flags   = D_TTY | D_NEEDGIANT,
104};
105
106static void
107xcon_probe(struct consdev* cp)
108{
109	if (arch_i386_is_xbox)
110		cp->cn_pri = CN_REMOTE;
111	else
112		cp->cn_pri = CN_DEAD;
113}
114
115static int
116xcon_getc(struct consdev* cp)
117{
118	return 0;
119}
120
121static int
122xcon_checkc(struct consdev* cp)
123{
124	return 0;
125}
126
127static void
128xcon_real_putc(int basecol, int c)
129{
130	int i, j, ch = c, col;
131	char mask;
132	int* ptri = (int*)xcon_map;
133
134	/* special control chars */
135	switch (ch) {
136	case '\r': /* carriage return */
137		xcon_x = 0;
138		return;
139	case '\n': /* newline */
140		xcon_y += CHAR_HEIGHT;
141		goto scroll;
142	case 7: /* beep */
143		return;
144	case 8: /* backspace */
145		if (xcon_x > 0) {
146			xcon_x -= CHAR_WIDTH;
147		} else {
148			if (xcon_y > CHAR_HEIGHT) {
149				xcon_y -= CHAR_HEIGHT;
150				xcon_x = (SCREEN_WIDTH - CHAR_WIDTH);
151			}
152		}
153		return;
154	case 9: /* tab */
155		xcon_real_putc (basecol, ' ');
156		while ((xcon_x % (8 * CHAR_WIDTH)) != 0) {
157			xcon_real_putc (basecol, ' ');
158		}
159		return;
160	}
161	ptri += (xcon_y * SCREEN_WIDTH) + xcon_x;
162
163	/* we plot the font pixel-by-pixel. bit 7 is skipped as it renders the
164	 * console unreadable ... */
165	for (i = 0; i < FONT_HEIGHT; i++) {
166		mask = 0x40;
167		for (j = 0; j < FONT_WIDTH; j++) {
168			col = (bold8x16.data[(ch * FONT_HEIGHT) + i] & mask) ? basecol : BLACK_COL;
169			*ptri++ = col;
170			mask >>= 1;
171		}
172		ptri += (SCREEN_WIDTH - FONT_WIDTH);
173	}
174
175	xcon_x += CHAR_WIDTH;
176	if (xcon_x >= SCREEN_WIDTH) {
177		xcon_x = 0;
178		xcon_y += CHAR_HEIGHT;
179	}
180
181scroll:
182	if (((xcon_yoffs + CHAR_HEIGHT) * SCREEN_WIDTH * SCREEN_BPP) > (FB_SIZE - SCREEN_SIZE)) {
183		/* we are about to run out of video memory, so move everything
184		 * back to the beginning of the video memory */
185		memcpy ((char*)xcon_map,
186		        (char*)(xcon_map + (xcon_yoffs * SCREEN_WIDTH * SCREEN_BPP)),
187		        SCREEN_SIZE);
188		xcon_y -= xcon_yoffs; xcon_yoffs = 0;
189		*xcon_memstartptr = FB_START;
190	}
191
192	/* we achieve much faster scrolling by just altering the video memory
193	 * address base. once all memory is used, we return to the beginning
194	 * again */
195	while ((xcon_y - xcon_yoffs) >= SCREEN_HEIGHT) {
196		xcon_yoffs += CHAR_HEIGHT;
197		memset ((char*)(xcon_map + (xcon_y * SCREEN_WIDTH * SCREEN_BPP)), 0, CHAR_HEIGHT * SCREEN_WIDTH * SCREEN_BPP);
198		*xcon_memstartptr = FB_START + (xcon_yoffs * SCREEN_WIDTH * SCREEN_BPP);
199	}
200}
201
202static void
203xcon_putc(struct consdev* cp, int c)
204{
205	xcon_real_putc (CONSOLE_COL, c);
206}
207
208static void
209xcon_init(struct consdev* cp)
210{
211	int i;
212	int* iptr;
213
214	/* Don't init the framebuffer on non-XBOX-es */
215	if (!arch_i386_is_xbox)
216		return;
217
218	/*
219	 * We must make a mapping from video framebuffer memory to real. This is
220	 * very crude:  we map the entire videomemory to PAGE_SIZE! Since our
221	 * kernel lives at it's relocated address range (0xc0xxxxxx), it won't
222	 * care.
223	 *
224	 * We use address PAGE_SIZE and up so we can still trap NULL pointers.
225	 * Once xboxfb_drvinit() is called, the mapping will be done via the OS
226	 * and stored in a more sensible location ... but since we're not fully
227	 * initialized, this is our only way to go :-(
228	 */
229	for (i = 0; i < (FB_SIZE / PAGE_SIZE); i++) {
230		pmap_kenter (((i + 1) * PAGE_SIZE), FB_START + (i * PAGE_SIZE));
231	}
232	pmap_kenter ((i + 1) * PAGE_SIZE, FB_START_PTR - FB_START_PTR % PAGE_SIZE);
233	xcon_map = (char*)PAGE_SIZE;
234	xcon_memstartptr = (int*)((i + 1) * PAGE_SIZE + FB_START_PTR % PAGE_SIZE);
235
236	/* clear the screen */
237	iptr = (int*)xcon_map;
238	for (i = 0; i < SCREEN_HEIGHT * SCREEN_WIDTH; i++)
239		*iptr++ = BLACK_COL;
240
241	sprintf(cp->cn_name, "xboxfb");
242	cp->cn_tp = xboxfb_tp;
243}
244
245static void
246xboxfb_timer(void* arg)
247{
248	int i;
249
250	if (xbfb_kbd != NULL)
251		return;
252
253	i = kbd_allocate ("*", 0, (void*)&xbfb_keyboard, xboxfb_kbdevent, NULL);
254	if (i != -1) {
255		/* allocation was successfull; xboxfb_kbdevent() is called to
256		 * feed the keystrokes to the tty driver */
257		xbfb_kbd = kbd_get_keyboard (i);
258		xbfb_keyboard = i;
259		return;
260	}
261
262	/* probe again in a few */
263	timeout (xboxfb_timer, NULL, hz / 10);
264}
265
266static int
267xboxfb_kbdevent(keyboard_t* thiskbd, int event, void* arg)
268{
269	int c;
270
271	if (event == KBDIO_UNLOADING) {
272		/* keyboard was unplugged; clean up and enable probing */
273		xbfb_kbd = NULL;
274		xbfb_keyboard = -1;
275		kbd_release (thiskbd, (void*)&xbfb_keyboard);
276		timeout (xboxfb_timer, NULL, hz / 10);
277		return 0;
278	}
279
280	for (;;) {
281		c = (kbdsw[xbfb_kbd->kb_index])->read_char (xbfb_kbd, 0);
282		if (c == NOKEY)
283			return 0;
284
285		/* only feed non-special keys to an open console */
286		if (c != ERRKEY) {
287			if ((KEYFLAGS(c)) == 0x0)
288				if (xboxfb_tp->t_state & TS_ISOPEN)
289					ttyld_rint (xboxfb_tp, KEYCHAR(c));
290		}
291	}
292
293	return 0;
294}
295
296static void
297xboxfb_drvinit (void* unused)
298{
299	struct cdev* dev;
300	int i;
301
302	/* Don't init the framebuffer on non-XBOX-es */
303	if (!arch_i386_is_xbox)
304		return;
305
306	/*
307	 * When this function is called, the OS is capable of doing
308	 * device-memory mappings using pmap_mapdev(). Therefore, we ditch the
309	 * ugly PAGE_SIZE-based mapping and ask the OS to create a decent
310	 * mapping for us.
311	 */
312	dev = make_dev (&xboxfb_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "%s", "xboxfb");
313	xcon_map = pmap_mapdev (FB_START, FB_SIZE);
314	xcon_memstartptr = (int*)pmap_mapdev (FB_START_PTR, PAGE_SIZE);
315	*xcon_memstartptr = FB_START;
316
317	/* ditch all ugly previous mappings */
318	for (i = 0; i < (FB_SIZE / PAGE_SIZE); i++) {
319		pmap_kremove (((i + 1) * PAGE_SIZE));
320	}
321	pmap_kremove (PAGE_SIZE + FB_SIZE);
322
323	/* probe for a keyboard */
324	xboxfb_timer (NULL);
325}
326
327static void
328xboxfb_tty_start(struct tty* tp)
329{
330	struct clist* cl;
331	int len, i;
332	u_char buf[128];
333
334	if (tp->t_state & TS_BUSY)
335		return;
336
337	/* simply feed all outstanding tty data to real_putc() */
338	tp->t_state |= TS_BUSY;
339	cl = &tp->t_outq;
340	len = q_to_b(cl, buf, 128);
341	for (i = 0; i < len; i++)
342		xcon_real_putc(NORM_COL, buf[i]);
343	tp->t_state &= ~TS_BUSY;
344}
345
346static void
347xboxfb_tty_stop(struct tty* tp, int flag) {
348	if (tp->t_state & TS_BUSY)
349		if ((tp->t_state & TS_TTSTOP) == 0)
350			tp->t_state |= TS_FLUSH;
351}
352
353static int
354xboxfb_tty_param(struct tty* tp, struct termios* t)
355{
356	return 0;
357}
358
359static int
360xboxfb_dev_open(struct cdev* dev, int flag, int mode, struct thread* td)
361{
362	struct tty* tp;
363
364	tp = xboxfb_tp = dev->si_tty = ttymalloc (xboxfb_tp);
365
366	tp->t_oproc = xboxfb_tty_start;
367	tp->t_param = xboxfb_tty_param;
368	tp->t_stop = xboxfb_tty_stop;
369	tp->t_dev = dev;
370
371	if ((tp->t_state & TS_ISOPEN) == 0) {
372		tp->t_state |= TS_CARR_ON;
373		ttychars(tp);
374		tp->t_iflag = TTYDEF_IFLAG;
375		tp->t_oflag = TTYDEF_OFLAG;
376		tp->t_cflag = TTYDEF_CFLAG | CLOCAL;
377		tp->t_lflag = TTYDEF_LFLAG;
378		tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
379		ttsetwater(tp);
380	}
381
382	return ttyld_open (tp, dev);
383}
384
385static int
386xboxfb_dev_close(struct cdev* dev, int flag, int mode, struct thread* td)
387{
388	struct tty* tp;
389
390	tp = xboxfb_tp;
391	ttyld_close (tp, flag);
392	tty_close(tp);
393	return 0;
394}
395
396SYSINIT(xboxfbdev, SI_SUB_CONFIGURE, SI_ORDER_MIDDLE, xboxfb_drvinit, NULL)
397
398CONS_DRIVER(xcon, xcon_probe, xcon_init, NULL, xcon_getc, xcon_checkc, xcon_putc, NULL);
399