1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2013 The FreeBSD Foundation 5 * All rights reserved. 6 * 7 * This software was developed by Aleksandr Rybalko under sponsorship from the 8 * FreeBSD Foundation. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 * 31 * $FreeBSD$ 32 */ 33 34 /* Generic framebuffer */ 35 /* TODO unlink from VT(9) */ 36 /* TODO done normal /dev/fb methods */ 37 38 #include <sys/cdefs.h> 39 __FBSDID("$FreeBSD$"); 40 41 #include <sys/param.h> 42 #include <sys/systm.h> 43 #include <sys/bus.h> 44 #include <sys/conf.h> 45 #include <sys/eventhandler.h> 46 #include <sys/kernel.h> 47 #include <sys/malloc.h> 48 #include <sys/module.h> 49 #include <sys/queue.h> 50 #include <sys/fbio.h> 51 52 #include <machine/bus.h> 53 54 #include <dev/vt/vt.h> 55 #include <dev/vt/hw/fb/vt_fb.h> 56 57 #include <vm/vm.h> 58 #include <vm/pmap.h> 59 60 #include "fb_if.h" 61 62 LIST_HEAD(fb_list_head_t, fb_list_entry) fb_list_head = 63 LIST_HEAD_INITIALIZER(fb_list_head); 64 struct fb_list_entry { 65 struct fb_info *fb_info; 66 struct cdev *fb_si; 67 LIST_ENTRY(fb_list_entry) fb_list; 68 }; 69 70 struct fbd_softc { 71 device_t sc_dev; 72 struct fb_info *sc_info; 73 }; 74 75 static void fbd_evh_init(void *); 76 /* SI_ORDER_SECOND, just after EVENTHANDLERs initialized. */ 77 SYSINIT(fbd_evh_init, SI_SUB_CONFIGURE, SI_ORDER_SECOND, fbd_evh_init, NULL); 78 79 static d_open_t fb_open; 80 static d_close_t fb_close; 81 static d_read_t fb_read; 82 static d_write_t fb_write; 83 static d_ioctl_t fb_ioctl; 84 static d_mmap_t fb_mmap; 85 86 static struct cdevsw fb_cdevsw = { 87 .d_version = D_VERSION, 88 .d_flags = D_NEEDGIANT, 89 .d_open = fb_open, 90 .d_close = fb_close, 91 .d_read = fb_read, 92 .d_write = fb_write, 93 .d_ioctl = fb_ioctl, 94 .d_mmap = fb_mmap, 95 .d_name = "fb", 96 }; 97 98 static int framebuffer_dev_unit = 0; 99 100 static int 101 fb_open(struct cdev *dev, int oflags, int devtype, struct thread *td) 102 { 103 104 return (0); 105 } 106 107 static int 108 fb_close(struct cdev *dev, int fflag, int devtype, struct thread *td) 109 { 110 111 return (0); 112 } 113 114 static int 115 fb_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, 116 struct thread *td) 117 { 118 struct fb_info *info; 119 int error; 120 121 error = 0; 122 info = dev->si_drv1; 123 124 switch (cmd) { 125 case FBIOGTYPE: 126 bcopy(info, (struct fbtype *)data, sizeof(struct fbtype)); 127 break; 128 129 case FBIO_GETWINORG: /* get frame buffer window origin */ 130 *(u_int *)data = 0; 131 break; 132 133 case FBIO_GETDISPSTART: /* get display start address */ 134 ((video_display_start_t *)data)->x = 0; 135 ((video_display_start_t *)data)->y = 0; 136 break; 137 138 case FBIO_GETLINEWIDTH: /* get scan line width in bytes */ 139 *(u_int *)data = info->fb_stride; 140 break; 141 142 case FBIO_BLANK: /* blank display */ 143 if (info->setblankmode != NULL) 144 error = info->setblankmode(info->fb_priv, *(int *)data); 145 break; 146 147 default: 148 error = ENOIOCTL; 149 break; 150 } 151 return (error); 152 } 153 154 static int 155 fb_read(struct cdev *dev, struct uio *uio, int ioflag) 156 { 157 158 return (0); /* XXX nothing to read, yet */ 159 } 160 161 static int 162 fb_write(struct cdev *dev, struct uio *uio, int ioflag) 163 { 164 165 return (0); /* XXX nothing written */ 166 } 167 168 static int 169 fb_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, int nprot, 170 vm_memattr_t *memattr) 171 { 172 struct fb_info *info; 173 174 info = dev->si_drv1; 175 176 if (info->fb_flags & FB_FLAG_NOMMAP) 177 return (ENODEV); 178 179 if (offset < info->fb_size) { 180 if (info->fb_pbase == 0) 181 *paddr = vtophys((uint8_t *)info->fb_vbase + offset); 182 else 183 *paddr = info->fb_pbase + offset; 184 if (info->fb_flags & FB_FLAG_MEMATTR) 185 *memattr = info->fb_memattr; 186 return (0); 187 } 188 return (EINVAL); 189 } 190 191 static int 192 fb_init(struct fb_list_entry *entry, int unit) 193 { 194 struct fb_info *info; 195 196 info = entry->fb_info; 197 entry->fb_si = make_dev(&fb_cdevsw, unit, UID_ROOT, GID_WHEEL, 198 0600, "fb%d", unit); 199 entry->fb_si->si_drv1 = info; 200 info->fb_cdev = entry->fb_si; 201 202 return (0); 203 } 204 205 int 206 fbd_list() 207 { 208 struct fb_list_entry *entry; 209 210 if (LIST_EMPTY(&fb_list_head)) 211 return (ENOENT); 212 213 LIST_FOREACH(entry, &fb_list_head, fb_list) { 214 printf("FB %s @%p\n", entry->fb_info->fb_name, 215 (void *)entry->fb_info->fb_pbase); 216 } 217 218 return (0); 219 } 220 221 static struct fb_list_entry * 222 fbd_find(struct fb_info* info) 223 { 224 struct fb_list_entry *entry, *tmp; 225 226 LIST_FOREACH_SAFE(entry, &fb_list_head, fb_list, tmp) { 227 if (entry->fb_info == info) { 228 return (entry); 229 } 230 } 231 232 return (NULL); 233 } 234 235 int 236 fbd_register(struct fb_info* info) 237 { 238 struct fb_list_entry *entry; 239 int err, first; 240 241 first = 0; 242 if (LIST_EMPTY(&fb_list_head)) 243 first++; 244 245 entry = fbd_find(info); 246 if (entry != NULL) { 247 /* XXX Update framebuffer params */ 248 return (0); 249 } 250 251 entry = malloc(sizeof(struct fb_list_entry), M_DEVBUF, M_WAITOK|M_ZERO); 252 entry->fb_info = info; 253 254 LIST_INSERT_HEAD(&fb_list_head, entry, fb_list); 255 256 err = fb_init(entry, framebuffer_dev_unit++); 257 if (err) 258 return (err); 259 260 if (first) { 261 err = vt_fb_attach(info); 262 if (err) 263 return (err); 264 } 265 266 return (0); 267 } 268 269 int 270 fbd_unregister(struct fb_info* info) 271 { 272 struct fb_list_entry *entry, *tmp; 273 274 LIST_FOREACH_SAFE(entry, &fb_list_head, fb_list, tmp) { 275 if (entry->fb_info == info) { 276 LIST_REMOVE(entry, fb_list); 277 if (LIST_EMPTY(&fb_list_head)) 278 vt_fb_detach(info); 279 free(entry, M_DEVBUF); 280 return (0); 281 } 282 } 283 284 return (ENOENT); 285 } 286 287 static void 288 register_fb_wrap(void *arg, void *ptr) 289 { 290 291 fbd_register((struct fb_info *)ptr); 292 } 293 294 static void 295 unregister_fb_wrap(void *arg, void *ptr) 296 { 297 298 fbd_unregister((struct fb_info *)ptr); 299 } 300 301 static void 302 fbd_evh_init(void *ctx) 303 { 304 305 EVENTHANDLER_REGISTER(register_framebuffer, register_fb_wrap, NULL, 306 EVENTHANDLER_PRI_ANY); 307 EVENTHANDLER_REGISTER(unregister_framebuffer, unregister_fb_wrap, NULL, 308 EVENTHANDLER_PRI_ANY); 309 } 310 311 /* Newbus methods. */ 312 static int 313 fbd_probe(device_t dev) 314 { 315 316 return (BUS_PROBE_NOWILDCARD); 317 } 318 319 static int 320 fbd_attach(device_t dev) 321 { 322 struct fbd_softc *sc; 323 int err; 324 325 sc = device_get_softc(dev); 326 327 sc->sc_dev = dev; 328 sc->sc_info = FB_GETINFO(device_get_parent(dev)); 329 if (sc->sc_info == NULL) 330 return (ENXIO); 331 err = fbd_register(sc->sc_info); 332 333 return (err); 334 } 335 336 static int 337 fbd_detach(device_t dev) 338 { 339 struct fbd_softc *sc; 340 int err; 341 342 sc = device_get_softc(dev); 343 344 err = fbd_unregister(sc->sc_info); 345 346 return (err); 347 } 348 349 static device_method_t fbd_methods[] = { 350 /* Device interface */ 351 DEVMETHOD(device_probe, fbd_probe), 352 DEVMETHOD(device_attach, fbd_attach), 353 DEVMETHOD(device_detach, fbd_detach), 354 355 DEVMETHOD(device_shutdown, bus_generic_shutdown), 356 357 { 0, 0 } 358 }; 359 360 driver_t fbd_driver = { 361 "fbd", 362 fbd_methods, 363 sizeof(struct fbd_softc) 364 }; 365 366 devclass_t fbd_devclass; 367 368 DRIVER_MODULE(fbd, fb, fbd_driver, fbd_devclass, 0, 0); 369 DRIVER_MODULE(fbd, drmn, fbd_driver, fbd_devclass, 0, 0); 370 DRIVER_MODULE(fbd, udl, fbd_driver, fbd_devclass, 0, 0); 371 MODULE_VERSION(fbd, 1); 372 373