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 31 /* Generic framebuffer */ 32 /* TODO unlink from VT(9) */ 33 /* TODO done normal /dev/fb methods */ 34 35 #include <sys/param.h> 36 #include <sys/systm.h> 37 #include <sys/bus.h> 38 #include <sys/conf.h> 39 #include <sys/eventhandler.h> 40 #include <sys/kernel.h> 41 #include <sys/malloc.h> 42 #include <sys/module.h> 43 #include <sys/queue.h> 44 #include <sys/fbio.h> 45 46 #include <machine/bus.h> 47 48 #include <dev/vt/vt.h> 49 #include <dev/vt/hw/fb/vt_fb.h> 50 51 #include <vm/vm.h> 52 #include <vm/pmap.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 if (info->setblankmode != NULL) 138 error = info->setblankmode(info->fb_priv, *(int *)data); 139 break; 140 141 default: 142 error = ENOIOCTL; 143 break; 144 } 145 return (error); 146 } 147 148 static int 149 fb_read(struct cdev *dev, struct uio *uio, int ioflag) 150 { 151 152 return (0); /* XXX nothing to read, yet */ 153 } 154 155 static int 156 fb_write(struct cdev *dev, struct uio *uio, int ioflag) 157 { 158 159 return (0); /* XXX nothing written */ 160 } 161 162 static int 163 fb_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, int nprot, 164 vm_memattr_t *memattr) 165 { 166 struct fb_info *info; 167 168 info = dev->si_drv1; 169 170 if (info->fb_flags & FB_FLAG_NOMMAP) 171 return (ENODEV); 172 173 if (offset < info->fb_size) { 174 if (info->fb_pbase == 0) 175 *paddr = vtophys((uint8_t *)info->fb_vbase + offset); 176 else 177 *paddr = info->fb_pbase + offset; 178 if (info->fb_flags & FB_FLAG_MEMATTR) 179 *memattr = info->fb_memattr; 180 return (0); 181 } 182 return (EINVAL); 183 } 184 185 static int 186 fb_init(struct fb_list_entry *entry, int unit) 187 { 188 struct fb_info *info; 189 190 info = entry->fb_info; 191 entry->fb_si = make_dev(&fb_cdevsw, unit, UID_ROOT, GID_WHEEL, 192 0600, "fb%d", unit); 193 entry->fb_si->si_drv1 = info; 194 info->fb_cdev = entry->fb_si; 195 196 return (0); 197 } 198 199 int 200 fbd_list(void) 201 { 202 struct fb_list_entry *entry; 203 204 if (LIST_EMPTY(&fb_list_head)) 205 return (ENOENT); 206 207 LIST_FOREACH(entry, &fb_list_head, fb_list) { 208 printf("FB %s @%#jx\n", entry->fb_info->fb_name, 209 (uintmax_t)entry->fb_info->fb_pbase); 210 } 211 212 return (0); 213 } 214 215 static struct fb_list_entry * 216 fbd_find(struct fb_info* info) 217 { 218 struct fb_list_entry *entry, *tmp; 219 220 LIST_FOREACH_SAFE(entry, &fb_list_head, fb_list, tmp) { 221 if (entry->fb_info == info) { 222 return (entry); 223 } 224 } 225 226 return (NULL); 227 } 228 229 int 230 fbd_register(struct fb_info* info) 231 { 232 struct fb_list_entry *entry; 233 int err, first; 234 235 first = 0; 236 if (LIST_EMPTY(&fb_list_head)) 237 first++; 238 239 entry = fbd_find(info); 240 if (entry != NULL) { 241 /* XXX Update framebuffer params */ 242 return (0); 243 } 244 245 entry = malloc(sizeof(struct fb_list_entry), M_DEVBUF, M_WAITOK|M_ZERO); 246 entry->fb_info = info; 247 248 LIST_INSERT_HEAD(&fb_list_head, entry, fb_list); 249 250 err = fb_init(entry, framebuffer_dev_unit++); 251 if (err) 252 return (err); 253 254 if (first) { 255 err = vt_fb_attach(info); 256 if (err) 257 return (err); 258 } 259 260 return (0); 261 } 262 263 int 264 fbd_unregister(struct fb_info* info) 265 { 266 struct fb_list_entry *entry, *tmp; 267 268 LIST_FOREACH_SAFE(entry, &fb_list_head, fb_list, tmp) { 269 if (entry->fb_info == info) { 270 LIST_REMOVE(entry, fb_list); 271 if (LIST_EMPTY(&fb_list_head)) 272 vt_fb_detach(info); 273 free(entry, M_DEVBUF); 274 return (0); 275 } 276 } 277 278 return (ENOENT); 279 } 280 281 static void 282 register_fb_wrap(void *arg, void *ptr) 283 { 284 285 fbd_register((struct fb_info *)ptr); 286 } 287 288 static void 289 unregister_fb_wrap(void *arg, void *ptr) 290 { 291 292 fbd_unregister((struct fb_info *)ptr); 293 } 294 295 static void 296 fbd_evh_init(void *ctx) 297 { 298 299 EVENTHANDLER_REGISTER(register_framebuffer, register_fb_wrap, NULL, 300 EVENTHANDLER_PRI_ANY); 301 EVENTHANDLER_REGISTER(unregister_framebuffer, unregister_fb_wrap, NULL, 302 EVENTHANDLER_PRI_ANY); 303 } 304 305 /* Newbus methods. */ 306 static int 307 fbd_probe(device_t dev) 308 { 309 310 return (BUS_PROBE_NOWILDCARD); 311 } 312 313 static int 314 fbd_attach(device_t dev) 315 { 316 struct fbd_softc *sc; 317 int err; 318 319 sc = device_get_softc(dev); 320 321 sc->sc_dev = dev; 322 sc->sc_info = FB_GETINFO(device_get_parent(dev)); 323 if (sc->sc_info == NULL) 324 return (ENXIO); 325 err = fbd_register(sc->sc_info); 326 327 return (err); 328 } 329 330 static int 331 fbd_detach(device_t dev) 332 { 333 struct fbd_softc *sc; 334 int err; 335 336 sc = device_get_softc(dev); 337 338 err = fbd_unregister(sc->sc_info); 339 340 return (err); 341 } 342 343 static device_method_t fbd_methods[] = { 344 /* Device interface */ 345 DEVMETHOD(device_probe, fbd_probe), 346 DEVMETHOD(device_attach, fbd_attach), 347 DEVMETHOD(device_detach, fbd_detach), 348 349 DEVMETHOD(device_shutdown, bus_generic_shutdown), 350 351 { 0, 0 } 352 }; 353 354 driver_t fbd_driver = { 355 "fbd", 356 fbd_methods, 357 sizeof(struct fbd_softc) 358 }; 359 360 DRIVER_MODULE(fbd, fb, fbd_driver, 0, 0); 361 DRIVER_MODULE(fbd, drmn, fbd_driver, 0, 0); 362 DRIVER_MODULE(fbd, udl, fbd_driver, 0, 0); 363 MODULE_VERSION(fbd, 1); 364 365