fbd.c revision 257727
1256905Sray/*-
2256905Sray * Copyright (c) 2013 The FreeBSD Foundation
3256905Sray * All rights reserved.
4256905Sray *
5256905Sray * This software was developed by Aleksandr Rybalko under sponsorship from the
6256905Sray * FreeBSD Foundation.
7256905Sray *
8256905Sray * Redistribution and use in source and binary forms, with or without
9256905Sray * modification, are permitted provided that the following conditions
10256905Sray * are met:
11256905Sray * 1. Redistributions of source code must retain the above copyright
12256905Sray *    notice, this list of conditions and the following disclaimer.
13256905Sray * 2. Redistributions in binary form must reproduce the above copyright
14256905Sray *    notice, this list of conditions and the following disclaimer in the
15256905Sray *    documentation and/or other materials provided with the distribution.
16256905Sray *
17256905Sray * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18256905Sray * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19256905Sray * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20256905Sray * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21256905Sray * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22256905Sray * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23256905Sray * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24256905Sray * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25256905Sray * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26256905Sray * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27256905Sray * SUCH DAMAGE.
28256905Sray *
29256905Sray * $FreeBSD: user/ed/newcons/sys/dev/fb/fbd.c 257727 2013-11-05 23:12:53Z ray $
30256905Sray */
31256905Sray
32256905Sray/* Generic framebuffer */
33256905Sray/* TODO unlink from VT(9) */
34256905Sray/* TODO done normal /dev/fb methods */
35256905Sray
36256905Sray#include <sys/cdefs.h>
37256905Sray__FBSDID("$FreeBSD: user/ed/newcons/sys/dev/fb/fbd.c 257727 2013-11-05 23:12:53Z ray $");
38256905Sray
39256905Sray#include <sys/param.h>
40256905Sray#include <sys/systm.h>
41257438Sray#include <sys/bus.h>
42256905Sray#include <sys/conf.h>
43256905Sray#include <sys/kernel.h>
44256905Sray#include <sys/malloc.h>
45257438Sray#include <sys/module.h>
46256905Sray#include <sys/queue.h>
47256905Sray#include <sys/fbio.h>
48257438Sray
49257438Sray#include <machine/bus.h>
50257438Sray
51257727Sray#include <dev/vt/vt.h>
52256905Sray#include <dev/vt/hw/fb/vt_fb.h>
53256905Sray
54257438Sray#include "fb_if.h"
55257438Sray
56256905SrayLIST_HEAD(fb_list_head_t, fb_list_entry) fb_list_head =
57256905Sray    LIST_HEAD_INITIALIZER(fb_list_head);
58256905Sraystruct fb_list_entry {
59256905Sray	struct fb_info	*fb_info;
60256905Sray	struct cdev	*fb_si;
61256905Sray	LIST_ENTRY(fb_list_entry) fb_list;
62256905Sray};
63256905Sray
64257438Sraystruct fbd_softc {
65257438Sray	device_t	sc_dev;
66257438Sray	struct fb_info	*sc_info;
67257438Sray};
68257438Sray
69256905Sraystatic void fbd_evh_init(void *);
70256905Sray/* SI_ORDER_SECOND, just after EVENTHANDLERs initialized. */
71256905SraySYSINIT(fbd_evh_init, SI_SUB_CONFIGURE, SI_ORDER_SECOND, fbd_evh_init, NULL);
72256905Sray
73256905Sraystatic d_open_t		fb_open;
74256905Sraystatic d_close_t	fb_close;
75256905Sraystatic d_read_t		fb_read;
76256905Sraystatic d_write_t	fb_write;
77256905Sraystatic d_ioctl_t	fb_ioctl;
78256905Sraystatic d_mmap_t		fb_mmap;
79256905Sray
80256905Sraystatic struct cdevsw fb_cdevsw = {
81256905Sray	.d_version =	D_VERSION,
82256905Sray	.d_flags =	D_NEEDGIANT,
83256905Sray	.d_open =	fb_open,
84256905Sray	.d_close =	fb_close,
85256905Sray	.d_read =	fb_read,
86256905Sray	.d_write =	fb_write,
87256905Sray	.d_ioctl =	fb_ioctl,
88256905Sray	.d_mmap =	fb_mmap,
89256905Sray	.d_name =	"fb",
90256905Sray};
91256905Sray
92256905Sraystatic int framebuffer_dev_unit = 0;
93256905Sray
94256905Sraystatic int
95256905Srayfb_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
96256905Sray{
97256905Sray
98256905Sray	return (0);
99256905Sray}
100256905Sray
101256905Sraystatic int
102256905Srayfb_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
103256905Sray{
104256905Sray
105256905Sray	return (0);
106256905Sray}
107256905Sray
108256905Sraystatic int
109256905Srayfb_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
110256905Sray    struct thread *td)
111256905Sray{
112256905Sray
113256905Sray	return (0);
114256905Sray}
115256905Sray
116256905Sraystatic int
117256905Srayfb_read(struct cdev *dev, struct uio *uio, int ioflag)
118256905Sray{
119256905Sray
120256905Sray	return (0); /* XXX nothing to read, yet */
121256905Sray}
122256905Sray
123256905Sraystatic int
124256905Srayfb_write(struct cdev *dev, struct uio *uio, int ioflag)
125256905Sray{
126256905Sray
127256905Sray	return (0); /* XXX nothing written */
128256905Sray}
129256905Sray
130256905Sraystatic int
131256905Srayfb_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, int nprot,
132256905Sray    vm_memattr_t *memattr)
133256905Sray{
134256905Sray	struct fb_info *info;
135256905Sray
136256905Sray	info = dev->si_drv1;
137256905Sray	if (offset < info->fb_size) {
138256905Sray		*paddr = info->fb_pbase + offset;
139256905Sray		return (0);
140256905Sray	}
141256905Sray	return (EINVAL);
142256905Sray}
143256905Sray
144256905Sray
145256905Sraystatic void
146256905Srayvt_fb_mem_wr1(struct fb_info *sc, uint32_t o, uint8_t v)
147256905Sray{
148256905Sray
149256905Sray	KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o));
150256905Sray	*(uint8_t *)(sc->fb_vbase + o) = v;
151256905Sray}
152256905Sray
153256905Sraystatic void
154256905Srayvt_fb_mem_wr2(struct fb_info *sc, uint32_t o, uint16_t v)
155256905Sray{
156256905Sray
157256905Sray	KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o));
158256905Sray	*(uint16_t *)(sc->fb_vbase + o) = v;
159256905Sray}
160256905Sray
161256905Sraystatic void
162256905Srayvt_fb_mem_wr4(struct fb_info *sc, uint32_t o, uint32_t v)
163256905Sray{
164256905Sray
165256905Sray	KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o));
166256905Sray	*(uint32_t *)(sc->fb_vbase + o) = v;
167256905Sray}
168256905Sray
169256905Sraystatic void
170257013Srayvt_fb_mem_copy(struct fb_info *sc, uint32_t offset_to, uint32_t offset_from,
171257013Sray    uint32_t size)
172257013Sray{
173257013Sray
174257013Sray	memmove((void *)(sc->fb_vbase + offset_to), (void *)(sc->fb_vbase +
175257013Sray	    offset_from), size);
176257013Sray}
177257013Sray
178257013Sraystatic void
179256905Srayvt_fb_indir_wr1(struct fb_info *sc, uint32_t o, uint8_t v)
180256905Sray{
181256905Sray
182256905Sray	KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o));
183256905Sray	sc->fb_write(sc->fb_priv, o, &v, 1);
184256905Sray}
185256905Sray
186256905Sraystatic void
187256905Srayvt_fb_indir_wr2(struct fb_info *sc, uint32_t o, uint16_t v)
188256905Sray{
189256905Sray
190256905Sray	KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o));
191256905Sray	sc->fb_write(sc->fb_priv, o, &v, 2);
192256905Sray}
193256905Sray
194256905Sraystatic void
195256905Srayvt_fb_indir_wr4(struct fb_info *sc, uint32_t o, uint32_t v)
196256905Sray{
197256905Sray
198256905Sray	KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o));
199256905Sray	sc->fb_write(sc->fb_priv, o, &v, 4);
200256905Sray}
201256905Sray
202257013Sraystatic void
203257013Srayvt_fb_indir_copy(struct fb_info *sc, uint32_t offset_to, uint32_t offset_from,
204257013Sray    uint32_t size)
205257013Sray{
206257013Sray
207257013Sray	sc->copy(sc->fb_priv, offset_to, offset_from, size);
208257013Sray}
209257013Sray
210257727Srayint
211256905Srayfb_probe(struct fb_info *info)
212256905Sray{
213256905Sray
214256905Sray	if (info->fb_size == 0)
215256905Sray		return (ENXIO);
216256905Sray
217256905Sray	if (info->fb_write != NULL) {
218256905Sray		if (info->fb_write == NULL) {
219256905Sray			return (EINVAL);
220256905Sray		}
221256905Sray		info->fb_flags |= FB_FLAG_NOMMAP;
222256905Sray		info->wr1 = &vt_fb_indir_wr1;
223256905Sray		info->wr2 = &vt_fb_indir_wr2;
224256905Sray		info->wr4 = &vt_fb_indir_wr4;
225257013Sray		info->copy = &vt_fb_indir_copy;
226256905Sray	} else if (info->fb_vbase != 0) {
227256905Sray		if (info->fb_pbase == 0)
228256905Sray			info->fb_flags |= FB_FLAG_NOMMAP;
229256905Sray		info->wr1 = &vt_fb_mem_wr1;
230256905Sray		info->wr2 = &vt_fb_mem_wr2;
231256905Sray		info->wr4 = &vt_fb_mem_wr4;
232257013Sray		info->copy = &vt_fb_mem_copy;
233256905Sray	} else
234256905Sray		return (ENXIO);
235256905Sray
236256905Sray	return (0);
237256905Sray}
238256905Sray
239256905Sray
240256905Sraystatic int
241256905Srayfb_init(struct fb_list_entry *entry, int unit)
242256905Sray{
243256905Sray	struct fb_info *info;
244256905Sray
245256905Sray	info = entry->fb_info;
246256905Sray	entry->fb_si = make_dev(&fb_cdevsw, unit, UID_ROOT, GID_WHEEL,
247256905Sray	    0600, "fb%d", unit);
248256905Sray	entry->fb_si->si_drv1 = info;
249256905Sray
250256905Sray	return (0);
251256905Sray}
252256905Sray
253256905Srayint
254256905Srayfbd_list()
255256905Sray{
256256905Sray	struct fb_list_entry *entry;
257256905Sray
258256905Sray	if (LIST_EMPTY(&fb_list_head))
259256905Sray		return (ENOENT);
260256905Sray
261256905Sray	LIST_FOREACH(entry, &fb_list_head, fb_list) {
262256905Sray		printf("FB %s @%p\n", entry->fb_info->fb_name,
263256905Sray		    (void *)entry->fb_info->fb_pbase);
264256905Sray	}
265256905Sray
266256905Sray	return (0);
267256905Sray}
268256905Sray
269256905Sraystatic struct fb_list_entry *
270256905Srayfbd_find(struct fb_info* info)
271256905Sray{
272256905Sray	struct fb_list_entry *entry, *tmp;
273256905Sray
274256905Sray	LIST_FOREACH_SAFE(entry, &fb_list_head, fb_list, tmp) {
275256905Sray		if (entry->fb_info == info) {
276256905Sray			return (entry);
277256905Sray		}
278256905Sray	}
279256905Sray
280256905Sray	return (NULL);
281256905Sray}
282256905Sray
283256905Srayint
284256905Srayfbd_register(struct fb_info* info)
285256905Sray{
286256905Sray	struct fb_list_entry *entry;
287256905Sray	int err, first;
288256905Sray
289256905Sray	first = 0;
290256905Sray	if (LIST_EMPTY(&fb_list_head))
291256905Sray		first++;
292256905Sray
293256905Sray	entry = fbd_find(info);
294256905Sray	if (entry != NULL) {
295256905Sray		/* XXX Update framebuffer params */
296256905Sray		return (0);
297256905Sray	}
298256905Sray
299256905Sray	err = fb_probe(info);
300256905Sray	if (err)
301256905Sray		return (err);
302256905Sray
303256905Sray	entry = malloc(sizeof(struct fb_list_entry), M_DEVBUF, M_WAITOK|M_ZERO);
304256905Sray	entry->fb_info = info;
305256905Sray
306256905Sray	LIST_INSERT_HEAD(&fb_list_head, entry, fb_list);
307256905Sray
308256905Sray	err = fb_init(entry, framebuffer_dev_unit++);
309256905Sray	if (err)
310256905Sray		return (err);
311256905Sray
312256905Sray	if (first)
313256905Sray		vt_fb_attach(info);
314256905Sray
315256905Sray	return (0);
316256905Sray}
317256905Sray
318256905Srayint
319256905Srayfbd_unregister(struct fb_info* info)
320256905Sray{
321256905Sray	struct fb_list_entry *entry, *tmp;
322256905Sray
323256905Sray	LIST_FOREACH_SAFE(entry, &fb_list_head, fb_list, tmp) {
324256905Sray		if (entry->fb_info == info) {
325256905Sray			LIST_REMOVE(entry, fb_list);
326256905Sray			free(entry, M_DEVBUF);
327256905Sray			return (0);
328256905Sray		}
329256905Sray	}
330256905Sray
331256905Sray	return (ENOENT);
332256905Sray}
333256905Sray
334256905Sraystatic void
335256905Srayregister_fb_wrap(void *arg, void *ptr)
336256905Sray{
337256905Sray
338256905Sray	fbd_register((struct fb_info *)ptr);
339256905Sray}
340256905Sray
341256905Sraystatic void
342256905Srayunregister_fb_wrap(void *arg, void *ptr)
343256905Sray{
344256905Sray
345256905Sray	fbd_unregister((struct fb_info *)ptr);
346256905Sray}
347256905Sray
348256905Sraystatic void
349256905Srayfbd_evh_init(void *ctx)
350256905Sray{
351256905Sray
352256905Sray	EVENTHANDLER_REGISTER(register_framebuffer, register_fb_wrap, NULL,
353256905Sray	    EVENTHANDLER_PRI_ANY);
354256905Sray	EVENTHANDLER_REGISTER(unregister_framebuffer, unregister_fb_wrap, NULL,
355256905Sray	    EVENTHANDLER_PRI_ANY);
356256905Sray}
357257438Sray
358257438Sray/* Newbus methods. */
359257438Sraystatic int
360257438Srayfbd_probe(device_t dev)
361257438Sray{
362257438Sray
363257438Sray	return (BUS_PROBE_NOWILDCARD);
364257438Sray}
365257438Sray
366257438Sraystatic int
367257438Srayfbd_attach(device_t dev)
368257438Sray{
369257438Sray	struct fbd_softc *sc;
370257438Sray	int err;
371257438Sray
372257438Sray	sc = device_get_softc(dev);
373257438Sray
374257438Sray	sc->sc_dev = dev;
375257438Sray	sc->sc_info = FB_GETINFO(device_get_parent(dev));
376257546Sray	if (sc->sc_info == NULL)
377257546Sray		return (ENXIO);
378257438Sray	err = fbd_register(sc->sc_info);
379257438Sray
380257438Sray	return (err);
381257438Sray}
382257438Sray
383257438Sraystatic int
384257438Srayfbd_detach(device_t dev)
385257438Sray{
386257438Sray	struct fbd_softc *sc;
387257438Sray	int err;
388257438Sray
389257438Sray	sc = device_get_softc(dev);
390257438Sray
391257438Sray	err = fbd_unregister(sc->sc_info);
392257438Sray
393257438Sray	return (err);
394257438Sray}
395257438Sray
396257438Sray
397257438Sraystatic device_method_t fbd_methods[] = {
398257438Sray	/* Device interface */
399257438Sray	DEVMETHOD(device_probe,		fbd_probe),
400257438Sray	DEVMETHOD(device_attach,	fbd_attach),
401257438Sray	DEVMETHOD(device_detach,	fbd_detach),
402257438Sray
403257438Sray	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
404257438Sray	DEVMETHOD(device_suspend,	bus_generic_suspend),
405257438Sray	DEVMETHOD(device_resume,	bus_generic_resume),
406257438Sray
407257438Sray	{ 0, 0 }
408257438Sray};
409257438Sray
410257438Sraydriver_t fbd_driver = {
411257438Sray	"fbd",
412257438Sray	fbd_methods,
413257438Sray	sizeof(struct fbd_softc)
414257438Sray};
415257438Sray
416257438Sraydevclass_t	fbd_devclass;
417257438Sray
418257438SrayDRIVER_MODULE(fbd, fb, fbd_driver, fbd_devclass, 0, 0);
419257517SrayDRIVER_MODULE(fbd, drmn, fbd_driver, fbd_devclass, 0, 0);
420257438SrayMODULE_VERSION(fbd, 1);
421257438Sray
422