
/*
 * video.c
 *
 * ============================================================================
 * Copyright (c) Texas Instruments Inc 2005
 *
 * Use of this software is controlled by the terms and conditions found in the
 * license agreement under which this software has been supplied or provided.
 * ============================================================================
 */

/* Standard Linux headers */
#include <stdio.h>
#include <math.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <asm/types.h>
#include <linux/videodev2.h>

/* Davinci specific kernel headers */
#include <video/davincifb.h>
#include <media/davinci_vpfe.h>
#include <media/tvp5146.h>

/* Codec Engine headers */
#include <xdc/std.h>
#include <ti/sdo/ce/Engine.h>
#include <ti/sdo/ce/osal/Memory.h>

/* Demo headers */
#include <rendezvous.h>
#include <fifoutil.h>
#include "rotate_demo.h"
#include "display.h"
#include "video.h"

#ifdef _USE_CUSTOM_SS
#include <ti/sdo/apps/extensions/rotate/rotate.h>
#else
#include "imgdec/rotate.h"
#endif

/* The levels of initialization */
#define INFIFOOPENED             0x1
#define OUTFIFOOPENED            0x2
#define DISPLAYTHREADCREATED     0x4
#define CAPTUREDEVICEINITIALIZED 0x8
#define ENGINEOPENED             0x10
#define ROTATEALGOCREATED        0x20
#define DISPLAYBUFFERSALLOCATED  0x40

/* Macro for clearing structures */
#define CLEAR(x) memset (&(x), 0, sizeof (x))

/* Number of buffers between video thread and display thread */
#define DISPLAY_BUFFERS     3

/* Black color in UYVY format */
#define UYVY_BLACK          0x10801080

/* Triple buffering for the capture driver */
#define NUM_BUFS                 3

#define ALGO_NAME           "rotate"
   
/* Describes a capture frame buffer */
typedef struct VideoBuffer {
    void   *start;
    size_t  length;
} VideoBuffer;

/* Local function prototypes */
static int  rotateAlgCreate(Engine_Handle hEngine,
                                 ROTATE_Handle *hRotatePtr,
                                 int imageSize);

static int color_rotate(ROTATE_Handle handle, char *inBuf, char *outBuf, 
    int imageSize);

static int  initCaptureDevice(VideoBuffer **vidBufsPtr, int *numVidBufsPtr,
                              int svideoInput, int captureWidth,
                              int captureHeight);
static void cleanupCaptureDevice(int fd, VideoBuffer *vidBufs, int numVidBufs);

/******************************************************************************
 * rotateAlgCreate
 ******************************************************************************/
static int rotateAlgCreate(Engine_Handle hEngine,
                                ROTATE_Handle *hRotatePtr, 
                                int imageSize)
{
    ROTATE_Params params = ROTATE_PARAMS;
    params.irotateParams.reverseImage = FALSE;  /* Do not invert the colors */
    params.maxImageSize = imageSize;            /* Size of image buffers */
                                                                                
    /* create video rotate algorithm on the engine */
    *hRotatePtr = ROTATE_create(hEngine, ALGO_NAME, &params);
                                                                                
    /* if video rotate algorithm handle is NULL, return -1 (failure) */
    if(*hRotatePtr == NULL){
        ERR("Can't create rotate algorithm: %s\n", ALGO_NAME);
        return FAILURE;
    }
                                                                                
    return SUCCESS;
}


/******************************************************************************
 * color_rotate
 ******************************************************************************/
static int color_rotate(ROTATE_Handle handle, char *inBuf, char *outBuf, 
    int imageSize)
{
    int status;
    int cosine;
    int sine;
    static double angle;
    const double PI = 3.1415927;
                                                                                
    /*
     * Slowly increment angle for each frame to achieve gradual color
     * rotation effect
     */
    angle += (PI / 30.0);
    if (angle >= (2 * PI)) {
      angle = 0;
    }
    cosine = 126*256 * cos(angle);
    sine = 126*256 * sin(angle);

    /* Call the process function to rotate the video buffer */
    status = ROTATE_process(handle,
                            inBuf,
                            outBuf,
                            imageSize,
                            cosine,
                            sine
                            );
                                                                                
    if(status != ROTATE_EOK){
        printf("ERROR:  ROTATE_process returned ROTATE_EFAIL \n");
        return FAILURE;
    }
                                                                                
    /* Else, return successful completion */
    return SUCCESS;
}

