/**************************************************************************** * apps/graphics/ft80x/ft80x_ramcmd.c * * 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. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #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 */ sigemptyset(&set); 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; 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; } } } pthread_sigmask(SIG_UNBLOCK, &set, NULL); return ret; }