p9100.c revision 1.2.4.1 1 /* $NetBSD: p9100.c,v 1.2.4.1 2002/01/10 19:58:12 thorpej 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.2.4.1 2002/01/10 19:58:12 thorpej 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