/******************************************************************************
 * initCaptureDevice
 ******************************************************************************/
static int initCaptureDevice(VideoBuffer **vidBufsPtr, int *numVidBufsPtr,
                             int svideoInput, int captureWidth,
                             int captureHeight)
{
    struct v4l2_requestbuffers  req;
    struct v4l2_capability      cap;
    struct v4l2_cropcap         cropCap;
    struct v4l2_crop            crop;
    struct v4l2_format          fmt;
    struct v4l2_buffer          buf;
    v4l2_std_id                 std;
    enum v4l2_buf_type          type;
    int                         input;
    int                         fd;
    int                         ret;
    VideoBuffer                *buffers;
    int                         numBufs;

    /* Open video capture device */
    fd = open(V4L2_DEVICE, O_RDWR | O_NONBLOCK, 0);

    if (fd == -1) {
        ERR("Cannot open %s (%s)\n", V4L2_DEVICE, strerror(errno));
        return FAILURE;
    }

    /* Select the video input */
    if (svideoInput == TRUE) {
        input = TVP5146_AMUX_SVIDEO;
    }
    else {
        input = TVP5146_AMUX_COMPOSITE;
    }

    if (ioctl(fd, VIDIOC_S_INPUT, &input) == -1) {
        ERR("Failed to set video input to %d\n", input);
        return FAILURE;
    }

    DBG("Set the capture input to id %d\n", input);

    /* Query for capture device capabilities */
    if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == -1) {
        if (errno == EINVAL) {
            ERR("%s is no V4L2 device\n", V4L2_DEVICE);
            return FAILURE;
        }
        ERR("Failed VIDIOC_QUERYCAP on %s (%s)\n", V4L2_DEVICE,
            strerror(errno));
        return FAILURE;
    }

    if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
        ERR("%s is no video capture device\n", V4L2_DEVICE);
        return FAILURE;
    }

    if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
        ERR("%s does not support streaming i/o\n", V4L2_DEVICE);
        return FAILURE;
    }

    cropCap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if (ioctl(fd, VIDIOC_CROPCAP, &cropCap) == -1) {
        ERR("VIDIOC_CROPCAP failed %d, %s\n", errno, strerror(errno));
        return FAILURE;
    }

    /* Auto detect PAL or NTSC using the capture driver as sanity check */ 
    std = VPFE_STD_AUTO;
    if(ioctl(fd, VIDIOC_S_STD, &std) == -1) {
        ERR("VIDIOC_S_STD (auto) failed on %s (%s)\n", V4L2_DEVICE,
                                                       strerror(errno));
        return FAILURE;
    }

    do {
        ret = ioctl(fd, VIDIOC_QUERYSTD, &std);
    } while (ret == -1 && errno == EAGAIN);

    if (ret == -1) {
        ERR("VIDIOC_QUERYSTD failed on %s (%s)\n", V4L2_DEVICE,
                                                   strerror(errno));
        return FAILURE;
    }

    switch (std) {
        case V4L2_STD_NTSC:
            DBG("NTSC camera detected\n");
            if (gblGetYFactor() == PALSTD) {
                ERR("NTSC camera connected but PAL selected.\n");
                return FAILURE;
            }
            break;
        case V4L2_STD_PAL:
            DBG("PAL camera detected\n");
            if (gblGetYFactor() == NTSCSTD) {
                ERR("PAL camera connected but NTSC selected.\n");
                return FAILURE;
            }
            break;
        default:
            ERR("Camera (%s) using unsupported video standard\n", V4L2_DEVICE);
            return FAILURE;
    }

    /* Use either NTSC or PAL depending on display kernel parameter */
    std = gblGetYFactor() == NTSCSTD ? V4L2_STD_NTSC : V4L2_STD_PAL;
    if(ioctl(fd, VIDIOC_S_STD, &std) == -1) {
        ERR("VIDIOC_S_STD failed on %s (%s)\n", V4L2_DEVICE,
                                                strerror(errno));
        return FAILURE;
    }

    /* Set the video capture image format */
    CLEAR(fmt);
    fmt.type                = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
    fmt.fmt.pix.field       = V4L2_FIELD_INTERLACED;
    fmt.fmt.pix.width       = D1_WIDTH;
    fmt.fmt.pix.height      = D1_HEIGHT;

    /* Set the video capture format */
    if (ioctl(fd, VIDIOC_S_FMT, &fmt) == -1) {
        ERR("VIDIOC_S_FMT failed on %s (%s)\n", V4L2_DEVICE,
                                                strerror(errno));
        return FAILURE;
    }

    crop.type     = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    crop.c.left   = D1_WIDTH / 2 - captureWidth / 2;
    crop.c.top    = D1_HEIGHT / 2 - captureHeight / 2;
    crop.c.width  = captureWidth;
    crop.c.height = captureHeight;

    /* Crop the image depending on requested image size */
    if (ioctl(fd, VIDIOC_S_CROP, &crop) == -1) {
        ERR("VIDIOC_S_CROP failed %d, %s\n", errno, strerror(errno));
        return FAILURE;
    }

    printf("Capturing %dx%d video (cropped to %dx%d)\n",
           fmt.fmt.pix.width, fmt.fmt.pix.height, crop.c.width, crop.c.height);

    CLEAR(req);

    req.count  = NUM_BUFS;
    req.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    req.memory = V4L2_MEMORY_MMAP;

    /* Allocate buffers in the capture device driver */
    if (ioctl(fd, VIDIOC_REQBUFS, &req) == -1) {
        ERR("VIDIOC_REQBUFS failed on %s (%s)\n", V4L2_DEVICE,
                                                  strerror(errno));
        return FAILURE;
    }

    DBG("%d capture buffers were successfully allocated.\n", req.count);

    if (req.count < NUM_BUFS) {
        ERR("Insufficient buffer memory on %s\n", V4L2_DEVICE);
        return FAILURE;
    }

    buffers = calloc(req.count, sizeof(*buffers));

    if (!buffers) {
        ERR("Failed to allocate memory for capture buffer structs.\n");
        return FAILURE;
    }

    /* Map the allocated buffers to user space */
    for (numBufs = 0; numBufs < req.count; numBufs++) {
        CLEAR(buf);

        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buf.memory = V4L2_MEMORY_MMAP;
        buf.index = numBufs;

        if (ioctl(fd, VIDIOC_QUERYBUF, &buf) == -1) {
            ERR("Failed VIDIOC_QUERYBUF on %s (%s)\n", V4L2_DEVICE,
                                                       strerror(errno));
            return FAILURE;
        }

        buffers[numBufs].length = buf.length;
        buffers[numBufs].start = mmap(NULL,
                                      buf.length,
                                      PROT_READ | PROT_WRITE,
                                      MAP_SHARED,
                                      fd, buf.m.offset);

        if (buffers[numBufs].start == MAP_FAILED) {
            ERR("Failed to mmap buffer on %s (%s)\n", V4L2_DEVICE,
                                                      strerror(errno));
            return FAILURE;
        }

        DBG("Capture buffer %d mapped to address %#lx\n", numBufs,
            (unsigned long) buffers[numBufs].start);

        if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) {
            ERR("VIODIOC_QBUF failed on %s (%s)\n", V4L2_DEVICE,
                                                    strerror(errno));
            return FAILURE;
        }
    }

    /* Start the video streaming */
    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    if (ioctl(fd, VIDIOC_STREAMON, &type) == -1) {
        ERR("VIDIOC_STREAMON failed on %s (%s)\n", V4L2_DEVICE,
                                                   strerror(errno));
        return FAILURE;
    }

    *vidBufsPtr = buffers;
    *numVidBufsPtr = numBufs;

    return fd;
}

