xref: /openbsd/sys/dev/sbus/tvtwo.c (revision 09467b48)
1 /*	$OpenBSD: tvtwo.c,v 1.15 2013/10/20 20:07:31 miod Exp $	*/
2 
3 /*
4  * Copyright (c) 2003, 2006, 2008, 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 Parallax XVideo and PowerVideo graphics boards.
32  *
33  * Some details about these board used to be available at:
34  *   http://www.jlw.com/~woolsey/parallax/support/developers/xvideotech.html
35  */
36 
37 /*
38  * The Parallax XVideo series frame buffers are 8/24-bit accelerated
39  * frame buffers, with hardware MPEG capabilities using a CCube chipset.
40  */
41 
42 /*
43  * Currently, this driver can only handle the 8-bit and 24-bit planes of the
44  * frame buffer, in an unaccelerated mode.
45  *
46  * TODO:
47  * - nvram handling
48  * - use the accelerator
49  * - interface to the c^3
50  */
51 
52 #include <sys/param.h>
53 #include <sys/systm.h>
54 #include <sys/buf.h>
55 #include <sys/device.h>
56 #include <sys/ioctl.h>
57 #include <sys/mman.h>
58 #include <sys/conf.h>
59 
60 #include <uvm/uvm_extern.h>
61 
62 #include <machine/autoconf.h>
63 #include <machine/bus.h>
64 #include <machine/pmap.h>
65 #include <machine/cpu.h>
66 #include <machine/conf.h>
67 
68 #include <dev/wscons/wsconsio.h>
69 #include <dev/wscons/wsdisplayvar.h>
70 #include <dev/rasops/rasops.h>
71 #include <machine/fbvar.h>
72 
73 #include <dev/sbus/sbusvar.h>
74 
75 /*
76  * The memory layout of the board is as follows:
77  *
78  *	PROM0		000000 - 00ffff
79  *	overlay plane	010000 - 037fff
80  *	registers	040000 - 0404d0
81  *	CCube		050000 - 05ffff
82  *	8-bit plane	080000 - 17ffff
83  *	24-bit plane	200000 - 6fffff
84  *	PROM1		7f0000 - 7fffff
85  */
86 
87 #define	PX_PROM0_OFFSET		0x000000
88 #define	PX_OVERLAY_OFFSET	0x010000
89 #define	PX_REG_OFFSET		0x040000
90 #define	PX_CCUBE_OFFSET		0x050000
91 #define	PX_PLANE8_OFFSET	0x080000
92 #define	PX_PLANE24_OFFSET	0x200000
93 #define	PX_PROM1_OFFSET		0x7f0000
94 
95 #define	PX_MAP_SIZE		0x800000
96 
97 /*
98  * Partial registers layout
99  */
100 
101 #define	PX_REG_DISPKLUGE	0x00b8	/* write only */
102 #define	DISPKLUGE_DEFAULT	0xc41f
103 #define	DISPKLUGE_BLANK		(1 << 12)
104 #define	DISPKLUGE_SYNC		(1 << 13)
105 
106 #define	PX_REG_BT463_RED	0x0480
107 #define	PX_REG_BT463_GREEN	0x0490
108 #define	PX_REG_BT463_BLUE	0x04a0
109 #define	PX_REG_BT463_ALL	0x04b0
110 
111 #define	PX_REG_SIZE		0x04d0
112 
113 
114 /* per-display variables */
115 struct tvtwo_softc {
116 	struct	sunfb	sc_sunfb;	/* common base device */
117 
118 	bus_space_tag_t	sc_bustag;
119 	bus_addr_t	sc_paddr;
120 
121 	volatile u_int8_t *sc_m8;
122 	volatile u_int8_t *sc_m24;
123 	volatile u_int8_t *sc_regs;
124 
125 	int	sc_nscreens;
126 };
127 
128 int	tvtwo_ioctl(void *, u_long, caddr_t, int, struct proc *);
129 paddr_t	tvtwo_mmap(void *, off_t, int);
130 void	tvtwo_burner(void *, u_int, u_int);
131 
132 struct wsdisplay_accessops tvtwo_accessops = {
133 	.ioctl = tvtwo_ioctl,
134 	.mmap = tvtwo_mmap,
135 	.burn_screen = tvtwo_burner
136 };
137 
138 void	tvtwo_directcmap(struct tvtwo_softc *);
139 static __inline__
140 void	tvtwo_ramdac_wraddr(struct tvtwo_softc *, u_int32_t);
141 void	tvtwo_reset(struct tvtwo_softc *, u_int);
142 void	tvtwo_setcolor(void *, u_int, u_int8_t, u_int8_t, u_int8_t);
143 
144 int	tvtwomatch(struct device *, void *, void *);
145 void	tvtwoattach(struct device *, struct device *, void *);
146 
147 struct cfattach tvtwo_ca = {
148 	sizeof(struct tvtwo_softc), tvtwomatch, tvtwoattach
149 };
150 
151 struct cfdriver tvtwo_cd = {
152 	NULL, "tvtwo", DV_DULL
153 };
154 
155 /*
156  * Default frame buffer resolution, depending upon the "freqcode"
157  */
158 #define	NFREQCODE	5
159 const int defwidth[NFREQCODE] = { 1152, 1152, 1152, 1024, 640 };
160 const int defheight[NFREQCODE] = { 900, 900, 900, 768, 480 };
161 
162 /*
163  * Match an XVideo or PowerVideo card.
164  */
165 int
166 tvtwomatch(struct device *parent, void *vcf, void *aux)
167 {
168 	struct sbus_attach_args *sa = aux;
169 
170 	if (strcmp(sa->sa_name, "PGI,tvtwo") == 0 ||
171 	    strcmp(sa->sa_name, "PGI,tvthree") == 0)
172 		return (1);
173 
174 	return (0);
175 }
176 
177 /*
178  * Attach a display.
179  */
180 void
181 tvtwoattach(struct device *parent, struct device *self, void *args)
182 {
183 	struct tvtwo_softc *sc = (struct tvtwo_softc *)self;
184 	struct sbus_attach_args *sa = args;
185 	bus_space_tag_t bt;
186 	bus_space_handle_t bh;
187 	int node, width, height, freqcode;
188 	int isconsole;
189 	char *freqstring, *revision;
190 
191 	bt = sa->sa_bustag;
192 	node = sa->sa_node;
193 
194 	printf(": %s", getpropstring(node, "model"));
195 	revision = getpropstring(node, "revision");
196 	if (*revision != '\0')
197 		printf(", revision %s", revision);
198 
199 	/* Older XVideo provide two sets of SBus registers:
200 	 *	R0		040000 - 040800
201 	 *	R1		080000 - 17d200
202 	 * While the more recent revisions provide only one register:
203 	 *	R0		000000 - 7fffff
204 	 *
205 	 * We'll simply ``rewrite'' R0 on older boards and handle them as
206 	 * recent boards.
207 	 */
208 	if (sa->sa_nreg > 1) {
209 		sa->sa_offset -= PX_REG_OFFSET;
210 		sa->sa_size = PX_MAP_SIZE;
211 	}
212 
213 	isconsole = node == fbnode;
214 
215 	/* Map registers. */
216 	sc->sc_bustag = bt;
217 	if (sbus_bus_map(bt, sa->sa_slot, sa->sa_offset + PX_REG_OFFSET,
218 	    PX_REG_SIZE, BUS_SPACE_MAP_LINEAR, 0, &bh) != 0) {
219 		printf("%s: couldn't map registers\n", self->dv_xname);
220 		return;
221 	}
222 	sc->sc_regs = bus_space_vaddr(bt, bh);
223 
224 	/*
225 	 * Compute framebuffer size.
226 	 * Older boards do not have the ``freqcode'' property and are
227 	 * restricted to 1152x900.
228 	 */
229 	freqstring = getpropstring(node, "freqcode");
230 	if (*freqstring != '\0') {
231 		freqcode = (int)*freqstring;
232 		if (freqcode == 'g') {
233 			width = height = 1024;
234 		} else {
235 			if (freqcode < '1' || freqcode > '6')
236 				freqcode = 0;
237 			else
238 				freqcode -= '1';
239 			width = defwidth[freqcode];
240 			height = defheight[freqcode];
241 
242 			/* in case our table is wrong or incomplete... */
243 			width = getpropint(node, "hres", width);
244 			height = getpropint(node, "vres", height);
245 		}
246 	} else {
247 		width = 1152;
248 		height = 900;
249 	}
250 
251 	/*
252 	 * Since the depth property is missing, we could do
253 	 * fb_setsize(&sc->sc_sunfb, 8, width, height, node, 0);
254 	 * but for safety in case it would exist and be set to 32, do it
255 	 * manually...
256 	 */
257 	sc->sc_sunfb.sf_depth = 8;
258 	sc->sc_sunfb.sf_width = width;
259 	sc->sc_sunfb.sf_height = height;
260 	sc->sc_sunfb.sf_linebytes = width >= 1024 ? width : 1024;
261 	sc->sc_sunfb.sf_fbsize = sc->sc_sunfb.sf_linebytes * height;
262 
263 	printf(", %dx%d\n", sc->sc_sunfb.sf_width, sc->sc_sunfb.sf_height);
264 
265 	/* Map the frame buffer memory area we're interested in. */
266 	sc->sc_paddr = sbus_bus_addr(bt, sa->sa_slot, sa->sa_offset);
267 	if (sbus_bus_map(bt, sa->sa_slot, sa->sa_offset + PX_PLANE8_OFFSET,
268 	    round_page(sc->sc_sunfb.sf_fbsize), BUS_SPACE_MAP_LINEAR, 0,
269 	    &bh) != 0) {
270 		printf("%s: couldn't map 8-bit video plane\n", self->dv_xname);
271 		return;
272 	}
273 	sc->sc_m8 = bus_space_vaddr(bt, bh);
274 	if (sbus_bus_map(bt, sa->sa_slot, sa->sa_offset + PX_PLANE24_OFFSET,
275 	    round_page(4 * sc->sc_sunfb.sf_fbsize), BUS_SPACE_MAP_LINEAR, 0,
276 	    &bh) != 0) {
277 		printf("%s: couldn't map 32-bit video plane\n", self->dv_xname);
278 		return;
279 	}
280 	sc->sc_m24 = bus_space_vaddr(bt, bh);
281 
282 	/* Enable video. */
283 	tvtwo_burner(sc, 1, 0);
284 
285 	sc->sc_sunfb.sf_ro.ri_hw = sc;
286 	sc->sc_sunfb.sf_ro.ri_bits = (u_char *)sc->sc_m8;
287 
288 	fbwscons_init(&sc->sc_sunfb, 0, isconsole);
289 	fbwscons_setcolormap(&sc->sc_sunfb, tvtwo_setcolor);
290 
291 	if (isconsole)
292 		fbwscons_console_init(&sc->sc_sunfb, -1);
293 
294 	fbwscons_attach(&sc->sc_sunfb, &tvtwo_accessops, isconsole);
295 }
296 
297 int
298 tvtwo_ioctl(void *dev, u_long cmd, caddr_t data, int flags, struct proc *p)
299 {
300 	struct tvtwo_softc *sc = dev;
301 	struct wsdisplay_fbinfo *wdf;
302 
303 	/*
304 	 * Note that, although the emulation (text) mode is running in a
305 	 * 8-bit plane, we advertize the frame buffer as 32-bit.
306 	 */
307 	switch (cmd) {
308 	case WSDISPLAYIO_GTYPE:
309 		*(u_int *)data = WSDISPLAY_TYPE_XVIDEO;
310 		break;
311 	case WSDISPLAYIO_GINFO:
312 		wdf = (struct wsdisplay_fbinfo *)data;
313 		wdf->height = sc->sc_sunfb.sf_height;
314 		wdf->width = sc->sc_sunfb.sf_width;
315 		wdf->depth = 32;
316 		wdf->cmsize = 0;
317 		break;
318 	case WSDISPLAYIO_GETSUPPORTEDDEPTH:
319 		*(u_int *)data = WSDISPLAYIO_DEPTH_24_32;
320 		break;
321 	case WSDISPLAYIO_LINEBYTES:
322 		*(u_int *)data = sc->sc_sunfb.sf_linebytes * 4;
323 		break;
324 
325 	case WSDISPLAYIO_GETCMAP:
326 	case WSDISPLAYIO_PUTCMAP:
327 		break;
328 
329 	case WSDISPLAYIO_SMODE:
330 		if (*(int *)data == WSDISPLAYIO_MODE_EMUL) {
331 			/* Back from X11 to text mode */
332 			tvtwo_reset(sc, 8);
333 		} else {
334 			/* Starting X11, initialize 32-bit mode */
335 			tvtwo_reset(sc, 32);
336 		}
337 		break;
338 
339 	case WSDISPLAYIO_SVIDEO:
340 	case WSDISPLAYIO_GVIDEO:
341 		break;
342 
343 	case WSDISPLAYIO_GCURPOS:
344 	case WSDISPLAYIO_SCURPOS:
345 	case WSDISPLAYIO_GCURMAX:
346 	case WSDISPLAYIO_GCURSOR:
347 	case WSDISPLAYIO_SCURSOR:
348 	default:
349 		return (-1);
350 	}
351 
352 	return (0);
353 }
354 
355 /*
356  * Return the address that would map the given device at the given
357  * offset, allowing for the given protection, or return -1 for error.
358  */
359 paddr_t
360 tvtwo_mmap(void *v, off_t offset, int prot)
361 {
362 	struct tvtwo_softc *sc = v;
363 
364 	if (offset & PGOFSET)
365 		return (-1);
366 
367 	/* Allow mapping as a dumb framebuffer from offset 0 */
368 	if (offset >= 0 && offset < sc->sc_sunfb.sf_fbsize * 4) {
369 		return (bus_space_mmap(sc->sc_bustag, sc->sc_paddr,
370 		    PX_PLANE24_OFFSET + offset, prot, BUS_SPACE_MAP_LINEAR));
371 	}
372 
373 	return (-1);
374 }
375 
376 void
377 tvtwo_burner(void *v, u_int on, u_int flags)
378 {
379 	struct tvtwo_softc *sc = v;
380 	u_int32_t dispkluge;
381 
382 	if (on)
383 		dispkluge = DISPKLUGE_DEFAULT & ~DISPKLUGE_BLANK;
384 	else {
385 		dispkluge = DISPKLUGE_DEFAULT | DISPKLUGE_BLANK;
386 		if (flags & WSDISPLAY_BURN_VBLANK)
387 			dispkluge |= DISPKLUGE_SYNC;
388 	}
389 
390 	*(volatile u_int32_t *)(sc->sc_regs + PX_REG_DISPKLUGE) = dispkluge;
391 }
392 
393 void
394 tvtwo_reset(struct tvtwo_softc *sc, u_int depth)
395 {
396 	if (depth == 32) {
397 		/* Initialize a direct color map. */
398 		tvtwo_directcmap(sc);
399 	} else {
400 		fbwscons_setcolormap(&sc->sc_sunfb, tvtwo_setcolor);
401 	}
402 }
403 
404 /*
405  * Simple Bt463 programming routines.
406  */
407 
408 static __inline__ void
409 tvtwo_ramdac_wraddr(struct tvtwo_softc *sc, u_int32_t addr)
410 {
411 	volatile u_int32_t *dac = (u_int32_t *)(sc->sc_regs + PX_REG_BT463_RED);
412 
413 	dac[0] = (addr & 0xff);		/* lo addr */
414 	dac[1] = ((addr >> 8) & 0xff);	/* hi addr */
415 }
416 
417 void
418 tvtwo_directcmap(struct tvtwo_softc *sc)
419 {
420 	volatile u_int32_t *dac = (u_int32_t *)(sc->sc_regs + PX_REG_BT463_RED);
421 	u_int32_t c;
422 
423 	tvtwo_ramdac_wraddr(sc, 0);
424 	for (c = 0; c < 256; c++) {
425 		dac[3] = c;	/* R */
426 		dac[3] = c;	/* G */
427 		dac[3] = c;	/* B */
428 	}
429 }
430 
431 void
432 tvtwo_setcolor(void *v, u_int index, u_int8_t r, u_int8_t g, u_int8_t b)
433 {
434 	struct tvtwo_softc *sc = v;
435 	volatile u_int32_t *dac = (u_int32_t *)(sc->sc_regs + PX_REG_BT463_RED);
436 
437 	tvtwo_ramdac_wraddr(sc, index);
438 	dac[3] = r;
439 	dac[3] = g;
440 	dac[3] = b;
441 }
442