1 /* $NetBSD: fb.c,v 1.36 2016/04/21 18:06:06 macallan 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.36 2016/04/21 18:06:06 macallan 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 if ((fbl->fb_next = malloc(sizeof (struct fbdevlist),
176 M_DEVBUF, M_NOWAIT)) == NULL)
177 printf("%s: replacing %s at /dev/fb0\n",
178 device_xname(fb->fb_device),
179 device_xname(fblist.fb_dev->fb_device));
180 else {
181 fbl = fbl->fb_next;
182 nfb++;
183 fbl->fb_dev = fblist.fb_dev;
184 fbl->fb_next = NULL;
185 aprint_normal_dev(fbl->fb_dev->fb_device,
186 "moved to /dev/fb%d\n", nfb);
187 aprint_normal_dev(fbl->fb_dev->fb_device,
188 "attached to /dev/fb0\n");
189 }
190 fblist.fb_dev = fb;
191 if (fb->fb_flags & FB_FORCE)
192 seen_force = 1;
193 /* Add to end of fb list. */
194 } else {
195 if (fblist.fb_dev != NULL) {
196 while (fbl->fb_next != NULL) {
197 fbl = fbl->fb_next;
198 nfb++;
199 }
200 if ((fbl->fb_next = malloc(sizeof (struct fbdevlist),
201 M_DEVBUF, M_NOWAIT)) == NULL) {
202 aprint_error_dev(fb->fb_device,
203 "no space to attach after /dev/fb%d\n",
204 nfb);
205 return;
206 }
207 fbl = fbl->fb_next;
208 nfb++;
209 }
210 fbl->fb_dev = fb;
211 fbl->fb_next = NULL;
212 aprint_normal_dev(fbl->fb_dev->fb_device,
213 "attached to /dev/fb%d\n", nfb);
214 }
215 }
216
217 int
fbopen(dev_t dev,int flags,int mode,struct lwp * l)218 fbopen(dev_t dev, int flags, int mode, struct lwp *l)
219 {
220 int unit, nunit;
221 struct fbdevlist *fbl = &fblist;
222
223 unit = minor(dev);
224 while (unit-- && fbl != NULL)
225 fbl = fbl->fb_next;
226 if (fbl == NULL || fbl->fb_dev == NULL)
227 return (ENXIO);
228
229 nunit = device_unit(fbl->fb_dev->fb_device);
230 return (fbl->fb_dev->fb_driver->fbd_open)(makedev(0, nunit), flags,
231 mode, l);
232 }
233
234 int
fbclose(dev_t dev,int flags,int mode,struct lwp * l)235 fbclose(dev_t dev, int flags, int mode, struct lwp *l)
236 {
237 int unit, nunit;
238 struct fbdevlist *fbl = &fblist;
239
240 unit = minor(dev);
241 while (unit-- && fbl != NULL)
242 fbl = fbl->fb_next;
243 if (fbl == NULL || fbl->fb_dev == NULL)
244 return (ENXIO);
245
246 nunit = device_unit(fbl->fb_dev->fb_device);
247 return (fbl->fb_dev->fb_driver->fbd_close)(makedev(0, nunit), flags,
248 mode, l);
249 }
250
251 int
fbioctl(dev_t dev,u_long cmd,void * data,int flags,struct lwp * l)252 fbioctl(dev_t dev, u_long cmd, void *data, int flags, struct lwp *l)
253 {
254 int unit, nunit;
255 struct fbdevlist *fbl = &fblist;
256
257 unit = minor(dev);
258 while (unit-- && fbl != NULL)
259 fbl = fbl->fb_next;
260 if (fbl == NULL || fbl->fb_dev == NULL)
261 return (ENXIO);
262
263 nunit = device_unit(fbl->fb_dev->fb_device);
264 return (fbl->fb_dev->fb_driver->fbd_ioctl)(makedev(0, nunit), cmd,
265 data, flags, l);
266 }
267
268 int
fbpoll(dev_t dev,int events,struct lwp * l)269 fbpoll(dev_t dev, int events, struct lwp *l)
270 {
271 int unit, nunit;
272 struct fbdevlist *fbl = &fblist;
273
274 unit = minor(dev);
275 while (unit-- && fbl != NULL)
276 fbl = fbl->fb_next;
277 if (fbl == NULL || fbl->fb_dev == NULL)
278 return (ENXIO);
279
280 nunit = device_unit(fbl->fb_dev->fb_device);
281 return (fbl->fb_dev->fb_driver->fbd_poll)(makedev(0, nunit), events,
282 l);
283 }
284
285 int
fbkqfilter(dev_t dev,struct knote * kn)286 fbkqfilter(dev_t dev, struct knote *kn)
287 {
288 int unit, nunit;
289 struct fbdevlist *fbl = &fblist;
290
291 unit = minor(dev);
292 while (unit-- && fbl != NULL)
293 fbl = fbl->fb_next;
294 if (fbl == NULL || fbl->fb_dev == NULL)
295 return (ENXIO);
296
297 nunit = device_unit(fbl->fb_dev->fb_device);
298 return (fbl->fb_dev->fb_driver->fbd_kqfilter)(makedev(0, nunit), kn);
299 }
300
301 paddr_t
fbmmap(dev_t dev,off_t off,int prot)302 fbmmap(dev_t dev, off_t off, int prot)
303 {
304 int unit, nunit;
305 struct fbdevlist *fbl = &fblist;
306
307 unit = minor(dev);
308 while (unit-- && fbl != NULL)
309 fbl = fbl->fb_next;
310 if (fbl == NULL || fbl->fb_dev == NULL)
311 return (ENXIO);
312
313 nunit = device_unit(fbl->fb_dev->fb_device);
314 paddr_t (*map)(dev_t, off_t, int) = fbl->fb_dev->fb_driver->fbd_mmap;
315
316 if (map == NULL)
317 return (-1);
318 return (map(makedev(0, nunit), off, prot));
319 }
320
321 void
fb_setsize_obp(struct fbdevice * fb,int depth,int def_width,int def_height,int node)322 fb_setsize_obp(struct fbdevice *fb, int depth, int def_width, int def_height, int node)
323 {
324 fb->fb_type.fb_width = prom_getpropint(node, "width", def_width);
325 fb->fb_type.fb_height = prom_getpropint(node, "height", def_height);
326 fb->fb_linebytes = prom_getpropint(node, "linebytes",
327 (fb->fb_type.fb_width * depth) / 8);
328 }
329
330 void
fb_setsize_eeprom(struct fbdevice * fb,int depth,int def_width,int def_height)331 fb_setsize_eeprom(struct fbdevice *fb, int depth, int def_width, int def_height)
332 {
333 #if !defined(SUN4U)
334 struct eeprom *eep = (struct eeprom *)eeprom_va;
335
336 if (!CPU_ISSUN4) {
337 printf("fb_setsize_eeprom: not sun4\n");
338 return;
339 }
340
341 /* Set up some defaults. */
342 fb->fb_type.fb_width = def_width;
343 fb->fb_type.fb_height = def_height;
344
345 if (fb->fb_flags & FB_PFOUR) {
346 #if NPFOUR > 0
347 fb_setsize_pfour(fb);
348 #endif
349 } else if (eep != NULL) {
350 switch (eep->eeScreenSize) {
351 case EE_SCR_1152X900:
352 fb->fb_type.fb_width = 1152;
353 fb->fb_type.fb_height = 900;
354 break;
355
356 case EE_SCR_1024X1024:
357 fb->fb_type.fb_width = 1024;
358 fb->fb_type.fb_height = 1024;
359 break;
360
361 case EE_SCR_1600X1280:
362 fb->fb_type.fb_width = 1600;
363 fb->fb_type.fb_height = 1280;
364 break;
365
366 case EE_SCR_1440X1440:
367 fb->fb_type.fb_width = 1440;
368 fb->fb_type.fb_height = 1440;
369 break;
370
371 default:
372 /*
373 * XXX: Do nothing, I guess.
374 * Should we print a warning about
375 * an unknown value? --thorpej
376 */
377 break;
378 }
379 }
380
381 fb->fb_linebytes = (fb->fb_type.fb_width * depth) / 8;
382 #endif /* !SUN4U */
383 }
384
385