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