agten.c revision 1.2 1 /* $NetBSD: agten.c,v 1.2 2007/08/26 07:24:28 macallan Exp $ */
2
3 /*-
4 * Copyright (c) 2007 Michael Lorenz
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of The NetBSD Foundation nor the names of its
16 * contributors may be used to endorse or promote products derived
17 * from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: agten.c,v 1.2 2007/08/26 07:24:28 macallan Exp $");
34
35 /*
36 * a driver for the Fujitsu AG-10e SBus framebuffer
37 *
38 * this thing is Frankenstein's Monster among graphics boards.
39 * it contains three graphics chips:
40 * a GLint - 24bit stuff, double-buffered
41 * an Imagine 128 which provides an 8bit overlay
42 * a Weitek P9100 which provides WIDs
43 * so here we need to mess only with the P9100 and the I128 - for X we just
44 * hide the overlay and let the Xserver mess with the GLint
45 */
46
47 #include <sys/param.h>
48 #include <sys/systm.h>
49 #include <sys/kernel.h>
50 #include <sys/device.h>
51 #include <sys/proc.h>
52 #include <sys/mutex.h>
53 #include <sys/ioctl.h>
54 #include <sys/kernel.h>
55 #include <sys/systm.h>
56 #include <sys/malloc.h>
57
58 #include <dev/sun/fbio.h>
59 #include <dev/sun/fbvar.h>
60 #include <dev/sun/btreg.h>
61 #include <dev/sun/btvar.h>
62
63 #include <machine/bus.h>
64 #include <machine/autoconf.h>
65
66 #include <dev/sbus/sbusvar.h>
67
68 #include <dev/wscons/wsconsio.h>
69 #include <dev/wscons/wsdisplayvar.h>
70 #include <dev/rasops/rasops.h>
71 #include <dev/wsfont/wsfont.h>
72
73 #include <dev/wscons/wsdisplay_vconsvar.h>
74
75 #include <dev/sbus/p9100reg.h>
76 #include <dev/ic/ibm561reg.h>
77 #include <dev/ic/i128reg.h>
78 #include <dev/ic/i128var.h>
79
80 #include "opt_agten.h"
81
82 static int agten_match(struct device *, struct cfdata *, void *);
83 static void agten_attach(struct device *, struct device *, void *);
84
85 #if 0
86 static void agten_unblank(struct device *);
87 #endif
88
89 static int agten_ioctl(void *, void *, u_long, void *, int, struct lwp *);
90 static paddr_t agten_mmap(void *, void *, off_t, int);
91 static void agten_init_screen(void *, struct vcons_screen *, int, long *);
92
93 struct agten_softc {
94 struct device sc_dev; /* base device */
95 struct sbusdev sc_sd; /* sbus device */
96 struct fbdevice sc_fb; /* frame buffer device */
97
98 struct vcons_screen sc_console_screen;
99 struct wsscreen_descr sc_defaultscreen_descr;
100 const struct wsscreen_descr *sc_screens[1];
101 struct wsscreen_list sc_screenlist;
102
103 bus_space_tag_t sc_bustag;
104
105 bus_space_handle_t sc_i128_fbh;
106 bus_size_t sc_i128_fbsz;
107 bus_space_handle_t sc_i128_regh;
108 bus_space_handle_t sc_p9100_regh;
109
110 uint32_t sc_width;
111 uint32_t sc_height; /* panel width / height */
112 uint32_t sc_stride;
113 uint32_t sc_depth;
114
115 union bt_cmap sc_cmap; /* Brooktree color map */
116
117 int sc_mode;
118 int sc_video, sc_powerstate;
119 uint32_t sc_bg;
120 struct vcons_data vd;
121 };
122
123 CFATTACH_DECL(agten, sizeof(struct agten_softc),
124 agten_match, agten_attach, NULL, NULL);
125
126
127 static int agten_putcmap(struct agten_softc *, struct wsdisplay_cmap *);
128 static int agten_getcmap(struct agten_softc *, struct wsdisplay_cmap *);
129 static int agten_putpalreg(struct agten_softc *, uint8_t, uint8_t,
130 uint8_t, uint8_t);
131 static void agten_init(struct agten_softc *);
132
133 static void agten_copycols(void *, int, int, int, int);
134 static void agten_erasecols(void *, int, int, int, long);
135 static void agten_copyrows(void *, int, int, int);
136 static void agten_eraserows(void *, int, int, long);
137
138 extern const u_char rasops_cmap[768];
139
140 struct wsdisplay_accessops agten_accessops = {
141 agten_ioctl,
142 agten_mmap,
143 NULL, /* alloc_screen */
144 NULL, /* free_screen */
145 NULL, /* show_screen */
146 NULL, /* load_font */
147 NULL, /* pollc */
148 NULL /* scroll */
149 };
150
151 static inline void
152 agten_write_dac(struct agten_softc *sc, int reg, uint8_t val)
153 {
154 bus_space_write_4(sc->sc_bustag, sc->sc_p9100_regh,
155 0x200 + (reg << 2), (uint32_t)val << 16);
156 }
157
158 static inline void
159 agten_write_idx(struct agten_softc *sc, int offset)
160 {
161 bus_space_write_4(sc->sc_bustag, sc->sc_p9100_regh,
162 0x200 + (IBM561_ADDR_LOW << 2), (offset & 0xff) << 16);
163 bus_space_write_4(sc->sc_bustag, sc->sc_p9100_regh,
164 0x200 + (IBM561_ADDR_HIGH << 2), ((offset >> 8) & 0xff) << 16);
165 }
166
167 static inline void
168 agten_write_dac_10(struct agten_softc *sc, int reg, uint16_t val)
169 {
170 agten_write_dac(sc, reg, (val >> 2) & 0xff);
171 agten_write_dac(sc, reg, (val & 0x3) << 6);
172 }
173
174 static int
175 agten_match(struct device *dev, struct cfdata *cf, void *aux)
176 {
177 struct sbus_attach_args *sa = aux;
178
179 if (strcmp("PFU,aga", sa->sa_name) == 0)
180 return 100;
181 return 0;
182 }
183
184 static void
185 agten_attach(struct device *parent, struct device *dev, void *aux)
186 {
187 struct agten_softc *sc = (struct agten_softc *)dev;
188 struct sbus_attach_args *sa = aux;
189 struct fbdevice *fb = &sc->sc_fb;
190 struct wsemuldisplaydev_attach_args aa;
191 struct rasops_info *ri;
192 long defattr;
193 uint32_t reg;
194 int node = sa->sa_node;
195 int console;
196
197 sc->sc_defaultscreen_descr = (struct wsscreen_descr){
198 "default",
199 0, 0,
200 NULL,
201 8, 16,
202 WSSCREEN_WSCOLORS | WSSCREEN_HILIT,
203 NULL
204 };
205 sc->sc_screens[0] = &sc->sc_defaultscreen_descr;
206 sc->sc_screenlist = (struct wsscreen_list){1, sc->sc_screens};
207 sc->sc_mode = WSDISPLAYIO_MODE_EMUL;
208 sc->sc_bustag = sa->sa_bustag;
209 #if 0
210 sc->sc_shadowfb = malloc(sc->sc_fbsize, M_DEVBUF, M_WAITOK);
211
212 dict = device_properties(&sc->sc_dev);
213
214 prop_dictionary_get_bool(dict, "is_console", &console);
215 #endif
216
217 reg = prom_getpropint(node, "i128_fb_physaddr", -1);
218 sc->sc_i128_fbsz = prom_getpropint(node, "i128_fb_size", -1);
219 if (sbus_bus_map(sc->sc_bustag,
220 sa->sa_reg[0].oa_space, sa->sa_reg[0].oa_base + reg,
221 sc->sc_i128_fbsz, BUS_SPACE_MAP_LINEAR, &sc->sc_i128_fbh) != 0) {
222
223 aprint_error("%s: unable to map the framebuffer\n",
224 dev->dv_xname);
225 return;
226 }
227 fb->fb_pixels = bus_space_vaddr(sc->sc_bustag, sc->sc_i128_fbh);
228
229 reg = prom_getpropint(node, "p9100_reg_physaddr", -1);
230 if (sbus_bus_map(sc->sc_bustag,
231 sa->sa_reg[0].oa_space, sa->sa_reg[0].oa_base + reg,
232 0x8000, 0, &sc->sc_p9100_regh) != 0) {
233
234 aprint_error("%s: unable to map P9100 registers\n",
235 dev->dv_xname);
236 return;
237 }
238
239 reg = prom_getpropint(node, "i128_reg_physaddr", -1);
240 if (sbus_bus_map(sc->sc_bustag,
241 sa->sa_reg[0].oa_space, sa->sa_reg[0].oa_base + reg,
242 0x10000, 0, &sc->sc_i128_regh) != 0) {
243
244 aprint_error("%s: unable to map I128 registers\n",
245 dev->dv_xname);
246 return;
247 }
248
249 sbus_establish(&sc->sc_sd, &sc->sc_dev);
250 #if 0
251 bus_intr_establish(sc->sc_bustag, sa->sa_pri, IPL_BIO,
252 agten_intr, sc);
253 #endif
254
255 sc->sc_width = prom_getpropint(node, "ffb_width", 800);
256 sc->sc_height = prom_getpropint(node, "ffb_height", 600);
257 sc->sc_depth = prom_getpropint(node, "ffb_depth", 8);
258 sc->sc_stride = sc->sc_width * (sc->sc_depth >> 3);
259
260 agten_init(sc);
261
262 console = fb_is_console(node);
263
264 vcons_init(&sc->vd, sc, &sc->sc_defaultscreen_descr,
265 &agten_accessops);
266 sc->vd.init_screen = agten_init_screen;
267
268 ri = &sc->sc_console_screen.scr_ri;
269
270 if (console) {
271 vcons_init_screen(&sc->vd, &sc->sc_console_screen, 1,
272 &defattr);
273 sc->sc_console_screen.scr_flags |= VCONS_SCREEN_IS_STATIC;
274
275 sc->sc_defaultscreen_descr.textops = &ri->ri_ops;
276 sc->sc_defaultscreen_descr.capabilities = ri->ri_caps;
277 sc->sc_defaultscreen_descr.nrows = ri->ri_rows;
278 sc->sc_defaultscreen_descr.ncols = ri->ri_cols;
279 wsdisplay_cnattach(&sc->sc_defaultscreen_descr, ri, 0, 0,
280 defattr);
281 i128_rectfill(sc->sc_bustag, sc->sc_i128_regh, 0, 0,
282 sc->sc_width, sc->sc_height,
283 ri->ri_devcmap[(defattr >> 16) & 0xff]);
284 } else {
285 /*
286 * since we're not the console we can postpone the rest
287 * until someone actually allocates a screen for us
288 */
289 }
290
291 /* Initialize the default color map. */
292
293 aa.console = console;
294 aa.scrdata = &sc->sc_screenlist;
295 aa.accessops = &agten_accessops;
296 aa.accesscookie = &sc->vd;
297
298 config_found(&sc->sc_dev, &aa, wsemuldisplaydevprint);
299 }
300
301 static int
302 agten_ioctl(void *v, void *vs, u_long cmd, void *data, int flag,
303 struct lwp *l)
304 {
305 struct vcons_data *vd = v;
306 struct agten_softc *sc = vd->cookie;
307 struct wsdisplay_fbinfo *wdf;
308 struct vcons_screen *ms = vd->active;
309
310 switch (cmd) {
311
312 case WSDISPLAYIO_GINFO:
313 if (ms == NULL)
314 return ENODEV;
315 wdf = (void *)data;
316 wdf->height = ms->scr_ri.ri_height;
317 wdf->width = ms->scr_ri.ri_width;
318 wdf->depth = ms->scr_ri.ri_depth;
319 wdf->cmsize = 256;
320 return 0;
321
322 case WSDISPLAYIO_GETCMAP:
323 return agten_getcmap(sc,
324 (struct wsdisplay_cmap *)data);
325
326 case WSDISPLAYIO_PUTCMAP:
327 return agten_putcmap(sc,
328 (struct wsdisplay_cmap *)data);
329
330 case WSDISPLAYIO_LINEBYTES:
331 *(u_int *)data = sc->sc_stride;
332 return 0;
333
334 case WSDISPLAYIO_SMODE:
335 {
336 int new_mode = *(int*)data;
337 if (new_mode != sc->sc_mode) {
338 sc->sc_mode = new_mode;
339 if(new_mode == WSDISPLAYIO_MODE_EMUL) {
340 agten_init(sc);
341 vcons_redraw_screen(ms);
342 }
343 }
344 }
345 return 0;
346 }
347 return EPASSTHROUGH;
348 }
349
350 static paddr_t
351 agten_mmap(void *v, void *vs, off_t offset, int prot)
352 {
353 struct vcons_data *vd = v;
354 struct agten_softc *sc = vd->cookie;
355
356 if (offset < sc->sc_i128_fbsz)
357 return bus_space_mmap(sc->sc_bustag, sc->sc_i128_fbh, offset,
358 prot, BUS_SPACE_MAP_LINEAR);
359 return -1;
360 }
361
362 static void
363 agten_init_screen(void *cookie, struct vcons_screen *scr,
364 int existing, long *defattr)
365 {
366 struct agten_softc *sc = cookie;
367 struct rasops_info *ri = &scr->scr_ri;
368
369 ri->ri_depth = sc->sc_depth;
370 ri->ri_width = sc->sc_width;
371 ri->ri_height = sc->sc_height;
372 ri->ri_stride = sc->sc_stride;
373 ri->ri_flg = RI_CENTER | RI_FULLCLEAR;
374
375 ri->ri_bits = (char *)sc->sc_fb.fb_pixels;
376
377 if (existing) {
378 ri->ri_flg |= RI_CLEAR;
379 }
380
381 rasops_init(ri, sc->sc_height / 8, sc->sc_width / 8);
382 ri->ri_caps = WSSCREEN_WSCOLORS;
383
384 rasops_reconfig(ri, sc->sc_height / ri->ri_font->fontheight,
385 sc->sc_width / ri->ri_font->fontwidth);
386
387 ri->ri_hw = scr;
388 ri->ri_ops.copyrows = agten_copyrows;
389 ri->ri_ops.eraserows = agten_eraserows;
390 ri->ri_ops.copycols = agten_copycols;
391 ri->ri_ops.erasecols = agten_erasecols;
392
393 }
394
395 static int
396 agten_putcmap(struct agten_softc *sc, struct wsdisplay_cmap *cm)
397 {
398 u_int index = cm->index;
399 u_int count = cm->count;
400 int i, error;
401 u_char rbuf[256], gbuf[256], bbuf[256];
402 u_char *r, *g, *b;
403
404 if (cm->index >= 256 || cm->count > 256 ||
405 (cm->index + cm->count) > 256)
406 return EINVAL;
407 error = copyin(cm->red, &rbuf[index], count);
408 if (error)
409 return error;
410 error = copyin(cm->green, &gbuf[index], count);
411 if (error)
412 return error;
413 error = copyin(cm->blue, &bbuf[index], count);
414 if (error)
415 return error;
416
417 r = &rbuf[index];
418 g = &gbuf[index];
419 b = &bbuf[index];
420
421 for (i = 0; i < count; i++) {
422 agten_putpalreg(sc, index, *r, *g, *b);
423 index++;
424 r++, g++, b++;
425 }
426 return 0;
427 }
428
429 static int
430 agten_getcmap(struct agten_softc *sc, struct wsdisplay_cmap *cm)
431 {
432 u_int index = cm->index;
433 u_int count = cm->count;
434 int error, i;
435 uint8_t red[256], green[256], blue[256];
436
437 if (index >= 255 || count > 256 || index + count > 256)
438 return EINVAL;
439
440 i = index;
441 while (i < (index + count)) {
442 red[i] = sc->sc_cmap.cm_map[i][0];
443 green[i] = sc->sc_cmap.cm_map[i][1];
444 blue[i] = sc->sc_cmap.cm_map[i][2];
445 i++;
446 }
447 error = copyout(&red[index], cm->red, count);
448 if (error)
449 return error;
450 error = copyout(&green[index], cm->green, count);
451 if (error)
452 return error;
453 error = copyout(&blue[index], cm->blue, count);
454 if (error)
455 return error;
456
457 return 0;
458 }
459
460 static int
461 agten_putpalreg(struct agten_softc *sc, uint8_t idx, uint8_t r, uint8_t g,
462 uint8_t b)
463 {
464
465 sc->sc_cmap.cm_map[idx][0] = r;
466 sc->sc_cmap.cm_map[idx][1] = g;
467 sc->sc_cmap.cm_map[idx][2] = b;
468 agten_write_idx(sc, IBM561_CMAP_TABLE + idx);
469 agten_write_dac(sc, IBM561_CMD_CMAP, r);
470 agten_write_dac(sc, IBM561_CMD_CMAP, g);
471 agten_write_dac(sc, IBM561_CMD_CMAP, b);
472 return 0;
473 }
474
475 static void
476 agten_init(struct agten_softc *sc)
477 {
478 int i, j;
479 uint32_t src, srcw;
480 volatile uint32_t junk;
481
482 /* first we set up the colour map */
483 j = 0;
484 for (i = 0; i < 256; i++) {
485
486 agten_putpalreg(sc, i, rasops_cmap[j], rasops_cmap[j + 1],
487 rasops_cmap[j + 2]);
488 j += 3;
489 }
490
491 /* now set up some window attributes */
492 agten_write_idx(sc, IBM561_OL_WINTYPE);
493 agten_write_dac_10(sc, IBM561_CMD_FB_WAT, 0x00);
494 for (i = 1; i < 256; i++)
495 agten_write_dac_10(sc, IBM561_CMD_FB_WAT, 0x01);
496
497 src = 0;
498 srcw = sc->sc_width << 16 | sc->sc_height;
499 bus_space_write_4(sc->sc_bustag, sc->sc_p9100_regh, FOREGROUND_COLOR,
500 0x0);
501 bus_space_write_4(sc->sc_bustag, sc->sc_p9100_regh, BACKGROUND_COLOR,
502 0x0);
503 bus_space_write_4(sc->sc_bustag, sc->sc_p9100_regh, RASTER_OP, ROP_PAT);
504 bus_space_write_4(sc->sc_bustag, sc->sc_p9100_regh, COORD_INDEX, 0);
505 bus_space_write_4(sc->sc_bustag, sc->sc_p9100_regh, RECT_RTW_XY, src);
506 bus_space_write_4(sc->sc_bustag, sc->sc_p9100_regh, RECT_RTW_XY, srcw);
507 junk = bus_space_read_4(sc->sc_bustag, sc->sc_p9100_regh, COMMAND_QUAD);
508
509 i128_init(sc->sc_bustag, sc->sc_i128_regh, sc->sc_stride, 8);
510 }
511
512 static void
513 agten_copycols(void *cookie, int row, int srccol, int dstcol, int ncols)
514 {
515 struct rasops_info *ri = cookie;
516 struct vcons_screen *scr = ri->ri_hw;
517 struct agten_softc *sc = scr->scr_cookie;
518 int32_t xs, xd, y, width, height;
519
520 xs = ri->ri_xorigin + ri->ri_font->fontwidth * srccol;
521 xd = ri->ri_xorigin + ri->ri_font->fontwidth * dstcol;
522 y = ri->ri_yorigin + ri->ri_font->fontheight * row;
523 width = ri->ri_font->fontwidth * ncols;
524 height = ri->ri_font->fontheight;
525 i128_bitblt(sc->sc_bustag, sc->sc_i128_regh, xs, y, xd, y, width,
526 height, CR_COPY);
527 }
528
529 static void
530 agten_erasecols(void *cookie, int row, int startcol, int ncols, long fillattr)
531 {
532 struct rasops_info *ri = cookie;
533 struct vcons_screen *scr = ri->ri_hw;
534 struct agten_softc *sc = scr->scr_cookie;
535 int32_t x, y, width, height, bg;
536
537 x = ri->ri_xorigin + ri->ri_font->fontwidth * startcol;
538 y = ri->ri_yorigin + ri->ri_font->fontheight * row;
539 width = ri->ri_font->fontwidth * ncols;
540 height = ri->ri_font->fontheight;
541 bg = (uint32_t)ri->ri_devcmap[(fillattr >> 16) & 0xff];
542 i128_rectfill(sc->sc_bustag, sc->sc_i128_regh, x, y, width, height, bg);
543 }
544
545 static void
546 agten_copyrows(void *cookie, int srcrow, int dstrow, int nrows)
547 {
548 struct rasops_info *ri = cookie;
549 struct vcons_screen *scr = ri->ri_hw;
550 struct agten_softc *sc = scr->scr_cookie;
551 int32_t x, ys, yd, width, height;
552
553 x = ri->ri_xorigin;
554 ys = ri->ri_yorigin + ri->ri_font->fontheight * srcrow;
555 yd = ri->ri_yorigin + ri->ri_font->fontheight * dstrow;
556 width = ri->ri_emuwidth;
557 height = ri->ri_font->fontheight * nrows;
558 i128_bitblt(sc->sc_bustag, sc->sc_i128_regh, x, ys, x, yd, width,
559 height, CR_COPY);
560 }
561
562 static void
563 agten_eraserows(void *cookie, int row, int nrows, long fillattr)
564 {
565 struct rasops_info *ri = cookie;
566 struct vcons_screen *scr = ri->ri_hw;
567 struct agten_softc *sc = scr->scr_cookie;
568 int32_t x, y, width, height, bg;
569
570 if ((row == 0) && (nrows == ri->ri_rows)) {
571 x = y = 0;
572 width = ri->ri_width;
573 height = ri->ri_height;
574 } else {
575 x = ri->ri_xorigin;
576 y = ri->ri_yorigin + ri->ri_font->fontheight * row;
577 width = ri->ri_emuwidth;
578 height = ri->ri_font->fontheight * nrows;
579 }
580 bg = (uint32_t)ri->ri_devcmap[(fillattr >> 16) & 0xff];
581 i128_rectfill(sc->sc_bustag, sc->sc_i128_regh, x, y, width, height, bg);
582 }
583