fbd.c revision 256905
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 256905 2013-10-22 14:45:35Z 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 256905 2013-10-22 14:45:35Z ray $");
38
39#include <sys/param.h>
40#include <sys/systm.h>
41#include <sys/conf.h>
42#include <sys/kernel.h>
43#include <sys/malloc.h>
44#include <sys/queue.h>
45#include <sys/fbio.h>
46#include <dev/vt/hw/fb/vt_fb.h>
47
48LIST_HEAD(fb_list_head_t, fb_list_entry) fb_list_head =
49    LIST_HEAD_INITIALIZER(fb_list_head);
50struct fb_list_entry {
51	struct fb_info	*fb_info;
52	struct cdev	*fb_si;
53	LIST_ENTRY(fb_list_entry) fb_list;
54};
55
56static void fbd_evh_init(void *);
57/* SI_ORDER_SECOND, just after EVENTHANDLERs initialized. */
58SYSINIT(fbd_evh_init, SI_SUB_CONFIGURE, SI_ORDER_SECOND, fbd_evh_init, NULL);
59
60static d_open_t		fb_open;
61static d_close_t	fb_close;
62static d_read_t		fb_read;
63static d_write_t	fb_write;
64static d_ioctl_t	fb_ioctl;
65static d_mmap_t		fb_mmap;
66
67static struct cdevsw fb_cdevsw = {
68	.d_version =	D_VERSION,
69	.d_flags =	D_NEEDGIANT,
70	.d_open =	fb_open,
71	.d_close =	fb_close,
72	.d_read =	fb_read,
73	.d_write =	fb_write,
74	.d_ioctl =	fb_ioctl,
75	.d_mmap =	fb_mmap,
76	.d_name =	"fb",
77};
78
79static int framebuffer_dev_unit = 0;
80
81static int
82fb_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
83{
84
85	return (0);
86}
87
88static int
89fb_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
90{
91
92	return (0);
93}
94
95static int
96fb_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
97    struct thread *td)
98{
99
100	return (0);
101}
102
103static int
104fb_read(struct cdev *dev, struct uio *uio, int ioflag)
105{
106
107	return (0); /* XXX nothing to read, yet */
108}
109
110static int
111fb_write(struct cdev *dev, struct uio *uio, int ioflag)
112{
113
114	return (0); /* XXX nothing written */
115}
116
117static int
118fb_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, int nprot,
119    vm_memattr_t *memattr)
120{
121	struct fb_info *info;
122
123	info = dev->si_drv1;
124	if (offset < info->fb_size) {
125		*paddr = info->fb_pbase + offset;
126		return (0);
127	}
128	return (EINVAL);
129}
130
131
132static void
133vt_fb_mem_wr1(struct fb_info *sc, uint32_t o, uint8_t v)
134{
135
136	KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o));
137	*(uint8_t *)(sc->fb_vbase + o) = v;
138}
139
140static void
141vt_fb_mem_wr2(struct fb_info *sc, uint32_t o, uint16_t v)
142{
143
144	KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o));
145	*(uint16_t *)(sc->fb_vbase + o) = v;
146}
147
148static void
149vt_fb_mem_wr4(struct fb_info *sc, uint32_t o, uint32_t v)
150{
151
152	KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o));
153	*(uint32_t *)(sc->fb_vbase + o) = v;
154}
155
156static void
157vt_fb_indir_wr1(struct fb_info *sc, uint32_t o, uint8_t v)
158{
159
160	KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o));
161	sc->fb_write(sc->fb_priv, o, &v, 1);
162}
163
164static void
165vt_fb_indir_wr2(struct fb_info *sc, uint32_t o, uint16_t v)
166{
167
168	KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o));
169	sc->fb_write(sc->fb_priv, o, &v, 2);
170}
171
172static void
173vt_fb_indir_wr4(struct fb_info *sc, uint32_t o, uint32_t v)
174{
175
176	KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o));
177	sc->fb_write(sc->fb_priv, o, &v, 4);
178}
179
180static int
181fb_probe(struct fb_info *info)
182{
183
184	if (info->fb_size == 0)
185		return (ENXIO);
186
187	if (info->fb_write != NULL) {
188		if (info->fb_write == NULL) {
189			return (EINVAL);
190		}
191		info->fb_flags |= FB_FLAG_NOMMAP;
192		info->wr1 = &vt_fb_indir_wr1;
193		info->wr2 = &vt_fb_indir_wr2;
194		info->wr4 = &vt_fb_indir_wr4;
195	} else if (info->fb_vbase != 0) {
196		if (info->fb_pbase == 0)
197			info->fb_flags |= FB_FLAG_NOMMAP;
198		info->wr1 = &vt_fb_mem_wr1;
199		info->wr2 = &vt_fb_mem_wr2;
200		info->wr4 = &vt_fb_mem_wr4;
201	} else
202		return (ENXIO);
203
204	return (0);
205}
206
207
208static int
209fb_init(struct fb_list_entry *entry, int unit)
210{
211	struct fb_info *info;
212
213	info = entry->fb_info;
214	entry->fb_si = make_dev(&fb_cdevsw, unit, UID_ROOT, GID_WHEEL,
215	    0600, "fb%d", unit);
216	entry->fb_si->si_drv1 = info;
217
218	return (0);
219}
220
221int
222fbd_list()
223{
224	struct fb_list_entry *entry;
225
226	if (LIST_EMPTY(&fb_list_head))
227		return (ENOENT);
228
229	LIST_FOREACH(entry, &fb_list_head, fb_list) {
230		printf("FB %s @%p\n", entry->fb_info->fb_name,
231		    (void *)entry->fb_info->fb_pbase);
232	}
233
234	return (0);
235}
236
237static struct fb_list_entry *
238fbd_find(struct fb_info* info)
239{
240	struct fb_list_entry *entry, *tmp;
241
242	LIST_FOREACH_SAFE(entry, &fb_list_head, fb_list, tmp) {
243		if (entry->fb_info == info) {
244			return (entry);
245		}
246	}
247
248	return (NULL);
249}
250
251int
252fbd_register(struct fb_info* info)
253{
254	struct fb_list_entry *entry;
255	int err, first;
256
257	first = 0;
258	if (LIST_EMPTY(&fb_list_head))
259		first++;
260
261	entry = fbd_find(info);
262	if (entry != NULL) {
263		/* XXX Update framebuffer params */
264		return (0);
265	}
266
267	err = fb_probe(info);
268	if (err)
269		return (err);
270
271	entry = malloc(sizeof(struct fb_list_entry), M_DEVBUF, M_WAITOK|M_ZERO);
272	entry->fb_info = info;
273
274	LIST_INSERT_HEAD(&fb_list_head, entry, fb_list);
275
276	err = fb_init(entry, framebuffer_dev_unit++);
277	if (err)
278		return (err);
279
280	if (first)
281		vt_fb_attach(info);
282
283	return (0);
284}
285
286int
287fbd_unregister(struct fb_info* info)
288{
289	struct fb_list_entry *entry, *tmp;
290
291	LIST_FOREACH_SAFE(entry, &fb_list_head, fb_list, tmp) {
292		if (entry->fb_info == info) {
293			LIST_REMOVE(entry, fb_list);
294			free(entry, M_DEVBUF);
295			return (0);
296		}
297	}
298
299	return (ENOENT);
300}
301
302static void
303register_fb_wrap(void *arg, void *ptr)
304{
305
306	fbd_register((struct fb_info *)ptr);
307}
308
309static void
310unregister_fb_wrap(void *arg, void *ptr)
311{
312
313	fbd_unregister((struct fb_info *)ptr);
314}
315
316static void
317fbd_evh_init(void *ctx)
318{
319
320	EVENTHANDLER_REGISTER(register_framebuffer, register_fb_wrap, NULL,
321	    EVENTHANDLER_PRI_ANY);
322	EVENTHANDLER_REGISTER(unregister_framebuffer, unregister_fb_wrap, NULL,
323	    EVENTHANDLER_PRI_ANY);
324}
325