xref: /third_party/NuttX/drivers/video/fb.c (revision beacf11b)
1/****************************************************************************
2 * drivers/video/fb.c
3 *
4 * Licensed to the Apache Software Foundation (ASF) under one or more
5 * contributor license agreements.  See the NOTICE file distributed with
6 * this work for additional information regarding copyright ownership.  The
7 * ASF licenses this file to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance with the
9 * License.  You may obtain a copy of the License at
10 *
11 *   http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
16 * License for the specific language governing permissions and limitations
17 * under the License.
18 *
19 ****************************************************************************/
20
21/* Framebuffer character driver */
22
23/****************************************************************************
24 * Included Files
25 ****************************************************************************/
26
27#include "stdio.h"
28#include "stdlib.h"
29#include "string.h"
30#include "fb.h"
31#include "fs/driver.h"
32#include "assert.h"
33#include "errno.h"
34#include "user_copy.h"
35
36#define gerr PRINT_ERR
37
38/****************************************************************************
39 * Private Types
40 ****************************************************************************/
41
42/* This structure defines one framebuffer device.  Note that which is
43 * everything in this structure is constant data set up and initialization
44 * time.  Therefore, no there is requirement for serialized access to this
45 * structure.
46 */
47
48struct fb_chardev_s
49{
50  struct fb_vtable_s *vtable; /* Framebuffer interface */
51  void *fbmem;                /* Start of frame buffer memory */
52  size_t fblen;                   /* Size of the framebuffer */
53  uint8_t plane;                  /* Video plan number */
54  uint8_t bpp;                    /* Bits per pixel */
55};
56
57#define FB_DEV_MAXNUM   32
58static struct fb_chardev_s *g_fb_dev[FB_DEV_MAXNUM] = {NULL};
59
60/****************************************************************************
61 * Private Function Prototypes
62 ****************************************************************************/
63
64static int     fb_open(struct file *filep);
65static int     fb_close(struct file *filep);
66static ssize_t fb_read(struct file *filep, char *buffer, size_t buflen);
67static ssize_t fb_write(struct file *filep, const char *buffer, size_t buflen);
68static off_t   fb_seek(struct file *filep, off_t offset, int whence);
69static int     fb_ioctl(struct file *filep, int cmd, unsigned long arg);
70static ssize_t fb_mmap(struct file* filep, LosVmMapRegion *region);
71
72/****************************************************************************
73 * Private Data
74 ****************************************************************************/
75
76static const struct file_operations_vfs fb_fops =
77{
78  fb_open,       /* open */
79  fb_close,      /* close */
80  fb_read,       /* read */
81  fb_write,      /* write */
82  fb_seek,       /* seek */
83  fb_ioctl,      /* ioctl */
84  fb_mmap,        /* mmap */
85#ifndef CONFIG_DISABLE_POLL
86  NULL,          /* poll */
87#endif
88#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
89  NULL,          /* unlink */
90#endif
91};
92
93/****************************************************************************
94 * Private Functions
95 ****************************************************************************/
96
97static ssize_t fb_mmap(struct file *filep, LosVmMapRegion *region)
98{
99  int ret = -EINVAL;
100  struct fb_chardev_s *fb;
101  struct fb_vtable_s *vtable;
102  struct drv_data *drvData;
103
104  drvData = (struct drv_data *)filep->f_vnode->data;
105  fb = (struct fb_chardev_s *)drvData->priv;
106  if (fb == NULL)
107    {
108      return -ENODEV;
109    }
110
111  vtable = fb->vtable;
112  if (vtable == NULL)
113    {
114      return -EINVAL;
115    }
116
117  if (vtable->fb_mmap != NULL)
118    {
119      ret = vtable->fb_mmap(vtable, region);
120    }
121
122  return ret;
123}
124
125/****************************************************************************
126 * Name: fb_open
127 *
128 * Description:
129 *   This function is called whenever the framebuffer device is opened.
130 *
131 ****************************************************************************/
132
133static int fb_open(struct file *filep)
134{
135  DEBUGASSERT(filep != NULL && filep->f_vnode != NULL);
136  struct fb_chardev_s *fb;
137  struct fb_vtable_s *vtable;
138  int ret = -EINVAL;
139  struct drv_data *drvData;
140
141  drvData = (struct drv_data *)filep->f_vnode->data;
142  fb = (struct fb_chardev_s *)drvData->priv;
143  if (fb == NULL)
144    {
145      return -ENODEV;
146    }
147
148  vtable = fb->vtable;
149  if (vtable == NULL)
150    {
151      return -EINVAL;
152    }
153
154  if (vtable->fb_open)
155    {
156      ret = vtable->fb_open(vtable);
157    }
158
159  return ret;
160}
161
162/****************************************************************************
163 * Name: fb_close
164 *
165 * Description:
166 *   This function is called when the framebuffer device is closed.
167 *
168 ****************************************************************************/
169
170static int fb_close(struct file *filep)
171{
172  DEBUGASSERT(filep != NULL && filep->f_vnode != NULL);
173  struct fb_chardev_s *fb;
174  struct fb_vtable_s *vtable;
175  int ret = -EINVAL;
176  struct drv_data *drvData;
177
178  drvData = (struct drv_data *)filep->f_vnode->data;
179  fb = (struct fb_chardev_s *)drvData->priv;
180  if (fb == NULL)
181    {
182      return -ENODEV;
183    }
184
185  vtable = fb->vtable;
186  if (vtable == NULL)
187    {
188      return -EINVAL;
189    }
190
191  if (vtable->fb_release)
192    {
193      ret = vtable->fb_release(vtable);
194    }
195
196  return ret;
197}
198
199/****************************************************************************
200 * Name: fb_read
201 ****************************************************************************/
202
203static ssize_t fb_read(struct file *filep, char *buffer, size_t len)
204{
205  struct fb_chardev_s *fb = NULL;
206  size_t start;
207  size_t end;
208  size_t size;
209  int ret;
210  struct drv_data *drvData;
211
212  /* Get the framebuffer instance */
213
214  DEBUGASSERT(filep != NULL && filep->f_vnode != NULL);
215  drvData = (struct drv_data *)filep->f_vnode->data;
216  fb = (struct fb_chardev_s *)drvData->priv;
217  /* Get the start and size of the transfer */
218
219  start = filep->f_pos;
220  if (start >= fb->fblen)
221    {
222      return 0;  /* Return end-of-file */
223    }
224
225  end = start + len;
226  if (end >= fb->fblen)
227    {
228      end = fb->fblen;
229    }
230
231  size = end - start;
232
233  /* And transfer the data from the frame buffer */
234
235  ret = LOS_ArchCopyToUser(buffer, fb->fbmem, size);
236  if (ret)
237    {
238      return -EFAULT;
239    }
240  filep->f_pos += size;
241  return size;
242}
243
244/****************************************************************************
245 * Name: fb_write
246 ****************************************************************************/
247
248static ssize_t fb_write(struct file *filep, const char *buffer,
249                        size_t len)
250{
251  struct fb_chardev_s *fb = NULL;
252  size_t start;
253  size_t end;
254  size_t size;
255  int ret;
256  struct drv_data *drvData;
257
258
259  /* Get the framebuffer instance */
260
261  DEBUGASSERT(filep != NULL && filep->f_vnode != NULL);
262  drvData = (struct drv_data *)filep->f_vnode->data;
263  fb = (struct fb_chardev_s *)drvData->priv;
264  /* Get the start and size of the transfer */
265
266  start = filep->f_pos;
267  if (start >= fb->fblen)
268    {
269      return -EFBIG;  /* Cannot extend the framebuffer */
270    }
271
272  end = start + len;
273  if (end >= fb->fblen)
274    {
275      end = fb->fblen;
276    }
277
278  size = end - start;
279
280  /* And transfer the data into the frame buffer */
281
282  ret = LOS_ArchCopyFromUser(fb->fbmem, buffer, size);
283  if (ret)
284    {
285      return -EFAULT;
286    }
287  filep->f_pos += size;
288  return size;
289}
290
291/****************************************************************************
292 * Name: fb_seek
293 *
294 * Description:
295 *   Seek the logical file pointer to the specified position.  The offset
296 *   is in units of pixels, with offset zero being the beginning of the
297 *   framebuffer.
298 *
299 ****************************************************************************/
300
301static off_t fb_seek(struct file *filep, off_t offset, int whence)
302{
303  struct fb_chardev_s *fb = NULL;
304  off_t newpos;
305  int ret;
306  struct drv_data *drvData;
307
308  /* Get the framebuffer instance */
309
310  DEBUGASSERT(filep != NULL && filep->f_vnode != NULL);
311  drvData = (struct drv_data *)filep->f_vnode->data;
312  fb = (struct fb_chardev_s *)drvData->priv;
313  /* Determine the new, requested file position */
314
315  switch (whence)
316    {
317    case SEEK_CUR:
318      newpos = filep->f_pos + offset;
319      break;
320
321    case SEEK_SET:
322      newpos = offset;
323      break;
324
325    case SEEK_END:
326      newpos = fb->fblen + offset;
327      break;
328
329    default:
330      /* Return EINVAL if the whence argument is invalid */
331
332      return -EINVAL;
333    }
334
335  /* Opengroup.org:
336   *
337   *  "The lseek() function shall allow the file offset to be set beyond the end
338   *   of the existing data in the file. If data is later written at this point,
339   *   subsequent reads of data in the gap shall return bytes with the value 0
340   *   until data is actually written into the gap."
341   *
342   * We can conform to the first part, but not the second.  But return EINVAL if
343   *
344   *  "...the resulting file offset would be negative for a regular file, block
345   *   special file, or directory."
346   */
347
348  if (newpos >= 0)
349    {
350      filep->f_pos = newpos;
351      ret = newpos;
352    }
353  else
354    {
355      ret = -EINVAL;
356    }
357
358  return ret;
359}
360
361/****************************************************************************
362 * Name: fb_ioctl
363 *
364 * Description:
365 *   The standard ioctl method.
366 *
367 ****************************************************************************/
368
369static int fb_ioctl(struct file *filep, int cmd, unsigned long arg)
370{
371  struct fb_chardev_s *fb = NULL;
372  int ret;
373  struct drv_data *drvData;
374
375  /* Get the framebuffer instance */
376
377  DEBUGASSERT(filep != NULL && filep->f_vnode != NULL);
378  drvData = (struct drv_data *)filep->f_vnode->data;
379  fb = (struct fb_chardev_s *)drvData->priv;
380  /* Process the IOCTL command */
381
382  switch (cmd)
383    {
384      case FIOC_MMAP:  /* Get color plane info */
385        {
386          void **ppv = (void **)((uintptr_t)arg);
387          uintptr_t fbmem = (uintptr_t)fb->fbmem;
388
389          /* Return the address corresponding to the start of frame buffer. */
390
391          ret = LOS_ArchCopyToUser(ppv, &fbmem, sizeof(uintptr_t));
392          if (ret)
393            {
394              ret = -EFAULT;
395            }
396        }
397        break;
398
399      case FBIOGET_VIDEOINFO:  /* Get color plane info */
400        {
401          struct fb_videoinfo_s vinfo = { 0 };
402
403          DEBUGASSERT(fb->vtable != NULL &&
404                      fb->vtable->getvideoinfo != NULL);
405          ret = fb->vtable->getvideoinfo(fb->vtable, &vinfo);
406          if (ret == ENOERR)
407            {
408              ret = LOS_ArchCopyToUser((void *)arg, &vinfo, sizeof(struct fb_videoinfo_s));
409              if (ret)
410                {
411                  ret = -EFAULT;
412                }
413            }
414        }
415        break;
416
417      case FBIOGET_PLANEINFO:  /* Get video plane info */
418        {
419          struct fb_planeinfo_s pinfo = { 0 };
420
421          DEBUGASSERT(fb->vtable != NULL &&
422                      fb->vtable->getplaneinfo != NULL);
423          ret = fb->vtable->getplaneinfo(fb->vtable, fb->plane, &pinfo);
424          if (ret == ENOERR)
425            {
426              ret = LOS_ArchCopyToUser((void *)arg, &pinfo, sizeof(struct fb_planeinfo_s));
427              if (ret)
428                {
429                  ret = -EFAULT;
430                }
431            }
432        }
433        break;
434
435#ifdef CONFIG_FB_CMAP
436      case FBIOGET_CMAP:       /* Get RGB color mapping */
437        {
438          struct fb_cmap_s cmap = { 0 };
439
440          DEBUGASSERT(fb->vtable != NULL &&
441                      fb->vtable->getcmap != NULL);
442          ret = fb->vtable->getcmap(fb->vtable, &cmap);
443          if (ret == ENOERR)
444            {
445              ret = LOS_ArchCopyToUser((void *)arg, &cmap, sizeof(struct fb_cmap_s));
446              if (ret)
447                {
448                  ret = -EFAULT;
449                }
450            }
451        }
452        break;
453
454      case FBIOPUT_CMAP:       /* Put RGB color mapping */
455        {
456          struct fb_cmap_s cmap;
457
458          DEBUGASSERT(fb->vtable != NULL &&
459                      fb->vtable->putcmap != NULL);
460          ret = LOS_ArchCopyFromUser(&cmap, (const void *)arg, sizeof(struct fb_cmap_s));
461          if (ret)
462            {
463              ret = -EFAULT;
464              break;
465            }
466          ret = fb->vtable->putcmap(fb->vtable, &cmap);
467        }
468        break;
469#endif
470#ifdef CONFIG_FB_HWCURSOR
471      case FBIOGET_CURSOR:     /* Get cursor attributes */
472        {
473          struct fb_cursorattrib_s attrib = { 0 };
474
475          DEBUGASSERT(fb->vtable != NULL &&
476                      fb->vtable->getcursor != NULL);
477          ret = fb->vtable->getcursor(fb->vtable, &attrib);
478          if (ret == ENOERR)
479            {
480              ret = LOS_ArchCopyToUser((void *)arg, &attrib, sizeof(struct fb_cursorattrib_s));
481              if (ret)
482                {
483                  ret = -EFAULT;
484                }
485            }
486        }
487        break;
488
489      case FBIOPUT_CURSOR:     /* Set cursor attibutes */
490        {
491          struct fb_setcursor_s cursor;
492
493          DEBUGASSERT(fb->vtable != NULL &&
494                      fb->vtable->setcursor != NULL);
495          ret = LOS_ArchCopyFromUser(&cursor, (const void *)arg, sizeof(struct fb_setcursor_s));
496          if (ret)
497            {
498              ret = -EFAULT;
499              break;
500            }
501          ret = fb->vtable->setcursor(fb->vtable, &cursor);
502        }
503        break;
504#endif
505
506#ifdef CONFIG_LCD_UPDATE
507      case FBIO_UPDATE:  /* Update the LCD with the modified framebuffer data  */
508        {
509          struct nxgl_rect_s rect;
510          struct fb_planeinfo_s pinfo;
511
512          DEBUGASSERT(fb->vtable != NULL && fb->vtable->getplaneinfo != NULL);
513          ret = LOS_ArchCopyFromUser(&rect, (const void *)arg, sizeof(struct nxgl_rect_s));
514          if (ret)
515            {
516              ret = -EFAULT;
517              break;
518            }
519          ret = fb->vtable->getplaneinfo(fb->vtable, fb->plane, &pinfo);
520          if (ret >= 0)
521            {
522               nx_notify_rectangle((NX_PLANEINFOTYPE *)&pinfo, &rect);
523            }
524        }
525        break;
526#endif
527
528#ifdef CONFIG_FB_SYNC
529      case FBIO_WAITFORVSYNC:  /* Wait upon vertical sync */
530        {
531          ret = fb->vtable->waitforvsync(fb->vtable);
532        }
533        break;
534#endif
535
536#ifdef CONFIG_FB_OVERLAY
537      case FBIO_SELECT_OVERLAY:  /* Select video overlay */
538        {
539          struct fb_overlayinfo_s oinfo;
540
541          DEBUGASSERT(fb->vtable != NULL && fb->vtable->getoverlayinfo != NULL);
542          ret = fb->vtable->getoverlayinfo(fb->vtable, arg, &oinfo);
543          if (ret == OK)
544            {
545              fb->fbmem = oinfo.fbmem;
546              fb->fblen = oinfo.fblen;
547              fb->bpp   = oinfo.bpp;
548            }
549        }
550        break;
551
552      case FBIOGET_OVERLAYINFO:  /* Get video overlay info */
553        {
554          struct fb_overlayinfo_s oinfo;
555
556          DEBUGASSERT(fb->vtable != NULL &&
557                      fb->vtable->getoverlayinfo != NULL);
558          ret = LOS_ArchCopyFromUser(&oinfo, (const void *)arg, sizeof(struct fb_overlayinfo_s));
559          if (ret)
560            {
561              ret = -EFAULT;
562              break;
563            }
564          ret = fb->vtable->getoverlayinfo(fb->vtable, oinfo.overlay, &oinfo);
565          if (ret == ENOERR)
566            {
567              ret = LOS_ArchCopyToUser((void *)arg, &oinfo, sizeof(struct fb_overlayinfo_s));
568              if (ret)
569                {
570                  ret = -EFAULT;
571                }
572            }
573        }
574        break;
575
576      case FBIOSET_TRANSP:  /* Set video overlay transparency */
577        {
578          struct fb_overlayinfo_s oinfo;
579
580          DEBUGASSERT(fb->vtable != NULL &&
581                      fb->vtable->settransp != NULL);
582          ret = LOS_ArchCopyFromUser(&oinfo, (const void *)arg, sizeof(struct fb_overlayinfo_s));
583          if (ret)
584            {
585              ret = -EFAULT;
586              break;
587            }
588          ret = fb->vtable->settransp(fb->vtable, &oinfo);
589        }
590        break;
591
592      case FBIOSET_CHROMAKEY:  /* Set video overlay chroma key */
593        {
594          struct fb_overlayinfo_s oinfo;
595
596          DEBUGASSERT(fb->vtable != NULL &&
597                      fb->vtable->setchromakey != NULL);
598          ret = LOS_ArchCopyFromUser(&oinfo, (const void *)arg, sizeof(struct fb_overlayinfo_s));
599          if (ret)
600            {
601              ret = -EFAULT;
602              break;
603            }
604          ret = fb->vtable->setchromakey(fb->vtable, &oinfo);
605        }
606        break;
607
608      case FBIOSET_COLOR:  /* Set video overlay color */
609        {
610          struct fb_overlayinfo_s oinfo;
611
612          DEBUGASSERT(fb->vtable != NULL &&
613                      fb->vtable->setcolor != NULL);
614          ret = LOS_ArchCopyFromUser(&oinfo, (const void *)arg, sizeof(struct fb_overlayinfo_s));
615          if (ret)
616            {
617              ret = -EFAULT;
618              break;
619            }
620          ret = fb->vtable->setcolor(fb->vtable, &oinfo);
621        }
622        break;
623
624      case FBIOSET_BLANK:  /* Blank or unblank video overlay */
625        {
626          struct fb_overlayinfo_s oinfo;
627
628          DEBUGASSERT(fb->vtable != NULL &&
629                      fb->vtable->setblank != NULL);
630          ret = LOS_ArchCopyFromUser(&oinfo, (const void *)arg, sizeof(struct fb_overlayinfo_s));
631          if (ret)
632            {
633              ret = -EFAULT;
634              break;
635            }
636          ret = fb->vtable->setblank(fb->vtable, &oinfo);
637        }
638        break;
639
640      case FBIOSET_AREA:  /* Set active video overlay area */
641        {
642          struct fb_overlayinfo_s oinfo;
643
644          DEBUGASSERT(fb->vtable != NULL &&
645                      fb->vtable->setarea != NULL);
646          ret = LOS_ArchCopyFromUser(&oinfo, (const void *)arg, sizeof(struct fb_overlayinfo_s));
647          if (ret)
648            {
649              ret = -EFAULT;
650              break;
651            }
652          ret = fb->vtable->setarea(fb->vtable, &oinfo);
653        }
654        break;
655
656#ifdef CONFIG_FB_OVERLAY_BLIT
657      case FBIOSET_BLIT:  /* Blit operation between video overlays */
658        {
659          struct fb_overlayblit_s blit;
660
661          DEBUGASSERT(fb->vtable != NULL &&
662                      fb->vtable->blit != NULL);
663          ret = LOS_ArchCopyFromUser(&blit, (const void *)arg, sizeof(struct fb_overlayblit_s));
664          if (ret)
665            {
666              ret = -EFAULT;
667              break;
668            }
669          ret = fb->vtable->blit(fb->vtable, &blit);
670        }
671        break;
672
673      case FBIOSET_BLEND:  /* Blend operation between video overlays */
674        {
675          struct fb_overlayblend_s blend;
676
677          DEBUGASSERT(fb->vtable != NULL &&
678                      fb->vtable->blend != NULL);
679          ret = LOS_ArchCopyFromUser(&blend, (const void *)arg, sizeof(struct fb_overlayblend_s));
680          if (ret)
681            {
682              ret = -EFAULT;
683              break;
684            }
685          ret = fb->vtable->blend(fb->vtable, &blend);
686        }
687        break;
688#endif
689#endif /* CONFIG_FB_OVERLAY */
690
691      default:
692        DEBUGASSERT(fb->vtable != NULL && fb->vtable->fb_ioctl != NULL);
693        ret = fb->vtable->fb_ioctl(fb->vtable, cmd, arg);
694        break;
695    }
696
697  return ret;
698}
699
700/****************************************************************************
701 * Public Functions
702 ****************************************************************************/
703
704/****************************************************************************
705 * Name: fb_register
706 *
707 * Description:
708 *   Register the framebuffer character device at /dev/fbN where N is the
709 *   display number if the devices supports only a single plane.  If the
710 *   hardware supports multiple color planes, then the device will be
711 *   registered at /dev/fbN.M where N is the again display number but M
712 *   is the display plane.
713 *
714 * Input Parameters:
715 *   display - The display number for the case of boards supporting multiple
716 *             displays or for hardware that supports multiple
717 *             layers (each layer is consider a display).  Typically zero.
718 *   plane   - Identifies the color plane on hardware that supports separate
719 *             framebuffer "planes" for each color component.
720 *
721 * Returned Value:
722 *   Zero (OK) is returned success; a negated errno value is returned on any
723 *   failure.
724 *
725 ****************************************************************************/
726
727int fb_register(int display, int plane)
728{
729  struct fb_chardev_s *fb = NULL;
730  struct fb_videoinfo_s vinfo;
731  struct fb_planeinfo_s pinfo;
732#ifdef CONFIG_FB_OVERLAY
733  struct fb_overlayinfo_s oinfo;
734#endif
735  char devname[16];
736  int nplanes;
737  int ret;
738
739  if (display < 0 || display >= FB_DEV_MAXNUM)
740    return -EINVAL;
741
742  /* Allocate a framebuffer state instance */
743
744  fb = (struct fb_chardev_s *)malloc(sizeof(struct fb_chardev_s));
745  if (fb == NULL)
746    {
747      return -ENOMEM;
748    }
749
750  /* Initialize the frame buffer device. */
751
752  ret = up_fbinitialize(display);
753  if (ret < 0)
754    {
755      gerr("ERROR: up_fbinitialize() failed for display %d: %d\n", display, ret);
756      goto errout_with_fb;
757    }
758
759  DEBUGASSERT((unsigned)plane <= UINT8_MAX);
760  fb->plane  = plane;
761
762  fb->vtable = up_fbgetvplane(display, plane);
763  if (fb->vtable == NULL)
764    {
765      gerr("ERROR: up_fbgetvplane() failed, vplane=%d\n", plane);
766      goto errout_with_fb;
767    }
768
769  /* Initialize the frame buffer instance. */
770
771  DEBUGASSERT(fb->vtable->getvideoinfo != NULL);
772  ret = fb->vtable->getvideoinfo(fb->vtable, &vinfo);
773  if (ret < 0)
774    {
775      gerr("ERROR: getvideoinfo() failed: %d\n", ret);
776      goto errout_with_fb;
777    }
778
779  nplanes = vinfo.nplanes;
780  DEBUGASSERT(vinfo.nplanes > 0 && (unsigned)plane < vinfo.nplanes);
781
782  DEBUGASSERT(fb->vtable->getplaneinfo != NULL);
783  ret = fb->vtable->getplaneinfo(fb->vtable, plane, &pinfo);
784  if (ret < 0)
785    {
786      gerr("ERROR: getplaneinfo() failed: %d\n", ret);
787      goto errout_with_fb;
788    }
789
790  fb->fbmem  = pinfo.fbmem;
791  fb->fblen  = pinfo.fblen;
792  fb->bpp    = pinfo.bpp;
793
794  /* Clear the framebuffer memory */
795
796  memset(pinfo.fbmem, 0, pinfo.fblen);
797
798#ifdef CONFIG_FB_OVERLAY
799  /* Initialize first overlay but do not select */
800
801  DEBUGASSERT(fb->vtable->getoverlayinfo != NULL);
802  ret = fb->vtable->getoverlayinfo(fb->vtable, 0, &oinfo);
803  if (ret < 0)
804    {
805      gerr("ERROR: getoverlayinfo() failed: %d\n", ret);
806      goto errout_with_fb;
807    }
808
809  /* Clear the overlay memory. Necessary when plane 0 and overlay 0
810   * different.
811   */
812
813  memset(oinfo.fbmem, 0, oinfo.fblen);
814#endif
815
816  /* Register the framebuffer device */
817
818  if (nplanes < 2)
819    {
820      (void)snprintf(devname, 16, "/dev/fb%d", display);
821    }
822  else
823    {
824      (void)snprintf(devname, 16, "/dev/fb%d.%d", display, plane);
825    }
826
827  ret = register_driver(devname, &fb_fops, 0666, (void *)fb);
828  if (ret < 0)
829    {
830      gerr("ERROR: register_driver() failed: %d\n", ret);
831      goto errout_with_fb;
832    }
833
834    g_fb_dev[display] = fb;
835
836    return OK;
837
838errout_with_fb:
839    free(fb);
840    return ret;
841}
842
843int fb_unregister(int display)
844{
845    struct fb_chardev_s *fb = NULL;
846
847    if (display < 0 || display >= FB_DEV_MAXNUM)
848        return -EINVAL;
849
850    fb = g_fb_dev[display];
851
852    up_fbuninitialize(display);
853
854    free(fb);
855    g_fb_dev[display] = NULL;
856
857    return 0;
858}
859