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