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}