1/**************************************************************************** 2 * video/fb.c 3 * Framebuffer character driver 4 * 5 * Copyright (C) 2017 Gregory Nutt. All rights reserved. 6 * Author: Gregory Nutt <gnutt@nuttx.org> 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 * 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 16 * the documentation and/or other materials provided with the 17 * distribution. 18 * 3. Neither the name NuttX nor the names of its contributors may be 19 * used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 25 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 26 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 29 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 30 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 32 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33 * POSSIBILITY OF SUCH DAMAGE. 34 * 35 ****************************************************************************/ 36 37/**************************************************************************** 38 * Included Files 39 ****************************************************************************/ 40 41#include "fb.h" 42 43#include "errno.h" 44#include "string.h" 45 46#define gerr PRINT_ERR 47#define DEBUGASSERT LOS_ASSERT 48 49/**************************************************************************** 50 * Private Types 51 ****************************************************************************/ 52 53/* This structure defines one framebuffer device. Note that which is 54 * everything in this structure is constant data set up and initialization 55 * time. Therefore, no there is requirement for serialized access to this 56 * structure. 57 */ 58 59struct fb_chardev_s { 60 struct fb_vtable_s *vtable; /* Framebuffer interface */ 61 void *fbmem; /* Start of frame buffer memory */ 62 size_t fblen; /* Size of the framebuffer */ 63 uint8_t plane; /* Video plan number */ 64 uint8_t bpp; /* Bits per pixel */ 65}; 66 67#define FB_DEV_MAXNUM 32 68static struct fb_chardev_s *g_fb_dev[FB_DEV_MAXNUM] = {NULL}; 69 70/**************************************************************************** 71 * Public Functions 72 ****************************************************************************/ 73int fb_open(const char *key, struct fb_mem **result) 74{ 75 struct fb_mem *fbmem = NULL; 76 struct fb_chardev_s *fb; 77 struct fb_vtable_s *vtable; 78 int ret = -EINVAL; 79 80 if (key == NULL || strlen(key) >= PATH_MAX) { 81 return -EINVAL; 82 } 83 FbMemHold(); 84 ret = FbMemLookup(key, &fbmem, 0); 85 FbMemDrop(); 86 if (ret == 0) { 87 fb = (struct fb_chardev_s *)fbmem->data; 88 if (fb == NULL) { 89 return -ENODEV; 90 } 91 92 vtable = fb->vtable; 93 if (vtable == NULL) { 94 return -EINVAL; 95 } 96 97 if (vtable->fb_open) { 98 ret = vtable->fb_open(vtable); 99 if (ret == 0) { 100 *result = fbmem; 101 } 102 } 103 } 104 return ret; 105} 106 107int fb_close(struct fb_mem *fbmem) 108{ 109 struct fb_chardev_s *fb; 110 struct fb_vtable_s *vtable; 111 int ret = -EINVAL; 112 113 fb = (struct fb_chardev_s *)fbmem->data; 114 if (fb == NULL) { 115 return -ENODEV; 116 } 117 118 vtable = fb->vtable; 119 if (vtable == NULL) { 120 return -EINVAL; 121 } 122 123 if (vtable->fb_release) { 124 ret = vtable->fb_release(vtable); 125 } 126 return ret; 127} 128 129int fb_ioctl(struct fb_mem *fbMem, int cmd, unsigned long arg) 130{ 131 struct fb_chardev_s *fb = NULL; 132 int ret; 133 134 /* Get the framebuffer instance */ 135 fb = (struct fb_chardev_s *)fbMem->data; 136 /* Process the IOCTL command */ 137 138 switch (cmd) { 139 case FIOC_MMAP: 140 { /* Get color plane info */ 141 void **ppv = (void **)((uintptr_t)arg); 142 /* Return the address corresponding to the start of frame buffer. */ 143 DEBUGASSERT(ppv != NULL); 144 *ppv = fb->fbmem; 145 ret = OK; 146 } 147 break; 148 149 case FBIOGET_VIDEOINFO: 150 { /* Get color plane info */ 151 struct fb_videoinfo_s *vinfo = (struct fb_videoinfo_s *)((uintptr_t)arg); 152 DEBUGASSERT(fb->vtable != NULL && fb->vtable->getvideoinfo != NULL); 153 ret = fb->vtable->getvideoinfo(fb->vtable, &vinfo); 154 } 155 break; 156 157 case FBIOGET_PLANEINFO: 158 { /* Get video plane info */ 159 struct fb_planeinfo_s *pinfo = (struct fb_planeinfo_s *)((uintptr_t)arg); 160 DEBUGASSERT(fb->vtable != NULL && fb->vtable->getplaneinfo != NULL); 161 ret = fb->vtable->getplaneinfo(fb->vtable, fb->plane, &pinfo); 162 } 163 break; 164 165#ifdef CONFIG_FB_CMAP 166 case FBIOGET_CMAP: 167 { /* Get RGB color mapping */ 168 struct fb_cmap_s *cmap = (struct fb_cmap_s *)((uintptr_t)arg); 169 DEBUGASSERT(fb->vtable != NULL && fb->vtable->getcmap != NULL); 170 ret = fb->vtable->getcmap(fb->vtable, &cmap); 171 } 172 break; 173 174 case FBIOPUT_CMAP: 175 { /* Put RGB color mapping */ 176 struct fb_cmap_s *cmap = (struct fb_cmap_s *)((uintptr_t)arg); 177 DEBUGASSERT(fb->vtable != NULL && fb->vtable->putcmap != NULL); 178 ret = fb->vtable->putcmap(fb->vtable, &cmap); 179 } 180 break; 181#endif 182#ifdef CONFIG_FB_HWCURSOR 183 case FBIOGET_CURSOR: 184 { /* Get cursor attributes */ 185 struct fb_cursorattrib_s *attrib = (struct fb_cursorattrib_s *)((uintptr_t)arg); 186 DEBUGASSERT(fb->vtable != NULL && fb->vtable->getcursor != NULL); 187 ret = fb->vtable->getcursor(fb->vtable, &attrib); 188 } 189 break; 190 191 case FBIOPUT_CURSOR: 192 { /* Set cursor attributes */ 193 struct fb_setcursor_s *cursor = (struct fb_setcursor_s *)((uintptr_t)arg); 194 DEBUGASSERT(fb->vtable != NULL && fb->vtable->setcursor != NULL); 195 ret = fb->vtable->setcursor(fb->vtable, &cursor); 196 } 197 break; 198#endif 199 200#ifdef CONFIG_FB_UPDATE 201 case FBIO_UPDATE: 202 { /* Update the modified framebuffer data */ 203 struct fb_area_s *area = (struct fb_area_s *)((uintptr_t)arg); 204 DEBUGASSERT(fb->vtable != NULL && fb->vtable->updatearea != NULL); 205 ret = fb->vtable->updatearea(fb->vtable, area); 206 } 207 break; 208#endif 209 210#ifdef CONFIG_FB_SYNC 211 case FBIO_WAITFORVSYNC: 212 { /* Wait upon vertical sync */ 213 ret = fb->vtable->waitforvsync(fb->vtable); 214 } 215 break; 216#endif 217 218#ifdef CONFIG_FB_OVERLAY 219 case FBIO_SELECT_OVERLAY: 220 { /* Select video overlay */ 221 struct fb_overlayinfo_s oinfo; 222 DEBUGASSERT(fb->vtable != NULL && fb->vtable->getoverlayinfo != NULL); 223 ret = fb->vtable->getoverlayinfo(fb->vtable, arg, &oinfo); 224 if (ret == OK) { 225 fb->fbmem = oinfo.fbmem; 226 fb->fblen = oinfo.fblen; 227 fb->bpp = oinfo.bpp; 228 } 229 } 230 break; 231 232 case FBIOGET_OVERLAYINFO: 233 { /* Get video overlay info */ 234 struct fb_overlayinfo_s *oinfo = (struct fb_overlayinfo_s *)((uintptr_t)arg); 235 DEBUGASSERT(fb->vtable != NULL && fb->vtable->getoverlayinfo != NULL); 236 ret = fb->vtable->getoverlayinfo(fb->vtable, oinfo->overlay, &oinfo); 237 } 238 break; 239 240 case FBIOSET_TRANSP: 241 { /* Set video overlay transparency */ 242 struct fb_overlayinfo_s *oinfo = (struct fb_overlayinfo_s *)((uintptr_t)arg); 243 DEBUGASSERT(fb->vtable != NULL && fb->vtable->settransp != NULL); 244 ret = fb->vtable->settransp(fb->vtable, &oinfo); 245 } 246 break; 247 248 case FBIOSET_CHROMAKEY: 249 { /* Set video overlay chroma key */ 250 struct fb_overlayinfo_s *oinfo = (struct fb_overlayinfo_s *)((uintptr_t)arg); 251 DEBUGASSERT(fb->vtable != NULL && fb->vtable->setchromakey != NULL); 252 ret = fb->vtable->setchromakey(fb->vtable, &oinfo); 253 } 254 break; 255 256 case FBIOSET_COLOR: 257 { /* Set video overlay color */ 258 struct fb_overlayinfo_s *oinfo = (struct fb_overlayinfo_s *)((uintptr_t)arg); 259 DEBUGASSERT(fb->vtable != NULL && fb->vtable->setcolor != NULL); 260 ret = fb->vtable->setcolor(fb->vtable, &oinfo); 261 } 262 break; 263 264 case FBIOSET_BLANK: 265 { /* Blank or unblank video overlay */ 266 struct fb_overlayinfo_s *oinfo = (struct fb_overlayinfo_s *)((uintptr_t)arg); 267 DEBUGASSERT(fb->vtable != NULL && fb->vtable->setblank != NULL); 268 ret = fb->vtable->setblank(fb->vtable, &oinfo); 269 } 270 break; 271 272 case FBIOSET_AREA: 273 { /* Set active video overlay area */ 274 struct fb_overlayinfo_s *oinfo = (struct fb_overlayinfo_s *)((uintptr_t)arg); 275 DEBUGASSERT(fb->vtable != NULL && fb->vtable->setarea != NULL); 276 ret = fb->vtable->setarea(fb->vtable, &oinfo); 277 } 278 break; 279 280#ifdef CONFIG_FB_OVERLAY_BLIT 281 case FBIOSET_BLIT: 282 { /* Blit operation between video overlays */ 283 struct fb_overlayblit_s *blit = (struct fb_overlayblit_s *)((uintptr_t)arg); 284 DEBUGASSERT(fb->vtable != NULL && fb->vtable->blit != NULL); 285 ret = fb->vtable->blit(fb->vtable, &blit); 286 } 287 break; 288 289 case FBIOSET_BLEND: 290 { /* Blend operation between video overlays */ 291 struct fb_overlayblend_s *blend = (struct fb_overlayblend_s *)((uintptr_t)arg); 292 DEBUGASSERT(fb->vtable != NULL && fb->vtable->blend != NULL); 293 ret = fb->vtable->blend(fb->vtable, &blend); 294 } 295 break; 296#endif 297#endif /* CONFIG_FB_OVERLAY */ 298 299 default: 300 DEBUGASSERT(fb->vtable != NULL && fb->vtable->fb_ioctl != NULL); 301 ret = fb->vtable->fb_ioctl(fb->vtable, cmd, arg); 302 break; 303 } 304 305 return ret; 306} 307 308int getplaneinfo(struct fb_mem *fbmem, struct fb_planeinfo_s **result) 309{ 310 int ret; 311 struct fb_chardev_s *fb; 312 313 fb = (struct fb_chardev_s *)fbmem->data; 314 315 struct fb_planeinfo_s pinfo; 316 317 ret = fb->vtable->getplaneinfo(fb->vtable, fb->plane, &pinfo); 318 if (ret == 0) { 319 *result = &pinfo; 320 } 321 322 return 0; 323} 324 325/**************************************************************************** 326 * Name: fb_register 327 * 328 * Description: 329 * Register the framebuffer character device at /dev/fbN where N is the 330 * display number if the devices supports only a single plane. If the 331 * hardware supports multiple color planes, then the device will be 332 * registered at /dev/fbN.M where N is the again display number but M 333 * is the display plane. 334 * 335 * Input Parameters: 336 * display - The display number for the case of boards supporting multiple 337 * displays or for hardware that supports multiple 338 * layers (each layer is consider a display). Typically zero. 339 * plane - Identifies the color plane on hardware that supports separate 340 * framebuffer "planes" for each color component. 341 * 342 * Returned Value: 343 * Zero (OK) is returned success; a negated errno value is returned on any 344 * failure. 345 * 346 ****************************************************************************/ 347 348int fb_register(int display, int plane) 349{ 350 struct fb_chardev_s *fb = NULL; 351 struct fb_videoinfo_s vinfo; 352 struct fb_planeinfo_s pinfo; 353#ifdef CONFIG_FB_OVERLAY 354 struct fb_overlayinfo_s oinfo; 355#endif 356 char devname[16]; 357 int nplanes; 358 int ret; 359 360 if (display < 0 || display >= FB_DEV_MAXNUM) return -EINVAL; 361 362 /* Allocate a framebuffer state instance */ 363 fb = (struct fb_chardev_s *)malloc(sizeof(struct fb_chardev_s)); 364 if (fb == NULL) { 365 return -ENOMEM; 366 } 367 368 /* Initialize the frame buffer device. */ 369 ret = up_fbinitialize(display); 370 if (ret < 0) { 371 gerr("ERROR: up_fbinitialize() failed for display %d: %d\n", display, ret); 372 goto errout_with_fb; 373 } 374 375 DEBUGASSERT((unsigned)plane <= UINT8_MAX); 376 fb->plane = plane; 377 378 fb->vtable = up_fbgetvplane(display, plane); 379 if (fb->vtable == NULL) { 380 gerr("ERROR: up_fbgetvplane() failed, vplane=%d\n", plane); 381 goto errout_with_fb; 382 } 383 384 /* Initialize the frame buffer instance. */ 385 DEBUGASSERT(fb->vtable->getvideoinfo != NULL); 386 ret = fb->vtable->getvideoinfo(fb->vtable, &vinfo); 387 if (ret < 0) { 388 gerr("ERROR: getvideoinfo() failed: %d\n", ret); 389 goto errout_with_fb; 390 } 391 392 nplanes = vinfo.nplanes; 393 DEBUGASSERT(vinfo.nplanes > 0 && (unsigned)plane < vinfo.nplanes); 394 395 DEBUGASSERT(fb->vtable->getplaneinfo != NULL); 396 ret = fb->vtable->getplaneinfo(fb->vtable, plane, &pinfo); 397 if (ret < 0) { 398 gerr("ERROR: getplaneinfo() failed: %d\n", ret); 399 goto errout_with_fb; 400 } 401 402 fb->fbmem = pinfo.fbmem; 403 fb->fblen = pinfo.fblen; 404 fb->bpp = pinfo.bpp; 405 406 /* Clear the framebuffer memory */ 407 memset(pinfo.fbmem, 0, pinfo.fblen); 408 409#ifdef CONFIG_FB_OVERLAY 410 /* Initialize first overlay but do not select */ 411 DEBUGASSERT(fb->vtable->getoverlayinfo != NULL); 412 ret = fb->vtable->getoverlayinfo(fb->vtable, 0, &oinfo); 413 if (ret < 0) { 414 gerr("ERROR: getoverlayinfo() failed: %d\n", ret); 415 goto errout_with_fb; 416 } 417 418 /* Clear the overlay memory. Necessary when plane 0 and overlay 0 419 * different. 420 */ 421 422 memset(oinfo.fbmem, 0, oinfo.fblen); 423#endif 424 425 /* Register the framebuffer device */ 426 if (nplanes < 2) { 427 (void)sprintf_s(devname, 16, "/dev/fb%d", display); 428 } else { 429 (void)sprintf_s(devname, 16, "/dev/fb%d.%d", display, plane); 430 } 431 432 ret = register_driver(devname, (void *)fb); 433 if (ret < 0) { 434 gerr("ERROR: register_driver() failed: %d\n", ret); 435 goto errout_with_fb; 436 } 437 438 g_fb_dev[display] = fb; 439 440 return OK; 441 442errout_with_fb: 443 free(fb); 444 return ret; 445} 446 447int fb_unregister(int display) 448{ 449 struct fb_chardev_s *fb = NULL; 450 char devname[16]; 451 452 if (display < 0 || display >= FB_DEV_MAXNUM) return -EINVAL; 453 454 (void)sprintf_s(devname, 16, "/dev/fb%d", display); 455 unregister_driver(devname); 456 457 up_fbuninitialize(display); 458 459 fb = g_fb_dev[display]; 460 free(fb); 461 g_fb_dev[display] = NULL; 462 463 return 0; 464}