agten.c revision 1.1 1 /* $NetBSD: agten.c,v 1.1 2007/08/26 00:39:39 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.1 2007/08/26 00:39:39 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 void agten_restore_palette(struct agten_softc *);
130 static int agten_putpalreg(struct agten_softc *, uint8_t, uint8_t,
131 uint8_t, uint8_t);
132 static void agten_init(struct agten_softc *);
133
134 static void agten_copycols(void *, int, int, int, int);
135 static void agten_erasecols(void *, int, int, int, long);
136 static void agten_copyrows(void *, int, int, int);
137 static void agten_eraserows(void *, int, int, long);
138
139 extern const u_char rasops_cmap[768];
140
141 struct wsdisplay_accessops agten_accessops = {
142 agten_ioctl,
143 agten_mmap,
144 NULL, /* alloc_screen */
145 NULL, /* free_screen */
146 NULL, /* show_screen */
147 NULL, /* load_font */
148 NULL, /* pollc */
149 NULL /* scroll */
150 };
151
152 static inline void
153 agten_write_dac(struct agten_softc *sc, int reg, uint8_t val)
154 {
155 bus_space_write_4(sc->sc_bustag, sc->sc_p9100_regh,
156 0x200 + (reg << 2), (uint32_t)val << 16);
157 }
158
159 static inline void
160 agten_write_idx(struct agten_softc *sc, int offset)
161 {
162 bus_space_write_4(sc->sc_bustag, sc->sc_p9100_regh,
163 0x200 + (IBM561_ADDR_LOW << 2), (offset & 0xff) << 16);
164 bus_space_write_4(sc->sc_bustag, sc->sc_p9100_regh,
165 0x200 + (IBM561_ADDR_HIGH << 2), ((offset >> 8) & 0xff) << 16);
166 }
167
168 static inline void
169 agten_write_dac_10(struct agten_softc *sc, int reg, uint16_t val)
170 {
171 agten_write_dac(sc, reg, (val >> 2) & 0xff);
172 agten_write_dac(sc, reg, (val & 0x3) << 6);
173 }
174
175 static int
176 agten_match(struct device *dev, struct cfdata *cf, void *aux)
177 {
178 struct sbus_attach_args *sa = aux;
179
180 if (strcmp("PFU,aga", sa->sa_name) == 0)
181 return 100;
182 return 0;
183 }
184
185 static void
186 agten_attach(struct device *parent, struct device *dev, void *aux)
187 {
188 struct agten_softc *sc = (struct agten_softc *)dev;
189 struct sbus_attach_args *sa = aux;
190 struct fbdevice *fb = &sc->sc_fb;
191 struct wsemuldisplaydev_attach_args aa;
192 struct rasops_info *ri;
193 long defattr;
194 uint32_t reg;
195 int node = sa->sa_node;
196 int console;
197
198 sc->sc_defaultscreen_descr = (struct wsscreen_descr){
199 "default",
200 0, 0,
201 NULL,
202 8, 16,
203 WSSCREEN_WSCOLORS | WSSCREEN_HILIT,
204 NULL
205 };
206 sc->sc_screens[0] = &sc->sc_defaultscreen_descr;
207 sc->sc_screenlist = (struct wsscreen_list){1, sc->sc_screens};
208 sc->sc_mode = WSDISPLAYIO_MODE_EMUL;
209 sc->sc_bustag = sa->sa_bustag;
210 #if 0
211 sc->sc_shadowfb = malloc(sc->sc_fbsize, M_DEVBUF, M_WAITOK);
212
213 dict = device_properties(&sc->sc_dev);
214
215 prop_dictionary_get_bool(dict, "is_console", &console);
216 #endif
217
218 reg = prom_getpropint(node, "i128_fb_physaddr", -1);
219 sc->sc_i128_fbsz = prom_getpropint(node, "i128_fb_size", -1);
220 if (sbus_bus_map(sc->sc_bustag,
221 sa->sa_reg[0].oa_space, sa->sa_reg[0].oa_base + reg,
222 sc->sc_i128_fbsz, BUS_SPACE_MAP_LINEAR, &sc->sc_i128_fbh) != 0) {
223
224 aprint_error("%s: unable to map the framebuffer\n",
225 dev->dv_xname);
226 return;
227 }
228 fb->fb_pixels = bus_space_vaddr(sc->sc_bustag, sc->sc_i128_fbh);
229
230 reg = prom_getpropint(node, "p9100_reg_physaddr", -1);
231 if (sbus_bus_map(sc->sc_bustag,
232 sa->sa_reg[0].oa_space, sa->sa_reg[0].oa_base + reg,
233 0x8000, 0, &sc->sc_p9100_regh) != 0) {
234
235 aprint_error("%s: unable to map P9100 registers\n",
236 dev->dv_xname);
237 return;
238 }
239
240 reg = prom_getpropint(node, "i128_reg_physaddr", -1);
241 if (sbus_bus_map(sc->sc_bustag,
242 sa->sa_reg[0].oa_space, sa->sa_reg[0].oa_base + reg,
243 0x10000, 0, &sc->sc_i128_regh) != 0) {
244
245 aprint_error("%s: unable to map I128 registers\n",
246 dev->dv_xname);
247 return;
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 } else {
282 /*
283 * since we're not the console we can postpone the rest
284 * until someone actually allocates a screen for us
285 */
286 }
287
288 /* Initialize the default color map. */
289
290 aa.console = console;
291 aa.scrdata = &sc->sc_screenlist;
292 aa.accessops = &agten_accessops;
293 aa.accesscookie = &sc->vd;
294
295 config_found(&sc->sc_dev, &aa, wsemuldisplaydevprint);
296 }
297
298 static int
299 agten_ioctl(void *v, void *vs, u_long cmd, void *data, int flag,
300 struct lwp *l)
301 {
302 struct vcons_data *vd = v;
303 struct agten_softc *sc = vd->cookie;
304 struct wsdisplay_fbinfo *wdf;
305 struct vcons_screen *ms = vd->active;
306
307 switch (cmd) {
308
309 case WSDISPLAYIO_GINFO:
310 if (ms == NULL)
311 return ENODEV;
312 wdf = (void *)data;
313 wdf->height = ms->scr_ri.ri_height;
314 wdf->width = ms->scr_ri.ri_width;
315 wdf->depth = ms->scr_ri.ri_depth;
316 wdf->cmsize = 256;
317 return 0;
318
319 case WSDISPLAYIO_GETCMAP:
320 return agten_getcmap(sc,
321 (struct wsdisplay_cmap *)data);
322
323 case WSDISPLAYIO_PUTCMAP:
324 return agten_putcmap(sc,
325 (struct wsdisplay_cmap *)data);
326
327 case WSDISPLAYIO_LINEBYTES:
328 *(u_int *)data = sc->sc_stride;
329 return 0;
330
331 case WSDISPLAYIO_SMODE:
332 {
333 int new_mode = *(int*)data;
334 if (new_mode != sc->sc_mode) {
335 sc->sc_mode = new_mode;
336 if(new_mode == WSDISPLAYIO_MODE_EMUL) {
337 agten_restore_palette(sc);
338 vcons_redraw_screen(ms);
339 }
340 }
341 }
342 return 0;
343 }
344 return EPASSTHROUGH;
345 }
346
347 static paddr_t
348 agten_mmap(void *v, void *vs, off_t offset, int prot)
349 {
350 struct vcons_data *vd = v;
351 struct agten_softc *sc = vd->cookie;
352
353 if (offset < sc->sc_i128_fbsz)
354 return bus_space_mmap(sc->sc_bustag, sc->sc_i128_fbh, offset,
355 prot, BUS_SPACE_MAP_LINEAR);
356 return -1;
357 }
358
359 static void
360 agten_init_screen(void *cookie, struct vcons_screen *scr,
361 int existing, long *defattr)
362 {
363 struct agten_softc *sc = cookie;
364 struct rasops_info *ri = &scr->scr_ri;
365
366 ri->ri_depth = sc->sc_depth;
367 ri->ri_width = sc->sc_width;
368 ri->ri_height = sc->sc_height;
369 ri->ri_stride = sc->sc_stride;
370 ri->ri_flg = RI_CENTER | RI_FULLCLEAR;
371
372 ri->ri_bits = (char *)sc->sc_fb.fb_pixels;
373
374 if (existing) {
375 ri->ri_flg |= RI_CLEAR;
376 }
377
378 rasops_init(ri, sc->sc_height / 8, sc->sc_width / 8);
379 ri->ri_caps = WSSCREEN_WSCOLORS;
380
381 rasops_reconfig(ri, sc->sc_height / ri->ri_font->fontheight,
382 sc->sc_width / ri->ri_font->fontwidth);
383
384 ri->ri_hw = scr;
385 ri->ri_ops.copyrows = agten_copyrows;
386 ri->ri_ops.eraserows = agten_eraserows;
387 ri->ri_ops.copycols = agten_copycols;
388 ri->ri_ops.erasecols = agten_erasecols;
389
390 }
391
392 static int
393 agten_putcmap(struct agten_softc *sc, struct wsdisplay_cmap *cm)
394 {
395 u_int index = cm->index;
396 u_int count = cm->count;
397 int i, error;
398 u_char rbuf[256], gbuf[256], bbuf[256];
399 u_char *r, *g, *b;
400
401 if (cm->index >= 256 || cm->count > 256 ||
402 (cm->index + cm->count) > 256)
403 return EINVAL;
404 error = copyin(cm->red, &rbuf[index], count);
405 if (error)
406 return error;
407 error = copyin(cm->green, &gbuf[index], count);
408 if (error)
409 return error;
410 error = copyin(cm->blue, &bbuf[index], count);
411 if (error)
412 return error;
413
414 r = &rbuf[index];
415 g = &gbuf[index];
416 b = &bbuf[index];
417
418 for (i = 0; i < count; i++) {
419 agten_putpalreg(sc, index, *r, *g, *b);
420 index++;
421 r++, g++, b++;
422 }
423 return 0;
424 }
425
426 static int
427 agten_getcmap(struct agten_softc *sc, struct wsdisplay_cmap *cm)
428 {
429 u_int index = cm->index;
430 u_int count = cm->count;
431 int error, i;
432 uint8_t red[256], green[256], blue[256];
433
434 if (index >= 255 || count > 256 || index + count > 256)
435 return EINVAL;
436
437 i = index;
438 while (i < (index + count)) {
439 red[i] = sc->sc_cmap.cm_map[i][0];
440 green[i] = sc->sc_cmap.cm_map[i][1];
441 blue[i] = sc->sc_cmap.cm_map[i][2];
442 i++;
443 }
444 error = copyout(&red[index], cm->red, count);
445 if (error)
446 return error;
447 error = copyout(&green[index], cm->green, count);
448 if (error)
449 return error;
450 error = copyout(&blue[index], cm->blue, count);
451 if (error)
452 return error;
453
454 return 0;
455 }
456
457 static void
458 agten_restore_palette(struct agten_softc *sc)
459 {
460 int i;
461
462 for (i = 0; i < (1 << (sc->sc_depth - 1)); i++) {
463 agten_putpalreg(sc, i, sc->sc_cmap.cm_map[i][0],
464 sc->sc_cmap.cm_map[i][1], sc->sc_cmap.cm_map[i][2]);
465 }
466 }
467
468 static int
469 agten_putpalreg(struct agten_softc *sc, uint8_t idx, uint8_t r, uint8_t g,
470 uint8_t b)
471 {
472
473 sc->sc_cmap.cm_map[idx][0] = r;
474 sc->sc_cmap.cm_map[idx][1] = g;
475 sc->sc_cmap.cm_map[idx][2] = b;
476 agten_write_idx(sc, IBM561_CMAP_TABLE + idx);
477 agten_write_dac(sc, IBM561_CMD_CMAP, r);
478 agten_write_dac(sc, IBM561_CMD_CMAP, g);
479 agten_write_dac(sc, IBM561_CMD_CMAP, b);
480 return 0;
481 }
482
483 static void
484 agten_init(struct agten_softc *sc)
485 {
486 int i, j;
487 uint32_t src, srcw;
488 volatile uint32_t junk;
489
490 /* first we set up the colour map */
491 j = 0;
492 for (i = 0; i < 256; i++) {
493
494 agten_putpalreg(sc, i, rasops_cmap[j], rasops_cmap[j + 1],
495 rasops_cmap[j + 2]);
496 j += 3;
497 }
498
499 /* now set up some window attributes */
500 agten_write_idx(sc, /*IBM561_FB_WINTYPE*/IBM561_OL_WINTYPE);
501 agten_write_dac_10(sc, IBM561_CMD_FB_WAT, 0x00);
502 for (i = 1; i < 256; i++)
503 agten_write_dac_10(sc, IBM561_CMD_FB_WAT, 0x01);
504
505 src = 0;
506 srcw = sc->sc_width << 16 | sc->sc_height;
507 bus_space_write_4(sc->sc_bustag, sc->sc_p9100_regh, FOREGROUND_COLOR,
508 0x0);
509 bus_space_write_4(sc->sc_bustag, sc->sc_p9100_regh, BACKGROUND_COLOR,
510 0x0);
511 bus_space_write_4(sc->sc_bustag, sc->sc_p9100_regh, RASTER_OP, ROP_PAT);
512 bus_space_write_4(sc->sc_bustag, sc->sc_p9100_regh, COORD_INDEX, 0);
513 bus_space_write_4(sc->sc_bustag, sc->sc_p9100_regh, RECT_RTW_XY, src);
514 bus_space_write_4(sc->sc_bustag, sc->sc_p9100_regh, RECT_RTW_XY, srcw);
515 junk = bus_space_read_4(sc->sc_bustag, sc->sc_p9100_regh, COMMAND_QUAD);
516
517 i128_init(sc->sc_bustag, sc->sc_p9100_regh, sc->sc_stride, 8);
518 }
519
520 static void
521 agten_copycols(void *cookie, int row, int srccol, int dstcol, int ncols)
522 {
523 struct rasops_info *ri = cookie;
524 struct vcons_screen *scr = ri->ri_hw;
525 struct agten_softc *sc = scr->scr_cookie;
526 int32_t xs, xd, y, width, height;
527
528 xs = ri->ri_xorigin + ri->ri_font->fontwidth * srccol;
529 xd = ri->ri_xorigin + ri->ri_font->fontwidth * dstcol;
530 y = ri->ri_yorigin + ri->ri_font->fontheight * row;
531 width = ri->ri_font->fontwidth * ncols;
532 height = ri->ri_font->fontheight;
533 i128_bitblt(sc->sc_bustag, sc->sc_i128_regh, xs, y, xd, y, width,
534 height, ROP_SRC);
535 }
536
537 static void
538 agten_erasecols(void *cookie, int row, int startcol, int ncols, long fillattr)
539 {
540 struct rasops_info *ri = cookie;
541 struct vcons_screen *scr = ri->ri_hw;
542 struct agten_softc *sc = scr->scr_cookie;
543 int32_t x, y, width, height, bg;
544
545 x = ri->ri_xorigin + ri->ri_font->fontwidth * startcol;
546 y = ri->ri_yorigin + ri->ri_font->fontheight * row;
547 width = ri->ri_font->fontwidth * ncols;
548 height = ri->ri_font->fontheight;
549 bg = (uint32_t)ri->ri_devcmap[(fillattr >> 16) & 0xff];
550 i128_rectfill(sc->sc_bustag, sc->sc_i128_regh, x, y, width, height, bg);
551 }
552
553 static void
554 agten_copyrows(void *cookie, int srcrow, int dstrow, int nrows)
555 {
556 struct rasops_info *ri = cookie;
557 struct vcons_screen *scr = ri->ri_hw;
558 struct agten_softc *sc = scr->scr_cookie;
559 int32_t x, ys, yd, width, height;
560
561 x = ri->ri_xorigin;
562 ys = ri->ri_yorigin + ri->ri_font->fontheight * srcrow;
563 yd = ri->ri_yorigin + ri->ri_font->fontheight * dstrow;
564 width = ri->ri_emuwidth;
565 height = ri->ri_font->fontheight * nrows;
566 i128_bitblt(sc->sc_bustag, sc->sc_i128_regh, x, ys, x, yd, width,
567 height, ROP_SRC);
568 }
569
570 static void
571 agten_eraserows(void *cookie, int row, int nrows, long fillattr)
572 {
573 struct rasops_info *ri = cookie;
574 struct vcons_screen *scr = ri->ri_hw;
575 struct agten_softc *sc = scr->scr_cookie;
576 int32_t x, y, width, height, bg;
577
578 if ((row == 0) && (nrows == ri->ri_rows)) {
579 x = y = 0;
580 width = ri->ri_width;
581 height = ri->ri_height;
582 } else {
583 x = ri->ri_xorigin;
584 y = ri->ri_yorigin + ri->ri_font->fontheight * row;
585 width = ri->ri_emuwidth;
586 height = ri->ri_font->fontheight * nrows;
587 }
588 bg = (uint32_t)ri->ri_devcmap[(fillattr >> 16) & 0xff];
589 i128_rectfill(sc->sc_bustag, sc->sc_i128_regh, x, y, width, height, bg);
590 }
591