1/*	$OpenBSD: tvtwo.c,v 1.18 2022/07/15 17:57:27 kettenis 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 */
115struct 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
128int	tvtwo_ioctl(void *, u_long, caddr_t, int, struct proc *);
129paddr_t	tvtwo_mmap(void *, off_t, int);
130void	tvtwo_burner(void *, u_int, u_int);
131
132struct wsdisplay_accessops tvtwo_accessops = {
133	.ioctl = tvtwo_ioctl,
134	.mmap = tvtwo_mmap,
135	.burn_screen = tvtwo_burner
136};
137
138void	tvtwo_directcmap(struct tvtwo_softc *);
139static __inline__
140void	tvtwo_ramdac_wraddr(struct tvtwo_softc *, u_int32_t);
141void	tvtwo_reset(struct tvtwo_softc *, u_int);
142void	tvtwo_setcolor(void *, u_int, u_int8_t, u_int8_t, u_int8_t);
143
144int	tvtwomatch(struct device *, void *, void *);
145void	tvtwoattach(struct device *, struct device *, void *);
146
147const struct cfattach tvtwo_ca = {
148	sizeof(struct tvtwo_softc), tvtwomatch, tvtwoattach
149};
150
151struct 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
159const int defwidth[NFREQCODE] = { 1152, 1152, 1152, 1024, 640 };
160const int defheight[NFREQCODE] = { 900, 900, 900, 768, 480 };
161
162/*
163 * Match an XVideo or PowerVideo card.
164 */
165int
166tvtwomatch(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 */
180void
181tvtwoattach(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
297int
298tvtwo_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 advertise 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->stride = sc->sc_sunfb.sf_linebytes * 4;
317		wdf->offset = 0;
318		wdf->cmsize = 0;
319		break;
320	case WSDISPLAYIO_GETSUPPORTEDDEPTH:
321		*(u_int *)data = WSDISPLAYIO_DEPTH_24_32;
322		break;
323	case WSDISPLAYIO_LINEBYTES:
324		*(u_int *)data = sc->sc_sunfb.sf_linebytes * 4;
325		break;
326
327	case WSDISPLAYIO_GETCMAP:
328	case WSDISPLAYIO_PUTCMAP:
329		break;
330
331	case WSDISPLAYIO_SMODE:
332		if (*(int *)data == WSDISPLAYIO_MODE_EMUL) {
333			/* Back from X11 to text mode */
334			tvtwo_reset(sc, 8);
335		} else {
336			/* Starting X11, initialize 32-bit mode */
337			tvtwo_reset(sc, 32);
338		}
339		break;
340
341	case WSDISPLAYIO_SVIDEO:
342	case WSDISPLAYIO_GVIDEO:
343		break;
344
345	case WSDISPLAYIO_GCURPOS:
346	case WSDISPLAYIO_SCURPOS:
347	case WSDISPLAYIO_GCURMAX:
348	case WSDISPLAYIO_GCURSOR:
349	case WSDISPLAYIO_SCURSOR:
350	default:
351		return (-1);
352	}
353
354	return (0);
355}
356
357/*
358 * Return the address that would map the given device at the given
359 * offset, allowing for the given protection, or return -1 for error.
360 */
361paddr_t
362tvtwo_mmap(void *v, off_t offset, int prot)
363{
364	struct tvtwo_softc *sc = v;
365
366	if (offset & PGOFSET)
367		return (-1);
368
369	/* Allow mapping as a dumb framebuffer from offset 0 */
370	if (offset >= 0 && offset < sc->sc_sunfb.sf_fbsize * 4) {
371		return (bus_space_mmap(sc->sc_bustag, sc->sc_paddr,
372		    PX_PLANE24_OFFSET + offset, prot, BUS_SPACE_MAP_LINEAR));
373	}
374
375	return (-1);
376}
377
378void
379tvtwo_burner(void *v, u_int on, u_int flags)
380{
381	struct tvtwo_softc *sc = v;
382	u_int32_t dispkluge;
383
384	if (on)
385		dispkluge = DISPKLUGE_DEFAULT & ~DISPKLUGE_BLANK;
386	else {
387		dispkluge = DISPKLUGE_DEFAULT | DISPKLUGE_BLANK;
388		if (flags & WSDISPLAY_BURN_VBLANK)
389			dispkluge |= DISPKLUGE_SYNC;
390	}
391
392	*(volatile u_int32_t *)(sc->sc_regs + PX_REG_DISPKLUGE) = dispkluge;
393}
394
395void
396tvtwo_reset(struct tvtwo_softc *sc, u_int depth)
397{
398	if (depth == 32) {
399		/* Initialize a direct color map. */
400		tvtwo_directcmap(sc);
401	} else {
402		fbwscons_setcolormap(&sc->sc_sunfb, tvtwo_setcolor);
403	}
404}
405
406/*
407 * Simple Bt463 programming routines.
408 */
409
410static __inline__ void
411tvtwo_ramdac_wraddr(struct tvtwo_softc *sc, u_int32_t addr)
412{
413	volatile u_int32_t *dac = (u_int32_t *)(sc->sc_regs + PX_REG_BT463_RED);
414
415	dac[0] = (addr & 0xff);		/* lo addr */
416	dac[1] = ((addr >> 8) & 0xff);	/* hi addr */
417}
418
419void
420tvtwo_directcmap(struct tvtwo_softc *sc)
421{
422	volatile u_int32_t *dac = (u_int32_t *)(sc->sc_regs + PX_REG_BT463_RED);
423	u_int32_t c;
424
425	tvtwo_ramdac_wraddr(sc, 0);
426	for (c = 0; c < 256; c++) {
427		dac[3] = c;	/* R */
428		dac[3] = c;	/* G */
429		dac[3] = c;	/* B */
430	}
431}
432
433void
434tvtwo_setcolor(void *v, u_int index, u_int8_t r, u_int8_t g, u_int8_t b)
435{
436	struct tvtwo_softc *sc = v;
437	volatile u_int32_t *dac = (u_int32_t *)(sc->sc_regs + PX_REG_BT463_RED);
438
439	tvtwo_ramdac_wraddr(sc, index);
440	dac[3] = r;
441	dac[3] = g;
442	dac[3] = b;
443}
444