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
110static u_char mouse_pointer[16] = {
111	0x00, 0x40, 0x60, 0x70, 0x78, 0x7c, 0x7e, 0x68,
112	0x0c, 0x0c, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00
113};
114#endif
115
116static void
117gfb_nop(scr_stat *scp)
118{
119}
120
121/* text mode renderer */
122
123static void
124gfb_clear(scr_stat *scp, int c, int attr)
125{
126	vidd_clear(scp->sc->adp);
127}
128
129static void
130gfb_border(scr_stat *scp, int color)
131{
132	vidd_set_border(scp->sc->adp, color);
133}
134
135static void
136gfb_draw(scr_stat *scp, int from, int count, int flip)
137{
138	int c;
139	int a;
140	int i, n;
141	video_adapter_t *adp;
142
143	adp = scp->sc->adp;
144
145	/*
146	   Determine if we need to scroll based on the offset
147	   and the number of characters to be displayed...
148	 */
149	if (from + count > scp->xsize*scp->ysize) {
150
151		/*
152		   Calculate the number of characters past the end of the
153		   visible screen...
154		*/
155		count = (from + count) -
156		    (adp->va_info.vi_width * adp->va_info.vi_height);
157
158		/*
159		   Calculate the number of rows past the end of the visible
160		   screen...
161		*/
162		n = (count / adp->va_info.vi_width) + 1;
163
164		/* Scroll to make room for new text rows... */
165		vidd_copy(adp, n, 0, n);
166#if 0
167		vidd_clear(adp, n);
168#endif
169
170		/* Display new text rows... */
171		vidd_puts(adp, from,
172		    (u_int16_t *)sc_vtb_pointer(&scp->vtb, from), count);
173	}
174
175	/*
176	   We don't need to scroll, so we can just put the characters
177	   all-at-once...
178	*/
179	else {
180
181		/*
182		   Determine the method by which we are to display characters
183		   (are we going to print forwards or backwards?
184		   do we need to do a character-by-character copy, then?)...
185		*/
186		if (flip)
187			for (i = count; i-- > 0; ++from) {
188				c = sc_vtb_getc(&scp->vtb, from);
189				a = sc_vtb_geta(&scp->vtb, from) >> 8;
190				vidd_putc(adp, from, c,
191				    (a >> 4) | ((a & 0xf) << 4));
192			}
193		else {
194			vidd_puts(adp, from,
195			    (u_int16_t *)sc_vtb_pointer(&scp->vtb, from),
196			    count);
197		}
198	}
199}
200
201static void
202gfb_cursor_shape(scr_stat *scp, int base, int height, int blink)
203{
204	if (base < 0 || base >= scp->font_size)
205		return;
206	/* the caller may set height <= 0 in order to disable the cursor */
207#if 0
208	scp->cursor_base = base;
209	scp->cursor_height = height;
210#endif
211	vidd_set_hw_cursor_shape(scp->sc->adp, base, height, scp->font_size,
212	    blink);
213}
214
215static int pxlblinkrate = 0;
216
217#if defined(SC_OFWFB)
218static void
219gfb_cursor(scr_stat *scp, int at, int blink, int on, int flip)
220{
221	video_adapter_t *adp;
222	int a, c;
223
224	if (scp->curs_attr.height <= 0)	/* the text cursor is disabled */
225		return;
226
227	adp = scp->sc->adp;
228	if(blink) {
229		scp->status |= VR_CURSOR_BLINK;
230		if (on) {
231			scp->status |= VR_CURSOR_ON;
232			vidd_set_hw_cursor(adp, at%scp->xsize, at/scp->xsize);
233		} else {
234			if (scp->status & VR_CURSOR_ON)
235				vidd_set_hw_cursor(adp, -1, -1);
236			scp->status &= ~VR_CURSOR_ON;
237		}
238	} else {
239		scp->status &= ~VR_CURSOR_BLINK;
240		if(on) {
241			scp->status |= VR_CURSOR_ON;
242			vidd_putc(scp->sc->adp, scp->cursor_oldpos,
243			    sc_vtb_getc(&scp->vtb, scp->cursor_oldpos),
244			    sc_vtb_geta(&scp->vtb, scp->cursor_oldpos) >> 8);
245			a = sc_vtb_geta(&scp->vtb, at) >> 8;
246			c = sc_vtb_getc(&scp->vtb, at);
247			vidd_putc(scp->sc->adp, at, c,
248			    (a >> 4) | ((a & 0xf) << 4));
249		} else {
250			if (scp->status & VR_CURSOR_ON)
251				vidd_putc(scp->sc->adp, at,
252				    sc_vtb_getc(&scp->vtb, at),
253				    sc_vtb_geta(&scp->vtb, at) >> 8);
254			scp->status &= ~VR_CURSOR_ON;
255		}
256	}
257}
258#else
259static void
260gfb_cursor(scr_stat *scp, int at, int blink, int on, int flip)
261{
262	video_adapter_t *adp;
263
264	adp = scp->sc->adp;
265	if (scp->curs_attr.height <= 0)
266		/* the text cursor is disabled */
267		return;
268
269	if (on) {
270		if (!blink) {
271			scp->status |= VR_CURSOR_ON;
272			vidd_set_hw_cursor(adp, at%scp->xsize, at/scp->xsize);
273		} else if (++pxlblinkrate & 4) {
274			pxlblinkrate = 0;
275			scp->status ^= VR_CURSOR_ON;
276			if(scp->status & VR_CURSOR_ON)
277				vidd_set_hw_cursor(adp, at%scp->xsize,
278				    at/scp->xsize);
279			else
280				vidd_set_hw_cursor(adp, -1, -1);
281		}
282	} else {
283		if (scp->status & VR_CURSOR_ON)
284			vidd_set_hw_cursor(adp, at%scp->xsize, at/scp->xsize);
285		scp->status &= ~VR_CURSOR_ON;
286	}
287	if (blink)
288		scp->status |= VR_CURSOR_BLINK;
289	else
290		scp->status &= ~VR_CURSOR_BLINK;
291}
292#endif
293
294static void
295gfb_blink(scr_stat *scp, int at, int flip)
296{
297	if (!(scp->status & VR_CURSOR_BLINK))
298		return;
299	if (!(++pxlblinkrate & 4))
300		return;
301	pxlblinkrate = 0;
302	scp->status ^= VR_CURSOR_ON;
303	gfb_cursor(scp, at, scp->status & VR_CURSOR_BLINK,
304	    scp->status & VR_CURSOR_ON, flip);
305}
306
307#ifndef SC_NO_CUTPASTE
308
309static void
310gfb_mouse(scr_stat *scp, int x, int y, int on)
311{
312	if (on) {
313		vidd_putm(scp->sc->adp, x, y, mouse_pointer,
314		    0xffffffff, 16, 8);
315	} else {
316		/* XXX: removal is incomplete for h/w cursors and borders. */
317	}
318}
319
320#endif /* SC_NO_CUTPASTE */
321