xboxfb.c revision 155510
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 155510 2006-02-10 18:48:22Z rink $
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/* colours */
73#define CONSOLE_COL	0xFF88FF88	/* greenish */
74#define NORM_COL	0xFFAAAAAA	/* grayish */
75#define BLACK_COL	0x00000000	/* black */
76
77static int xcon_x     = 0;
78static int xcon_y     = 0;
79static int xcon_yoffs = 0;
80
81extern struct gfb_font bold8x16;
82
83static char* xcon_map;
84static int* xcon_memstartptr;
85
86static struct tty* xboxfb_tp = NULL;
87static struct keyboard* xbfb_kbd = NULL;
88static int xbfb_keyboard = -1;
89static d_open_t xboxfb_dev_open;
90static d_close_t xboxfb_dev_close;
91static int xboxfb_kbdevent(keyboard_t* thiskbd, int event, void* arg);
92
93static struct cdevsw xboxfb_cdevsw = {
94	.d_version = D_VERSION,
95	.d_open    = xboxfb_dev_open,
96	.d_close   = xboxfb_dev_close,
97	.d_name    = "xboxfb",
98	.d_flags   = D_TTY | D_NEEDGIANT,
99};
100
101static void
102xcon_probe(struct consdev* cp)
103{
104	if (arch_i386_is_xbox)
105		cp->cn_pri = CN_REMOTE;
106	else
107		cp->cn_pri = CN_DEAD;
108}
109
110static int
111xcon_getc(struct consdev* cp)
112{
113	return 0;
114}
115
116static int
117xcon_checkc(struct consdev* cp)
118{
119	return 0;
120}
121
122static void
123xcon_real_putc(int basecol, int c)
124{
125	int i, j, ch = c, col;
126	char mask;
127	int* ptri = (int*)xcon_map;
128
129	/* special control chars */
130	switch (ch) {
131	case '\r': /* carriage return */
132		xcon_x = 0;
133		return;
134	case '\n': /* newline */
135		xcon_y += CHAR_HEIGHT;
136		goto scroll;
137	case 7: /* beep */
138		return;
139	case 8: /* backspace */
140		if (xcon_x > 0) {
141			xcon_x -= CHAR_WIDTH;
142		} else {
143			if (xcon_y > CHAR_HEIGHT) {
144				xcon_y -= CHAR_HEIGHT;
145				xcon_x = (SCREEN_WIDTH - CHAR_WIDTH);
146			}
147		}
148		return;
149	case 9: /* tab */
150		xcon_real_putc (basecol, ' ');
151		while ((xcon_x % (8 * CHAR_WIDTH)) != 0) {
152			xcon_real_putc (basecol, ' ');
153		}
154		return;
155	}
156	ptri += (xcon_y * SCREEN_WIDTH) + xcon_x;
157
158	/* we plot the font pixel-by-pixel. bit 7 is skipped as it renders the
159	 * console unreadable ... */
160	for (i = 0; i < FONT_HEIGHT; i++) {
161		mask = 0x40;
162		for (j = 0; j < FONT_WIDTH; j++) {
163			col = (bold8x16.data[(ch * FONT_HEIGHT) + i] & mask) ? basecol : BLACK_COL;
164			*ptri++ = col;
165			mask >>= 1;
166		}
167		ptri += (SCREEN_WIDTH - FONT_WIDTH);
168	}
169
170	xcon_x += CHAR_WIDTH;
171	if (xcon_x >= SCREEN_WIDTH) {
172		xcon_x = 0;
173		xcon_y += CHAR_HEIGHT;
174	}
175
176scroll:
177	if (((xcon_yoffs + CHAR_HEIGHT) * SCREEN_WIDTH * SCREEN_BPP) > (XBOX_FB_SIZE - SCREEN_SIZE)) {
178		/* we are about to run out of video memory, so move everything
179		 * back to the beginning of the video memory */
180		memcpy ((char*)xcon_map,
181		        (char*)(xcon_map + (xcon_yoffs * SCREEN_WIDTH * SCREEN_BPP)),
182		        SCREEN_SIZE);
183		xcon_y -= xcon_yoffs; xcon_yoffs = 0;
184		*xcon_memstartptr = XBOX_FB_START;
185	}
186
187	/* we achieve much faster scrolling by just altering the video memory
188	 * address base. once all memory is used, we return to the beginning
189	 * again */
190	while ((xcon_y - xcon_yoffs) >= SCREEN_HEIGHT) {
191		xcon_yoffs += CHAR_HEIGHT;
192		memset ((char*)(xcon_map + (xcon_y * SCREEN_WIDTH * SCREEN_BPP)), 0, CHAR_HEIGHT * SCREEN_WIDTH * SCREEN_BPP);
193		*xcon_memstartptr = XBOX_FB_START + (xcon_yoffs * SCREEN_WIDTH * SCREEN_BPP);
194	}
195}
196
197static void
198xcon_putc(struct consdev* cp, int c)
199{
200	xcon_real_putc (CONSOLE_COL, c);
201}
202
203static void
204xcon_init(struct consdev* cp)
205{
206	int i;
207	int* iptr;
208
209	/* Don't init the framebuffer on non-XBOX-es */
210	if (!arch_i386_is_xbox)
211		return;
212
213	/*
214	 * We must make a mapping from video framebuffer memory to real. This is
215	 * very crude:  we map the entire videomemory to PAGE_SIZE! Since our
216	 * kernel lives at it's relocated address range (0xc0xxxxxx), it won't
217	 * care.
218	 *
219	 * We use address PAGE_SIZE and up so we can still trap NULL pointers.
220	 * Once xboxfb_drvinit() is called, the mapping will be done via the OS
221	 * and stored in a more sensible location ... but since we're not fully
222	 * initialized, this is our only way to go :-(
223	 */
224	for (i = 0; i < (XBOX_FB_SIZE / PAGE_SIZE); i++) {
225		pmap_kenter (((i + 1) * PAGE_SIZE), XBOX_FB_START + (i * PAGE_SIZE));
226	}
227	pmap_kenter ((i + 1) * PAGE_SIZE, XBOX_FB_START_PTR - XBOX_FB_START_PTR % PAGE_SIZE);
228	xcon_map = (char*)PAGE_SIZE;
229	xcon_memstartptr = (int*)((i + 1) * PAGE_SIZE + XBOX_FB_START_PTR % PAGE_SIZE);
230
231	/* clear the screen */
232	iptr = (int*)xcon_map;
233	for (i = 0; i < SCREEN_HEIGHT * SCREEN_WIDTH; i++)
234		*iptr++ = BLACK_COL;
235
236	sprintf(cp->cn_name, "xboxfb");
237	cp->cn_tp = xboxfb_tp;
238}
239
240static void
241xboxfb_timer(void* arg)
242{
243	int i;
244
245	if (xbfb_kbd != NULL)
246		return;
247
248	i = kbd_allocate ("*", 0, (void*)&xbfb_keyboard, xboxfb_kbdevent, NULL);
249	if (i != -1) {
250		/* allocation was successfull; xboxfb_kbdevent() is called to
251		 * feed the keystrokes to the tty driver */
252		xbfb_kbd = kbd_get_keyboard (i);
253		xbfb_keyboard = i;
254		return;
255	}
256
257	/* probe again in a few */
258	timeout (xboxfb_timer, NULL, hz / 10);
259}
260
261static int
262xboxfb_kbdevent(keyboard_t* thiskbd, int event, void* arg)
263{
264	int c;
265
266	if (event == KBDIO_UNLOADING) {
267		/* keyboard was unplugged; clean up and enable probing */
268		xbfb_kbd = NULL;
269		xbfb_keyboard = -1;
270		kbd_release (thiskbd, (void*)&xbfb_keyboard);
271		timeout (xboxfb_timer, NULL, hz / 10);
272		return 0;
273	}
274
275	for (;;) {
276		c = (kbdsw[xbfb_kbd->kb_index])->read_char (xbfb_kbd, 0);
277		if (c == NOKEY)
278			return 0;
279
280		/* only feed non-special keys to an open console */
281		if (c != ERRKEY) {
282			if ((KEYFLAGS(c)) == 0x0)
283				if (xboxfb_tp->t_state & TS_ISOPEN)
284					ttyld_rint (xboxfb_tp, KEYCHAR(c));
285		}
286	}
287
288	return 0;
289}
290
291static void
292xboxfb_drvinit (void* unused)
293{
294	struct cdev* dev;
295	int i;
296
297	/* Don't init the framebuffer on non-XBOX-es */
298	if (!arch_i386_is_xbox)
299		return;
300
301	/*
302	 * When this function is called, the OS is capable of doing
303	 * device-memory mappings using pmap_mapdev(). Therefore, we ditch the
304	 * ugly PAGE_SIZE-based mapping and ask the OS to create a decent
305	 * mapping for us.
306	 */
307	dev = make_dev (&xboxfb_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "%s", "xboxfb");
308	xcon_map = pmap_mapdev (XBOX_FB_START, XBOX_FB_SIZE);
309	xcon_memstartptr = (int*)pmap_mapdev (XBOX_FB_START_PTR, PAGE_SIZE);
310	*xcon_memstartptr = XBOX_FB_START;
311
312	/* ditch all ugly previous mappings */
313	for (i = 0; i < (XBOX_FB_SIZE / PAGE_SIZE); i++) {
314		pmap_kremove (((i + 1) * PAGE_SIZE));
315	}
316	pmap_kremove (PAGE_SIZE + XBOX_FB_SIZE);
317
318	/* probe for a keyboard */
319	xboxfb_timer (NULL);
320	xboxfb_tp = ttyalloc();
321}
322
323static void
324xboxfb_tty_start(struct tty* tp)
325{
326	struct clist* cl;
327	int len, i;
328	u_char buf[128];
329
330	if (tp->t_state & TS_BUSY)
331		return;
332
333	/* simply feed all outstanding tty data to real_putc() */
334	tp->t_state |= TS_BUSY;
335	cl = &tp->t_outq;
336	len = q_to_b(cl, buf, 128);
337	for (i = 0; i < len; i++)
338		xcon_real_putc(NORM_COL, buf[i]);
339	tp->t_state &= ~TS_BUSY;
340}
341
342static void
343xboxfb_tty_stop(struct tty* tp, int flag) {
344	if (tp->t_state & TS_BUSY)
345		if ((tp->t_state & TS_TTSTOP) == 0)
346			tp->t_state |= TS_FLUSH;
347}
348
349static int
350xboxfb_tty_param(struct tty* tp, struct termios* t)
351{
352	return 0;
353}
354
355static int
356xboxfb_dev_open(struct cdev* dev, int flag, int mode, struct thread* td)
357{
358	struct tty* tp;
359
360	tp = dev->si_tty = xboxfb_tp;
361
362	tp->t_oproc = xboxfb_tty_start;
363	tp->t_param = xboxfb_tty_param;
364	tp->t_stop = xboxfb_tty_stop;
365	tp->t_dev = dev;
366
367	if ((tp->t_state & TS_ISOPEN) == 0) {
368		tp->t_state |= TS_CARR_ON;
369		ttychars(tp);
370		tp->t_iflag = TTYDEF_IFLAG;
371		tp->t_oflag = TTYDEF_OFLAG;
372		tp->t_cflag = TTYDEF_CFLAG | CLOCAL;
373		tp->t_lflag = TTYDEF_LFLAG;
374		tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
375		ttsetwater(tp);
376	}
377
378	return ttyld_open (tp, dev);
379}
380
381static int
382xboxfb_dev_close(struct cdev* dev, int flag, int mode, struct thread* td)
383{
384	struct tty* tp;
385
386	tp = xboxfb_tp;
387	ttyld_close (tp, flag);
388	tty_close(tp);
389	return 0;
390}
391
392SYSINIT(xboxfbdev, SI_SUB_CONFIGURE, SI_ORDER_MIDDLE, xboxfb_drvinit, NULL)
393
394CONS_DRIVER(xcon, xcon_probe, xcon_init, NULL, xcon_getc, xcon_checkc, xcon_putc, NULL);
395