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#ifdef __powerpc__
36#include "opt_ofwfb.h"
37#endif
38
39#include <sys/param.h>
40#include <sys/systm.h>
41#include <sys/kernel.h>
42#include <sys/fbio.h>
43#include <sys/consio.h>
44
45#include <machine/bus.h>
46
47#include <dev/fb/fbreg.h>
48#include <dev/syscons/syscons.h>
49
50#ifndef SC_RENDER_DEBUG
51#define SC_RENDER_DEBUG		0
52#endif
53
54static vr_clear_t		gfb_clear;
55static vr_draw_border_t		gfb_border;
56static vr_draw_t		gfb_draw;
57static vr_set_cursor_t		gfb_cursor_shape;
58static vr_draw_cursor_t		gfb_cursor;
59static vr_blink_cursor_t	gfb_blink;
60#ifndef SC_NO_CUTPASTE
61static vr_draw_mouse_t		gfb_mouse;
62#else
63#define gfb_mouse		(vr_draw_mouse_t *)gfb_nop
64#endif
65
66static void			gfb_nop(scr_stat *scp);
67
68sc_rndr_sw_t txtrndrsw = {
69	(vr_init_t *)gfb_nop,
70	gfb_clear,
71	gfb_border,
72	gfb_draw,
73	gfb_cursor_shape,
74	gfb_cursor,
75	gfb_blink,
76	(vr_set_mouse_t *)gfb_nop,
77	gfb_mouse,
78};
79
80#ifdef SC_PIXEL_MODE
81sc_rndr_sw_t gfbrndrsw = {
82	(vr_init_t *)gfb_nop,
83	gfb_clear,
84	gfb_border,
85	gfb_draw,
86	gfb_cursor_shape,
87	gfb_cursor,
88	gfb_blink,
89	(vr_set_mouse_t *)gfb_nop,
90	gfb_mouse,
91};
92#endif /* SC_PIXEL_MODE */
93
94#ifndef SC_NO_MODE_CHANGE
95sc_rndr_sw_t grrndrsw = {
96	(vr_init_t *)gfb_nop,
97	(vr_clear_t *)gfb_nop,
98	gfb_border,
99	(vr_draw_t *)gfb_nop,
100	(vr_set_cursor_t *)gfb_nop,
101	(vr_draw_cursor_t *)gfb_nop,
102	(vr_blink_cursor_t *)gfb_nop,
103	(vr_set_mouse_t *)gfb_nop,
104	(vr_draw_mouse_t *)gfb_nop,
105};
106#endif /* SC_NO_MODE_CHANGE */
107
108#ifndef SC_NO_CUTPASTE
109static u_char mouse_pointer[16] = {
110	0x00, 0x40, 0x60, 0x70, 0x78, 0x7c, 0x7e, 0x68,
111	0x0c, 0x0c, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00
112};
113#endif
114
115static void
116gfb_nop(scr_stat *scp)
117{
118}
119
120/* text mode renderer */
121
122static void
123gfb_clear(scr_stat *scp, int c, int attr)
124{
125	vidd_clear(scp->sc->adp);
126}
127
128static void
129gfb_border(scr_stat *scp, int color)
130{
131	vidd_set_border(scp->sc->adp, color);
132}
133
134static void
135gfb_draw(scr_stat *scp, int from, int count, int flip)
136{
137	int c;
138	int a;
139	int i, n;
140	video_adapter_t *adp;
141
142	adp = scp->sc->adp;
143
144	/*
145	   Determine if we need to scroll based on the offset
146	   and the number of characters to be displayed...
147	 */
148	if (from + count > scp->xsize*scp->ysize) {
149		/*
150		   Calculate the number of characters past the end of the
151		   visible screen...
152		*/
153		count = (from + count) -
154		    (adp->va_info.vi_width * adp->va_info.vi_height);
155
156		/*
157		   Calculate the number of rows past the end of the visible
158		   screen...
159		*/
160		n = (count / adp->va_info.vi_width) + 1;
161
162		/* Scroll to make room for new text rows... */
163		vidd_copy(adp, n, 0, n);
164#if 0
165		vidd_clear(adp, n);
166#endif
167
168		/* Display new text rows... */
169		vidd_puts(adp, from,
170		    (u_int16_t *)sc_vtb_pointer(&scp->vtb, from), count);
171	}
172
173	/*
174	   We don't need to scroll, so we can just put the characters
175	   all-at-once...
176	*/
177	else {
178		/*
179		   Determine the method by which we are to display characters
180		   (are we going to print forwards or backwards?
181		   do we need to do a character-by-character copy, then?)...
182		*/
183		if (flip)
184			for (i = count; i-- > 0; ++from) {
185				c = sc_vtb_getc(&scp->vtb, from);
186				a = sc_vtb_geta(&scp->vtb, from) >> 8;
187				vidd_putc(adp, from, c,
188				    (a >> 4) | ((a & 0xf) << 4));
189			}
190		else {
191			vidd_puts(adp, from,
192			    (u_int16_t *)sc_vtb_pointer(&scp->vtb, from),
193			    count);
194		}
195	}
196}
197
198static void
199gfb_cursor_shape(scr_stat *scp, int base, int height, int blink)
200{
201	if (base < 0 || base >= scp->font_size)
202		return;
203	/* the caller may set height <= 0 in order to disable the cursor */
204#if 0
205	scp->cursor_base = base;
206	scp->cursor_height = height;
207#endif
208	vidd_set_hw_cursor_shape(scp->sc->adp, base, height, scp->font_size,
209	    blink);
210}
211
212static int pxlblinkrate = 0;
213
214#if defined(SC_OFWFB)
215static void
216gfb_cursor(scr_stat *scp, int at, int blink, int on, int flip)
217{
218	video_adapter_t *adp;
219	int a, c;
220
221	if (scp->curs_attr.height <= 0)	/* the text cursor is disabled */
222		return;
223
224	adp = scp->sc->adp;
225	if(blink) {
226		scp->status |= VR_CURSOR_BLINK;
227		if (on) {
228			scp->status |= VR_CURSOR_ON;
229			vidd_set_hw_cursor(adp, at%scp->xsize, at/scp->xsize);
230		} else {
231			if (scp->status & VR_CURSOR_ON)
232				vidd_set_hw_cursor(adp, -1, -1);
233			scp->status &= ~VR_CURSOR_ON;
234		}
235	} else {
236		scp->status &= ~VR_CURSOR_BLINK;
237		if(on) {
238			scp->status |= VR_CURSOR_ON;
239			vidd_putc(scp->sc->adp, scp->cursor_oldpos,
240			    sc_vtb_getc(&scp->vtb, scp->cursor_oldpos),
241			    sc_vtb_geta(&scp->vtb, scp->cursor_oldpos) >> 8);
242			a = sc_vtb_geta(&scp->vtb, at) >> 8;
243			c = sc_vtb_getc(&scp->vtb, at);
244			vidd_putc(scp->sc->adp, at, c,
245			    (a >> 4) | ((a & 0xf) << 4));
246		} else {
247			if (scp->status & VR_CURSOR_ON)
248				vidd_putc(scp->sc->adp, at,
249				    sc_vtb_getc(&scp->vtb, at),
250				    sc_vtb_geta(&scp->vtb, at) >> 8);
251			scp->status &= ~VR_CURSOR_ON;
252		}
253	}
254}
255#else
256static void
257gfb_cursor(scr_stat *scp, int at, int blink, int on, int flip)
258{
259	video_adapter_t *adp;
260
261	adp = scp->sc->adp;
262	if (scp->curs_attr.height <= 0)
263		/* the text cursor is disabled */
264		return;
265
266	if (on) {
267		if (!blink) {
268			scp->status |= VR_CURSOR_ON;
269			vidd_set_hw_cursor(adp, at%scp->xsize, at/scp->xsize);
270		} else if (++pxlblinkrate & 4) {
271			pxlblinkrate = 0;
272			scp->status ^= VR_CURSOR_ON;
273			if(scp->status & VR_CURSOR_ON)
274				vidd_set_hw_cursor(adp, at%scp->xsize,
275				    at/scp->xsize);
276			else
277				vidd_set_hw_cursor(adp, -1, -1);
278		}
279	} else {
280		if (scp->status & VR_CURSOR_ON)
281			vidd_set_hw_cursor(adp, at%scp->xsize, at/scp->xsize);
282		scp->status &= ~VR_CURSOR_ON;
283	}
284	if (blink)
285		scp->status |= VR_CURSOR_BLINK;
286	else
287		scp->status &= ~VR_CURSOR_BLINK;
288}
289#endif
290
291static void
292gfb_blink(scr_stat *scp, int at, int flip)
293{
294	if (!(scp->status & VR_CURSOR_BLINK))
295		return;
296	if (!(++pxlblinkrate & 4))
297		return;
298	pxlblinkrate = 0;
299	scp->status ^= VR_CURSOR_ON;
300	gfb_cursor(scp, at, scp->status & VR_CURSOR_BLINK,
301	    scp->status & VR_CURSOR_ON, flip);
302}
303
304#ifndef SC_NO_CUTPASTE
305
306static void
307gfb_mouse(scr_stat *scp, int x, int y, int on)
308{
309	if (on) {
310		vidd_putm(scp->sc->adp, x, y, mouse_pointer,
311		    0xffffffff, 16, 8);
312	} else {
313		/* XXX: removal is incomplete for h/w cursors and borders. */
314	}
315}
316
317#endif /* SC_NO_CUTPASTE */
318