diff options
Diffstat (limited to 'plugins/v4l2')
| -rw-r--r-- | plugins/v4l2/Makefile | 22 | ||||
| -rw-r--r-- | plugins/v4l2/v4l2.c | 721 | ||||
| -rw-r--r-- | plugins/v4l2/v4l2.h | 289 | ||||
| -rw-r--r-- | plugins/v4l2/xlnx-v4l2.c | 417 | 
4 files changed, 1449 insertions, 0 deletions
| diff --git a/plugins/v4l2/Makefile b/plugins/v4l2/Makefile new file mode 100644 index 0000000..fcce631 --- /dev/null +++ b/plugins/v4l2/Makefile @@ -0,0 +1,22 @@ +AR	:= $(CROSS_COMPILE)ar +CC	:= $(CROSS_COMPILE)gcc +CFLAGS	:= -O2 -W -Wall -Wno-unused-parameter -I$(KDIR)/usr/include -I$(topdir)/3rdparty/media-ctl/include -I$(topdir)/include -I. -fPIC +LDFLAGS	:= -L$(topdir)/3rdparty/media-ctl +LIBS    := -lmediactl + +OBJECTS := xlnx-v4l2.o +TARGET := xlnx-v4l2.so + +%.o : %.c +	$(CC) $(CFLAGS) -c -o $@ $< + +all: $(TARGET) + +$(TARGET): $(OBJECTS) +	$(CC) -o $@ -shared $^ + +clean: +	-$(RM) *.o +	-$(RM) $(TARGET) + +install: diff --git a/plugins/v4l2/v4l2.c b/plugins/v4l2/v4l2.c new file mode 100644 index 0000000..c0fdd54 --- /dev/null +++ b/plugins/v4l2/v4l2.c @@ -0,0 +1,721 @@ +/* + * Xilinx Video Library - Generic V4L2 support + * + * 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. + */ + +#include <stdio.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdint.h> +#include <stdlib.h> +#include <errno.h> +#include <getopt.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sys/select.h> +#include <sys/time.h> + +#include <linux/videodev2.h> + +#include <xlnx-buffers.h> +#include <xlnx-list.h> +#include <xlnx-tools.h> + +#include "v4l2.h" + +#ifndef V4L2_BUF_FLAG_ERROR +#define V4L2_BUF_FLAG_ERROR	0x0040 +#endif + +struct v4l2_ival_desc { +	struct v4l2_fract min; +	struct v4l2_fract max; +	struct v4l2_fract step; + +	struct list_entry list; +}; + +struct v4l2_frame_desc { +	unsigned int min_width; +	unsigned int min_height; +	unsigned int max_width; +	unsigned int max_height; +	unsigned int step_width; +	unsigned int step_height; + +	struct list_entry list; +	struct list_entry ivals; +}; + +struct v4l2_format_desc { +	unsigned int pixelformat; + +	struct list_entry list; +	struct list_entry frames; +}; + +/* ----------------------------------------------------------------------------- + * Formats enumeration + */ + +static int +v4l2_enum_frame_intervals(struct v4l2_device *dev, struct v4l2_format_desc *format, +	struct v4l2_frame_desc *frame) +{ +	struct v4l2_ival_desc *ival; +	unsigned int i; +	int ret; + +	for (i = 0; ; ++i) { +		struct v4l2_frmivalenum ivalenum; + +		memset(&ivalenum, 0, sizeof ivalenum); +		ivalenum.index = i; +		ivalenum.pixel_format = format->pixelformat; +		ivalenum.width = frame->min_width; +		ivalenum.height = frame->min_height; +		ret = ioctl(dev->fd, VIDIOC_ENUM_FRAMEINTERVALS, &ivalenum); +		if (ret < 0) +			break; + +		if (i != ivalenum.index) +			printf("Warning: driver returned wrong ival index " +				"%u.\n", ivalenum.index); +		if (format->pixelformat != ivalenum.pixel_format) +			printf("Warning: driver returned wrong ival pixel " +				"format %08x.\n", ivalenum.pixel_format); +		if (frame->min_width != ivalenum.width) +			printf("Warning: driver returned wrong ival width " +				"%u.\n", ivalenum.width); +		if (frame->min_height != ivalenum.height) +			printf("Warning: driver returned wrong ival height " +				"%u.\n", ivalenum.height); + +		ival = malloc(sizeof *ival); +		if (ival == NULL) +			return -ENOMEM; + +		memset(ival, 0, sizeof *ival); + +		switch (ivalenum.type) { +		case V4L2_FRMIVAL_TYPE_DISCRETE: +			ival->min = ivalenum.discrete; +			ival->max = ivalenum.discrete; +			ival->step.numerator = 1; +			ival->step.denominator = 1; +			break; + +		case V4L2_FRMIVAL_TYPE_STEPWISE: +			ival->min = ivalenum.stepwise.min; +			ival->max = ivalenum.stepwise.max; +			ival->step = ivalenum.stepwise.step; +			break; + +		default: +			printf("Error: driver returned invalid frame ival " +				"type %u\n", ivalenum.type); +			return -EINVAL; +		} + +		list_append(&ival->list, &frame->ivals); +	} + +	return 0; +} + +static int +v4l2_enum_frame_sizes(struct v4l2_device *dev, struct v4l2_format_desc *format) +{ +	struct v4l2_frame_desc *frame; +	unsigned int i; +	int ret; + +	for (i = 0; ; ++i) { +		struct v4l2_frmsizeenum frmenum; + +		memset(&frmenum, 0, sizeof frmenum); +		frmenum.index = i; +		frmenum.pixel_format = format->pixelformat; + +		ret = ioctl(dev->fd, VIDIOC_ENUM_FRAMESIZES, &frmenum); +		if (ret < 0) +			break; + +		if (i != frmenum.index) +			printf("Warning: driver returned wrong frame index " +				"%u.\n", frmenum.index); +		if (format->pixelformat != frmenum.pixel_format) +			printf("Warning: driver returned wrong frame pixel " +				"format %08x.\n", frmenum.pixel_format); + +		frame = malloc(sizeof *frame); +		if (frame == NULL) +			return -ENOMEM; + +		memset(frame, 0, sizeof *frame); + +		list_init(&frame->ivals); +		frame->step_width = 1; +		frame->step_height = 1; + +		switch (frmenum.type) { +		case V4L2_FRMSIZE_TYPE_DISCRETE: +			frame->min_width = frmenum.discrete.width; +			frame->min_height = frmenum.discrete.height; +			frame->max_width = frmenum.discrete.width; +			frame->max_height = frmenum.discrete.height; +			break; + +		case V4L2_FRMSIZE_TYPE_STEPWISE: +			frame->step_width = frmenum.stepwise.step_width; +			frame->step_height = frmenum.stepwise.step_height; +		case V4L2_FRMSIZE_TYPE_CONTINUOUS: +			frame->min_width = frmenum.stepwise.min_width; +			frame->min_height = frmenum.stepwise.min_height; +			frame->max_width = frmenum.stepwise.max_width; +			frame->max_height = frmenum.stepwise.max_height; +			break; + +		default: +			printf("Error: driver returned invalid frame size " +				"type %u\n", frmenum.type); +			return -EINVAL; +		} + +		list_append(&frame->list, &format->frames); + +		ret = v4l2_enum_frame_intervals(dev, format, frame); +		if (ret < 0) +			return ret; +	} + +	return 0; +} +static int v4l2_enum_formats(struct v4l2_device *dev) +{ +	struct v4l2_format_desc *format; +	unsigned int i; +	int ret; + +	for (i = 0; ; ++i) { +		struct v4l2_fmtdesc fmtenum; + +		memset(&fmtenum, 0, sizeof fmtenum); +		fmtenum.index = i; +		fmtenum.type = dev->type; + +		ret = ioctl(dev->fd, VIDIOC_ENUM_FMT, &fmtenum); +		if (ret < 0) +			break; + +		if (i != fmtenum.index) +			printf("Warning: driver returned wrong format index " +				"%u.\n", fmtenum.index); +		if (dev->type != fmtenum.type) +			printf("Warning: driver returned wrong format type " +				"%u.\n", fmtenum.type); + +		format = malloc(sizeof *format); +		if (format == NULL) +			return -ENOMEM; + +		memset(format, 0, sizeof *format); + +		list_init(&format->frames); +		format->pixelformat = fmtenum.pixelformat; + +		list_append(&format->list, &dev->formats); + +		ret = v4l2_enum_frame_sizes(dev, format); +		if (ret < 0) +			return ret; +	} + +	return 0; +} + +/* ----------------------------------------------------------------------------- + * Open/close + */ + +struct v4l2_device *v4l2_open(const char *devname) +{ +	struct v4l2_device *dev; +	struct v4l2_capability cap; +	int ret; + +	dev = malloc(sizeof *dev); +	if (dev == NULL) +		return NULL; + +	memset(dev, 0, sizeof *dev); +	dev->fd = -1; +	dev->name = strdup(devname); +	list_init(&dev->formats); + +	dev->fd = open(devname, O_RDWR | O_NONBLOCK); +	if (dev->fd < 0) { +		printf("Error opening device %s: %d.\n", devname, errno); +		v4l2_close(dev); +		return NULL; +	} + +	memset(&cap, 0, sizeof cap); +	ret = ioctl(dev->fd, VIDIOC_QUERYCAP, &cap); +	if (ret < 0) { +		printf("Error opening device %s: unable to query " +			"device.\n", devname); +		v4l2_close(dev); +		return NULL; +	} + +	if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) +		dev->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; +	else if (cap.capabilities & V4L2_CAP_VIDEO_OUTPUT) +		dev->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; +	else { +		printf("Error opening device %s: neither video capture " +			"nor video output supported.\n", devname); +		v4l2_close(dev); +		return NULL; +	} + +	ret = v4l2_enum_formats(dev); +	if (ret < 0) { +		printf("Error opening device %s: unable to enumerate " +			"formats.\n", devname); +		v4l2_close(dev); +		return NULL; +	} + +	printf("Device %s opened: %s (%s).\n", devname, cap.card, cap.bus_info); + +	return dev; +} + +void v4l2_close(struct v4l2_device *dev) +{ +	struct v4l2_format_desc *format, *next_fmt; +	struct v4l2_frame_desc *frame, *next_frm; +	struct v4l2_ival_desc *ival, *next_ival; + +	if (dev == NULL) +		return; + +	list_for_each_entry_safe(format, next_fmt, &dev->formats, list) { +		list_for_each_entry_safe(frame, next_frm, &format->frames, list) { +			list_for_each_entry_safe(ival, next_ival, &frame->ivals, list) { +				free(ival); +			} +			free(frame); +		} +		free(format); +	} + +	free(dev->name); +	close(dev->fd); +} + +/* ----------------------------------------------------------------------------- + * Controls + */ + +int v4l2_get_control(struct v4l2_device *dev, unsigned int id, int32_t *value) +{ +	struct v4l2_control ctrl; +	int ret; + +	ctrl.id = id; + +	ret = ioctl(dev->fd, VIDIOC_G_CTRL, &ctrl); +	if (ret < 0) { +		printf("%s: unable to get control (%d).\n", dev->name, errno); +		return -errno; +	} + +	*value = ctrl.value; +	return 0; +} + +int v4l2_set_control(struct v4l2_device *dev, unsigned int id, int32_t *value) +{ +	struct v4l2_control ctrl; +	int ret; + +	ctrl.id = id; +	ctrl.value = *value; + +	ret = ioctl(dev->fd, VIDIOC_S_CTRL, &ctrl); +	if (ret < 0) { +		printf("%s: unable to set control (%d).\n", dev->name, errno); +		return -errno; +	} + +	*value = ctrl.value; +	return 0; +} + +int v4l2_get_controls(struct v4l2_device *dev, unsigned int count, +		      struct v4l2_ext_control *ctrls) +{ +	struct v4l2_ext_controls controls; +	int ret; + +	memset(&controls, 0, sizeof controls); +	controls.count = count; +	controls.controls = ctrls; + +	ret = ioctl(dev->fd, VIDIOC_G_EXT_CTRLS, &controls); +	if (ret < 0) +		printf("%s: unable to get multiple controls (%d).\n", dev->name, +		       errno); + +	return ret; +} + +int v4l2_set_controls(struct v4l2_device *dev, unsigned int count, +		      struct v4l2_ext_control *ctrls) +{ +	struct v4l2_ext_controls controls; +	int ret; + +	memset(&controls, 0, sizeof controls); +	controls.count = count; +	controls.controls = ctrls; + +	ret = ioctl(dev->fd, VIDIOC_S_EXT_CTRLS, &controls); +	if (ret < 0) +		printf("%s: unable to set multiple controls (%d).\n", dev->name, +		       errno); + +	return ret; +} + +/* ----------------------------------------------------------------------------- + * Formats and frame rates + */ + +int v4l2_get_crop(struct v4l2_device *dev, struct v4l2_rect *rect) +{ +	struct v4l2_crop crop; +	int ret; + +	memset(&crop, 0, sizeof crop); +	crop.type = dev->type; + +	ret = ioctl(dev->fd, VIDIOC_G_CROP, &crop); +	if (ret < 0) { +		printf("%s: unable to get crop rectangle (%d).\n", dev->name, +		       errno); +		return -errno; +	} + +	dev->crop = crop.c; +	*rect = crop.c; + +	return 0; +} + +int v4l2_set_crop(struct v4l2_device *dev, struct v4l2_rect *rect) +{ +	struct v4l2_crop crop; +	int ret; + +	memset(&crop, 0, sizeof crop); +	crop.type = dev->type; +	crop.c = *rect; + +	ret = ioctl(dev->fd, VIDIOC_S_CROP, &crop); +	if (ret < 0) { +		printf("%s: unable to set crop rectangle (%d).\n", dev->name, +		       errno); +		return -errno; +	} + +	dev->crop = crop.c; +	*rect = crop.c; + +	return 0; +} + +int v4l2_get_format(struct v4l2_device *dev, struct v4l2_pix_format *format) +{ +	struct v4l2_format fmt; +	int ret; + +	memset(&fmt, 0, sizeof fmt); +	fmt.type = dev->type; + +	ret = ioctl(dev->fd, VIDIOC_G_FMT, &fmt); +	if (ret < 0) { +		printf("%s: unable to get format (%d).\n", dev->name, errno); +		return -errno; +	} + +	dev->format = fmt.fmt.pix; +	*format = fmt.fmt.pix; + +	return 0; +} + +int v4l2_set_format(struct v4l2_device *dev, struct v4l2_pix_format *format) +{ +	struct v4l2_format fmt; +	int ret; + +	memset(&fmt, 0, sizeof fmt); +	fmt.type = dev->type; +	fmt.fmt.pix.width = format->width; +	fmt.fmt.pix.height = format->height; +	fmt.fmt.pix.pixelformat = format->pixelformat; +	fmt.fmt.pix.field = V4L2_FIELD_ANY; + +	ret = ioctl(dev->fd, VIDIOC_S_FMT, &fmt); +	if (ret < 0) { +		printf("%s: unable to set format (%d).\n", dev->name, errno); +		return -errno; +	} + +	dev->format = fmt.fmt.pix; +	*format = fmt.fmt.pix; + +	return 0; +} + +/* ----------------------------------------------------------------------------- + * Buffers management + */ + +int v4l2_alloc_buffers(struct v4l2_device *dev, +		       struct xlnx_video_buffers_pool *pool, +		       enum v4l2_memory memtype) +{ +	struct v4l2_requestbuffers rb; +	struct v4l2_buffer buf; +	unsigned int i; +	int ret; + +	dev->memtype = memtype; +	dev->pool = pool; + +	memset(&rb, 0, sizeof rb); +	rb.count = pool->nbufs; +	rb.type = dev->type; +	rb.memory = memtype; + +	ret = ioctl(dev->fd, VIDIOC_REQBUFS, &rb); +	if (ret < 0) { +		printf("%s: unable to request buffers (%d).\n", dev->name, +		       errno); +		ret = -errno; +		goto done; +	} + +	if (rb.count > pool->nbufs) { +		printf("%s: driver needs more buffers (%u) than available (%u).\n", +		       dev->name, rb.count, pool->nbufs); +		ret = -E2BIG; +		goto done; +	} + +	dev->nbufs = rb.count; + +	printf("%s: %u buffers requested.\n", dev->name, rb.count); + +	/* Map the buffers. */ +	for (i = 0; i < rb.count; ++i) { +		memset(&buf, 0, sizeof buf); +		buf.index = i; +		buf.type = dev->type; +		buf.memory = memtype; +		ret = ioctl(dev->fd, VIDIOC_QUERYBUF, &buf); +		if (ret < 0) { +			printf("%s: unable to query buffer %u (%d).\n", +			       dev->name, i, errno); +			ret = -errno; +			goto done; +		} + +		switch (memtype) { +		case V4L2_MEMORY_MMAP: +			pool->buffers[i].mem = mmap(0, buf.length, PROT_READ | PROT_WRITE, +					      MAP_SHARED, dev->fd, buf.m.offset); +			if (pool->buffers[i].mem == MAP_FAILED) { +				printf("%s: unable to map buffer %u (%d)\n", +				       dev->name, i, errno); +				ret = -errno; +				goto done; +			} +			pool->buffers[i].size = buf.length; +			printf("%s: buffer %u mapped at address %p.\n", +			       dev->name, i, pool->buffers[i].mem); +			break; + +		case V4L2_MEMORY_USERPTR: +			if (pool->buffers[i].size < buf.length) { +				printf("%s: buffer %u too small (%u bytes required, " +				       "%u bytes available.\n", dev->name, i, +				       buf.length, pool->buffers[i].size); +				ret = -EINVAL; +				goto done; +			} + +			printf("%s: buffer %u valid.\n", dev->name, i); +			break; + +		default: +			break; +		} +	} + +	ret = 0; + +done: +	if (ret < 0) +		v4l2_free_buffers(dev); + +	return ret; +} + +int v4l2_free_buffers(struct v4l2_device *dev) +{ +	struct v4l2_requestbuffers rb; +	unsigned int i; +	int ret; + +	if (dev->pool == NULL) +		return 0; + +	if (dev->memtype == V4L2_MEMORY_MMAP) { +		for (i = 0; i < dev->nbufs; ++i) { +			struct xlnx_video_buffer *buffer = &dev->pool->buffers[i]; + +			if (buffer->mem == NULL) +				continue; + +			ret = munmap(buffer->mem, buffer->size); +			if (ret < 0) { +				printf("%s: unable to unmap buffer %u (%d)\n", +				       dev->name, i, errno); +				return -errno; +			} + +			buffer->mem = NULL; +			buffer->size = 0; +		} +	} + +	memset(&rb, 0, sizeof rb); +	rb.count = 0; +	rb.type = dev->type; +	rb.memory = dev->memtype; + +	ret = ioctl(dev->fd, VIDIOC_REQBUFS, &rb); +	if (ret < 0) { +		printf("%s: unable to release buffers (%d)\n", dev->name, +		       errno); +		return -errno; +	} + +	dev->nbufs = 0; +	dev->pool = NULL; + +	return 0; +} + +int v4l2_dequeue_buffer(struct v4l2_device *dev, struct xlnx_video_buffer *buffer) +{ +	struct v4l2_buffer buf; +	int ret; + +	memset(&buf, 0, sizeof buf); +	buf.type = dev->type; +	buf.memory = dev->memtype; + +	ret = ioctl(dev->fd, VIDIOC_DQBUF, &buf); +	if (ret < 0) { +		printf("%s: unable to dequeue buffer index %u/%u (%d)\n", +		       dev->name, buf.index, dev->nbufs, errno); +		return -errno; +	} + +	*buffer = dev->pool->buffers[buf.index]; +	buffer->bytesused = buf.bytesused; +	buffer->timestamp = buf.timestamp; +	buffer->error = !!(buf.flags & V4L2_BUF_FLAG_ERROR); + +	return 0; +} + +int v4l2_queue_buffer(struct v4l2_device *dev, struct xlnx_video_buffer *buffer) +{ +	struct v4l2_buffer buf; +	int ret; + +	if (buffer->index >= dev->nbufs) +		return -EINVAL; + +	memset(&buf, 0, sizeof buf); +	buf.index = buffer->index; +	buf.type = dev->type; +	buf.memory = dev->memtype; + +	if (dev->memtype == V4L2_MEMORY_USERPTR) +		buf.m.userptr = (unsigned long)dev->pool->buffers[buffer->index].mem; + +	buf.length = dev->pool->buffers[buffer->index].size; + +	if (dev->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) +		buf.bytesused = buffer->bytesused; + +	ret = ioctl(dev->fd, VIDIOC_QBUF, &buf); +	if (ret < 0) { +		printf("%s: unable to queue buffer index %u/%u (%d)\n", +		       dev->name, buf.index, dev->nbufs, errno); +		return -errno; +	} + +	return 0; +} + +/* ----------------------------------------------------------------------------- + * Stream management + */ + +int v4l2_stream_on(struct v4l2_device *dev) +{ +	int type = dev->type; +	int ret; + +	ret = ioctl(dev->fd, VIDIOC_STREAMON, &type); +	if (ret < 0) +		return -errno; + +	return 0; +} + +int v4l2_stream_off(struct v4l2_device *dev) +{ +	int type = dev->type; +	int ret; + +	ret = ioctl(dev->fd, VIDIOC_STREAMOFF, &type); +	if (ret < 0) +		return -errno; + +	return 0; +} diff --git a/plugins/v4l2/v4l2.h b/plugins/v4l2/v4l2.h new file mode 100644 index 0000000..c2b683a --- /dev/null +++ b/plugins/v4l2/v4l2.h @@ -0,0 +1,289 @@ +/* + * Xilinx Video Library - Generic V4L2 support + * + * 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. + */ +#ifndef __V4L2_H +#define __V4L2_H + +#include <linux/videodev2.h> +#include <stdbool.h> +#include <stdint.h> + +#include <xlnx-list.h> + +struct xlnx_video_buffers_pool; +struct xlnx_video_buffer; + +struct v4l2_device +{ +	int fd; +	char *name; + +	enum v4l2_buf_type type; +	enum v4l2_memory memtype; + +	struct list_entry formats; +	struct v4l2_pix_format format; +	struct v4l2_rect crop; + +	unsigned int nbufs; +	struct xlnx_video_buffers_pool *pool; +}; + +/* + * v4l2_open - Open a V4L2 device + * @devname: Name (including path) of the device node + * + * Open the V4L2 device referenced by @devname for video capture or display in + * non-blocking mode. + * + * If the device can be opened, query its capabilities and enumerates frame + * formats, sizes and intervals. + * + * Return a pointer to a newly allocated v4l2_device structure instance on + * success and NULL on failure. The returned pointer must be freed with + * v4l2_close when the device isn't needed anymore. + */ +struct v4l2_device *v4l2_open(const char *devname); + +/* + * v4l2_close - Close a V4L2 device + * @dev: Device instance + * + * Close the device instance given as argument and free allocated resources. + * Access to the device instance is forbidden after this function returns. + */ +void v4l2_close(struct v4l2_device *dev); + +/* + * v4l2_get_format - Retrieve the current pixel format + * @dev: Device instance + * @format: Pixel format structure to be filled + * + * Query the device to retrieve the current pixel format and frame size and fill + * the @format structure. + * + * Return 0 on success or a negative error code on failure. + */ +int v4l2_get_format(struct v4l2_device *dev, struct v4l2_pix_format *format); + +/* + * v4l2_set_format - Set the pixel format + * @dev: Device instance + * @format: Pixel format structure to be set + * + * Set the pixel format and frame size stored in @format. The device can modify + * the requested format and size, in which case the @format structure will be + * updated to reflect the modified settings. + * + * Return 0 on success or a negative error code on failure. + */ +int v4l2_set_format(struct v4l2_device *dev, struct v4l2_pix_format *format); + +/* + * v4l2_get_crop - Retrieve the current crop rectangle + * @dev: Device instance + * @rect: Crop rectangle structure to be filled + * + * Query the device to retrieve the current crop rectangle and fill the @rect + * structure. + * + * Return 0 on success or a negative error code on failure. + */ +int v4l2_get_crop(struct v4l2_device *dev, struct v4l2_rect *rect); + +/* + * v4l2_set_crop - Set the crop rectangle + * @dev: Device instance + * @rect: Crop rectangle structure to be set + * + * Set the crop rectangle stored in @rect. The device can modify the requested + * rectangle, in which case the @rect structure will be updated to reflect the + * modified settings. + * + * Return 0 on success or a negative error code on failure. + */ +int v4l2_set_crop(struct v4l2_device *dev, struct v4l2_rect *rect); + +/* + * v4l2_alloc_buffers - Allocate buffers for video frames + * @dev: Device instance + * @pool: Buffers pool + * @memtype: Type of buffers + * + * Request the driver to allocate the number of buffers in the pool. The driver + * can modify the number of buffers depending on its needs. The number of + * allocated buffers will be stored in the @dev::nbufs field. + * + * When @memtype is set to V4L2_MEMORY_MMAP the buffers are allocated by the + * driver and mapped to userspace. When @memtype is set to V4L2_MEMORY_USERPTR + * the driver only allocates buffer objects and relies on the application to + * provide memory storage for video frames. + * + * Upon successful allocation the @dev::pool field points to the buffers pool. + * + * Return 0 on success or a negative error code on failure. + */ +int v4l2_alloc_buffers(struct v4l2_device *dev, +		       struct xlnx_video_buffers_pool *pool, +		       enum v4l2_memory memtype); + +/* + * v4l2_free_buffers - Free buffers + * @dev: Device instance + * + * Free buffers previously allocated by v4l2_alloc_buffers(). If the buffers + * have been allocated with the V4L2_MEMORY_USERPTR memory type only the buffer + * objects are freed, and the caller is responsible for freeing the video frames + * memory if required. + * + * When successful this function sets the @dev::nbufs field to zero and the + * @dev::pool field to NULL. + * + * Return 0 on success or a negative error code on failure. + */ +int v4l2_free_buffers(struct v4l2_device *dev); + +/* + * v4l2_queue_buffer - Queue a buffer for video capture/output + * @dev: Device instance + * @buffer: Buffer to be queued + * + * Queue the buffer identified by @buffer for video capture or output, depending + * on the device type. + * + * The caller must initialize the @buffer::index field with the index of the + * buffer to be queued. The index is zero-based and must be lower than the + * number of allocated buffers. + * + * For V4L2_MEMORY_USERPTR buffers, the caller must initialize the @buffer::mem + * field with the address of the video frame memory, and the @buffer:length + * field with its size in bytes. For optimal performances the address and length + * should be identical between v4l2_queue_buffer() calls for a given buffer + * index. + * + * For video output, the caller must initialize the @buffer::bytesused field + * with the size of video data. The value should differ from the buffer length + * for variable-size video formats only. + * + * Upon successful return the buffer ownership is transferred to the driver. The + * caller must not touch video memory for that buffer before calling + * v4l2_dequeue_buffer(). Attempting to queue an already queued buffer will + * fail. + * + * Return 0 on success or a negative error code on failure. + */ +int v4l2_queue_buffer(struct v4l2_device *dev, struct xlnx_video_buffer *buffer); + +/* + * v4l2_dequeue_buffer - Dequeue the next buffer + * @dev: Device instance + * @buffer: Dequeued buffer data to be filled + * + * Dequeue the next buffer processed by the driver and fill all fields in + * @buffer.  + * + * This function does not block. If no buffer is ready it will return + * immediately with -EAGAIN. + * + * If an error occured during video capture or display, the @buffer::error field + * is set to true. Depending on the device the video data can be partly + * corrupted or complete garbage. + * + * Once dequeued the buffer ownership is transferred to the caller. Video memory + * for that buffer can be safely read from and written to. + * + * Return 0 on success or a negative error code on failure. An error that + * results in @buffer:error being set is not considered as a failure condition + * for the purpose of the return value. + */ +int v4l2_dequeue_buffer(struct v4l2_device *dev, struct xlnx_video_buffer *buffer); + +/* + * v4l2_stream_on - Start video streaming + * @dev: Device instance + * + * Start video capture or output on the device. For video output devices at + * least one buffer must be queued before starting the stream. + * + * Return 0 on success or a negative error code on failure. + */ +int v4l2_stream_on(struct v4l2_device *dev); + +/* + * v4l2_stream_off - Stop video streaming + * @dev: Device instance + * + * Stop video capture or output on the device. Upon successful return ownership + * of all buffers is returned to the caller. + * + * Return 0 on success or a negative error code on failure. + */ +int v4l2_stream_off(struct v4l2_device *dev); + +/* + * v4l2_get_control - Read the value of a control + * @dev: Device instance + * @id: Control ID + * @value: Control value to be filled + * + * Retrieve the current value of control @id and store it in @value. + * + * Return 0 on success or a negative error code on failure. + */ +int v4l2_get_control(struct v4l2_device *dev, unsigned int id, int32_t *value); + +/* + * v4l2_set_control - Write the value of a control + * @dev: Device instance + * @id: Control ID + * @value: Control value + * + * Set control @id to @value. The device is allowed to modify the requested + * value, in which case @value is updated to the modified value. + * + * Return 0 on success or a negative error code on failure. + */ +int v4l2_set_control(struct v4l2_device *dev, unsigned int id, int32_t *value); + +/* + * v4l2_get_controls - Read the value of multiple controls + * @dev: Device instance + * @count: Number of controls + * @ctrls: Controls to be read + * + * Retrieve the current value of controls identified by @ctrls. + * + * Return 0 on success or a negative error code on failure. + */ +int v4l2_get_controls(struct v4l2_device *dev, unsigned int count, +		      struct v4l2_ext_control *ctrls); + +/* + * v4l2_set_controls - Write the value of multiple controls + * @dev: Device instance + * @count: Number of controls + * @ctrls: Controls to be written + * + * Set controls identified by @ctrls. The device is allowed to modify the + * requested values, in which case @ctrls is updated to the modified value. + * + * Return 0 on success or a negative error code on failure. + */ +int v4l2_set_controls(struct v4l2_device *dev, unsigned int count, +		      struct v4l2_ext_control *ctrls); + +#endif 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, +}; | 
