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