1 /*	$OpenBSD: mgx.c,v 1.11 2006/06/02 20:00:54 miod Exp $	*/
2 /*
3  * Copyright (c) 2003, Miodrag Vallat.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
19  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
23  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
24  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25  * POSSIBILITY OF SUCH DAMAGE.
26  *
27  */
28 
29 /*
30  * Driver for the Southland Media Systems (now Quantum 3D) MGX and MGXPlus
31  * frame buffers.
32  *
33  * Pretty crude, due to the lack of documentation. Works as a dumb frame
34  * buffer in 8 bit mode, although the hardware can run in an 32 bit
35  * accelerated mode. Also, interrupts are not handled.
36  */
37 
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/buf.h>
41 #include <sys/device.h>
42 #include <sys/ioctl.h>
43 #include <sys/malloc.h>
44 #include <sys/mman.h>
45 #include <sys/tty.h>
46 #include <sys/conf.h>
47 
48 #include <uvm/uvm_extern.h>
49 
50 #include <machine/autoconf.h>
51 #include <machine/pmap.h>
52 #include <machine/cpu.h>
53 #include <machine/conf.h>
54 
55 #include <dev/wscons/wsconsio.h>
56 #include <dev/wscons/wsdisplayvar.h>
57 #include <dev/rasops/rasops.h>
58 #include <machine/fbvar.h>
59 
60 #include <sparc/dev/sbusvar.h>
61 
62 /*
63  * MGX PROM register layout
64  */
65 
66 #define	MGX_NREG	9
67 #define	MGX_REG_CRTC	4	/* video control and ramdac */
68 #define	MGX_REG_CTRL	5	/* control engine */
69 #define	MGX_REG_VRAM8	8	/* 8-bit memory space */
70 
71 /*
72  * MGX CRTC empirical constants
73  */
74 #define	ADDRESS_REVERSE(x)	((x) ^ 0x03)
75 #define	CRTC_COMMAND	ADDRESS_REVERSE(0x03c4)
76 #define	CRTC_DATA	ADDRESS_REVERSE(0x03c5)
77 #define	CRTC_CMAP	ADDRESS_REVERSE(0x03c9)
78 #define	CD_DISABLEVIDEO	0x0020
79 
80 /* per-display variables */
81 struct mgx_softc {
82 	struct	sunfb	sc_sunfb;	/* common base device */
83 	struct	rom_reg sc_phys;
84 	u_int8_t	sc_cmap[256 * 3];	/* shadow colormap */
85 	volatile u_int8_t *sc_vidc;	/* ramdac registers */
86 };
87 
88 void	mgx_burner(void *, u_int ,u_int);
89 int	mgx_getcmap(u_int8_t *, struct wsdisplay_cmap *);
90 int	mgx_ioctl(void *, u_long, caddr_t, int, struct proc *);
91 void	mgx_loadcmap(struct mgx_softc *, int, int);
92 paddr_t	mgx_mmap(void *, off_t, int);
93 int	mgx_putcmap(u_int8_t *, struct wsdisplay_cmap *);
94 void	mgx_setcolor(void *, u_int, u_int8_t, u_int8_t, u_int8_t);
95 
96 struct wsdisplay_accessops mgx_accessops = {
97 	mgx_ioctl,
98 	mgx_mmap,
99 	NULL,	/* alloc_screen */
100 	NULL,	/* free_screen */
101 	NULL,	/* show_screen */
102 	NULL,	/* load_font */
103 	NULL,	/* scrollback */
104 	NULL,	/* getchar */
105 	mgx_burner,
106 	NULL	/* pollc */
107 };
108 
109 int	mgxmatch(struct device *, void *, void *);
110 void	mgxattach(struct device *, struct device *, void *);
111 
112 struct cfattach mgx_ca = {
113 	sizeof(struct mgx_softc), mgxmatch, mgxattach
114 };
115 
116 struct cfdriver mgx_cd = {
117 	NULL, "mgx", DV_DULL
118 };
119 
120 /*
121  * Match an MGX or MGX+ card.
122  */
123 int
mgxmatch(struct device * parent,void * vcf,void * aux)124 mgxmatch(struct device *parent, void *vcf, void *aux)
125 {
126 	struct confargs *ca = aux;
127 	struct romaux *ra = &ca->ca_ra;
128 
129 	if (strcmp(ra->ra_name, "SMSI,mgx") != 0 &&
130 	    strcmp(ra->ra_name, "mgx") != 0)
131 		return (0);
132 
133 	return (1);
134 }
135 
136 /*
137  * Attach an MGX frame buffer.
138  * This will keep the frame buffer in the actual PROM mode, and attach
139  * a wsdisplay child device to itself.
140  */
141 void
mgxattach(struct device * parent,struct device * self,void * args)142 mgxattach(struct device *parent, struct device *self, void *args)
143 {
144 	struct mgx_softc *sc = (struct mgx_softc *)self;
145 	struct confargs *ca = args;
146 	int node, fbsize;
147 	int isconsole;
148 
149 	node = ca->ca_ra.ra_node;
150 
151 	printf(": %s", getpropstring(node, "model"));
152 
153 	isconsole = node == fbnode;
154 
155 	/* Check registers */
156 	if (ca->ca_ra.ra_nreg < MGX_NREG) {
157 		printf("\n%s: expected %d registers, got %d\n",
158 		    self->dv_xname, MGX_NREG, ca->ca_ra.ra_nreg);
159 		return;
160 	}
161 
162 	sc->sc_vidc = (volatile u_int8_t *)mapiodev(
163 	    &ca->ca_ra.ra_reg[MGX_REG_CRTC], 0, PAGE_SIZE);
164 
165 	/* enable video */
166 	mgx_burner(sc, 1, 0);
167 
168 	fb_setsize(&sc->sc_sunfb, 8, 1152, 900, node, ca->ca_bustype);
169 
170 	/* Sanity check frame buffer memory */
171 	fbsize = getpropint(node, "fb_size", 0);
172 	if (fbsize != 0 && sc->sc_sunfb.sf_fbsize > fbsize) {
173 		printf("\n%s: expected at least %d bytes of vram, but card "
174 		    "only provides %d\n",
175 		    self->dv_xname, sc->sc_sunfb.sf_fbsize, fbsize);
176 		return;
177 	}
178 
179 	/* Map the frame buffer memory area we're interested in */
180 	sc->sc_phys = ca->ca_ra.ra_reg[MGX_REG_VRAM8];
181 	sc->sc_sunfb.sf_ro.ri_bits = mapiodev(&sc->sc_phys,
182 	    0, round_page(sc->sc_sunfb.sf_fbsize));
183 	sc->sc_sunfb.sf_ro.ri_hw = sc;
184 
185 	fbwscons_init(&sc->sc_sunfb, isconsole ? 0 : RI_CLEAR);
186 
187 	bzero(sc->sc_cmap, sizeof(sc->sc_cmap));
188 	fbwscons_setcolormap(&sc->sc_sunfb, mgx_setcolor);
189 
190 	printf(", %dx%d\n",
191 	    sc->sc_sunfb.sf_width, sc->sc_sunfb.sf_height);
192 
193 	if (isconsole) {
194 		fbwscons_console_init(&sc->sc_sunfb, -1);
195 	}
196 
197 	fbwscons_attach(&sc->sc_sunfb, &mgx_accessops, isconsole);
198 }
199 
200 /*
201  * wsdisplay operations
202  */
203 
204 int
mgx_ioctl(void * dev,u_long cmd,caddr_t data,int flags,struct proc * p)205 mgx_ioctl(void *dev, u_long cmd, caddr_t data, int flags, struct proc *p)
206 {
207 	struct mgx_softc *sc = dev;
208 	struct wsdisplay_cmap *cm;
209 	struct wsdisplay_fbinfo *wdf;
210 	int error;
211 
212 	switch (cmd) {
213 	case WSDISPLAYIO_GTYPE:
214 		*(u_int *)data = WSDISPLAY_TYPE_MGX;
215 		break;
216 	case WSDISPLAYIO_GINFO:
217 		wdf = (struct wsdisplay_fbinfo *)data;
218 		wdf->height = sc->sc_sunfb.sf_height;
219 		wdf->width = sc->sc_sunfb.sf_width;
220 		wdf->depth = sc->sc_sunfb.sf_depth;
221 		wdf->cmsize = 256;
222 		break;
223 	case WSDISPLAYIO_LINEBYTES:
224 		*(u_int *)data = sc->sc_sunfb.sf_linebytes;
225 		break;
226 
227 	case WSDISPLAYIO_GETCMAP:
228 		cm = (struct wsdisplay_cmap *)data;
229 		error = mgx_getcmap(sc->sc_cmap, cm);
230 		if (error != 0)
231 			return (error);
232 		break;
233 	case WSDISPLAYIO_PUTCMAP:
234 		cm = (struct wsdisplay_cmap *)data;
235 		error = mgx_putcmap(sc->sc_cmap, cm);
236 		if (error != 0)
237 			return (error);
238 		mgx_loadcmap(sc, cm->index, cm->count);
239 		break;
240 
241 	case WSDISPLAYIO_SVIDEO:
242 	case WSDISPLAYIO_GVIDEO:
243 		break;
244 
245 	default:
246 		return (-1);
247 	}
248 
249 	return (0);
250 }
251 
252 paddr_t
mgx_mmap(void * v,off_t offset,int prot)253 mgx_mmap(void *v, off_t offset, int prot)
254 {
255 	struct mgx_softc *sc = v;
256 
257 	if (offset & PGOFSET)
258 		return (-1);
259 
260 	/* Allow mapping as a dumb framebuffer from offset 0 */
261 	if (offset >= 0 && offset < sc->sc_sunfb.sf_fbsize) {
262 		return (REG2PHYS(&sc->sc_phys, offset) | PMAP_NC);
263 	}
264 
265 	return (-1);
266 }
267 
268 void
mgx_burner(void * v,u_int on,u_int flags)269 mgx_burner(void *v, u_int on, u_int flags)
270 {
271 	struct mgx_softc *sc = v;
272 
273 	sc->sc_vidc[CRTC_COMMAND] = 1;	/* trigger? */
274 	if (on)
275 		sc->sc_vidc[CRTC_DATA] &= ~CD_DISABLEVIDEO;
276 	else
277 		sc->sc_vidc[CRTC_DATA] |= CD_DISABLEVIDEO;
278 }
279 
280 /*
281  * Colormap handling routines
282  */
283 
284 void
mgx_setcolor(void * v,u_int index,u_int8_t r,u_int8_t g,u_int8_t b)285 mgx_setcolor(void *v, u_int index, u_int8_t r, u_int8_t g, u_int8_t b)
286 {
287 	struct mgx_softc *sc = v;
288 
289 	index *= 3;
290 	sc->sc_cmap[index++] = r;
291 	sc->sc_cmap[index++] = g;
292 	sc->sc_cmap[index] = b;
293 
294 	mgx_loadcmap(sc, index, 1);
295 }
296 
297 void
mgx_loadcmap(struct mgx_softc * sc,int start,int ncolors)298 mgx_loadcmap(struct mgx_softc *sc, int start, int ncolors)
299 {
300 	u_int8_t *color;
301 	int i;
302 
303 	/*
304 	 * Apparently there is no way to load an incomplete cmap to this
305 	 * DAC. What a waste.
306 	 */
307 	for (color = sc->sc_cmap, i = 0; i < 256 * 3; i++)
308 		sc->sc_vidc[CRTC_CMAP] = *color++;
309 }
310 
311 int
mgx_getcmap(u_int8_t * cm,struct wsdisplay_cmap * rcm)312 mgx_getcmap(u_int8_t *cm, struct wsdisplay_cmap *rcm)
313 {
314 	u_int index = rcm->index, count = rcm->count, i;
315 	int error;
316 
317 	if (index >= 256 || count > 256 - index)
318 		return (EINVAL);
319 
320 	for (i = 0; i < count; i++) {
321 		if ((error =
322 		    copyout(cm + (index + i) * 3 + 0, &rcm->red[i], 1)) != 0)
323 			return (error);
324 		if ((error =
325 		    copyout(cm + (index + i) * 3 + 1, &rcm->green[i], 1)) != 0)
326 			return (error);
327 		if ((error =
328 		    copyout(cm + (index + i) * 3 + 2, &rcm->blue[i], 1)) != 0)
329 			return (error);
330 	}
331 
332 	return (0);
333 }
334 
335 int
mgx_putcmap(u_int8_t * cm,struct wsdisplay_cmap * rcm)336 mgx_putcmap(u_int8_t *cm, struct wsdisplay_cmap *rcm)
337 {
338 	u_int index = rcm->index, count = rcm->count, i;
339 	int error;
340 
341 	if (index >= 256 || count > 256 - index)
342 		return (EINVAL);
343 
344 	for (i = 0; i < count; i++) {
345 		if ((error =
346 		    copyin(&rcm->red[i], cm + (index + i) * 3 + 0, 1)) != 0)
347 			return (error);
348 		if ((error =
349 		    copyin(&rcm->green[i], cm + (index + i) * 3 + 1, 1)) != 0)
350 			return (error);
351 		if ((error =
352 		    copyin(&rcm->blue[i], cm + (index + i) * 3 + 2, 1)) != 0)
353 			return (error);
354 	}
355 
356 	return (0);
357 }
358