xref: /netbsd/sys/dev/sbus/p9100.c (revision bf9ec67e)
1 /*	$NetBSD: p9100.c,v 1.8 2002/03/11 16:00:57 pk Exp $ */
2 
3 /*-
4  * Copyright (c) 1998 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Matt Thomas.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *        This product includes software developed by the NetBSD
21  *        Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 /*
40  * color display (p9100) driver.
41  *
42  * Does not handle interrupts, even though they can occur.
43  *
44  * XXX should defer colormap updates to vertical retrace interrupts
45  */
46 
47 #include <sys/cdefs.h>
48 __KERNEL_RCSID(0, "$NetBSD: p9100.c,v 1.8 2002/03/11 16:00:57 pk Exp $");
49 
50 #include <sys/param.h>
51 #include <sys/systm.h>
52 #include <sys/buf.h>
53 #include <sys/device.h>
54 #include <sys/ioctl.h>
55 #include <sys/malloc.h>
56 #include <sys/mman.h>
57 #include <sys/tty.h>
58 #include <sys/conf.h>
59 
60 #include <machine/bus.h>
61 #include <machine/autoconf.h>
62 
63 #include <dev/sun/fbio.h>
64 #include <dev/sun/fbvar.h>
65 #include <dev/sun/btreg.h>
66 #include <dev/sun/btvar.h>
67 #if 0
68 #include <dev/sbus/p9100reg.h>
69 #endif
70 
71 #include <dev/sbus/sbusvar.h>
72 
73 #include "tctrl.h"
74 #if NTCTRL > 0
75 #include <machine/tctrl.h>
76 #include <sparc/dev/tctrlvar.h>/*XXX*/
77 #endif
78 
79 #include <machine/conf.h>
80 
81 /* per-display variables */
82 struct p9100_softc {
83 	struct device	sc_dev;		/* base device */
84 	struct sbusdev	sc_sd;		/* sbus device */
85 	struct fbdevice	sc_fb;		/* frame buffer device */
86 	bus_space_tag_t	sc_bustag;
87 
88 	bus_addr_t	sc_ctl_paddr;	/* phys address description */
89 	bus_size_t	sc_ctl_psize;	/*   for device mmap() */
90 	bus_space_handle_t sc_ctl_memh;	/*   bus space handle */
91 
92 	bus_addr_t	sc_cmd_paddr;	/* phys address description */
93 	bus_size_t	sc_cmd_psize;	/*   for device mmap() */
94 	bus_space_handle_t sc_cmd_memh;	/*   bus space handle */
95 
96 	bus_addr_t	sc_fb_paddr;	/* phys address description */
97 	bus_size_t	sc_fb_psize;	/*   for device mmap() */
98 	bus_space_handle_t sc_fb_memh;	/*   bus space handle */
99 
100 	uint32_t sc_junk;
101 
102 	union	bt_cmap sc_cmap;	/* Brooktree color map */
103 };
104 
105 /* The Tadpole 3GX Technical Reference Manual lies.  The ramdac registers
106  * are map in 4 byte increments, not 8.
107  */
108 #define	SCRN_RPNT_CTL_1	0x0138	/* Screen Respaint Timing Control 1 */
109 #define	VIDEO_ENABLED	0x00000020
110 #define	PWRUP_CNFG	0x0194	/* Power Up Configuration */
111 #define	DAC_CMAP_WRIDX	0x0200	/* IBM RGB528 Palette Address (Write) */
112 #define	DAC_CMAP_DATA	0x0204	/* IBM RGB528 Palette Data */
113 #define	DAC_PXL_MASK	0x0208	/* IBM RGB528 Pixel Mask */
114 #define	DAC_CMAP_RDIDX	0x020c	/* IBM RGB528 Palette Address (Read) */
115 #define	DAC_INDX_LO	0x0210	/* IBM RGB528 Index Low */
116 #define	DAC_INDX_HI	0x0214	/* IBM RGB528 Index High */
117 #define	DAC_INDX_DATA	0x0218	/* IBM RGB528 Index Data (Indexed Registers) */
118 #define	DAC_INDX_CTL	0x021c	/* IBM RGB528 Index Control */
119 
120 /* autoconfiguration driver */
121 static int	p9100_sbus_match(struct device *, struct cfdata *, void *);
122 static void	p9100_sbus_attach(struct device *, struct device *, void *);
123 
124 static void	p9100unblank(struct device *);
125 static void	p9100_shutdown(void *);
126 
127 /* cdevsw prototypes */
128 cdev_decl(p9100);
129 
130 struct cfattach pnozz_ca = {
131 	sizeof(struct p9100_softc), p9100_sbus_match, p9100_sbus_attach
132 };
133 
134 extern struct cfdriver pnozz_cd;
135 
136 /* frame buffer generic driver */
137 static struct fbdriver p9100fbdriver = {
138 	p9100unblank, p9100open, p9100close, p9100ioctl, p9100poll,
139 	p9100mmap
140 };
141 
142 static void p9100loadcmap(struct p9100_softc *, int, int);
143 static void p9100_set_video(struct p9100_softc *, int);
144 static int p9100_get_video(struct p9100_softc *);
145 static uint32_t p9100_ctl_read_4(struct p9100_softc *, bus_size_t);
146 static void p9100_ctl_write_4(struct p9100_softc *, bus_size_t, uint32_t);
147 #if 0
148 static uint8_t p9100_ramdac_read(struct p9100_softc *, bus_size_t);
149 #endif
150 static void p9100_ramdac_write(struct p9100_softc *, bus_size_t, uint8_t);
151 
152 /*
153  * Match a p9100.
154  */
155 static int
156 p9100_sbus_match(struct device *parent, struct cfdata *cf, void *aux)
157 {
158 	struct sbus_attach_args *sa = aux;
159 
160 	return (strcmp("p9100", sa->sa_name) == 0);
161 }
162 
163 
164 /*
165  * Attach a display.  We need to notice if it is the console, too.
166  */
167 static void
168 p9100_sbus_attach(struct device *parent, struct device *self, void *args)
169 {
170 	struct p9100_softc *sc = (struct p9100_softc *)self;
171 	struct sbus_attach_args *sa = args;
172 	struct fbdevice *fb = &sc->sc_fb;
173 	int isconsole;
174 	int node;
175 	int i;
176 
177 	/* Remember cookies for p9100_mmap() */
178 	sc->sc_bustag = sa->sa_bustag;
179 	sc->sc_ctl_paddr = sbus_bus_addr(sa->sa_bustag,
180 		sa->sa_reg[0].sbr_slot, sa->sa_reg[0].sbr_offset);
181 	sc->sc_ctl_psize = (bus_size_t)sa->sa_reg[0].sbr_size;
182 
183 	sc->sc_cmd_paddr = sbus_bus_addr(sa->sa_bustag,
184 		sa->sa_reg[1].sbr_slot, sa->sa_reg[1].sbr_offset);
185 	sc->sc_cmd_psize = (bus_size_t)sa->sa_reg[1].sbr_size;
186 
187 	sc->sc_fb_paddr = sbus_bus_addr(sa->sa_bustag,
188 		sa->sa_reg[2].sbr_slot, sa->sa_reg[2].sbr_offset);
189 	sc->sc_fb_psize = (bus_size_t)sa->sa_reg[2].sbr_size;
190 
191 	fb->fb_driver = &p9100fbdriver;
192 	fb->fb_device = &sc->sc_dev;
193 	fb->fb_flags = sc->sc_dev.dv_cfdata->cf_flags & FB_USERMASK;
194 	fb->fb_type.fb_type = FBTYPE_SUN3COLOR;
195 	fb->fb_pixels = NULL;
196 
197 	node = sa->sa_node;
198 	isconsole = fb_is_console(node);
199 	if (!isconsole) {
200 		printf("\n%s: fatal error: PROM didn't configure device: not console\n", self->dv_xname);
201 		return;
202 	}
203 
204 	/*
205 	 * When the ROM has mapped in a p9100 display, the address
206 	 * maps only the video RAM, so in any case we have to map the
207 	 * registers ourselves.  We only need the video RAM if we are
208 	 * going to print characters via rconsole.
209 	 */
210 	if (sbus_bus_map(sc->sc_bustag,
211 			 sa->sa_reg[0].sbr_slot,
212 			 sa->sa_reg[0].sbr_offset,
213 			 sc->sc_ctl_psize,
214 			 BUS_SPACE_MAP_LINEAR, &sc->sc_ctl_memh) != 0) {
215 		printf("%s: cannot map control registers\n", self->dv_xname);
216 		return;
217 	}
218 
219 	if (sbus_bus_map(sc->sc_bustag,
220 			 sa->sa_reg[1].sbr_slot,
221 			 sa->sa_reg[1].sbr_offset,
222 			 sc->sc_cmd_psize,
223 			 BUS_SPACE_MAP_LINEAR, &sc->sc_cmd_memh) != 0) {
224 		printf("%s: cannot map command registers\n", self->dv_xname);
225 		return;
226 	}
227 
228 	if (sa->sa_npromvaddrs != 0)
229 		fb->fb_pixels = (caddr_t)sa->sa_promvaddrs[0];
230 
231 	if (fb->fb_pixels == NULL) {
232 		if (sbus_bus_map(sc->sc_bustag,
233 				sa->sa_reg[2].sbr_slot,
234 				sa->sa_reg[2].sbr_offset,
235 				sc->sc_fb_psize,
236 				BUS_SPACE_MAP_LINEAR, &sc->sc_fb_memh) != 0) {
237 			printf("%s: cannot map framebuffer\n", self->dv_xname);
238 			return;
239 		}
240 		fb->fb_pixels = (char *)sc->sc_fb_memh;
241 	} else {
242 		sc->sc_fb_memh = (bus_space_handle_t) fb->fb_pixels;
243 	}
244 
245 	i = p9100_ctl_read_4(sc, 0x0004);
246 	switch ((i >> 26) & 7) {
247 	    case 5: fb->fb_type.fb_depth = 32; break;
248 	    case 7: fb->fb_type.fb_depth = 24; break;
249 	    case 3: fb->fb_type.fb_depth = 16; break;
250 	    case 2: fb->fb_type.fb_depth = 8; break;
251 	    default: {
252 		panic("pnozz: can't determine screen depth (0x%02x)", i);
253 	    }
254 	}
255 	fb_setsize_obp(fb, fb->fb_type.fb_depth, 800, 600, node);
256 
257 	sbus_establish(&sc->sc_sd, &sc->sc_dev);
258 
259 	fb->fb_type.fb_size = fb->fb_type.fb_height * fb->fb_linebytes;
260 	printf(": rev %d, %dx%d, depth %d",
261 	       (i & 7), fb->fb_type.fb_width, fb->fb_type.fb_height,
262 	       fb->fb_type.fb_depth);
263 
264 	fb->fb_type.fb_cmsize = PROM_getpropint(node, "cmsize", 256);
265 	if ((1 << fb->fb_type.fb_depth) != fb->fb_type.fb_cmsize)
266 		printf(", %d entry colormap", fb->fb_type.fb_cmsize);
267 
268 	/* Initialize the default color map. */
269 	bt_initcmap(&sc->sc_cmap, 256);
270 	p9100loadcmap(sc, 0, 256);
271 
272 	/* make sure we are not blanked */
273 	if (isconsole)
274 		p9100_set_video(sc, 1);
275 
276 	if (shutdownhook_establish(p9100_shutdown, sc) == NULL) {
277 		panic("%s: could not establish shutdown hook",
278 		      sc->sc_dev.dv_xname);
279 	}
280 
281 	if (isconsole) {
282 		printf(" (console)\n");
283 #ifdef RASTERCONSOLE
284 		for (i = 0; i < fb->fb_type.fb_size; i++) {
285 		     if (fb->fb_pixels[i] == 0) {
286 			 fb->fb_pixels[i] = 1;
287 		     } else if (fb->fb_pixels[i] == (char) 255) {
288 			 fb->fb_pixels[i] = 0;
289 		     }
290 		}
291 		p9100loadcmap(sc, 255, 1);
292 		fbrcons_init(fb);
293 #endif
294 	} else
295 		printf("\n");
296 
297 	fb_attach(fb, isconsole);
298 }
299 
300 static void
301 p9100_shutdown(arg)
302 	void *arg;
303 {
304 	struct p9100_softc *sc = arg;
305 #ifdef RASTERCONSOLE
306 	struct fbdevice *fb = &sc->sc_fb;
307 	int i;
308 
309 	for (i = 0; i < fb->fb_type.fb_size; i++) {
310 	     if (fb->fb_pixels[i] == 1) {
311 		 fb->fb_pixels[i] = 0;
312 	     } else if (fb->fb_pixels[i] == 0) {
313 		 fb->fb_pixels[i] = 255;
314 	     }
315 	}
316 	sc->sc_cmap.cm_map[0][0] = 0xff;
317 	sc->sc_cmap.cm_map[0][1] = 0xff;
318 	sc->sc_cmap.cm_map[0][2] = 0xff;
319 	sc->sc_cmap.cm_map[1][0] = 0;
320 	sc->sc_cmap.cm_map[1][1] = 0;
321 	sc->sc_cmap.cm_map[1][2] = 0x80;
322 	p9100loadcmap(sc, 0, 2);
323 	sc->sc_cmap.cm_map[255][0] = 0;
324 	sc->sc_cmap.cm_map[255][1] = 0;
325 	sc->sc_cmap.cm_map[255][2] = 0;
326 	p9100loadcmap(sc, 255, 1);
327 #endif
328 	p9100_set_video(sc, 1);
329 }
330 
331 int
332 p9100open(dev_t dev, int flags, int mode, struct proc *p)
333 {
334 	int unit = minor(dev);
335 
336 	if (unit >= pnozz_cd.cd_ndevs || pnozz_cd.cd_devs[unit] == NULL)
337 		return (ENXIO);
338 	return (0);
339 }
340 
341 int
342 p9100close(dev_t dev, int flags, int mode, struct proc *p)
343 {
344 	return (0);
345 }
346 
347 int
348 p9100ioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
349 {
350 	struct p9100_softc *sc = pnozz_cd.cd_devs[minor(dev)];
351 	struct fbgattr *fba;
352 	int error;
353 
354 	switch (cmd) {
355 
356 	case FBIOGTYPE:
357 		*(struct fbtype *)data = sc->sc_fb.fb_type;
358 		break;
359 
360 	case FBIOGATTR:
361 		fba = (struct fbgattr *)data;
362 		fba->real_type = sc->sc_fb.fb_type.fb_type;
363 		fba->owner = 0;		/* XXX ??? */
364 		fba->fbtype = sc->sc_fb.fb_type;
365 		fba->sattr.flags = 0;
366 		fba->sattr.emu_type = sc->sc_fb.fb_type.fb_type;
367 		fba->sattr.dev_specific[0] = -1;
368 		fba->emu_types[0] = sc->sc_fb.fb_type.fb_type;
369 		fba->emu_types[1] = -1;
370 		break;
371 
372 	case FBIOGETCMAP:
373 #define p ((struct fbcmap *)data)
374 		return (bt_getcmap(p, &sc->sc_cmap, 256, 1));
375 
376 	case FBIOPUTCMAP:
377 		/* copy to software map */
378 		error = bt_putcmap(p, &sc->sc_cmap, 256, 1);
379 		if (error)
380 			return (error);
381 		/* now blast them into the chip */
382 		/* XXX should use retrace interrupt */
383 		p9100loadcmap(sc, p->index, p->count);
384 #undef p
385 		break;
386 
387 	case FBIOGVIDEO:
388 		*(int *)data = p9100_get_video(sc);
389 		break;
390 
391 	case FBIOSVIDEO:
392 		p9100_set_video(sc, *(int *)data);
393 		break;
394 
395 	default:
396 		return (ENOTTY);
397 	}
398 	return (0);
399 }
400 
401 int
402 p9100poll(dev_t dev, int events, struct proc *p)
403 {
404 	return seltrue(dev, events, p);
405 }
406 
407 static uint32_t
408 p9100_ctl_read_4(struct p9100_softc *sc, bus_size_t off)
409 {
410 	sc->sc_junk = bus_space_read_4(sc->sc_bustag, sc->sc_fb_memh, off);
411 	return bus_space_read_4(sc->sc_bustag, sc->sc_ctl_memh, off);
412 }
413 
414 static void
415 p9100_ctl_write_4(struct p9100_softc *sc, bus_size_t off, uint32_t v)
416 {
417 	sc->sc_junk = bus_space_read_4(sc->sc_bustag, sc->sc_fb_memh, off);
418 	bus_space_write_4(sc->sc_bustag, sc->sc_ctl_memh, off, v);
419 }
420 
421 #if 0
422 static uint8_t
423 p9100_ramdac_read(struct p9100_softc *sc, bus_size_t off)
424 {
425 	sc->sc_junk = p9100_ctl_read_4(sc, PWRUP_CNFG);
426 	return p9100_ctl_read_4(sc, off) >> 16;
427 }
428 #endif
429 
430 static void
431 p9100_ramdac_write(struct p9100_softc *sc, bus_size_t off, uint8_t v)
432 {
433 	sc->sc_junk = p9100_ctl_read_4(sc, PWRUP_CNFG);
434 	p9100_ctl_write_4(sc, off, v << 16);
435 }
436 
437 /*
438  * Undo the effect of an FBIOSVIDEO that turns the video off.
439  */
440 static void
441 p9100unblank(struct device *dev)
442 {
443 
444 	p9100_set_video((struct p9100_softc *)dev, 1);
445 }
446 
447 static void
448 p9100_set_video(struct p9100_softc *sc, int enable)
449 {
450 	u_int32_t v = p9100_ctl_read_4(sc, SCRN_RPNT_CTL_1);
451 	if (enable)
452 		v |= VIDEO_ENABLED;
453 	else
454 		v &= ~VIDEO_ENABLED;
455 	p9100_ctl_write_4(sc, SCRN_RPNT_CTL_1, v);
456 #if NTCTRL > 0
457 	/* Turn On/Off the TFT if we know how.
458 	 */
459 	tadpole_set_video(enable);
460 #endif
461 }
462 
463 static int
464 p9100_get_video(struct p9100_softc *sc)
465 {
466 	return (p9100_ctl_read_4(sc, SCRN_RPNT_CTL_1) & VIDEO_ENABLED) != 0;
467 }
468 
469 /*
470  * Load a subset of the current (new) colormap into the IBM RAMDAC.
471  */
472 static void
473 p9100loadcmap(struct p9100_softc *sc, int start, int ncolors)
474 {
475 	u_char *p;
476 
477 	p9100_ramdac_write(sc, DAC_CMAP_WRIDX, start);
478 
479 	for (p = sc->sc_cmap.cm_map[start], ncolors *= 3; ncolors-- > 0; p++) {
480 		p9100_ramdac_write(sc, DAC_CMAP_DATA, *p);
481 	}
482 }
483 
484 /*
485  * Return the address that would map the given device at the given
486  * offset, allowing for the given protection, or return -1 for error.
487  */
488 paddr_t
489 p9100mmap(dev_t dev, off_t off, int prot)
490 {
491 	struct p9100_softc *sc = pnozz_cd.cd_devs[minor(dev)];
492 
493 	if (off & PGOFSET)
494 		panic("p9100mmap");
495 	if (off < 0)
496 		return (-1);
497 
498 #define CG3_MMAP_OFFSET	0x04000000
499 	/* Make Xsun think we are a CG3 (SUN3COLOR)
500 	 */
501 	if (off >= CG3_MMAP_OFFSET && off < CG3_MMAP_OFFSET + sc->sc_fb_psize) {
502 		off -= CG3_MMAP_OFFSET;
503 		return (bus_space_mmap(sc->sc_bustag,
504 			sc->sc_fb_paddr,
505 			off,
506 			prot,
507 			BUS_SPACE_MAP_LINEAR));
508 	}
509 
510 	if (off >= sc->sc_fb_psize + sc->sc_ctl_psize + sc->sc_cmd_psize)
511 		return (-1);
512 
513 	if (off < sc->sc_fb_psize) {
514 		return (bus_space_mmap(sc->sc_bustag,
515 			sc->sc_fb_paddr,
516 			off,
517 			prot,
518 			BUS_SPACE_MAP_LINEAR));
519 	}
520 	off -= sc->sc_fb_psize;
521 	if (off < sc->sc_ctl_psize) {
522 		return (bus_space_mmap(sc->sc_bustag,
523 			sc->sc_ctl_paddr,
524 			off,
525 			prot,
526 			BUS_SPACE_MAP_LINEAR));
527 	}
528 	off -= sc->sc_ctl_psize;
529 
530 	return (bus_space_mmap(sc->sc_bustag,
531 		sc->sc_cmd_paddr,
532 		off,
533 		prot,
534 		BUS_SPACE_MAP_LINEAR));
535 }
536