1 /*        $NetBSD: fb.c,v 1.37 2019/11/10 21:16:37 chs Exp $ */
2 
3 /*
4  * Copyright (c) 1992, 1993
5  *        The Regents of the University of California.  All rights reserved.
6  *
7  * This software was developed by the Computer Systems Engineering group
8  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
9  * contributed to Berkeley.
10  *
11  * All advertising materials mentioning features or use of this software
12  * must display the following acknowledgement:
13  *        This product includes software developed by the University of
14  *        California, Lawrence Berkeley Laboratory.
15  *
16  * Redistribution and use in source and binary forms, with or without
17  * modification, are permitted provided that the following conditions
18  * are met:
19  * 1. Redistributions of source code must retain the above copyright
20  *    notice, this list of conditions and the following disclaimer.
21  * 2. Redistributions in binary form must reproduce the above copyright
22  *    notice, this list of conditions and the following disclaimer in the
23  *    documentation and/or other materials provided with the distribution.
24  * 3. Neither the name of the University nor the names of its contributors
25  *    may be used to endorse or promote products derived from this software
26  *    without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38  * SUCH DAMAGE.
39  *
40  *        @(#)fb.c  8.1 (Berkeley) 6/11/93
41  */
42 
43 /*
44  * /dev/fb (indirect frame buffer driver).  This is gross; we should
45  * just build cdevsw[] dynamically.
46  */
47 
48 #include <sys/cdefs.h>
49 __KERNEL_RCSID(0, "$NetBSD: fb.c,v 1.37 2019/11/10 21:16:37 chs Exp $");
50 
51 #include <sys/param.h>
52 #include <sys/systm.h>
53 #include <sys/device.h>
54 #include <sys/proc.h>
55 #include <sys/conf.h>
56 #include <sys/malloc.h>
57 #include <sys/types.h>
58 
59 #include <machine/promlib.h>
60 #include <machine/autoconf.h>
61 #include <machine/kbd.h>
62 #include <machine/eeprom.h>
63 #include <sparc/dev/cons.h>
64 
65 #include <dev/sun/fbio.h>
66 #include <dev/sun/fbvar.h>
67 
68 #include "kbd.h"
69 #include "pfour.h"
70 
71 
72 struct fbdevlist {
73           struct fbdevice *fb_dev;
74           struct fbdevlist *fb_next;
75 };
76 
77 static struct fbdevlist fblist = {
78     NULL,
79     NULL,
80 };
81 
82 dev_type_open(fbopen);
83 dev_type_close(fbclose);
84 dev_type_ioctl(fbioctl);
85 dev_type_poll(fbpoll);
86 dev_type_mmap(fbmmap);
87 dev_type_kqfilter(fbkqfilter);
88 
89 const struct cdevsw fb_cdevsw = {
90           .d_open = fbopen,
91           .d_close = fbclose,
92           .d_read = noread,
93           .d_write = nowrite,
94           .d_ioctl = fbioctl,
95           .d_stop = nostop,
96           .d_tty = notty,
97           .d_poll = fbpoll,
98           .d_mmap = fbmmap,
99           .d_kqfilter = fbkqfilter,
100           .d_discard = nodiscard,
101           .d_flag = D_OTHER
102 };
103 
104 void
fb_unblank(void)105 fb_unblank(void)
106 {
107 
108           struct fbdevlist *fbl = &fblist;
109 
110           while (fbl != NULL && fbl->fb_dev != NULL) {
111                     (*fbl->fb_dev->fb_driver->fbd_unblank)(fbl->fb_dev->fb_device);
112                     fbl = fbl->fb_next;
113           }
114 }
115 
116 /*
117  * Helper function for frame buffer devices. Decides whether
118  * the device can be the console output device according to
119  * PROM info. The result from this function may not be conclusive
120  * on machines with old PROMs; in that case, drivers should consult
121  * other sources of configuration information (e.g. EEPROM entries).
122  */
123 int
fb_is_console(int node)124 fb_is_console(int node)
125 {
126 #if !defined(SUN4U)
127           int fbnode;
128 
129           switch (prom_version()) {
130           case PROM_OLDMON:
131                     /* `node' is not valid; just check for any fb device */
132                     return (prom_stdout() == PROMDEV_SCREEN);
133 
134           case PROM_OBP_V0:
135                     /*
136                      * First, check if prom_stdout() represents a frame buffer,
137                      * then match on the `fb' property on the root node, if any.
138                      */
139                     if (prom_stdout() != PROMDEV_SCREEN)
140                               return (0);
141 
142                     fbnode = prom_getpropint(findroot(), "fb", 0);
143                     return (fbnode == 0 || node == fbnode);
144 
145           case PROM_OBP_V2:
146           case PROM_OBP_V3:
147           case PROM_OPENFIRM:
148                     /* Just match the nodes */
149                     return (node == prom_stdout_node);
150           }
151 
152           return (0);
153 #else
154                     return (node == prom_stdout_node);
155 #endif
156 }
157 
158 void
fb_attach(struct fbdevice * fb,int isconsole)159 fb_attach(struct fbdevice *fb, int isconsole)
160 {
161           static int seen_force = 0;
162           int nfb = 0;
163           struct fbdevlist *fbl = &fblist;
164 
165           /*
166            * Check to see if we're being forced into /dev/fb0, or if we're
167            * the console.  If we are, then move/replace the current fb0.
168            */
169           if ((fb->fb_flags & FB_FORCE || (isconsole && !seen_force)) &&
170               fblist.fb_dev != NULL) {
171                     while (fbl->fb_next != NULL) {
172                               fbl = fbl->fb_next;
173                               nfb++;
174                     }
175                     fbl->fb_next = malloc(sizeof (struct fbdevlist),
176                         M_DEVBUF, M_WAITOK);
177                     fbl = fbl->fb_next;
178                     nfb++;
179                     fbl->fb_dev = fblist.fb_dev;
180                     fbl->fb_next = NULL;
181                     aprint_normal_dev(fbl->fb_dev->fb_device,
182                         "moved to /dev/fb%d\n", nfb);
183                     aprint_normal_dev(fbl->fb_dev->fb_device,
184                         "attached to /dev/fb0\n");
185                     fblist.fb_dev = fb;
186                     if (fb->fb_flags & FB_FORCE)
187                               seen_force = 1;
188           /* Add to end of fb list. */
189           } else {
190                     if (fblist.fb_dev != NULL) {
191                               while (fbl->fb_next != NULL) {
192                                         fbl = fbl->fb_next;
193                                         nfb++;
194                               }
195                               fbl->fb_next = malloc(sizeof (struct fbdevlist),
196                                   M_DEVBUF, M_WAITOK);
197                               fbl = fbl->fb_next;
198                               nfb++;
199                     }
200                     fbl->fb_dev = fb;
201                     fbl->fb_next = NULL;
202                     aprint_normal_dev(fbl->fb_dev->fb_device,
203                          "attached to /dev/fb%d\n", nfb);
204           }
205 }
206 
207 int
fbopen(dev_t dev,int flags,int mode,struct lwp * l)208 fbopen(dev_t dev, int flags, int mode, struct lwp *l)
209 {
210           int unit, nunit;
211           struct fbdevlist *fbl = &fblist;
212 
213           unit = minor(dev);
214           while (unit-- && fbl != NULL)
215                     fbl = fbl->fb_next;
216           if (fbl == NULL || fbl->fb_dev == NULL)
217                     return (ENXIO);
218 
219           nunit = device_unit(fbl->fb_dev->fb_device);
220           return (fbl->fb_dev->fb_driver->fbd_open)(makedev(0, nunit), flags,
221               mode, l);
222 }
223 
224 int
fbclose(dev_t dev,int flags,int mode,struct lwp * l)225 fbclose(dev_t dev, int flags, int mode, struct lwp *l)
226 {
227           int unit, nunit;
228           struct fbdevlist *fbl = &fblist;
229 
230           unit = minor(dev);
231           while (unit-- && fbl != NULL)
232                     fbl = fbl->fb_next;
233           if (fbl == NULL || fbl->fb_dev == NULL)
234                     return (ENXIO);
235 
236           nunit = device_unit(fbl->fb_dev->fb_device);
237           return (fbl->fb_dev->fb_driver->fbd_close)(makedev(0, nunit), flags,
238               mode, l);
239 }
240 
241 int
fbioctl(dev_t dev,u_long cmd,void * data,int flags,struct lwp * l)242 fbioctl(dev_t dev, u_long cmd, void *data, int flags, struct lwp *l)
243 {
244           int unit, nunit;
245           struct fbdevlist *fbl = &fblist;
246 
247           unit = minor(dev);
248           while (unit-- && fbl != NULL)
249                     fbl = fbl->fb_next;
250           if (fbl == NULL || fbl->fb_dev == NULL)
251                     return (ENXIO);
252 
253           nunit = device_unit(fbl->fb_dev->fb_device);
254           return (fbl->fb_dev->fb_driver->fbd_ioctl)(makedev(0, nunit), cmd,
255               data, flags, l);
256 }
257 
258 int
fbpoll(dev_t dev,int events,struct lwp * l)259 fbpoll(dev_t dev, int events, struct lwp *l)
260 {
261           int unit, nunit;
262           struct fbdevlist *fbl = &fblist;
263 
264           unit = minor(dev);
265           while (unit-- && fbl != NULL)
266                     fbl = fbl->fb_next;
267           if (fbl == NULL || fbl->fb_dev == NULL)
268                     return (ENXIO);
269 
270           nunit = device_unit(fbl->fb_dev->fb_device);
271           return (fbl->fb_dev->fb_driver->fbd_poll)(makedev(0, nunit), events,
272               l);
273 }
274 
275 int
fbkqfilter(dev_t dev,struct knote * kn)276 fbkqfilter(dev_t dev, struct knote *kn)
277 {
278           int unit, nunit;
279           struct fbdevlist *fbl = &fblist;
280 
281           unit = minor(dev);
282           while (unit-- && fbl != NULL)
283                     fbl = fbl->fb_next;
284           if (fbl == NULL || fbl->fb_dev == NULL)
285                     return (ENXIO);
286 
287           nunit = device_unit(fbl->fb_dev->fb_device);
288           return (fbl->fb_dev->fb_driver->fbd_kqfilter)(makedev(0, nunit), kn);
289 }
290 
291 paddr_t
fbmmap(dev_t dev,off_t off,int prot)292 fbmmap(dev_t dev, off_t off, int prot)
293 {
294           int unit, nunit;
295           struct fbdevlist *fbl = &fblist;
296 
297           unit = minor(dev);
298           while (unit-- && fbl != NULL)
299                     fbl = fbl->fb_next;
300           if (fbl == NULL || fbl->fb_dev == NULL)
301                     return (ENXIO);
302 
303           nunit = device_unit(fbl->fb_dev->fb_device);
304           paddr_t (*map)(dev_t, off_t, int) = fbl->fb_dev->fb_driver->fbd_mmap;
305 
306           if (map == NULL)
307                     return (-1);
308           return (map(makedev(0, nunit), off, prot));
309 }
310 
311 void
fb_setsize_obp(struct fbdevice * fb,int depth,int def_width,int def_height,int node)312 fb_setsize_obp(struct fbdevice *fb, int depth, int def_width, int def_height, int node)
313 {
314           fb->fb_type.fb_width = prom_getpropint(node, "width", def_width);
315           fb->fb_type.fb_height = prom_getpropint(node, "height", def_height);
316           fb->fb_linebytes = prom_getpropint(node, "linebytes",
317                                              (fb->fb_type.fb_width * depth) / 8);
318 }
319 
320 void
fb_setsize_eeprom(struct fbdevice * fb,int depth,int def_width,int def_height)321 fb_setsize_eeprom(struct fbdevice *fb, int depth, int def_width, int def_height)
322 {
323 #if !defined(SUN4U)
324           struct eeprom *eep = (struct eeprom *)eeprom_va;
325 
326           if (!CPU_ISSUN4) {
327                     printf("fb_setsize_eeprom: not sun4\n");
328                     return;
329           }
330 
331           /* Set up some defaults. */
332           fb->fb_type.fb_width = def_width;
333           fb->fb_type.fb_height = def_height;
334 
335           if (fb->fb_flags & FB_PFOUR) {
336 #if NPFOUR > 0
337                     fb_setsize_pfour(fb);
338 #endif
339           } else if (eep != NULL) {
340                     switch (eep->eeScreenSize) {
341                     case EE_SCR_1152X900:
342                               fb->fb_type.fb_width = 1152;
343                               fb->fb_type.fb_height = 900;
344                               break;
345 
346                     case EE_SCR_1024X1024:
347                               fb->fb_type.fb_width = 1024;
348                               fb->fb_type.fb_height = 1024;
349                               break;
350 
351                     case EE_SCR_1600X1280:
352                               fb->fb_type.fb_width = 1600;
353                               fb->fb_type.fb_height = 1280;
354                               break;
355 
356                     case EE_SCR_1440X1440:
357                               fb->fb_type.fb_width = 1440;
358                               fb->fb_type.fb_height = 1440;
359                               break;
360 
361                     default:
362                               /*
363                                * XXX: Do nothing, I guess.
364                                * Should we print a warning about
365                                * an unknown value? --thorpej
366                                */
367                               break;
368                     }
369           }
370 
371           fb->fb_linebytes = (fb->fb_type.fb_width * depth) / 8;
372 #endif /* !SUN4U */
373 }
374