From c9601d407a7ca943e53fa49a5a5f69b868c6ed11 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 18 Nov 2014 22:52:06 +0200 Subject: Xilinx video library - Initial import Signed-off-by: Laurent Pinchart --- plugins/v4l2/xlnx-v4l2.c | 417 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 417 insertions(+) create mode 100644 plugins/v4l2/xlnx-v4l2.c (limited to 'plugins/v4l2/xlnx-v4l2.c') 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 + * + * 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 +#include +#include +#include + +#include +#include + +#include +#include +#include + +/*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 +#include + +#include +#include +#include + +#include + +#include + +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, +}; -- cgit v1.2.3