/* * 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, };