1/*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 1999 Michael Smith <msmith@freebsd.org>
5 * Copyright (c) 1999 Kazutaka YOKOTA <yokota@freebsd.org>
6 * Copyright (c) 1999 Dag-Erling Co��dan Sm��rgrav
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer
14 *    in this position and unchanged.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 3. The name of the author may not be used to endorse or promote products
19 *    derived from this software without specific prior written permission
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 * $FreeBSD$
33 */
34
35#include <sys/param.h>
36#include <sys/systm.h>
37#include <sys/kernel.h>
38#include <sys/linker.h>
39#include <sys/module.h>
40#include <sys/fbio.h>
41
42#include <dev/fb/fbreg.h>
43#include <dev/fb/splashreg.h>
44
45static int splash_mode = -1;
46static int splash_on = FALSE;
47
48static int pcx_start(video_adapter_t *adp);
49static int pcx_end(video_adapter_t *adp);
50static int pcx_splash(video_adapter_t *adp, int on);
51static int pcx_init(void *data, int sdepth);
52static int pcx_draw(video_adapter_t *adp);
53
54static splash_decoder_t pcx_decoder = {
55	.name = "splash_pcx",
56	.init = pcx_start,
57	.term = pcx_end,
58	.splash = pcx_splash,
59	.data_type = SPLASH_IMAGE,
60};
61
62SPLASH_DECODER(splash_pcx, pcx_decoder);
63
64static struct {
65	int		 width;
66	int		 height;
67	int		 bpsl;
68	int		 bpp;
69	int		 planes;
70	int		 zlen;
71	const uint8_t	*zdata;
72	uint8_t		*palette;
73} pcx_info;
74
75static int
76pcx_start(video_adapter_t *adp)
77{
78	static int modes[] = {
79		M_VGA_CG320,
80		M_VESA_CG640x480,
81		M_VESA_CG800x600,
82		M_VESA_CG1024x768,
83		-1,
84	};
85	video_info_t info;
86	int i;
87
88	if (pcx_decoder.data == NULL ||
89	    pcx_decoder.data_size <= 0 ||
90	    pcx_init(pcx_decoder.data, pcx_decoder.data_size))
91		return (ENODEV);
92
93	if (bootverbose)
94		printf("splash_pcx: image good:\n"
95		    "  width = %d\n"
96		    "  height = %d\n"
97		    "  depth = %d\n"
98		    "  planes = %d\n",
99		    pcx_info.width, pcx_info.height,
100		    pcx_info.bpp, pcx_info.planes);
101
102	for (i = 0; modes[i] >= 0; ++i) {
103		if (vidd_get_info(adp, modes[i], &info) != 0)
104			continue;
105		if (bootverbose)
106			printf("splash_pcx: considering mode %d:\n"
107			    "  vi_width = %d\n"
108			    "  vi_height = %d\n"
109			    "  vi_depth = %d\n"
110			    "  vi_planes = %d\n",
111			    modes[i],
112			    info.vi_width, info.vi_height,
113			    info.vi_depth, info.vi_planes);
114		if (info.vi_width >= pcx_info.width
115		    && info.vi_height >= pcx_info.height
116		    && info.vi_depth == pcx_info.bpp
117		    && info.vi_planes == pcx_info.planes)
118			break;
119	}
120
121	splash_mode = modes[i];
122	if (splash_mode == -1)
123		return (ENODEV);
124	if (bootverbose)
125		printf("splash_pcx: selecting mode %d\n", splash_mode);
126	return (0);
127}
128
129static int
130pcx_end(video_adapter_t *adp)
131{
132	/* nothing to do */
133	return (0);
134}
135
136static int
137pcx_splash(video_adapter_t *adp, int on)
138{
139	if (on) {
140		if (!splash_on) {
141			if (vidd_set_mode(adp, splash_mode) || pcx_draw(adp))
142				return 1;
143			splash_on = TRUE;
144		}
145		return (0);
146	} else {
147		splash_on = FALSE;
148		return (0);
149	}
150}
151
152struct pcx_header {
153	uint8_t		 manufactor;
154	uint8_t		 version;
155	uint8_t		 encoding;
156	uint8_t		 bpp;
157	uint16_t	 xmin;
158	uint16_t	 ymin;
159	uint16_t	 xmax;
160	uint16_t	 ymax;
161	uint16_t	 hres;
162	uint16_t	 vres;
163	uint8_t		 colormap[48];
164	uint8_t		 rsvd;
165	uint8_t		 nplanes;
166	uint16_t	 bpsl;
167	uint16_t	 palinfo;
168	uint16_t	 hsize;
169	uint16_t	 vsize;
170};
171
172#define MAXSCANLINE 1024
173
174static int
175pcx_init(void *data, int size)
176{
177	const struct pcx_header *hdr = data;
178
179	if (size < 128 + 1 + 1 + 768 ||
180	    hdr->manufactor != 10 ||
181	    hdr->version != 5 ||
182	    hdr->encoding != 1 ||
183	    hdr->nplanes != 1 ||
184	    hdr->bpp != 8 ||
185	    hdr->bpsl > MAXSCANLINE ||
186	    ((uint8_t *)data)[size - 769] != 12) {
187		printf("splash_pcx: invalid PCX image\n");
188		return (1);
189	}
190	pcx_info.width = hdr->xmax - hdr->xmin + 1;
191	pcx_info.height = hdr->ymax - hdr->ymin + 1;
192	pcx_info.bpsl = hdr->bpsl;
193	pcx_info.bpp = hdr->bpp;
194	pcx_info.planes = hdr->nplanes;
195	pcx_info.zlen = size - (128 + 1 + 768);
196	pcx_info.zdata = (uint8_t *)data + 128;
197	pcx_info.palette = (uint8_t *)data + size - 768;
198	return (0);
199}
200
201static int
202pcx_draw(video_adapter_t *adp)
203{
204	uint8_t *vidmem;
205	int swidth, sheight, sbpsl, sdepth, splanes;
206	int banksize, origin;
207	int c, i, j, pos, scan, x, y;
208	uint8_t line[MAXSCANLINE];
209
210	if (pcx_info.zlen < 1)
211		return (1);
212
213	vidd_load_palette(adp, pcx_info.palette);
214
215	vidmem = (uint8_t *)adp->va_window;
216	swidth = adp->va_info.vi_width;
217	sheight = adp->va_info.vi_height;
218	sbpsl = adp->va_line_width;
219	sdepth = adp->va_info.vi_depth;
220	splanes = adp->va_info.vi_planes;
221	banksize = adp->va_window_size;
222
223	for (origin = 0; origin < sheight*sbpsl; origin += banksize) {
224		vidd_set_win_org(adp, origin);
225		bzero(vidmem, banksize);
226	}
227
228	x = (swidth - pcx_info.width) / 2;
229	y = (sheight - pcx_info.height) / 2;
230	origin = 0;
231	pos = y * sbpsl + x;
232	while (pos > banksize) {
233		pos -= banksize;
234		origin += banksize;
235	}
236	vidd_set_win_org(adp, origin);
237
238	for (scan = i = 0; scan < pcx_info.height; ++scan, ++y, pos += sbpsl) {
239		for (j = 0; j < pcx_info.bpsl && i < pcx_info.zlen; ++i) {
240			if ((pcx_info.zdata[i] & 0xc0) == 0xc0) {
241				c = pcx_info.zdata[i++] & 0x3f;
242				if (i >= pcx_info.zlen)
243					return (1);
244			} else {
245				c = 1;
246			}
247			if (j + c > pcx_info.bpsl)
248				return (1);
249			while (c--)
250				line[j++] = pcx_info.zdata[i];
251		}
252
253		if (pos > banksize) {
254			origin += banksize;
255			pos -= banksize;
256			vidd_set_win_org(adp, origin);
257		}
258
259		if (pos + pcx_info.width > banksize) {
260			/* scanline crosses bank boundary */
261			j = banksize - pos;
262			bcopy(line, vidmem + pos, j);
263			origin += banksize;
264			pos -= banksize;
265			vidd_set_win_org(adp, origin);
266			bcopy(line + j, vidmem, pcx_info.width - j);
267		} else {
268			bcopy(line, vidmem + pos, pcx_info.width);
269		}
270	}
271
272	return (0);
273}
274