1185029Spjd/*-
2185029Spjd * Copyright (c) 1999 Michael Smith <msmith@freebsd.org>
3185029Spjd * Copyright (c) 1999 Kazutaka YOKOTA <yokota@freebsd.org>
4185029Spjd * Copyright (c) 1999 Dag-Erling Co��dan Sm��rgrav
5185029Spjd * All rights reserved.
6185029Spjd *
7185029Spjd * Redistribution and use in source and binary forms, with or without
8185029Spjd * modification, are permitted provided that the following conditions
9185029Spjd * are met:
10185029Spjd * 1. Redistributions of source code must retain the above copyright
11185029Spjd *    notice, this list of conditions and the following disclaimer
12185029Spjd *    in this position and unchanged.
13185029Spjd * 2. Redistributions in binary form must reproduce the above copyright
14185029Spjd *    notice, this list of conditions and the following disclaimer in the
15185029Spjd *    documentation and/or other materials provided with the distribution.
16185029Spjd * 3. The name of the author may not be used to endorse or promote products
17185029Spjd *    derived from this software without specific prior written permission
18185029Spjd *
19185029Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20185029Spjd * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21185029Spjd * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22185096Sdfr * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23185096Sdfr * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24185096Sdfr * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25185029Spjd * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26185029Spjd * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27185029Spjd * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28185029Spjd * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29185029Spjd *
30200309Sjhb * $FreeBSD$
31185029Spjd */
32185029Spjd
33185029Spjd#include <sys/param.h>
34185029Spjd#include <sys/systm.h>
35185029Spjd#include <sys/kernel.h>
36185029Spjd#include <sys/linker.h>
37185029Spjd#include <sys/module.h>
38185029Spjd#include <sys/fbio.h>
39185029Spjd
40213136Spjd#include <dev/fb/fbreg.h>
41213136Spjd#include <dev/fb/splashreg.h>
42213136Spjd
43213136Spjdstatic int splash_mode = -1;
44237756Savgstatic int splash_on = FALSE;
45185029Spjd
46237766Savgstatic int pcx_start(video_adapter_t *adp);
47237766Savgstatic int pcx_end(video_adapter_t *adp);
48231287Sbaptstatic int pcx_splash(video_adapter_t *adp, int on);
49231287Sbaptstatic int pcx_init(void *data, int sdepth);
50199714Srnolandstatic int pcx_draw(video_adapter_t *adp);
51185029Spjd
52185029Spjdstatic splash_decoder_t pcx_decoder = {
53185029Spjd	.name = "splash_pcx",
54185029Spjd	.init = pcx_start,
55185029Spjd	.term = pcx_end,
56185029Spjd	.splash = pcx_splash,
57212805Spjd	.data_type = SPLASH_IMAGE,
58185029Spjd};
59185029Spjd
60185029SpjdSPLASH_DECODER(splash_pcx, pcx_decoder);
61185029Spjd
62185029Spjdstatic struct {
63185029Spjd	int		 width;
64185029Spjd	int		 height;
65185029Spjd	int		 bpsl;
66185029Spjd	int		 bpp;
67185029Spjd	int		 planes;
68185096Sdfr	int		 zlen;
69185096Sdfr	const uint8_t	*zdata;
70185096Sdfr	uint8_t		*palette;
71185029Spjd} pcx_info;
72185029Spjd
73185029Spjdstatic int
74185029Spjdpcx_start(video_adapter_t *adp)
75185029Spjd{
76185029Spjd	static int modes[] = {
77185029Spjd		M_VGA_CG320,
78185029Spjd		M_VESA_CG640x480,
79185029Spjd		M_VESA_CG800x600,
80185029Spjd		M_VESA_CG1024x768,
81185029Spjd		-1,
82185029Spjd	};
83185029Spjd	video_info_t info;
84185029Spjd	int i;
85185029Spjd
86185029Spjd	if (pcx_decoder.data == NULL ||
87185029Spjd	    pcx_decoder.data_size <= 0 ||
88213136Spjd	    pcx_init(pcx_decoder.data, pcx_decoder.data_size))
89185029Spjd		return (ENODEV);
90185029Spjd
91185029Spjd	if (bootverbose)
92185029Spjd		printf("splash_pcx: image good:\n"
93185029Spjd		    "  width = %d\n"
94234679Savg		    "  height = %d\n"
95185029Spjd		    "  depth = %d\n"
96237766Savg		    "  planes = %d\n",
97185029Spjd		    pcx_info.width, pcx_info.height,
98185029Spjd		    pcx_info.bpp, pcx_info.planes);
99185029Spjd
100237766Savg	for (i = 0; modes[i] >= 0; ++i) {
101237766Savg		if (vidd_get_info(adp, modes[i], &info) != 0)
102185029Spjd			continue;
103200309Sjhb		if (bootverbose)
104200309Sjhb			printf("splash_pcx: considering mode %d:\n"
105200309Sjhb			    "  vi_width = %d\n"
106200309Sjhb			    "  vi_height = %d\n"
107200309Sjhb			    "  vi_depth = %d\n"
108200309Sjhb			    "  vi_planes = %d\n",
109200309Sjhb			    modes[i],
110200309Sjhb			    info.vi_width, info.vi_height,
111200309Sjhb			    info.vi_depth, info.vi_planes);
112200309Sjhb		if (info.vi_width >= pcx_info.width
113200309Sjhb		    && info.vi_height >= pcx_info.height
114200309Sjhb		    && info.vi_depth == pcx_info.bpp
115200309Sjhb		    && info.vi_planes == pcx_info.planes)
116185029Spjd			break;
117185029Spjd	}
118185029Spjd
119185029Spjd	splash_mode = modes[i];
120185029Spjd	if (splash_mode == -1)
121185029Spjd		return (ENODEV);
122185029Spjd	if (bootverbose)
123185029Spjd		printf("splash_pcx: selecting mode %d\n", splash_mode);
124185029Spjd	return (0);
125185029Spjd}
126185029Spjd
127200309Sjhbstatic int
128185029Spjdpcx_end(video_adapter_t *adp)
129185029Spjd{
130185029Spjd	/* nothing to do */
131185029Spjd	return (0);
132185029Spjd}
133185029Spjd
134185029Spjdstatic int
135185029Spjdpcx_splash(video_adapter_t *adp, int on)
136185029Spjd{
137185029Spjd	if (on) {
138185029Spjd		if (!splash_on) {
139185029Spjd			if (vidd_set_mode(adp, splash_mode) || pcx_draw(adp))
140185029Spjd				return 1;
141185029Spjd			splash_on = TRUE;
142185029Spjd		}
143185029Spjd		return (0);
144185029Spjd	} else {
145185029Spjd		splash_on = FALSE;
146185029Spjd		return (0);
147185029Spjd	}
148185029Spjd}
149185029Spjd
150185029Spjdstruct pcx_header {
151185029Spjd	uint8_t		 manufactor;
152185029Spjd	uint8_t		 version;
153185029Spjd	uint8_t		 encoding;
154185029Spjd	uint8_t		 bpp;
155185029Spjd	uint16_t	 xmin;
156185029Spjd	uint16_t	 ymin;
157185029Spjd	uint16_t	 xmax;
158185029Spjd	uint16_t	 ymax;
159185029Spjd	uint16_t	 hres;
160185029Spjd	uint16_t	 vres;
161185029Spjd	uint8_t		 colormap[48];
162185029Spjd	uint8_t		 rsvd;
163185029Spjd	uint8_t		 nplanes;
164185029Spjd	uint16_t	 bpsl;
165185029Spjd	uint16_t	 palinfo;
166185029Spjd	uint16_t	 hsize;
167185029Spjd	uint16_t	 vsize;
168185029Spjd};
169185029Spjd
170185029Spjd#define MAXSCANLINE 1024
171185029Spjd
172185029Spjdstatic int
173185029Spjdpcx_init(void *data, int size)
174185029Spjd{
175185029Spjd	const struct pcx_header *hdr = data;
176185029Spjd
177185029Spjd	if (size < 128 + 1 + 1 + 768 ||
178237766Savg	    hdr->manufactor != 10 ||
179242241Savg	    hdr->version != 5 ||
180242241Savg	    hdr->encoding != 1 ||
181185029Spjd	    hdr->nplanes != 1 ||
182185029Spjd	    hdr->bpp != 8 ||
183185029Spjd	    hdr->bpsl > MAXSCANLINE ||
184185029Spjd	    ((uint8_t *)data)[size - 769] != 12) {
185185029Spjd		printf("splash_pcx: invalid PCX image\n");
186185029Spjd		return (1);
187185029Spjd	}
188185029Spjd	pcx_info.width = hdr->xmax - hdr->xmin + 1;
189185029Spjd	pcx_info.height = hdr->ymax - hdr->ymin + 1;
190199579Sjhb	pcx_info.bpsl = hdr->bpsl;
191199579Sjhb	pcx_info.bpp = hdr->bpp;
192185029Spjd	pcx_info.planes = hdr->nplanes;
193185029Spjd	pcx_info.zlen = size - (128 + 1 + 768);
194185029Spjd	pcx_info.zdata = (uint8_t *)data + 128;
195185029Spjd	pcx_info.palette = (uint8_t *)data + size - 768;
196185029Spjd	return (0);
197185029Spjd}
198185029Spjd
199213136Spjdstatic int
200185029Spjdpcx_draw(video_adapter_t *adp)
201185029Spjd{
202185029Spjd	uint8_t *vidmem;
203185029Spjd	int swidth, sheight, sbpsl, sdepth, splanes;
204185029Spjd	int banksize, origin;
205185029Spjd	int c, i, j, pos, scan, x, y;
206185029Spjd	uint8_t line[MAXSCANLINE];
207185029Spjd
208185029Spjd	if (pcx_info.zlen < 1)
209185029Spjd		return (1);
210185029Spjd
211185029Spjd	vidd_load_palette(adp, pcx_info.palette);
212185029Spjd
213185029Spjd	vidmem = (uint8_t *)adp->va_window;
214185029Spjd	swidth = adp->va_info.vi_width;
215185029Spjd	sheight = adp->va_info.vi_height;
216185029Spjd	sbpsl = adp->va_line_width;
217185029Spjd	sdepth = adp->va_info.vi_depth;
218185029Spjd	splanes = adp->va_info.vi_planes;
219237766Savg	banksize = adp->va_window_size;
220185029Spjd
221185029Spjd	for (origin = 0; origin < sheight*sbpsl; origin += banksize) {
222185029Spjd		vidd_set_win_org(adp, origin);
223185029Spjd		bzero(vidmem, banksize);
224185029Spjd	}
225200309Sjhb
226200309Sjhb	x = (swidth - pcx_info.width) / 2;
227185029Spjd	y = (sheight - pcx_info.height) / 2;
228200309Sjhb	origin = 0;
229185029Spjd	pos = y * sbpsl + x;
230200309Sjhb	while (pos > banksize) {
231200309Sjhb		pos -= banksize;
232200309Sjhb		origin += banksize;
233200309Sjhb	}
234200309Sjhb	vidd_set_win_org(adp, origin);
235200309Sjhb
236200309Sjhb	for (scan = i = 0; scan < pcx_info.height; ++scan, ++y, pos += sbpsl) {
237200309Sjhb		for (j = 0; j < pcx_info.bpsl && i < pcx_info.zlen; ++i) {
238200309Sjhb			if ((pcx_info.zdata[i] & 0xc0) == 0xc0) {
239200309Sjhb				c = pcx_info.zdata[i++] & 0x3f;
240200309Sjhb				if (i >= pcx_info.zlen)
241200309Sjhb					return (1);
242200309Sjhb			} else {
243200309Sjhb				c = 1;
244200309Sjhb			}
245200309Sjhb			if (j + c > pcx_info.bpsl)
246200309Sjhb				return (1);
247200309Sjhb			while (c--)
248200309Sjhb				line[j++] = pcx_info.zdata[i];
249200309Sjhb		}
250200309Sjhb
251200309Sjhb		if (pos > banksize) {
252200309Sjhb			origin += banksize;
253200309Sjhb			pos -= banksize;
254200309Sjhb			vidd_set_win_org(adp, origin);
255200309Sjhb		}
256200309Sjhb
257200309Sjhb		if (pos + pcx_info.width > banksize) {
258200309Sjhb			/* scanline crosses bank boundary */
259200309Sjhb			j = banksize - pos;
260200309Sjhb			bcopy(line, vidmem + pos, j);
261200309Sjhb			origin += banksize;
262200309Sjhb			pos -= banksize;
263200309Sjhb			vidd_set_win_org(adp, origin);
264200309Sjhb			bcopy(line + j, vidmem, pcx_info.width - j);
265200309Sjhb		} else {
266200309Sjhb			bcopy(line, vidmem + pos, pcx_info.width);
267200309Sjhb		}
268200309Sjhb	}
269200309Sjhb
270200309Sjhb	return (0);
271200309Sjhb}
272200309Sjhb