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