/******************************************************************************
 * cleanupCaptureDevice
 ******************************************************************************/
static void cleanupCaptureDevice(int fd, VideoBuffer *vidBufs, int numVidBufs)
{
    enum v4l2_buf_type type;
    unsigned int       i;

    /* Shut off the video capture */
    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if (ioctl(fd, VIDIOC_STREAMOFF, &type) == -1) {
        ERR("VIDIOC_STREAMOFF failed (%s)\n", strerror(errno));
    }

    /* Unmap the capture frame buffers from user space */
    for (i = 0; i < numVidBufs; ++i) {
        if (munmap(vidBufs[i].start, vidBufs[i].length) == -1) {
            ERR("Failed to unmap capture buffer %d\n", i);
        }
    }

    free(vidBufs);

    if (close(fd) == -1) {
        ERR("Failed to close capture device (%s)\n", strerror(errno));
    }
}

/******************************************************************************
 * videoThrFxn
 ******************************************************************************/
void *videoThrFxn(void *arg)
{
    BufferElement      flush          = { DISPLAY_FLUSH };
    Engine_Handle      hEngine        = NULL;
    unsigned int       initMask       = 0;
    VideoEnv          *envp           = (VideoEnv *) arg;
    void              *status         = THREAD_SUCCESS;
    int                captureFd      = 0;
    int                numDisplayBufs = 0;
    BufferElement      bufferElements[DISPLAY_BUFFERS];
    struct sched_param schedParam;
    ROTATE_Handle      hRotate;
    struct v4l2_buffer v4l2buf;
    DisplayEnv         displayEnv;
    unsigned int       numVidBufs;
    VideoBuffer       *vidBufs;
    void              *ret;
    int                imageSize;
    pthread_t          displayThread;
    pthread_attr_t     attr;
    BufferElement      e;
    int                i;

    imageSize = envp->imageWidth * envp->imageHeight * SCREEN_BPP / 8;

    /* Open the input fifo */
    if (FifoUtil_open(&displayEnv.inFifo,
                      sizeof(BufferElement)) == FIFOUTIL_FAILURE) {
        ERR("Failed to open input fifo\n");
        cleanup(THREAD_FAILURE);
    }

    initMask |= INFIFOOPENED;

    /* Open the output fifo */
    if (FifoUtil_open(&displayEnv.outFifo,
                      sizeof(BufferElement)) == FIFOUTIL_FAILURE) {
        ERR("Failed to open output fifo\n");
        cleanup(THREAD_FAILURE);
    }

    initMask |= OUTFIFOOPENED;

    /* Initialize the priming synchronization mutex */
    pthread_mutex_init(&displayEnv.prime, NULL);

    /* Initialize the thread attributes */
    if (pthread_attr_init(&attr)) {
        ERR("Failed to initialize thread attrs\n");
        cleanup(THREAD_FAILURE);
    }

    /* Force the thread to use custom scheduling attributes */
    if (pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED)) {
        ERR("Failed to set schedule inheritance attribute\n");
        cleanup(THREAD_FAILURE);
    }

    /* Set the thread to be fifo real time scheduled */
    if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO)) {
        ERR("Failed to set FIFO scheduling policy\n");
        cleanup(THREAD_FAILURE);
    }

    /* Set the thread priority */
    schedParam.sched_priority = sched_get_priority_max(SCHED_FIFO);
    if (pthread_attr_setschedparam(&attr, &schedParam)) {
        ERR("Failed to set scheduler parameters\n");
        cleanup(THREAD_FAILURE);
    }

    /* Create the thread */
    displayEnv.hRendezvous = envp->hRendezvous;

    if (pthread_create(&displayThread, &attr, displayThrFxn, &displayEnv)) {
        ERR("Failed to create display thread\n");
        cleanup(THREAD_FAILURE);
    }

    initMask |= DISPLAYTHREADCREATED;

    /* Initialize the video capture device */
    captureFd = initCaptureDevice(&vidBufs, &numVidBufs, envp->svideoInput,
                                  envp->imageWidth, envp->imageHeight);

    if (captureFd == FAILURE) {
        cleanup(THREAD_FAILURE);
    }

    DBG("Video capture initialized and started\n");

    initMask |= CAPTUREDEVICEINITIALIZED;

    /* Reset, load, and start DSP Engine */
    if ((hEngine = Engine_open(ENGINE_NAME, NULL, NULL)) == NULL) {
        ERR("Failed to open codec engine %s\n", ENGINE_NAME);
        cleanup(THREAD_FAILURE);
    }

    DBG("Codec Engine opened in video thread\n");

    initMask |= ENGINEOPENED;

    /* Allocate and initialize video decoder on the engine */
    if (rotateAlgCreate(hEngine, &hRotate, imageSize) == FAILURE) {
        cleanup(THREAD_FAILURE);
    }

    DBG("Rotate Algorithm created\n");

    initMask |= ROTATEALGOCREATED;

    memset(bufferElements, 0, sizeof(BufferElement) * DISPLAY_BUFFERS);

    for (i=0; i < DISPLAY_BUFFERS; i++) {
        bufferElements[i].frameBuffer = (char *)
            Memory_contigAlloc(imageSize, Memory_DEFAULTALIGNMENT);

        if (bufferElements[i].frameBuffer == NULL) {
            ERR("Failed to allocate contiguous memory block.\n");
            cleanup(THREAD_FAILURE);
        }

        DBG("Contiguous buffer allocated at physical address 0x%lx\n",
            Memory_getPhysicalAddress(bufferElements[i].frameBuffer));
    }

    initMask |= DISPLAYBUFFERSALLOCATED;

    /* Signal that initialization is done and wait for other threads */
    Rendezvous_meet(envp->hRendezvous);

    /* Lock the display priming mutex */
    pthread_mutex_lock(&displayEnv.prime);

    /* Tell the display thread that we are priming */
    e.id = DISPLAY_PRIME;
    if (FifoUtil_put(&displayEnv.outFifo, &e) == FIFOUTIL_FAILURE) {
        ERR("Failed to put buffer to output fifo\n");
        pthread_mutex_unlock(&displayEnv.prime);
        cleanup(THREAD_FAILURE);
    }

    /* Prime the display thread */
    for (i=0; i < DISPLAY_BUFFERS; i++) {
        CLEAR(v4l2buf);
        v4l2buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        v4l2buf.memory = V4L2_MEMORY_MMAP;

        /* Dequeue a frame buffer from the capture device driver */
        if (ioctl(captureFd, VIDIOC_DQBUF, &v4l2buf) == -1) {
            ERR("VIDIOC_DQBUF failed (%s)\n", strerror(errno));
            breakLoop(THREAD_FAILURE);
        }

        if (envp->passThrough) {
            /* Copy the buffer without processing */
            memcpy(bufferElements[i].frameBuffer, vidBufs[v4l2buf.index].start,
                   imageSize);

            /* Issue capture buffer back to capture device driver */
            if (ioctl(captureFd, VIDIOC_QBUF, &v4l2buf) == -1) {
                ERR("VIDIOC_QBUF failed (%s)\n", strerror(errno));
                breakLoop(THREAD_FAILURE);
            }
        }
        else {
            /* Process input buffer */
            if (color_rotate(hRotate, vidBufs[v4l2buf.index].start,
                                  bufferElements[i].frameBuffer,
                                  imageSize) == FAILURE) {
                breakLoop(THREAD_FAILURE);
            }

            /* Issue capture buffer back to capture device driver */
            if (ioctl(captureFd, VIDIOC_QBUF, &v4l2buf) == -1) {
                ERR("VIDIOC_QBUF failed (%s)\n", strerror(errno));
                breakLoop(THREAD_FAILURE);
            }

        }

        bufferElements[i].width  = envp->imageWidth;
        bufferElements[i].height = envp->imageHeight;

        /* Send frame buffer to display thread */
        if (FifoUtil_put(&displayEnv.outFifo,
                         &bufferElements[i]) == FIFOUTIL_FAILURE) {
            ERR("Failed to put buffer in output fifo\n");
            breakLoop(THREAD_FAILURE);
        }

        numDisplayBufs++;
    }

    /* Release the display thread */
    pthread_mutex_unlock(&displayEnv.prime);

    DBG("Entering video main loop.\n");
    while (!gblGetQuit()) {
        /* Receive a buffer with a displayed frame from the display thread */
        if (FifoUtil_get(&displayEnv.inFifo, &e) == FIFOUTIL_FAILURE) {
            ERR("Failed to get buffer from input fifo\n");
            breakLoop(THREAD_FAILURE);
        }

        numDisplayBufs--;

        /* Is the display thread flushing the pipe? */
        if (e.id == DISPLAY_FLUSH) {
            breakLoop(THREAD_SUCCESS);
        }

        CLEAR(v4l2buf);
        v4l2buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        v4l2buf.memory = V4L2_MEMORY_MMAP;

        /* Dequeue a frame buffer from the capture device driver */
        if (ioctl(captureFd, VIDIOC_DQBUF, &v4l2buf) == -1) {
            ERR("VIDIOC_DQBUF failed (%s)\n", strerror(errno));
            breakLoop(THREAD_FAILURE);
        }

        if (envp->passThrough) {
            /* Copy the buffer without processing */
            memcpy(e.frameBuffer, vidBufs[v4l2buf.index].start, imageSize);

            /* Issue capture buffer back to capture device driver */
            if (ioctl(captureFd, VIDIOC_QBUF, &v4l2buf) == -1) {
                ERR("VIDIOC_QBUF failed (%s)\n", strerror(errno));
                breakLoop(THREAD_FAILURE);
            }
        }
        else {
            /* Process input buffer */
            if (color_rotate(hRotate, vidBufs[v4l2buf.index].start,
                e.frameBuffer, imageSize) == FAILURE) {
                breakLoop(THREAD_FAILURE);
            }

            /* Issue capture buffer back to capture device driver */
            if (ioctl(captureFd, VIDIOC_QBUF, &v4l2buf) == -1) {
                ERR("VIDIOC_QBUF failed (%s)\n", strerror(errno));
                breakLoop(THREAD_FAILURE);
            }
        }

        /* Send frame buffer to display thread */
        if (FifoUtil_put(&displayEnv.outFifo, &e) == FIFOUTIL_FAILURE) {
            ERR("Failed to put buffer in output fifo\n");
            breakLoop(THREAD_FAILURE);
        }

        numDisplayBufs++;

    }


