xref: /freebsd/sys/dev/fb/fbd.c (revision 7ec2f6bce5d28e6662c29e63f6ab6b7ef57d98b2)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2013 The FreeBSD Foundation
5  * All rights reserved.
6  *
7  * This software was developed by Aleksandr Rybalko under sponsorship from the
8  * FreeBSD Foundation.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  *
31  * $FreeBSD$
32  */
33 
34 /* Generic framebuffer */
35 /* TODO unlink from VT(9) */
36 /* TODO done normal /dev/fb methods */
37 
38 #include <sys/cdefs.h>
39 __FBSDID("$FreeBSD$");
40 
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/bus.h>
44 #include <sys/conf.h>
45 #include <sys/eventhandler.h>
46 #include <sys/kernel.h>
47 #include <sys/malloc.h>
48 #include <sys/module.h>
49 #include <sys/queue.h>
50 #include <sys/fbio.h>
51 
52 #include <machine/bus.h>
53 
54 #include <dev/vt/vt.h>
55 #include <dev/vt/hw/fb/vt_fb.h>
56 
57 #include <vm/vm.h>
58 #include <vm/pmap.h>
59 
60 #include "fb_if.h"
61 
62 LIST_HEAD(fb_list_head_t, fb_list_entry) fb_list_head =
63     LIST_HEAD_INITIALIZER(fb_list_head);
64 struct fb_list_entry {
65 	struct fb_info	*fb_info;
66 	struct cdev	*fb_si;
67 	LIST_ENTRY(fb_list_entry) fb_list;
68 };
69 
70 struct fbd_softc {
71 	device_t	sc_dev;
72 	struct fb_info	*sc_info;
73 };
74 
75 static void fbd_evh_init(void *);
76 /* SI_ORDER_SECOND, just after EVENTHANDLERs initialized. */
77 SYSINIT(fbd_evh_init, SI_SUB_CONFIGURE, SI_ORDER_SECOND, fbd_evh_init, NULL);
78 
79 static d_open_t		fb_open;
80 static d_close_t	fb_close;
81 static d_read_t		fb_read;
82 static d_write_t	fb_write;
83 static d_ioctl_t	fb_ioctl;
84 static d_mmap_t		fb_mmap;
85 
86 static struct cdevsw fb_cdevsw = {
87 	.d_version =	D_VERSION,
88 	.d_flags =	D_NEEDGIANT,
89 	.d_open =	fb_open,
90 	.d_close =	fb_close,
91 	.d_read =	fb_read,
92 	.d_write =	fb_write,
93 	.d_ioctl =	fb_ioctl,
94 	.d_mmap =	fb_mmap,
95 	.d_name =	"fb",
96 };
97 
98 static int framebuffer_dev_unit = 0;
99 
100 static int
101 fb_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
102 {
103 
104 	return (0);
105 }
106 
107 static int
108 fb_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
109 {
110 
111 	return (0);
112 }
113 
114 static int
115 fb_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
116     struct thread *td)
117 {
118 	struct fb_info *info;
119 	int error;
120 
121 	error = 0;
122 	info = dev->si_drv1;
123 
124 	switch (cmd) {
125 	case FBIOGTYPE:
126 		bcopy(info, (struct fbtype *)data, sizeof(struct fbtype));
127 		break;
128 
129 	case FBIO_GETWINORG:	/* get frame buffer window origin */
130 		*(u_int *)data = 0;
131 		break;
132 
133 	case FBIO_GETDISPSTART:	/* get display start address */
134 		((video_display_start_t *)data)->x = 0;
135 		((video_display_start_t *)data)->y = 0;
136 		break;
137 
138 	case FBIO_GETLINEWIDTH:	/* get scan line width in bytes */
139 		*(u_int *)data = info->fb_stride;
140 		break;
141 
142 	case FBIO_BLANK:	/* blank display */
143 		if (info->setblankmode != NULL)
144 			error = info->setblankmode(info->fb_priv, *(int *)data);
145 		break;
146 
147 	default:
148 		error = ENOIOCTL;
149 		break;
150 	}
151 	return (error);
152 }
153 
154 static int
155 fb_read(struct cdev *dev, struct uio *uio, int ioflag)
156 {
157 
158 	return (0); /* XXX nothing to read, yet */
159 }
160 
161 static int
162 fb_write(struct cdev *dev, struct uio *uio, int ioflag)
163 {
164 
165 	return (0); /* XXX nothing written */
166 }
167 
168 static int
169 fb_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, int nprot,
170     vm_memattr_t *memattr)
171 {
172 	struct fb_info *info;
173 
174 	info = dev->si_drv1;
175 
176 	if (info->fb_flags & FB_FLAG_NOMMAP)
177 		return (ENODEV);
178 
179 	if (offset < info->fb_size) {
180 		if (info->fb_pbase == 0)
181 			*paddr = vtophys((uint8_t *)info->fb_vbase + offset);
182 		else
183 			*paddr = info->fb_pbase + offset;
184 		if (info->fb_flags & FB_FLAG_MEMATTR)
185 			*memattr = info->fb_memattr;
186 		return (0);
187 	}
188 	return (EINVAL);
189 }
190 
191 static int
192 fb_init(struct fb_list_entry *entry, int unit)
193 {
194 	struct fb_info *info;
195 
196 	info = entry->fb_info;
197 	entry->fb_si = make_dev(&fb_cdevsw, unit, UID_ROOT, GID_WHEEL,
198 	    0600, "fb%d", unit);
199 	entry->fb_si->si_drv1 = info;
200 	info->fb_cdev = entry->fb_si;
201 
202 	return (0);
203 }
204 
205 int
206 fbd_list()
207 {
208 	struct fb_list_entry *entry;
209 
210 	if (LIST_EMPTY(&fb_list_head))
211 		return (ENOENT);
212 
213 	LIST_FOREACH(entry, &fb_list_head, fb_list) {
214 		printf("FB %s @%p\n", entry->fb_info->fb_name,
215 		    (void *)entry->fb_info->fb_pbase);
216 	}
217 
218 	return (0);
219 }
220 
221 static struct fb_list_entry *
222 fbd_find(struct fb_info* info)
223 {
224 	struct fb_list_entry *entry, *tmp;
225 
226 	LIST_FOREACH_SAFE(entry, &fb_list_head, fb_list, tmp) {
227 		if (entry->fb_info == info) {
228 			return (entry);
229 		}
230 	}
231 
232 	return (NULL);
233 }
234 
235 int
236 fbd_register(struct fb_info* info)
237 {
238 	struct fb_list_entry *entry;
239 	int err, first;
240 
241 	first = 0;
242 	if (LIST_EMPTY(&fb_list_head))
243 		first++;
244 
245 	entry = fbd_find(info);
246 	if (entry != NULL) {
247 		/* XXX Update framebuffer params */
248 		return (0);
249 	}
250 
251 	entry = malloc(sizeof(struct fb_list_entry), M_DEVBUF, M_WAITOK|M_ZERO);
252 	entry->fb_info = info;
253 
254 	LIST_INSERT_HEAD(&fb_list_head, entry, fb_list);
255 
256 	err = fb_init(entry, framebuffer_dev_unit++);
257 	if (err)
258 		return (err);
259 
260 	if (first) {
261 		err = vt_fb_attach(info);
262 		if (err)
263 			return (err);
264 	}
265 
266 	return (0);
267 }
268 
269 int
270 fbd_unregister(struct fb_info* info)
271 {
272 	struct fb_list_entry *entry, *tmp;
273 
274 	LIST_FOREACH_SAFE(entry, &fb_list_head, fb_list, tmp) {
275 		if (entry->fb_info == info) {
276 			LIST_REMOVE(entry, fb_list);
277 			if (LIST_EMPTY(&fb_list_head))
278 				vt_fb_detach(info);
279 			free(entry, M_DEVBUF);
280 			return (0);
281 		}
282 	}
283 
284 	return (ENOENT);
285 }
286 
287 static void
288 register_fb_wrap(void *arg, void *ptr)
289 {
290 
291 	fbd_register((struct fb_info *)ptr);
292 }
293 
294 static void
295 unregister_fb_wrap(void *arg, void *ptr)
296 {
297 
298 	fbd_unregister((struct fb_info *)ptr);
299 }
300 
301 static void
302 fbd_evh_init(void *ctx)
303 {
304 
305 	EVENTHANDLER_REGISTER(register_framebuffer, register_fb_wrap, NULL,
306 	    EVENTHANDLER_PRI_ANY);
307 	EVENTHANDLER_REGISTER(unregister_framebuffer, unregister_fb_wrap, NULL,
308 	    EVENTHANDLER_PRI_ANY);
309 }
310 
311 /* Newbus methods. */
312 static int
313 fbd_probe(device_t dev)
314 {
315 
316 	return (BUS_PROBE_NOWILDCARD);
317 }
318 
319 static int
320 fbd_attach(device_t dev)
321 {
322 	struct fbd_softc *sc;
323 	int err;
324 
325 	sc = device_get_softc(dev);
326 
327 	sc->sc_dev = dev;
328 	sc->sc_info = FB_GETINFO(device_get_parent(dev));
329 	if (sc->sc_info == NULL)
330 		return (ENXIO);
331 	err = fbd_register(sc->sc_info);
332 
333 	return (err);
334 }
335 
336 static int
337 fbd_detach(device_t dev)
338 {
339 	struct fbd_softc *sc;
340 	int err;
341 
342 	sc = device_get_softc(dev);
343 
344 	err = fbd_unregister(sc->sc_info);
345 
346 	return (err);
347 }
348 
349 static device_method_t fbd_methods[] = {
350 	/* Device interface */
351 	DEVMETHOD(device_probe,		fbd_probe),
352 	DEVMETHOD(device_attach,	fbd_attach),
353 	DEVMETHOD(device_detach,	fbd_detach),
354 
355 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
356 
357 	{ 0, 0 }
358 };
359 
360 driver_t fbd_driver = {
361 	"fbd",
362 	fbd_methods,
363 	sizeof(struct fbd_softc)
364 };
365 
366 devclass_t	fbd_devclass;
367 
368 DRIVER_MODULE(fbd, fb, fbd_driver, fbd_devclass, 0, 0);
369 DRIVER_MODULE(fbd, drmn, fbd_driver, fbd_devclass, 0, 0);
370 DRIVER_MODULE(fbd, udl, fbd_driver, fbd_devclass, 0, 0);
371 MODULE_VERSION(fbd, 1);
372 
373