1 /* $NetBSD: grf.c,v 1.69 2023/12/20 00:40:42 thorpej Exp $ */
2
3 /*
4 * Copyright (c) 1988 University of Utah.
5 * Copyright (c) 1990 The Regents of the University of California.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * the Systems Programming Group of the University of Utah Computer
10 * Science Department.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 * from: Utah $Hdr: grf.c 1.31 91/01/21$
37 *
38 * @(#)grf.c 7.8 (Berkeley) 5/7/91
39 */
40
41 #include <sys/cdefs.h>
42 __KERNEL_RCSID(0, "$NetBSD: grf.c,v 1.69 2023/12/20 00:40:42 thorpej Exp $");
43
44 /*
45 * Graphics display driver for the Amiga
46 * This is the hardware-independent portion of the driver.
47 * Hardware access is through the grf_softc->g_mode routine.
48 */
49
50 #include "view.h"
51 #include "grf.h"
52 #include "kbd.h"
53 #include "wsdisplay.h"
54
55 #include <sys/param.h>
56 #include <sys/proc.h>
57 #include <sys/ioctl.h>
58 #include <sys/device.h>
59 #include <sys/file.h>
60 #include <sys/systm.h>
61 #include <sys/vnode.h>
62 #include <sys/mman.h>
63 #include <sys/bus.h>
64 #include <sys/kauth.h>
65
66 #include <machine/cpu.h>
67
68 #include <dev/cons.h>
69 #include <dev/sun/fbio.h>
70 #include <dev/wscons/wsconsio.h>
71 #include <dev/wscons/wsdisplayvar.h>
72 #include <dev/rasops/rasops.h>
73 #include <dev/wscons/wsdisplay_vconsvar.h>
74
75 #include <amiga/amiga/color.h> /* DEBUG */
76 #include <amiga/amiga/device.h>
77 #include <amiga/dev/grfioctl.h>
78 #include <amiga/dev/grfvar.h>
79 #include <amiga/dev/itevar.h>
80 #include <amiga/dev/kbdvar.h>
81 #include <amiga/dev/viewioctl.h>
82
83 #include <sys/conf.h>
84
85 #if NGRF > 0
86 #include "ite.h"
87 #if NITE == 0
88 #define ite_on(u,f)
89 #define ite_off(u,f)
90 #define ite_reinit(d)
91 #endif
92
93 int grfon(dev_t);
94 int grfoff(dev_t);
95 int grfsinfo(dev_t, struct grfdyninfo *);
96
97 void grfattach(device_t, device_t, void *);
98 int grfmatch(device_t, cfdata_t, void *);
99 int grfprint(void *, const char *);
100 #ifdef DEBUG
101 void grfdebug(struct grf_softc *, const char *, ...);
102 #endif
103 /*
104 * pointers to grf drivers device structs
105 */
106 struct grf_softc *grfsp[NGRF];
107
108 CFATTACH_DECL_NEW(grf, 0,
109 grfmatch, grfattach, NULL, NULL);
110
111 dev_type_open(grfopen);
112 dev_type_close(grfclose);
113 dev_type_ioctl(grfioctl);
114 dev_type_mmap(grfmmap);
115
116 const struct cdevsw grf_cdevsw = {
117 .d_open = grfopen,
118 .d_close = grfclose,
119 .d_read = nullread,
120 .d_write = nullwrite,
121 .d_ioctl = grfioctl,
122 .d_stop = nostop,
123 .d_tty = notty,
124 .d_poll = nopoll,
125 .d_mmap = grfmmap,
126 .d_kqfilter = nokqfilter,
127 .d_discard = nodiscard,
128 .d_flag = 0
129 };
130
131 /*
132 * only used in console init.
133 */
134 static cfdata_t cfdata;
135
136 #if NWSDISPLAY > 0
137 static struct vcons_screen console_vcons;
138
139 static void grf_init_screen(void *, struct vcons_screen *, int, long *);
140 static struct rasops_info *grf_setup_rasops(struct grf_softc *,
141 struct vcons_screen *);
142
143 cons_decl(grf);
144 #endif
145
146 /*
147 * match if the unit of grf matches its perspective
148 * low level board driver.
149 */
150 int
grfmatch(device_t parent,cfdata_t cf,void * aux)151 grfmatch(device_t parent, cfdata_t cf, void *aux)
152 {
153 struct grf_softc *psc;
154
155 psc = device_private(parent);
156 if (cf->cf_unit != psc->g_unit)
157 return(0);
158 cfdata = cf;
159 return(1);
160 }
161
162 /*
163 * Attach.. plug pointer in and print some info.
164 * Then try and attach a wsdisplay or ite to us.
165 * Note: self is NULL during console init.
166 */
167 void
grfattach(device_t parent,device_t self,void * aux)168 grfattach(device_t parent, device_t self, void *aux)
169 {
170 #if NWSDISPLAY > 0
171 struct wsemuldisplaydev_attach_args wa;
172 long defattr;
173 #endif
174 struct grf_softc *gp;
175 int maj;
176
177 gp = device_private(parent);
178 gp->g_device = self;
179 grfsp[gp->g_unit] = gp;
180
181 /*
182 * find our major device number, make device
183 */
184 maj = cdevsw_lookup_major(&grf_cdevsw);
185 gp->g_grfdev = makedev(maj, gp->g_unit);
186
187 if (self != NULL) {
188 printf(": width %d height %d", gp->g_display.gd_dwidth,
189 gp->g_display.gd_dheight);
190 if (gp->g_display.gd_colors == 2)
191 printf(" monochrome\n");
192 else
193 printf(" colors %d\n", gp->g_display.gd_colors);
194
195 #if NWSDISPLAY > 0
196 vcons_init(&gp->g_vd, gp, gp->g_defaultscr, gp->g_accessops);
197 gp->g_vd.init_screen = grf_init_screen;
198
199 if (gp->g_flags & GF_CONSOLE) {
200 console_vcons.scr_flags |= VCONS_SCREEN_IS_STATIC;
201 vcons_init_screen(&gp->g_vd,
202 &console_vcons, 1, &defattr);
203 gp->g_defaultscr->textops =
204 &console_vcons.scr_ri.ri_ops;
205 wsdisplay_cnattach(gp->g_defaultscr,
206 &console_vcons.scr_ri, 0, 0, defattr);
207 vcons_replay_msgbuf(&console_vcons);
208 }
209
210 /* attach wsdisplay */
211 wa.console = (gp->g_flags & GF_CONSOLE) != 0;
212 wa.scrdata = gp->g_scrlist;
213 wa.accessops = gp->g_accessops;
214 wa.accesscookie = &gp->g_vd;
215 config_found(self, &wa, wsemuldisplaydevprint,
216 CFARGS(.iattr = "wsemuldisplaydev"));
217 #endif /* NWSDISPLAY > 0 */
218 }
219
220 #if NWSDISPLAY == 0
221 /*
222 * try and attach an ite
223 */
224 amiga_config_found(cfdata, self, gp, grfprint,
225 CFARGS(.iattr = "grf"));
226 #endif
227 }
228
229 int
grfprint(void * aux,const char * pnp)230 grfprint(void *aux, const char *pnp)
231 {
232 if (pnp)
233 aprint_normal("ite at %s", pnp);
234 return(UNCONF);
235 }
236
237 /*ARGSUSED*/
238 int
grfopen(dev_t dev,int flags,int devtype,struct lwp * l)239 grfopen(dev_t dev, int flags, int devtype, struct lwp *l)
240 {
241 struct grf_softc *gp;
242
243 if (GRFUNIT(dev) >= NGRF || (gp = grfsp[GRFUNIT(dev)]) == NULL)
244 return(ENXIO);
245
246 if ((gp->g_flags & GF_ALIVE) == 0)
247 return(ENXIO);
248
249 if ((gp->g_flags & (GF_OPEN|GF_EXCLUDE)) == (GF_OPEN|GF_EXCLUDE))
250 return(EBUSY);
251
252 return(0);
253 }
254
255 /*ARGSUSED*/
256 int
grfclose(dev_t dev,int flags,int mode,struct lwp * l)257 grfclose(dev_t dev, int flags, int mode, struct lwp *l)
258 {
259 struct grf_softc *gp;
260
261 gp = grfsp[GRFUNIT(dev)];
262 (void)grfoff(dev);
263 gp->g_flags &= GF_ALIVE;
264 return(0);
265 }
266
267 /*ARGSUSED*/
268 int
grfioctl(dev_t dev,u_long cmd,void * data,int flag,struct lwp * l)269 grfioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
270 {
271 struct grf_softc *gp;
272 int error;
273
274 gp = grfsp[GRFUNIT(dev)];
275 error = 0;
276
277 switch (cmd) {
278 case OGRFIOCGINFO:
279 /* argl.. no bank-member.. */
280 memcpy(data, (void *)&gp->g_display, sizeof(struct grfinfo)-4);
281 break;
282 case GRFIOCGINFO:
283 memcpy(data, (void *)&gp->g_display, sizeof(struct grfinfo));
284 break;
285 case GRFIOCON:
286 error = grfon(dev);
287 break;
288 case GRFIOCOFF:
289 error = grfoff(dev);
290 break;
291 case GRFIOCSINFO:
292 error = grfsinfo(dev, (struct grfdyninfo *) data);
293 break;
294 case GRFGETVMODE:
295 return(gp->g_mode(gp, GM_GRFGETVMODE, data, 0, 0));
296 case GRFSETVMODE:
297 error = gp->g_mode(gp, GM_GRFSETVMODE, data, 0, 0);
298 if (error == 0 && gp->g_itedev && !(gp->g_flags & GF_GRFON))
299 ite_reinit(gp->g_itedev);
300 break;
301 case GRFGETNUMVM:
302 return(gp->g_mode(gp, GM_GRFGETNUMVM, data, 0, 0));
303 /*
304 * these are all hardware dependent, and have to be resolved
305 * in the respective driver.
306 */
307 case GRFIOCPUTCMAP:
308 case GRFIOCGETCMAP:
309 case GRFIOCSSPRITEPOS:
310 case GRFIOCGSPRITEPOS:
311 case GRFIOCSSPRITEINF:
312 case GRFIOCGSPRITEINF:
313 case GRFIOCGSPRITEMAX:
314 case GRFIOCBITBLT:
315 case GRFIOCSETMON:
316 case GRFTOGGLE: /* Toggles between Cirrus boards and native ECS on
317 Amiga. 15/11/94 ill */
318 /*
319 * We need the minor dev number to get the overlay/image
320 * information for grf_ul.
321 */
322 return(gp->g_mode(gp, GM_GRFIOCTL, data, cmd, dev));
323
324 case GRFIOCBLANK: /* blank ioctl, IOCON/OFF will turn ite on */
325 case FBIOSVIDEO:
326 error = gp->g_mode(gp, GM_GRFIOCTL, data, GRFIOCBLANK, dev);
327 if (!error)
328 gp->g_blank = *(int *)data;
329 return (error);
330
331 case FBIOGVIDEO:
332 *(int *)data = gp->g_blank;
333 return (0);
334
335 default:
336 #if NVIEW > 0
337 /*
338 * check to see whether it's a command recognized by the
339 * view code if the unit is 0
340 * XXX
341 */
342 if (GRFUNIT(dev) == 0) {
343 extern const struct cdevsw view_cdevsw;
344
345 return((*view_cdevsw.d_ioctl)(dev, cmd, data, flag, l));
346 }
347 #endif
348 error = EPASSTHROUGH;
349 break;
350
351 }
352 return(error);
353 }
354
355 /*
356 * map the contents of a graphics display card into process'
357 * memory space.
358 */
359 paddr_t
grfmmap(dev_t dev,off_t off,int prot)360 grfmmap(dev_t dev, off_t off, int prot)
361 {
362 struct grf_softc *gp;
363 struct grfinfo *gi;
364
365 gp = grfsp[GRFUNIT(dev)];
366 gi = &gp->g_display;
367
368 /*
369 * control registers
370 */
371 if (off >= 0 && off < gi->gd_regsize)
372 return MD_BTOP((paddr_t)gi->gd_regaddr + off);
373
374 /*
375 * frame buffer
376 */
377 if (off >= gi->gd_regsize && off < gi->gd_regsize+gi->gd_fbsize) {
378 off -= gi->gd_regsize;
379 return MD_BTOP((paddr_t)gi->gd_fbaddr + off);
380 }
381 /* bogus */
382 return(-1);
383 }
384
385 int
grfon(dev_t dev)386 grfon(dev_t dev)
387 {
388 struct grf_softc *gp;
389
390 gp = grfsp[GRFUNIT(dev)];
391
392 if (gp->g_flags & GF_GRFON)
393 return(0);
394
395 gp->g_flags |= GF_GRFON;
396 if (gp->g_itedev != NODEV)
397 ite_off(gp->g_itedev, 3);
398
399 return(gp->g_mode(gp, (dev & GRFOVDEV) ? GM_GRFOVON : GM_GRFON,
400 NULL, 0, 0));
401 }
402
403 int
grfoff(dev_t dev)404 grfoff(dev_t dev)
405 {
406 struct grf_softc *gp;
407 int error;
408
409 gp = grfsp[GRFUNIT(dev)];
410
411 if ((gp->g_flags & GF_GRFON) == 0)
412 return(0);
413
414 gp->g_flags &= ~GF_GRFON;
415 error = gp->g_mode(gp, (dev & GRFOVDEV) ? GM_GRFOVOFF : GM_GRFOFF,
416 NULL, 0, 0);
417
418 /*
419 * Closely tied together no X's
420 */
421 if (gp->g_itedev != NODEV)
422 ite_on(gp->g_itedev, 2);
423
424 return(error);
425 }
426
427 int
grfsinfo(dev_t dev,struct grfdyninfo * dyninfo)428 grfsinfo(dev_t dev, struct grfdyninfo *dyninfo)
429 {
430 struct grf_softc *gp;
431 int error;
432
433 gp = grfsp[GRFUNIT(dev)];
434 error = gp->g_mode(gp, GM_GRFCONFIG, dyninfo, 0, 0);
435
436 /*
437 * Closely tied together no X's
438 */
439 if (gp->g_itedev != NODEV)
440 ite_reinit(gp->g_itedev);
441 return(error);
442 }
443
444 #if NWSDISPLAY > 0
445 void
grfcnprobe(struct consdev * cd)446 grfcnprobe(struct consdev *cd)
447 {
448 struct grf_softc *gp;
449 int unit;
450
451 /*
452 * Find the first working grf device for being console.
453 * Ignore unit 0 (grfcc), which should use amidisplaycc instead.
454 */
455 for (unit = 1; unit < NGRF; unit++) {
456 gp = grfsp[unit];
457 if (gp != NULL && (gp->g_flags & GF_ALIVE)) {
458 cd->cn_pri = CN_INTERNAL;
459 cd->cn_dev = NODEV; /* initialized later by wscons */
460 return;
461 }
462 }
463
464 /* no grf console alive */
465 cd->cn_pri = CN_DEAD;
466 }
467
468 void
grfcninit(struct consdev * cd)469 grfcninit(struct consdev *cd)
470 {
471 struct grf_softc *gp;
472 struct rasops_info *ri;
473 long defattr;
474 int unit;
475
476 /* find console grf and set up wsdisplay for it */
477 for (unit = 1; unit < NGRF; unit++) {
478 gp = grfsp[unit];
479 if (gp != NULL && (gp->g_flags & GF_ALIVE)) {
480 gp->g_flags |= GF_CONSOLE; /* we are console! */
481
482 gp->g_defaultscr->ncols = gp->g_display.gd_fbwidth /
483 gp->g_defaultscr->fontwidth;
484 gp->g_defaultscr->nrows = gp->g_display.gd_fbheight /
485 gp->g_defaultscr->fontheight;
486
487 ri = grf_setup_rasops(gp, &console_vcons);
488 console_vcons.scr_cookie = gp;
489 defattr = 0; /* XXX */
490
491 wsdisplay_preattach(gp->g_defaultscr, ri, 0, 0,
492 defattr);
493 #if NKBD > 0
494 /* tell kbd device it is used as console keyboard */
495 kbd_cnattach();
496 #endif
497 return;
498 }
499 }
500 panic("grfcninit: lost console");
501 }
502
503 static void
grf_init_screen(void * cookie,struct vcons_screen * scr,int existing,long * defattr)504 grf_init_screen(void *cookie, struct vcons_screen *scr, int existing,
505 long *defattr)
506 {
507 struct grf_softc *gp;
508 struct rasops_info *ri __unused;
509
510 gp = cookie;
511 ri = grf_setup_rasops(gp, scr);
512 }
513
514 static struct rasops_info *
grf_setup_rasops(struct grf_softc * gp,struct vcons_screen * scr)515 grf_setup_rasops(struct grf_softc *gp, struct vcons_screen *scr)
516 {
517 struct rasops_info *ri;
518 int i;
519
520 ri = &scr->scr_ri;
521 scr->scr_flags |= VCONS_DONT_READ;
522 memset(ri, 0, sizeof(struct rasops_info));
523
524 ri->ri_rows = gp->g_defaultscr->nrows;
525 ri->ri_cols = gp->g_defaultscr->ncols;
526 ri->ri_hw = scr;
527 ri->ri_ops.cursor = gp->g_emulops->cursor;
528 ri->ri_ops.mapchar = gp->g_emulops->mapchar;
529 ri->ri_ops.copyrows = gp->g_emulops->copyrows;
530 ri->ri_ops.eraserows = gp->g_emulops->eraserows;
531 ri->ri_ops.copycols = gp->g_emulops->copycols;
532 ri->ri_ops.erasecols = gp->g_emulops->erasecols;
533 ri->ri_ops.putchar = gp->g_emulops->putchar;
534 ri->ri_ops.allocattr = gp->g_emulops->allocattr;
535
536 /* multiplication table for row-offsets */
537 for (i = 0; i < ri->ri_rows; i++)
538 gp->g_rowoffset[i] = i * ri->ri_cols;
539
540 return ri;
541 }
542
543 /*
544 * Called as fallback for ioctls which are not handled by the specific
545 * grf driver.
546 */
547 int
grf_wsioctl(void * v,void * vs,u_long cmd,void * data,int flag,struct lwp * l)548 grf_wsioctl(void *v, void *vs, u_long cmd, void *data, int flag, struct lwp *l)
549 {
550 struct wsdisplayio_fbinfo *iofbi;
551 struct wsdisplay_fbinfo *fbinfo;
552 struct vcons_data *vd;
553 struct grf_softc *gp;
554 struct vcons_screen *scr;
555 struct grfinfo *gi;
556
557 vd = v;
558 gp = vd->cookie;
559 scr = vd->active;
560
561 switch (cmd) {
562 case WSDISPLAYIO_GET_FBINFO:
563 if (scr != NULL) {
564 iofbi = data;
565 return wsdisplayio_get_fbinfo(&scr->scr_ri, iofbi);
566 }
567 return ENODEV;
568
569 case WSDISPLAYIO_GINFO:
570 if (scr != NULL) {
571 fbinfo = (struct wsdisplay_fbinfo *)data;
572 gi = &gp->g_display;
573
574 /*
575 * We should return truth about the current mode here,
576 * because X11 wsfb driver depends on this!
577 */
578 fbinfo->height = gi->gd_fbheight;
579 fbinfo->width = gi->gd_fbwidth;
580 fbinfo->depth = gi->gd_planes;
581 fbinfo->cmsize = gi->gd_colors;
582 return 0;
583 }
584 return ENODEV;
585
586 case WSDISPLAYIO_GTYPE:
587 *(u_int *)data = WSDISPLAY_TYPE_GRF;
588 return 0;
589
590 case WSDISPLAYIO_SMODE:
591 if ((*(int *)data) != gp->g_wsmode) {
592 gp->g_wsmode = *(int *)data;
593 if (gp->g_wsmode == WSDISPLAYIO_MODE_EMUL &&
594 scr != NULL)
595 vcons_redraw_screen(scr);
596 }
597 return 0;
598 }
599
600 return EPASSTHROUGH;
601 }
602
603 paddr_t
grf_wsmmap(void * v,void * vs,off_t off,int prot)604 grf_wsmmap(void *v, void *vs, off_t off, int prot)
605 {
606 struct vcons_data *vd;
607 struct grf_softc *gp;
608 struct grfinfo *gi;
609
610 vd = v;
611 gp = vd->cookie;
612 gi = &gp->g_display;
613
614 /* Normal fb mapping */
615 if (off < gi->gd_fbsize)
616 return MD_BTOP(((paddr_t)gi->gd_fbaddr) + off);
617
618 /*
619 * restrict all other mappings to processes with superuser privileges
620 * or the kernel itself
621 */
622 if (kauth_authorize_machdep(kauth_cred_get(), KAUTH_MACHDEP_UNMANAGEDMEM,
623 NULL, NULL, NULL, NULL) != 0) {
624 aprint_normal("%s: permission to mmap denied.\n",
625 device_xname(gp->g_device));
626 return -1;
627 }
628
629 /* Handle register mapping */
630 if ((off >= (paddr_t)gi->gd_regaddr) &&
631 (off < ((paddr_t)gi->gd_regaddr + (size_t)gi->gd_regsize)))
632 return MD_BTOP(off);
633
634 if ((off >= (paddr_t)gi->gd_fbaddr) &&
635 (off < ((paddr_t)gi->gd_fbaddr + (size_t)gi->gd_fbsize)))
636 return MD_BTOP(off);
637
638 return -1;
639 }
640
641 #endif /* NWSDISPLAY > 0 */
642
643 #endif /* NGRF > 0 */
644