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