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