• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
48 struct 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
58 static struct fb_chardev_s *g_fb_dev[FB_DEV_MAXNUM] = {NULL};
59 
60 /****************************************************************************
61  * Private Function Prototypes
62  ****************************************************************************/
63 
64 static int     fb_open(struct file *filep);
65 static int     fb_close(struct file *filep);
66 static ssize_t fb_read(struct file *filep, char *buffer, size_t buflen);
67 static ssize_t fb_write(struct file *filep, const char *buffer, size_t buflen);
68 static off_t   fb_seek(struct file *filep, off_t offset, int whence);
69 static int     fb_ioctl(struct file *filep, int cmd, unsigned long arg);
70 static ssize_t fb_mmap(struct file* filep, LosVmMapRegion *region);
71 
72 /****************************************************************************
73  * Private Data
74  ****************************************************************************/
75 
76 static 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 
fb_mmap(struct file * filep,LosVmMapRegion * region)97 static 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 
fb_open(struct file * filep)133 static 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 
fb_close(struct file * filep)170 static 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 
fb_read(struct file * filep,char * buffer,size_t len)203 static 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 
fb_write(struct file * filep,const char * buffer,size_t len)248 static 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 
fb_seek(struct file * filep,off_t offset,int whence)301 static 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 
fb_ioctl(struct file * filep,int cmd,unsigned long arg)369 static 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 
fb_register(int display,int plane)727 int 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 
838 errout_with_fb:
839     free(fb);
840     return ret;
841 }
842 
fb_unregister(int display)843 int 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