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