p9100.c revision 1.5 1 /* $NetBSD: p9100.c,v 1.5 2001/11/13 06:58:17 lukem 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.5 2001/11/13 06:58:17 lukem 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_btype; /* 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_btype; /* 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_btype; /* 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_btype = (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_btype = (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_btype = (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
198 node = sa->sa_node;
199
200 /*
201 * When the ROM has mapped in a p9100 display, the address
202 * maps only the video RAM, so in any case we have to map the
203 * registers ourselves. We only need the video RAM if we are
204 * going to print characters via rconsole.
205 */
206 if (sbus_bus_map(sc->sc_bustag, sc->sc_ctl_btype,
207 sc->sc_ctl_paddr, sc->sc_ctl_psize,
208 BUS_SPACE_MAP_LINEAR, 0,
209 &sc->sc_ctl_memh) != 0) {
210 printf("%s: cannot map control registers\n", self->dv_xname);
211 return;
212 }
213
214 if (sbus_bus_map(sc->sc_bustag, sc->sc_cmd_btype,
215 sc->sc_cmd_paddr, sc->sc_cmd_psize,
216 BUS_SPACE_MAP_LINEAR, 0,
217 &sc->sc_cmd_memh) != 0) {
218 printf("%s: cannot map command registers\n", self->dv_xname);
219 return;
220 }
221
222 isconsole = fb_is_console(node);
223
224 if (sa->sa_npromvaddrs != 0)
225 fb->fb_pixels = (caddr_t)sa->sa_promvaddrs[0];
226 if (isconsole && fb->fb_pixels == NULL) {
227 if (sbus_bus_map(sc->sc_bustag, sc->sc_fb_btype,
228 sc->sc_fb_paddr, sc->sc_fb_psize,
229 BUS_SPACE_MAP_LINEAR, 0,
230 &sc->sc_fb_memh) != 0) {
231 printf("%s: cannot map framebuffer\n", self->dv_xname);
232 return;
233 }
234 fb->fb_pixels = (char *)sc->sc_fb_memh;
235 } else {
236 sc->sc_fb_memh = (bus_space_handle_t) fb->fb_pixels;
237 }
238
239 i = p9100_ctl_read_4(sc, 0x0004);
240 switch ((i >> 26) & 7) {
241 case 5: fb->fb_type.fb_depth = 32; break;
242 case 7: fb->fb_type.fb_depth = 24; break;
243 case 3: fb->fb_type.fb_depth = 16; break;
244 case 2: fb->fb_type.fb_depth = 8; break;
245 default: {
246 panic("pnozz: can't determine screen depth (0x%02x)", i);
247 }
248 }
249 fb_setsize_obp(fb, fb->fb_type.fb_depth, 800, 600, node);
250
251 sbus_establish(&sc->sc_sd, &sc->sc_dev);
252
253 fb->fb_type.fb_size = fb->fb_type.fb_height * fb->fb_linebytes;
254 printf(": rev %d, %dx%d, depth %d",
255 (i & 7), fb->fb_type.fb_width, fb->fb_type.fb_height,
256 fb->fb_type.fb_depth);
257
258 fb->fb_type.fb_cmsize = PROM_getpropint(node, "cmsize", 256);
259 if ((1 << fb->fb_type.fb_depth) != fb->fb_type.fb_cmsize)
260 printf(", %d entry colormap", fb->fb_type.fb_cmsize);
261
262 /* Initialize the default color map. */
263 bt_initcmap(&sc->sc_cmap, 256);
264 p9100loadcmap(sc, 0, 256);
265
266 /* make sure we are not blanked */
267 p9100_set_video(sc, 1);
268
269 if (shutdownhook_establish(p9100_shutdown, sc) == NULL) {
270 panic("%s: could not establish shutdown hook",
271 sc->sc_dev.dv_xname);
272 }
273
274 if (isconsole) {
275 printf(" (console)\n");
276 #ifdef RASTERCONSOLE
277 for (i = 0; i < fb->fb_type.fb_size; i++) {
278 if (fb->fb_pixels[i] == 0) {
279 fb->fb_pixels[i] = 1;
280 } else if (fb->fb_pixels[i] == (char) 255) {
281 fb->fb_pixels[i] = 0;
282 }
283 }
284 p9100loadcmap(sc, 255, 1);
285 fbrcons_init(fb);
286 #endif
287 } else
288 printf("\n");
289
290 fb_attach(fb, isconsole);
291 }
292
293 static void
294 p9100_shutdown(arg)
295 void *arg;
296 {
297 struct p9100_softc *sc = arg;
298 #ifdef RASTERCONSOLE
299 struct fbdevice *fb = &sc->sc_fb;
300 int i;
301
302 for (i = 0; i < fb->fb_type.fb_size; i++) {
303 if (fb->fb_pixels[i] == 1) {
304 fb->fb_pixels[i] = 0;
305 } else if (fb->fb_pixels[i] == 0) {
306 fb->fb_pixels[i] = 255;
307 }
308 }
309 sc->sc_cmap.cm_map[0][0] = 0xff;
310 sc->sc_cmap.cm_map[0][1] = 0xff;
311 sc->sc_cmap.cm_map[0][2] = 0xff;
312 sc->sc_cmap.cm_map[1][0] = 0;
313 sc->sc_cmap.cm_map[1][1] = 0;
314 sc->sc_cmap.cm_map[1][2] = 0x80;
315 p9100loadcmap(sc, 0, 2);
316 sc->sc_cmap.cm_map[255][0] = 0;
317 sc->sc_cmap.cm_map[255][1] = 0;
318 sc->sc_cmap.cm_map[255][2] = 0;
319 p9100loadcmap(sc, 255, 1);
320 #endif
321 p9100_set_video(sc, 1);
322 }
323
324 int
325 p9100open(dev_t dev, int flags, int mode, struct proc *p)
326 {
327 int unit = minor(dev);
328
329 if (unit >= pnozz_cd.cd_ndevs || pnozz_cd.cd_devs[unit] == NULL)
330 return (ENXIO);
331 return (0);
332 }
333
334 int
335 p9100close(dev_t dev, int flags, int mode, struct proc *p)
336 {
337 return (0);
338 }
339
340 int
341 p9100ioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
342 {
343 struct p9100_softc *sc = pnozz_cd.cd_devs[minor(dev)];
344 struct fbgattr *fba;
345 int error;
346
347 switch (cmd) {
348
349 case FBIOGTYPE:
350 *(struct fbtype *)data = sc->sc_fb.fb_type;
351 break;
352
353 case FBIOGATTR:
354 fba = (struct fbgattr *)data;
355 fba->real_type = sc->sc_fb.fb_type.fb_type;
356 fba->owner = 0; /* XXX ??? */
357 fba->fbtype = sc->sc_fb.fb_type;
358 fba->sattr.flags = 0;
359 fba->sattr.emu_type = sc->sc_fb.fb_type.fb_type;
360 fba->sattr.dev_specific[0] = -1;
361 fba->emu_types[0] = sc->sc_fb.fb_type.fb_type;
362 fba->emu_types[1] = -1;
363 break;
364
365 case FBIOGETCMAP:
366 #define p ((struct fbcmap *)data)
367 return (bt_getcmap(p, &sc->sc_cmap, 256, 1));
368
369 case FBIOPUTCMAP:
370 /* copy to software map */
371 error = bt_putcmap(p, &sc->sc_cmap, 256, 1);
372 if (error)
373 return (error);
374 /* now blast them into the chip */
375 /* XXX should use retrace interrupt */
376 p9100loadcmap(sc, p->index, p->count);
377 #undef p
378 break;
379
380 case FBIOGVIDEO:
381 *(int *)data = p9100_get_video(sc);
382 break;
383
384 case FBIOSVIDEO:
385 p9100_set_video(sc, *(int *)data);
386 break;
387
388 default:
389 return (ENOTTY);
390 }
391 return (0);
392 }
393
394 int
395 p9100poll(dev_t dev, int events, struct proc *p)
396 {
397 return seltrue(dev, events, p);
398 }
399
400 static uint32_t
401 p9100_ctl_read_4(struct p9100_softc *sc, bus_size_t off)
402 {
403 sc->sc_junk = bus_space_read_4(sc->sc_bustag, sc->sc_fb_memh, off);
404 return bus_space_read_4(sc->sc_bustag, sc->sc_ctl_memh, off);
405 }
406
407 static void
408 p9100_ctl_write_4(struct p9100_softc *sc, bus_size_t off, uint32_t v)
409 {
410 sc->sc_junk = bus_space_read_4(sc->sc_bustag, sc->sc_fb_memh, off);
411 bus_space_write_4(sc->sc_bustag, sc->sc_ctl_memh, off, v);
412 }
413
414 #if 0
415 static uint8_t
416 p9100_ramdac_read(struct p9100_softc *sc, bus_size_t off)
417 {
418 sc->sc_junk = p9100_ctl_read_4(sc, PWRUP_CNFG);
419 return p9100_ctl_read_4(sc, off) >> 16;
420 }
421 #endif
422
423 static void
424 p9100_ramdac_write(struct p9100_softc *sc, bus_size_t off, uint8_t v)
425 {
426 sc->sc_junk = p9100_ctl_read_4(sc, PWRUP_CNFG);
427 p9100_ctl_write_4(sc, off, v << 16);
428 }
429
430 /*
431 * Undo the effect of an FBIOSVIDEO that turns the video off.
432 */
433 static void
434 p9100unblank(struct device *dev)
435 {
436
437 p9100_set_video((struct p9100_softc *)dev, 1);
438 }
439
440 static void
441 p9100_set_video(struct p9100_softc *sc, int enable)
442 {
443 u_int32_t v = p9100_ctl_read_4(sc, SCRN_RPNT_CTL_1);
444 if (enable)
445 v |= VIDEO_ENABLED;
446 else
447 v &= ~VIDEO_ENABLED;
448 p9100_ctl_write_4(sc, SCRN_RPNT_CTL_1, v);
449 #if NTCTRL > 0
450 /* Turn On/Off the TFT if we know how.
451 */
452 tadpole_set_video(enable);
453 #endif
454 }
455
456 static int
457 p9100_get_video(struct p9100_softc *sc)
458 {
459 return (p9100_ctl_read_4(sc, SCRN_RPNT_CTL_1) & VIDEO_ENABLED) != 0;
460 }
461
462 /*
463 * Load a subset of the current (new) colormap into the IBM RAMDAC.
464 */
465 static void
466 p9100loadcmap(struct p9100_softc *sc, int start, int ncolors)
467 {
468 u_char *p;
469
470 p9100_ramdac_write(sc, DAC_CMAP_WRIDX, start);
471
472 for (p = sc->sc_cmap.cm_map[start], ncolors *= 3; ncolors-- > 0; p++) {
473 p9100_ramdac_write(sc, DAC_CMAP_DATA, *p);
474 }
475 }
476
477 /*
478 * Return the address that would map the given device at the given
479 * offset, allowing for the given protection, or return -1 for error.
480 */
481 paddr_t
482 p9100mmap(dev_t dev, off_t off, int prot)
483 {
484 struct p9100_softc *sc = pnozz_cd.cd_devs[minor(dev)];
485
486 if (off & PGOFSET)
487 panic("p9100mmap");
488 if (off < 0)
489 return (-1);
490
491 #define CG3_MMAP_OFFSET 0x04000000
492 /* Make Xsun think we are a CG3 (SUN3COLOR)
493 */
494 if (off >= CG3_MMAP_OFFSET && off < CG3_MMAP_OFFSET + sc->sc_fb_psize) {
495 off -= CG3_MMAP_OFFSET;
496 return (bus_space_mmap(sc->sc_bustag,
497 sc->sc_fb_paddr,
498 off,
499 prot,
500 BUS_SPACE_MAP_LINEAR));
501 }
502
503 if (off >= sc->sc_fb_psize + sc->sc_ctl_psize + sc->sc_cmd_psize)
504 return (-1);
505
506 if (off < sc->sc_fb_psize) {
507 return (bus_space_mmap(sc->sc_bustag,
508 sc->sc_fb_paddr,
509 off,
510 prot,
511 BUS_SPACE_MAP_LINEAR));
512 }
513 off -= sc->sc_fb_psize;
514 if (off < sc->sc_ctl_psize) {
515 return (bus_space_mmap(sc->sc_bustag,
516 sc->sc_ctl_paddr,
517 off,
518 prot,
519 BUS_SPACE_MAP_LINEAR));
520 }
521 off -= sc->sc_ctl_psize;
522
523 return (bus_space_mmap(sc->sc_bustag,
524 sc->sc_cmd_paddr,
525 off,
526 prot,
527 BUS_SPACE_MAP_LINEAR));
528 }
529