fbd.c revision 257517
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 257517 2013-11-01 19:19:47Z 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 257517 2013-11-01 19:19:47Z 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
51256905Sray#include <dev/vt/hw/fb/vt_fb.h>
52256905Sray
53257438Sray#include "fb_if.h"
54257438Sray
55256905SrayLIST_HEAD(fb_list_head_t, fb_list_entry) fb_list_head =
56256905Sray    LIST_HEAD_INITIALIZER(fb_list_head);
57256905Sraystruct fb_list_entry {
58256905Sray	struct fb_info	*fb_info;
59256905Sray	struct cdev	*fb_si;
60256905Sray	LIST_ENTRY(fb_list_entry) fb_list;
61256905Sray};
62256905Sray
63257438Sraystruct fbd_softc {
64257438Sray	device_t	sc_dev;
65257438Sray	struct fb_info	*sc_info;
66257438Sray};
67257438Sray
68256905Sraystatic void fbd_evh_init(void *);
69256905Sray/* SI_ORDER_SECOND, just after EVENTHANDLERs initialized. */
70256905SraySYSINIT(fbd_evh_init, SI_SUB_CONFIGURE, SI_ORDER_SECOND, fbd_evh_init, NULL);
71256905Sray
72256905Sraystatic d_open_t		fb_open;
73256905Sraystatic d_close_t	fb_close;
74256905Sraystatic d_read_t		fb_read;
75256905Sraystatic d_write_t	fb_write;
76256905Sraystatic d_ioctl_t	fb_ioctl;
77256905Sraystatic d_mmap_t		fb_mmap;
78256905Sray
79256905Sraystatic struct cdevsw fb_cdevsw = {
80256905Sray	.d_version =	D_VERSION,
81256905Sray	.d_flags =	D_NEEDGIANT,
82256905Sray	.d_open =	fb_open,
83256905Sray	.d_close =	fb_close,
84256905Sray	.d_read =	fb_read,
85256905Sray	.d_write =	fb_write,
86256905Sray	.d_ioctl =	fb_ioctl,
87256905Sray	.d_mmap =	fb_mmap,
88256905Sray	.d_name =	"fb",
89256905Sray};
90256905Sray
91256905Sraystatic int framebuffer_dev_unit = 0;
92256905Sray
93256905Sraystatic int
94256905Srayfb_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
95256905Sray{
96256905Sray
97256905Sray	return (0);
98256905Sray}
99256905Sray
100256905Sraystatic int
101256905Srayfb_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
102256905Sray{
103256905Sray
104256905Sray	return (0);
105256905Sray}
106256905Sray
107256905Sraystatic int
108256905Srayfb_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
109256905Sray    struct thread *td)
110256905Sray{
111256905Sray
112256905Sray	return (0);
113256905Sray}
114256905Sray
115256905Sraystatic int
116256905Srayfb_read(struct cdev *dev, struct uio *uio, int ioflag)
117256905Sray{
118256905Sray
119256905Sray	return (0); /* XXX nothing to read, yet */
120256905Sray}
121256905Sray
122256905Sraystatic int
123256905Srayfb_write(struct cdev *dev, struct uio *uio, int ioflag)
124256905Sray{
125256905Sray
126256905Sray	return (0); /* XXX nothing written */
127256905Sray}
128256905Sray
129256905Sraystatic int
130256905Srayfb_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, int nprot,
131256905Sray    vm_memattr_t *memattr)
132256905Sray{
133256905Sray	struct fb_info *info;
134256905Sray
135256905Sray	info = dev->si_drv1;
136256905Sray	if (offset < info->fb_size) {
137256905Sray		*paddr = info->fb_pbase + offset;
138256905Sray		return (0);
139256905Sray	}
140256905Sray	return (EINVAL);
141256905Sray}
142256905Sray
143256905Sray
144256905Sraystatic void
145256905Srayvt_fb_mem_wr1(struct fb_info *sc, uint32_t o, uint8_t v)
146256905Sray{
147256905Sray
148256905Sray	KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o));
149256905Sray	*(uint8_t *)(sc->fb_vbase + o) = v;
150256905Sray}
151256905Sray
152256905Sraystatic void
153256905Srayvt_fb_mem_wr2(struct fb_info *sc, uint32_t o, uint16_t v)
154256905Sray{
155256905Sray
156256905Sray	KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o));
157256905Sray	*(uint16_t *)(sc->fb_vbase + o) = v;
158256905Sray}
159256905Sray
160256905Sraystatic void
161256905Srayvt_fb_mem_wr4(struct fb_info *sc, uint32_t o, uint32_t v)
162256905Sray{
163256905Sray
164256905Sray	KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o));
165256905Sray	*(uint32_t *)(sc->fb_vbase + o) = v;
166256905Sray}
167256905Sray
168256905Sraystatic void
169257013Srayvt_fb_mem_copy(struct fb_info *sc, uint32_t offset_to, uint32_t offset_from,
170257013Sray    uint32_t size)
171257013Sray{
172257013Sray
173257013Sray	memmove((void *)(sc->fb_vbase + offset_to), (void *)(sc->fb_vbase +
174257013Sray	    offset_from), size);
175257013Sray}
176257013Sray
177257013Sraystatic void
178256905Srayvt_fb_indir_wr1(struct fb_info *sc, uint32_t o, uint8_t v)
179256905Sray{
180256905Sray
181256905Sray	KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o));
182256905Sray	sc->fb_write(sc->fb_priv, o, &v, 1);
183256905Sray}
184256905Sray
185256905Sraystatic void
186256905Srayvt_fb_indir_wr2(struct fb_info *sc, uint32_t o, uint16_t v)
187256905Sray{
188256905Sray
189256905Sray	KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o));
190256905Sray	sc->fb_write(sc->fb_priv, o, &v, 2);
191256905Sray}
192256905Sray
193256905Sraystatic void
194256905Srayvt_fb_indir_wr4(struct fb_info *sc, uint32_t o, uint32_t v)
195256905Sray{
196256905Sray
197256905Sray	KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o));
198256905Sray	sc->fb_write(sc->fb_priv, o, &v, 4);
199256905Sray}
200256905Sray
201257013Sraystatic void
202257013Srayvt_fb_indir_copy(struct fb_info *sc, uint32_t offset_to, uint32_t offset_from,
203257013Sray    uint32_t size)
204257013Sray{
205257013Sray
206257013Sray	sc->copy(sc->fb_priv, offset_to, offset_from, size);
207257013Sray}
208257013Sray
209256905Sraystatic int
210256905Srayfb_probe(struct fb_info *info)
211256905Sray{
212256905Sray
213256905Sray	if (info->fb_size == 0)
214256905Sray		return (ENXIO);
215256905Sray
216256905Sray	if (info->fb_write != NULL) {
217256905Sray		if (info->fb_write == NULL) {
218256905Sray			return (EINVAL);
219256905Sray		}
220256905Sray		info->fb_flags |= FB_FLAG_NOMMAP;
221256905Sray		info->wr1 = &vt_fb_indir_wr1;
222256905Sray		info->wr2 = &vt_fb_indir_wr2;
223256905Sray		info->wr4 = &vt_fb_indir_wr4;
224257013Sray		info->copy = &vt_fb_indir_copy;
225256905Sray	} else if (info->fb_vbase != 0) {
226256905Sray		if (info->fb_pbase == 0)
227256905Sray			info->fb_flags |= FB_FLAG_NOMMAP;
228256905Sray		info->wr1 = &vt_fb_mem_wr1;
229256905Sray		info->wr2 = &vt_fb_mem_wr2;
230256905Sray		info->wr4 = &vt_fb_mem_wr4;
231257013Sray		info->copy = &vt_fb_mem_copy;
232256905Sray	} else
233256905Sray		return (ENXIO);
234256905Sray
235256905Sray	return (0);
236256905Sray}
237256905Sray
238256905Sray
239256905Sraystatic int
240256905Srayfb_init(struct fb_list_entry *entry, int unit)
241256905Sray{
242256905Sray	struct fb_info *info;
243256905Sray
244256905Sray	info = entry->fb_info;
245256905Sray	entry->fb_si = make_dev(&fb_cdevsw, unit, UID_ROOT, GID_WHEEL,
246256905Sray	    0600, "fb%d", unit);
247256905Sray	entry->fb_si->si_drv1 = info;
248256905Sray
249256905Sray	return (0);
250256905Sray}
251256905Sray
252256905Srayint
253256905Srayfbd_list()
254256905Sray{
255256905Sray	struct fb_list_entry *entry;
256256905Sray
257256905Sray	if (LIST_EMPTY(&fb_list_head))
258256905Sray		return (ENOENT);
259256905Sray
260256905Sray	LIST_FOREACH(entry, &fb_list_head, fb_list) {
261256905Sray		printf("FB %s @%p\n", entry->fb_info->fb_name,
262256905Sray		    (void *)entry->fb_info->fb_pbase);
263256905Sray	}
264256905Sray
265256905Sray	return (0);
266256905Sray}
267256905Sray
268256905Sraystatic struct fb_list_entry *
269256905Srayfbd_find(struct fb_info* info)
270256905Sray{
271256905Sray	struct fb_list_entry *entry, *tmp;
272256905Sray
273256905Sray	LIST_FOREACH_SAFE(entry, &fb_list_head, fb_list, tmp) {
274256905Sray		if (entry->fb_info == info) {
275256905Sray			return (entry);
276256905Sray		}
277256905Sray	}
278256905Sray
279256905Sray	return (NULL);
280256905Sray}
281256905Sray
282256905Srayint
283256905Srayfbd_register(struct fb_info* info)
284256905Sray{
285256905Sray	struct fb_list_entry *entry;
286256905Sray	int err, first;
287256905Sray
288256905Sray	first = 0;
289256905Sray	if (LIST_EMPTY(&fb_list_head))
290256905Sray		first++;
291256905Sray
292256905Sray	entry = fbd_find(info);
293256905Sray	if (entry != NULL) {
294256905Sray		/* XXX Update framebuffer params */
295256905Sray		return (0);
296256905Sray	}
297256905Sray
298256905Sray	err = fb_probe(info);
299256905Sray	if (err)
300256905Sray		return (err);
301256905Sray
302256905Sray	entry = malloc(sizeof(struct fb_list_entry), M_DEVBUF, M_WAITOK|M_ZERO);
303256905Sray	entry->fb_info = info;
304256905Sray
305256905Sray	LIST_INSERT_HEAD(&fb_list_head, entry, fb_list);
306256905Sray
307256905Sray	err = fb_init(entry, framebuffer_dev_unit++);
308256905Sray	if (err)
309256905Sray		return (err);
310256905Sray
311256905Sray	if (first)
312256905Sray		vt_fb_attach(info);
313256905Sray
314256905Sray	return (0);
315256905Sray}
316256905Sray
317256905Srayint
318256905Srayfbd_unregister(struct fb_info* info)
319256905Sray{
320256905Sray	struct fb_list_entry *entry, *tmp;
321256905Sray
322256905Sray	LIST_FOREACH_SAFE(entry, &fb_list_head, fb_list, tmp) {
323256905Sray		if (entry->fb_info == info) {
324256905Sray			LIST_REMOVE(entry, fb_list);
325256905Sray			free(entry, M_DEVBUF);
326256905Sray			return (0);
327256905Sray		}
328256905Sray	}
329256905Sray
330256905Sray	return (ENOENT);
331256905Sray}
332256905Sray
333256905Sraystatic void
334256905Srayregister_fb_wrap(void *arg, void *ptr)
335256905Sray{
336256905Sray
337256905Sray	fbd_register((struct fb_info *)ptr);
338256905Sray}
339256905Sray
340256905Sraystatic void
341256905Srayunregister_fb_wrap(void *arg, void *ptr)
342256905Sray{
343256905Sray
344256905Sray	fbd_unregister((struct fb_info *)ptr);
345256905Sray}
346256905Sray
347256905Sraystatic void
348256905Srayfbd_evh_init(void *ctx)
349256905Sray{
350256905Sray
351256905Sray	EVENTHANDLER_REGISTER(register_framebuffer, register_fb_wrap, NULL,
352256905Sray	    EVENTHANDLER_PRI_ANY);
353256905Sray	EVENTHANDLER_REGISTER(unregister_framebuffer, unregister_fb_wrap, NULL,
354256905Sray	    EVENTHANDLER_PRI_ANY);
355256905Sray}
356257438Sray
357257438Sray/* Newbus methods. */
358257438Sraystatic int
359257438Srayfbd_probe(device_t dev)
360257438Sray{
361257438Sray
362257438Sray	return (BUS_PROBE_NOWILDCARD);
363257438Sray}
364257438Sray
365257438Sraystatic int
366257438Srayfbd_attach(device_t dev)
367257438Sray{
368257438Sray	struct fbd_softc *sc;
369257438Sray	int err;
370257438Sray
371257438Sray	sc = device_get_softc(dev);
372257438Sray
373257438Sray	sc->sc_dev = dev;
374257438Sray	sc->sc_info = FB_GETINFO(device_get_parent(dev));
375257438Sray	err = fbd_register(sc->sc_info);
376257438Sray
377257438Sray	return (err);
378257438Sray}
379257438Sray
380257438Sraystatic int
381257438Srayfbd_detach(device_t dev)
382257438Sray{
383257438Sray	struct fbd_softc *sc;
384257438Sray	int err;
385257438Sray
386257438Sray	sc = device_get_softc(dev);
387257438Sray
388257438Sray	err = fbd_unregister(sc->sc_info);
389257438Sray
390257438Sray	return (err);
391257438Sray}
392257438Sray
393257438Sray
394257438Sraystatic device_method_t fbd_methods[] = {
395257438Sray	/* Device interface */
396257438Sray	DEVMETHOD(device_probe,		fbd_probe),
397257438Sray	DEVMETHOD(device_attach,	fbd_attach),
398257438Sray	DEVMETHOD(device_detach,	fbd_detach),
399257438Sray
400257438Sray	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
401257438Sray	DEVMETHOD(device_suspend,	bus_generic_suspend),
402257438Sray	DEVMETHOD(device_resume,	bus_generic_resume),
403257438Sray
404257438Sray	{ 0, 0 }
405257438Sray};
406257438Sray
407257438Sraydriver_t fbd_driver = {
408257438Sray	"fbd",
409257438Sray	fbd_methods,
410257438Sray	sizeof(struct fbd_softc)
411257438Sray};
412257438Sray
413257438Sraydevclass_t	fbd_devclass;
414257438Sray
415257438SrayDRIVER_MODULE(fbd, fb, fbd_driver, fbd_devclass, 0, 0);
416257517SrayDRIVER_MODULE(fbd, drmn, fbd_driver, fbd_devclass, 0, 0);
417257438SrayMODULE_VERSION(fbd, 1);
418257438Sray
419