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