145617Sdes/*-
245617Sdes * Copyright (c) 1999 Michael Smith <msmith@freebsd.org>
345617Sdes * Copyright (c) 1999 Kazutaka YOKOTA <yokota@freebsd.org>
4230132Suqs * Copyright (c) 1999 Dag-Erling Co��dan Sm��rgrav
545617Sdes * All rights reserved.
645617Sdes *
745617Sdes * Redistribution and use in source and binary forms, with or without
845617Sdes * modification, are permitted provided that the following conditions
945617Sdes * are met:
1045617Sdes * 1. Redistributions of source code must retain the above copyright
1145617Sdes *    notice, this list of conditions and the following disclaimer
1245617Sdes *    in this position and unchanged.
1345617Sdes * 2. Redistributions in binary form must reproduce the above copyright
1445617Sdes *    notice, this list of conditions and the following disclaimer in the
1545617Sdes *    documentation and/or other materials provided with the distribution.
1645617Sdes * 3. The name of the author may not be used to endorse or promote products
1745617Sdes *    derived from this software without specific prior written permission
1845617Sdes *
1945617Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2045617Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2145617Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2245617Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2345617Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2445617Sdes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2545617Sdes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2645617Sdes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2745617Sdes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2845617Sdes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2945617Sdes *
3050477Speter * $FreeBSD$
3145617Sdes */
3245617Sdes
3345617Sdes#include <sys/param.h>
3445617Sdes#include <sys/systm.h>
3545617Sdes#include <sys/kernel.h>
3645617Sdes#include <sys/linker.h>
37129880Sphk#include <sys/module.h>
3848104Syokota#include <sys/fbio.h>
3945617Sdes
4045617Sdes#include <dev/fb/fbreg.h>
4145617Sdes#include <dev/fb/splashreg.h>
4245617Sdes
4345617Sdesstatic int splash_mode = -1;
4445617Sdesstatic int splash_on = FALSE;
4545617Sdes
4645617Sdesstatic int pcx_start(video_adapter_t *adp);
4745617Sdesstatic int pcx_end(video_adapter_t *adp);
4845617Sdesstatic int pcx_splash(video_adapter_t *adp, int on);
49133227Sdesstatic int pcx_init(void *data, int sdepth);
5045617Sdesstatic int pcx_draw(video_adapter_t *adp);
5145617Sdes
5245617Sdesstatic splash_decoder_t pcx_decoder = {
53133227Sdes	.name = "splash_pcx",
54133227Sdes	.init = pcx_start,
55133227Sdes	.term = pcx_end,
56133227Sdes	.splash = pcx_splash,
57133227Sdes	.data_type = SPLASH_IMAGE,
5845617Sdes};
5945617Sdes
6045617SdesSPLASH_DECODER(splash_pcx, pcx_decoder);
6145617Sdes
62133227Sdesstatic struct {
63133227Sdes	int		 width;
64133227Sdes	int		 height;
65133227Sdes	int		 bpsl;
66133227Sdes	int		 bpp;
67133227Sdes	int		 planes;
68133227Sdes	int		 zlen;
69133227Sdes	const uint8_t	*zdata;
70133227Sdes	uint8_t		*palette;
7145617Sdes} pcx_info;
7245617Sdes
73133227Sdesstatic int
7445617Sdespcx_start(video_adapter_t *adp)
7545617Sdes{
76133227Sdes	static int modes[] = {
77133227Sdes		M_VGA_CG320,
78133227Sdes		M_VESA_CG640x480,
79133227Sdes		M_VESA_CG800x600,
80133227Sdes		M_VESA_CG1024x768,
81133227Sdes		-1,
82133227Sdes	};
83133227Sdes	video_info_t info;
84133227Sdes	int i;
8545617Sdes
86133227Sdes	if (pcx_decoder.data == NULL ||
87133227Sdes	    pcx_decoder.data_size <= 0 ||
88133227Sdes	    pcx_init(pcx_decoder.data, pcx_decoder.data_size))
89133227Sdes		return (ENODEV);
9045617Sdes
9145617Sdes	if (bootverbose)
92133227Sdes		printf("splash_pcx: image good:\n"
93133227Sdes		    "  width = %d\n"
94133227Sdes		    "  height = %d\n"
95133227Sdes		    "  depth = %d\n"
96133227Sdes		    "  planes = %d\n",
97133227Sdes		    pcx_info.width, pcx_info.height,
98133227Sdes		    pcx_info.bpp, pcx_info.planes);
99133227Sdes
100133227Sdes	for (i = 0; modes[i] >= 0; ++i) {
101174985Swkoszek		if (vidd_get_info(adp, modes[i], &info) != 0)
102133227Sdes			continue;
103133227Sdes		if (bootverbose)
104133227Sdes			printf("splash_pcx: considering mode %d:\n"
105133227Sdes			    "  vi_width = %d\n"
106133227Sdes			    "  vi_height = %d\n"
107133227Sdes			    "  vi_depth = %d\n"
108133227Sdes			    "  vi_planes = %d\n",
109133227Sdes			    modes[i],
110133227Sdes			    info.vi_width, info.vi_height,
111133227Sdes			    info.vi_depth, info.vi_planes);
112133227Sdes		if (info.vi_width >= pcx_info.width
113133227Sdes		    && info.vi_height >= pcx_info.height
114133227Sdes		    && info.vi_depth == pcx_info.bpp
115133227Sdes		    && info.vi_planes == pcx_info.planes)
116133227Sdes			break;
117133227Sdes	}
118133227Sdes
119133227Sdes	splash_mode = modes[i];
120133227Sdes	if (splash_mode == -1)
121133227Sdes		return (ENODEV);
122133227Sdes	if (bootverbose)
123133227Sdes		printf("splash_pcx: selecting mode %d\n", splash_mode);
124133227Sdes	return (0);
12545617Sdes}
12645617Sdes
12745617Sdesstatic int
12845617Sdespcx_end(video_adapter_t *adp)
12945617Sdes{
130133227Sdes	/* nothing to do */
131133227Sdes	return (0);
13245617Sdes}
13345617Sdes
13445617Sdesstatic int
13545617Sdespcx_splash(video_adapter_t *adp, int on)
13645617Sdes{
137133227Sdes	if (on) {
138133227Sdes		if (!splash_on) {
139174985Swkoszek			if (vidd_set_mode(adp, splash_mode) || pcx_draw(adp))
140133227Sdes				return 1;
141133227Sdes			splash_on = TRUE;
142133227Sdes		}
143133227Sdes		return (0);
144133227Sdes	} else {
145133227Sdes		splash_on = FALSE;
146133227Sdes		return (0);
14745617Sdes	}
14845617Sdes}
14945617Sdes
150133227Sdesstruct pcx_header {
151133227Sdes	uint8_t		 manufactor;
152133227Sdes	uint8_t		 version;
153133227Sdes	uint8_t		 encoding;
154133227Sdes	uint8_t		 bpp;
155133227Sdes	uint16_t	 xmin;
156133227Sdes	uint16_t	 ymin;
157133227Sdes	uint16_t	 xmax;
158133227Sdes	uint16_t	 ymax;
159133227Sdes	uint16_t	 hres;
160133227Sdes	uint16_t	 vres;
161133227Sdes	uint8_t		 colormap[48];
162133227Sdes	uint8_t		 rsvd;
163133227Sdes	uint8_t		 nplanes;
164133227Sdes	uint16_t	 bpsl;
165133227Sdes	uint16_t	 palinfo;
166133227Sdes	uint16_t	 hsize;
167133227Sdes	uint16_t	 vsize;
16845617Sdes};
16945617Sdes
17045617Sdes#define MAXSCANLINE 1024
17145617Sdes
17245617Sdesstatic int
173133227Sdespcx_init(void *data, int size)
17445617Sdes{
175133227Sdes	const struct pcx_header *hdr = data;
17645617Sdes
177133227Sdes	if (size < 128 + 1 + 1 + 768 ||
178133227Sdes	    hdr->manufactor != 10 ||
179133227Sdes	    hdr->version != 5 ||
180133227Sdes	    hdr->encoding != 1 ||
181133227Sdes	    hdr->nplanes != 1 ||
182133227Sdes	    hdr->bpp != 8 ||
183133227Sdes	    hdr->bpsl > MAXSCANLINE ||
184133227Sdes	    ((uint8_t *)data)[size - 769] != 12) {
185133227Sdes		printf("splash_pcx: invalid PCX image\n");
186133227Sdes		return (1);
187133227Sdes	}
188133227Sdes	pcx_info.width = hdr->xmax - hdr->xmin + 1;
189133227Sdes	pcx_info.height = hdr->ymax - hdr->ymin + 1;
190133227Sdes	pcx_info.bpsl = hdr->bpsl;
191133227Sdes	pcx_info.bpp = hdr->bpp;
192133227Sdes	pcx_info.planes = hdr->nplanes;
193133227Sdes	pcx_info.zlen = size - (128 + 1 + 768);
194133227Sdes	pcx_info.zdata = (uint8_t *)data + 128;
195133227Sdes	pcx_info.palette = (uint8_t *)data + size - 768;
196133227Sdes	return (0);
19745617Sdes}
19845617Sdes
19945617Sdesstatic int
20045617Sdespcx_draw(video_adapter_t *adp)
20145617Sdes{
202133227Sdes	uint8_t *vidmem;
203133227Sdes	int swidth, sheight, sbpsl, sdepth, splanes;
204133227Sdes	int banksize, origin;
205133227Sdes	int c, i, j, pos, scan, x, y;
206133227Sdes	uint8_t line[MAXSCANLINE];
20745617Sdes
208133227Sdes	if (pcx_info.zlen < 1)
209133227Sdes		return (1);
210133227Sdes
211174985Swkoszek	vidd_load_palette(adp, pcx_info.palette);
212133227Sdes
213133227Sdes	vidmem = (uint8_t *)adp->va_window;
214133227Sdes	swidth = adp->va_info.vi_width;
215133227Sdes	sheight = adp->va_info.vi_height;
216133227Sdes	sbpsl = adp->va_line_width;
217133227Sdes	sdepth = adp->va_info.vi_depth;
218133227Sdes	splanes = adp->va_info.vi_planes;
219133227Sdes	banksize = adp->va_window_size;
220133227Sdes
221133227Sdes	for (origin = 0; origin < sheight*sbpsl; origin += banksize) {
222174985Swkoszek		vidd_set_win_org(adp, origin);
223133227Sdes		bzero(vidmem, banksize);
22445617Sdes	}
22545617Sdes
226133227Sdes	x = (swidth - pcx_info.width) / 2;
227133227Sdes	y = (sheight - pcx_info.height) / 2;
228133227Sdes	origin = 0;
229133227Sdes	pos = y * sbpsl + x;
230133227Sdes	while (pos > banksize) {
231133227Sdes		pos -= banksize;
232133227Sdes		origin += banksize;
23345617Sdes	}
234174985Swkoszek	vidd_set_win_org(adp, origin);
23545617Sdes
236133227Sdes	for (scan = i = 0; scan < pcx_info.height; ++scan, ++y, pos += sbpsl) {
237133227Sdes		for (j = 0; j < pcx_info.bpsl && i < pcx_info.zlen; ++i) {
238133227Sdes			if ((pcx_info.zdata[i] & 0xc0) == 0xc0) {
239133227Sdes				c = pcx_info.zdata[i++] & 0x3f;
240133227Sdes				if (i >= pcx_info.zlen)
241133227Sdes					return (1);
242133227Sdes			} else {
243133227Sdes				c = 1;
244133227Sdes			}
245133227Sdes			if (j + c > pcx_info.bpsl)
246133227Sdes				return (1);
247133227Sdes			while (c--)
248133227Sdes				line[j++] = pcx_info.zdata[i];
249133227Sdes		}
250133227Sdes
251133227Sdes		if (pos > banksize) {
252133227Sdes			origin += banksize;
253133227Sdes			pos -= banksize;
254174985Swkoszek			vidd_set_win_org(adp, origin);
255133227Sdes		}
256133227Sdes
257133227Sdes		if (pos + pcx_info.width > banksize) {
258133227Sdes			/* scanline crosses bank boundary */
259133227Sdes			j = banksize - pos;
260133227Sdes			bcopy(line, vidmem + pos, j);
261133227Sdes			origin += banksize;
262133227Sdes			pos -= banksize;
263174985Swkoszek			vidd_set_win_org(adp, origin);
264133227Sdes			bcopy(line + j, vidmem, pcx_info.width - j);
265133227Sdes		} else {
266133227Sdes			bcopy(line, vidmem + pos, pcx_info.width);
267133227Sdes		}
26845617Sdes	}
26945617Sdes
270133227Sdes	return (0);
27145617Sdes}
272