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$ 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$"); 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/vt.h> 52 #include <dev/vt/hw/fb/vt_fb.h> 53 54 #include "fb_if.h" 55 56 LIST_HEAD(fb_list_head_t, fb_list_entry) fb_list_head = 57 LIST_HEAD_INITIALIZER(fb_list_head); 58 struct fb_list_entry { 59 struct fb_info *fb_info; 60 struct cdev *fb_si; 61 LIST_ENTRY(fb_list_entry) fb_list; 62 }; 63 64 struct fbd_softc { 65 device_t sc_dev; 66 struct fb_info *sc_info; 67 }; 68 69 static void fbd_evh_init(void *); 70 /* SI_ORDER_SECOND, just after EVENTHANDLERs initialized. */ 71 SYSINIT(fbd_evh_init, SI_SUB_CONFIGURE, SI_ORDER_SECOND, fbd_evh_init, NULL); 72 73 static d_open_t fb_open; 74 static d_close_t fb_close; 75 static d_read_t fb_read; 76 static d_write_t fb_write; 77 static d_ioctl_t fb_ioctl; 78 static d_mmap_t fb_mmap; 79 80 static struct cdevsw fb_cdevsw = { 81 .d_version = D_VERSION, 82 .d_flags = D_NEEDGIANT, 83 .d_open = fb_open, 84 .d_close = fb_close, 85 .d_read = fb_read, 86 .d_write = fb_write, 87 .d_ioctl = fb_ioctl, 88 .d_mmap = fb_mmap, 89 .d_name = "fb", 90 }; 91 92 static int framebuffer_dev_unit = 0; 93 94 static int 95 fb_open(struct cdev *dev, int oflags, int devtype, struct thread *td) 96 { 97 98 return (0); 99 } 100 101 static int 102 fb_close(struct cdev *dev, int fflag, int devtype, struct thread *td) 103 { 104 105 return (0); 106 } 107 108 static int 109 fb_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, 110 struct thread *td) 111 { 112 struct fb_info *info; 113 int error; 114 115 error = 0; 116 info = dev->si_drv1; 117 118 switch (cmd) { 119 case FBIOGTYPE: 120 bcopy(info, (struct fbtype *)data, sizeof(struct fbtype)); 121 break; 122 123 case FBIO_GETWINORG: /* get frame buffer window origin */ 124 *(u_int *)data = 0; 125 break; 126 127 case FBIO_GETDISPSTART: /* get display start address */ 128 ((video_display_start_t *)data)->x = 0; 129 ((video_display_start_t *)data)->y = 0; 130 break; 131 132 case FBIO_GETLINEWIDTH: /* get scan line width in bytes */ 133 *(u_int *)data = info->fb_stride; 134 break; 135 136 case FBIO_BLANK: /* blank display */ 137 error = 0; /* TODO */ 138 break; 139 140 default: 141 error = ENOIOCTL; 142 break; 143 } 144 return (error); 145 } 146 147 static int 148 fb_read(struct cdev *dev, struct uio *uio, int ioflag) 149 { 150 151 return (0); /* XXX nothing to read, yet */ 152 } 153 154 static int 155 fb_write(struct cdev *dev, struct uio *uio, int ioflag) 156 { 157 158 return (0); /* XXX nothing written */ 159 } 160 161 static int 162 fb_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, int nprot, 163 vm_memattr_t *memattr) 164 { 165 struct fb_info *info; 166 167 info = dev->si_drv1; 168 if (offset < info->fb_size) { 169 *paddr = info->fb_pbase + offset; 170 return (0); 171 } 172 return (EINVAL); 173 } 174 175 176 static void 177 vt_fb_mem_wr1(struct fb_info *sc, uint32_t o, uint8_t v) 178 { 179 180 KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o)); 181 *(uint8_t *)(sc->fb_vbase + o) = v; 182 } 183 184 static void 185 vt_fb_mem_wr2(struct fb_info *sc, uint32_t o, uint16_t v) 186 { 187 188 KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o)); 189 *(uint16_t *)(sc->fb_vbase + o) = v; 190 } 191 192 static void 193 vt_fb_mem_wr4(struct fb_info *sc, uint32_t o, uint32_t v) 194 { 195 196 KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o)); 197 *(uint32_t *)(sc->fb_vbase + o) = v; 198 } 199 200 static void 201 vt_fb_mem_copy(struct fb_info *sc, uint32_t offset_to, uint32_t offset_from, 202 uint32_t size) 203 { 204 205 memmove((void *)(sc->fb_vbase + offset_to), (void *)(sc->fb_vbase + 206 offset_from), size); 207 } 208 209 static void 210 vt_fb_indir_wr1(struct fb_info *sc, uint32_t o, uint8_t v) 211 { 212 213 KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o)); 214 sc->fb_write(sc->fb_priv, o, &v, 1); 215 } 216 217 static void 218 vt_fb_indir_wr2(struct fb_info *sc, uint32_t o, uint16_t v) 219 { 220 221 KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o)); 222 sc->fb_write(sc->fb_priv, o, &v, 2); 223 } 224 225 static void 226 vt_fb_indir_wr4(struct fb_info *sc, uint32_t o, uint32_t v) 227 { 228 229 KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o)); 230 sc->fb_write(sc->fb_priv, o, &v, 4); 231 } 232 233 static void 234 vt_fb_indir_copy(struct fb_info *sc, uint32_t offset_to, uint32_t offset_from, 235 uint32_t size) 236 { 237 238 sc->copy(sc->fb_priv, offset_to, offset_from, size); 239 } 240 241 int 242 fb_probe(struct fb_info *info) 243 { 244 245 if (info->fb_size == 0) 246 return (ENXIO); 247 248 if (info->fb_write != NULL) { 249 if (info->fb_write == NULL) { 250 return (EINVAL); 251 } 252 info->fb_flags |= FB_FLAG_NOMMAP; 253 info->wr1 = &vt_fb_indir_wr1; 254 info->wr2 = &vt_fb_indir_wr2; 255 info->wr4 = &vt_fb_indir_wr4; 256 info->copy = &vt_fb_indir_copy; 257 } else if (info->fb_vbase != 0) { 258 if (info->fb_pbase == 0) { 259 info->fb_flags |= FB_FLAG_NOMMAP; 260 } else { 261 if (info->fb_mmap == NULL) 262 info->fb_mmap = &fb_mmap; 263 } 264 info->wr1 = &vt_fb_mem_wr1; 265 info->wr2 = &vt_fb_mem_wr2; 266 info->wr4 = &vt_fb_mem_wr4; 267 info->copy = &vt_fb_mem_copy; 268 } else 269 return (ENXIO); 270 271 if (info->fb_ioctl == NULL) 272 info->fb_ioctl = &fb_ioctl; 273 274 275 return (0); 276 } 277 278 279 static int 280 fb_init(struct fb_list_entry *entry, int unit) 281 { 282 struct fb_info *info; 283 284 info = entry->fb_info; 285 entry->fb_si = make_dev(&fb_cdevsw, unit, UID_ROOT, GID_WHEEL, 286 0600, "fb%d", unit); 287 entry->fb_si->si_drv1 = info; 288 info->fb_cdev = entry->fb_si; 289 290 return (0); 291 } 292 293 int 294 fbd_list() 295 { 296 struct fb_list_entry *entry; 297 298 if (LIST_EMPTY(&fb_list_head)) 299 return (ENOENT); 300 301 LIST_FOREACH(entry, &fb_list_head, fb_list) { 302 printf("FB %s @%p\n", entry->fb_info->fb_name, 303 (void *)entry->fb_info->fb_pbase); 304 } 305 306 return (0); 307 } 308 309 static struct fb_list_entry * 310 fbd_find(struct fb_info* info) 311 { 312 struct fb_list_entry *entry, *tmp; 313 314 LIST_FOREACH_SAFE(entry, &fb_list_head, fb_list, tmp) { 315 if (entry->fb_info == info) { 316 return (entry); 317 } 318 } 319 320 return (NULL); 321 } 322 323 int 324 fbd_register(struct fb_info* info) 325 { 326 struct fb_list_entry *entry; 327 int err, first; 328 329 first = 0; 330 if (LIST_EMPTY(&fb_list_head)) 331 first++; 332 333 entry = fbd_find(info); 334 if (entry != NULL) { 335 /* XXX Update framebuffer params */ 336 return (0); 337 } 338 339 err = fb_probe(info); 340 if (err) 341 return (err); 342 343 entry = malloc(sizeof(struct fb_list_entry), M_DEVBUF, M_WAITOK|M_ZERO); 344 entry->fb_info = info; 345 346 LIST_INSERT_HEAD(&fb_list_head, entry, fb_list); 347 348 err = fb_init(entry, framebuffer_dev_unit++); 349 if (err) 350 return (err); 351 352 if (first) 353 vt_fb_attach(info); 354 355 return (0); 356 } 357 358 int 359 fbd_unregister(struct fb_info* info) 360 { 361 struct fb_list_entry *entry, *tmp; 362 363 LIST_FOREACH_SAFE(entry, &fb_list_head, fb_list, tmp) { 364 if (entry->fb_info == info) { 365 LIST_REMOVE(entry, fb_list); 366 free(entry, M_DEVBUF); 367 return (0); 368 } 369 } 370 371 return (ENOENT); 372 } 373 374 static void 375 register_fb_wrap(void *arg, void *ptr) 376 { 377 378 fbd_register((struct fb_info *)ptr); 379 } 380 381 static void 382 unregister_fb_wrap(void *arg, void *ptr) 383 { 384 385 fbd_unregister((struct fb_info *)ptr); 386 } 387 388 static void 389 fbd_evh_init(void *ctx) 390 { 391 392 EVENTHANDLER_REGISTER(register_framebuffer, register_fb_wrap, NULL, 393 EVENTHANDLER_PRI_ANY); 394 EVENTHANDLER_REGISTER(unregister_framebuffer, unregister_fb_wrap, NULL, 395 EVENTHANDLER_PRI_ANY); 396 } 397 398 /* Newbus methods. */ 399 static int 400 fbd_probe(device_t dev) 401 { 402 403 return (BUS_PROBE_NOWILDCARD); 404 } 405 406 static int 407 fbd_attach(device_t dev) 408 { 409 struct fbd_softc *sc; 410 int err; 411 412 sc = device_get_softc(dev); 413 414 sc->sc_dev = dev; 415 sc->sc_info = FB_GETINFO(device_get_parent(dev)); 416 if (sc->sc_info == NULL) 417 return (ENXIO); 418 err = fbd_register(sc->sc_info); 419 420 return (err); 421 } 422 423 static int 424 fbd_detach(device_t dev) 425 { 426 struct fbd_softc *sc; 427 int err; 428 429 sc = device_get_softc(dev); 430 431 err = fbd_unregister(sc->sc_info); 432 433 return (err); 434 } 435 436 static int 437 fbd_suspend(device_t dev) 438 { 439 440 vt_fb_suspend(); 441 return (bus_generic_suspend(dev)); 442 } 443 444 static int 445 fbd_resume(device_t dev) 446 { 447 448 vt_fb_resume(); 449 return (bus_generic_resume(dev)); 450 } 451 452 static device_method_t fbd_methods[] = { 453 /* Device interface */ 454 DEVMETHOD(device_probe, fbd_probe), 455 DEVMETHOD(device_attach, fbd_attach), 456 DEVMETHOD(device_detach, fbd_detach), 457 458 DEVMETHOD(device_shutdown, bus_generic_shutdown), 459 DEVMETHOD(device_suspend, fbd_suspend), 460 DEVMETHOD(device_resume, fbd_resume), 461 462 { 0, 0 } 463 }; 464 465 driver_t fbd_driver = { 466 "fbd", 467 fbd_methods, 468 sizeof(struct fbd_softc) 469 }; 470 471 devclass_t fbd_devclass; 472 473 DRIVER_MODULE(fbd, fb, fbd_driver, fbd_devclass, 0, 0); 474 DRIVER_MODULE(fbd, drmn, fbd_driver, fbd_devclass, 0, 0); 475 MODULE_VERSION(fbd, 1); 476 477