xref: /netbsd-src/sys/dev/sbus/p9100.c (revision 9fbd88883c38d0c0fbfcbe66d76fe6b0fab3f9de)
1 /*	$NetBSD: p9100.c,v 1.7 2001/12/08 19:42:45 cyber 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.7 2001/12/08 19:42:45 cyber 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 	bus_type_t	sc_ctl_slot;	/* phys address description */
88 	bus_addr_t	sc_ctl_paddr;	/*   for device mmap() */
89 	bus_size_t	sc_ctl_psize;	/*   for device mmap() */
90 	bus_space_handle_t sc_ctl_memh;	/*   bus space handle */
91 	bus_type_t	sc_cmd_slot;	/* phys address description */
92 	bus_addr_t	sc_cmd_paddr;	/*   for device mmap() */
93 	bus_size_t	sc_cmd_psize;	/*   for device mmap() */
94 	bus_space_handle_t sc_cmd_memh;	/*   bus space handle */
95 	bus_type_t	sc_fb_slot;	/* phys address description */
96 	bus_addr_t	sc_fb_paddr;	/*   for device mmap() */
97 	bus_size_t	sc_fb_psize;	/*   for device mmap() */
98 	bus_space_handle_t sc_fb_memh;	/*   bus space handle */
99 	uint32_t sc_junk;
100 
101 	union	bt_cmap sc_cmap;	/* Brooktree color map */
102 };
103 
104 /* The Tadpole 3GX Technical Reference Manual lies.  The ramdac registers
105  * are map in 4 byte increments, not 8.
106  */
107 #define	SCRN_RPNT_CTL_1	0x0138	/* Screen Respaint Timing Control 1 */
108 #define	VIDEO_ENABLED	0x00000020
109 #define	PWRUP_CNFG	0x0194	/* Power Up Configuration */
110 #define	DAC_CMAP_WRIDX	0x0200	/* IBM RGB528 Palette Address (Write) */
111 #define	DAC_CMAP_DATA	0x0204	/* IBM RGB528 Palette Data */
112 #define	DAC_PXL_MASK	0x0208	/* IBM RGB528 Pixel Mask */
113 #define	DAC_CMAP_RDIDX	0x020c	/* IBM RGB528 Palette Address (Read) */
114 #define	DAC_INDX_LO	0x0210	/* IBM RGB528 Index Low */
115 #define	DAC_INDX_HI	0x0214	/* IBM RGB528 Index High */
116 #define	DAC_INDX_DATA	0x0218	/* IBM RGB528 Index Data (Indexed Registers) */
117 #define	DAC_INDX_CTL	0x021c	/* IBM RGB528 Index Control */
118 
119 /* autoconfiguration driver */
120 static int	p9100_sbus_match(struct device *, struct cfdata *, void *);
121 static void	p9100_sbus_attach(struct device *, struct device *, void *);
122 
123 static void	p9100unblank(struct device *);
124 static void	p9100_shutdown(void *);
125 
126 /* cdevsw prototypes */
127 cdev_decl(p9100);
128 
129 struct cfattach pnozz_ca = {
130 	sizeof(struct p9100_softc), p9100_sbus_match, p9100_sbus_attach
131 };
132 
133 extern struct cfdriver pnozz_cd;
134 
135 /* frame buffer generic driver */
136 static struct fbdriver p9100fbdriver = {
137 	p9100unblank, p9100open, p9100close, p9100ioctl, p9100poll,
138 	p9100mmap
139 };
140 
141 static void p9100loadcmap(struct p9100_softc *, int, int);
142 static void p9100_set_video(struct p9100_softc *, int);
143 static int p9100_get_video(struct p9100_softc *);
144 static uint32_t p9100_ctl_read_4(struct p9100_softc *, bus_size_t);
145 static void p9100_ctl_write_4(struct p9100_softc *, bus_size_t, uint32_t);
146 #if 0
147 static uint8_t p9100_ramdac_read(struct p9100_softc *, bus_size_t);
148 #endif
149 static void p9100_ramdac_write(struct p9100_softc *, bus_size_t, uint8_t);
150 
151 /*
152  * Match a p9100.
153  */
154 static int
155 p9100_sbus_match(struct device *parent, struct cfdata *cf, void *aux)
156 {
157 	struct sbus_attach_args *sa = aux;
158 
159 	return (strcmp("p9100", sa->sa_name) == 0);
160 }
161 
162 
163 /*
164  * Attach a display.  We need to notice if it is the console, too.
165  */
166 static void
167 p9100_sbus_attach(struct device *parent, struct device *self, void *args)
168 {
169 	struct p9100_softc *sc = (struct p9100_softc *)self;
170 	struct sbus_attach_args *sa = args;
171 	struct fbdevice *fb = &sc->sc_fb;
172 	int isconsole;
173 	int node;
174 	int i;
175 
176 	/* Remember cookies for p9100_mmap() */
177 	sc->sc_bustag = sa->sa_bustag;
178 	sc->sc_ctl_slot = (bus_type_t)sa->sa_reg[0].sbr_slot;
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_slot = (bus_type_t)sa->sa_reg[1].sbr_slot;
184 	sc->sc_cmd_paddr = sbus_bus_addr(sa->sa_bustag,
185 		sa->sa_reg[1].sbr_slot, sa->sa_reg[1].sbr_offset);
186 	sc->sc_cmd_psize = (bus_size_t)sa->sa_reg[1].sbr_size;
187 
188 	sc->sc_fb_slot = (bus_type_t)sa->sa_reg[2].sbr_slot;
189 	sc->sc_fb_paddr = sbus_bus_addr(sa->sa_bustag,
190 		sa->sa_reg[2].sbr_slot, sa->sa_reg[2].sbr_offset);
191 	sc->sc_fb_psize = (bus_size_t)sa->sa_reg[2].sbr_size;
192 
193 	fb->fb_driver = &p9100fbdriver;
194 	fb->fb_device = &sc->sc_dev;
195 	fb->fb_flags = sc->sc_dev.dv_cfdata->cf_flags & FB_USERMASK;
196 	fb->fb_type.fb_type = FBTYPE_SUN3COLOR;
197 	fb->fb_pixels = NULL;
198 
199 	node = sa->sa_node;
200 	isconsole = fb_is_console(node);
201 	if (!isconsole) {
202 		printf("\n%s: fatal error: PROM didn't configure device: not console\n", self->dv_xname);
203 		return;
204 	}
205 
206 	/*
207 	 * When the ROM has mapped in a p9100 display, the address
208 	 * maps only the video RAM, so in any case we have to map the
209 	 * registers ourselves.  We only need the video RAM if we are
210 	 * going to print characters via rconsole.
211 	 */
212 	if (sbus_bus_map(sc->sc_bustag, sc->sc_ctl_slot,
213 			 sa->sa_reg[0].sbr_slot + sa->sa_reg[0].sbr_offset,
214 			 sc->sc_ctl_psize, BUS_SPACE_MAP_LINEAR, 0,
215 			 &sc->sc_ctl_memh) != 0) {
216 		printf("%s: cannot map control registers\n", self->dv_xname);
217 		return;
218 	}
219 
220 	if (sbus_bus_map(sc->sc_bustag, sc->sc_cmd_slot,
221 			 sa->sa_reg[1].sbr_slot + sa->sa_reg[1].sbr_offset,
222 			 sc->sc_cmd_psize, BUS_SPACE_MAP_LINEAR, 0,
223 			 &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, sc->sc_fb_slot,
233 				 sa->sa_reg[2].sbr_slot +
234 				     sa->sa_reg[2].sbr_offset,
235 				 sc->sc_fb_psize,
236 				 BUS_SPACE_MAP_LINEAR, 0,
237 				 &sc->sc_fb_memh) != 0) {
238 			printf("%s: cannot map framebuffer\n", self->dv_xname);
239 			return;
240 		}
241 		fb->fb_pixels = (char *)sc->sc_fb_memh;
242 	} else {
243 		sc->sc_fb_memh = (bus_space_handle_t) fb->fb_pixels;
244 	}
245 
246 	i = p9100_ctl_read_4(sc, 0x0004);
247 	switch ((i >> 26) & 7) {
248 	    case 5: fb->fb_type.fb_depth = 32; break;
249 	    case 7: fb->fb_type.fb_depth = 24; break;
250 	    case 3: fb->fb_type.fb_depth = 16; break;
251 	    case 2: fb->fb_type.fb_depth = 8; break;
252 	    default: {
253 		panic("pnozz: can't determine screen depth (0x%02x)", i);
254 	    }
255 	}
256 	fb_setsize_obp(fb, fb->fb_type.fb_depth, 800, 600, node);
257 
258 	sbus_establish(&sc->sc_sd, &sc->sc_dev);
259 
260 	fb->fb_type.fb_size = fb->fb_type.fb_height * fb->fb_linebytes;
261 	printf(": rev %d, %dx%d, depth %d",
262 	       (i & 7), fb->fb_type.fb_width, fb->fb_type.fb_height,
263 	       fb->fb_type.fb_depth);
264 
265 	fb->fb_type.fb_cmsize = PROM_getpropint(node, "cmsize", 256);
266 	if ((1 << fb->fb_type.fb_depth) != fb->fb_type.fb_cmsize)
267 		printf(", %d entry colormap", fb->fb_type.fb_cmsize);
268 
269 	/* Initialize the default color map. */
270 	bt_initcmap(&sc->sc_cmap, 256);
271 	p9100loadcmap(sc, 0, 256);
272 
273 	/* make sure we are not blanked */
274 	if (isconsole)
275 		p9100_set_video(sc, 1);
276 
277 	if (shutdownhook_establish(p9100_shutdown, sc) == NULL) {
278 		panic("%s: could not establish shutdown hook",
279 		      sc->sc_dev.dv_xname);
280 	}
281 
282 	if (isconsole) {
283 		printf(" (console)\n");
284 #ifdef RASTERCONSOLE
285 		for (i = 0; i < fb->fb_type.fb_size; i++) {
286 		     if (fb->fb_pixels[i] == 0) {
287 			 fb->fb_pixels[i] = 1;
288 		     } else if (fb->fb_pixels[i] == (char) 255) {
289 			 fb->fb_pixels[i] = 0;
290 		     }
291 		}
292 		p9100loadcmap(sc, 255, 1);
293 		fbrcons_init(fb);
294 #endif
295 	} else
296 		printf("\n");
297 
298 	fb_attach(fb, isconsole);
299 }
300 
301 static void
302 p9100_shutdown(arg)
303 	void *arg;
304 {
305 	struct p9100_softc *sc = arg;
306 #ifdef RASTERCONSOLE
307 	struct fbdevice *fb = &sc->sc_fb;
308 	int i;
309 
310 	for (i = 0; i < fb->fb_type.fb_size; i++) {
311 	     if (fb->fb_pixels[i] == 1) {
312 		 fb->fb_pixels[i] = 0;
313 	     } else if (fb->fb_pixels[i] == 0) {
314 		 fb->fb_pixels[i] = 255;
315 	     }
316 	}
317 	sc->sc_cmap.cm_map[0][0] = 0xff;
318 	sc->sc_cmap.cm_map[0][1] = 0xff;
319 	sc->sc_cmap.cm_map[0][2] = 0xff;
320 	sc->sc_cmap.cm_map[1][0] = 0;
321 	sc->sc_cmap.cm_map[1][1] = 0;
322 	sc->sc_cmap.cm_map[1][2] = 0x80;
323 	p9100loadcmap(sc, 0, 2);
324 	sc->sc_cmap.cm_map[255][0] = 0;
325 	sc->sc_cmap.cm_map[255][1] = 0;
326 	sc->sc_cmap.cm_map[255][2] = 0;
327 	p9100loadcmap(sc, 255, 1);
328 #endif
329 	p9100_set_video(sc, 1);
330 }
331 
332 int
333 p9100open(dev_t dev, int flags, int mode, struct proc *p)
334 {
335 	int unit = minor(dev);
336 
337 	if (unit >= pnozz_cd.cd_ndevs || pnozz_cd.cd_devs[unit] == NULL)
338 		return (ENXIO);
339 	return (0);
340 }
341 
342 int
343 p9100close(dev_t dev, int flags, int mode, struct proc *p)
344 {
345 	return (0);
346 }
347 
348 int
349 p9100ioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
350 {
351 	struct p9100_softc *sc = pnozz_cd.cd_devs[minor(dev)];
352 	struct fbgattr *fba;
353 	int error;
354 
355 	switch (cmd) {
356 
357 	case FBIOGTYPE:
358 		*(struct fbtype *)data = sc->sc_fb.fb_type;
359 		break;
360 
361 	case FBIOGATTR:
362 		fba = (struct fbgattr *)data;
363 		fba->real_type = sc->sc_fb.fb_type.fb_type;
364 		fba->owner = 0;		/* XXX ??? */
365 		fba->fbtype = sc->sc_fb.fb_type;
366 		fba->sattr.flags = 0;
367 		fba->sattr.emu_type = sc->sc_fb.fb_type.fb_type;
368 		fba->sattr.dev_specific[0] = -1;
369 		fba->emu_types[0] = sc->sc_fb.fb_type.fb_type;
370 		fba->emu_types[1] = -1;
371 		break;
372 
373 	case FBIOGETCMAP:
374 #define p ((struct fbcmap *)data)
375 		return (bt_getcmap(p, &sc->sc_cmap, 256, 1));
376 
377 	case FBIOPUTCMAP:
378 		/* copy to software map */
379 		error = bt_putcmap(p, &sc->sc_cmap, 256, 1);
380 		if (error)
381 			return (error);
382 		/* now blast them into the chip */
383 		/* XXX should use retrace interrupt */
384 		p9100loadcmap(sc, p->index, p->count);
385 #undef p
386 		break;
387 
388 	case FBIOGVIDEO:
389 		*(int *)data = p9100_get_video(sc);
390 		break;
391 
392 	case FBIOSVIDEO:
393 		p9100_set_video(sc, *(int *)data);
394 		break;
395 
396 	default:
397 		return (ENOTTY);
398 	}
399 	return (0);
400 }
401 
402 int
403 p9100poll(dev_t dev, int events, struct proc *p)
404 {
405 	return seltrue(dev, events, p);
406 }
407 
408 static uint32_t
409 p9100_ctl_read_4(struct p9100_softc *sc, bus_size_t off)
410 {
411 	sc->sc_junk = bus_space_read_4(sc->sc_bustag, sc->sc_fb_memh, off);
412 	return bus_space_read_4(sc->sc_bustag, sc->sc_ctl_memh, off);
413 }
414 
415 static void
416 p9100_ctl_write_4(struct p9100_softc *sc, bus_size_t off, uint32_t v)
417 {
418 	sc->sc_junk = bus_space_read_4(sc->sc_bustag, sc->sc_fb_memh, off);
419 	bus_space_write_4(sc->sc_bustag, sc->sc_ctl_memh, off, v);
420 }
421 
422 #if 0
423 static uint8_t
424 p9100_ramdac_read(struct p9100_softc *sc, bus_size_t off)
425 {
426 	sc->sc_junk = p9100_ctl_read_4(sc, PWRUP_CNFG);
427 	return p9100_ctl_read_4(sc, off) >> 16;
428 }
429 #endif
430 
431 static void
432 p9100_ramdac_write(struct p9100_softc *sc, bus_size_t off, uint8_t v)
433 {
434 	sc->sc_junk = p9100_ctl_read_4(sc, PWRUP_CNFG);
435 	p9100_ctl_write_4(sc, off, v << 16);
436 }
437 
438 /*
439  * Undo the effect of an FBIOSVIDEO that turns the video off.
440  */
441 static void
442 p9100unblank(struct device *dev)
443 {
444 
445 	p9100_set_video((struct p9100_softc *)dev, 1);
446 }
447 
448 static void
449 p9100_set_video(struct p9100_softc *sc, int enable)
450 {
451 	u_int32_t v = p9100_ctl_read_4(sc, SCRN_RPNT_CTL_1);
452 	if (enable)
453 		v |= VIDEO_ENABLED;
454 	else
455 		v &= ~VIDEO_ENABLED;
456 	p9100_ctl_write_4(sc, SCRN_RPNT_CTL_1, v);
457 #if NTCTRL > 0
458 	/* Turn On/Off the TFT if we know how.
459 	 */
460 	tadpole_set_video(enable);
461 #endif
462 }
463 
464 static int
465 p9100_get_video(struct p9100_softc *sc)
466 {
467 	return (p9100_ctl_read_4(sc, SCRN_RPNT_CTL_1) & VIDEO_ENABLED) != 0;
468 }
469 
470 /*
471  * Load a subset of the current (new) colormap into the IBM RAMDAC.
472  */
473 static void
474 p9100loadcmap(struct p9100_softc *sc, int start, int ncolors)
475 {
476 	u_char *p;
477 
478 	p9100_ramdac_write(sc, DAC_CMAP_WRIDX, start);
479 
480 	for (p = sc->sc_cmap.cm_map[start], ncolors *= 3; ncolors-- > 0; p++) {
481 		p9100_ramdac_write(sc, DAC_CMAP_DATA, *p);
482 	}
483 }
484 
485 /*
486  * Return the address that would map the given device at the given
487  * offset, allowing for the given protection, or return -1 for error.
488  */
489 paddr_t
490 p9100mmap(dev_t dev, off_t off, int prot)
491 {
492 	struct p9100_softc *sc = pnozz_cd.cd_devs[minor(dev)];
493 
494 	if (off & PGOFSET)
495 		panic("p9100mmap");
496 	if (off < 0)
497 		return (-1);
498 
499 #define CG3_MMAP_OFFSET	0x04000000
500 	/* Make Xsun think we are a CG3 (SUN3COLOR)
501 	 */
502 	if (off >= CG3_MMAP_OFFSET && off < CG3_MMAP_OFFSET + sc->sc_fb_psize) {
503 		off -= CG3_MMAP_OFFSET;
504 		return (bus_space_mmap(sc->sc_bustag,
505 			sc->sc_fb_paddr,
506 			off,
507 			prot,
508 			BUS_SPACE_MAP_LINEAR));
509 	}
510 
511 	if (off >= sc->sc_fb_psize + sc->sc_ctl_psize + sc->sc_cmd_psize)
512 		return (-1);
513 
514 	if (off < sc->sc_fb_psize) {
515 		return (bus_space_mmap(sc->sc_bustag,
516 			sc->sc_fb_paddr,
517 			off,
518 			prot,
519 			BUS_SPACE_MAP_LINEAR));
520 	}
521 	off -= sc->sc_fb_psize;
522 	if (off < sc->sc_ctl_psize) {
523 		return (bus_space_mmap(sc->sc_bustag,
524 			sc->sc_ctl_paddr,
525 			off,
526 			prot,
527 			BUS_SPACE_MAP_LINEAR));
528 	}
529 	off -= sc->sc_ctl_psize;
530 
531 	return (bus_space_mmap(sc->sc_bustag,
532 		sc->sc_cmd_paddr,
533 		off,
534 		prot,
535 		BUS_SPACE_MAP_LINEAR));
536 }
537