1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 1999 Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer as
12 *    the first lines of this file unmodified.
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 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * Copyright (c) 2000 Andrew Miklic
29 */
30
31#include <sys/cdefs.h>
32__FBSDID("$FreeBSD$");
33
34#include "opt_syscons.h"
35#include "opt_gfb.h"
36#ifdef __powerpc__
37#include "opt_ofwfb.h"
38#endif
39
40#include <sys/param.h>
41#include <sys/systm.h>
42#include <sys/kernel.h>
43#include <sys/fbio.h>
44#include <sys/consio.h>
45
46#include <machine/bus.h>
47
48#include <dev/fb/fbreg.h>
49#include <dev/syscons/syscons.h>
50
51#ifndef SC_RENDER_DEBUG
52#define SC_RENDER_DEBUG		0
53#endif
54
55static vr_clear_t		gfb_clear;
56static vr_draw_border_t		gfb_border;
57static vr_draw_t		gfb_draw;
58static vr_set_cursor_t		gfb_cursor_shape;
59static vr_draw_cursor_t		gfb_cursor;
60static vr_blink_cursor_t	gfb_blink;
61#ifndef SC_NO_CUTPASTE
62static vr_draw_mouse_t		gfb_mouse;
63#else
64#define gfb_mouse		(vr_draw_mouse_t *)gfb_nop
65#endif
66
67static void			gfb_nop(scr_stat *scp);
68
69sc_rndr_sw_t txtrndrsw = {
70	(vr_init_t *)gfb_nop,
71	gfb_clear,
72	gfb_border,
73	gfb_draw,
74	gfb_cursor_shape,
75	gfb_cursor,
76	gfb_blink,
77	(vr_set_mouse_t *)gfb_nop,
78	gfb_mouse,
79};
80
81#ifdef SC_PIXEL_MODE
82sc_rndr_sw_t gfbrndrsw = {
83	(vr_init_t *)gfb_nop,
84	gfb_clear,
85	gfb_border,
86	gfb_draw,
87	gfb_cursor_shape,
88	gfb_cursor,
89	gfb_blink,
90	(vr_set_mouse_t *)gfb_nop,
91	gfb_mouse,
92};
93#endif /* SC_PIXEL_MODE */
94
95#ifndef SC_NO_MODE_CHANGE
96sc_rndr_sw_t grrndrsw = {
97	(vr_init_t *)gfb_nop,
98	(vr_clear_t *)gfb_nop,
99	gfb_border,
100	(vr_draw_t *)gfb_nop,
101	(vr_set_cursor_t *)gfb_nop,
102	(vr_draw_cursor_t *)gfb_nop,
103	(vr_blink_cursor_t *)gfb_nop,
104	(vr_set_mouse_t *)gfb_nop,
105	(vr_draw_mouse_t *)gfb_nop,
106};
107#endif /* SC_NO_MODE_CHANGE */
108
109#ifndef SC_NO_CUTPASTE
110#ifdef __sparc64__
111static u_char mouse_pointer[22 * 2] = {
112	0x00, 0x00,	/* ............ */
113	0x80, 0x00,	/* *........... */
114	0xc0, 0x00,	/* **.......... */
115	0xe0, 0x00,	/* ***......... */
116	0xf0, 0x00,	/* ****........ */
117	0xf8, 0x00,	/* *****....... */
118	0xfc, 0x00,	/* ******...... */
119	0xfe, 0x00,	/* *******..... */
120	0xff, 0x00,	/* ********.... */
121	0xff, 0x80,	/* *********... */
122	0xfc, 0xc0,	/* ******..**.. */
123	0xdc, 0x00,	/* **.***...... */
124	0x8e, 0x00,	/* *...***..... */
125	0x0e, 0x00,	/* ....***..... */
126	0x07, 0x00,	/* .....***.... */
127	0x04, 0x00,	/* .....*...... */
128	0x00, 0x00,	/* ............ */
129	0x00, 0x00,	/* ............ */
130	0x00, 0x00,	/* ............ */
131	0x00, 0x00,	/* ............ */
132	0x00, 0x00,	/* ............ */
133	0x00, 0x00	/* ............ */
134};
135#else
136static u_char mouse_pointer[16] = {
137	0x00, 0x40, 0x60, 0x70, 0x78, 0x7c, 0x7e, 0x68,
138	0x0c, 0x0c, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00
139};
140#endif
141#endif
142
143static void
144gfb_nop(scr_stat *scp)
145{
146}
147
148/* text mode renderer */
149
150static void
151gfb_clear(scr_stat *scp, int c, int attr)
152{
153	vidd_clear(scp->sc->adp);
154}
155
156static void
157gfb_border(scr_stat *scp, int color)
158{
159	vidd_set_border(scp->sc->adp, color);
160}
161
162static void
163gfb_draw(scr_stat *scp, int from, int count, int flip)
164{
165	int c;
166	int a;
167	int i, n;
168	video_adapter_t *adp;
169
170	adp = scp->sc->adp;
171
172	/*
173	   Determine if we need to scroll based on the offset
174	   and the number of characters to be displayed...
175	 */
176	if (from + count > scp->xsize*scp->ysize) {
177
178		/*
179		   Calculate the number of characters past the end of the
180		   visible screen...
181		*/
182		count = (from + count) -
183		    (adp->va_info.vi_width * adp->va_info.vi_height);
184
185		/*
186		   Calculate the number of rows past the end of the visible
187		   screen...
188		*/
189		n = (count / adp->va_info.vi_width) + 1;
190
191		/* Scroll to make room for new text rows... */
192		vidd_copy(adp, n, 0, n);
193#if 0
194		vidd_clear(adp, n);
195#endif
196
197		/* Display new text rows... */
198		vidd_puts(adp, from,
199		    (u_int16_t *)sc_vtb_pointer(&scp->vtb, from), count);
200	}
201
202	/*
203	   We don't need to scroll, so we can just put the characters
204	   all-at-once...
205	*/
206	else {
207
208		/*
209		   Determine the method by which we are to display characters
210		   (are we going to print forwards or backwards?
211		   do we need to do a character-by-character copy, then?)...
212		*/
213		if (flip)
214			for (i = count; i-- > 0; ++from) {
215				c = sc_vtb_getc(&scp->vtb, from);
216				a = sc_vtb_geta(&scp->vtb, from) >> 8;
217				vidd_putc(adp, from, c,
218				    (a >> 4) | ((a & 0xf) << 4));
219			}
220		else {
221			vidd_puts(adp, from,
222			    (u_int16_t *)sc_vtb_pointer(&scp->vtb, from),
223			    count);
224		}
225	}
226}
227
228static void
229gfb_cursor_shape(scr_stat *scp, int base, int height, int blink)
230{
231	if (base < 0 || base >= scp->font_size)
232		return;
233	/* the caller may set height <= 0 in order to disable the cursor */
234#if 0
235	scp->cursor_base = base;
236	scp->cursor_height = height;
237#endif
238	vidd_set_hw_cursor_shape(scp->sc->adp, base, height, scp->font_size,
239	    blink);
240}
241
242static int pxlblinkrate = 0;
243
244#if defined(__sparc64__) || defined(SC_OFWFB)
245static void
246gfb_cursor(scr_stat *scp, int at, int blink, int on, int flip)
247{
248	video_adapter_t *adp;
249	int a, c;
250
251	if (scp->curs_attr.height <= 0)	/* the text cursor is disabled */
252		return;
253
254	adp = scp->sc->adp;
255	if(blink) {
256		scp->status |= VR_CURSOR_BLINK;
257		if (on) {
258			scp->status |= VR_CURSOR_ON;
259			vidd_set_hw_cursor(adp, at%scp->xsize, at/scp->xsize);
260		} else {
261			if (scp->status & VR_CURSOR_ON)
262				vidd_set_hw_cursor(adp, -1, -1);
263			scp->status &= ~VR_CURSOR_ON;
264		}
265	} else {
266		scp->status &= ~VR_CURSOR_BLINK;
267		if(on) {
268			scp->status |= VR_CURSOR_ON;
269			vidd_putc(scp->sc->adp, scp->cursor_oldpos,
270			    sc_vtb_getc(&scp->vtb, scp->cursor_oldpos),
271			    sc_vtb_geta(&scp->vtb, scp->cursor_oldpos) >> 8);
272			a = sc_vtb_geta(&scp->vtb, at) >> 8;
273			c = sc_vtb_getc(&scp->vtb, at);
274			vidd_putc(scp->sc->adp, at, c,
275			    (a >> 4) | ((a & 0xf) << 4));
276		} else {
277			if (scp->status & VR_CURSOR_ON)
278				vidd_putc(scp->sc->adp, at,
279				    sc_vtb_getc(&scp->vtb, at),
280				    sc_vtb_geta(&scp->vtb, at) >> 8);
281			scp->status &= ~VR_CURSOR_ON;
282		}
283	}
284}
285#else
286static void
287gfb_cursor(scr_stat *scp, int at, int blink, int on, int flip)
288{
289	video_adapter_t *adp;
290
291	adp = scp->sc->adp;
292	if (scp->curs_attr.height <= 0)
293		/* the text cursor is disabled */
294		return;
295
296	if (on) {
297		if (!blink) {
298			scp->status |= VR_CURSOR_ON;
299			vidd_set_hw_cursor(adp, at%scp->xsize, at/scp->xsize);
300		} else if (++pxlblinkrate & 4) {
301			pxlblinkrate = 0;
302			scp->status ^= VR_CURSOR_ON;
303			if(scp->status & VR_CURSOR_ON)
304				vidd_set_hw_cursor(adp, at%scp->xsize,
305				    at/scp->xsize);
306			else
307				vidd_set_hw_cursor(adp, -1, -1);
308		}
309	} else {
310		if (scp->status & VR_CURSOR_ON)
311			vidd_set_hw_cursor(adp, at%scp->xsize, at/scp->xsize);
312		scp->status &= ~VR_CURSOR_ON;
313	}
314	if (blink)
315		scp->status |= VR_CURSOR_BLINK;
316	else
317		scp->status &= ~VR_CURSOR_BLINK;
318}
319#endif
320
321static void
322gfb_blink(scr_stat *scp, int at, int flip)
323{
324	if (!(scp->status & VR_CURSOR_BLINK))
325		return;
326	if (!(++pxlblinkrate & 4))
327		return;
328	pxlblinkrate = 0;
329	scp->status ^= VR_CURSOR_ON;
330	gfb_cursor(scp, at, scp->status & VR_CURSOR_BLINK,
331	    scp->status & VR_CURSOR_ON, flip);
332}
333
334#ifndef SC_NO_CUTPASTE
335
336static void
337gfb_mouse(scr_stat *scp, int x, int y, int on)
338{
339#ifdef __sparc64__
340	vidd_putm(scp->sc->adp, x, y, mouse_pointer,
341	    on ? 0xffffffff : 0x0, 22, 12);
342#else
343	if (on) {
344		vidd_putm(scp->sc->adp, x, y, mouse_pointer,
345		    0xffffffff, 16, 8);
346	} else {
347		/* XXX: removal is incomplete for h/w cursors and borders. */
348	}
349#endif
350}
351
352#endif /* SC_NO_CUTPASTE */
353