cleanup:
    /* Make sure the other threads aren't waiting for init to complete */
    Rendezvous_force(envp->hRendezvous);

    /* Make sure the video thread isn't stuck in FifoUtil_get() */
    FifoUtil_put(&displayEnv.outFifo, &flush);

    /* Drain the display thread */
    while (numDisplayBufs-- > 0 && e.id != DISPLAY_FLUSH) {
        if (FifoUtil_get(&displayEnv.inFifo, &e) == FIFOUTIL_FAILURE) {
            ERR("Failed to get buffer from input fifo\n");
            break;
        }
    }

    /* Clean up the video thread */
    if (initMask & DISPLAYBUFFERSALLOCATED) {
        for (i=0; i < DISPLAY_BUFFERS; i++) {
            if (bufferElements[i].frameBuffer) {
                Memory_contigFree(bufferElements[i].frameBuffer,
                                  imageSize);
            }
        }
    }

    if (initMask & ROTATEALGOCREATED) {
        ROTATE_delete(hRotate);
    }

    if (initMask & ENGINEOPENED) {
        Engine_close(hEngine);
    }

    if (initMask & CAPTUREDEVICEINITIALIZED) {
        cleanupCaptureDevice(captureFd, vidBufs, numVidBufs);
    }

    if (initMask & DISPLAYTHREADCREATED) {
        if (pthread_join(displayThread, &ret) == 0) {
            status = ret;
        }
        pthread_mutex_destroy(&displayEnv.prime);
    }

    if (initMask & OUTFIFOOPENED) {
        FifoUtil_close(&displayEnv.outFifo);
    }

    if (initMask & INFIFOOPENED) {
        FifoUtil_close(&displayEnv.inFifo);
    }

    return status;
}

