/**************************************************************************** * apps/graphics/ft80x/ft80x_ramcmd.c * * Copyright (C) 2018 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * 3. Neither the name NuttX nor the names of its contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include "graphics/ft80x.h" #include "ft80x.h" /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: ft80x_ramcmd_append * * Description: * Append new display list data to RAM CMD * * Input Parameters: * fd - The file descriptor of the FT80x device. Opened by the caller * with write access. * data - A pointer to the start of the data to be append to RAM CMD. * len - The number of bytes to be appended. * * Returned Value: * Zero (OK) on success. A negated errno value on failure. * ****************************************************************************/ int ft80x_ramcmd_append(int fd, FAR const void *data, size_t len) { struct ft80x_relmem_s wrdesc; FAR const uint8_t *src; ssize_t remaining; size_t wrsize; uint16_t offset; uint16_t maxsize; int ret; DEBUGASSERT(data != NULL && ((uintptr_t)data & 3) == 0 && len > 0 && (len & 3) == 0); /* Loop until all of the display list commands have been transferred to * FIFO. */ src = data; remaining = len; do { /* Write the number of bytes remaining to be transferred */ wrsize = remaining; /* Get the amount of free space in the FIFO. */ maxsize = 0; ret = ft80x_ramcmd_freespace(fd, &offset, &maxsize); if (ret < 0) { ft80x_err("ERROR: ft80x_ramcmd_freespace() failed: %d\n", ret); return ret; } /* If the FIFO full? * * REVISIT: Will the FIFO be consumed as we generate the display * list? Is it fatal if it becomes full? Or could we wait for space * to free up. */ if (maxsize == 0) { #if 1 /* Yes.. wait for the FIFO to empty, then try again. * * REVISIT: Would it not be sufficient to wait only until the * FIFO is not full? */ ft80x_warn("WARNING: FIFO is full: %d\n", ret); ret = ft80x_ramcmd_waitfifoempty(fd); if (ret < 0) { ft80x_err("ERROR: ft80x_ramcmd_waitfifoempty() failed: %d\n", ret); return ret; } continue; #else ft80x_err("ERROR: FIFO is full: %d\n", ret); return -ENOSPC; #endif } /* Limit the write size to the size of the available FIFO memory */ if (wrsize > (size_t)maxsize) { wrsize = (size_t)maxsize; } /* Perform the transfer */ wrdesc.offset = offset; wrdesc.nbytes = wrsize; wrdesc.value = (FAR void *)src; /* Discards 'const' qualifier */ ret = ioctl(fd, FT80X_IOC_PUTRAMCMD, (unsigned long)((uintptr_t)&wrdesc)); if (ret < 0) { int errcode = errno; ft80x_err("ERROR: ioctl() FT80X_IOC_PUTRAMCMD failed: %d\n", errcode); return -errcode; } /* Update the command FIFO */ ret = ft80x_putreg16(fd, FT80X_REG_CMD_WRITE, offset + wrsize); if (ret < 0) { ft80x_err("ERROR: ft80x_putreg16() failed: %d\n", ret); return ret; } /* Wait for the FIFO to empty */ if (remaining > wrsize) { ret = ft80x_ramcmd_waitfifoempty(fd); if (ret < 0) { ft80x_err("ERROR: ft80x_ramcmd_waitfifoempty() failed: %d\n", ret); return ret; } } /* Set up for the next time through the loop. */ remaining -= wrsize; src += wrsize; } while (remaining > 0); return OK; } /**************************************************************************** * Name: ft80x_ramcmd_freespace * * Description: * Return the free space in RAM CMD memory * * Input Parameters: * fd - The file descriptor of the FT80x device. Opened by the caller * with write access. * offset - Pointer to location to return the write offset to use if the * FIFO is not full. * avail - Pointer to location to return the FIFO free space * * Returned Value: * The (positive) number of free bytes in RAM CMD on success. A negated * errno value is returned on any failure. * ****************************************************************************/ uint16_t ft80x_ramcmd_freespace(int fd, FAR uint16_t *offset, FAR uint16_t *avail) { uint32_t regs[2]; int ret; /* Read both the FT80X_REG_CMD_WRITE and FT80X_REG_CMD_READ registers. */ ret = ft80x_getregs(fd, FT80X_REG_CMD_READ, 2, regs); if (ret < 0) { ft80x_err("ERROR: ft80x_getregs failed: %d\n", ret); return ret; } /* Return the free space in the FIFO. NOTE that 4 bytes are not available */ *offset = regs[1] & FT80X_CMDFIFO_MASK; *avail = (FT80X_CMDFIFO_SIZE - 4) - ((regs[1] - regs[0]) & FT80X_CMDFIFO_MASK); return OK; } /**************************************************************************** * Name: ft80x_ramcmd_waitfifoempty * * Description: * Wait until co processor completes the operation and the FIFO is again * empty. * * Input Parameters: * fd - The file descriptor of the FT80x device. Opened by the caller * with write access. * * Returned Value: * Zero (OK) on success. A negated errno value on failure. * ****************************************************************************/ int ft80x_ramcmd_waitfifoempty(int fd) { struct ft80x_notify_s notify; struct timespec timeout; sigset_t set; uint32_t regs[2]; uint16_t head; uint16_t tail; int ret; /* Block the signal so that it will pend if received asynchronously */ (void)sigemptyset(&set); (void)sigaddset(&set, CONFIG_GRAPHICS_FT80X_CMDEMPTY_SIGNAL); ret = pthread_sigmask(SIG_BLOCK, &set, NULL); if (ret < 0) { ret = -errno; ft80x_err("ERROR: pthread_sigmask for signal %d failed: %d\n", CONFIG_GRAPHICS_FT80X_CMDEMPTY_SIGNAL, ret); return ret; } notify.event.sigev_notify = SIGEV_SIGNAL; notify.event.sigev_signo = CONFIG_GRAPHICS_FT80X_CMDEMPTY_SIGNAL; notify.pid = getpid(); notify.id = FT80X_NOTIFY_CMDEMPTY; notify.enable = false; for (; ; ) { /* Read both the FT80X_REG_CMD_WRITE and FT80X_REG_CMD_READ registers. */ ret = ft80x_getregs(fd, FT80X_REG_CMD_READ, 2, regs); if (ret < 0) { ft80x_err("ERROR: ft80x_getregs failed: %d\n", ret); return ret; } /* Check if the FIFO is already empty */ head = regs[1] & FT80X_CMDFIFO_MASK; tail = regs[0] & FT80X_CMDFIFO_MASK; ft80x_info("head=%u tail=%u\n", head, tail); if (head == tail) { /* The FIFO is empty... return success */ ret = OK; break; } /* Set up to receive a notification when the CMD FIFO becomes empty * NOTE that there is a race condition here between the preceding test * and the time when the event is registered. We catch this with a * timeout below. */ notify.enable = true; ret = ioctl(fd, FT80X_IOC_EVENTNOTIFY, (unsigned long)((uintptr_t)¬ify)); if (ret < 0) { ret = -errno; ft80x_err("ERROR: ioctl(FT80X_IOC_EVENTNOTIFY) failed: %d\n", ret); break; } /* Now wait for the signal event (or a timeout) */ timeout.tv_sec = 0; timeout.tv_nsec = 400 * 1000 * 1000; ret = sigtimedwait(&set, NULL, &timeout); /* Make sure that the event notification is again disabled */ notify.enable = false; (void)ioctl(fd, FT80X_IOC_EVENTNOTIFY, (unsigned long)((uintptr_t)¬ify)); /* Check if the signal was received correctly or if the timeout occurred. */ if (ret < 0) { int errcode = errno; if (errcode != EAGAIN) { ft80x_err("ERROR: ioctl(FT80X_IOC_EVENTNOTIFY) failed: %d\n", errcode); ret = -errcode; break; } } } (void)pthread_sigmask(SIG_UNBLOCK, &set, NULL); return ret; }