fbd.c revision 257013
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 257013 2013-10-23 19:45:14Z 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 257013 2013-10-23 19:45:14Z 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_mem_copy(struct fb_info *sc, uint32_t offset_to, uint32_t offset_from,
158    uint32_t size)
159{
160
161	memmove((void *)(sc->fb_vbase + offset_to), (void *)(sc->fb_vbase +
162	    offset_from), size);
163}
164
165static void
166vt_fb_indir_wr1(struct fb_info *sc, uint32_t o, uint8_t v)
167{
168
169	KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o));
170	sc->fb_write(sc->fb_priv, o, &v, 1);
171}
172
173static void
174vt_fb_indir_wr2(struct fb_info *sc, uint32_t o, uint16_t v)
175{
176
177	KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o));
178	sc->fb_write(sc->fb_priv, o, &v, 2);
179}
180
181static void
182vt_fb_indir_wr4(struct fb_info *sc, uint32_t o, uint32_t v)
183{
184
185	KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o));
186	sc->fb_write(sc->fb_priv, o, &v, 4);
187}
188
189static void
190vt_fb_indir_copy(struct fb_info *sc, uint32_t offset_to, uint32_t offset_from,
191    uint32_t size)
192{
193
194	sc->copy(sc->fb_priv, offset_to, offset_from, size);
195}
196
197static int
198fb_probe(struct fb_info *info)
199{
200
201	if (info->fb_size == 0)
202		return (ENXIO);
203
204	if (info->fb_write != NULL) {
205		if (info->fb_write == NULL) {
206			return (EINVAL);
207		}
208		info->fb_flags |= FB_FLAG_NOMMAP;
209		info->wr1 = &vt_fb_indir_wr1;
210		info->wr2 = &vt_fb_indir_wr2;
211		info->wr4 = &vt_fb_indir_wr4;
212		info->copy = &vt_fb_indir_copy;
213	} else if (info->fb_vbase != 0) {
214		if (info->fb_pbase == 0)
215			info->fb_flags |= FB_FLAG_NOMMAP;
216		info->wr1 = &vt_fb_mem_wr1;
217		info->wr2 = &vt_fb_mem_wr2;
218		info->wr4 = &vt_fb_mem_wr4;
219		info->copy = &vt_fb_mem_copy;
220	} else
221		return (ENXIO);
222
223	return (0);
224}
225
226
227static int
228fb_init(struct fb_list_entry *entry, int unit)
229{
230	struct fb_info *info;
231
232	info = entry->fb_info;
233	entry->fb_si = make_dev(&fb_cdevsw, unit, UID_ROOT, GID_WHEEL,
234	    0600, "fb%d", unit);
235	entry->fb_si->si_drv1 = info;
236
237	return (0);
238}
239
240int
241fbd_list()
242{
243	struct fb_list_entry *entry;
244
245	if (LIST_EMPTY(&fb_list_head))
246		return (ENOENT);
247
248	LIST_FOREACH(entry, &fb_list_head, fb_list) {
249		printf("FB %s @%p\n", entry->fb_info->fb_name,
250		    (void *)entry->fb_info->fb_pbase);
251	}
252
253	return (0);
254}
255
256static struct fb_list_entry *
257fbd_find(struct fb_info* info)
258{
259	struct fb_list_entry *entry, *tmp;
260
261	LIST_FOREACH_SAFE(entry, &fb_list_head, fb_list, tmp) {
262		if (entry->fb_info == info) {
263			return (entry);
264		}
265	}
266
267	return (NULL);
268}
269
270int
271fbd_register(struct fb_info* info)
272{
273	struct fb_list_entry *entry;
274	int err, first;
275
276	first = 0;
277	if (LIST_EMPTY(&fb_list_head))
278		first++;
279
280	entry = fbd_find(info);
281	if (entry != NULL) {
282		/* XXX Update framebuffer params */
283		return (0);
284	}
285
286	err = fb_probe(info);
287	if (err)
288		return (err);
289
290	entry = malloc(sizeof(struct fb_list_entry), M_DEVBUF, M_WAITOK|M_ZERO);
291	entry->fb_info = info;
292
293	LIST_INSERT_HEAD(&fb_list_head, entry, fb_list);
294
295	err = fb_init(entry, framebuffer_dev_unit++);
296	if (err)
297		return (err);
298
299	if (first)
300		vt_fb_attach(info);
301
302	return (0);
303}
304
305int
306fbd_unregister(struct fb_info* info)
307{
308	struct fb_list_entry *entry, *tmp;
309
310	LIST_FOREACH_SAFE(entry, &fb_list_head, fb_list, tmp) {
311		if (entry->fb_info == info) {
312			LIST_REMOVE(entry, fb_list);
313			free(entry, M_DEVBUF);
314			return (0);
315		}
316	}
317
318	return (ENOENT);
319}
320
321static void
322register_fb_wrap(void *arg, void *ptr)
323{
324
325	fbd_register((struct fb_info *)ptr);
326}
327
328static void
329unregister_fb_wrap(void *arg, void *ptr)
330{
331
332	fbd_unregister((struct fb_info *)ptr);
333}
334
335static void
336fbd_evh_init(void *ctx)
337{
338
339	EVENTHANDLER_REGISTER(register_framebuffer, register_fb_wrap, NULL,
340	    EVENTHANDLER_PRI_ANY);
341	EVENTHANDLER_REGISTER(unregister_framebuffer, unregister_fb_wrap, NULL,
342	    EVENTHANDLER_PRI_ANY);
343}
344