summaryrefslogtreecommitdiff
path: root/plugins/v4l2/xlnx-v4l2.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/v4l2/xlnx-v4l2.c')
-rw-r--r--plugins/v4l2/xlnx-v4l2.c417
1 files changed, 417 insertions, 0 deletions
diff --git a/plugins/v4l2/xlnx-v4l2.c b/plugins/v4l2/xlnx-v4l2.c
new file mode 100644
index 0000000..c307041
--- /dev/null
+++ b/plugins/v4l2/xlnx-v4l2.c
@@ -0,0 +1,417 @@
+/*
+ * Xilinx Video Library - V4L2 plugin
+ *
+ * Copyright (C) 2014-2016 Ideas on board Oy
+ *
+ * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ *
+ * This library 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.
+ */
+
+#if 0
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <v4l2.h>
+#include <v4l2subdev.h>
+
+#include <xlnx-list.h>
+#include <xlnx-tools.h>
+#include <xlnx-video.h>
+
+/*static inline struct xlnx_video_node *
+to_xlnx_video_node(struct xlnx_video_entity *e) {
+ return container_of(e, struct xlnx_video_node, entity);
+}*/
+
+/* -----------------------------------------------------------------------------
+ * Helper functions
+ */
+
+/*
+ * struct xlnx_video_format_info - Media bus format information
+ * @code: V4L2 media bus format code
+ * @pixelformat: V4L2 pixel format FCC identifier
+ */
+struct xlnx_video_format_info {
+ enum v4l2_mbus_pixelcode code;
+ __u32 pixelformat;
+};
+
+static const struct xlnx_video_format_info formats[] = {
+ { MEDIA_BUS_FMT_RGB888_1X32_PADHI, V4L2_PIX_FMT_BGR32, },
+ { MEDIA_BUS_FMT_UYVY8_1X16, V4L2_PIX_FMT_YUYV, },
+ { MEDIA_BUS_FMT_VUY8_1X24, V4L2_PIX_FMT_YUV444, },
+ { MEDIA_BUS_FMT_SRGGB8_1X8, V4L2_PIX_FMT_SGRBG8, },
+ { MEDIA_BUS_FMT_SGRBG8_1X8, V4L2_PIX_FMT_SGRBG8, },
+ { MEDIA_BUS_FMT_SGBRG8_1X8, V4L2_PIX_FMT_SGBRG8, },
+ { MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_PIX_FMT_SBGGR8, },
+};
+
+static __u32 mbus_to_pix(enum v4l2_mbus_pixelcode code)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(formats); ++i) {
+ if (formats[i].code == code)
+ return formats[i].pixelformat;
+ }
+
+ return 0;
+}
+
+static enum v4l2_mbus_pixelcode pix_to_mbus(__u32 pixelformat)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(formats); ++i) {
+ if (formats[i].pixelformat == pixelformat)
+ return formats[i].code;
+ }
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Video streaming
+ */
+
+int xlnx_video_component_set_format(struct xlnx_video_component *xvcomp,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ struct v4l2_mbus_framefmt format;
+ struct v4l2_pix_format v4l2_fmt;
+ struct xlnx_video_entity *source = NULL;
+ struct xlnx_video_entity *sink;
+ struct xlnx_video_node *video;
+ struct media_entity_pad *pad;
+ int ret;
+
+ /* Configure formats. Start from the input and propagate the format
+ * through the component.
+ */
+ format = *initial_fmt;
+
+ list_for_each_entry(sink, &xvcomp->entities, list) {
+ if (source == NULL) {
+ /* Configure the first entity in the component with the
+ * initial format.
+ */
+ pad = sink->source.link->source;
+
+ if ((sink->info->type & MEDIA_ENT_TYPE_MASK) == MEDIA_ENT_T_V4L2_SUBDEV) {
+ ret = v4l2_subdev_set_format(sink->entity, &format, 0,
+ V4L2_SUBDEV_FORMAT_ACTIVE);
+ if (ret < 0) {
+ printf("error: set format on component input failed.\n");
+ return ret;
+ }
+ } else if (sink->info->type == MEDIA_ENT_T_DEVNODE_V4L) {
+ video = to_xlnx_video_node(sink);
+
+ memset(&v4l2_fmt, 0, sizeof v4l2_fmt);
+ v4l2_fmt.pixelformat = mbus_to_pix(format.code);
+ v4l2_fmt.width = format.width;
+ v4l2_fmt.height = format.height;
+
+ ret = v4l2_set_format(video->video, &v4l2_fmt);
+ if (ret < 0) {
+ printf("error: set format failed on %s.\n",
+ sink->info->name);
+ return ret;
+ }
+
+ format.code = pix_to_mbus(v4l2_fmt.pixelformat);
+ format.width = v4l2_fmt.width;
+ format.height = v4l2_fmt.height;
+
+ video->format = format;
+ }
+
+ source = sink;
+ continue;
+ }
+
+ if ((source->info->type & MEDIA_ENT_TYPE_MASK) == MEDIA_ENT_T_V4L2_SUBDEV) {
+ pad = source->source.link->source;
+
+ /* Try to force the output format code onto the source pad. */
+ ret = v4l2_subdev_get_format(pad->entity, &format, pad->index,
+ V4L2_SUBDEV_FORMAT_ACTIVE);
+ if (ret < 0) {
+ printf("error: set format failed on %s:%u.\n",
+ pad->entity->info->name, pad->index);
+ return ret;
+ }
+
+ if (sink->last)
+ format = *fmt;
+ else
+ format.code = fmt->code;
+
+ ret = v4l2_subdev_set_format(pad->entity, &format, pad->index,
+ V4L2_SUBDEV_FORMAT_ACTIVE);
+ if (ret < 0) {
+ printf("error: set format failed on %s:%u.\n",
+ pad->entity->info.name, pad->index);
+ return ret;
+ }
+
+ if (sink->last)
+ *fmt = format;
+ }
+
+ source->source.format = format;
+
+ if ((sink->info->type & MEDIA_ENT_TYPE_MASK) == MEDIA_ENT_T_V4L2_SUBDEV) {
+ /* Propagate the format to the link target. */
+ pad = sink->sink.link->sink;
+
+ ret = v4l2_subdev_set_format(pad->entity, &format, pad->index,
+ V4L2_SUBDEV_FORMAT_ACTIVE);
+ if (ret < 0) {
+ printf("error: set format failed on %s:%u.\n",
+ pad->entity->info.name, pad->index);
+ return ret;
+ }
+ } else if (sink->info->type == MEDIA_ENT_T_DEVNODE_V4L) {
+ video = to_xlnx_video_node(sink);
+
+ /* Set the capture format on the capture or output video
+ * node. If the connected source is a pool, v4l2_fmt
+ * already contains the format that has been setup on
+ * the associated capture video node.
+ */
+ memset(&v4l2_fmt, 0, sizeof v4l2_fmt);
+ v4l2_fmt.pixelformat = mbus_to_pix(format.code);
+ v4l2_fmt.width = format.width;
+ v4l2_fmt.height = format.height;
+
+ ret = v4l2_set_format(video->video, &v4l2_fmt);
+ if (ret < 0) {
+ printf("error: set format failed on %s.\n",
+ sink->info->name);
+ return ret;
+ }
+
+ format.code = pix_to_mbus(v4l2_fmt.pixelformat);
+ format.width = v4l2_fmt.width;
+ format.height = v4l2_fmt.height;
+
+ video->format = format;
+ }
+
+ sink->sink.format = format;
+
+ source = sink;
+ }
+
+ return 0;
+}
+
+static int xlnx_video_validate_entity(struct media_entity *entity,
+ const struct media_link **linkp)
+{
+ const struct media_link *link_source = NULL;
+ const struct media_link *link_sink = NULL;
+ const struct media_entity_desc *info;
+ unsigned int num_links;
+ unsigned int i;
+
+ info = media_entity_get_info(entity);
+
+ if (info->type == MEDIA_ENT_T_DEVNODE_V4L) {
+ const struct media_link *link;
+
+ /* Video nodes must have a single pad and a single link. */
+ if (info->pads != 1)
+ return -EINVAL;
+
+ if (media_entity_get_links_count(entity) != 1)
+ return -EINVAL;
+
+ link = media_entity_get_link(entity, 0);
+ *linkp = link->sink->entity == entity ? link : NULL;
+ return 0;
+ }
+
+ if ((info->type & MEDIA_ENT_TYPE_MASK) != MEDIA_ENT_T_V4L2_SUBDEV)
+ return -EINVAL;
+
+ /*
+ * Only components with linear internal pipelines are supported, the
+ * entity must have a single connected source and a single connected
+ * sink.
+ */
+ num_links = media_entity_get_links_count(entity);
+
+ for (i = 0; i < num_links; ++i) {
+ const struct media_link *link = media_entity_get_link(entity, i);
+
+ /*
+ * Components with configurable internal pipelines are not
+ * supported.
+ */
+ if (!(link->flags & MEDIA_LNK_FL_IMMUTABLE))
+ return -EINVAL;
+
+ if (!(link->flags & MEDIA_LNK_FL_ENABLED))
+ continue;
+
+ if (link->source->entity == entity) {
+ if (link_source && link_source != link)
+ return -EINVAL;
+ link_source = link;
+ } else {
+ if (link_sink && link_sink != link)
+ return -EINVAL;
+ link_sink = link;
+ }
+ }
+
+ *linkp = link_sink;
+ return 0;
+}
+
+static int xlnx_video_scan_component(struct xlnx_video_component *xvcomp)
+{
+ struct media_entity *entity;
+
+ entity = xvcomp->output->entity;
+
+ while (1) {
+ const struct media_link *link;
+ int ret;
+
+ ret = xlnx_video_validate_entity(entity, &link);
+ if (ret < 0)
+ return ret;
+
+ if (link == NULL) {
+ const struct media_entity_desc *info;
+
+ info = media_entity_get_info(entity);
+ if (info->type == MEDIA_ENT_T_DEVNODE_V4L)
+ xvcomp->input.entity = entity;
+ break;
+ }
+
+ entity = link->source->entity;
+ }
+
+ return 0;
+}
+
+static int xlnx_video_scan_media(struct xlnx_video *xv, struct media_device *media)
+{
+}
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include <linux/media-bus-format.h>
+#include <linux/videodev2.h>
+#include <linux/v4l2-subdev.h>
+
+#include <mediactl.h>
+
+#include <xlnx-plugin.h>
+
+static const struct xlnx_video_plugin_info v4l2_plugin_info = {
+ .name = "V4L2 plugin",
+ .type = XLNX_VIDEO_PLUGIN_TYPE_HARDWARE,
+};
+
+static const struct xlnx_video_plugin_info *v4l2_info(void)
+{
+ return &v4l2_plugin_info;
+}
+
+/*
+ * \brief Xilinx V4L2 video node
+ */
+struct v4l2_video_node {
+ /* V4L2 device */
+ struct v4l2_device *video;
+#if 0
+ /* Current video format */
+ struct v4l2_mbus_framefmt format;
+ struct xlnx_video_buffers_pool *pool;
+ /* Number of buffers queued to the driver */
+ unsigned int queued;
+ /* Whether video capture is running on the device */
+ bool running;
+#endif
+};
+
+struct v4l2_component {
+ struct xlnx_video_component *xvcomp;
+
+ struct v4l2_video_node *input;
+ struct v4l2_video_node *output;
+};
+
+static struct xlnx_video_component *v4l2_scan(struct xlnx_video *xv,
+ struct media_device *mdev)
+{
+ struct media_entity *entity;
+ unsigned int num_entities;
+ unsigned int i;
+
+ num_entities = media_get_entities_count(mdev);
+ if (num_entities == 0)
+ return 0;
+
+ for (i = 0; i < num_entities; ++i) {
+ const struct media_entity_desc *info;
+ const struct media_pad *pad;
+ struct v4l2_component *xvcomp;
+ int ret;
+
+ entity = media_get_entity(mdev, i);
+ info = media_entity_get_info(entity);
+
+ if (info->type != MEDIA_ENT_T_DEVNODE_V4L ||
+ info->pads != 1)
+ continue;
+
+ pad = media_entity_get_pad(entity, 0);
+ if (!(pad->flags & MEDIA_PAD_FL_SINK))
+ continue;
+
+ xvcomp = calloc(1, sizeof *xvcomp);
+ if (!xvcomp)
+ return -ENOMEM;
+
+ list_init(&xvcomp->entities);
+ xvcomp->xv = xv;
+ xvcomp->output.entity = entity;
+
+ ret = xlnx_video_scan_component(xvcomp);
+ if (ret < 0) {
+ free(xvcomp);
+ return ret;
+ }
+
+ list_append(&xv->components, &xvcomp->list);
+ xv->num_components++;
+ }
+
+ return 0;
+}
+
+EXPORT_XLNX_VIDEO_PLUGIN(v4l2) = {
+ .info = v4l2_info,
+ .scan = v4l2_scan,
+};