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 <vm/vm.h> 55 #include <vm/pmap.h> 56 57 #include "fb_if.h" 58 59 LIST_HEAD(fb_list_head_t, fb_list_entry) fb_list_head = 60 LIST_HEAD_INITIALIZER(fb_list_head); 61 struct fb_list_entry { 62 struct fb_info *fb_info; 63 struct cdev *fb_si; 64 LIST_ENTRY(fb_list_entry) fb_list; 65 }; 66 67 struct fbd_softc { 68 device_t sc_dev; 69 struct fb_info *sc_info; 70 }; 71 72 static void fbd_evh_init(void *); 73 /* SI_ORDER_SECOND, just after EVENTHANDLERs initialized. */ 74 SYSINIT(fbd_evh_init, SI_SUB_CONFIGURE, SI_ORDER_SECOND, fbd_evh_init, NULL); 75 76 static d_open_t fb_open; 77 static d_close_t fb_close; 78 static d_read_t fb_read; 79 static d_write_t fb_write; 80 static d_ioctl_t fb_ioctl; 81 static d_mmap_t fb_mmap; 82 83 static struct cdevsw fb_cdevsw = { 84 .d_version = D_VERSION, 85 .d_flags = D_NEEDGIANT, 86 .d_open = fb_open, 87 .d_close = fb_close, 88 .d_read = fb_read, 89 .d_write = fb_write, 90 .d_ioctl = fb_ioctl, 91 .d_mmap = fb_mmap, 92 .d_name = "fb", 93 }; 94 95 static int framebuffer_dev_unit = 0; 96 97 static int 98 fb_open(struct cdev *dev, int oflags, int devtype, struct thread *td) 99 { 100 101 return (0); 102 } 103 104 static int 105 fb_close(struct cdev *dev, int fflag, int devtype, struct thread *td) 106 { 107 108 return (0); 109 } 110 111 static int 112 fb_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, 113 struct thread *td) 114 { 115 struct fb_info *info; 116 int error; 117 118 error = 0; 119 info = dev->si_drv1; 120 121 switch (cmd) { 122 case FBIOGTYPE: 123 bcopy(info, (struct fbtype *)data, sizeof(struct fbtype)); 124 break; 125 126 case FBIO_GETWINORG: /* get frame buffer window origin */ 127 *(u_int *)data = 0; 128 break; 129 130 case FBIO_GETDISPSTART: /* get display start address */ 131 ((video_display_start_t *)data)->x = 0; 132 ((video_display_start_t *)data)->y = 0; 133 break; 134 135 case FBIO_GETLINEWIDTH: /* get scan line width in bytes */ 136 *(u_int *)data = info->fb_stride; 137 break; 138 139 case FBIO_BLANK: /* blank display */ 140 if (info->setblankmode != NULL) 141 error = info->setblankmode(info->fb_priv, *(int *)data); 142 break; 143 144 default: 145 error = ENOIOCTL; 146 break; 147 } 148 return (error); 149 } 150 151 static int 152 fb_read(struct cdev *dev, struct uio *uio, int ioflag) 153 { 154 155 return (0); /* XXX nothing to read, yet */ 156 } 157 158 static int 159 fb_write(struct cdev *dev, struct uio *uio, int ioflag) 160 { 161 162 return (0); /* XXX nothing written */ 163 } 164 165 static int 166 fb_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, int nprot, 167 vm_memattr_t *memattr) 168 { 169 struct fb_info *info; 170 171 info = dev->si_drv1; 172 173 if (info->fb_flags & FB_FLAG_NOMMAP) 174 return (ENODEV); 175 176 if (offset >= 0 && offset < info->fb_size) { 177 if (info->fb_pbase == 0) 178 *paddr = vtophys((uint8_t *)info->fb_vbase + offset); 179 else 180 *paddr = info->fb_pbase + offset; 181 if (info->fb_flags & FB_FLAG_MEMATTR) 182 *memattr = info->fb_memattr; 183 return (0); 184 } 185 return (EINVAL); 186 } 187 188 static int 189 fb_init(struct fb_list_entry *entry, int unit) 190 { 191 struct fb_info *info; 192 193 info = entry->fb_info; 194 entry->fb_si = make_dev(&fb_cdevsw, unit, UID_ROOT, GID_WHEEL, 195 0600, "fb%d", unit); 196 entry->fb_si->si_drv1 = info; 197 info->fb_cdev = entry->fb_si; 198 199 return (0); 200 } 201 202 int 203 fbd_list() 204 { 205 struct fb_list_entry *entry; 206 207 if (LIST_EMPTY(&fb_list_head)) 208 return (ENOENT); 209 210 LIST_FOREACH(entry, &fb_list_head, fb_list) { 211 printf("FB %s @%p\n", entry->fb_info->fb_name, 212 (void *)entry->fb_info->fb_pbase); 213 } 214 215 return (0); 216 } 217 218 static struct fb_list_entry * 219 fbd_find(struct fb_info* info) 220 { 221 struct fb_list_entry *entry, *tmp; 222 223 LIST_FOREACH_SAFE(entry, &fb_list_head, fb_list, tmp) { 224 if (entry->fb_info == info) { 225 return (entry); 226 } 227 } 228 229 return (NULL); 230 } 231 232 int 233 fbd_register(struct fb_info* info) 234 { 235 struct fb_list_entry *entry; 236 int err, first; 237 238 first = 0; 239 if (LIST_EMPTY(&fb_list_head)) 240 first++; 241 242 entry = fbd_find(info); 243 if (entry != NULL) { 244 /* XXX Update framebuffer params */ 245 return (0); 246 } 247 248 entry = malloc(sizeof(struct fb_list_entry), M_DEVBUF, M_WAITOK|M_ZERO); 249 entry->fb_info = info; 250 251 LIST_INSERT_HEAD(&fb_list_head, entry, fb_list); 252 253 err = fb_init(entry, framebuffer_dev_unit++); 254 if (err) 255 return (err); 256 257 if (first) { 258 err = vt_fb_attach(info); 259 if (err) 260 return (err); 261 } 262 263 return (0); 264 } 265 266 int 267 fbd_unregister(struct fb_info* info) 268 { 269 struct fb_list_entry *entry, *tmp; 270 271 LIST_FOREACH_SAFE(entry, &fb_list_head, fb_list, tmp) { 272 if (entry->fb_info == info) { 273 LIST_REMOVE(entry, fb_list); 274 if (LIST_EMPTY(&fb_list_head)) 275 vt_fb_detach(info); 276 free(entry, M_DEVBUF); 277 return (0); 278 } 279 } 280 281 return (ENOENT); 282 } 283 284 static void 285 register_fb_wrap(void *arg, void *ptr) 286 { 287 288 fbd_register((struct fb_info *)ptr); 289 } 290 291 static void 292 unregister_fb_wrap(void *arg, void *ptr) 293 { 294 295 fbd_unregister((struct fb_info *)ptr); 296 } 297 298 static void 299 fbd_evh_init(void *ctx) 300 { 301 302 EVENTHANDLER_REGISTER(register_framebuffer, register_fb_wrap, NULL, 303 EVENTHANDLER_PRI_ANY); 304 EVENTHANDLER_REGISTER(unregister_framebuffer, unregister_fb_wrap, NULL, 305 EVENTHANDLER_PRI_ANY); 306 } 307 308 /* Newbus methods. */ 309 static int 310 fbd_probe(device_t dev) 311 { 312 313 return (BUS_PROBE_NOWILDCARD); 314 } 315 316 static int 317 fbd_attach(device_t dev) 318 { 319 struct fbd_softc *sc; 320 int err; 321 322 sc = device_get_softc(dev); 323 324 sc->sc_dev = dev; 325 sc->sc_info = FB_GETINFO(device_get_parent(dev)); 326 if (sc->sc_info == NULL) 327 return (ENXIO); 328 err = fbd_register(sc->sc_info); 329 330 return (err); 331 } 332 333 static int 334 fbd_detach(device_t dev) 335 { 336 struct fbd_softc *sc; 337 int err; 338 339 sc = device_get_softc(dev); 340 341 err = fbd_unregister(sc->sc_info); 342 343 return (err); 344 } 345 346 static device_method_t fbd_methods[] = { 347 /* Device interface */ 348 DEVMETHOD(device_probe, fbd_probe), 349 DEVMETHOD(device_attach, fbd_attach), 350 DEVMETHOD(device_detach, fbd_detach), 351 352 DEVMETHOD(device_shutdown, bus_generic_shutdown), 353 354 { 0, 0 } 355 }; 356 357 driver_t fbd_driver = { 358 "fbd", 359 fbd_methods, 360 sizeof(struct fbd_softc) 361 }; 362 363 devclass_t fbd_devclass; 364 365 DRIVER_MODULE(fbd, fb, fbd_driver, fbd_devclass, 0, 0); 366 DRIVER_MODULE(fbd, drmn, fbd_driver, fbd_devclass, 0, 0); 367 DRIVER_MODULE(fbd, udl, fbd_driver, fbd_devclass, 0, 0); 368 MODULE_VERSION(fbd, 1); 369 370