diff options
Diffstat (limited to 'isp-dsp.c')
-rw-r--r-- | isp-dsp.c | 588 |
1 files changed, 588 insertions, 0 deletions
diff --git a/isp-dsp.c b/isp-dsp.c new file mode 100644 index 0000000..441be50 --- /dev/null +++ b/isp-dsp.c @@ -0,0 +1,588 @@ +/* + * Sample ISP & DSP application + * + * Copyright (C) 2010-2011 Laurent Pinchart <laurent.pinchart@ideasonboard.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <errno.h> +#include <fcntl.h> +#include <getopt.h> +#include <signal.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sys/select.h> +#include <sys/time.h> + +#include <mediactl/media.h> +#include <mediactl/subdev.h> + +#include "bridge/dmm_buffer.h" +#include "bridge/dsp_bridge.h" +#include "v4l2.h" + +#define BUFFERS_COUNT 4 + +/* ----------------------------------------------------------------------------- + * DSP + */ + +#define DSP_LIBRARY "/lib/dsp/threshold.dll64P" + +struct omap3_dsp_device { + int handle; + void *proc; + struct dsp_uuid uuid; + struct dsp_node *node; + + dmm_buffer_t *buffers[BUFFERS_COUNT]; +}; + +static int omap3dsp_setup(struct omap3_dsp_device *dsp, struct v4l2_video_buffer *vbufs) +{ + /* Dummy UUID, must be identical to the one used in the DSP dll64P library */ + static const struct dsp_uuid uuid = { 0x3dac26d0, 0x6d4b, 0x11dd, 0xad, 0x8b, + { 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66 } }; + unsigned int i; + int ret; + + /* Open the DSP, attach, register library and node and create the + * node. + */ + dsp->handle = dsp_open(); + if (dsp->handle < 0) { + printf("error: failed to open DSP.\n"); + return dsp->handle; + } + + ret = dsp_attach(dsp->handle, 0, NULL, &dsp->proc); + if (!ret) { + printf("error: DSP attach failed.\n"); + return -1; + } + + dsp->uuid = uuid; + + ret = dsp_register(dsp->handle, &dsp->uuid, DSP_DCD_LIBRARYTYPE, + DSP_LIBRARY); + if (!ret) { + printf("error: DSP library registration failed.\n"); + return -1; + } + + ret = dsp_register(dsp->handle, &dsp->uuid, DSP_DCD_NODETYPE, + DSP_LIBRARY); + if (!ret) { + printf("error: DSP library registration failed.\n"); + return -1; + } + + ret = dsp_node_allocate(dsp->handle, dsp->proc, &dsp->uuid, NULL, NULL, + &dsp->node); + if (!ret) { + printf("error: DSP node allocation failed.\n"); + return -1; + } + + ret = dsp_node_create(dsp->handle, dsp->node); + if (!ret) { + printf("error: DSP node creation failed.\n"); + return -1; + } + + /* Allocate and map the buffers. The threshold algorithm performs + * in-place processing, so we can use the same buffer for both the + * CPU-DSP and DSP-CPU direction. + * + * Use the V4L2 buffers memory for the DSP buffers, avoiding a costly + * memcpy() operation. An alternative with similar efficiency would be + * to allocated DSP buffers memory here and use it as USERPTR for video + * capture. + */ + for (i = 0; i < BUFFERS_COUNT; ++i) { + dmm_buffer_t *buffer; + + buffer = dmm_buffer_new(dsp->handle, dsp->proc, + DMA_BIDIRECTIONAL); + if (buffer == NULL) { + printf("error: DSP buffer new failed.\n"); + return -1; + } + + dmm_buffer_use(buffer, vbufs[i].mem, vbufs[i].size); + ret = dmm_buffer_map(buffer); + printf("DSP buffer %u mapped: %d\n", i, ret); + dsp->buffers[i] = buffer; + } + + return 0; +} + +static int omap3dsp_start(struct omap3_dsp_device *dsp) +{ + int ret; + + ret = dsp_node_run(dsp->handle, dsp->node); + if (!ret) { + printf("error: failed to start DSP node\n"); + return -1; + } + + printf("DSP node running\n"); + return 0; +} + +static int omap3dsp_stop(struct omap3_dsp_device *dsp) +{ + unsigned long status; + int ret; + + ret = dsp_node_terminate(dsp->handle, dsp->node, &status); + if (!ret) { + printf("error: failed to stop DSP node\n"); + return -1; + } + + printf("DSP node stopped\n"); + return 0; +} + +static void omap3dsp_cleanup(struct omap3_dsp_device *dsp) +{ + unsigned int i; + + for (i = 0; i < BUFFERS_COUNT; ++i) { + if (dsp->buffers[i]) { + dmm_buffer_unmap(dsp->buffers[i]); + dmm_buffer_free(dsp->buffers[i]); + } + } + + if (dsp->node) + dsp_node_free(dsp->handle, dsp->node); + if (dsp->proc) + dsp_detach(dsp->handle, dsp->proc); + if (dsp->handle > 0) + dsp_close(dsp->handle); +} + +/* ----------------------------------------------------------------------------- + * Video acquisition + */ + +#define MEDIA_DEVICE "/dev/media0" +#define ENTITY_CCDC "OMAP3 ISP CCDC" +#define ENTITY_SENSOR "mt9v032 2-005c" + +struct omap3_isp_device { + struct media_device *mdev; + + struct media_entity *ccdc; + struct media_entity *sensor; + struct media_entity *video; + + struct v4l2_mbus_framefmt format; + struct v4l2_device *vdev; + + fd_set fds; +}; + +static int omap3isp_pipeline_setup(struct omap3_isp_device *isp) +{ + struct v4l2_mbus_framefmt format; + struct media_entity *entity; + unsigned int i; + int ret; + + /* Reset all links to make sure we're in a consistent, known state. */ + ret = media_reset_links(isp->mdev); + if (ret < 0) { + printf("error: unable to reset links.\n"); + return ret; + } + + /* Setup a Sensor -> CCDC -> memory pipeline. + * + * Start by locating the three entities. The output video node is + * located by looking for a devnode connected to the CCDC. + */ + isp->ccdc = media_get_entity_by_name(isp->mdev, ENTITY_CCDC, + strlen(ENTITY_CCDC)); + if (isp->ccdc == NULL) { + printf("error: unable to locate CCDC.\n"); + return -ENOENT; + } + + isp->sensor = media_get_entity_by_name(isp->mdev, ENTITY_SENSOR, + strlen(ENTITY_CCDC)); + if (isp->sensor == NULL) { + printf("error: unable to locate sensor.\n"); + return -ENOENT; + } + + for (i = 0; i < isp->ccdc->info.links; ++i) { + entity = isp->ccdc->links[i].sink->entity; + if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE) + break; + } + + if (i == isp->ccdc->info.links) { + printf("error: unable to locate CCDC output video node.\n"); + return -ENOENT; + } + + isp->video = entity; + + /* Enable the Sensor -> CCDC and CCDC -> memory links. */ + ret = media_setup_link(isp->mdev, &isp->sensor->pads[0], + &isp->ccdc->pads[0], MEDIA_LNK_FL_ENABLED); + if (ret < 0) { + printf("error: unable to setup sensor -> CCDC link.\n"); + return ret; + } + + ret = media_setup_link(isp->mdev, &isp->ccdc->pads[1], + &isp->video->pads[0], MEDIA_LNK_FL_ENABLED); + if (ret < 0) { + printf("error: unable to setup CCDC -> devnode link.\n"); + return ret; + } + + /* Configure formats. Retrieve the default format at the sensor output + * and propagate it through the pipeline. As the CCDC will not perform + * any cropping we can just apply the same format on all pads. + */ + ret = v4l2_subdev_get_format(isp->sensor, &format, 0, + V4L2_SUBDEV_FORMAT_TRY); + if (ret < 0) { + printf("error: get format on sensor output failed.\n"); + return ret; + } + + ret = v4l2_subdev_set_format(isp->sensor, &format, 0, + V4L2_SUBDEV_FORMAT_ACTIVE); + if (ret < 0) { + printf("error: set format failed on %s:%u.\n", + isp->sensor->info.name, 0); + return ret; + } + + ret = v4l2_subdev_set_format(isp->ccdc, &format, 0, + V4L2_SUBDEV_FORMAT_ACTIVE); + if (ret < 0) { + printf("error: set format failed on %s:%u.\n", + isp->ccdc->info.name, 1); + return ret; + } + + ret = v4l2_subdev_set_format(isp->ccdc, &format, 1, + V4L2_SUBDEV_FORMAT_ACTIVE); + if (ret < 0) { + printf("error: set format failed on %s:%u.\n", + isp->ccdc->info.name, 1); + return ret; + } + + isp->format = format; + + return 0; +} + +static int omap3isp_video_setup(struct omap3_isp_device *isp) +{ + struct v4l2_pix_format format; + int ret; + + /* Set the capture format on the CCDC output video node. */ + memset(&format, 0, sizeof format); + format.pixelformat = V4L2_PIX_FMT_SGRBG10; + format.width = isp->format.width; + format.height = isp->format.height; + + ret = v4l2_set_format(isp->vdev, &format); + if (ret < 0) + return ret; + + /* Allocate video buffers. */ + ret = v4l2_alloc_buffers(isp->vdev, V4L2_MEMORY_MMAP, BUFFERS_COUNT); + if (ret < 0) + return ret; + + /* Store the CCDC output device node file handle in the file descriptors + * set, to be used by select() in the main loop. + */ + FD_ZERO(&isp->fds); + FD_SET(isp->vdev->fd, &isp->fds); + + return 0; +} + +static int omap3isp_video_start(struct omap3_isp_device *isp) +{ + struct v4l2_video_buffer buffer; + unsigned int i; + int ret; + + /* Queue all buffers for video capture. */ + for (i = 0; i < isp->vdev->nbufs; ++i) { + buffer.index = i; + + ret = v4l2_queue_buffer(isp->vdev, &buffer); + if (ret < 0) { + printf("error: unable to queue buffer %u\n", i); + return -errno; + } + } + + /* Start video streaming. */ + ret = v4l2_stream_on(isp->vdev); + if (ret < 0) { + printf("error: failed to start video stream: %s (%d)\n", + strerror(-ret), ret); + return ret; + } + + return 0; +} + +static int omap3isp_video_stop(struct omap3_isp_device *isp) +{ + int ret; + + /* Stop video streaming. */ + ret = v4l2_stream_off(isp->vdev); + if (ret < 0) { + printf("error: failed to stop video stream: %s (%d)\n", + strerror(-ret), ret); + return ret; + } + + return 0; +} + +static void omap3isp_video_cleanup(struct omap3_isp_device *isp) +{ + if (isp->vdev) { + v4l2_free_buffers(isp->vdev); + v4l2_close(isp->vdev); + } +} + +/* -------------------------------------------------------------------------- */ + +#define SELECT_TIMEOUT 2000 /* in milliseconds */ + +static bool done = false; + +static void sigint_handler(int signal __attribute__((__unused__))) +{ + /* Set the done flag to true when the user presses CTRL-C to interrupt + * the main loop. + */ + done = true; +} + +static int process_image(struct omap3_isp_device *isp, + struct omap3_dsp_device *dsp) +{ + struct v4l2_video_buffer buffer; + dmm_buffer_t *dsp_buffer; + struct dsp_msg msg; + int ret; + + /* Dequeue the buffer */ + ret = v4l2_dequeue_buffer(isp->vdev, &buffer); + if (ret < 0) { + printf("error: unable to dequeue buffer: %s (%d)\n", + strerror(-ret), ret); + return ret; + } + + if (buffer.error) { + printf("warning: error in dequeued buffer, skipping\n"); + return 0; + } + + /* Send the data to the DSP and wait for the answer. + * + * Cache is cleaned first with dmm_buffer_begin() before passing it to + * the DSP, to make sure that all data has hit memory. Similarly + * dmm_buffer_end() makes sure that the CPU won't read stale data back + * from the cache. As the data isn't touched by the CPU between captured + * by the ISP and processing by the DSP this could be optimized. + */ + dsp_buffer = dsp->buffers[buffer.index]; + dmm_buffer_begin(dsp_buffer, buffer.bytesused); + + msg.cmd = 0; + msg.arg_1 = (uint32_t)dsp_buffer->map; + msg.arg_2 = buffer.bytesused; + dsp_node_put_message(dsp->handle, dsp->node, &msg, -1); + dsp_node_get_message(dsp->handle, dsp->node, &msg, -1); + + dmm_buffer_end(dsp_buffer, buffer.bytesused); + + /* Requeue the buffer */ + ret = v4l2_queue_buffer(isp->vdev, &buffer); + if (ret < 0) { + printf("error: unable to requeue buffer: %s (%d)\n", + strerror(-ret), ret); + return ret; + } + + return 0; +} + +int main(int argc __attribute__((__unused__)), char *argv[] __attribute__((__unused__))) +{ + struct omap3_dsp_device dsp; + struct omap3_isp_device isp; + struct timespec start, end; + unsigned int count = 0; + int exit_code = EXIT_FAILURE; + float fps; + int ret; + + /* Register a signal handler for SIGINT, received when the user presses + * CTRL-C. This will allow the main loop to be interrupted, and resources + * to be freed cleanly. + */ + signal(SIGINT, sigint_handler); + + memset(&dsp, 0, sizeof dsp); + memset(&isp, 0, sizeof isp); + + /* Open the media device and setup the capture pipeline. The pipeline + * topology is hardcoded to sensor -> CCDC -> CCDC output. + */ + isp.mdev = media_open(MEDIA_DEVICE, 0); + if (isp.mdev == NULL) { + printf("error: unable to open media device %s\n", MEDIA_DEVICE); + goto cleanup; + } + + ret = omap3isp_pipeline_setup(&isp); + if (ret < 0) { + printf("error: unable to setup pipeline\n"); + goto cleanup; + } + + /* Open the video capture device, setup the format and allocate the + * buffers. + */ + isp.vdev = v4l2_open(isp.video->devname); + if (isp.vdev == NULL) { + printf("error: unable to open video capture device %s\n", + isp.video->devname); + goto cleanup; + } + + ret = omap3isp_video_setup(&isp); + if (ret < 0) { + printf("error: unable to setup video capture\n"); + goto cleanup; + } + + /* Initialize the DSP. */ + ret = omap3dsp_setup(&dsp, isp.vdev->buffers); + if (ret < 0) { + printf("error: unable to setup DSP\n"); + goto cleanup; + } + + /* Start the DSP and ISP. */ + ret = omap3dsp_start(&dsp); + if (ret < 0) + goto cleanup; + + ret = omap3isp_video_start(&isp); + if (ret < 0) + goto cleanup; + + clock_gettime(CLOCK_MONOTONIC, &start); + + /* Main capture loop. Wait for a video buffer using select() and process + * it. + */ + while (!done) { + struct timeval timeout; + fd_set rfds; + + timeout.tv_sec = SELECT_TIMEOUT / 1000; + timeout.tv_usec = (SELECT_TIMEOUT % 1000) * 1000; + rfds = isp.fds; + + ret = select(isp.vdev->fd + 1, &rfds, NULL, NULL, &timeout); + if (ret < 0) { + /* EINTR means that a signal has been received, continue + * to the next iteration in that case. + */ + if (errno == EINTR) + continue; + + printf("error: select failed with %d\n", errno); + goto cleanup; + } + if (ret == 0) { + /* select() should never time out as the ISP is supposed + * to capture images continuously. A timeout is thus + * considered as a fatal error. + */ + printf("error: select timeout\n"); + goto cleanup; + } + + process_image(&isp, &dsp); + count++; + } + + clock_gettime(CLOCK_MONOTONIC, &end); + + /* Stop the DSP and ISP. */ + omap3isp_video_stop(&isp); + omap3dsp_stop(&dsp); + + /* Print some statistics. */ + end.tv_sec -= start.tv_sec; + end.tv_nsec -= start.tv_nsec; + if (end.tv_nsec < 0) { + end.tv_sec--; + end.tv_nsec += 1000000000; + } + + fps = count / (end.tv_sec + end.tv_nsec / 1000000000.0); + + printf("%u images processed in %lu.%06lu seconds (%f fps)\n", count, + end.tv_sec, end.tv_nsec / 1000, fps); + + exit_code = EXIT_SUCCESS; + +cleanup: + /* Cleanup the DSP and ISP resources. */ + omap3dsp_cleanup(&dsp); + omap3isp_video_cleanup(&isp); + if (isp.mdev) + media_close(isp.mdev); + + return exit_code; +} |