| /**************************************************************************** |
| * drivers/video/fb.c |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. The |
| * ASF licenses this file to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the |
| * License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations |
| * under the License. |
| * |
| ****************************************************************************/ |
| |
| /* Framebuffer character driver */ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <nuttx/config.h> |
| |
| #include <sys/types.h> |
| #include <stdbool.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <assert.h> |
| #include <debug.h> |
| #include <errno.h> |
| #include <poll.h> |
| |
| #include <nuttx/irq.h> |
| #include <nuttx/kmalloc.h> |
| #include <nuttx/fs/fs.h> |
| #include <nuttx/fs/ioctl.h> |
| #include <nuttx/video/fb.h> |
| #include <nuttx/clock.h> |
| #include <nuttx/wdog.h> |
| #include <nuttx/circbuf.h> |
| #include <nuttx/sched_note.h> |
| |
| #ifdef CONFIG_VIDEO_FB_SPLASHSCREEN |
| # include <nuttx/signal.h> |
| #endif |
| |
| /**************************************************************************** |
| * Pre-processor definitions |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_VIDEO_FB_SPLASHSCREEN |
| # define SPLASH_BGCOL CONFIG_VIDEO_FB_SPLASHSCREEN_BG_COLOUR |
| # define SPLASH_SLEEP CONFIG_VIDEO_FB_SPLASHSCREEN_DISP_TIME |
| # |
| # if defined(CONFIG_VIDEO_FB_SPLASHSCREEN_BPP32) |
| # define SPLASHSCREEN_FMT FB_FMT_RGBA32 |
| # elif defined(CONFIG_VIDEO_FB_SPLASHSCREEN_BPP24) |
| # define SPLASHSCREEN_FMT FB_FMT_RGB24 |
| # elif defined(CONFIG_VIDEO_FB_SPLASHSCREEN_BPP16) |
| # define SPLASHSCREEN_FMT FB_FMT_RGB16_565 |
| # elif defined(CONFIG_VIDEO_FB_SPLASHSCREEN_BPP8) |
| # define SPLASHSCREEN_FMT FB_FMT_RGB8_332 |
| # elif defined(CONFIG_VIDEO_FB_SPLASHSCREEN_GREY) |
| # define SPLASHSCREEN_FMT FB_FMT_GREY |
| # elif defined(CONFIG_VIDEO_FB_SPLASHSCREEN_MONO) |
| # define SPLASHSCREEN_FMT FB_FMT_MONO |
| # endif /* CONFIG_VIDEO_FB_SPLASHSCREEN_BPP32 */ |
| # |
| #endif /* CONFIG_VIDEO_FB_SPLASHSCREEN */ |
| |
| /**************************************************************************** |
| * Private Types |
| ****************************************************************************/ |
| |
| struct fb_priv_s |
| { |
| /* Supports a singly linked list */ |
| |
| FAR struct fb_priv_s *flink; |
| |
| int overlay; /* Overlay number */ |
| |
| /* Polling fds of waiting threads */ |
| |
| FAR struct pollfd *fds[CONFIG_VIDEO_FB_NPOLLWAITERS]; |
| |
| #ifdef CONFIG_FB_SYNC |
| sem_t wait; |
| #endif |
| }; |
| |
| struct fb_paninfo_s |
| { |
| FAR struct circbuf_s buf; /* Pan buffer queued list */ |
| struct wdog_s wdog; /* VSync offset timer */ |
| FAR struct fb_chardev_s *dev; |
| }; |
| |
| /* This structure defines one framebuffer device. Note that which is |
| * everything in this structure is constant data set up and initialization |
| * time. Therefore, no there is requirement for serialized access to this |
| * structure. |
| */ |
| |
| struct fb_chardev_s |
| { |
| FAR struct fb_vtable_s *vtable; /* Framebuffer interface */ |
| uint8_t plane; /* Video plan number */ |
| clock_t vsyncoffset; /* VSync offset ticks */ |
| FAR struct fb_priv_s *head; |
| FAR struct fb_paninfo_s *paninfo; /* Pan info array */ |
| size_t paninfo_count; /* Pan info count */ |
| }; |
| |
| struct fb_panelinfo_s |
| { |
| FAR void *fbmem; /* Start of frame buffer memory */ |
| size_t fblen; /* Size of the framebuffer */ |
| uint8_t fbcount; /* Count of frame buffer */ |
| uint8_t bpp; /* Bits per pixel */ |
| }; |
| |
| /**************************************************************************** |
| * Private Function Prototypes |
| ****************************************************************************/ |
| |
| static FAR struct circbuf_s *fb_get_panbuf(FAR struct fb_chardev_s *fb, |
| int overlay); |
| static int fb_add_paninfo(FAR struct fb_chardev_s *fb, |
| FAR const union fb_paninfo_u *info, |
| int overlay); |
| static int fb_clear_paninfo(FAR struct fb_chardev_s *fb, |
| int overlay); |
| static int fb_open(FAR struct file *filep); |
| static int fb_close(FAR struct file *filep); |
| static ssize_t fb_read(FAR struct file *filep, FAR char *buffer, |
| size_t buflen); |
| static ssize_t fb_write(FAR struct file *filep, FAR const char *buffer, |
| size_t buflen); |
| static off_t fb_seek(FAR struct file *filep, off_t offset, int whence); |
| static int fb_ioctl(FAR struct file *filep, int cmd, unsigned long arg); |
| static int fb_mmap(FAR struct file *filep, |
| FAR struct mm_map_entry_s *map); |
| static int fb_poll(FAR struct file *filep, FAR struct pollfd *fds, |
| bool setup); |
| static int fb_get_panelinfo(FAR struct fb_chardev_s *fb, |
| FAR struct fb_panelinfo_s *panelinfo, |
| int overlay); |
| static int fb_get_planeinfo(FAR struct fb_chardev_s *fb, |
| FAR struct fb_planeinfo_s *pinfo, |
| uint8_t display); |
| #ifdef CONFIG_FB_SYNC |
| static int fb_sem_wait(FAR struct fb_chardev_s *fb, |
| FAR struct fb_priv_s *priv, |
| int overlay); |
| static void fb_sem_post(FAR struct fb_chardev_s *fb, int overlay); |
| #endif |
| |
| #ifdef CONFIG_BUILD_KERNEL |
| static int fb_munmap(FAR struct task_group_s *group, |
| FAR struct mm_map_entry_s *entry, |
| FAR void *start, size_t length); |
| #endif |
| |
| #ifdef CONFIG_VIDEO_FB_SPLASHSCREEN |
| static int fb_splashscreen(FAR struct fb_videoinfo_s *vinfo, |
| FAR struct fb_planeinfo_s *pinfo); |
| static int fb_splash_fill(FAR struct fb_videoinfo_s *vinfo, |
| FAR struct fb_planeinfo_s *pinfo, |
| uint32_t colour); |
| #endif |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| static const struct file_operations g_fb_fops = |
| { |
| fb_open, /* open */ |
| fb_close, /* close */ |
| fb_read, /* read */ |
| fb_write, /* write */ |
| fb_seek, /* seek */ |
| fb_ioctl, /* ioctl */ |
| fb_mmap, /* mmap */ |
| NULL, /* truncate */ |
| fb_poll /* poll */ |
| }; |
| |
| #ifdef CONFIG_VIDEO_FB_SPLASHSCREEN |
| extern const struct palette_bitmap_s g_splscr; |
| #endif |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_VIDEO_FB_SPLASHSCREEN |
| static int fb_splash_fill(FAR struct fb_videoinfo_s *vinfo, |
| FAR struct fb_planeinfo_s *pinfo, |
| uint32_t colour) |
| { |
| FAR uint8_t *row; |
| int rgb_colour; |
| int y; |
| #if !defined(CONFIG_VIDEO_FB_SPLASHSCREEN_MONO) && \ |
| !defined(CONFIG_VIDEO_FB_SPLASHSCREEN_GREY) && \ |
| !defined(CONFIG_VIDEO_FB_SPLASHSCREEN_BPP32) |
| const int r = (colour >> 16) & 0xff; |
| const int g = (colour >> 8) & 0xff; |
| const int b = (colour) & 0xff; |
| #endif |
| |
| #ifdef CONFIG_VIDEO_FB_SPLASHSCREEN_BPP32 |
| rgb_colour = colour; |
| #elif defined(CONFIG_VIDEO_FB_SPLASHSCREEN_BPP24) |
| rgb_colour = MKRGB(r, g, b); |
| #elif defined(CONFIG_VIDEO_FB_SPLASHSCREEN_BPP16) |
| rgb_colour = MKRGB(r, g, b); |
| #elif defined(CONFIG_VIDEO_FB_SPLASHSCREEN_BPP8) |
| rgb_colour = MKRGB(r, g, b); |
| #elif defined(CONFIG_VIDEO_FB_SPLASHSCREEN_MONO) || \ |
| defined(CONFIG_VIDEO_FB_SPLASHSCREEN_GREY) |
| rgb_colour = colour & 0xff; /* No conversion needed */ |
| #endif |
| |
| row = (FAR uint8_t *)pinfo->fbmem; |
| for (y = 0; y < (vinfo->yres - 1); y++) |
| { |
| memset(row, rgb_colour, pinfo->stride); |
| row += pinfo->stride; |
| } |
| |
| return OK; |
| } |
| |
| static int fb_splashscreen(FAR struct fb_videoinfo_s *vinfo, |
| FAR struct fb_planeinfo_s *pinfo) |
| { |
| FAR const struct splscr_bitmap_s *record = &g_splscr.data[0]; |
| FAR uint8_t *dst; |
| unsigned int row; |
| unsigned int nrun; |
| FAR fb_pixel_t *buf; |
| FAR fb_pixel_t colour; |
| int ret = OK; |
| |
| DEBUGASSERT(SPLASHSCREEN_FMT == vinfo->fmt); |
| if (SPLASHSCREEN_FMT != vinfo->fmt) |
| { |
| gwarn("WARNING: Splashscreen format (%d) doesn't match LCD (%d)\n", |
| SPLASHSCREEN_FMT, vinfo->fmt); |
| } |
| |
| DEBUGASSERT(g_splscr.width <= vinfo->xres); |
| if (g_splscr.width > vinfo->xres) |
| { |
| gwarn("Splashscreen width %d wider than the display width: %d\n", |
| (int)g_splscr.width, vinfo->xres); |
| return -EINVAL; |
| } |
| |
| DEBUGASSERT(g_splscr.height <= vinfo->yres); |
| if (g_splscr.height > vinfo->yres) |
| { |
| gerr("ERROR: Splashscreen height %d taller than the display: %d\n", |
| (int)g_splscr.height, vinfo->yres); |
| return -EINVAL; |
| } |
| |
| buf = kmm_malloc(sizeof(fb_pixel_t) * g_splscr.width); |
| if (buf == NULL) |
| { |
| gerr("ERROR: Failed to allocate memory for splashsceeb buffer\n"); |
| return -ENOMEM; |
| } |
| |
| /* Centre the image and set destination to start of area to be used */ |
| |
| dst = pinfo->fbmem + (pinfo->stride * |
| ((vinfo->yres - g_splscr.height) / 2)) + |
| (((vinfo->xres - g_splscr.width) / 2) * |
| sizeof(fb_pixel_t)); |
| |
| /* Now output the rows */ |
| |
| for (row = 0; row < g_splscr.height; row++) |
| { |
| unsigned int width; |
| FAR fb_pixel_t *bufp = buf; /* Start address of the buffer */ |
| |
| /* Process each run-length encoded pixel in the image */ |
| |
| for (width = 0; width < g_splscr.width; record++) |
| { |
| nrun = (unsigned int)record->npixels; /* num pixels of the colour */ |
| colour = g_splscr.lut[record->lookup]; |
| width += nrun; |
| while (nrun-- > 0) |
| { |
| *bufp = colour; /* fill with the colour */ |
| bufp++; |
| } |
| } |
| |
| DEBUGASSERT(width == g_splscr.width); |
| gerr("ERROR: Splashscreen file RLE line length doe not match LCD\n"); |
| |
| /* copy the decoded/expanded pixel data for this row to framebuffer */ |
| |
| memcpy(dst, buf, sizeof(fb_pixel_t) * g_splscr.width); |
| |
| /* Increment the vertical position */ |
| |
| dst += pinfo->stride; |
| } |
| |
| return ret; |
| } |
| #endif /* CONFIG_VIDEO_FB_SPLASHSCREEN */ |
| |
| /**************************************************************************** |
| * Name: fb_get_panbuf |
| ****************************************************************************/ |
| |
| static FAR struct circbuf_s *fb_get_panbuf(FAR struct fb_chardev_s *fb, |
| int overlay) |
| { |
| int id = overlay + 1; |
| |
| DEBUGASSERT(id >= 0 && id < fb->paninfo_count); |
| |
| return &(fb->paninfo[id].buf); |
| } |
| |
| /**************************************************************************** |
| * Name: fb_add_paninfo |
| ****************************************************************************/ |
| |
| static int fb_add_paninfo(FAR struct fb_chardev_s *fb, |
| FAR const union fb_paninfo_u *info, |
| int overlay) |
| { |
| FAR struct circbuf_s *panbuf; |
| irqstate_t flags; |
| ssize_t ret; |
| |
| DEBUGASSERT(fb != NULL); |
| |
| panbuf = fb_get_panbuf(fb, overlay); |
| if (panbuf == NULL) |
| { |
| return -EINVAL; |
| } |
| |
| /* Disable the interrupt when writing to the queue to |
| * prevent it from being modified by the interrupted |
| * thread during the writing process. |
| */ |
| |
| flags = enter_critical_section(); |
| |
| /* Write planeinfo information to the queue. */ |
| |
| ret = circbuf_write(panbuf, info, sizeof(union fb_paninfo_u)); |
| if (ret != sizeof(union fb_paninfo_u)) |
| { |
| gwarn("WARNING: circbuf_write(panbuf) failed\n"); |
| } |
| |
| /* Re-enable interrupts */ |
| |
| leave_critical_section(flags); |
| return ret <= 0 ? -ENOSPC : OK; |
| } |
| |
| /**************************************************************************** |
| * Name: fb_clear_paninfo |
| ****************************************************************************/ |
| |
| static int fb_clear_paninfo(FAR struct fb_chardev_s *fb, |
| int overlay) |
| { |
| FAR struct circbuf_s *panbuf; |
| irqstate_t flags; |
| |
| DEBUGASSERT(fb != NULL); |
| |
| panbuf = fb_get_panbuf(fb, overlay); |
| if (panbuf == NULL) |
| { |
| return -EINVAL; |
| } |
| |
| /* Disable the interrupt when writing to the queue to |
| * prevent it from being modified by the interrupted |
| * thread during the writing process. |
| */ |
| |
| flags = enter_critical_section(); |
| |
| circbuf_reset(panbuf); |
| |
| /* Re-enable interrupts */ |
| |
| leave_critical_section(flags); |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: fb_open |
| ****************************************************************************/ |
| |
| static int fb_open(FAR struct file *filep) |
| { |
| FAR struct inode *inode; |
| FAR struct fb_chardev_s *fb; |
| FAR struct fb_priv_s *priv; |
| irqstate_t flags; |
| int ret; |
| |
| inode = filep->f_inode; |
| fb = inode->i_private; |
| |
| DEBUGASSERT(fb->vtable != NULL); |
| |
| priv = kmm_zalloc(sizeof(*priv)); |
| if (priv == NULL) |
| { |
| return -ENOMEM; |
| } |
| |
| flags = enter_critical_section(); |
| |
| if (fb->head == NULL) |
| { |
| if (fb->vtable->open != NULL && |
| (ret = fb->vtable->open(fb->vtable)) < 0) |
| { |
| goto err_fb; |
| } |
| } |
| |
| priv->overlay = FB_NO_OVERLAY; |
| #ifdef CONFIG_FB_SYNC |
| nxsem_init(&priv->wait, 0, 0); |
| #endif |
| |
| /* Attach the open structure to the device */ |
| |
| priv->flink = fb->head; |
| fb->head = priv; |
| |
| filep->f_priv = priv; |
| |
| leave_critical_section(flags); |
| return 0; |
| |
| err_fb: |
| leave_critical_section(flags); |
| kmm_free(priv); |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: fb_close |
| ****************************************************************************/ |
| |
| static int fb_close(FAR struct file *filep) |
| { |
| FAR struct inode *inode; |
| FAR struct fb_chardev_s *fb; |
| FAR struct fb_priv_s *priv; |
| FAR struct fb_priv_s *curr; |
| FAR struct fb_priv_s *prev; |
| irqstate_t flags; |
| int ret; |
| |
| inode = filep->f_inode; |
| fb = inode->i_private; |
| priv = filep->f_priv; |
| |
| DEBUGASSERT(fb->vtable != NULL && priv != NULL); |
| |
| flags = enter_critical_section(); |
| |
| DEBUGASSERT(fb->head); |
| if (fb->head->flink == NULL) |
| { |
| if (fb->vtable->close != NULL) |
| { |
| ret = fb->vtable->close(fb->vtable); |
| if (ret < 0) |
| { |
| leave_critical_section(flags); |
| return ret; |
| } |
| } |
| } |
| |
| /* Find the open structure in the list of |
| * open structures for the device. |
| */ |
| |
| for (prev = NULL, curr = fb->head; |
| curr && curr != priv; |
| prev = curr, curr = curr->flink); |
| |
| DEBUGASSERT(curr); |
| |
| /* Remove the structure from the device */ |
| |
| if (prev) |
| { |
| prev->flink = priv->flink; |
| } |
| else |
| { |
| fb->head = priv->flink; |
| } |
| |
| leave_critical_section(flags); |
| |
| #ifdef CONFIG_FB_SYNC |
| nxsem_destroy(&priv->wait); |
| #endif |
| kmm_free(priv); |
| |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: fb_read |
| ****************************************************************************/ |
| |
| static ssize_t fb_read(FAR struct file *filep, FAR char *buffer, size_t len) |
| { |
| FAR struct inode *inode; |
| FAR struct fb_chardev_s *fb; |
| FAR struct fb_priv_s *priv; |
| struct fb_panelinfo_s panelinfo; |
| size_t start; |
| size_t end; |
| size_t size; |
| int ret; |
| |
| ginfo("len: %u\n", (unsigned int)len); |
| |
| /* Get the framebuffer instance */ |
| |
| inode = filep->f_inode; |
| fb = inode->i_private; |
| priv = filep->f_priv; |
| |
| DEBUGASSERT(fb->vtable != NULL && priv != NULL); |
| |
| /* Get panel info */ |
| |
| ret = fb_get_panelinfo(fb, &panelinfo, priv->overlay); |
| |
| if (ret < 0) |
| { |
| return ret; |
| } |
| |
| /* Get the start and size of the transfer */ |
| |
| start = filep->f_pos; |
| if (start >= panelinfo.fblen) |
| { |
| return 0; /* Return end-of-file */ |
| } |
| |
| end = start + len; |
| if (end >= panelinfo.fblen) |
| { |
| end = panelinfo.fblen; |
| } |
| |
| size = end - start; |
| |
| /* And transfer the data from the frame buffer */ |
| |
| memcpy(buffer, panelinfo.fbmem + start, size); |
| filep->f_pos += size; |
| return size; |
| } |
| |
| /**************************************************************************** |
| * Name: fb_write |
| ****************************************************************************/ |
| |
| static ssize_t fb_write(FAR struct file *filep, FAR const char *buffer, |
| size_t len) |
| { |
| FAR struct inode *inode; |
| FAR struct fb_chardev_s *fb; |
| FAR struct fb_priv_s *priv; |
| struct fb_panelinfo_s panelinfo; |
| size_t start; |
| size_t end; |
| size_t size; |
| int ret; |
| |
| ginfo("len: %u\n", (unsigned int)len); |
| |
| /* Get the framebuffer instance */ |
| |
| inode = filep->f_inode; |
| fb = inode->i_private; |
| priv = filep->f_priv; |
| |
| DEBUGASSERT(fb->vtable != NULL && priv != NULL); |
| |
| /* Get panel info */ |
| |
| ret = fb_get_panelinfo(fb, &panelinfo, priv->overlay); |
| |
| if (ret < 0) |
| { |
| return ret; |
| } |
| |
| /* Get the start and size of the transfer */ |
| |
| start = filep->f_pos; |
| if (start >= panelinfo.fblen) |
| { |
| return -EFBIG; /* Cannot extend the framebuffer */ |
| } |
| |
| end = start + len; |
| if (end >= panelinfo.fblen) |
| { |
| end = panelinfo.fblen; |
| } |
| |
| size = end - start; |
| |
| /* And transfer the data into the frame buffer */ |
| |
| memcpy(panelinfo.fbmem + start, buffer, size); |
| filep->f_pos += size; |
| return size; |
| } |
| |
| /**************************************************************************** |
| * Name: fb_seek |
| * |
| * Description: |
| * Seek the logical file pointer to the specified position. The offset |
| * is in units of pixels, with offset zero being the beginning of the |
| * framebuffer. |
| * |
| ****************************************************************************/ |
| |
| static off_t fb_seek(FAR struct file *filep, off_t offset, int whence) |
| { |
| FAR struct inode *inode; |
| FAR struct fb_chardev_s *fb; |
| FAR struct fb_priv_s *priv; |
| struct fb_panelinfo_s panelinfo; |
| off_t newpos; |
| int ret; |
| |
| ginfo("offset: %u whence: %d\n", (unsigned int)offset, whence); |
| |
| /* Get the framebuffer instance */ |
| |
| inode = filep->f_inode; |
| fb = inode->i_private; |
| priv = filep->f_priv; |
| |
| DEBUGASSERT(fb->vtable != NULL && priv != NULL); |
| |
| /* Determine the new, requested file position */ |
| |
| switch (whence) |
| { |
| case SEEK_CUR: |
| newpos = filep->f_pos + offset; |
| break; |
| |
| case SEEK_SET: |
| newpos = offset; |
| break; |
| |
| case SEEK_END: |
| |
| /* Get panel info */ |
| |
| ret = fb_get_panelinfo(fb, &panelinfo, priv->overlay); |
| |
| if (ret < 0) |
| { |
| return ret; |
| } |
| |
| newpos = panelinfo.fblen + offset; |
| break; |
| |
| default: |
| |
| /* Return EINVAL if the whence argument is invalid */ |
| |
| return -EINVAL; |
| } |
| |
| /* Opengroup.org: |
| * |
| * "The lseek() function shall allow the file offset to be set beyond the |
| * end of the existing data in the file. If data is later written at this |
| * point, subsequent reads of data in the gap shall return bytes with the |
| * value 0 until data is actually written into the gap." |
| * |
| * We can conform to the first part, but not the second. Return EINVAL if |
| * "...the resulting file offset would be negative for a regular file, |
| * block special file, or directory." |
| */ |
| |
| if (newpos >= 0) |
| { |
| filep->f_pos = newpos; |
| ret = newpos; |
| } |
| else |
| { |
| ret = -EINVAL; |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: fb_ioctl |
| * |
| * Description: |
| * The standard ioctl method. |
| * |
| ****************************************************************************/ |
| |
| static int fb_ioctl(FAR struct file *filep, int cmd, unsigned long arg) |
| { |
| FAR struct inode *inode; |
| FAR struct fb_chardev_s *fb; |
| int ret = OK; |
| |
| ginfo("cmd: %d arg: %ld\n", cmd, arg); |
| |
| /* Get the framebuffer instance */ |
| |
| inode = filep->f_inode; |
| fb = inode->i_private; |
| |
| /* Process the IOCTL command */ |
| |
| switch (cmd) |
| { |
| case FBIOGET_VIDEOINFO: /* Get color plane info */ |
| { |
| FAR struct fb_videoinfo_s *vinfo = |
| (FAR struct fb_videoinfo_s *)((uintptr_t)arg); |
| |
| DEBUGASSERT(vinfo != NULL && fb->vtable != NULL); |
| if (fb->vtable->getvideoinfo == NULL) |
| { |
| ret = -ENOTTY; |
| break; |
| } |
| |
| ret = fb->vtable->getvideoinfo(fb->vtable, vinfo); |
| } |
| break; |
| |
| case FBIOGET_PLANEINFO: /* Get video plane info */ |
| { |
| FAR struct fb_planeinfo_s *pinfo = |
| (FAR struct fb_planeinfo_s *)((uintptr_t)arg); |
| |
| DEBUGASSERT(pinfo != NULL); |
| ret = fb_get_planeinfo(fb, pinfo, pinfo->display); |
| } |
| break; |
| |
| #ifdef CONFIG_FB_CMAP |
| case FBIOGET_CMAP: /* Get RGB color mapping */ |
| { |
| FAR struct fb_cmap_s *cmap = |
| (FAR struct fb_cmap_s *)((uintptr_t)arg); |
| |
| DEBUGASSERT(cmap != NULL && fb->vtable != NULL); |
| if (fb->vtable->getcmap == NULL) |
| { |
| ret = -ENOTTY; |
| break; |
| } |
| |
| ret = fb->vtable->getcmap(fb->vtable, cmap); |
| } |
| break; |
| |
| case FBIOPUT_CMAP: /* Put RGB color mapping */ |
| { |
| FAR const struct fb_cmap_s *cmap = |
| (FAR struct fb_cmap_s *)((uintptr_t)arg); |
| |
| DEBUGASSERT(cmap != NULL && fb->vtable != NULL); |
| if (fb->vtable->putcmap == NULL) |
| { |
| ret = -ENOTTY; |
| break; |
| } |
| |
| ret = fb->vtable->putcmap(fb->vtable, cmap); |
| } |
| break; |
| #endif |
| #ifdef CONFIG_FB_HWCURSOR |
| case FBIOGET_CURSOR: /* Get cursor attributes */ |
| { |
| FAR struct fb_cursorattrib_s *attrib = |
| (FAR struct fb_cursorattrib_s *)((uintptr_t)arg); |
| |
| DEBUGASSERT(attrib != NULL && fb->vtable != NULL); |
| if (fb->vtable->getcursor == NULL) |
| { |
| ret = -ENOTTY; |
| } |
| |
| ret = fb->vtable->getcursor(fb->vtable, attrib); |
| } |
| break; |
| |
| case FBIOPUT_CURSOR: /* Set cursor attributes */ |
| { |
| FAR struct fb_setcursor_s *cursor = |
| (FAR struct fb_setcursor_s *)((uintptr_t)arg); |
| |
| DEBUGASSERT(cursor != NULL && fb->vtable != NULL); |
| if (fb->vtable->setcursor == NULL) |
| { |
| ret = -ENOTTY; |
| break; |
| } |
| |
| ret = fb->vtable->setcursor(fb->vtable, cursor); |
| } |
| break; |
| #endif |
| |
| #ifdef CONFIG_FB_UPDATE |
| case FBIO_UPDATE: /* Update the modified framebuffer data */ |
| { |
| struct fb_area_s *area = (FAR struct fb_area_s *)((uintptr_t)arg); |
| |
| DEBUGASSERT(fb->vtable != NULL); |
| if (fb->vtable->updatearea == NULL) |
| { |
| ret = -ENOTTY; |
| break; |
| } |
| |
| ret = fb->vtable->updatearea(fb->vtable, area); |
| } |
| break; |
| #endif |
| |
| #ifdef CONFIG_FB_SYNC |
| case FBIO_WAITFORVSYNC: /* Wait upon vertical sync */ |
| { |
| FAR struct fb_priv_s *priv = (FAR struct fb_priv_s *)filep->f_priv; |
| |
| DEBUGASSERT(fb->vtable != NULL); |
| if (fb->vtable->waitforvsync != NULL) |
| { |
| ret = fb->vtable->waitforvsync(fb->vtable); |
| break; |
| } |
| |
| ret = fb_sem_wait(fb, priv, priv->overlay); |
| } |
| break; |
| #endif |
| |
| #ifdef CONFIG_FB_OVERLAY |
| case FBIO_SELECT_OVERLAY: /* Select video overlay */ |
| { |
| struct fb_overlayinfo_s oinfo; |
| FAR struct fb_priv_s *priv = filep->f_priv; |
| |
| DEBUGASSERT(priv != NULL && fb->vtable != NULL); |
| if (fb->vtable->getoverlayinfo == NULL) |
| { |
| ret = -ENOTTY; |
| break; |
| } |
| |
| if (arg != FB_NO_OVERLAY) |
| { |
| memset(&oinfo, 0, sizeof(oinfo)); |
| ret = fb->vtable->getoverlayinfo(fb->vtable, arg, &oinfo); |
| } |
| |
| if (ret >= 0) |
| { |
| priv->overlay = arg; |
| } |
| } |
| break; |
| |
| case FBIOGET_OVERLAYINFO: /* Get video overlay info */ |
| { |
| FAR struct fb_overlayinfo_s *oinfo = |
| (FAR struct fb_overlayinfo_s *)((uintptr_t)arg); |
| |
| DEBUGASSERT(oinfo != NULL && fb->vtable != NULL); |
| if (fb->vtable->getoverlayinfo == NULL) |
| { |
| ret = -ENOTTY; |
| break; |
| } |
| |
| ret = fb->vtable->getoverlayinfo(fb->vtable, |
| oinfo->overlay, oinfo); |
| } |
| break; |
| |
| case FBIOSET_TRANSP: /* Set video overlay transparency */ |
| { |
| FAR struct fb_overlayinfo_s *oinfo = |
| (FAR struct fb_overlayinfo_s *)((uintptr_t)arg); |
| |
| DEBUGASSERT(oinfo != NULL && fb->vtable != NULL); |
| if (fb->vtable->settransp == NULL) |
| { |
| ret = -ENOTTY; |
| break; |
| } |
| |
| ret = fb->vtable->settransp(fb->vtable, oinfo); |
| } |
| break; |
| |
| case FBIOSET_CHROMAKEY: /* Set video overlay chroma key */ |
| { |
| FAR struct fb_overlayinfo_s *oinfo = |
| (FAR struct fb_overlayinfo_s *)((uintptr_t)arg); |
| |
| DEBUGASSERT(oinfo != NULL && fb->vtable != NULL); |
| if (fb->vtable->setchromakey == NULL) |
| { |
| ret = -ENOTTY; |
| break; |
| } |
| |
| ret = fb->vtable->setchromakey(fb->vtable, oinfo); |
| } |
| break; |
| |
| case FBIOSET_COLOR: /* Set video overlay color */ |
| { |
| FAR struct fb_overlayinfo_s *oinfo = |
| (FAR struct fb_overlayinfo_s *)((uintptr_t)arg); |
| |
| DEBUGASSERT(oinfo != NULL && fb->vtable != NULL); |
| if (fb->vtable->setcolor == NULL) |
| { |
| ret = -ENOTTY; |
| break; |
| } |
| |
| ret = fb->vtable->setcolor(fb->vtable, oinfo); |
| } |
| break; |
| |
| case FBIOSET_BLANK: /* Blank or unblank video overlay */ |
| { |
| FAR struct fb_overlayinfo_s *oinfo = |
| (FAR struct fb_overlayinfo_s *)((uintptr_t)arg); |
| |
| DEBUGASSERT(oinfo != NULL && fb->vtable != NULL); |
| if (fb->vtable->setblank == NULL) |
| { |
| ret = -ENOTTY; |
| break; |
| } |
| |
| ret = fb->vtable->setblank(fb->vtable, oinfo); |
| } |
| break; |
| |
| case FBIOSET_AREA: /* Set active video overlay area */ |
| { |
| FAR struct fb_overlayinfo_s *oinfo = |
| (FAR struct fb_overlayinfo_s *)((uintptr_t)arg); |
| |
| DEBUGASSERT(oinfo != NULL && fb->vtable != NULL); |
| if (fb->vtable->setarea == NULL) |
| { |
| ret = -ENOTTY; |
| break; |
| } |
| |
| ret = fb->vtable->setarea(fb->vtable, oinfo); |
| } |
| break; |
| |
| case FBIOSET_DESTAREA: /* Set destination area on the primary FB */ |
| { |
| FAR struct fb_overlayinfo_s *oinfo = |
| (FAR struct fb_overlayinfo_s *)((uintptr_t)arg); |
| |
| DEBUGASSERT(oinfo != NULL && fb->vtable != NULL); |
| if (fb->vtable->setdestarea == NULL) |
| { |
| ret = -ENOTTY; |
| break; |
| } |
| |
| ret = fb->vtable->setdestarea(fb->vtable, oinfo); |
| } |
| break; |
| |
| #ifdef CONFIG_FB_OVERLAY_BLIT |
| case FBIOSET_BLIT: /* Blit operation between video overlays */ |
| { |
| FAR struct fb_overlayblit_s *blit = |
| (FAR struct fb_overlayblit_s *)((uintptr_t)arg); |
| |
| DEBUGASSERT(blit != NULL && fb->vtable != NULL); |
| if (fb->vtable->blit == NULL) |
| { |
| ret = -ENOTTY; |
| break; |
| } |
| |
| ret = fb->vtable->blit(fb->vtable, blit); |
| } |
| break; |
| |
| case FBIOSET_BLEND: /* Blend operation between video overlays */ |
| { |
| FAR struct fb_overlayblend_s *blend = |
| (FAR struct fb_overlayblend_s *)((uintptr_t)arg); |
| |
| DEBUGASSERT(blend != NULL && fb->vtable != NULL); |
| if (fb->vtable->blend == NULL) |
| { |
| ret = -ENOTTY; |
| break; |
| } |
| |
| ret = fb->vtable->blend(fb->vtable, blend); |
| } |
| break; |
| #endif |
| |
| case FBIOPAN_OVERLAY: |
| { |
| FAR struct fb_overlayinfo_s *oinfo = |
| (FAR struct fb_overlayinfo_s *)((uintptr_t)arg); |
| union fb_paninfo_u paninfo; |
| |
| DEBUGASSERT(oinfo != NULL && fb->vtable != NULL); |
| |
| memcpy(&paninfo, oinfo, sizeof(*oinfo)); |
| |
| if (fb->vtable->panoverlay != NULL) |
| { |
| fb->vtable->panoverlay(fb->vtable, oinfo); |
| } |
| |
| ret = fb_add_paninfo(fb, &paninfo, oinfo->overlay); |
| } |
| break; |
| |
| #endif /* CONFIG_FB_OVERLAY */ |
| |
| case FBIOSET_POWER: |
| { |
| DEBUGASSERT(fb->vtable != NULL); |
| if (fb->vtable->setpower == NULL) |
| { |
| ret = -ENOTTY; |
| break; |
| } |
| |
| ret = fb->vtable->setpower(fb->vtable, (int)arg); |
| } |
| break; |
| |
| case FBIOGET_POWER: |
| { |
| FAR int *power = (FAR int *)((uintptr_t)arg); |
| |
| DEBUGASSERT(power != NULL && fb->vtable != NULL); |
| if (fb->vtable->getpower == NULL) |
| { |
| ret = -ENOTTY; |
| break; |
| } |
| |
| *power = fb->vtable->getpower(fb->vtable); |
| } |
| break; |
| |
| case FBIOGET_FRAMERATE: |
| { |
| FAR int *rate = (FAR int *)((uintptr_t)arg); |
| |
| DEBUGASSERT(rate != NULL && fb->vtable != NULL); |
| if (fb->vtable->getframerate == NULL) |
| { |
| ret = -ENOTTY; |
| break; |
| } |
| |
| *rate = fb->vtable->getframerate(fb->vtable); |
| } |
| break; |
| |
| case FBIOSET_FRAMERATE: |
| { |
| DEBUGASSERT(fb->vtable != NULL && |
| fb->vtable->setframerate != NULL); |
| ret = fb->vtable->setframerate(fb->vtable, (int)arg); |
| } |
| break; |
| |
| case FBIOPAN_DISPLAY: |
| { |
| FAR struct fb_planeinfo_s *pinfo = |
| (FAR struct fb_planeinfo_s *)((uintptr_t)arg); |
| union fb_paninfo_u paninfo; |
| |
| DEBUGASSERT(pinfo != NULL && fb->vtable != NULL); |
| |
| memcpy(&paninfo, pinfo, sizeof(*pinfo)); |
| |
| if (fb->vtable->pandisplay != NULL) |
| { |
| ret = fb->vtable->pandisplay(fb->vtable, pinfo); |
| if (ret < 0) |
| { |
| break; |
| } |
| } |
| |
| ret = fb_add_paninfo(fb, &paninfo, FB_NO_OVERLAY); |
| } |
| break; |
| |
| case FBIOPAN_CLEAR: |
| { |
| ret = fb_clear_paninfo(fb, (int)arg); |
| } |
| break; |
| |
| case FBIOSET_VSYNCOFFSET: |
| { |
| fb->vsyncoffset = USEC2TICK(arg); |
| } |
| break; |
| |
| case FBIOGET_VSCREENINFO: |
| { |
| struct fb_videoinfo_s vinfo; |
| struct fb_planeinfo_s pinfo; |
| FAR struct fb_var_screeninfo *varinfo = |
| (FAR struct fb_var_screeninfo *)((uintptr_t)arg); |
| |
| DEBUGASSERT(varinfo != NULL && fb->vtable != NULL); |
| if (fb->vtable->getvideoinfo == NULL) |
| { |
| ret = -ENOTTY; |
| break; |
| } |
| |
| ret = fb->vtable->getvideoinfo(fb->vtable, &vinfo); |
| if (ret < 0) |
| { |
| break; |
| } |
| |
| ret = fb_get_planeinfo(fb, &pinfo, 0); |
| if (ret < 0) |
| { |
| break; |
| } |
| |
| memset(varinfo, 0, sizeof(struct fb_var_screeninfo)); |
| varinfo->xres = vinfo.xres; |
| varinfo->yres = vinfo.yres; |
| varinfo->xres_virtual = pinfo.xres_virtual; |
| varinfo->yres_virtual = pinfo.yres_virtual; |
| varinfo->xoffset = pinfo.xoffset; |
| varinfo->yoffset = pinfo.yoffset; |
| varinfo->bits_per_pixel = pinfo.bpp; |
| varinfo->grayscale = FB_ISMONO(vinfo.fmt); |
| switch (vinfo.fmt) |
| { |
| case FB_FMT_Y1: |
| varinfo->red.offset = 0; |
| varinfo->green.offset = 0; |
| varinfo->blue.offset = 0; |
| varinfo->red.length = 1; |
| varinfo->green.length = 1; |
| varinfo->blue.length = 1; |
| break; |
| |
| case FB_FMT_Y8: |
| varinfo->red.offset = 0; |
| varinfo->green.offset = 0; |
| varinfo->blue.offset = 0; |
| varinfo->red.length = 8; |
| varinfo->green.length = 8; |
| varinfo->blue.length = 8; |
| break; |
| |
| case FB_FMT_RGB16_555: |
| varinfo->red.offset = 10; |
| varinfo->green.offset = 5; |
| varinfo->blue.offset = 0; |
| varinfo->red.length = 5; |
| varinfo->green.length = 5; |
| varinfo->blue.length = 5; |
| break; |
| |
| case FB_FMT_RGB16_565: |
| varinfo->red.offset = 11; |
| varinfo->green.offset = 5; |
| varinfo->blue.offset = 0; |
| varinfo->red.length = 5; |
| varinfo->green.length = 6; |
| varinfo->blue.length = 5; |
| break; |
| |
| case FB_FMT_RGB24: |
| case FB_FMT_RGB32: |
| varinfo->red.offset = 16; |
| varinfo->green.offset = 8; |
| varinfo->blue.offset = 0; |
| varinfo->red.length = 8; |
| varinfo->green.length = 8; |
| varinfo->blue.length = 8; |
| break; |
| |
| case FB_FMT_RGBA32: |
| varinfo->red.offset = 16; |
| varinfo->green.offset = 8; |
| varinfo->blue.offset = 0; |
| varinfo->transp.offset = 24; |
| varinfo->red.length = 8; |
| varinfo->green.length = 8; |
| varinfo->blue.length = 8; |
| varinfo->transp.length = 8; |
| break; |
| } |
| } |
| break; |
| |
| case FBIOGET_FSCREENINFO: |
| { |
| struct fb_videoinfo_s vinfo; |
| struct fb_planeinfo_s pinfo; |
| FAR struct fb_fix_screeninfo *fixinfo = |
| (FAR struct fb_fix_screeninfo *)((uintptr_t)arg); |
| |
| DEBUGASSERT(fixinfo != NULL && fb->vtable != NULL); |
| if (fb->vtable->getvideoinfo == NULL) |
| { |
| ret = -ENOTTY; |
| break; |
| } |
| |
| ret = fb->vtable->getvideoinfo(fb->vtable, &vinfo); |
| if (ret < 0) |
| { |
| break; |
| } |
| |
| ret = fb_get_planeinfo(fb, &pinfo, 0); |
| if (ret < 0) |
| { |
| break; |
| } |
| |
| memset(fixinfo, 0, sizeof(struct fb_fix_screeninfo)); |
| #ifdef CONFIG_FB_MODULEINFO |
| strlcpy(fixinfo->id, (FAR const char *)vinfo.moduleinfo, |
| sizeof(fixinfo->id)); |
| #endif |
| fixinfo->smem_start = (unsigned long)pinfo.fbmem; |
| fixinfo->smem_len = pinfo.fblen; |
| fixinfo->type = FB_ISYUVPLANAR(vinfo.fmt) ? |
| FB_TYPE_INTERLEAVED_PLANES : |
| FB_TYPE_PACKED_PIXELS; |
| fixinfo->visual = FB_ISMONO(vinfo.fmt) ? |
| FB_VISUAL_MONO10 : FB_VISUAL_TRUECOLOR; |
| fixinfo->line_length = pinfo.stride; |
| } |
| break; |
| |
| case FBIOGET_PANINFOCNT: |
| { |
| ret = fb_paninfo_count(fb->vtable, (int)arg); |
| } |
| break; |
| |
| default: |
| if (fb->vtable->ioctl != NULL) |
| { |
| ret = fb->vtable->ioctl(fb->vtable, cmd, arg); |
| } |
| else |
| { |
| gerr("ERROR: Unsupported IOCTL command: %d\n", cmd); |
| ret = -ENOTTY; |
| } |
| break; |
| } |
| |
| return ret; |
| } |
| |
| #ifdef CONFIG_BUILD_KERNEL |
| static int fb_munmap(FAR struct task_group_s *group, |
| FAR struct mm_map_entry_s *entry, |
| FAR void *start, size_t length) |
| { |
| if (group && entry) |
| { |
| ginfo("%p, len=%zu\n", entry->vaddr, entry->length); |
| vm_unmap_region(entry->vaddr, entry->length); |
| mm_map_remove(get_current_mm(), entry); |
| } |
| |
| return OK; |
| } |
| #endif |
| |
| static int fb_mmap(FAR struct file *filep, FAR struct mm_map_entry_s *map) |
| { |
| FAR struct inode *inode; |
| FAR struct fb_chardev_s *fb; |
| FAR struct fb_priv_s *priv; |
| struct fb_panelinfo_s panelinfo; |
| int ret; |
| |
| /* Get the framebuffer instance */ |
| |
| inode = filep->f_inode; |
| fb = inode->i_private; |
| priv = filep->f_priv; |
| |
| DEBUGASSERT(fb->vtable != NULL && priv != NULL); |
| |
| /* Get panel info */ |
| |
| ret = fb_get_panelinfo(fb, &panelinfo, priv->overlay); |
| |
| if (ret < 0) |
| { |
| return ret; |
| } |
| |
| /* Return the address corresponding to the start of frame buffer. */ |
| |
| if (map->offset >= 0 && map->offset < panelinfo.fblen && |
| map->length && map->offset + map->length <= panelinfo.fblen) |
| { |
| #ifdef CONFIG_BUILD_KERNEL |
| map->vaddr = vm_map_region((uintptr_t)panelinfo.fbmem + map->offset, |
| panelinfo.fblen); |
| map->length = panelinfo.fblen; |
| map->munmap = fb_munmap; |
| mm_map_add(get_current_mm(), map); |
| #else |
| map->vaddr = (FAR char *)panelinfo.fbmem + map->offset; |
| #endif |
| return OK; |
| } |
| |
| return -EINVAL; |
| } |
| |
| /**************************************************************************** |
| * Name: fb_poll |
| * |
| * Description: |
| * Wait for framebuffer to be writable. |
| * |
| ****************************************************************************/ |
| |
| static int fb_poll(FAR struct file *filep, struct pollfd *fds, bool setup) |
| { |
| FAR struct inode *inode; |
| FAR struct fb_chardev_s *fb; |
| FAR struct fb_priv_s *priv; |
| FAR struct circbuf_s *panbuf; |
| FAR struct pollfd **pollfds = NULL; |
| irqstate_t flags; |
| int ret = OK; |
| int i; |
| |
| /* Get the framebuffer instance */ |
| |
| inode = filep->f_inode; |
| fb = inode->i_private; |
| priv = filep->f_priv; |
| |
| DEBUGASSERT(fb->vtable != NULL && priv != NULL); |
| |
| flags = enter_critical_section(); |
| |
| if (setup) |
| { |
| for (i = 0; i < CONFIG_VIDEO_FB_NPOLLWAITERS; ++i) |
| { |
| if (!priv->fds[i]) |
| { |
| pollfds = &priv->fds[i]; |
| break; |
| } |
| } |
| |
| if (pollfds == NULL) |
| { |
| ret = -EBUSY; |
| goto errout; |
| } |
| |
| *pollfds = fds; |
| fds->priv = pollfds; |
| |
| panbuf = fb_get_panbuf(fb, priv->overlay); |
| if (!circbuf_is_full(panbuf)) |
| { |
| poll_notify(&fds, 1, POLLOUT); |
| } |
| } |
| else if (fds->priv != NULL) |
| { |
| /* This is a request to tear down the poll. */ |
| |
| FAR struct pollfd **slot = (FAR struct pollfd **)fds->priv; |
| *slot = NULL; |
| fds->priv = NULL; |
| } |
| |
| errout: |
| leave_critical_section(flags); |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: fb_get_panelinfo |
| ****************************************************************************/ |
| |
| static int fb_get_panelinfo(FAR struct fb_chardev_s *fb, |
| FAR struct fb_panelinfo_s *panelinfo, |
| int overlay) |
| { |
| struct fb_planeinfo_s pinfo; |
| struct fb_videoinfo_s vinfo; |
| int ret; |
| |
| #ifdef CONFIG_FB_OVERLAY |
| if (overlay != FB_NO_OVERLAY) |
| { |
| struct fb_overlayinfo_s oinfo; |
| DEBUGASSERT(fb->vtable->getoverlayinfo != NULL); |
| memset(&oinfo, 0, sizeof(oinfo)); |
| ret = fb->vtable->getoverlayinfo(fb->vtable, overlay, &oinfo); |
| |
| if (ret < 0) |
| { |
| gerr("ERROR: getoverlayinfo() failed: %d\n", ret); |
| return ret; |
| } |
| |
| panelinfo->fbmem = oinfo.fbmem; |
| panelinfo->fblen = oinfo.fblen; |
| panelinfo->fbcount = oinfo.yres_virtual == 0 ? |
| 1 : (oinfo.yres_virtual / oinfo.yres); |
| panelinfo->bpp = oinfo.bpp; |
| return OK; |
| } |
| #endif |
| |
| ret = fb_get_planeinfo(fb, &pinfo, 0); |
| if (ret < 0) |
| { |
| return ret; |
| } |
| |
| ret = fb->vtable->getvideoinfo(fb->vtable, &vinfo); |
| if (ret < 0) |
| { |
| gerr("ERROR: getvideoinfo() failed: %d\n", ret); |
| return ret; |
| } |
| |
| panelinfo->fbmem = pinfo.fbmem; |
| panelinfo->fblen = pinfo.fblen; |
| panelinfo->fbcount = pinfo.yres_virtual == 0 ? |
| 1 : (pinfo.yres_virtual / vinfo.yres); |
| panelinfo->bpp = pinfo.bpp; |
| |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: fb_get_planeinfo |
| ****************************************************************************/ |
| |
| static int fb_get_planeinfo(FAR struct fb_chardev_s *fb, |
| FAR struct fb_planeinfo_s *pinfo, |
| uint8_t display) |
| { |
| int ret; |
| |
| DEBUGASSERT(fb->vtable != NULL); |
| DEBUGASSERT(fb->vtable->getplaneinfo != NULL); |
| |
| memset(pinfo, 0, sizeof(struct fb_planeinfo_s)); |
| pinfo->display = display; |
| |
| ret = fb->vtable->getplaneinfo(fb->vtable, fb->plane, pinfo); |
| |
| if (ret < 0) |
| { |
| gerr("ERROR: getplaneinfo() failed: %d\n", ret); |
| return ret; |
| } |
| |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: fb_do_pollnotify |
| ****************************************************************************/ |
| |
| static void fb_do_pollnotify(wdparm_t arg) |
| { |
| FAR struct fb_paninfo_s *paninfo = (FAR struct fb_paninfo_s *)arg; |
| FAR struct fb_priv_s * priv; |
| irqstate_t flags; |
| int overlay; |
| |
| overlay = paninfo - paninfo->dev->paninfo - 1; |
| |
| flags = enter_critical_section(); |
| for (priv = paninfo->dev->head; priv; priv = priv->flink) |
| { |
| if (priv->overlay != overlay) |
| { |
| continue; |
| } |
| |
| /* Notify framebuffer is writable. */ |
| |
| poll_notify(priv->fds, CONFIG_VIDEO_FB_NPOLLWAITERS, POLLOUT); |
| } |
| |
| leave_critical_section(flags); |
| } |
| |
| #ifdef CONFIG_FB_SYNC |
| /**************************************************************************** |
| * Name: fb_sem_wait |
| ****************************************************************************/ |
| |
| static int fb_sem_wait(FAR struct fb_chardev_s *fb, |
| FAR struct fb_priv_s *priv, |
| int overlay) |
| { |
| struct fb_panelinfo_s panelinfo; |
| irqstate_t flags; |
| int ret; |
| |
| ret = fb_get_panelinfo(fb, &panelinfo, overlay); |
| if (ret < 0) |
| { |
| return ret; |
| } |
| |
| flags = enter_critical_section(); |
| |
| if (fb_paninfo_count(fb->vtable, overlay) == panelinfo.fbcount) |
| { |
| ret = nxsem_wait(&priv->wait); |
| } |
| |
| leave_critical_section(flags); |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: fb_sem_post |
| ****************************************************************************/ |
| |
| static void fb_sem_post(FAR struct fb_chardev_s *fb, int overlay) |
| { |
| FAR struct fb_priv_s * priv; |
| irqstate_t flags; |
| |
| DEBUGASSERT(fb != NULL); |
| |
| flags = enter_critical_section(); |
| |
| for (priv = fb->head; priv; priv = priv->flink) |
| { |
| if (priv->overlay != overlay) |
| { |
| continue; |
| } |
| |
| for (; ; ) |
| { |
| int semcount = 0; |
| |
| nxsem_get_value(&priv->wait, &semcount); |
| if (semcount >= 0) |
| { |
| break; |
| } |
| |
| /* Wakeup all waiters. */ |
| |
| nxsem_post(&priv->wait); |
| } |
| } |
| |
| leave_critical_section(flags); |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: fb_pollnotify |
| * |
| * Description: |
| * Notify the waiting thread that the framebuffer can be written. |
| * |
| * Input Parameters: |
| * vtable - Pointer to framebuffer's virtual table. |
| * |
| ****************************************************************************/ |
| |
| static void fb_pollnotify(FAR struct fb_chardev_s *fb, int overlay) |
| { |
| int id = overlay + 1; |
| |
| DEBUGASSERT(fb != NULL); |
| |
| DEBUGASSERT(id >= 0 && id < fb->paninfo_count); |
| |
| if (fb->vsyncoffset > 0) |
| { |
| wd_start(&fb->paninfo[id].wdog, fb->vsyncoffset, fb_do_pollnotify, |
| (wdparm_t)(&fb->paninfo[id])); |
| } |
| else |
| { |
| fb_do_pollnotify((wdparm_t)(&fb->paninfo[id])); |
| } |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: fb_notify_vsync |
| * Description: |
| * notify that the vsync comes. |
| * |
| * Input Parameters: |
| * vtable - Pointer to framebuffer's virtual table. |
| * |
| ****************************************************************************/ |
| |
| void fb_notify_vsync(FAR struct fb_vtable_s *vtable) |
| { |
| FAR struct fb_chardev_s *fb; |
| FAR struct fb_priv_s *priv; |
| irqstate_t flags; |
| |
| sched_note_mark(NOTE_TAG_GRAPHICS, __func__); |
| |
| fb = vtable->priv; |
| if (fb != NULL) |
| { |
| flags = enter_critical_section(); |
| for (priv = fb->head; priv; priv = priv->flink) |
| { |
| /* Notify that the vsync comes. */ |
| |
| poll_notify(priv->fds, CONFIG_VIDEO_FB_NPOLLWAITERS, POLLPRI); |
| } |
| |
| leave_critical_section(flags); |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: fb_peek_paninfo |
| * Description: |
| * Peek a frame from pan info queue of the specified overlay. |
| * |
| * Input Parameters: |
| * vtable - Pointer to framebuffer's virtual table. |
| * info - Pointer to pan info. |
| * overlay - Overlay index. |
| * |
| * Returned Value: |
| * Zero is returned on success; a negated errno value is returned on any |
| * failure. |
| ****************************************************************************/ |
| |
| int fb_peek_paninfo(FAR struct fb_vtable_s *vtable, |
| FAR union fb_paninfo_u *info, |
| int overlay) |
| { |
| FAR struct circbuf_s *panbuf; |
| FAR struct fb_chardev_s *fb; |
| irqstate_t flags; |
| ssize_t ret; |
| |
| /* Prevent calling before getting the vtable. */ |
| |
| fb = vtable->priv; |
| if (fb == NULL) |
| { |
| return -EINVAL; |
| } |
| |
| panbuf = fb_get_panbuf(fb, overlay); |
| if (panbuf == NULL) |
| { |
| return -EINVAL; |
| } |
| |
| flags = enter_critical_section(); |
| |
| /* Attempt to peek a frame from the vsync queue. */ |
| |
| ret = circbuf_peek(panbuf, info, sizeof(union fb_paninfo_u)); |
| DEBUGASSERT(ret <= 0 || ret == sizeof(union fb_paninfo_u)); |
| |
| /* Re-enable interrupts */ |
| |
| leave_critical_section(flags); |
| return ret <= 0 ? -ENOSPC : OK; |
| } |
| |
| /**************************************************************************** |
| * Name: fb_remove_paninfo |
| * Description: |
| * Remove a frame from pan info queue of the specified overlay. |
| * |
| * Input Parameters: |
| * vtable - Pointer to framebuffer's virtual table. |
| * overlay - Overlay index. |
| * |
| * Returned Value: |
| * Zero is returned on success; a negated errno value is returned on any |
| * failure. |
| ****************************************************************************/ |
| |
| int fb_remove_paninfo(FAR struct fb_vtable_s *vtable, int overlay) |
| { |
| FAR struct circbuf_s *panbuf; |
| FAR struct fb_chardev_s *fb; |
| irqstate_t flags; |
| ssize_t ret; |
| bool full; |
| |
| sched_note_mark(NOTE_TAG_GRAPHICS, __func__); |
| |
| fb = vtable->priv; |
| if (fb == NULL) |
| { |
| return -EINVAL; |
| } |
| |
| panbuf = fb_get_panbuf(fb, overlay); |
| if (panbuf == NULL) |
| { |
| return -EINVAL; |
| } |
| |
| flags = enter_critical_section(); |
| |
| full = (circbuf_space(panbuf) == 0); |
| |
| /* Attempt to take a frame from the pan info. */ |
| |
| ret = circbuf_skip(panbuf, sizeof(union fb_paninfo_u)); |
| DEBUGASSERT(ret <= 0 || ret == sizeof(union fb_paninfo_u)); |
| |
| /* Re-enable interrupts */ |
| |
| leave_critical_section(flags); |
| |
| if (ret == sizeof(union fb_paninfo_u)) |
| { |
| fb_pollnotify(fb, overlay); |
| if (full) |
| { |
| #ifdef CONFIG_FB_SYNC |
| fb_sem_post(fb, overlay); |
| #endif |
| } |
| } |
| |
| return ret <= 0 ? -ENOSPC : OK; |
| } |
| |
| /**************************************************************************** |
| * Name: fb_paninfo_count |
| * Description: |
| * Get pan info count of specified overlay pan info queue. |
| * |
| * Input Parameters: |
| * vtable - Pointer to framebuffer's virtual table. |
| * overlay - Overlay index. |
| * |
| * Returned Value: |
| * a non-negative value is returned on success; a negated errno value is |
| * returned on any failure. |
| ****************************************************************************/ |
| |
| int fb_paninfo_count(FAR struct fb_vtable_s *vtable, int overlay) |
| { |
| FAR struct circbuf_s *panbuf; |
| FAR struct fb_chardev_s *fb; |
| irqstate_t flags; |
| ssize_t ret; |
| |
| /* Prevent calling before getting the vtable. */ |
| |
| fb = vtable->priv; |
| if (fb == NULL) |
| { |
| return -EINVAL; |
| } |
| |
| panbuf = fb_get_panbuf(fb, overlay); |
| if (panbuf == NULL) |
| { |
| return -EINVAL; |
| } |
| |
| flags = enter_critical_section(); |
| |
| ret = circbuf_used(panbuf) / sizeof(union fb_paninfo_u); |
| |
| leave_critical_section(flags); |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: fb_register_device |
| * |
| * Description: |
| * Register the framebuffer character device at /dev/fbN where N is the |
| * display number if the devices supports only a single plane. If the |
| * hardware supports multiple color planes, then the device will be |
| * registered at /dev/fbN.M where N is the again display number but M |
| * is the display plane. |
| * |
| * Input Parameters: |
| * display - The display number for the case of boards supporting multiple |
| * displays or for hardware that supports multiple |
| * layers (each layer is consider a display). Typically zero. |
| * plane - Identifies the color plane on hardware that supports separate |
| * framebuffer "planes" for each color component. |
| * vtable - Pointer to framebuffer's virtual table. |
| * |
| * Returned Value: |
| * Zero (OK) is returned success; a negated errno value is returned on any |
| * failure. |
| * |
| ****************************************************************************/ |
| |
| int fb_register_device(int display, int plane, |
| FAR struct fb_vtable_s *vtable) |
| { |
| FAR struct fb_chardev_s *fb; |
| struct fb_panelinfo_s panelinfo; |
| #ifdef CONFIG_VIDEO_FB_SPLASHSCREEN |
| struct fb_planeinfo_s pinfo; |
| #endif |
| struct fb_videoinfo_s vinfo; |
| char devname[16]; |
| int nplanes; |
| int ret; |
| ssize_t i; |
| |
| /* Allocate a framebuffer state instance */ |
| |
| fb = kmm_zalloc(sizeof(struct fb_chardev_s)); |
| if (fb == NULL) |
| { |
| return -ENOMEM; |
| } |
| |
| DEBUGASSERT((unsigned)plane <= UINT8_MAX); |
| DEBUGASSERT(vtable != NULL); |
| fb->plane = plane; |
| fb->vtable = vtable; |
| |
| /* Initialize the frame buffer instance. */ |
| |
| DEBUGASSERT(vtable->getvideoinfo != NULL); |
| ret = vtable->getvideoinfo(vtable, &vinfo); |
| if (ret < 0) |
| { |
| gerr("ERROR: getvideoinfo() failed: %d\n", ret); |
| goto errout_with_fb; |
| } |
| |
| nplanes = vinfo.nplanes; |
| DEBUGASSERT(vinfo.nplanes > 0 && (unsigned)plane < vinfo.nplanes); |
| |
| #ifdef CONFIG_FB_OVERLAY |
| fb->paninfo_count = vinfo.noverlays; |
| #endif |
| |
| /* Add the primary framebuffer */ |
| |
| fb->paninfo_count += 1; |
| fb->paninfo = kmm_zalloc(sizeof(struct fb_paninfo_s) * fb->paninfo_count); |
| |
| if (fb->paninfo == NULL) |
| { |
| gerr("ERROR: alloc panbuf failed\n"); |
| goto errout_with_fb; |
| } |
| |
| for (i = 0; i < fb->paninfo_count; i++) |
| { |
| ret = fb_get_panelinfo(fb, &panelinfo, i - 1); |
| if (ret < 0) |
| { |
| goto errout_with_paninfo; |
| } |
| |
| ret = circbuf_init(&(fb->paninfo[i].buf), NULL, panelinfo.fbcount |
| * sizeof(union fb_paninfo_u)); |
| |
| DEBUGASSERT(ret == 0); |
| |
| fb->paninfo[i].dev = fb; |
| |
| /* Clear the framebuffer memory */ |
| |
| memset(panelinfo.fbmem, 0, panelinfo.fblen); |
| } |
| |
| /* Register the framebuffer device */ |
| |
| if (nplanes < 2) |
| { |
| snprintf(devname, sizeof(devname), "/dev/fb%d", display); |
| } |
| else |
| { |
| snprintf(devname, sizeof(devname), "/dev/fb%d.%d", display, plane); |
| } |
| |
| ret = register_driver(devname, &g_fb_fops, 0666, fb); |
| |
| if (ret < 0) |
| { |
| gerr("ERROR: register_driver() failed: %d\n", ret); |
| goto errout_with_paninfo; |
| } |
| |
| #ifdef CONFIG_VIDEO_FB_SPLASHSCREEN |
| ret = fb_get_planeinfo(fb, &pinfo, 0); |
| if (ret < 0) |
| { |
| goto errout_with_paninfo; |
| } |
| |
| ret = fb_splash_fill(&vinfo, &pinfo, SPLASH_BGCOL); |
| if (ret < 0) |
| { |
| goto errout_with_paninfo; |
| } |
| |
| ret = fb_splashscreen(&vinfo, &pinfo); |
| if (ret < 0) |
| { |
| goto errout_with_paninfo; |
| } |
| |
| if (SPLASH_SLEEP != 0) |
| { |
| nxsched_sleep(SPLASH_SLEEP); |
| } |
| |
| # ifdef VIDEO_FB_SPLASHSCREEN_CLR_ON_EXIT |
| ret = fb_splash_fill(&vinfo, &pinfo, 0); /* Fill with black to clear LCD */ |
| if (ret < 0) |
| { |
| goto errout_with_paninfo; |
| } |
| # endif |
| #endif |
| |
| vtable->priv = fb; |
| return OK; |
| |
| errout_with_paninfo: |
| while (i-- > 0) |
| { |
| circbuf_uninit(&(fb->paninfo[i].buf)); |
| } |
| |
| kmm_free(fb->paninfo); |
| errout_with_fb: |
| kmm_free(fb); |
| return ret; |
| } |