xref: /openbsd-src/sys/dev/sbus/rfx.c (revision 898184e3e61f9129feb5978fad5a8c6865f00b92)
1 /*	$OpenBSD: rfx.c,v 1.9 2008/12/27 17:23:03 miod Exp $	*/
2 
3 /*
4  * Copyright (c) 2004, Miodrag Vallat.
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  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
20  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
24  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
25  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  *
28  */
29 
30 /*
31  * Driver for the Vitec RasterFlex family of frame buffers.
32  * It should support RasterFlex-24, RasterFlex-32 and RasterFlex-HR.
33  */
34 
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/buf.h>
38 #include <sys/device.h>
39 #include <sys/ioctl.h>
40 #include <sys/malloc.h>
41 #include <sys/mman.h>
42 #include <sys/tty.h>
43 #include <sys/conf.h>
44 
45 #include <uvm/uvm_extern.h>
46 
47 #include <machine/autoconf.h>
48 #include <machine/pmap.h>
49 #include <machine/cpu.h>
50 #include <machine/conf.h>
51 #include <machine/openfirm.h>
52 
53 #include <dev/wscons/wsconsio.h>
54 #include <dev/wscons/wsdisplayvar.h>
55 #include <dev/rasops/rasops.h>
56 #include <machine/fbvar.h>
57 
58 #include <dev/sbus/sbusvar.h>
59 
60 #include <dev/ic/bt463reg.h>
61 
62 /*
63  * Configuration structure
64  */
65 struct rfx_config {
66 	u_int16_t	unknown;
67 	u_int16_t	version;
68 	u_int32_t	scanline;
69 	u_int32_t	maxwidth;	/* unsure */
70 	u_int32_t	maxheight;	/* unsure */
71 	u_int32_t	width;
72 	u_int32_t	height;
73 };
74 
75 /*
76  * In-memory offsets
77  */
78 
79 #define	RFX_RAMDAC_ADDR		0x00020000
80 #define	RFX_RAMDAC_SIZE		0x00000004
81 
82 #define	RFX_CONTROL_ADDR	0x00040000
83 #define	RFX_CONTROL_SIZE	0x000000e0
84 
85 #define	RFX_INIT_ADDR		0x00018000
86 #define	RFX_INIT_OFFSET		0x0000001c
87 #define	RFX_INIT_SIZE		0x00008000
88 
89 #define	RFX_VRAM_ADDR		0x00100000
90 
91 /*
92  * Control registers
93  */
94 
95 #define	RFX_VIDCTRL_REG		0x10
96 #define	RFX_VSYNC_ENABLE	0x00000001
97 #define	RFX_VIDEO_DISABLE	0x00000002
98 
99 /*
100  * Shadow colormap
101  */
102 struct rfx_cmap {
103 	u_int8_t	red[256];
104 	u_int8_t	green[256];
105 	u_int8_t	blue[256];
106 };
107 
108 struct rfx_softc {
109 	struct	sunfb		 sc_sunfb;
110 
111 	bus_space_tag_t		 sc_bustag;
112 	bus_addr_t		 sc_paddr;
113 
114 	struct	intrhand	 sc_ih;
115 
116 	struct rfx_cmap		 sc_cmap;
117 	volatile u_int8_t	*sc_ramdac;
118 	volatile u_int32_t	*sc_ctrl;
119 
120 	int			 sc_nscreens;
121 };
122 
123 void	rfx_burner(void *, u_int, u_int);
124 int	rfx_ioctl(void *, u_long, caddr_t, int, struct proc *);
125 paddr_t	rfx_mmap(void *, off_t, int);
126 
127 int	rfx_getcmap(struct rfx_cmap *, struct wsdisplay_cmap *);
128 int	rfx_initialize(struct rfx_softc *, struct sbus_attach_args *,
129 	    struct rfx_config *);
130 int	rfx_intr(void *);
131 void	rfx_loadcmap(struct rfx_softc *, int, int);
132 int	rfx_putcmap(struct rfx_cmap *, struct wsdisplay_cmap *);
133 void	rfx_setcolor(void *, u_int, u_int8_t, u_int8_t, u_int8_t);
134 
135 struct wsdisplay_accessops rfx_accessops = {
136 	rfx_ioctl,
137 	rfx_mmap,
138 	NULL,	/* alloc_screen */
139 	NULL,	/* free_screen */
140 	NULL,	/* show_screen */
141 	NULL,	/* load_font */
142 	NULL,	/* scrollback */
143 	NULL,	/* getchar */
144 	rfx_burner,
145 };
146 
147 int	rfxmatch(struct device *, void *, void *);
148 void	rfxattach(struct device *, struct device *, void *);
149 
150 #if defined(OpenBSD)
151 struct cfattach rfx_ca = {
152 	sizeof (struct rfx_softc), rfxmatch, rfxattach
153 };
154 
155 struct cfdriver rfx_cd = {
156 	NULL, "rfx", DV_DULL
157 };
158 #else
159 CFATTACH_DECL(rfx, sizeof (struct rfx_softc), rfxmatch, rfxattach, NULL, NULL);
160 #endif
161 
162 /*
163  * Match a supported RasterFlex card.
164  */
165 int
166 rfxmatch(struct device *parent, void *vcf, void *aux)
167 {
168 	struct sbus_attach_args *sa = aux;
169 	const char *device = sa->sa_name;
170 
171 	/* skip vendor name (could be CWARE, VITec, ...) */
172 	while (*device != ',' && *device != '\0')
173 		device++;
174 	if (*device == '\0')
175 		device = sa->sa_name;
176 	else
177 		device++;
178 
179 	if (strncmp(device, "RasterFLEX", strlen("RasterFLEX")) != 0)
180 		return (0);
181 
182 	/* RasterVideo and RasterFlex-TV are frame grabbers */
183 	if (strcmp(device, "RasterFLEX-TV") == 0)
184 		return (0);
185 
186 	return (1);
187 }
188 
189 /*
190  * Attach and initialize a rfx display, as well as a child wsdisplay.
191  */
192 void
193 rfxattach(struct device *parent, struct device *self, void *args)
194 {
195 	struct rfx_softc *sc = (struct rfx_softc *)self;
196 	struct sbus_attach_args *sa = args;
197 	const char *device = sa->sa_name;
198 	struct rfx_config cf;
199 	bus_space_tag_t bt;
200 	bus_space_handle_t bh;
201 	int node, cflen, isconsole = 0;
202 
203 	/* skip vendor name (could be CWARE, VITec, ...) */
204 	while (*device != ',' && *device != '\0')
205 		device++;
206 	if (*device == '\0')
207 		device = sa->sa_name;
208 	else
209 		device++;
210 
211 	printf(": %s", device);
212 
213 	if (sa->sa_nreg == 0) {
214 		printf("\n%s: no SBus registers!\n", self->dv_xname);
215 		return;
216 	}
217 
218 	bt = sa->sa_bustag;
219 	node = sa->sa_node;
220 	isconsole = node == fbnode;
221 
222 	/*
223 	 * Parse configuration structure
224 	 */
225 	cflen = getproplen(node, "configuration");
226 	if (cflen != sizeof cf) {
227 		printf(", unknown %d bytes conf. structure", cflen);
228 		/* fill in default values */
229 		cf.version = 0;
230 		cf.scanline = 2048;
231 		cf.width = 1152;
232 		cf.height = 900;
233 	} else {
234 		OF_getprop(node, "configuration", &cf, cflen);
235 		printf(", revision %d", cf.version);
236 	}
237 
238 	/*
239 	 * Map registers
240 	 */
241 
242 	sc->sc_bustag = bt;
243 	sc->sc_paddr = sbus_bus_addr(bt, sa->sa_slot, sa->sa_offset);
244 
245 	if (sbus_bus_map(bt, sa->sa_slot, sa->sa_offset + RFX_RAMDAC_ADDR,
246 	    RFX_RAMDAC_SIZE, BUS_SPACE_MAP_LINEAR, 0, &bh) != 0) {
247 		printf("\n%s: couldn't map ramdac registers\n", self->dv_xname);
248 		return;
249 	}
250 	sc->sc_ramdac = (u_int8_t *)bus_space_vaddr(bt, bh);
251 
252 	if (sbus_bus_map(bt, sa->sa_slot, sa->sa_offset + RFX_CONTROL_ADDR,
253 	    RFX_CONTROL_SIZE, BUS_SPACE_MAP_LINEAR, 0, &bh) != 0) {
254 		printf("\n%s: couldn't map control registers\n", self->dv_xname);
255 		return;
256 	}
257 	sc->sc_ctrl = (u_int32_t *)bus_space_vaddr(bt, bh);
258 
259 #if 0	/* not yet */
260 	sc->sc_ih.ih_fun = rfx_intr;
261 	sc->sc_ih.ih_arg = sc;
262 	intr_establish(ca->ca_ra.ra_intr[0].int_pri, &sc->sc_ih, IPL_FB);
263 #endif
264 
265 	/*
266 	 * The following is an equivalent for
267 	 *   fb_setsize(&sc->sc_sunfb, 8, cf.width, cf.height,
268 	 *     node, ca->ca_bustype);
269 	 * forcing the correct scan line value. Since the usual frame buffer
270 	 * properties are missing on this card, no need to go through
271 	 * fb_setsize()...
272 	 */
273 	sc->sc_sunfb.sf_depth = 8;
274 	sc->sc_sunfb.sf_width = cf.width;
275 	sc->sc_sunfb.sf_height = cf.height;
276 	sc->sc_sunfb.sf_linebytes = cf.scanline;
277 	sc->sc_sunfb.sf_fbsize = cf.height * cf.scanline;
278 
279 	printf(", %dx%d\n", sc->sc_sunfb.sf_width, sc->sc_sunfb.sf_height);
280 
281 	if (sbus_bus_map(bt, sa->sa_slot, sa->sa_offset + RFX_VRAM_ADDR,
282 	    round_page(sc->sc_sunfb.sf_fbsize), BUS_SPACE_MAP_LINEAR,
283 	    0, &bh) != 0) {
284 		printf("\n%s: couldn't map video memory\n", self->dv_xname);
285 		return;
286 	}
287 	sc->sc_sunfb.sf_ro.ri_bits = bus_space_vaddr(bt, bh);
288 	sc->sc_sunfb.sf_ro.ri_hw = sc;
289 
290 	/*
291 	 * If we are not the console, the frame buffer has not been
292 	 * initialized by the PROM - do this ourselves.
293 	 */
294 	if (!isconsole) {
295 		if (rfx_initialize(sc, sa, &cf) != 0)
296 			return;
297 	}
298 
299 	fbwscons_init(&sc->sc_sunfb, 0, isconsole);
300 
301 	bzero(&sc->sc_cmap, sizeof(sc->sc_cmap));
302 	fbwscons_setcolormap(&sc->sc_sunfb, rfx_setcolor);
303 
304 	if (isconsole)
305 		fbwscons_console_init(&sc->sc_sunfb, -1);
306 
307 	/* enable video */
308 	rfx_burner(sc, 1, 0);
309 
310 	fbwscons_attach(&sc->sc_sunfb, &rfx_accessops, isconsole);
311 }
312 
313 /*
314  * Common wsdisplay operations
315  */
316 
317 int
318 rfx_ioctl(void *v, u_long cmd, caddr_t data, int flags, struct proc *p)
319 {
320 	struct rfx_softc *sc = v;
321 	struct wsdisplay_cmap *cm;
322 	struct wsdisplay_fbinfo *wdf;
323 	int error;
324 
325 	switch (cmd) {
326 	case WSDISPLAYIO_GTYPE:
327 		*(u_int *)data = WSDISPLAY_TYPE_RFLEX;
328 		break;
329 	case WSDISPLAYIO_GINFO:
330 		wdf = (struct wsdisplay_fbinfo *)data;
331 		wdf->height = sc->sc_sunfb.sf_height;
332 		wdf->width  = sc->sc_sunfb.sf_width;
333 		wdf->depth  = sc->sc_sunfb.sf_depth;
334 		wdf->cmsize = 256;
335 		break;
336 	case WSDISPLAYIO_LINEBYTES:
337 		*(u_int *)data = sc->sc_sunfb.sf_linebytes;
338 		break;
339 
340 	case WSDISPLAYIO_GETCMAP:
341 		cm = (struct wsdisplay_cmap *)data;
342 		error = rfx_getcmap(&sc->sc_cmap, cm);
343 		if (error != 0)
344 			return (error);
345 		break;
346 	case WSDISPLAYIO_PUTCMAP:
347 		cm = (struct wsdisplay_cmap *)data;
348 		error = rfx_putcmap(&sc->sc_cmap, cm);
349 		if (error != 0)
350 			return (error);
351 		rfx_loadcmap(sc, cm->index, cm->count);
352 		break;
353 
354 	case WSDISPLAYIO_SVIDEO:
355 	case WSDISPLAYIO_GVIDEO:
356 		break;
357 
358 	default:
359 		return (-1);
360         }
361 
362 	return (0);
363 }
364 
365 paddr_t
366 rfx_mmap(void *v, off_t offset, int prot)
367 {
368 	struct rfx_softc *sc = v;
369 
370 	if (offset & PGOFSET)
371 		return (-1);
372 
373 	if (offset >= 0 && offset < sc->sc_sunfb.sf_fbsize) {
374 		return (bus_space_mmap(sc->sc_bustag, sc->sc_paddr,
375 		    RFX_VRAM_ADDR + offset, prot, BUS_SPACE_MAP_LINEAR));
376 	}
377 
378 	return (-1);
379 }
380 
381 void
382 rfx_burner(void *v, u_int on, u_int flags)
383 {
384 	struct rfx_softc *sc = v;
385 
386 	if (on) {
387 		sc->sc_ctrl[RFX_VIDCTRL_REG] &= ~RFX_VIDEO_DISABLE;
388 		sc->sc_ctrl[RFX_VIDCTRL_REG] |= RFX_VSYNC_ENABLE;
389 	} else {
390 		sc->sc_ctrl[RFX_VIDCTRL_REG] |= RFX_VIDEO_DISABLE;
391 		if (flags & WSDISPLAY_BURN_VBLANK)
392 			sc->sc_ctrl[RFX_VIDCTRL_REG] &= ~RFX_VSYNC_ENABLE;
393 	}
394 }
395 
396 /*
397  * Colormap helper functions
398  */
399 
400 void
401 rfx_setcolor(void *v, u_int index, u_int8_t r, u_int8_t g, u_int8_t b)
402 {
403 	struct rfx_softc *sc = v;
404 
405 	sc->sc_cmap.red[index] = r;
406 	sc->sc_cmap.green[index] = g;
407 	sc->sc_cmap.blue[index] = b;
408 
409 	rfx_loadcmap(sc, index, 1);
410 }
411 
412 int
413 rfx_getcmap(struct rfx_cmap *cm, struct wsdisplay_cmap *rcm)
414 {
415 	u_int index = rcm->index, count = rcm->count;
416 	int error;
417 
418 	if (index >= 256 || count > 256 - index)
419 		return (EINVAL);
420 
421 	if ((error = copyout(cm->red + index, rcm->red, count)) != 0)
422 		return (error);
423 	if ((error = copyout(cm->green + index, rcm->green, count)) != 0)
424 		return (error);
425 	if ((error = copyout(cm->blue + index, rcm->blue, count)) != 0)
426 		return (error);
427 
428 	return (0);
429 }
430 
431 int
432 rfx_putcmap(struct rfx_cmap *cm, struct wsdisplay_cmap *rcm)
433 {
434 	u_int index = rcm->index, count = rcm->count;
435 	u_int8_t red[256], green[256], blue[256];
436 	int error;
437 
438 	if (index >= 256 || count > 256 - index)
439 		return (EINVAL);
440 
441 	if ((error = copyin(rcm->red, red, count)) != 0)
442 		return (error);
443 	if ((error = copyin(rcm->green, green, count)) != 0)
444 		return (error);
445 	if ((error = copyin(rcm->blue, blue, count)) != 0)
446 		return (error);
447 
448 	bcopy(red, cm->red + index, count);
449 	bcopy(green, cm->green + index, count);
450 	bcopy(blue, cm->blue + index, count);
451 
452 	return (0);
453 }
454 
455 void
456 rfx_loadcmap(struct rfx_softc *sc, int start, int ncolors)
457 {
458 	u_int8_t *r, *g, *b;
459 
460 	r = sc->sc_cmap.red + start;
461 	g = sc->sc_cmap.green + start;
462 	b = sc->sc_cmap.blue + start;
463 
464 	start += BT463_IREG_CPALETTE_RAM;
465 	sc->sc_ramdac[BT463_REG_ADDR_LOW] = start & 0xff;
466 	sc->sc_ramdac[BT463_REG_ADDR_HIGH] = (start >> 8) & 0xff;
467 
468 	while (ncolors-- != 0) {
469 		sc->sc_ramdac[BT463_REG_CMAP_DATA] = *r++;
470 		sc->sc_ramdac[BT463_REG_CMAP_DATA] = *g++;
471 		sc->sc_ramdac[BT463_REG_CMAP_DATA] = *b++;
472 	}
473 }
474 
475 /*
476  * Initialization code parser
477  */
478 
479 int
480 rfx_initialize(struct rfx_softc *sc, struct sbus_attach_args *sa,
481     struct rfx_config *cf)
482 {
483 	u_int32_t *data, offset, value;
484 	size_t cnt;
485 	bus_space_handle_t bh;
486 	int error;
487 
488 	/*
489 	 * Map the initialization data
490 	 */
491 	if ((error = sbus_bus_map(sa->sa_bustag, sa->sa_slot, sa->sa_offset +
492 	    RFX_INIT_ADDR, RFX_INIT_SIZE, BUS_SPACE_MAP_LINEAR, 0, &bh)) != 0) {
493 		printf("\n%s: couldn't map initialization data\n",
494 		    sc->sc_sunfb.sf_dev.dv_xname);
495 		return error;
496 	}
497 	data = (u_int32_t *)bus_space_vaddr(sa->sa_bustag, bh);
498 
499 	/*
500 	 * Skip copyright notice
501 	 */
502 	data += RFX_INIT_OFFSET / sizeof(u_int32_t);
503 	cnt = (RFX_INIT_SIZE - RFX_INIT_OFFSET) / sizeof(u_int32_t);
504 	cnt >>= 1;
505 
506 	/*
507 	 * Parse and apply settings
508 	 */
509 	while (cnt != 0) {
510 		offset = *data++;
511 		value = *data++;
512 
513 		if (offset == (u_int32_t)-1 && value == (u_int32_t)-1)
514 			break;
515 
516 		/* Old PROM are little-endian */
517 		if (cf->version <= 1) {
518 			offset = letoh32(offset);
519 			value = letoh32(offset);
520 		}
521 
522 		if (offset & (1 << 31)) {
523 			offset = (offset & ~(1 << 31)) - RFX_RAMDAC_ADDR;
524 			if (offset < RFX_RAMDAC_SIZE)
525 				sc->sc_ramdac[offset] = value >> 24;
526 		} else {
527 			offset -= RFX_CONTROL_ADDR;
528 			if (offset < RFX_CONTROL_SIZE)
529 				sc->sc_ctrl[offset >> 2] = value;
530 		}
531 
532 		cnt--;
533 	}
534 
535 #ifdef DEBUG
536 	if (cnt != 0)
537 		printf("%s: incoherent initialization data!\n");
538 #endif
539 
540 	bus_space_unmap(sa->sa_bustag, bh, RFX_INIT_SIZE);
541 
542 	return 0;
543 }
544