diff options
author | Florian Fainelli <florian@openwrt.org> | 2014-09-27 19:11:37 +0000 |
---|---|---|
committer | Florian Fainelli <florian@openwrt.org> | 2014-09-27 19:11:37 +0000 |
commit | 3f5cc1882b585db0d3aa8846cd8a2c305201e9f9 (patch) | |
tree | 5eee81e053a752327b3da279030c8ee41e5f26b4 /target/linux/brcm2708/patches-3.14/0030-bcm2835-add-v4l2-camera-device.patch | |
parent | 04c0ef5263d7aa22e8895fdf24628e0c39215d12 (diff) | |
download | master-31e0f0ae-3f5cc1882b585db0d3aa8846cd8a2c305201e9f9.tar.gz master-31e0f0ae-3f5cc1882b585db0d3aa8846cd8a2c305201e9f9.tar.bz2 master-31e0f0ae-3f5cc1882b585db0d3aa8846cd8a2c305201e9f9.zip |
brcm2708: add 3.14 support
Signed-off-by: Florian Fainelli <florian@openwrt.org>
SVN-Revision: 42680
Diffstat (limited to 'target/linux/brcm2708/patches-3.14/0030-bcm2835-add-v4l2-camera-device.patch')
-rw-r--r-- | target/linux/brcm2708/patches-3.14/0030-bcm2835-add-v4l2-camera-device.patch | 5990 |
1 files changed, 5990 insertions, 0 deletions
diff --git a/target/linux/brcm2708/patches-3.14/0030-bcm2835-add-v4l2-camera-device.patch b/target/linux/brcm2708/patches-3.14/0030-bcm2835-add-v4l2-camera-device.patch new file mode 100644 index 0000000000..c250a051de --- /dev/null +++ b/target/linux/brcm2708/patches-3.14/0030-bcm2835-add-v4l2-camera-device.patch @@ -0,0 +1,5990 @@ +From 40c37d87f774c86135ad5b9b9313cfedf232c864 Mon Sep 17 00:00:00 2001 +From: Vincent Sanders <vincent.sanders@collabora.co.uk> +Date: Wed, 30 Jan 2013 12:45:18 +0000 +Subject: [PATCH 30/54] bcm2835: add v4l2 camera device + +- Supports raw YUV capture, preview, JPEG and H264. +- Uses videobuf2 for data transfer, using dma_buf. +- Uses 3.6.10 timestamping +- Camera power based on use +- Uses immutable input mode on video encoder + +Signed-off-by: Daniel Stone <daniels@collabora.com> +Signed-off-by: Luke Diamand <luked@broadcom.com> +--- + Documentation/video4linux/bcm2835-v4l2.txt | 60 + + drivers/media/platform/Kconfig | 1 + + drivers/media/platform/Makefile | 2 + + drivers/media/platform/bcm2835/Kconfig | 25 + + drivers/media/platform/bcm2835/Makefile | 5 + + drivers/media/platform/bcm2835/bcm2835-camera.c | 1478 +++++++++++++++++ + drivers/media/platform/bcm2835/bcm2835-camera.h | 113 ++ + drivers/media/platform/bcm2835/controls.c | 725 ++++++++ + drivers/media/platform/bcm2835/mmal-common.h | 52 + + drivers/media/platform/bcm2835/mmal-encodings.h | 93 ++ + drivers/media/platform/bcm2835/mmal-msg-common.h | 50 + + drivers/media/platform/bcm2835/mmal-msg-format.h | 81 + + drivers/media/platform/bcm2835/mmal-msg-port.h | 107 ++ + drivers/media/platform/bcm2835/mmal-msg.h | 404 +++++ + drivers/media/platform/bcm2835/mmal-parameters.h | 539 ++++++ + drivers/media/platform/bcm2835/mmal-vchiq.c | 1916 ++++++++++++++++++++++ + drivers/media/platform/bcm2835/mmal-vchiq.h | 178 ++ + 17 files changed, 5829 insertions(+) + create mode 100644 Documentation/video4linux/bcm2835-v4l2.txt + create mode 100644 drivers/media/platform/bcm2835/Kconfig + create mode 100644 drivers/media/platform/bcm2835/Makefile + create mode 100644 drivers/media/platform/bcm2835/bcm2835-camera.c + create mode 100644 drivers/media/platform/bcm2835/bcm2835-camera.h + create mode 100644 drivers/media/platform/bcm2835/controls.c + create mode 100644 drivers/media/platform/bcm2835/mmal-common.h + create mode 100644 drivers/media/platform/bcm2835/mmal-encodings.h + create mode 100644 drivers/media/platform/bcm2835/mmal-msg-common.h + create mode 100644 drivers/media/platform/bcm2835/mmal-msg-format.h + create mode 100644 drivers/media/platform/bcm2835/mmal-msg-port.h + create mode 100644 drivers/media/platform/bcm2835/mmal-msg.h + create mode 100644 drivers/media/platform/bcm2835/mmal-parameters.h + create mode 100644 drivers/media/platform/bcm2835/mmal-vchiq.c + create mode 100644 drivers/media/platform/bcm2835/mmal-vchiq.h + +diff --git a/Documentation/video4linux/bcm2835-v4l2.txt b/Documentation/video4linux/bcm2835-v4l2.txt +new file mode 100644 +index 0000000..c585a8f +--- /dev/null ++++ b/Documentation/video4linux/bcm2835-v4l2.txt +@@ -0,0 +1,60 @@ ++ ++BCM2835 (aka Raspberry Pi) V4L2 driver ++====================================== ++ ++1. Copyright ++============ ++ ++Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ ++2. License ++========== ++ ++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., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++3. Quick Start ++============== ++ ++You need a version 1.0 or later of v4l2-ctl, available from: ++ git://git.linuxtv.org/v4l-utils.git ++ ++$ sudo modprobe bcm2835-v4l2 ++ ++Turn on the overlay: ++ ++$ v4l2-ctl --overlay=1 ++ ++Turn off the overlay: ++ ++$ v4l2-ctl --overlay=0 ++ ++Set the capture format for video: ++ ++$ v4l2-ctl --set-fmt-video=width=1920,height=1088,pixelformat=4 ++ ++(Note: 1088 not 1080). ++ ++Capture: ++ ++$ v4l2-ctl --stream-mmap=3 --stream-count=100 --stream-to=somefile.h264 ++ ++Stills capture: ++ ++$ v4l2-ctl --set-fmt-video=width=2592,height=1944,pixelformat=3 ++$ v4l2-ctl --stream-mmap=3 --stream-count=1 --stream-to=somefile.jpg ++ ++List of available formats: ++ ++$ v4l2-ctl --list-formats +diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig +index b2a4403..c1f82ec 100644 +--- a/drivers/media/platform/Kconfig ++++ b/drivers/media/platform/Kconfig +@@ -118,6 +118,7 @@ config VIDEO_S3C_CAMIF + source "drivers/media/platform/soc_camera/Kconfig" + source "drivers/media/platform/exynos4-is/Kconfig" + source "drivers/media/platform/s5p-tv/Kconfig" ++source "drivers/media/platform/bcm2835/Kconfig" + + endif # V4L_PLATFORM_DRIVERS + +diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile +index e5269da..6e01549 100644 +--- a/drivers/media/platform/Makefile ++++ b/drivers/media/platform/Makefile +@@ -51,4 +51,6 @@ obj-y += davinci/ + + obj-$(CONFIG_ARCH_OMAP) += omap/ + ++obj-$(CONFIG_VIDEO_BCM2835) += bcm2835/ ++ + ccflags-y += -I$(srctree)/drivers/media/i2c +diff --git a/drivers/media/platform/bcm2835/Kconfig b/drivers/media/platform/bcm2835/Kconfig +new file mode 100644 +index 0000000..a8fd172 +--- /dev/null ++++ b/drivers/media/platform/bcm2835/Kconfig +@@ -0,0 +1,25 @@ ++# Broadcom VideoCore IV v4l2 camera support ++ ++config VIDEO_BCM2835 ++ bool "Broadcom BCM2835 camera interface driver" ++ depends on VIDEO_V4L2 && ARCH_BCM2708 ++ ---help--- ++ Say Y here to enable camera host interface devices for ++ Broadcom BCM2835 SoC. This operates over the VCHIQ interface ++ to a service running on VideoCore. ++ ++ ++if VIDEO_BCM2835 ++ ++config VIDEO_BCM2835_MMAL ++ tristate "Broadcom BM2835 MMAL camera interface driver" ++ depends on BCM2708_VCHIQ ++ select VIDEOBUF2_VMALLOC ++ ---help--- ++ This is a V4L2 driver for the Broadcom BCM2835 MMAL camera host interface ++ ++ To compile this driver as a module, choose M here: the ++ module will be called bcm2835-v4l2.o ++ ++ ++endif # VIDEO_BM2835 +diff --git a/drivers/media/platform/bcm2835/Makefile b/drivers/media/platform/bcm2835/Makefile +new file mode 100644 +index 0000000..f17c79c +--- /dev/null ++++ b/drivers/media/platform/bcm2835/Makefile +@@ -0,0 +1,5 @@ ++bcm2835-v4l2-objs := bcm2835-camera.o controls.o mmal-vchiq.o ++ ++obj-$(CONFIG_VIDEO_BCM2835_MMAL) += bcm2835-v4l2.o ++ ++ccflags-$(CONFIG_VIDEO_BCM2835) += -Idrivers/misc/vc04_services -Idrivers/misc/vc04_services/interface/vcos/linuxkernel -D__VCCOREVER__=0x04000000 +diff --git a/drivers/media/platform/bcm2835/bcm2835-camera.c b/drivers/media/platform/bcm2835/bcm2835-camera.c +new file mode 100644 +index 0000000..47fe45d +--- /dev/null ++++ b/drivers/media/platform/bcm2835/bcm2835-camera.c +@@ -0,0 +1,1478 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders <vincent.sanders@collabora.co.uk> ++ * Dave Stevenson <dsteve@broadcom.com> ++ * Simon Mellor <simellor@broadcom.com> ++ * Luke Diamand <luked@broadcom.com> ++ */ ++ ++#include <linux/errno.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/slab.h> ++#include <media/videobuf2-vmalloc.h> ++#include <media/videobuf2-dma-contig.h> ++#include <media/v4l2-device.h> ++#include <media/v4l2-ioctl.h> ++#include <media/v4l2-ctrls.h> ++#include <media/v4l2-fh.h> ++#include <media/v4l2-event.h> ++#include <media/v4l2-common.h> ++#include <linux/delay.h> ++ ++#include "mmal-common.h" ++#include "mmal-encodings.h" ++#include "mmal-vchiq.h" ++#include "mmal-msg.h" ++#include "mmal-parameters.h" ++#include "bcm2835-camera.h" ++ ++#define BM2835_MMAL_VERSION "0.0.2" ++#define BM2835_MMAL_MODULE_NAME "bcm2835-v4l2" ++ ++#define MAX_WIDTH 2592 ++#define MAX_HEIGHT 1944 ++#define MIN_BUFFER_SIZE (80*1024) ++ ++#define MAX_VIDEO_MODE_WIDTH 1280 ++#define MAX_VIDEO_MODE_HEIGHT 720 ++ ++MODULE_DESCRIPTION("Broadcom 2835 MMAL video capture"); ++MODULE_AUTHOR("Vincent Sanders"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION(BM2835_MMAL_VERSION); ++ ++int bcm2835_v4l2_debug; ++module_param_named(debug, bcm2835_v4l2_debug, int, 0644); ++MODULE_PARM_DESC(bcm2835_v4l2_debug, "Debug level 0-2"); ++ ++static struct bm2835_mmal_dev *gdev; /* global device data */ ++ ++/* video formats */ ++static struct mmal_fmt formats[] = { ++ { ++ .name = "4:2:0, packed YUV", ++ .fourcc = V4L2_PIX_FMT_YUV420, ++ .mmal = MMAL_ENCODING_I420, ++ .depth = 12, ++ .mmal_component = MMAL_COMPONENT_CAMERA, ++ }, ++ { ++ .name = "4:2:2, packed, YUYV", ++ .fourcc = V4L2_PIX_FMT_YUYV, ++ .mmal = MMAL_ENCODING_YUYV, ++ .depth = 16, ++ .mmal_component = MMAL_COMPONENT_CAMERA, ++ }, ++ { ++ .name = "RGB24 (BE)", ++ .fourcc = V4L2_PIX_FMT_BGR24, ++ .mmal = MMAL_ENCODING_BGR24, ++ .depth = 24, ++ .mmal_component = MMAL_COMPONENT_CAMERA, ++ }, ++ { ++ .name = "JPEG", ++ .fourcc = V4L2_PIX_FMT_JPEG, ++ .mmal = MMAL_ENCODING_JPEG, ++ .depth = 8, ++ .mmal_component = MMAL_COMPONENT_IMAGE_ENCODE, ++ }, ++ { ++ .name = "H264", ++ .fourcc = V4L2_PIX_FMT_H264, ++ .mmal = MMAL_ENCODING_H264, ++ .depth = 8, ++ .mmal_component = MMAL_COMPONENT_VIDEO_ENCODE, ++ } ++}; ++ ++static struct mmal_fmt *get_format(struct v4l2_format *f) ++{ ++ struct mmal_fmt *fmt; ++ unsigned int k; ++ ++ for (k = 0; k < ARRAY_SIZE(formats); k++) { ++ fmt = &formats[k]; ++ if (fmt->fourcc == f->fmt.pix.pixelformat) ++ break; ++ } ++ ++ if (k == ARRAY_SIZE(formats)) ++ return NULL; ++ ++ return &formats[k]; ++} ++ ++/* ------------------------------------------------------------------ ++ Videobuf queue operations ++ ------------------------------------------------------------------*/ ++ ++static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, ++ unsigned int *nbuffers, unsigned int *nplanes, ++ unsigned int sizes[], void *alloc_ctxs[]) ++{ ++ struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vq); ++ unsigned long size; ++ ++ /* refuse queue setup if port is not configured */ ++ if (dev->capture.port == NULL) { ++ v4l2_err(&dev->v4l2_dev, ++ "%s: capture port not configured\n", __func__); ++ return -EINVAL; ++ } ++ ++ size = dev->capture.port->current_buffer.size; ++ if (size == 0) { ++ v4l2_err(&dev->v4l2_dev, ++ "%s: capture port buffer size is zero\n", __func__); ++ return -EINVAL; ++ } ++ ++ if (*nbuffers < (dev->capture.port->current_buffer.num + 2)) ++ *nbuffers = (dev->capture.port->current_buffer.num + 2); ++ ++ *nplanes = 1; ++ ++ sizes[0] = size; ++ ++ /* ++ * videobuf2-vmalloc allocator is context-less so no need to set ++ * alloc_ctxs array. ++ */ ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "%s: dev:%p\n", ++ __func__, dev); ++ ++ return 0; ++} ++ ++static int buffer_prepare(struct vb2_buffer *vb) ++{ ++ struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vb->vb2_queue); ++ unsigned long size; ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "%s: dev:%p\n", ++ __func__, dev); ++ ++ BUG_ON(dev->capture.port == NULL); ++ BUG_ON(dev->capture.fmt == NULL); ++ ++ size = dev->capture.stride * dev->capture.height; ++ if (vb2_plane_size(vb, 0) < size) { ++ v4l2_err(&dev->v4l2_dev, ++ "%s data will not fit into plane (%lu < %lu)\n", ++ __func__, vb2_plane_size(vb, 0), size); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static inline bool is_capturing(struct bm2835_mmal_dev *dev) ++{ ++ return dev->capture.camera_port == ++ &dev-> ++ component[MMAL_COMPONENT_CAMERA]->output[MMAL_CAMERA_PORT_CAPTURE]; ++} ++ ++static void buffer_cb(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ int status, ++ struct mmal_buffer *buf, ++ unsigned long length, u32 mmal_flags, s64 dts, s64 pts) ++{ ++ struct bm2835_mmal_dev *dev = port->cb_ctx; ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "%s: status:%d, buf:%p, length:%lu, flags %u, pts %lld\n", ++ __func__, status, buf, length, mmal_flags, pts); ++ ++ if (status != 0) { ++ /* error in transfer */ ++ if (buf != NULL) { ++ /* there was a buffer with the error so return it */ ++ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); ++ } ++ return; ++ } else if (length == 0) { ++ /* stream ended */ ++ if (buf != NULL) { ++ /* this should only ever happen if the port is ++ * disabled and there are buffers still queued ++ */ ++ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); ++ pr_debug("Empty buffer"); ++ } else if (dev->capture.frame_count) { ++ /* grab another frame */ ++ if (is_capturing(dev)) { ++ pr_debug("Grab another frame"); ++ vchiq_mmal_port_parameter_set( ++ instance, ++ dev->capture. ++ camera_port, ++ MMAL_PARAMETER_CAPTURE, ++ &dev->capture. ++ frame_count, ++ sizeof(dev->capture.frame_count)); ++ } ++ } else { ++ /* signal frame completion */ ++ complete(&dev->capture.frame_cmplt); ++ } ++ } else { ++ if (dev->capture.frame_count) { ++ if (dev->capture.vc_start_timestamp != -1) { ++ s64 runtime_us = pts - ++ dev->capture.vc_start_timestamp; ++ u32 div = 0; ++ u32 rem = 0; ++ ++ div = ++ div_u64_rem(runtime_us, USEC_PER_SEC, &rem); ++ buf->vb.v4l2_buf.timestamp.tv_sec = ++ dev->capture.kernel_start_ts.tv_sec - 1 + ++ div; ++ buf->vb.v4l2_buf.timestamp.tv_usec = ++ dev->capture.kernel_start_ts.tv_usec + rem; ++ ++ if (buf->vb.v4l2_buf.timestamp.tv_usec >= ++ USEC_PER_SEC) { ++ buf->vb.v4l2_buf.timestamp.tv_sec++; ++ buf->vb.v4l2_buf.timestamp.tv_usec -= ++ USEC_PER_SEC; ++ } ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "Convert start time %d.%06d and %llu" ++ "with offset %llu to %d.%06d\n", ++ (int)dev->capture.kernel_start_ts. ++ tv_sec, ++ (int)dev->capture.kernel_start_ts. ++ tv_usec, ++ dev->capture.vc_start_timestamp, pts, ++ (int)buf->vb.v4l2_buf.timestamp.tv_sec, ++ (int)buf->vb.v4l2_buf.timestamp. ++ tv_usec); ++ } else { ++ v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); ++ } ++ ++ vb2_set_plane_payload(&buf->vb, 0, length); ++ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); ++ ++ if (mmal_flags & MMAL_BUFFER_HEADER_FLAG_EOS && ++ is_capturing(dev)) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "Grab another frame as buffer has EOS"); ++ vchiq_mmal_port_parameter_set( ++ instance, ++ dev->capture. ++ camera_port, ++ MMAL_PARAMETER_CAPTURE, ++ &dev->capture. ++ frame_count, ++ sizeof(dev->capture.frame_count)); ++ } ++ } else { ++ /* signal frame completion */ ++ complete(&dev->capture.frame_cmplt); ++ } ++ } ++} ++ ++static int enable_camera(struct bm2835_mmal_dev *dev) ++{ ++ int ret; ++ if (!dev->camera_use_count) { ++ ret = vchiq_mmal_component_enable( ++ dev->instance, ++ dev->component[MMAL_COMPONENT_CAMERA]); ++ if (ret < 0) { ++ v4l2_err(&dev->v4l2_dev, ++ "Failed enabling camera, ret %d\n", ret); ++ return -EINVAL; ++ } ++ } ++ dev->camera_use_count++; ++ v4l2_dbg(1, bcm2835_v4l2_debug, ++ &dev->v4l2_dev, "enabled camera (refcount %d)\n", ++ dev->camera_use_count); ++ return 0; ++} ++ ++static int disable_camera(struct bm2835_mmal_dev *dev) ++{ ++ int ret; ++ if (!dev->camera_use_count) { ++ v4l2_err(&dev->v4l2_dev, ++ "Disabled the camera when already disabled\n"); ++ return -EINVAL; ++ } ++ dev->camera_use_count--; ++ if (!dev->camera_use_count) { ++ unsigned int i = 0xFFFFFFFF; ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "Disabling camera\n"); ++ ret = ++ vchiq_mmal_component_disable( ++ dev->instance, ++ dev->component[MMAL_COMPONENT_CAMERA]); ++ if (ret < 0) { ++ v4l2_err(&dev->v4l2_dev, ++ "Failed disabling camera, ret %d\n", ret); ++ return -EINVAL; ++ } ++ vchiq_mmal_port_parameter_set( ++ dev->instance, ++ &dev->component[MMAL_COMPONENT_CAMERA]->control, ++ MMAL_PARAMETER_CAMERA_NUM, &i, ++ sizeof(i)); ++ } ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "Camera refcount now %d\n", dev->camera_use_count); ++ return 0; ++} ++ ++static void buffer_queue(struct vb2_buffer *vb) ++{ ++ struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vb->vb2_queue); ++ struct mmal_buffer *buf = container_of(vb, struct mmal_buffer, vb); ++ int ret; ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "%s: dev:%p buf:%p\n", __func__, dev, buf); ++ ++ buf->buffer = vb2_plane_vaddr(&buf->vb, 0); ++ buf->buffer_size = vb2_plane_size(&buf->vb, 0); ++ ++ ret = vchiq_mmal_submit_buffer(dev->instance, dev->capture.port, buf); ++ if (ret < 0) ++ v4l2_err(&dev->v4l2_dev, "%s: error submitting buffer\n", ++ __func__); ++} ++ ++static int start_streaming(struct vb2_queue *vq, unsigned int count) ++{ ++ struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vq); ++ int ret; ++ int parameter_size; ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "%s: dev:%p\n", ++ __func__, dev); ++ ++ /* ensure a format has actually been set */ ++ if (dev->capture.port == NULL) ++ return -EINVAL; ++ ++ if (enable_camera(dev) < 0) { ++ v4l2_err(&dev->v4l2_dev, "Failed to enable camera\n"); ++ return -EINVAL; ++ } ++ ++ /*init_completion(&dev->capture.frame_cmplt); */ ++ ++ /* enable frame capture */ ++ dev->capture.frame_count = 1; ++ ++ /* if the preview is not already running, wait for a few frames for AGC ++ * to settle down. ++ */ ++ if (!dev->component[MMAL_COMPONENT_PREVIEW]->enabled) ++ msleep(300); ++ ++ /* enable the connection from camera to encoder (if applicable) */ ++ if (dev->capture.camera_port != dev->capture.port ++ && dev->capture.camera_port) { ++ ret = vchiq_mmal_port_enable(dev->instance, ++ dev->capture.camera_port, NULL); ++ if (ret) { ++ v4l2_err(&dev->v4l2_dev, ++ "Failed to enable encode tunnel - error %d\n", ++ ret); ++ return -1; ++ } ++ } ++ ++ /* Get VC timestamp at this point in time */ ++ parameter_size = sizeof(dev->capture.vc_start_timestamp); ++ if (vchiq_mmal_port_parameter_get(dev->instance, ++ dev->capture.camera_port, ++ MMAL_PARAMETER_SYSTEM_TIME, ++ &dev->capture.vc_start_timestamp, ++ ¶meter_size)) { ++ v4l2_err(&dev->v4l2_dev, ++ "Failed to get VC start time - update your VC f/w\n"); ++ ++ /* Flag to indicate just to rely on kernel timestamps */ ++ dev->capture.vc_start_timestamp = -1; ++ } else ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "Start time %lld size %d\n", ++ dev->capture.vc_start_timestamp, parameter_size); ++ ++ v4l2_get_timestamp(&dev->capture.kernel_start_ts); ++ ++ /* enable the camera port */ ++ dev->capture.port->cb_ctx = dev; ++ ret = ++ vchiq_mmal_port_enable(dev->instance, dev->capture.port, buffer_cb); ++ if (ret) { ++ v4l2_err(&dev->v4l2_dev, ++ "Failed to enable capture port - error %d\n", ret); ++ return -1; ++ } ++ ++ /* capture the first frame */ ++ vchiq_mmal_port_parameter_set(dev->instance, ++ dev->capture.camera_port, ++ MMAL_PARAMETER_CAPTURE, ++ &dev->capture.frame_count, ++ sizeof(dev->capture.frame_count)); ++ return 0; ++} ++ ++/* abort streaming and wait for last buffer */ ++static int stop_streaming(struct vb2_queue *vq) ++{ ++ int ret; ++ struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vq); ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "%s: dev:%p\n", ++ __func__, dev); ++ ++ init_completion(&dev->capture.frame_cmplt); ++ dev->capture.frame_count = 0; ++ ++ /* ensure a format has actually been set */ ++ if (dev->capture.port == NULL) ++ return -EINVAL; ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "stopping capturing\n"); ++ ++ /* stop capturing frames */ ++ vchiq_mmal_port_parameter_set(dev->instance, ++ dev->capture.camera_port, ++ MMAL_PARAMETER_CAPTURE, ++ &dev->capture.frame_count, ++ sizeof(dev->capture.frame_count)); ++ ++ /* wait for last frame to complete */ ++ ret = wait_for_completion_timeout(&dev->capture.frame_cmplt, HZ); ++ if (ret <= 0) ++ v4l2_err(&dev->v4l2_dev, ++ "error %d waiting for frame completion\n", ret); ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "disabling connection\n"); ++ ++ /* disable the connection from camera to encoder */ ++ ret = vchiq_mmal_port_disable(dev->instance, dev->capture.camera_port); ++ if (!ret && dev->capture.camera_port != dev->capture.port) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "disabling port\n"); ++ ret = vchiq_mmal_port_disable(dev->instance, dev->capture.port); ++ } else if (dev->capture.camera_port != dev->capture.port) { ++ v4l2_err(&dev->v4l2_dev, "port_disable failed, error %d\n", ++ ret); ++ } ++ ++ if (disable_camera(dev) < 0) { ++ v4l2_err(&dev->v4l2_dev, "Failed to disable camera"); ++ return -EINVAL; ++ } ++ ++ return ret; ++} ++ ++static void bm2835_mmal_lock(struct vb2_queue *vq) ++{ ++ struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vq); ++ mutex_lock(&dev->mutex); ++} ++ ++static void bm2835_mmal_unlock(struct vb2_queue *vq) ++{ ++ struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vq); ++ mutex_unlock(&dev->mutex); ++} ++ ++static struct vb2_ops bm2835_mmal_video_qops = { ++ .queue_setup = queue_setup, ++ .buf_prepare = buffer_prepare, ++ .buf_queue = buffer_queue, ++ .start_streaming = start_streaming, ++ .stop_streaming = stop_streaming, ++ .wait_prepare = bm2835_mmal_unlock, ++ .wait_finish = bm2835_mmal_lock, ++}; ++ ++/* ------------------------------------------------------------------ ++ IOCTL operations ++ ------------------------------------------------------------------*/ ++ ++/* overlay ioctl */ ++static int vidioc_enum_fmt_vid_overlay(struct file *file, void *priv, ++ struct v4l2_fmtdesc *f) ++{ ++ struct mmal_fmt *fmt; ++ ++ if (f->index >= ARRAY_SIZE(formats)) ++ return -EINVAL; ++ ++ fmt = &formats[f->index]; ++ ++ strlcpy(f->description, fmt->name, sizeof(f->description)); ++ f->pixelformat = fmt->fourcc; ++ ++ return 0; ++} ++ ++static int vidioc_g_fmt_vid_overlay(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct bm2835_mmal_dev *dev = video_drvdata(file); ++ ++ f->fmt.win = dev->overlay; ++ ++ return 0; ++} ++ ++static int vidioc_try_fmt_vid_overlay(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ /* Only support one format so get the current one. */ ++ vidioc_g_fmt_vid_overlay(file, priv, f); ++ ++ /* todo: allow the size and/or offset to be changed. */ ++ return 0; ++} ++ ++static int vidioc_s_fmt_vid_overlay(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct bm2835_mmal_dev *dev = video_drvdata(file); ++ ++ vidioc_try_fmt_vid_overlay(file, priv, f); ++ ++ dev->overlay = f->fmt.win; ++ ++ /* todo: program the preview port parameters */ ++ return 0; ++} ++ ++static int vidioc_overlay(struct file *file, void *f, unsigned int on) ++{ ++ int ret; ++ struct bm2835_mmal_dev *dev = video_drvdata(file); ++ struct vchiq_mmal_port *src; ++ struct vchiq_mmal_port *dst; ++ struct mmal_parameter_displayregion prev_config = { ++ .set = MMAL_DISPLAY_SET_LAYER | MMAL_DISPLAY_SET_ALPHA | ++ MMAL_DISPLAY_SET_DEST_RECT | MMAL_DISPLAY_SET_FULLSCREEN, ++ .layer = PREVIEW_LAYER, ++ .alpha = 255, ++ .fullscreen = 0, ++ .dest_rect = { ++ .x = dev->overlay.w.left, ++ .y = dev->overlay.w.top, ++ .width = dev->overlay.w.width, ++ .height = dev->overlay.w.height, ++ }, ++ }; ++ ++ if ((on && dev->component[MMAL_COMPONENT_PREVIEW]->enabled) || ++ (!on && !dev->component[MMAL_COMPONENT_PREVIEW]->enabled)) ++ return 0; /* already in requested state */ ++ ++ src = ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_PREVIEW]; ++ ++ if (!on) { ++ /* disconnect preview ports and disable component */ ++ ret = vchiq_mmal_port_disable(dev->instance, src); ++ if (!ret) ++ ret = ++ vchiq_mmal_port_connect_tunnel(dev->instance, src, ++ NULL); ++ if (ret >= 0) ++ ret = vchiq_mmal_component_disable( ++ dev->instance, ++ dev->component[MMAL_COMPONENT_PREVIEW]); ++ ++ disable_camera(dev); ++ return ret; ++ } ++ ++ /* set preview port format and connect it to output */ ++ dst = &dev->component[MMAL_COMPONENT_PREVIEW]->input[0]; ++ ++ ret = vchiq_mmal_port_set_format(dev->instance, src); ++ if (ret < 0) ++ goto error; ++ ++ ret = vchiq_mmal_port_parameter_set(dev->instance, dst, ++ MMAL_PARAMETER_DISPLAYREGION, ++ &prev_config, sizeof(prev_config)); ++ if (ret < 0) ++ goto error; ++ ++ if (enable_camera(dev) < 0) ++ goto error; ++ ++ ret = vchiq_mmal_component_enable( ++ dev->instance, ++ dev->component[MMAL_COMPONENT_PREVIEW]); ++ if (ret < 0) ++ goto error; ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "connecting %p to %p\n", ++ src, dst); ++ ret = vchiq_mmal_port_connect_tunnel(dev->instance, src, dst); ++ if (!ret) ++ ret = vchiq_mmal_port_enable(dev->instance, src, NULL); ++error: ++ return ret; ++} ++ ++static int vidioc_g_fbuf(struct file *file, void *fh, ++ struct v4l2_framebuffer *a) ++{ ++ /* The video overlay must stay within the framebuffer and can't be ++ positioned independently. */ ++ a->flags = V4L2_FBUF_FLAG_OVERLAY; ++ ++ /* todo: v4l2_framebuffer still needs more info filling in ++ * in order to pass the v4l2-compliance test. */ ++ ++ return 0; ++} ++ ++/* input ioctls */ ++static int vidioc_enum_input(struct file *file, void *priv, ++ struct v4l2_input *inp) ++{ ++ /* only a single camera input */ ++ if (inp->index != 0) ++ return -EINVAL; ++ ++ inp->type = V4L2_INPUT_TYPE_CAMERA; ++ sprintf(inp->name, "Camera %u", inp->index); ++ return 0; ++} ++ ++static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) ++{ ++ *i = 0; ++ return 0; ++} ++ ++static int vidioc_s_input(struct file *file, void *priv, unsigned int i) ++{ ++ if (i != 0) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++/* capture ioctls */ ++static int vidioc_querycap(struct file *file, void *priv, ++ struct v4l2_capability *cap) ++{ ++ struct bm2835_mmal_dev *dev = video_drvdata(file); ++ u32 major; ++ u32 minor; ++ ++ vchiq_mmal_version(dev->instance, &major, &minor); ++ ++ strcpy(cap->driver, "bm2835 mmal"); ++ snprintf(cap->card, sizeof(cap->card), "mmal service %d.%d", ++ major, minor); ++ ++ snprintf(cap->bus_info, sizeof(cap->bus_info), ++ "platform:%s", dev->v4l2_dev.name); ++ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY | ++ V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; ++ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; ++ ++ return 0; ++} ++ ++static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_fmtdesc *f) ++{ ++ struct mmal_fmt *fmt; ++ ++ if (f->index >= ARRAY_SIZE(formats)) ++ return -EINVAL; ++ ++ fmt = &formats[f->index]; ++ ++ strlcpy(f->description, fmt->name, sizeof(f->description)); ++ f->pixelformat = fmt->fourcc; ++ return 0; ++} ++ ++static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct bm2835_mmal_dev *dev = video_drvdata(file); ++ ++ f->fmt.pix.width = dev->capture.width; ++ f->fmt.pix.height = dev->capture.height; ++ f->fmt.pix.field = V4L2_FIELD_NONE; ++ f->fmt.pix.pixelformat = dev->capture.fmt->fourcc; ++ f->fmt.pix.bytesperline = ++ (f->fmt.pix.width * dev->capture.fmt->depth) >> 3; ++ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; ++ if (dev->capture.fmt->fourcc == V4L2_PIX_FMT_JPEG ++ && f->fmt.pix.sizeimage < (100 << 10)) { ++ /* Need a minimum size for JPEG to account for EXIF. */ ++ f->fmt.pix.sizeimage = (100 << 10); ++ } ++ ++ if (dev->capture.fmt->fourcc == V4L2_PIX_FMT_YUYV || ++ dev->capture.fmt->fourcc == V4L2_PIX_FMT_UYVY) ++ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; ++ else ++ f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; ++ f->fmt.pix.priv = 0; ++ ++ v4l2_dump_pix_format(1, bcm2835_v4l2_debug, &dev->v4l2_dev, &f->fmt.pix, ++ __func__); ++ return 0; ++} ++ ++static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct bm2835_mmal_dev *dev = video_drvdata(file); ++ struct mmal_fmt *mfmt; ++ ++ mfmt = get_format(f); ++ if (!mfmt) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "Fourcc format (0x%08x) unknown.\n", ++ f->fmt.pix.pixelformat); ++ f->fmt.pix.pixelformat = formats[0].fourcc; ++ mfmt = get_format(f); ++ } ++ ++ f->fmt.pix.field = V4L2_FIELD_NONE; ++ /* image must be a multiple of 32 pixels wide and 16 lines high */ ++ v4l_bound_align_image(&f->fmt.pix.width, 48, MAX_WIDTH, 5, ++ &f->fmt.pix.height, 32, MAX_HEIGHT, 4, 0); ++ f->fmt.pix.bytesperline = (f->fmt.pix.width * mfmt->depth) >> 3; ++ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; ++ if (f->fmt.pix.sizeimage < MIN_BUFFER_SIZE) ++ f->fmt.pix.sizeimage = MIN_BUFFER_SIZE; ++ ++ if (mfmt->fourcc == V4L2_PIX_FMT_YUYV || ++ mfmt->fourcc == V4L2_PIX_FMT_UYVY) ++ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; ++ else ++ f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; ++ f->fmt.pix.priv = 0; ++ ++ v4l2_dump_pix_format(1, bcm2835_v4l2_debug, &dev->v4l2_dev, &f->fmt.pix, ++ __func__); ++ return 0; ++} ++ ++static int mmal_setup_components(struct bm2835_mmal_dev *dev, ++ struct v4l2_format *f) ++{ ++ int ret; ++ struct vchiq_mmal_port *port = NULL, *camera_port = NULL; ++ struct vchiq_mmal_component *encode_component = NULL; ++ struct mmal_fmt *mfmt = get_format(f); ++ ++ BUG_ON(!mfmt); ++ ++ if (dev->capture.encode_component) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "vid_cap - disconnect previous tunnel\n"); ++ ++ /* Disconnect any previous connection */ ++ vchiq_mmal_port_connect_tunnel(dev->instance, ++ dev->capture.camera_port, NULL); ++ dev->capture.camera_port = NULL; ++ ret = vchiq_mmal_component_disable(dev->instance, ++ dev->capture. ++ encode_component); ++ if (ret) ++ v4l2_err(&dev->v4l2_dev, ++ "Failed to disable encode component %d\n", ++ ret); ++ ++ dev->capture.encode_component = NULL; ++ } ++ /* format dependant port setup */ ++ switch (mfmt->mmal_component) { ++ case MMAL_COMPONENT_CAMERA: ++ /* Make a further decision on port based on resolution */ ++ if (f->fmt.pix.width <= MAX_VIDEO_MODE_WIDTH ++ && f->fmt.pix.height <= MAX_VIDEO_MODE_HEIGHT) ++ camera_port = port = ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_VIDEO]; ++ else ++ camera_port = port = ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_CAPTURE]; ++ break; ++ case MMAL_COMPONENT_IMAGE_ENCODE: ++ encode_component = dev->component[MMAL_COMPONENT_IMAGE_ENCODE]; ++ port = &dev->component[MMAL_COMPONENT_IMAGE_ENCODE]->output[0]; ++ camera_port = ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_CAPTURE]; ++ break; ++ case MMAL_COMPONENT_VIDEO_ENCODE: ++ encode_component = dev->component[MMAL_COMPONENT_VIDEO_ENCODE]; ++ port = &dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->output[0]; ++ camera_port = ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_VIDEO]; ++ break; ++ default: ++ break; ++ } ++ ++ if (!port) ++ return -EINVAL; ++ ++ if (encode_component) ++ camera_port->format.encoding = MMAL_ENCODING_OPAQUE; ++ else ++ camera_port->format.encoding = mfmt->mmal; ++ ++ camera_port->format.encoding_variant = 0; ++ camera_port->es.video.width = f->fmt.pix.width; ++ camera_port->es.video.height = f->fmt.pix.height; ++ camera_port->es.video.crop.x = 0; ++ camera_port->es.video.crop.y = 0; ++ camera_port->es.video.crop.width = f->fmt.pix.width; ++ camera_port->es.video.crop.height = f->fmt.pix.height; ++ camera_port->es.video.frame_rate.num = 30; ++ camera_port->es.video.frame_rate.den = 1; ++ ++ ret = vchiq_mmal_port_set_format(dev->instance, camera_port); ++ ++ if (!ret ++ && camera_port == ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_VIDEO]) { ++ bool overlay_enabled = ++ !!dev->component[MMAL_COMPONENT_PREVIEW]->enabled; ++ struct vchiq_mmal_port *preview_port = ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_PREVIEW]; ++ /* Preview and encode ports need to match on resolution */ ++ if (overlay_enabled) { ++ /* Need to disable the overlay before we can update ++ * the resolution ++ */ ++ ret = ++ vchiq_mmal_port_disable(dev->instance, ++ preview_port); ++ if (!ret) ++ ret = ++ vchiq_mmal_port_connect_tunnel( ++ dev->instance, ++ preview_port, ++ NULL); ++ } ++ preview_port->es.video.width = f->fmt.pix.width; ++ preview_port->es.video.height = f->fmt.pix.height; ++ preview_port->es.video.crop.x = 0; ++ preview_port->es.video.crop.y = 0; ++ preview_port->es.video.crop.width = f->fmt.pix.width; ++ preview_port->es.video.crop.height = f->fmt.pix.height; ++ preview_port->es.video.frame_rate.num = 30; ++ preview_port->es.video.frame_rate.den = 1; ++ ret = vchiq_mmal_port_set_format(dev->instance, preview_port); ++ if (overlay_enabled) { ++ ret = vchiq_mmal_port_connect_tunnel( ++ dev->instance, ++ preview_port, ++ &dev->component[MMAL_COMPONENT_PREVIEW]->input[0]); ++ if (!ret) ++ ret = vchiq_mmal_port_enable(dev->instance, ++ preview_port, ++ NULL); ++ } ++ } ++ ++ if (ret) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "%s failed to set format\n", __func__); ++ /* ensure capture is not going to be tried */ ++ dev->capture.port = NULL; ++ } else { ++ if (encode_component) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "vid_cap - set up encode comp\n"); ++ ++ /* configure buffering */ ++ camera_port->current_buffer.size = ++ camera_port->recommended_buffer.size; ++ camera_port->current_buffer.num = ++ camera_port->recommended_buffer.num; ++ ++ port->format.encoding = mfmt->mmal; ++ port->format.encoding_variant = 0; ++ /* Set any encoding specific parameters */ ++ switch (mfmt->mmal_component) { ++ case MMAL_COMPONENT_VIDEO_ENCODE: ++ port->format.bitrate = ++ dev->capture.encode_bitrate; ++ break; ++ case MMAL_COMPONENT_IMAGE_ENCODE: ++ /* Could set EXIF parameters here */ ++ break; ++ default: ++ break; ++ } ++ ret = vchiq_mmal_port_set_format(dev->instance, port); ++ ++ if (ret) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "%s failed to set format\n", __func__); ++ } else { ++ ret = vchiq_mmal_component_enable( ++ dev->instance, ++ encode_component); ++ if (ret) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, ++ &dev->v4l2_dev, ++ "%s Failed to enable encode components\n", ++ __func__); ++ } else { ++ /* configure buffering */ ++ port->current_buffer.num = 1; ++ port->current_buffer.size = ++ f->fmt.pix.sizeimage; ++ if (port->format.encoding == ++ MMAL_ENCODING_JPEG) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, ++ &dev->v4l2_dev, ++ "JPEG - fiddle buffer size\n"); ++ port->current_buffer.size = ++ (f->fmt.pix.sizeimage < ++ (100 << 10)) ++ ? (100 << 10) : f->fmt.pix. ++ sizeimage; ++ } ++ v4l2_dbg(1, bcm2835_v4l2_debug, ++ &dev->v4l2_dev, ++ "vid_cap - current_buffer.size being set to %d\n", ++ f->fmt.pix.sizeimage); ++ port->current_buffer.alignment = 0; ++ ret = ++ vchiq_mmal_port_connect_tunnel( ++ dev->instance, ++ camera_port, ++ &encode_component->input[0]); ++ if (ret) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, ++ &dev->v4l2_dev, ++ "%s failed to create connection\n", ++ __func__); ++ /* ensure capture is not going to be tried */ ++ dev->capture.port = NULL; ++ } ++ } ++ } ++ } else { ++ /* configure buffering */ ++ camera_port->current_buffer.num = 1; ++ camera_port->current_buffer.size = f->fmt.pix.sizeimage; ++ camera_port->current_buffer.alignment = 0; ++ } ++ ++ if (!ret) { ++ dev->capture.fmt = mfmt; ++ dev->capture.stride = f->fmt.pix.bytesperline; ++ dev->capture.width = port->es.video.crop.width; ++ dev->capture.height = port->es.video.crop.height; ++ ++ /* select port for capture */ ++ dev->capture.port = port; ++ dev->capture.camera_port = camera_port; ++ dev->capture.encode_component = encode_component; ++ } ++ } ++ ++ /* todo: Need to convert the vchiq/mmal error into a v4l2 error. */ ++ return ret; ++} ++ ++static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ int ret; ++ struct bm2835_mmal_dev *dev = video_drvdata(file); ++ struct mmal_fmt *mfmt; ++ ++ /* try the format to set valid parameters */ ++ ret = vidioc_try_fmt_vid_cap(file, priv, f); ++ if (ret) { ++ v4l2_err(&dev->v4l2_dev, ++ "vid_cap - vidioc_try_fmt_vid_cap failed\n"); ++ return ret; ++ } ++ ++ /* if a capture is running refuse to set format */ ++ if (vb2_is_busy(&dev->capture.vb_vidq)) { ++ v4l2_info(&dev->v4l2_dev, "%s device busy\n", __func__); ++ return -EBUSY; ++ } ++ ++ /* If the format is unsupported v4l2 says we should switch to ++ * a supported one and not return an error. */ ++ mfmt = get_format(f); ++ if (!mfmt) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "Fourcc format (0x%08x) unknown.\n", ++ f->fmt.pix.pixelformat); ++ f->fmt.pix.pixelformat = formats[0].fourcc; ++ mfmt = get_format(f); ++ } ++ ++ ret = mmal_setup_components(dev, f); ++ if (ret != 0) ++ v4l2_err(&dev->v4l2_dev, ++ "%s: failed to setup mmal components: %d\n", ++ __func__, ret); ++ ++ return ret; ++} ++ ++static const struct v4l2_ioctl_ops camera0_ioctl_ops = { ++ /* overlay */ ++ .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt_vid_overlay, ++ .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_vid_overlay, ++ .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_vid_overlay, ++ .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_vid_overlay, ++ .vidioc_overlay = vidioc_overlay, ++ .vidioc_g_fbuf = vidioc_g_fbuf, ++ ++ /* inputs */ ++ .vidioc_enum_input = vidioc_enum_input, ++ .vidioc_g_input = vidioc_g_input, ++ .vidioc_s_input = vidioc_s_input, ++ ++ /* capture */ ++ .vidioc_querycap = vidioc_querycap, ++ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, ++ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, ++ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, ++ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, ++ ++ /* buffer management */ ++ .vidioc_reqbufs = vb2_ioctl_reqbufs, ++ .vidioc_create_bufs = vb2_ioctl_create_bufs, ++ .vidioc_prepare_buf = vb2_ioctl_prepare_buf, ++ .vidioc_querybuf = vb2_ioctl_querybuf, ++ .vidioc_qbuf = vb2_ioctl_qbuf, ++ .vidioc_dqbuf = vb2_ioctl_dqbuf, ++ .vidioc_streamon = vb2_ioctl_streamon, ++ .vidioc_streamoff = vb2_ioctl_streamoff, ++ ++ .vidioc_log_status = v4l2_ctrl_log_status, ++ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, ++ .vidioc_unsubscribe_event = v4l2_event_unsubscribe, ++}; ++ ++/* ------------------------------------------------------------------ ++ Driver init/finalise ++ ------------------------------------------------------------------*/ ++ ++static const struct v4l2_file_operations camera0_fops = { ++ .owner = THIS_MODULE, ++ .open = v4l2_fh_open, ++ .release = vb2_fop_release, ++ .read = vb2_fop_read, ++ .poll = vb2_fop_poll, ++ .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ ++ .mmap = vb2_fop_mmap, ++}; ++ ++static struct video_device vdev_template = { ++ .name = "camera0", ++ .fops = &camera0_fops, ++ .ioctl_ops = &camera0_ioctl_ops, ++ .release = video_device_release_empty, ++}; ++ ++static int set_camera_parameters(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_component *camera) ++{ ++ int ret; ++ struct mmal_parameter_camera_config cam_config = { ++ .max_stills_w = MAX_WIDTH, ++ .max_stills_h = MAX_HEIGHT, ++ .stills_yuv422 = 1, ++ .one_shot_stills = 1, ++ .max_preview_video_w = 1920, ++ .max_preview_video_h = 1088, ++ .num_preview_video_frames = 3, ++ .stills_capture_circular_buffer_height = 0, ++ .fast_preview_resume = 0, ++ .use_stc_timestamp = MMAL_PARAM_TIMESTAMP_MODE_RAW_STC ++ }; ++ ++ ret = vchiq_mmal_port_parameter_set(instance, &camera->control, ++ MMAL_PARAMETER_CAMERA_CONFIG, ++ &cam_config, sizeof(cam_config)); ++ return ret; ++} ++ ++/* MMAL instance and component init */ ++static int __init mmal_init(struct bm2835_mmal_dev *dev) ++{ ++ int ret; ++ struct mmal_es_format *format; ++ ++ ret = vchiq_mmal_init(&dev->instance); ++ if (ret < 0) ++ return ret; ++ ++ /* get the camera component ready */ ++ ret = vchiq_mmal_component_init(dev->instance, "ril.camera", ++ &dev->component[MMAL_COMPONENT_CAMERA]); ++ if (ret < 0) ++ goto unreg_mmal; ++ ++ if (dev->component[MMAL_COMPONENT_CAMERA]->outputs < ++ MMAL_CAMERA_PORT_COUNT) { ++ ret = -EINVAL; ++ goto unreg_camera; ++ } ++ ++ ret = set_camera_parameters(dev->instance, ++ dev->component[MMAL_COMPONENT_CAMERA]); ++ if (ret < 0) ++ goto unreg_camera; ++ ++ format = ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_PREVIEW].format; ++ ++ format->encoding = MMAL_ENCODING_OPAQUE; ++ format->encoding_variant = MMAL_ENCODING_I420; ++ ++ format->es->video.width = 1024; ++ format->es->video.height = 768; ++ format->es->video.crop.x = 0; ++ format->es->video.crop.y = 0; ++ format->es->video.crop.width = 1024; ++ format->es->video.crop.height = 768; ++ format->es->video.frame_rate.num = PREVIEW_FRAME_RATE_NUM; ++ format->es->video.frame_rate.den = PREVIEW_FRAME_RATE_DEN; ++ ++ format = ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_VIDEO].format; ++ ++ format->encoding = MMAL_ENCODING_OPAQUE; ++ format->encoding_variant = MMAL_ENCODING_I420; ++ ++ format->es->video.width = 1024; ++ format->es->video.height = 768; ++ format->es->video.crop.x = 0; ++ format->es->video.crop.y = 0; ++ format->es->video.crop.width = 1024; ++ format->es->video.crop.height = 768; ++ format->es->video.frame_rate.num = PREVIEW_FRAME_RATE_NUM; ++ format->es->video.frame_rate.den = PREVIEW_FRAME_RATE_DEN; ++ ++ format = ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_CAPTURE].format; ++ ++ format->encoding = MMAL_ENCODING_OPAQUE; ++ ++ format->es->video.width = 2592; ++ format->es->video.height = 1944; ++ format->es->video.crop.x = 0; ++ format->es->video.crop.y = 0; ++ format->es->video.crop.width = 2592; ++ format->es->video.crop.height = 1944; ++ format->es->video.frame_rate.num = 30; ++ format->es->video.frame_rate.den = 1; ++ ++ dev->capture.width = format->es->video.width; ++ dev->capture.height = format->es->video.height; ++ dev->capture.fmt = &formats[0]; ++ dev->capture.encode_component = NULL; ++ ++ /* get the preview component ready */ ++ ret = vchiq_mmal_component_init( ++ dev->instance, "ril.video_render", ++ &dev->component[MMAL_COMPONENT_PREVIEW]); ++ if (ret < 0) ++ goto unreg_camera; ++ ++ if (dev->component[MMAL_COMPONENT_PREVIEW]->inputs < 1) { ++ ret = -EINVAL; ++ pr_debug("too few input ports %d needed %d\n", ++ dev->component[MMAL_COMPONENT_PREVIEW]->inputs, 1); ++ goto unreg_preview; ++ } ++ ++ /* get the image encoder component ready */ ++ ret = vchiq_mmal_component_init( ++ dev->instance, "ril.image_encode", ++ &dev->component[MMAL_COMPONENT_IMAGE_ENCODE]); ++ if (ret < 0) ++ goto unreg_preview; ++ ++ if (dev->component[MMAL_COMPONENT_IMAGE_ENCODE]->inputs < 1) { ++ ret = -EINVAL; ++ v4l2_err(&dev->v4l2_dev, "too few input ports %d needed %d\n", ++ dev->component[MMAL_COMPONENT_IMAGE_ENCODE]->inputs, ++ 1); ++ goto unreg_image_encoder; ++ } ++ ++ /* get the video encoder component ready */ ++ ret = vchiq_mmal_component_init(dev->instance, "ril.video_encode", ++ &dev-> ++ component[MMAL_COMPONENT_VIDEO_ENCODE]); ++ if (ret < 0) ++ goto unreg_image_encoder; ++ ++ if (dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->inputs < 1) { ++ ret = -EINVAL; ++ v4l2_err(&dev->v4l2_dev, "too few input ports %d needed %d\n", ++ dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->inputs, ++ 1); ++ goto unreg_vid_encoder; ++ } ++ ++ { ++ unsigned int enable = 1; ++ vchiq_mmal_port_parameter_set( ++ dev->instance, ++ &dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->control, ++ MMAL_PARAMETER_VIDEO_IMMUTABLE_INPUT, ++ &enable, sizeof(enable)); ++ ++ vchiq_mmal_port_parameter_set(dev->instance, ++ &dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->control, ++ MMAL_PARAMETER_MINIMISE_FRAGMENTATION, ++ &enable, ++ sizeof(enable)); ++ } ++ ret = bm2835_mmal_set_all_camera_controls(dev); ++ if (ret < 0) ++ goto unreg_vid_encoder; ++ ++ return 0; ++ ++unreg_vid_encoder: ++ pr_err("Cleanup: Destroy video encoder\n"); ++ vchiq_mmal_component_finalise( ++ dev->instance, ++ dev->component[MMAL_COMPONENT_VIDEO_ENCODE]); ++ ++unreg_image_encoder: ++ pr_err("Cleanup: Destroy image encoder\n"); ++ vchiq_mmal_component_finalise( ++ dev->instance, ++ dev->component[MMAL_COMPONENT_IMAGE_ENCODE]); ++ ++unreg_preview: ++ pr_err("Cleanup: Destroy video render\n"); ++ vchiq_mmal_component_finalise(dev->instance, ++ dev->component[MMAL_COMPONENT_PREVIEW]); ++ ++unreg_camera: ++ pr_err("Cleanup: Destroy camera\n"); ++ vchiq_mmal_component_finalise(dev->instance, ++ dev->component[MMAL_COMPONENT_CAMERA]); ++ ++unreg_mmal: ++ vchiq_mmal_finalise(dev->instance); ++ return ret; ++} ++ ++static int __init bm2835_mmal_init_device(struct bm2835_mmal_dev *dev, ++ struct video_device *vfd) ++{ ++ int ret; ++ ++ *vfd = vdev_template; ++ ++ vfd->v4l2_dev = &dev->v4l2_dev; ++ ++ vfd->lock = &dev->mutex; ++ ++ vfd->queue = &dev->capture.vb_vidq; ++ ++ set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); ++ ++ /* video device needs to be able to access instance data */ ++ video_set_drvdata(vfd, dev); ++ ++ ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); ++ if (ret < 0) ++ return ret; ++ ++ v4l2_info(vfd->v4l2_dev, "V4L2 device registered as %s\n", ++ video_device_node_name(vfd)); ++ ++ return 0; ++} ++ ++static struct v4l2_format default_v4l2_format = { ++ .fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG, ++ .fmt.pix.width = 1024, ++ .fmt.pix.bytesperline = 1024 * 3 / 2, ++ .fmt.pix.height = 768, ++ .fmt.pix.sizeimage = 1<<18, ++}; ++ ++static int __init bm2835_mmal_init(void) ++{ ++ int ret; ++ struct bm2835_mmal_dev *dev; ++ struct vb2_queue *q; ++ ++ dev = kzalloc(sizeof(*gdev), GFP_KERNEL); ++ if (!dev) ++ return -ENOMEM; ++ ++ /* setup device defaults */ ++ dev->overlay.w.left = 150; ++ dev->overlay.w.top = 50; ++ dev->overlay.w.width = 1024; ++ dev->overlay.w.height = 768; ++ dev->overlay.clipcount = 0; ++ dev->overlay.field = V4L2_FIELD_NONE; ++ ++ dev->capture.fmt = &formats[3]; /* JPEG */ ++ ++ /* v4l device registration */ ++ snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name), ++ "%s", BM2835_MMAL_MODULE_NAME); ++ ret = v4l2_device_register(NULL, &dev->v4l2_dev); ++ if (ret) ++ goto free_dev; ++ ++ /* setup v4l controls */ ++ ret = bm2835_mmal_init_controls(dev, &dev->ctrl_handler); ++ if (ret < 0) ++ goto unreg_dev; ++ dev->v4l2_dev.ctrl_handler = &dev->ctrl_handler; ++ ++ /* mmal init */ ++ ret = mmal_init(dev); ++ if (ret < 0) ++ goto unreg_dev; ++ ++ /* initialize queue */ ++ q = &dev->capture.vb_vidq; ++ memset(q, 0, sizeof(*q)); ++ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; ++ q->drv_priv = dev; ++ q->buf_struct_size = sizeof(struct mmal_buffer); ++ q->ops = &bm2835_mmal_video_qops; ++ q->mem_ops = &vb2_vmalloc_memops; ++ q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; ++ ret = vb2_queue_init(q); ++ if (ret < 0) ++ goto unreg_dev; ++ ++ /* v4l2 core mutex used to protect all fops and v4l2 ioctls. */ ++ mutex_init(&dev->mutex); ++ ++ /* initialise video devices */ ++ ret = bm2835_mmal_init_device(dev, &dev->vdev); ++ if (ret < 0) ++ goto unreg_dev; ++ ++ ret = mmal_setup_components(dev, &default_v4l2_format); ++ if (ret < 0) { ++ v4l2_err(&dev->v4l2_dev, ++ "%s: could not setup components\n", __func__); ++ goto unreg_dev; ++ } ++ ++ v4l2_info(&dev->v4l2_dev, ++ "Broadcom 2835 MMAL video capture ver %s loaded.\n", ++ BM2835_MMAL_VERSION); ++ ++ gdev = dev; ++ return 0; ++ ++unreg_dev: ++ v4l2_ctrl_handler_free(&dev->ctrl_handler); ++ v4l2_device_unregister(&dev->v4l2_dev); ++ ++free_dev: ++ kfree(dev); ++ ++ v4l2_err(&dev->v4l2_dev, ++ "%s: error %d while loading driver\n", ++ BM2835_MMAL_MODULE_NAME, ret); ++ ++ return ret; ++} ++ ++static void __exit bm2835_mmal_exit(void) ++{ ++ if (!gdev) ++ return; ++ ++ v4l2_info(&gdev->v4l2_dev, "unregistering %s\n", ++ video_device_node_name(&gdev->vdev)); ++ ++ video_unregister_device(&gdev->vdev); ++ ++ if (gdev->capture.encode_component) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, &gdev->v4l2_dev, ++ "mmal_exit - disconnect tunnel\n"); ++ vchiq_mmal_port_connect_tunnel(gdev->instance, ++ gdev->capture.camera_port, NULL); ++ vchiq_mmal_component_disable(gdev->instance, ++ gdev->capture.encode_component); ++ } ++ vchiq_mmal_component_disable(gdev->instance, ++ gdev->component[MMAL_COMPONENT_CAMERA]); ++ ++ vchiq_mmal_component_finalise(gdev->instance, ++ gdev-> ++ component[MMAL_COMPONENT_VIDEO_ENCODE]); ++ ++ vchiq_mmal_component_finalise(gdev->instance, ++ gdev-> ++ component[MMAL_COMPONENT_IMAGE_ENCODE]); ++ ++ vchiq_mmal_component_finalise(gdev->instance, ++ gdev->component[MMAL_COMPONENT_PREVIEW]); ++ ++ vchiq_mmal_component_finalise(gdev->instance, ++ gdev->component[MMAL_COMPONENT_CAMERA]); ++ ++ vchiq_mmal_finalise(gdev->instance); ++ ++ v4l2_ctrl_handler_free(&gdev->ctrl_handler); ++ ++ v4l2_device_unregister(&gdev->v4l2_dev); ++ ++ kfree(gdev); ++} ++ ++module_init(bm2835_mmal_init); ++module_exit(bm2835_mmal_exit); +diff --git a/drivers/media/platform/bcm2835/bcm2835-camera.h b/drivers/media/platform/bcm2835/bcm2835-camera.h +new file mode 100644 +index 0000000..883eab7 +--- /dev/null ++++ b/drivers/media/platform/bcm2835/bcm2835-camera.h +@@ -0,0 +1,113 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders <vincent.sanders@collabora.co.uk> ++ * Dave Stevenson <dsteve@broadcom.com> ++ * Simon Mellor <simellor@broadcom.com> ++ * Luke Diamand <luked@broadcom.com> ++ * ++ * core driver device ++ */ ++ ++#define V4L2_CTRL_COUNT 18 /* number of v4l controls */ ++ ++enum { ++ MMAL_COMPONENT_CAMERA = 0, ++ MMAL_COMPONENT_PREVIEW, ++ MMAL_COMPONENT_IMAGE_ENCODE, ++ MMAL_COMPONENT_VIDEO_ENCODE, ++ MMAL_COMPONENT_COUNT ++}; ++ ++enum { ++ MMAL_CAMERA_PORT_PREVIEW = 0, ++ MMAL_CAMERA_PORT_VIDEO, ++ MMAL_CAMERA_PORT_CAPTURE, ++ MMAL_CAMERA_PORT_COUNT ++}; ++ ++#define PREVIEW_FRAME_RATE_NUM 30 ++#define PREVIEW_FRAME_RATE_DEN 1 ++ ++#define PREVIEW_LAYER 2 ++ ++extern int bcm2835_v4l2_debug; ++ ++struct bm2835_mmal_dev { ++ /* v4l2 devices */ ++ struct v4l2_device v4l2_dev; ++ struct video_device vdev; ++ struct mutex mutex; ++ ++ /* controls */ ++ struct v4l2_ctrl_handler ctrl_handler; ++ struct v4l2_ctrl *ctrls[V4L2_CTRL_COUNT]; ++ struct mmal_colourfx colourfx; ++ int hflip; ++ int vflip; ++ ++ /* allocated mmal instance and components */ ++ struct vchiq_mmal_instance *instance; ++ struct vchiq_mmal_component *component[MMAL_COMPONENT_COUNT]; ++ int camera_use_count; ++ ++ struct v4l2_window overlay; ++ ++ struct { ++ unsigned int width; /* width */ ++ unsigned int height; /* height */ ++ unsigned int stride; /* stride */ ++ struct mmal_fmt *fmt; ++ ++ /* H264 encode bitrate */ ++ int encode_bitrate; ++ /* H264 bitrate mode. CBR/VBR */ ++ int encode_bitrate_mode; ++ /* JPEG Q-factor */ ++ int q_factor; ++ ++ struct vb2_queue vb_vidq; ++ ++ /* VC start timestamp for streaming */ ++ s64 vc_start_timestamp; ++ /* Kernel start timestamp for streaming */ ++ struct timeval kernel_start_ts; ++ ++ struct vchiq_mmal_port *port; /* port being used for capture */ ++ /* camera port being used for capture */ ++ struct vchiq_mmal_port *camera_port; ++ /* component being used for encode */ ++ struct vchiq_mmal_component *encode_component; ++ /* number of frames remaining which driver should capture */ ++ unsigned int frame_count; ++ /* last frame completion */ ++ struct completion frame_cmplt; ++ ++ } capture; ++ ++}; ++ ++int bm2835_mmal_init_controls( ++ struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl_handler *hdl); ++ ++int bm2835_mmal_set_all_camera_controls(struct bm2835_mmal_dev *dev); ++ ++ ++/* Debug helpers */ ++ ++#define v4l2_dump_pix_format(level, debug, dev, pix_fmt, desc) \ ++{ \ ++ v4l2_dbg(level, debug, dev, \ ++"%s: w %u h %u field %u pfmt 0x%x bpl %u sz_img %u colorspace 0x%x priv %u\n", \ ++ desc == NULL ? "" : desc, \ ++ (pix_fmt)->width, (pix_fmt)->height, (pix_fmt)->field, \ ++ (pix_fmt)->pixelformat, (pix_fmt)->bytesperline, \ ++ (pix_fmt)->sizeimage, (pix_fmt)->colorspace, (pix_fmt)->priv); \ ++} +diff --git a/drivers/media/platform/bcm2835/controls.c b/drivers/media/platform/bcm2835/controls.c +new file mode 100644 +index 0000000..d1408e5 +--- /dev/null ++++ b/drivers/media/platform/bcm2835/controls.c +@@ -0,0 +1,725 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders <vincent.sanders@collabora.co.uk> ++ * Dave Stevenson <dsteve@broadcom.com> ++ * Simon Mellor <simellor@broadcom.com> ++ * Luke Diamand <luked@broadcom.com> ++ */ ++ ++#include <linux/errno.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/slab.h> ++#include <media/videobuf2-vmalloc.h> ++#include <media/v4l2-device.h> ++#include <media/v4l2-ioctl.h> ++#include <media/v4l2-ctrls.h> ++#include <media/v4l2-fh.h> ++#include <media/v4l2-event.h> ++#include <media/v4l2-common.h> ++ ++#include "mmal-common.h" ++#include "mmal-vchiq.h" ++#include "mmal-parameters.h" ++#include "bcm2835-camera.h" ++ ++/* The supported V4L2_CID_AUTO_EXPOSURE_BIAS values are from -24 to +24. ++ * These are in 1/6th increments so the effective range is -4.0EV to +4.0EV. ++ */ ++static const s64 ev_bias_qmenu[] = { ++ -24, -21, -18, -15, -12, -9, -6, -3, 0, 3, 6, 9, 12, 15, 18, 21, 24 ++}; ++ ++/* Supported ISO values ++ * ISOO = auto ISO ++ */ ++static const s64 iso_qmenu[] = { ++ 0, 100, 200, 400, 800, ++}; ++ ++/* Supported video encode modes */ ++static const s64 bitrate_mode_qmenu[] = { ++ (s64)V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, ++ (s64)V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, ++}; ++ ++ ++enum bm2835_mmal_ctrl_type { ++ MMAL_CONTROL_TYPE_STD, ++ MMAL_CONTROL_TYPE_STD_MENU, ++ MMAL_CONTROL_TYPE_INT_MENU, ++ MMAL_CONTROL_TYPE_CLUSTER, /* special cluster entry */ ++}; ++ ++struct bm2835_mmal_v4l2_ctrl; ++ ++typedef int(bm2835_mmal_v4l2_ctrl_cb)( ++ struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl); ++ ++struct bm2835_mmal_v4l2_ctrl { ++ u32 id; /* v4l2 control identifier */ ++ enum bm2835_mmal_ctrl_type type; ++ /* control minimum value or ++ * mask for MMAL_CONTROL_TYPE_STD_MENU */ ++ s32 min; ++ s32 max; /* maximum value of control */ ++ s32 def; /* default value of control */ ++ s32 step; /* step size of the control */ ++ const s64 *imenu; /* integer menu array */ ++ u32 mmal_id; /* mmal parameter id */ ++ bm2835_mmal_v4l2_ctrl_cb *setter; ++}; ++ ++struct v4l2_to_mmal_effects_setting { ++ u32 v4l2_effect; ++ u32 mmal_effect; ++ s32 col_fx_enable; ++ s32 col_fx_fixed_cbcr; ++ u32 u; ++ u32 v; ++ u32 num_effect_params; ++ u32 effect_params[MMAL_MAX_IMAGEFX_PARAMETERS]; ++}; ++ ++static const struct v4l2_to_mmal_effects_setting ++ v4l2_to_mmal_effects_values[] = { ++ { V4L2_COLORFX_NONE, MMAL_PARAM_IMAGEFX_NONE, ++ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_BW, MMAL_PARAM_IMAGEFX_NONE, ++ 1, 0, 128, 128, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_SEPIA, MMAL_PARAM_IMAGEFX_NONE, ++ 1, 0, 87, 151, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_NEGATIVE, MMAL_PARAM_IMAGEFX_NEGATIVE, ++ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_EMBOSS, MMAL_PARAM_IMAGEFX_EMBOSS, ++ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_SKETCH, MMAL_PARAM_IMAGEFX_SKETCH, ++ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_SKY_BLUE, MMAL_PARAM_IMAGEFX_PASTEL, ++ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_GRASS_GREEN, MMAL_PARAM_IMAGEFX_WATERCOLOUR, ++ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_SKIN_WHITEN, MMAL_PARAM_IMAGEFX_WASHEDOUT, ++ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_VIVID, MMAL_PARAM_IMAGEFX_SATURATION, ++ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_AQUA, MMAL_PARAM_IMAGEFX_NONE, ++ 1, 0, 171, 121, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_ART_FREEZE, MMAL_PARAM_IMAGEFX_HATCH, ++ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_SILHOUETTE, MMAL_PARAM_IMAGEFX_FILM, ++ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_SOLARIZATION, MMAL_PARAM_IMAGEFX_SOLARIZE, ++ 0, 0, 0, 0, 5, {1, 128, 160, 160, 48} }, ++ { V4L2_COLORFX_ANTIQUE, MMAL_PARAM_IMAGEFX_COLOURBALANCE, ++ 0, 0, 0, 0, 3, {108, 274, 238, 0, 0} }, ++ { V4L2_COLORFX_SET_CBCR, MMAL_PARAM_IMAGEFX_NONE, ++ 1, 1, 0, 0, 0, {0, 0, 0, 0, 0} } ++}; ++ ++ ++/* control handlers*/ ++ ++static int ctrl_set_rational(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ struct { ++ s32 num; /**< Numerator */ ++ s32 den; /**< Denominator */ ++ } rational_value; ++ struct vchiq_mmal_port *control; ++ ++ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; ++ ++ rational_value.num = ctrl->val; ++ rational_value.den = 100; ++ ++ return vchiq_mmal_port_parameter_set(dev->instance, control, ++ mmal_ctrl->mmal_id, ++ &rational_value, ++ sizeof(rational_value)); ++} ++ ++static int ctrl_set_value(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ u32 u32_value; ++ struct vchiq_mmal_port *control; ++ ++ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; ++ ++ u32_value = ctrl->val; ++ ++ return vchiq_mmal_port_parameter_set(dev->instance, control, ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++} ++ ++static int ctrl_set_rotate(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ int ret; ++ u32 u32_value; ++ struct vchiq_mmal_component *camera; ++ ++ camera = dev->component[MMAL_COMPONENT_CAMERA]; ++ ++ u32_value = ((ctrl->val % 360) / 90) * 90; ++ ++ ret = vchiq_mmal_port_parameter_set(dev->instance, &camera->output[0], ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++ if (ret < 0) ++ return ret; ++ ++ ret = vchiq_mmal_port_parameter_set(dev->instance, &camera->output[1], ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++ if (ret < 0) ++ return ret; ++ ++ ret = vchiq_mmal_port_parameter_set(dev->instance, &camera->output[2], ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++ ++ return ret; ++} ++ ++static int ctrl_set_flip(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ int ret; ++ u32 u32_value; ++ struct vchiq_mmal_component *camera; ++ ++ if (ctrl->id == V4L2_CID_HFLIP) ++ dev->hflip = ctrl->val; ++ else ++ dev->vflip = ctrl->val; ++ ++ camera = dev->component[MMAL_COMPONENT_CAMERA]; ++ ++ if (dev->hflip && dev->vflip) ++ u32_value = MMAL_PARAM_MIRROR_BOTH; ++ else if (dev->hflip) ++ u32_value = MMAL_PARAM_MIRROR_HORIZONTAL; ++ else if (dev->vflip) ++ u32_value = MMAL_PARAM_MIRROR_VERTICAL; ++ else ++ u32_value = MMAL_PARAM_MIRROR_NONE; ++ ++ ret = vchiq_mmal_port_parameter_set(dev->instance, &camera->output[0], ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++ if (ret < 0) ++ return ret; ++ ++ ret = vchiq_mmal_port_parameter_set(dev->instance, &camera->output[1], ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++ if (ret < 0) ++ return ret; ++ ++ ret = vchiq_mmal_port_parameter_set(dev->instance, &camera->output[2], ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++ ++ return ret; ++ ++} ++ ++static int ctrl_set_exposure(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ u32 u32_value; ++ struct vchiq_mmal_port *control; ++ ++ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; ++ ++ switch (ctrl->val) { ++ case V4L2_EXPOSURE_AUTO: ++ u32_value = MMAL_PARAM_EXPOSUREMODE_AUTO; ++ break; ++ ++ case V4L2_EXPOSURE_MANUAL: ++ u32_value = MMAL_PARAM_EXPOSUREMODE_OFF; ++ break; ++ ++ case V4L2_EXPOSURE_SHUTTER_PRIORITY: ++ u32_value = MMAL_PARAM_EXPOSUREMODE_SPORTS; ++ break; ++ ++ case V4L2_EXPOSURE_APERTURE_PRIORITY: ++ u32_value = MMAL_PARAM_EXPOSUREMODE_NIGHT; ++ break; ++ ++ } ++ ++ /* todo: what about the other ten modes there are MMAL parameters for */ ++ return vchiq_mmal_port_parameter_set(dev->instance, control, ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++} ++ ++static int ctrl_set_metering_mode(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ u32 u32_value; ++ struct vchiq_mmal_port *control; ++ ++ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; ++ ++ switch (ctrl->val) { ++ case V4L2_EXPOSURE_METERING_AVERAGE: ++ u32_value = MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE; ++ break; ++ ++ case V4L2_EXPOSURE_METERING_CENTER_WEIGHTED: ++ u32_value = MMAL_PARAM_EXPOSUREMETERINGMODE_BACKLIT; ++ break; ++ ++ case V4L2_EXPOSURE_METERING_SPOT: ++ u32_value = MMAL_PARAM_EXPOSUREMETERINGMODE_SPOT; ++ break; ++ ++ /* todo matrix weighting not added to Linux API till 3.9 ++ case V4L2_EXPOSURE_METERING_MATRIX: ++ u32_value = MMAL_PARAM_EXPOSUREMETERINGMODE_MATRIX; ++ break; ++ */ ++ ++ } ++ ++ return vchiq_mmal_port_parameter_set(dev->instance, control, ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++} ++ ++static int ctrl_set_awb_mode(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ u32 u32_value; ++ struct vchiq_mmal_port *control; ++ ++ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; ++ ++ switch (ctrl->val) { ++ case V4L2_WHITE_BALANCE_MANUAL: ++ u32_value = MMAL_PARAM_AWBMODE_OFF; ++ break; ++ ++ case V4L2_WHITE_BALANCE_AUTO: ++ u32_value = MMAL_PARAM_AWBMODE_AUTO; ++ break; ++ ++ case V4L2_WHITE_BALANCE_INCANDESCENT: ++ u32_value = MMAL_PARAM_AWBMODE_INCANDESCENT; ++ break; ++ ++ case V4L2_WHITE_BALANCE_FLUORESCENT: ++ u32_value = MMAL_PARAM_AWBMODE_FLUORESCENT; ++ break; ++ ++ case V4L2_WHITE_BALANCE_FLUORESCENT_H: ++ u32_value = MMAL_PARAM_AWBMODE_TUNGSTEN; ++ break; ++ ++ case V4L2_WHITE_BALANCE_HORIZON: ++ u32_value = MMAL_PARAM_AWBMODE_HORIZON; ++ break; ++ ++ case V4L2_WHITE_BALANCE_DAYLIGHT: ++ u32_value = MMAL_PARAM_AWBMODE_SUNLIGHT; ++ break; ++ ++ case V4L2_WHITE_BALANCE_FLASH: ++ u32_value = MMAL_PARAM_AWBMODE_FLASH; ++ break; ++ ++ case V4L2_WHITE_BALANCE_CLOUDY: ++ u32_value = MMAL_PARAM_AWBMODE_CLOUDY; ++ break; ++ ++ case V4L2_WHITE_BALANCE_SHADE: ++ u32_value = MMAL_PARAM_AWBMODE_SHADE; ++ break; ++ ++ } ++ ++ return vchiq_mmal_port_parameter_set(dev->instance, control, ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++} ++ ++static int ctrl_set_image_effect(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ int ret = -EINVAL; ++ int i, j; ++ struct vchiq_mmal_port *control; ++ struct mmal_parameter_imagefx_parameters imagefx; ++ ++ for (i = 0; i < ARRAY_SIZE(v4l2_to_mmal_effects_values); i++) { ++ if (ctrl->val == v4l2_to_mmal_effects_values[i].v4l2_effect) { ++ ++ imagefx.effect = ++ v4l2_to_mmal_effects_values[i].mmal_effect; ++ imagefx.num_effect_params = ++ v4l2_to_mmal_effects_values[i].num_effect_params; ++ ++ if (imagefx.num_effect_params > MMAL_MAX_IMAGEFX_PARAMETERS) ++ imagefx.num_effect_params = MMAL_MAX_IMAGEFX_PARAMETERS; ++ ++ for (j = 0; j < imagefx.num_effect_params; j++) ++ imagefx.effect_parameter[j] = ++ v4l2_to_mmal_effects_values[i].effect_params[j]; ++ ++ dev->colourfx.enable = ++ v4l2_to_mmal_effects_values[i].col_fx_enable; ++ if (!v4l2_to_mmal_effects_values[i].col_fx_fixed_cbcr) { ++ dev->colourfx.u = ++ v4l2_to_mmal_effects_values[i].u; ++ dev->colourfx.v = ++ v4l2_to_mmal_effects_values[i].v; ++ } ++ ++ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; ++ ++ ret = vchiq_mmal_port_parameter_set( ++ dev->instance, control, ++ MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS, ++ &imagefx, sizeof(imagefx)); ++ if (ret) ++ goto exit; ++ ++ ret = vchiq_mmal_port_parameter_set( ++ dev->instance, control, ++ MMAL_PARAMETER_COLOUR_EFFECT, ++ &dev->colourfx, sizeof(dev->colourfx)); ++ } ++ } ++ ++exit: ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "mmal_ctrl:%p ctrl id:0x%x ctrl val:%d imagefx:0x%x color_effect:%s u:%d v:%d ret %d(%d)\n", ++ mmal_ctrl, ctrl->id, ctrl->val, imagefx.effect, ++ dev->colourfx.enable ? "true" : "false", ++ dev->colourfx.u, dev->colourfx.v, ++ ret, (ret == 0 ? 0 : -EINVAL)); ++ return (ret == 0 ? 0 : EINVAL); ++} ++ ++static int ctrl_set_colfx(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ int ret = -EINVAL; ++ struct vchiq_mmal_port *control; ++ ++ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; ++ ++ dev->colourfx.enable = (ctrl->val & 0xff00) >> 8; ++ dev->colourfx.enable = ctrl->val & 0xff; ++ ++ ret = vchiq_mmal_port_parameter_set(dev->instance, control, ++ MMAL_PARAMETER_COLOUR_EFFECT, ++ &dev->colourfx, sizeof(dev->colourfx)); ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "After: mmal_ctrl:%p ctrl id:0x%x ctrl val:%d ret %d(%d)\n", ++ mmal_ctrl, ctrl->id, ctrl->val, ret, ++ (ret == 0 ? 0 : -EINVAL)); ++ return (ret == 0 ? 0 : EINVAL); ++} ++ ++static int ctrl_set_bitrate(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ int ret; ++ struct vchiq_mmal_port *encoder_out; ++ ++ dev->capture.encode_bitrate = ctrl->val; ++ ++ encoder_out = &dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->output[0]; ++ ++ ret = vchiq_mmal_port_parameter_set(dev->instance, encoder_out, ++ mmal_ctrl->mmal_id, ++ &ctrl->val, sizeof(ctrl->val)); ++ ret = 0; ++ return ret; ++} ++ ++static int ctrl_set_bitrate_mode(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ u32 bitrate_mode; ++ struct vchiq_mmal_port *encoder_out; ++ ++ encoder_out = &dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->output[0]; ++ ++ dev->capture.encode_bitrate_mode = ctrl->val; ++ switch (ctrl->val) { ++ default: ++ case V4L2_MPEG_VIDEO_BITRATE_MODE_VBR: ++ bitrate_mode = MMAL_VIDEO_RATECONTROL_VARIABLE; ++ break; ++ case V4L2_MPEG_VIDEO_BITRATE_MODE_CBR: ++ bitrate_mode = MMAL_VIDEO_RATECONTROL_CONSTANT; ++ break; ++ } ++ ++ vchiq_mmal_port_parameter_set(dev->instance, encoder_out, ++ mmal_ctrl->mmal_id, ++ &bitrate_mode, ++ sizeof(bitrate_mode)); ++ return 0; ++} ++ ++static int ctrl_set_q_factor(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ u32 u32_value; ++ struct vchiq_mmal_port *jpeg_out; ++ ++ jpeg_out = &dev->component[MMAL_COMPONENT_IMAGE_ENCODE]->output[0]; ++ ++ u32_value = ctrl->val; ++ ++ return vchiq_mmal_port_parameter_set(dev->instance, jpeg_out, ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++} ++ ++static int bm2835_mmal_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct bm2835_mmal_dev *dev = ++ container_of(ctrl->handler, struct bm2835_mmal_dev, ++ ctrl_handler); ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl = ctrl->priv; ++ ++ if ((mmal_ctrl == NULL) || ++ (mmal_ctrl->id != ctrl->id) || ++ (mmal_ctrl->setter == NULL)) { ++ pr_warn("mmal_ctrl:%p ctrl id:%d\n", mmal_ctrl, ctrl->id); ++ return -EINVAL; ++ } ++ ++ return mmal_ctrl->setter(dev, ctrl, mmal_ctrl); ++} ++ ++static const struct v4l2_ctrl_ops bm2835_mmal_ctrl_ops = { ++ .s_ctrl = bm2835_mmal_s_ctrl, ++}; ++ ++ ++ ++static const struct bm2835_mmal_v4l2_ctrl v4l2_ctrls[V4L2_CTRL_COUNT] = { ++ { ++ V4L2_CID_SATURATION, MMAL_CONTROL_TYPE_STD, ++ -100, 100, 0, 1, NULL, ++ MMAL_PARAMETER_SATURATION, &ctrl_set_rational ++ }, ++ { ++ V4L2_CID_SHARPNESS, MMAL_CONTROL_TYPE_STD, ++ -100, 100, 0, 1, NULL, ++ MMAL_PARAMETER_SHARPNESS, &ctrl_set_rational ++ }, ++ { ++ V4L2_CID_CONTRAST, MMAL_CONTROL_TYPE_STD, ++ -100, 100, 0, 1, NULL, ++ MMAL_PARAMETER_CONTRAST, &ctrl_set_rational ++ }, ++ { ++ V4L2_CID_BRIGHTNESS, MMAL_CONTROL_TYPE_STD, ++ 0, 100, 50, 1, NULL, ++ MMAL_PARAMETER_BRIGHTNESS, &ctrl_set_rational ++ }, ++ { ++ V4L2_CID_ISO_SENSITIVITY, MMAL_CONTROL_TYPE_INT_MENU, ++ 0, ARRAY_SIZE(iso_qmenu) - 1, 0, 1, iso_qmenu, ++ MMAL_PARAMETER_ISO, &ctrl_set_value ++ }, ++ { ++ V4L2_CID_IMAGE_STABILIZATION, MMAL_CONTROL_TYPE_STD, ++ 0, 1, 0, 1, NULL, ++ MMAL_PARAMETER_VIDEO_STABILISATION, &ctrl_set_value ++ }, ++/* { ++ 0, MMAL_CONTROL_TYPE_CLUSTER, 3, 1, 0, NULL, 0, NULL ++ }, ++*/ { ++ V4L2_CID_EXPOSURE_AUTO, MMAL_CONTROL_TYPE_STD_MENU, ++ ~0x03, 3, V4L2_EXPOSURE_AUTO, 0, NULL, ++ MMAL_PARAMETER_EXPOSURE_MODE, &ctrl_set_exposure ++ }, ++/* todo this needs mixing in with set exposure ++ { ++ V4L2_CID_SCENE_MODE, MMAL_CONTROL_TYPE_STD_MENU, ++ }, ++ */ ++ { ++ V4L2_CID_AUTO_EXPOSURE_BIAS, MMAL_CONTROL_TYPE_INT_MENU, ++ 0, ARRAY_SIZE(ev_bias_qmenu) - 1, ++ (ARRAY_SIZE(ev_bias_qmenu)+1)/2 - 1, 0, ev_bias_qmenu, ++ MMAL_PARAMETER_EXPOSURE_COMP, &ctrl_set_value ++ }, ++ { ++ V4L2_CID_EXPOSURE_METERING, ++ MMAL_CONTROL_TYPE_STD_MENU, ++ ~0x7, 2, V4L2_EXPOSURE_METERING_AVERAGE, 0, NULL, ++ MMAL_PARAMETER_EXP_METERING_MODE, &ctrl_set_metering_mode ++ }, ++ { ++ V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE, ++ MMAL_CONTROL_TYPE_STD_MENU, ++ ~0x3fe, 9, V4L2_WHITE_BALANCE_AUTO, 0, NULL, ++ MMAL_PARAMETER_AWB_MODE, &ctrl_set_awb_mode ++ }, ++ { ++ V4L2_CID_COLORFX, MMAL_CONTROL_TYPE_STD_MENU, ++ 0, 15, V4L2_COLORFX_NONE, 0, NULL, ++ MMAL_PARAMETER_IMAGE_EFFECT, &ctrl_set_image_effect ++ }, ++ { ++ V4L2_CID_COLORFX_CBCR, MMAL_CONTROL_TYPE_STD, ++ 0, 0xffff, 0x8080, 1, NULL, ++ MMAL_PARAMETER_COLOUR_EFFECT, &ctrl_set_colfx ++ }, ++ { ++ V4L2_CID_ROTATE, MMAL_CONTROL_TYPE_STD, ++ 0, 360, 0, 90, NULL, ++ MMAL_PARAMETER_ROTATION, &ctrl_set_rotate ++ }, ++ { ++ V4L2_CID_HFLIP, MMAL_CONTROL_TYPE_STD, ++ 0, 1, 0, 1, NULL, ++ MMAL_PARAMETER_MIRROR, &ctrl_set_flip ++ }, ++ { ++ V4L2_CID_VFLIP, MMAL_CONTROL_TYPE_STD, ++ 0, 1, 0, 1, NULL, ++ MMAL_PARAMETER_MIRROR, &ctrl_set_flip ++ }, ++ { ++ V4L2_CID_MPEG_VIDEO_BITRATE_MODE, MMAL_CONTROL_TYPE_STD_MENU, ++ 0, ARRAY_SIZE(bitrate_mode_qmenu) - 1, ++ 0, 0, bitrate_mode_qmenu, ++ MMAL_PARAMETER_RATECONTROL, &ctrl_set_bitrate_mode ++ }, ++ { ++ V4L2_CID_MPEG_VIDEO_BITRATE, MMAL_CONTROL_TYPE_STD, ++ 25*1000, 25*1000*1000, 10*1000*1000, 25*1000, NULL, ++ MMAL_PARAMETER_VIDEO_BIT_RATE, &ctrl_set_bitrate ++ }, ++ { ++ V4L2_CID_JPEG_COMPRESSION_QUALITY, MMAL_CONTROL_TYPE_STD, ++ 0, 100, ++ 30, 1, NULL, ++ MMAL_PARAMETER_JPEG_Q_FACTOR, &ctrl_set_q_factor ++ }, ++}; ++ ++int bm2835_mmal_set_all_camera_controls(struct bm2835_mmal_dev *dev) ++{ ++ int c; ++ int ret; ++ ++ for (c = 0; c < V4L2_CTRL_COUNT; c++) { ++ if ((dev->ctrls[c]) && (v4l2_ctrls[c].setter)) { ++ ret = v4l2_ctrls[c].setter(dev, dev->ctrls[c], ++ &v4l2_ctrls[c]); ++ if (ret) ++ break; ++ } ++ } ++ return ret; ++} ++ ++int bm2835_mmal_init_controls(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl_handler *hdl) ++{ ++ int c; ++ const struct bm2835_mmal_v4l2_ctrl *ctrl; ++ ++ v4l2_ctrl_handler_init(hdl, V4L2_CTRL_COUNT); ++ ++ for (c = 0; c < V4L2_CTRL_COUNT; c++) { ++ ctrl = &v4l2_ctrls[c]; ++ ++ switch (ctrl->type) { ++ case MMAL_CONTROL_TYPE_STD: ++ dev->ctrls[c] = v4l2_ctrl_new_std(hdl, ++ &bm2835_mmal_ctrl_ops, ctrl->id, ++ ctrl->min, ctrl->max, ctrl->step, ctrl->def); ++ break; ++ ++ case MMAL_CONTROL_TYPE_STD_MENU: ++ dev->ctrls[c] = v4l2_ctrl_new_std_menu(hdl, ++ &bm2835_mmal_ctrl_ops, ctrl->id, ++ ctrl->max, ctrl->min, ctrl->def); ++ break; ++ ++ case MMAL_CONTROL_TYPE_INT_MENU: ++ dev->ctrls[c] = v4l2_ctrl_new_int_menu(hdl, ++ &bm2835_mmal_ctrl_ops, ctrl->id, ++ ctrl->max, ctrl->def, ctrl->imenu); ++ break; ++ ++ case MMAL_CONTROL_TYPE_CLUSTER: ++ /* skip this entry when constructing controls */ ++ continue; ++ } ++ ++ if (hdl->error) ++ break; ++ ++ dev->ctrls[c]->priv = (void *)ctrl; ++ } ++ ++ if (hdl->error) { ++ pr_err("error adding control %d/%d id 0x%x\n", c, ++ V4L2_CTRL_COUNT, ctrl->id); ++ return hdl->error; ++ } ++ ++ for (c = 0; c < V4L2_CTRL_COUNT; c++) { ++ ctrl = &v4l2_ctrls[c]; ++ ++ switch (ctrl->type) { ++ case MMAL_CONTROL_TYPE_CLUSTER: ++ v4l2_ctrl_auto_cluster(ctrl->min, ++ &dev->ctrls[c+1], ++ ctrl->max, ++ ctrl->def); ++ break; ++ ++ case MMAL_CONTROL_TYPE_STD: ++ case MMAL_CONTROL_TYPE_STD_MENU: ++ case MMAL_CONTROL_TYPE_INT_MENU: ++ break; ++ } ++ ++ } ++ ++ return 0; ++} +diff --git a/drivers/media/platform/bcm2835/mmal-common.h b/drivers/media/platform/bcm2835/mmal-common.h +new file mode 100644 +index 0000000..602b4a7 +--- /dev/null ++++ b/drivers/media/platform/bcm2835/mmal-common.h +@@ -0,0 +1,52 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders <vincent.sanders@collabora.co.uk> ++ * Dave Stevenson <dsteve@broadcom.com> ++ * Simon Mellor <simellor@broadcom.com> ++ * Luke Diamand <luked@broadcom.com> ++ * ++ * MMAL structures ++ * ++ */ ++ ++#define MMAL_FOURCC(a, b, c, d) ((a) | (b << 8) | (c << 16) | (d << 24)) ++#define MMAL_MAGIC MMAL_FOURCC('m', 'm', 'a', 'l') ++ ++/** Special value signalling that time is not known */ ++#define MMAL_TIME_UNKNOWN (1LL<<63) ++ ++/* mapping between v4l and mmal video modes */ ++struct mmal_fmt { ++ char *name; ++ u32 fourcc; /* v4l2 format id */ ++ u32 mmal; ++ int depth; ++ u32 mmal_component; /* MMAL component index to be used to encode */ ++}; ++ ++/* buffer for one video frame */ ++struct mmal_buffer { ++ /* v4l buffer data -- must be first */ ++ struct vb2_buffer vb; ++ ++ /* list of buffers available */ ++ struct list_head list; ++ ++ void *buffer; /* buffer pointer */ ++ unsigned long buffer_size; /* size of allocated buffer */ ++}; ++ ++/* */ ++struct mmal_colourfx { ++ s32 enable; ++ u32 u; ++ u32 v; ++}; ++ +diff --git a/drivers/media/platform/bcm2835/mmal-encodings.h b/drivers/media/platform/bcm2835/mmal-encodings.h +new file mode 100644 +index 0000000..856e80e +--- /dev/null ++++ b/drivers/media/platform/bcm2835/mmal-encodings.h +@@ -0,0 +1,93 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders <vincent.sanders@collabora.co.uk> ++ * Dave Stevenson <dsteve@broadcom.com> ++ * Simon Mellor <simellor@broadcom.com> ++ * Luke Diamand <luked@broadcom.com> ++ */ ++ ++#define MMAL_ENCODING_H264 MMAL_FOURCC('H', '2', '6', '4') ++#define MMAL_ENCODING_H263 MMAL_FOURCC('H', '2', '6', '3') ++#define MMAL_ENCODING_MP4V MMAL_FOURCC('M', 'P', '4', 'V') ++#define MMAL_ENCODING_MP2V MMAL_FOURCC('M', 'P', '2', 'V') ++#define MMAL_ENCODING_MP1V MMAL_FOURCC('M', 'P', '1', 'V') ++#define MMAL_ENCODING_WMV3 MMAL_FOURCC('W', 'M', 'V', '3') ++#define MMAL_ENCODING_WMV2 MMAL_FOURCC('W', 'M', 'V', '2') ++#define MMAL_ENCODING_WMV1 MMAL_FOURCC('W', 'M', 'V', '1') ++#define MMAL_ENCODING_WVC1 MMAL_FOURCC('W', 'V', 'C', '1') ++#define MMAL_ENCODING_VP8 MMAL_FOURCC('V', 'P', '8', ' ') ++#define MMAL_ENCODING_VP7 MMAL_FOURCC('V', 'P', '7', ' ') ++#define MMAL_ENCODING_VP6 MMAL_FOURCC('V', 'P', '6', ' ') ++#define MMAL_ENCODING_THEORA MMAL_FOURCC('T', 'H', 'E', 'O') ++#define MMAL_ENCODING_SPARK MMAL_FOURCC('S', 'P', 'R', 'K') ++ ++#define MMAL_ENCODING_JPEG MMAL_FOURCC('J', 'P', 'E', 'G') ++#define MMAL_ENCODING_GIF MMAL_FOURCC('G', 'I', 'F', ' ') ++#define MMAL_ENCODING_PNG MMAL_FOURCC('P', 'N', 'G', ' ') ++#define MMAL_ENCODING_PPM MMAL_FOURCC('P', 'P', 'M', ' ') ++#define MMAL_ENCODING_TGA MMAL_FOURCC('T', 'G', 'A', ' ') ++#define MMAL_ENCODING_BMP MMAL_FOURCC('B', 'M', 'P', ' ') ++ ++#define MMAL_ENCODING_I420 MMAL_FOURCC('I', '4', '2', '0') ++#define MMAL_ENCODING_I420_SLICE MMAL_FOURCC('S', '4', '2', '0') ++#define MMAL_ENCODING_YV12 MMAL_FOURCC('Y', 'V', '1', '2') ++#define MMAL_ENCODING_I422 MMAL_FOURCC('I', '4', '2', '2') ++#define MMAL_ENCODING_I422_SLICE MMAL_FOURCC('S', '4', '2', '2') ++#define MMAL_ENCODING_YUYV MMAL_FOURCC('Y', 'U', 'Y', 'V') ++#define MMAL_ENCODING_YVYU MMAL_FOURCC('Y', 'V', 'Y', 'U') ++#define MMAL_ENCODING_UYVY MMAL_FOURCC('U', 'Y', 'V', 'Y') ++#define MMAL_ENCODING_VYUY MMAL_FOURCC('V', 'Y', 'U', 'Y') ++#define MMAL_ENCODING_NV12 MMAL_FOURCC('N', 'V', '1', '2') ++#define MMAL_ENCODING_NV21 MMAL_FOURCC('N', 'V', '2', '1') ++#define MMAL_ENCODING_ARGB MMAL_FOURCC('A', 'R', 'G', 'B') ++#define MMAL_ENCODING_RGBA MMAL_FOURCC('R', 'G', 'B', 'A') ++#define MMAL_ENCODING_ABGR MMAL_FOURCC('A', 'B', 'G', 'R') ++#define MMAL_ENCODING_BGRA MMAL_FOURCC('B', 'G', 'R', 'A') ++#define MMAL_ENCODING_RGB16 MMAL_FOURCC('R', 'G', 'B', '2') ++#define MMAL_ENCODING_RGB24 MMAL_FOURCC('R', 'G', 'B', '3') ++#define MMAL_ENCODING_RGB32 MMAL_FOURCC('R', 'G', 'B', '4') ++#define MMAL_ENCODING_BGR16 MMAL_FOURCC('B', 'G', 'R', '2') ++#define MMAL_ENCODING_BGR24 MMAL_FOURCC('B', 'G', 'R', '3') ++#define MMAL_ENCODING_BGR32 MMAL_FOURCC('B', 'G', 'R', '4') ++ ++/** SAND Video (YUVUV128) format, native format understood by VideoCore. ++ * This format is *not* opaque - if requested you will receive full frames ++ * of YUV_UV video. ++ */ ++#define MMAL_ENCODING_YUVUV128 MMAL_FOURCC('S', 'A', 'N', 'D') ++ ++/** VideoCore opaque image format, image handles are returned to ++ * the host but not the actual image data. ++ */ ++#define MMAL_ENCODING_OPAQUE MMAL_FOURCC('O', 'P', 'Q', 'V') ++ ++/** An EGL image handle ++ */ ++#define MMAL_ENCODING_EGL_IMAGE MMAL_FOURCC('E', 'G', 'L', 'I') ++ ++/* }@ */ ++ ++/** \name Pre-defined audio encodings */ ++/* @{ */ ++#define MMAL_ENCODING_PCM_UNSIGNED_BE MMAL_FOURCC('P', 'C', 'M', 'U') ++#define MMAL_ENCODING_PCM_UNSIGNED_LE MMAL_FOURCC('p', 'c', 'm', 'u') ++#define MMAL_ENCODING_PCM_SIGNED_BE MMAL_FOURCC('P', 'C', 'M', 'S') ++#define MMAL_ENCODING_PCM_SIGNED_LE MMAL_FOURCC('p', 'c', 'm', 's') ++#define MMAL_ENCODING_PCM_FLOAT_BE MMAL_FOURCC('P', 'C', 'M', 'F') ++#define MMAL_ENCODING_PCM_FLOAT_LE MMAL_FOURCC('p', 'c', 'm', 'f') ++ ++/* Pre-defined H264 encoding variants */ ++ ++/** ISO 14496-10 Annex B byte stream format */ ++#define MMAL_ENCODING_VARIANT_H264_DEFAULT 0 ++/** ISO 14496-15 AVC stream format */ ++#define MMAL_ENCODING_VARIANT_H264_AVC1 MMAL_FOURCC('A', 'V', 'C', '1') ++/** Implicitly delineated NAL units without emulation prevention */ ++#define MMAL_ENCODING_VARIANT_H264_RAW MMAL_FOURCC('R', 'A', 'W', ' ') +diff --git a/drivers/media/platform/bcm2835/mmal-msg-common.h b/drivers/media/platform/bcm2835/mmal-msg-common.h +new file mode 100644 +index 0000000..66e8a6e +--- /dev/null ++++ b/drivers/media/platform/bcm2835/mmal-msg-common.h +@@ -0,0 +1,50 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders <vincent.sanders@collabora.co.uk> ++ * Dave Stevenson <dsteve@broadcom.com> ++ * Simon Mellor <simellor@broadcom.com> ++ * Luke Diamand <luked@broadcom.com> ++ */ ++ ++#ifndef MMAL_MSG_COMMON_H ++#define MMAL_MSG_COMMON_H ++ ++enum mmal_msg_status { ++ MMAL_MSG_STATUS_SUCCESS = 0, /**< Success */ ++ MMAL_MSG_STATUS_ENOMEM, /**< Out of memory */ ++ MMAL_MSG_STATUS_ENOSPC, /**< Out of resources other than memory */ ++ MMAL_MSG_STATUS_EINVAL, /**< Argument is invalid */ ++ MMAL_MSG_STATUS_ENOSYS, /**< Function not implemented */ ++ MMAL_MSG_STATUS_ENOENT, /**< No such file or directory */ ++ MMAL_MSG_STATUS_ENXIO, /**< No such device or address */ ++ MMAL_MSG_STATUS_EIO, /**< I/O error */ ++ MMAL_MSG_STATUS_ESPIPE, /**< Illegal seek */ ++ MMAL_MSG_STATUS_ECORRUPT, /**< Data is corrupt \attention */ ++ MMAL_MSG_STATUS_ENOTREADY, /**< Component is not ready */ ++ MMAL_MSG_STATUS_ECONFIG, /**< Component is not configured */ ++ MMAL_MSG_STATUS_EISCONN, /**< Port is already connected */ ++ MMAL_MSG_STATUS_ENOTCONN, /**< Port is disconnected */ ++ MMAL_MSG_STATUS_EAGAIN, /**< Resource temporarily unavailable. */ ++ MMAL_MSG_STATUS_EFAULT, /**< Bad address */ ++}; ++ ++struct mmal_rect { ++ s32 x; /**< x coordinate (from left) */ ++ s32 y; /**< y coordinate (from top) */ ++ s32 width; /**< width */ ++ s32 height; /**< height */ ++}; ++ ++struct mmal_rational { ++ s32 num; /**< Numerator */ ++ s32 den; /**< Denominator */ ++}; ++ ++#endif /* MMAL_MSG_COMMON_H */ +diff --git a/drivers/media/platform/bcm2835/mmal-msg-format.h b/drivers/media/platform/bcm2835/mmal-msg-format.h +new file mode 100644 +index 0000000..123d86e +--- /dev/null ++++ b/drivers/media/platform/bcm2835/mmal-msg-format.h +@@ -0,0 +1,81 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders <vincent.sanders@collabora.co.uk> ++ * Dave Stevenson <dsteve@broadcom.com> ++ * Simon Mellor <simellor@broadcom.com> ++ * Luke Diamand <luked@broadcom.com> ++ */ ++ ++#ifndef MMAL_MSG_FORMAT_H ++#define MMAL_MSG_FORMAT_H ++ ++#include "mmal-msg-common.h" ++ ++/* MMAL_ES_FORMAT_T */ ++ ++ ++struct mmal_audio_format { ++ u32 channels; /**< Number of audio channels */ ++ u32 sample_rate; /**< Sample rate */ ++ ++ u32 bits_per_sample; /**< Bits per sample */ ++ u32 block_align; /**< Size of a block of data */ ++}; ++ ++struct mmal_video_format { ++ u32 width; /**< Width of frame in pixels */ ++ u32 height; /**< Height of frame in rows of pixels */ ++ struct mmal_rect crop; /**< Visible region of the frame */ ++ struct mmal_rational frame_rate; /**< Frame rate */ ++ struct mmal_rational par; /**< Pixel aspect ratio */ ++ ++ /* FourCC specifying the color space of the video stream. See the ++ * \ref MmalColorSpace "pre-defined color spaces" for some examples. ++ */ ++ u32 color_space; ++}; ++ ++struct mmal_subpicture_format { ++ u32 x_offset; ++ u32 y_offset; ++}; ++ ++union mmal_es_specific_format { ++ struct mmal_audio_format audio; ++ struct mmal_video_format video; ++ struct mmal_subpicture_format subpicture; ++}; ++ ++/** Definition of an elementary stream format (MMAL_ES_FORMAT_T) */ ++struct mmal_es_format { ++ u32 type; /* enum mmal_es_type */ ++ ++ u32 encoding; /* FourCC specifying encoding of the elementary stream.*/ ++ u32 encoding_variant; /* FourCC specifying the specific ++ * encoding variant of the elementary ++ * stream. ++ */ ++ ++ union mmal_es_specific_format *es; /* TODO: pointers in ++ * message serialisation?!? ++ */ ++ /* Type specific ++ * information for the ++ * elementary stream ++ */ ++ ++ u32 bitrate; /**< Bitrate in bits per second */ ++ u32 flags; /**< Flags describing properties of the elementary stream. */ ++ ++ u32 extradata_size; /**< Size of the codec specific data */ ++ u8 *extradata; /**< Codec specific data */ ++}; ++ ++#endif /* MMAL_MSG_FORMAT_H */ +diff --git a/drivers/media/platform/bcm2835/mmal-msg-port.h b/drivers/media/platform/bcm2835/mmal-msg-port.h +new file mode 100644 +index 0000000..a55c1ea +--- /dev/null ++++ b/drivers/media/platform/bcm2835/mmal-msg-port.h +@@ -0,0 +1,107 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders <vincent.sanders@collabora.co.uk> ++ * Dave Stevenson <dsteve@broadcom.com> ++ * Simon Mellor <simellor@broadcom.com> ++ * Luke Diamand <luked@broadcom.com> ++ */ ++ ++/* MMAL_PORT_TYPE_T */ ++enum mmal_port_type { ++ MMAL_PORT_TYPE_UNKNOWN = 0, /**< Unknown port type */ ++ MMAL_PORT_TYPE_CONTROL, /**< Control port */ ++ MMAL_PORT_TYPE_INPUT, /**< Input port */ ++ MMAL_PORT_TYPE_OUTPUT, /**< Output port */ ++ MMAL_PORT_TYPE_CLOCK, /**< Clock port */ ++}; ++ ++/** The port is pass-through and doesn't need buffer headers allocated */ ++#define MMAL_PORT_CAPABILITY_PASSTHROUGH 0x01 ++/** The port wants to allocate the buffer payloads. ++ * This signals a preference that payload allocation should be done ++ * on this port for efficiency reasons. */ ++#define MMAL_PORT_CAPABILITY_ALLOCATION 0x02 ++/** The port supports format change events. ++ * This applies to input ports and is used to let the client know ++ * whether the port supports being reconfigured via a format ++ * change event (i.e. without having to disable the port). */ ++#define MMAL_PORT_CAPABILITY_SUPPORTS_EVENT_FORMAT_CHANGE 0x04 ++ ++/* mmal port structure (MMAL_PORT_T) ++ * ++ * most elements are informational only, the pointer values for ++ * interogation messages are generally provided as additional ++ * strucures within the message. When used to set values only teh ++ * buffer_num, buffer_size and userdata parameters are writable. ++ */ ++struct mmal_port { ++ void *priv; /* Private member used by the framework */ ++ const char *name; /* Port name. Used for debugging purposes (RO) */ ++ ++ u32 type; /* Type of the port (RO) enum mmal_port_type */ ++ u16 index; /* Index of the port in its type list (RO) */ ++ u16 index_all; /* Index of the port in the list of all ports (RO) */ ++ ++ u32 is_enabled; /* Indicates whether the port is enabled or not (RO) */ ++ struct mmal_es_format *format; /* Format of the elementary stream */ ++ ++ u32 buffer_num_min; /* Minimum number of buffers the port ++ * requires (RO). This is set by the ++ * component. ++ */ ++ ++ u32 buffer_size_min; /* Minimum size of buffers the port ++ * requires (RO). This is set by the ++ * component. ++ */ ++ ++ u32 buffer_alignment_min; /* Minimum alignment requirement for ++ * the buffers (RO). A value of ++ * zero means no special alignment ++ * requirements. This is set by the ++ * component. ++ */ ++ ++ u32 buffer_num_recommended; /* Number of buffers the port ++ * recommends for optimal ++ * performance (RO). A value of ++ * zero means no special ++ * recommendation. This is set ++ * by the component. ++ */ ++ ++ u32 buffer_size_recommended; /* Size of buffers the port ++ * recommends for optimal ++ * performance (RO). A value of ++ * zero means no special ++ * recommendation. This is set ++ * by the component. ++ */ ++ ++ u32 buffer_num; /* Actual number of buffers the port will use. ++ * This is set by the client. ++ */ ++ ++ u32 buffer_size; /* Actual maximum size of the buffers that ++ * will be sent to the port. This is set by ++ * the client. ++ */ ++ ++ void *component; /* Component this port belongs to (Read Only) */ ++ ++ void *userdata; /* Field reserved for use by the client */ ++ ++ u32 capabilities; /* Flags describing the capabilities of a ++ * port (RO). Bitwise combination of \ref ++ * portcapabilities "Port capabilities" ++ * values. ++ */ ++ ++}; +diff --git a/drivers/media/platform/bcm2835/mmal-msg.h b/drivers/media/platform/bcm2835/mmal-msg.h +new file mode 100644 +index 0000000..67b1076 +--- /dev/null ++++ b/drivers/media/platform/bcm2835/mmal-msg.h +@@ -0,0 +1,404 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders <vincent.sanders@collabora.co.uk> ++ * Dave Stevenson <dsteve@broadcom.com> ++ * Simon Mellor <simellor@broadcom.com> ++ * Luke Diamand <luked@broadcom.com> ++ */ ++ ++/* all the data structures which serialise the MMAL protocol. note ++ * these are directly mapped onto the recived message data. ++ * ++ * BEWARE: They seem to *assume* pointers are u32 and that there is no ++ * structure padding! ++ * ++ * NOTE: this implementation uses kernel types to ensure sizes. Rather ++ * than assigning values to enums to force their size the ++ * implementation uses fixed size types and not the enums (though the ++ * comments have the actual enum type ++ */ ++ ++#define VC_MMAL_VER 15 ++#define VC_MMAL_MIN_VER 10 ++#define VC_MMAL_SERVER_NAME MAKE_FOURCC("mmal") ++ ++/* max total message size is 512 bytes */ ++#define MMAL_MSG_MAX_SIZE 512 ++/* with six 32bit header elements max payload is therefore 488 bytes */ ++#define MMAL_MSG_MAX_PAYLOAD 488 ++ ++#include "mmal-msg-common.h" ++#include "mmal-msg-format.h" ++#include "mmal-msg-port.h" ++ ++enum mmal_msg_type { ++ MMAL_MSG_TYPE_QUIT = 1, ++ MMAL_MSG_TYPE_SERVICE_CLOSED, ++ MMAL_MSG_TYPE_GET_VERSION, ++ MMAL_MSG_TYPE_COMPONENT_CREATE, ++ MMAL_MSG_TYPE_COMPONENT_DESTROY, /* 5 */ ++ MMAL_MSG_TYPE_COMPONENT_ENABLE, ++ MMAL_MSG_TYPE_COMPONENT_DISABLE, ++ MMAL_MSG_TYPE_PORT_INFO_GET, ++ MMAL_MSG_TYPE_PORT_INFO_SET, ++ MMAL_MSG_TYPE_PORT_ACTION, /* 10 */ ++ MMAL_MSG_TYPE_BUFFER_FROM_HOST, ++ MMAL_MSG_TYPE_BUFFER_TO_HOST, ++ MMAL_MSG_TYPE_GET_STATS, ++ MMAL_MSG_TYPE_PORT_PARAMETER_SET, ++ MMAL_MSG_TYPE_PORT_PARAMETER_GET, /* 15 */ ++ MMAL_MSG_TYPE_EVENT_TO_HOST, ++ MMAL_MSG_TYPE_GET_CORE_STATS_FOR_PORT, ++ MMAL_MSG_TYPE_OPAQUE_ALLOCATOR, ++ MMAL_MSG_TYPE_CONSUME_MEM, ++ MMAL_MSG_TYPE_LMK, /* 20 */ ++ MMAL_MSG_TYPE_OPAQUE_ALLOCATOR_DESC, ++ MMAL_MSG_TYPE_DRM_GET_LHS32, ++ MMAL_MSG_TYPE_DRM_GET_TIME, ++ MMAL_MSG_TYPE_BUFFER_FROM_HOST_ZEROLEN, ++ MMAL_MSG_TYPE_PORT_FLUSH, /* 25 */ ++ MMAL_MSG_TYPE_HOST_LOG, ++ MMAL_MSG_TYPE_MSG_LAST ++}; ++ ++/* port action request messages differ depending on the action type */ ++enum mmal_msg_port_action_type { ++ MMAL_MSG_PORT_ACTION_TYPE_UNKNOWN = 0, /* Unkown action */ ++ MMAL_MSG_PORT_ACTION_TYPE_ENABLE, /* Enable a port */ ++ MMAL_MSG_PORT_ACTION_TYPE_DISABLE, /* Disable a port */ ++ MMAL_MSG_PORT_ACTION_TYPE_FLUSH, /* Flush a port */ ++ MMAL_MSG_PORT_ACTION_TYPE_CONNECT, /* Connect ports */ ++ MMAL_MSG_PORT_ACTION_TYPE_DISCONNECT, /* Disconnect ports */ ++ MMAL_MSG_PORT_ACTION_TYPE_SET_REQUIREMENTS, /* Set buffer requirements*/ ++}; ++ ++struct mmal_msg_header { ++ u32 magic; ++ u32 type; /** enum mmal_msg_type */ ++ ++ /* Opaque handle to the control service */ ++ struct mmal_control_service *control_service; ++ ++ struct mmal_msg_context *context; /** a u32 per message context */ ++ u32 status; /** The status of the vchiq operation */ ++ u32 padding; ++}; ++ ++/* Send from VC to host to report version */ ++struct mmal_msg_version { ++ u32 flags; ++ u32 major; ++ u32 minor; ++ u32 minimum; ++}; ++ ++/* request to VC to create component */ ++struct mmal_msg_component_create { ++ void *client_component; /* component context */ ++ char name[128]; ++ u32 pid; /* For debug */ ++}; ++ ++/* reply from VC to component creation request */ ++struct mmal_msg_component_create_reply { ++ u32 status; /** enum mmal_msg_status - how does this differ to ++ * the one in the header? ++ */ ++ u32 component_handle; /* VideoCore handle for component */ ++ u32 input_num; /* Number of input ports */ ++ u32 output_num; /* Number of output ports */ ++ u32 clock_num; /* Number of clock ports */ ++}; ++ ++/* request to VC to destroy a component */ ++struct mmal_msg_component_destroy { ++ u32 component_handle; ++}; ++ ++struct mmal_msg_component_destroy_reply { ++ u32 status; /** The component destruction status */ ++}; ++ ++ ++/* request and reply to VC to enable a component */ ++struct mmal_msg_component_enable { ++ u32 component_handle; ++}; ++ ++struct mmal_msg_component_enable_reply { ++ u32 status; /** The component enable status */ ++}; ++ ++ ++/* request and reply to VC to disable a component */ ++struct mmal_msg_component_disable { ++ u32 component_handle; ++}; ++ ++struct mmal_msg_component_disable_reply { ++ u32 status; /** The component disable status */ ++}; ++ ++/* request to VC to get port information */ ++struct mmal_msg_port_info_get { ++ u32 component_handle; /* component handle port is associated with */ ++ u32 port_type; /* enum mmal_msg_port_type */ ++ u32 index; /* port index to query */ ++}; ++ ++/* reply from VC to get port info request */ ++struct mmal_msg_port_info_get_reply { ++ u32 status; /** enum mmal_msg_status */ ++ u32 component_handle; /* component handle port is associated with */ ++ u32 port_type; /* enum mmal_msg_port_type */ ++ u32 port_index; /* port indexed in query */ ++ s32 found; /* unused */ ++ u32 port_handle; /**< Handle to use for this port */ ++ struct mmal_port port; ++ struct mmal_es_format format; /* elementry stream format */ ++ union mmal_es_specific_format es; /* es type specific data */ ++ u8 extradata[MMAL_FORMAT_EXTRADATA_MAX_SIZE]; /* es extra data */ ++}; ++ ++/* request to VC to set port information */ ++struct mmal_msg_port_info_set { ++ u32 component_handle; ++ u32 port_type; /* enum mmal_msg_port_type */ ++ u32 port_index; /* port indexed in query */ ++ struct mmal_port port; ++ struct mmal_es_format format; ++ union mmal_es_specific_format es; ++ u8 extradata[MMAL_FORMAT_EXTRADATA_MAX_SIZE]; ++}; ++ ++/* reply from VC to port info set request */ ++struct mmal_msg_port_info_set_reply { ++ u32 status; ++ u32 component_handle; /* component handle port is associated with */ ++ u32 port_type; /* enum mmal_msg_port_type */ ++ u32 index; /* port indexed in query */ ++ s32 found; /* unused */ ++ u32 port_handle; /**< Handle to use for this port */ ++ struct mmal_port port; ++ struct mmal_es_format format; ++ union mmal_es_specific_format es; ++ u8 extradata[MMAL_FORMAT_EXTRADATA_MAX_SIZE]; ++}; ++ ++ ++/* port action requests that take a mmal_port as a parameter */ ++struct mmal_msg_port_action_port { ++ u32 component_handle; ++ u32 port_handle; ++ u32 action; /* enum mmal_msg_port_action_type */ ++ struct mmal_port port; ++}; ++ ++/* port action requests that take handles as a parameter */ ++struct mmal_msg_port_action_handle { ++ u32 component_handle; ++ u32 port_handle; ++ u32 action; /* enum mmal_msg_port_action_type */ ++ u32 connect_component_handle; ++ u32 connect_port_handle; ++}; ++ ++struct mmal_msg_port_action_reply { ++ u32 status; /** The port action operation status */ ++}; ++ ++ ++ ++ ++/* MMAL buffer transfer */ ++ ++/** Size of space reserved in a buffer message for short messages. */ ++#define MMAL_VC_SHORT_DATA 128 ++ ++/** Signals that the current payload is the end of the stream of data */ ++#define MMAL_BUFFER_HEADER_FLAG_EOS (1<<0) ++/** Signals that the start of the current payload starts a frame */ ++#define MMAL_BUFFER_HEADER_FLAG_FRAME_START (1<<1) ++/** Signals that the end of the current payload ends a frame */ ++#define MMAL_BUFFER_HEADER_FLAG_FRAME_END (1<<2) ++/** Signals that the current payload contains only complete frames (>1) */ ++#define MMAL_BUFFER_HEADER_FLAG_FRAME \ ++ (MMAL_BUFFER_HEADER_FLAG_FRAME_START|MMAL_BUFFER_HEADER_FLAG_FRAME_END) ++/** Signals that the current payload is a keyframe (i.e. self decodable) */ ++#define MMAL_BUFFER_HEADER_FLAG_KEYFRAME (1<<3) ++/** Signals a discontinuity in the stream of data (e.g. after a seek). ++ * Can be used for instance by a decoder to reset its state */ ++#define MMAL_BUFFER_HEADER_FLAG_DISCONTINUITY (1<<4) ++/** Signals a buffer containing some kind of config data for the component ++ * (e.g. codec config data) */ ++#define MMAL_BUFFER_HEADER_FLAG_CONFIG (1<<5) ++/** Signals an encrypted payload */ ++#define MMAL_BUFFER_HEADER_FLAG_ENCRYPTED (1<<6) ++/** Signals a buffer containing side information */ ++#define MMAL_BUFFER_HEADER_FLAG_CODECSIDEINFO (1<<7) ++/** Signals a buffer which is the snapshot/postview image from a stills ++ * capture ++ */ ++#define MMAL_BUFFER_HEADER_FLAGS_SNAPSHOT (1<<8) ++/** Signals a buffer which contains data known to be corrupted */ ++#define MMAL_BUFFER_HEADER_FLAG_CORRUPTED (1<<9) ++/** Signals that a buffer failed to be transmitted */ ++#define MMAL_BUFFER_HEADER_FLAG_TRANSMISSION_FAILED (1<<10) ++ ++struct mmal_driver_buffer { ++ u32 magic; ++ u32 component_handle; ++ u32 port_handle; ++ void *client_context; ++}; ++ ++/* buffer header */ ++struct mmal_buffer_header { ++ struct mmal_buffer_header *next; /* next header */ ++ void *priv; /* framework private data */ ++ u32 cmd; ++ void *data; ++ u32 alloc_size; ++ u32 length; ++ u32 offset; ++ u32 flags; ++ s64 pts; ++ s64 dts; ++ void *type; ++ void *user_data; ++}; ++ ++struct mmal_buffer_header_type_specific { ++ union { ++ struct { ++ u32 planes; ++ u32 offset[4]; ++ u32 pitch[4]; ++ u32 flags; ++ } video; ++ } u; ++}; ++ ++struct mmal_msg_buffer_from_host { ++ /* The front 32 bytes of the buffer header are copied ++ * back to us in the reply to allow for context. This ++ * area is used to store two mmal_driver_buffer structures to ++ * allow for multiple concurrent service users. ++ */ ++ /* control data */ ++ struct mmal_driver_buffer drvbuf; ++ ++ /* referenced control data for passthrough buffer management */ ++ struct mmal_driver_buffer drvbuf_ref; ++ struct mmal_buffer_header buffer_header; /* buffer header itself */ ++ struct mmal_buffer_header_type_specific buffer_header_type_specific; ++ s32 is_zero_copy; ++ s32 has_reference; ++ ++ /** allows short data to be xfered in control message */ ++ u32 payload_in_message; ++ u8 short_data[MMAL_VC_SHORT_DATA]; ++}; ++ ++ ++/* port parameter setting */ ++ ++#define MMAL_WORKER_PORT_PARAMETER_SPACE 96 ++ ++struct mmal_msg_port_parameter_set { ++ u32 component_handle; /* component */ ++ u32 port_handle; /* port */ ++ u32 id; /* Parameter ID */ ++ u32 size; /* Parameter size */ ++ uint32_t value[MMAL_WORKER_PORT_PARAMETER_SPACE]; ++}; ++ ++struct mmal_msg_port_parameter_set_reply { ++ u32 status; /** enum mmal_msg_status todo: how does this ++ * differ to the one in the header? ++ */ ++}; ++ ++/* port parameter getting */ ++ ++struct mmal_msg_port_parameter_get { ++ u32 component_handle; /* component */ ++ u32 port_handle; /* port */ ++ u32 id; /* Parameter ID */ ++ u32 size; /* Parameter size */ ++}; ++ ++struct mmal_msg_port_parameter_get_reply { ++ u32 status; /* Status of mmal_port_parameter_get call */ ++ u32 id; /* Parameter ID */ ++ u32 size; /* Parameter size */ ++ uint32_t value[MMAL_WORKER_PORT_PARAMETER_SPACE]; ++}; ++ ++/* event messages */ ++#define MMAL_WORKER_EVENT_SPACE 256 ++ ++struct mmal_msg_event_to_host { ++ void *client_component; /* component context */ ++ ++ u32 port_type; ++ u32 port_num; ++ ++ u32 cmd; ++ u32 length; ++ u8 data[MMAL_WORKER_EVENT_SPACE]; ++ struct mmal_buffer_header *delayed_buffer; ++}; ++ ++/* all mmal messages are serialised through this structure */ ++struct mmal_msg { ++ /* header */ ++ struct mmal_msg_header h; ++ /* payload */ ++ union { ++ struct mmal_msg_version version; ++ ++ struct mmal_msg_component_create component_create; ++ struct mmal_msg_component_create_reply component_create_reply; ++ ++ struct mmal_msg_component_destroy component_destroy; ++ struct mmal_msg_component_destroy_reply component_destroy_reply; ++ ++ struct mmal_msg_component_enable component_enable; ++ struct mmal_msg_component_enable_reply component_enable_reply; ++ ++ struct mmal_msg_component_disable component_disable; ++ struct mmal_msg_component_disable_reply component_disable_reply; ++ ++ struct mmal_msg_port_info_get port_info_get; ++ struct mmal_msg_port_info_get_reply port_info_get_reply; ++ ++ struct mmal_msg_port_info_set port_info_set; ++ struct mmal_msg_port_info_set_reply port_info_set_reply; ++ ++ struct mmal_msg_port_action_port port_action_port; ++ struct mmal_msg_port_action_handle port_action_handle; ++ struct mmal_msg_port_action_reply port_action_reply; ++ ++ struct mmal_msg_buffer_from_host buffer_from_host; ++ ++ struct mmal_msg_port_parameter_set port_parameter_set; ++ struct mmal_msg_port_parameter_set_reply ++ port_parameter_set_reply; ++ struct mmal_msg_port_parameter_get ++ port_parameter_get; ++ struct mmal_msg_port_parameter_get_reply ++ port_parameter_get_reply; ++ ++ struct mmal_msg_event_to_host event_to_host; ++ ++ u8 payload[MMAL_MSG_MAX_PAYLOAD]; ++ } u; ++}; +diff --git a/drivers/media/platform/bcm2835/mmal-parameters.h b/drivers/media/platform/bcm2835/mmal-parameters.h +new file mode 100644 +index 0000000..c611b58 +--- /dev/null ++++ b/drivers/media/platform/bcm2835/mmal-parameters.h +@@ -0,0 +1,539 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders <vincent.sanders@collabora.co.uk> ++ * Dave Stevenson <dsteve@broadcom.com> ++ * Simon Mellor <simellor@broadcom.com> ++ * Luke Diamand <luked@broadcom.com> ++ */ ++ ++/* common parameters */ ++ ++/** @name Parameter groups ++ * Parameters are divided into groups, and then allocated sequentially within ++ * a group using an enum. ++ * @{ ++ */ ++ ++/** Common parameter ID group, used with many types of component. */ ++#define MMAL_PARAMETER_GROUP_COMMON (0<<16) ++/** Camera-specific parameter ID group. */ ++#define MMAL_PARAMETER_GROUP_CAMERA (1<<16) ++/** Video-specific parameter ID group. */ ++#define MMAL_PARAMETER_GROUP_VIDEO (2<<16) ++/** Audio-specific parameter ID group. */ ++#define MMAL_PARAMETER_GROUP_AUDIO (3<<16) ++/** Clock-specific parameter ID group. */ ++#define MMAL_PARAMETER_GROUP_CLOCK (4<<16) ++/** Miracast-specific parameter ID group. */ ++#define MMAL_PARAMETER_GROUP_MIRACAST (5<<16) ++ ++/* Common parameters */ ++enum mmal_parameter_common_type { ++ MMAL_PARAMETER_UNUSED /**< Never a valid parameter ID */ ++ = MMAL_PARAMETER_GROUP_COMMON, ++ MMAL_PARAMETER_SUPPORTED_ENCODINGS, /**< MMAL_PARAMETER_ENCODING_T */ ++ MMAL_PARAMETER_URI, /**< MMAL_PARAMETER_URI_T */ ++ ++ /** MMAL_PARAMETER_CHANGE_EVENT_REQUEST_T */ ++ MMAL_PARAMETER_CHANGE_EVENT_REQUEST, ++ ++ /** MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_ZERO_COPY, ++ ++ /**< MMAL_PARAMETER_BUFFER_REQUIREMENTS_T */ ++ MMAL_PARAMETER_BUFFER_REQUIREMENTS, ++ ++ MMAL_PARAMETER_STATISTICS, /**< MMAL_PARAMETER_STATISTICS_T */ ++ MMAL_PARAMETER_CORE_STATISTICS, /**< MMAL_PARAMETER_CORE_STATISTICS_T */ ++ MMAL_PARAMETER_MEM_USAGE, /**< MMAL_PARAMETER_MEM_USAGE_T */ ++ MMAL_PARAMETER_BUFFER_FLAG_FILTER, /**< MMAL_PARAMETER_UINT32_T */ ++ MMAL_PARAMETER_SEEK, /**< MMAL_PARAMETER_SEEK_T */ ++ MMAL_PARAMETER_POWERMON_ENABLE, /**< MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_LOGGING, /**< MMAL_PARAMETER_LOGGING_T */ ++ MMAL_PARAMETER_SYSTEM_TIME /**< MMAL_PARAMETER_UINT64_T */ ++}; ++ ++/* camera parameters */ ++ ++enum mmal_parameter_camera_type { ++ /* 0 */ ++ /** @ref MMAL_PARAMETER_THUMBNAIL_CONFIG_T */ ++ MMAL_PARAMETER_THUMBNAIL_CONFIGURATION ++ = MMAL_PARAMETER_GROUP_CAMERA, ++ MMAL_PARAMETER_CAPTURE_QUALITY, /**< Unused? */ ++ MMAL_PARAMETER_ROTATION, /**< @ref MMAL_PARAMETER_INT32_T */ ++ MMAL_PARAMETER_EXIF_DISABLE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_EXIF, /**< @ref MMAL_PARAMETER_EXIF_T */ ++ MMAL_PARAMETER_AWB_MODE, /**< @ref MMAL_PARAM_AWBMODE_T */ ++ MMAL_PARAMETER_IMAGE_EFFECT, /**< @ref MMAL_PARAMETER_IMAGEFX_T */ ++ MMAL_PARAMETER_COLOUR_EFFECT, /**< @ref MMAL_PARAMETER_COLOURFX_T */ ++ MMAL_PARAMETER_FLICKER_AVOID, /**< @ref MMAL_PARAMETER_FLICKERAVOID_T */ ++ MMAL_PARAMETER_FLASH, /**< @ref MMAL_PARAMETER_FLASH_T */ ++ MMAL_PARAMETER_REDEYE, /**< @ref MMAL_PARAMETER_REDEYE_T */ ++ MMAL_PARAMETER_FOCUS, /**< @ref MMAL_PARAMETER_FOCUS_T */ ++ MMAL_PARAMETER_FOCAL_LENGTHS, /**< Unused? */ ++ MMAL_PARAMETER_EXPOSURE_COMP, /**< @ref MMAL_PARAMETER_INT32_T */ ++ MMAL_PARAMETER_ZOOM, /**< @ref MMAL_PARAMETER_SCALEFACTOR_T */ ++ MMAL_PARAMETER_MIRROR, /**< @ref MMAL_PARAMETER_MIRROR_T */ ++ ++ /* 0x10 */ ++ MMAL_PARAMETER_CAMERA_NUM, /**< @ref MMAL_PARAMETER_UINT32_T */ ++ MMAL_PARAMETER_CAPTURE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_EXPOSURE_MODE, /**< @ref MMAL_PARAMETER_EXPOSUREMODE_T */ ++ MMAL_PARAMETER_EXP_METERING_MODE, /**< @ref MMAL_PARAMETER_EXPOSUREMETERINGMODE_T */ ++ MMAL_PARAMETER_FOCUS_STATUS, /**< @ref MMAL_PARAMETER_FOCUS_STATUS_T */ ++ MMAL_PARAMETER_CAMERA_CONFIG, /**< @ref MMAL_PARAMETER_CAMERA_CONFIG_T */ ++ MMAL_PARAMETER_CAPTURE_STATUS, /**< @ref MMAL_PARAMETER_CAPTURE_STATUS_T */ ++ MMAL_PARAMETER_FACE_TRACK, /**< @ref MMAL_PARAMETER_FACE_TRACK_T */ ++ MMAL_PARAMETER_DRAW_BOX_FACES_AND_FOCUS, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_JPEG_Q_FACTOR, /**< @ref MMAL_PARAMETER_UINT32_T */ ++ MMAL_PARAMETER_FRAME_RATE, /**< @ref MMAL_PARAMETER_FRAME_RATE_T */ ++ MMAL_PARAMETER_USE_STC, /**< @ref MMAL_PARAMETER_CAMERA_STC_MODE_T */ ++ MMAL_PARAMETER_CAMERA_INFO, /**< @ref MMAL_PARAMETER_CAMERA_INFO_T */ ++ MMAL_PARAMETER_VIDEO_STABILISATION, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_FACE_TRACK_RESULTS, /**< @ref MMAL_PARAMETER_FACE_TRACK_RESULTS_T */ ++ MMAL_PARAMETER_ENABLE_RAW_CAPTURE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ ++ /* 0x20 */ ++ MMAL_PARAMETER_DPF_FILE, /**< @ref MMAL_PARAMETER_URI_T */ ++ MMAL_PARAMETER_ENABLE_DPF_FILE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_DPF_FAIL_IS_FATAL, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_CAPTURE_MODE, /**< @ref MMAL_PARAMETER_CAPTUREMODE_T */ ++ MMAL_PARAMETER_FOCUS_REGIONS, /**< @ref MMAL_PARAMETER_FOCUS_REGIONS_T */ ++ MMAL_PARAMETER_INPUT_CROP, /**< @ref MMAL_PARAMETER_INPUT_CROP_T */ ++ MMAL_PARAMETER_SENSOR_INFORMATION, /**< @ref MMAL_PARAMETER_SENSOR_INFORMATION_T */ ++ MMAL_PARAMETER_FLASH_SELECT, /**< @ref MMAL_PARAMETER_FLASH_SELECT_T */ ++ MMAL_PARAMETER_FIELD_OF_VIEW, /**< @ref MMAL_PARAMETER_FIELD_OF_VIEW_T */ ++ MMAL_PARAMETER_HIGH_DYNAMIC_RANGE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_DYNAMIC_RANGE_COMPRESSION, /**< @ref MMAL_PARAMETER_DRC_T */ ++ MMAL_PARAMETER_ALGORITHM_CONTROL, /**< @ref MMAL_PARAMETER_ALGORITHM_CONTROL_T */ ++ MMAL_PARAMETER_SHARPNESS, /**< @ref MMAL_PARAMETER_RATIONAL_T */ ++ MMAL_PARAMETER_CONTRAST, /**< @ref MMAL_PARAMETER_RATIONAL_T */ ++ MMAL_PARAMETER_BRIGHTNESS, /**< @ref MMAL_PARAMETER_RATIONAL_T */ ++ MMAL_PARAMETER_SATURATION, /**< @ref MMAL_PARAMETER_RATIONAL_T */ ++ ++ /* 0x30 */ ++ MMAL_PARAMETER_ISO, /**< @ref MMAL_PARAMETER_UINT32_T */ ++ MMAL_PARAMETER_ANTISHAKE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ ++ /** @ref MMAL_PARAMETER_IMAGEFX_PARAMETERS_T */ ++ MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_CAMERA_BURST_CAPTURE, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T */ ++ MMAL_PARAMETER_CAMERA_MIN_ISO, ++ ++ /** @ref MMAL_PARAMETER_CAMERA_USE_CASE_T */ ++ MMAL_PARAMETER_CAMERA_USE_CASE, ++ ++ /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_CAPTURE_STATS_PASS, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T */ ++ MMAL_PARAMETER_CAMERA_CUSTOM_SENSOR_CONFIG, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_ENABLE_REGISTER_FILE, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_REGISTER_FAIL_IS_FATAL, ++ ++ /** @ref MMAL_PARAMETER_CONFIGFILE_T */ ++ MMAL_PARAMETER_CONFIGFILE_REGISTERS, ++ ++ /** @ref MMAL_PARAMETER_CONFIGFILE_CHUNK_T */ ++ MMAL_PARAMETER_CONFIGFILE_CHUNK_REGISTERS, ++ MMAL_PARAMETER_JPEG_ATTACH_LOG, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_ZERO_SHUTTER_LAG, /**< @ref MMAL_PARAMETER_ZEROSHUTTERLAG_T */ ++ MMAL_PARAMETER_FPS_RANGE, /**< @ref MMAL_PARAMETER_FPS_RANGE_T */ ++ MMAL_PARAMETER_CAPTURE_EXPOSURE_COMP, /**< @ref MMAL_PARAMETER_INT32_T */ ++ ++ /* 0x40 */ ++ MMAL_PARAMETER_SW_SHARPEN_DISABLE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_FLASH_REQUIRED, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_SW_SATURATION_DISABLE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++}; ++ ++enum mmal_parameter_camera_config_timestamp_mode { ++ MMAL_PARAM_TIMESTAMP_MODE_ZERO = 0, /* Always timestamp frames as 0 */ ++ MMAL_PARAM_TIMESTAMP_MODE_RAW_STC, /* Use the raw STC value ++ * for the frame timestamp ++ */ ++ MMAL_PARAM_TIMESTAMP_MODE_RESET_STC, /* Use the STC timestamp ++ * but subtract the ++ * timestamp of the first ++ * frame sent to give a ++ * zero based timestamp. ++ */ ++}; ++ ++/* camera configuration parameter */ ++struct mmal_parameter_camera_config { ++ /* Parameters for setting up the image pools */ ++ u32 max_stills_w; /* Max size of stills capture */ ++ u32 max_stills_h; ++ u32 stills_yuv422; /* Allow YUV422 stills capture */ ++ u32 one_shot_stills; /* Continuous or one shot stills captures. */ ++ ++ u32 max_preview_video_w; /* Max size of the preview or video ++ * capture frames ++ */ ++ u32 max_preview_video_h; ++ u32 num_preview_video_frames; ++ ++ /** Sets the height of the circular buffer for stills capture. */ ++ u32 stills_capture_circular_buffer_height; ++ ++ /** Allows preview/encode to resume as fast as possible after the stills ++ * input frame has been received, and then processes the still frame in ++ * the background whilst preview/encode has resumed. ++ * Actual mode is controlled by MMAL_PARAMETER_CAPTURE_MODE. ++ */ ++ u32 fast_preview_resume; ++ ++ /** Selects algorithm for timestamping frames if ++ * there is no clock component connected. ++ * enum mmal_parameter_camera_config_timestamp_mode ++ */ ++ s32 use_stc_timestamp; ++}; ++ ++ ++enum mmal_parameter_exposuremode { ++ MMAL_PARAM_EXPOSUREMODE_OFF, ++ MMAL_PARAM_EXPOSUREMODE_AUTO, ++ MMAL_PARAM_EXPOSUREMODE_NIGHT, ++ MMAL_PARAM_EXPOSUREMODE_NIGHTPREVIEW, ++ MMAL_PARAM_EXPOSUREMODE_BACKLIGHT, ++ MMAL_PARAM_EXPOSUREMODE_SPOTLIGHT, ++ MMAL_PARAM_EXPOSUREMODE_SPORTS, ++ MMAL_PARAM_EXPOSUREMODE_SNOW, ++ MMAL_PARAM_EXPOSUREMODE_BEACH, ++ MMAL_PARAM_EXPOSUREMODE_VERYLONG, ++ MMAL_PARAM_EXPOSUREMODE_FIXEDFPS, ++ MMAL_PARAM_EXPOSUREMODE_ANTISHAKE, ++ MMAL_PARAM_EXPOSUREMODE_FIREWORKS, ++}; ++ ++enum mmal_parameter_exposuremeteringmode { ++ MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE, ++ MMAL_PARAM_EXPOSUREMETERINGMODE_SPOT, ++ MMAL_PARAM_EXPOSUREMETERINGMODE_BACKLIT, ++ MMAL_PARAM_EXPOSUREMETERINGMODE_MATRIX, ++}; ++ ++enum mmal_parameter_awbmode { ++ MMAL_PARAM_AWBMODE_OFF, ++ MMAL_PARAM_AWBMODE_AUTO, ++ MMAL_PARAM_AWBMODE_SUNLIGHT, ++ MMAL_PARAM_AWBMODE_CLOUDY, ++ MMAL_PARAM_AWBMODE_SHADE, ++ MMAL_PARAM_AWBMODE_TUNGSTEN, ++ MMAL_PARAM_AWBMODE_FLUORESCENT, ++ MMAL_PARAM_AWBMODE_INCANDESCENT, ++ MMAL_PARAM_AWBMODE_FLASH, ++ MMAL_PARAM_AWBMODE_HORIZON, ++}; ++ ++enum mmal_parameter_imagefx { ++ MMAL_PARAM_IMAGEFX_NONE, ++ MMAL_PARAM_IMAGEFX_NEGATIVE, ++ MMAL_PARAM_IMAGEFX_SOLARIZE, ++ MMAL_PARAM_IMAGEFX_POSTERIZE, ++ MMAL_PARAM_IMAGEFX_WHITEBOARD, ++ MMAL_PARAM_IMAGEFX_BLACKBOARD, ++ MMAL_PARAM_IMAGEFX_SKETCH, ++ MMAL_PARAM_IMAGEFX_DENOISE, ++ MMAL_PARAM_IMAGEFX_EMBOSS, ++ MMAL_PARAM_IMAGEFX_OILPAINT, ++ MMAL_PARAM_IMAGEFX_HATCH, ++ MMAL_PARAM_IMAGEFX_GPEN, ++ MMAL_PARAM_IMAGEFX_PASTEL, ++ MMAL_PARAM_IMAGEFX_WATERCOLOUR, ++ MMAL_PARAM_IMAGEFX_FILM, ++ MMAL_PARAM_IMAGEFX_BLUR, ++ MMAL_PARAM_IMAGEFX_SATURATION, ++ MMAL_PARAM_IMAGEFX_COLOURSWAP, ++ MMAL_PARAM_IMAGEFX_WASHEDOUT, ++ MMAL_PARAM_IMAGEFX_POSTERISE, ++ MMAL_PARAM_IMAGEFX_COLOURPOINT, ++ MMAL_PARAM_IMAGEFX_COLOURBALANCE, ++ MMAL_PARAM_IMAGEFX_CARTOON, ++}; ++ ++/** Manner of video rate control */ ++enum mmal_parameter_rate_control_mode { ++ MMAL_VIDEO_RATECONTROL_DEFAULT, ++ MMAL_VIDEO_RATECONTROL_VARIABLE, ++ MMAL_VIDEO_RATECONTROL_CONSTANT, ++ MMAL_VIDEO_RATECONTROL_VARIABLE_SKIP_FRAMES, ++ MMAL_VIDEO_RATECONTROL_CONSTANT_SKIP_FRAMES ++}; ++ ++/* video parameters */ ++ ++enum mmal_parameter_video_type { ++ /** @ref MMAL_DISPLAYREGION_T */ ++ MMAL_PARAMETER_DISPLAYREGION = MMAL_PARAMETER_GROUP_VIDEO, ++ ++ /** @ref MMAL_PARAMETER_VIDEO_PROFILE_T */ ++ MMAL_PARAMETER_SUPPORTED_PROFILES, ++ ++ /** @ref MMAL_PARAMETER_VIDEO_PROFILE_T */ ++ MMAL_PARAMETER_PROFILE, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T */ ++ MMAL_PARAMETER_INTRAPERIOD, ++ ++ /** @ref MMAL_PARAMETER_VIDEO_RATECONTROL_T */ ++ MMAL_PARAMETER_RATECONTROL, ++ ++ /** @ref MMAL_PARAMETER_VIDEO_NALUNITFORMAT_T */ ++ MMAL_PARAMETER_NALUNITFORMAT, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_MINIMISE_FRAGMENTATION, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T. ++ * Setting the value to zero resets to the default (one slice per frame). ++ */ ++ MMAL_PARAMETER_MB_ROWS_PER_SLICE, ++ ++ /** @ref MMAL_PARAMETER_VIDEO_LEVEL_EXTENSION_T */ ++ MMAL_PARAMETER_VIDEO_LEVEL_EXTENSION, ++ ++ /** @ref MMAL_PARAMETER_VIDEO_EEDE_ENABLE_T */ ++ MMAL_PARAMETER_VIDEO_EEDE_ENABLE, ++ ++ /** @ref MMAL_PARAMETER_VIDEO_EEDE_LOSSRATE_T */ ++ MMAL_PARAMETER_VIDEO_EEDE_LOSSRATE, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T. Request an I-frame. */ ++ MMAL_PARAMETER_VIDEO_REQUEST_I_FRAME, ++ /** @ref MMAL_PARAMETER_VIDEO_INTRA_REFRESH_T */ ++ MMAL_PARAMETER_VIDEO_INTRA_REFRESH, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T. */ ++ MMAL_PARAMETER_VIDEO_IMMUTABLE_INPUT, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T. Run-time bit rate control */ ++ MMAL_PARAMETER_VIDEO_BIT_RATE, ++ ++ /** @ref MMAL_PARAMETER_FRAME_RATE_T */ ++ MMAL_PARAMETER_VIDEO_FRAME_RATE, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_MIN_QUANT, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_MAX_QUANT, ++ ++ /** @ref MMAL_PARAMETER_VIDEO_ENCODE_RC_MODEL_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_RC_MODEL, ++ ++ MMAL_PARAMETER_EXTRA_BUFFERS, /**< @ref MMAL_PARAMETER_UINT32_T. */ ++ /** @ref MMAL_PARAMETER_UINT32_T. ++ * Changing this parameter from the default can reduce frame rate ++ * because image buffers need to be re-pitched. ++ */ ++ MMAL_PARAMETER_VIDEO_ALIGN_HORIZ, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T. ++ * Changing this parameter from the default can reduce frame rate ++ * because image buffers need to be re-pitched. ++ */ ++ MMAL_PARAMETER_VIDEO_ALIGN_VERT, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T. */ ++ MMAL_PARAMETER_VIDEO_DROPPABLE_PFRAMES, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_INITIAL_QUANT, ++ ++ /**< @ref MMAL_PARAMETER_UINT32_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_QP_P, ++ ++ /**< @ref MMAL_PARAMETER_UINT32_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_RC_SLICE_DQUANT, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T */ ++ MMAL_PARAMETER_VIDEO_ENCODE_FRAME_LIMIT_BITS, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_PEAK_RATE, ++ ++ /* H264 specific parameters */ ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_H264_DISABLE_CABAC, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_H264_LOW_LATENCY, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_H264_AU_DELIMITERS, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_H264_DEBLOCK_IDC, ++ ++ /** @ref MMAL_PARAMETER_VIDEO_ENCODER_H264_MB_INTRA_MODES_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_H264_MB_INTRA_MODE, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_VIDEO_ENCODE_HEADER_ON_OPEN, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_VIDEO_ENCODE_PRECODE_FOR_QP, ++ ++ /** @ref MMAL_PARAMETER_VIDEO_DRM_INIT_INFO_T. */ ++ MMAL_PARAMETER_VIDEO_DRM_INIT_INFO, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_VIDEO_TIMESTAMP_FIFO, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_VIDEO_DECODE_ERROR_CONCEALMENT, ++ ++ /** @ref MMAL_PARAMETER_VIDEO_DRM_PROTECT_BUFFER_T. */ ++ MMAL_PARAMETER_VIDEO_DRM_PROTECT_BUFFER, ++ ++ /** @ref MMAL_PARAMETER_BYTES_T */ ++ MMAL_PARAMETER_VIDEO_DECODE_CONFIG_VD3 ++}; ++ ++/** Valid mirror modes */ ++enum mmal_parameter_mirror { ++ MMAL_PARAM_MIRROR_NONE, ++ MMAL_PARAM_MIRROR_VERTICAL, ++ MMAL_PARAM_MIRROR_HORIZONTAL, ++ MMAL_PARAM_MIRROR_BOTH, ++}; ++ ++enum mmal_parameter_displaytransform { ++ MMAL_DISPLAY_ROT0 = 0, ++ MMAL_DISPLAY_MIRROR_ROT0 = 1, ++ MMAL_DISPLAY_MIRROR_ROT180 = 2, ++ MMAL_DISPLAY_ROT180 = 3, ++ MMAL_DISPLAY_MIRROR_ROT90 = 4, ++ MMAL_DISPLAY_ROT270 = 5, ++ MMAL_DISPLAY_ROT90 = 6, ++ MMAL_DISPLAY_MIRROR_ROT270 = 7, ++}; ++ ++enum mmal_parameter_displaymode { ++ MMAL_DISPLAY_MODE_FILL = 0, ++ MMAL_DISPLAY_MODE_LETTERBOX = 1, ++}; ++ ++enum mmal_parameter_displayset { ++ MMAL_DISPLAY_SET_NONE = 0, ++ MMAL_DISPLAY_SET_NUM = 1, ++ MMAL_DISPLAY_SET_FULLSCREEN = 2, ++ MMAL_DISPLAY_SET_TRANSFORM = 4, ++ MMAL_DISPLAY_SET_DEST_RECT = 8, ++ MMAL_DISPLAY_SET_SRC_RECT = 0x10, ++ MMAL_DISPLAY_SET_MODE = 0x20, ++ MMAL_DISPLAY_SET_PIXEL = 0x40, ++ MMAL_DISPLAY_SET_NOASPECT = 0x80, ++ MMAL_DISPLAY_SET_LAYER = 0x100, ++ MMAL_DISPLAY_SET_COPYPROTECT = 0x200, ++ MMAL_DISPLAY_SET_ALPHA = 0x400, ++}; ++ ++struct mmal_parameter_displayregion { ++ /** Bitfield that indicates which fields are set and should be ++ * used. All other fields will maintain their current value. ++ * \ref MMAL_DISPLAYSET_T defines the bits that can be ++ * combined. ++ */ ++ u32 set; ++ ++ /** Describes the display output device, with 0 typically ++ * being a directly connected LCD display. The actual values ++ * will depend on the hardware. Code using hard-wired numbers ++ * (e.g. 2) is certain to fail. ++ */ ++ ++ u32 display_num; ++ /** Indicates that we are using the full device screen area, ++ * rather than a window of the display. If zero, then ++ * dest_rect is used to specify a region of the display to ++ * use. ++ */ ++ ++ s32 fullscreen; ++ /** Indicates any rotation or flipping used to map frames onto ++ * the natural display orientation. ++ */ ++ u32 transform; /* enum mmal_parameter_displaytransform */ ++ ++ /** Where to display the frame within the screen, if ++ * fullscreen is zero. ++ */ ++ struct vchiq_mmal_rect dest_rect; ++ ++ /** Indicates which area of the frame to display. If all ++ * values are zero, the whole frame will be used. ++ */ ++ struct vchiq_mmal_rect src_rect; ++ ++ /** If set to non-zero, indicates that any display scaling ++ * should disregard the aspect ratio of the frame region being ++ * displayed. ++ */ ++ s32 noaspect; ++ ++ /** Indicates how the image should be scaled to fit the ++ * display. \code MMAL_DISPLAY_MODE_FILL \endcode indicates ++ * that the image should fill the screen by potentially ++ * cropping the frames. Setting \code mode \endcode to \code ++ * MMAL_DISPLAY_MODE_LETTERBOX \endcode indicates that all the ++ * source region should be displayed and black bars added if ++ * necessary. ++ */ ++ u32 mode; /* enum mmal_parameter_displaymode */ ++ ++ /** If non-zero, defines the width of a source pixel relative ++ * to \code pixel_y \endcode. If zero, then pixels default to ++ * being square. ++ */ ++ u32 pixel_x; ++ ++ /** If non-zero, defines the height of a source pixel relative ++ * to \code pixel_x \endcode. If zero, then pixels default to ++ * being square. ++ */ ++ u32 pixel_y; ++ ++ /** Sets the relative depth of the images, with greater values ++ * being in front of smaller values. ++ */ ++ u32 layer; ++ ++ /** Set to non-zero to ensure copy protection is used on ++ * output. ++ */ ++ s32 copyprotect_required; ++ ++ /** Level of opacity of the layer, where zero is fully ++ * transparent and 255 is fully opaque. ++ */ ++ u32 alpha; ++}; ++ ++#define MMAL_MAX_IMAGEFX_PARAMETERS 5 ++ ++struct mmal_parameter_imagefx_parameters { ++ enum mmal_parameter_imagefx effect; ++ u32 num_effect_params; ++ u32 effect_parameter[MMAL_MAX_IMAGEFX_PARAMETERS]; ++}; +diff --git a/drivers/media/platform/bcm2835/mmal-vchiq.c b/drivers/media/platform/bcm2835/mmal-vchiq.c +new file mode 100644 +index 0000000..a06fb44 +--- /dev/null ++++ b/drivers/media/platform/bcm2835/mmal-vchiq.c +@@ -0,0 +1,1916 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders <vincent.sanders@collabora.co.uk> ++ * Dave Stevenson <dsteve@broadcom.com> ++ * Simon Mellor <simellor@broadcom.com> ++ * Luke Diamand <luked@broadcom.com> ++ * ++ * V4L2 driver MMAL vchiq interface code ++ */ ++ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include <linux/errno.h> ++#include <linux/kernel.h> ++#include <linux/mutex.h> ++#include <linux/mm.h> ++#include <linux/slab.h> ++#include <linux/completion.h> ++#include <linux/vmalloc.h> ++#include <asm/cacheflush.h> ++#include <media/videobuf2-vmalloc.h> ++ ++#include "mmal-common.h" ++#include "mmal-vchiq.h" ++#include "mmal-msg.h" ++ ++#define USE_VCHIQ_ARM ++#include "interface/vchi/vchi.h" ++ ++/* maximum number of components supported */ ++#define VCHIQ_MMAL_MAX_COMPONENTS 4 ++ ++/*#define FULL_MSG_DUMP 1*/ ++ ++#ifdef DEBUG ++static const char *const msg_type_names[] = { ++ "UNKNOWN", ++ "QUIT", ++ "SERVICE_CLOSED", ++ "GET_VERSION", ++ "COMPONENT_CREATE", ++ "COMPONENT_DESTROY", ++ "COMPONENT_ENABLE", ++ "COMPONENT_DISABLE", ++ "PORT_INFO_GET", ++ "PORT_INFO_SET", ++ "PORT_ACTION", ++ "BUFFER_FROM_HOST", ++ "BUFFER_TO_HOST", ++ "GET_STATS", ++ "PORT_PARAMETER_SET", ++ "PORT_PARAMETER_GET", ++ "EVENT_TO_HOST", ++ "GET_CORE_STATS_FOR_PORT", ++ "OPAQUE_ALLOCATOR", ++ "CONSUME_MEM", ++ "LMK", ++ "OPAQUE_ALLOCATOR_DESC", ++ "DRM_GET_LHS32", ++ "DRM_GET_TIME", ++ "BUFFER_FROM_HOST_ZEROLEN", ++ "PORT_FLUSH", ++ "HOST_LOG", ++}; ++#endif ++ ++static const char *const port_action_type_names[] = { ++ "UNKNOWN", ++ "ENABLE", ++ "DISABLE", ++ "FLUSH", ++ "CONNECT", ++ "DISCONNECT", ++ "SET_REQUIREMENTS", ++}; ++ ++#if defined(DEBUG) ++#if defined(FULL_MSG_DUMP) ++#define DBG_DUMP_MSG(MSG, MSG_LEN, TITLE) \ ++ do { \ ++ pr_debug(TITLE" type:%s(%d) length:%d\n", \ ++ msg_type_names[(MSG)->h.type], \ ++ (MSG)->h.type, (MSG_LEN)); \ ++ print_hex_dump(KERN_DEBUG, "<<h: ", DUMP_PREFIX_OFFSET, \ ++ 16, 4, (MSG), \ ++ sizeof(struct mmal_msg_header), 1); \ ++ print_hex_dump(KERN_DEBUG, "<<p: ", DUMP_PREFIX_OFFSET, \ ++ 16, 4, \ ++ ((u8 *)(MSG)) + sizeof(struct mmal_msg_header),\ ++ (MSG_LEN) - sizeof(struct mmal_msg_header), 1); \ ++ } while (0) ++#else ++#define DBG_DUMP_MSG(MSG, MSG_LEN, TITLE) \ ++ { \ ++ pr_debug(TITLE" type:%s(%d) length:%d\n", \ ++ msg_type_names[(MSG)->h.type], \ ++ (MSG)->h.type, (MSG_LEN)); \ ++ } ++#endif ++#else ++#define DBG_DUMP_MSG(MSG, MSG_LEN, TITLE) ++#endif ++ ++/* normal message context */ ++struct mmal_msg_context { ++ union { ++ struct { ++ /* work struct for defered callback - must come first */ ++ struct work_struct work; ++ /* mmal instance */ ++ struct vchiq_mmal_instance *instance; ++ /* mmal port */ ++ struct vchiq_mmal_port *port; ++ /* actual buffer used to store bulk reply */ ++ struct mmal_buffer *buffer; ++ /* amount of buffer used */ ++ unsigned long buffer_used; ++ /* MMAL buffer flags */ ++ u32 mmal_flags; ++ /* Presentation and Decode timestamps */ ++ s64 pts; ++ s64 dts; ++ ++ int status; /* context status */ ++ ++ } bulk; /* bulk data */ ++ ++ struct { ++ /* message handle to release */ ++ VCHI_HELD_MSG_T msg_handle; ++ /* pointer to received message */ ++ struct mmal_msg *msg; ++ /* received message length */ ++ u32 msg_len; ++ /* completion upon reply */ ++ struct completion cmplt; ++ } sync; /* synchronous response */ ++ } u; ++ ++}; ++ ++struct vchiq_mmal_instance { ++ VCHI_SERVICE_HANDLE_T handle; ++ ++ /* ensure serialised access to service */ ++ struct mutex vchiq_mutex; ++ ++ /* ensure serialised access to bulk operations */ ++ struct mutex bulk_mutex; ++ ++ /* vmalloc page to receive scratch bulk xfers into */ ++ void *bulk_scratch; ++ ++ /* component to use next */ ++ int component_idx; ++ struct vchiq_mmal_component component[VCHIQ_MMAL_MAX_COMPONENTS]; ++}; ++ ++static struct mmal_msg_context *get_msg_context(struct vchiq_mmal_instance ++ *instance) ++{ ++ struct mmal_msg_context *msg_context; ++ ++ /* todo: should this be allocated from a pool to avoid kmalloc */ ++ msg_context = kmalloc(sizeof(*msg_context), GFP_KERNEL); ++ memset(msg_context, 0, sizeof(*msg_context)); ++ ++ return msg_context; ++} ++ ++static void release_msg_context(struct mmal_msg_context *msg_context) ++{ ++ kfree(msg_context); ++} ++ ++/* deals with receipt of event to host message */ ++static void event_to_host_cb(struct vchiq_mmal_instance *instance, ++ struct mmal_msg *msg, u32 msg_len) ++{ ++ pr_debug("unhandled event\n"); ++ pr_debug("component:%p port type:%d num:%d cmd:0x%x length:%d\n", ++ msg->u.event_to_host.client_component, ++ msg->u.event_to_host.port_type, ++ msg->u.event_to_host.port_num, ++ msg->u.event_to_host.cmd, msg->u.event_to_host.length); ++} ++ ++/* workqueue scheduled callback ++ * ++ * we do this because it is important we do not call any other vchiq ++ * sync calls from witin the message delivery thread ++ */ ++static void buffer_work_cb(struct work_struct *work) ++{ ++ struct mmal_msg_context *msg_context = (struct mmal_msg_context *)work; ++ ++ msg_context->u.bulk.port->buffer_cb(msg_context->u.bulk.instance, ++ msg_context->u.bulk.port, ++ msg_context->u.bulk.status, ++ msg_context->u.bulk.buffer, ++ msg_context->u.bulk.buffer_used, ++ msg_context->u.bulk.mmal_flags, ++ msg_context->u.bulk.dts, ++ msg_context->u.bulk.pts); ++ ++ /* release message context */ ++ release_msg_context(msg_context); ++} ++ ++/* enqueue a bulk receive for a given message context */ ++static int bulk_receive(struct vchiq_mmal_instance *instance, ++ struct mmal_msg *msg, ++ struct mmal_msg_context *msg_context) ++{ ++ unsigned long rd_len; ++ unsigned long flags = 0; ++ int ret; ++ ++ /* bulk mutex stops other bulk operations while we have a ++ * receive in progress - released in callback ++ */ ++ ret = mutex_lock_interruptible(&instance->bulk_mutex); ++ if (ret != 0) ++ return ret; ++ ++ rd_len = msg->u.buffer_from_host.buffer_header.length; ++ ++ /* take buffer from queue */ ++ spin_lock_irqsave(&msg_context->u.bulk.port->slock, flags); ++ if (list_empty(&msg_context->u.bulk.port->buffers)) { ++ spin_unlock_irqrestore(&msg_context->u.bulk.port->slock, flags); ++ pr_err("buffer list empty trying to submit bulk receive\n"); ++ ++ /* todo: this is a serious error, we should never have ++ * commited a buffer_to_host operation to the mmal ++ * port without the buffer to back it up (underflow ++ * handling) and there is no obvious way to deal with ++ * this - how is the mmal servie going to react when ++ * we fail to do the xfer and reschedule a buffer when ++ * it arrives? perhaps a starved flag to indicate a ++ * waiting bulk receive? ++ */ ++ ++ mutex_unlock(&instance->bulk_mutex); ++ ++ return -EINVAL; ++ } ++ ++ msg_context->u.bulk.buffer = ++ list_entry(msg_context->u.bulk.port->buffers.next, ++ struct mmal_buffer, list); ++ list_del(&msg_context->u.bulk.buffer->list); ++ ++ spin_unlock_irqrestore(&msg_context->u.bulk.port->slock, flags); ++ ++ /* ensure we do not overrun the available buffer */ ++ if (rd_len > msg_context->u.bulk.buffer->buffer_size) { ++ rd_len = msg_context->u.bulk.buffer->buffer_size; ++ pr_warn("short read as not enough receive buffer space\n"); ++ /* todo: is this the correct response, what happens to ++ * the rest of the message data? ++ */ ++ } ++ ++ /* store length */ ++ msg_context->u.bulk.buffer_used = rd_len; ++ msg_context->u.bulk.mmal_flags = ++ msg->u.buffer_from_host.buffer_header.flags; ++ msg_context->u.bulk.dts = msg->u.buffer_from_host.buffer_header.dts; ++ msg_context->u.bulk.pts = msg->u.buffer_from_host.buffer_header.pts; ++ ++ // only need to flush L1 cache here, as VCHIQ takes care of the L2 ++ // cache. ++ __cpuc_flush_dcache_area(msg_context->u.bulk.buffer->buffer, rd_len); ++ ++ /* queue the bulk submission */ ++ vchi_service_use(instance->handle); ++ ret = vchi_bulk_queue_receive(instance->handle, ++ msg_context->u.bulk.buffer->buffer, ++ /* Actual receive needs to be a multiple ++ * of 4 bytes ++ */ ++ (rd_len + 3) & ~3, ++ VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE | ++ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, ++ msg_context); ++ ++ vchi_service_release(instance->handle); ++ ++ if (ret != 0) { ++ /* callback will not be clearing the mutex */ ++ mutex_unlock(&instance->bulk_mutex); ++ } ++ ++ return ret; ++} ++ ++/* enque a dummy bulk receive for a given message context */ ++static int dummy_bulk_receive(struct vchiq_mmal_instance *instance, ++ struct mmal_msg_context *msg_context) ++{ ++ int ret; ++ ++ /* bulk mutex stops other bulk operations while we have a ++ * receive in progress - released in callback ++ */ ++ ret = mutex_lock_interruptible(&instance->bulk_mutex); ++ if (ret != 0) ++ return ret; ++ ++ /* zero length indicates this was a dummy transfer */ ++ msg_context->u.bulk.buffer_used = 0; ++ ++ /* queue the bulk submission */ ++ vchi_service_use(instance->handle); ++ ++ ret = vchi_bulk_queue_receive(instance->handle, ++ instance->bulk_scratch, ++ 8, ++ VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE | ++ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, ++ msg_context); ++ ++ vchi_service_release(instance->handle); ++ ++ if (ret != 0) { ++ /* callback will not be clearing the mutex */ ++ mutex_unlock(&instance->bulk_mutex); ++ } ++ ++ return ret; ++} ++ ++/* data in message, memcpy from packet into output buffer */ ++static int inline_receive(struct vchiq_mmal_instance *instance, ++ struct mmal_msg *msg, ++ struct mmal_msg_context *msg_context) ++{ ++ unsigned long flags = 0; ++ ++ /* take buffer from queue */ ++ spin_lock_irqsave(&msg_context->u.bulk.port->slock, flags); ++ if (list_empty(&msg_context->u.bulk.port->buffers)) { ++ spin_unlock_irqrestore(&msg_context->u.bulk.port->slock, flags); ++ pr_err("buffer list empty trying to receive inline\n"); ++ ++ /* todo: this is a serious error, we should never have ++ * commited a buffer_to_host operation to the mmal ++ * port without the buffer to back it up (with ++ * underflow handling) and there is no obvious way to ++ * deal with this. Less bad than the bulk case as we ++ * can just drop this on the floor but...unhelpful ++ */ ++ return -EINVAL; ++ } ++ ++ msg_context->u.bulk.buffer = ++ list_entry(msg_context->u.bulk.port->buffers.next, ++ struct mmal_buffer, list); ++ list_del(&msg_context->u.bulk.buffer->list); ++ ++ spin_unlock_irqrestore(&msg_context->u.bulk.port->slock, flags); ++ ++ memcpy(msg_context->u.bulk.buffer->buffer, ++ msg->u.buffer_from_host.short_data, ++ msg->u.buffer_from_host.payload_in_message); ++ ++ msg_context->u.bulk.buffer_used = ++ msg->u.buffer_from_host.payload_in_message; ++ ++ return 0; ++} ++ ++/* queue the buffer availability with MMAL_MSG_TYPE_BUFFER_FROM_HOST */ ++static int ++buffer_from_host(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, struct mmal_buffer *buf) ++{ ++ struct mmal_msg_context *msg_context; ++ struct mmal_msg m; ++ int ret; ++ ++ pr_debug("instance:%p buffer:%p\n", instance->handle, buf); ++ ++ /* bulk mutex stops other bulk operations while we ++ * have a receive in progress ++ */ ++ if (mutex_lock_interruptible(&instance->bulk_mutex)) ++ return -EINTR; ++ ++ /* get context */ ++ msg_context = get_msg_context(instance); ++ if (msg_context == NULL) ++ return -ENOMEM; ++ ++ /* store bulk message context for when data arrives */ ++ msg_context->u.bulk.instance = instance; ++ msg_context->u.bulk.port = port; ++ msg_context->u.bulk.buffer = NULL; /* not valid until bulk xfer */ ++ msg_context->u.bulk.buffer_used = 0; ++ ++ /* initialise work structure ready to schedule callback */ ++ INIT_WORK(&msg_context->u.bulk.work, buffer_work_cb); ++ ++ /* prep the buffer from host message */ ++ memset(&m, 0xbc, sizeof(m)); /* just to make debug clearer */ ++ ++ m.h.type = MMAL_MSG_TYPE_BUFFER_FROM_HOST; ++ m.h.magic = MMAL_MAGIC; ++ m.h.context = msg_context; ++ m.h.status = 0; ++ ++ /* drvbuf is our private data passed back */ ++ m.u.buffer_from_host.drvbuf.magic = MMAL_MAGIC; ++ m.u.buffer_from_host.drvbuf.component_handle = port->component->handle; ++ m.u.buffer_from_host.drvbuf.port_handle = port->handle; ++ m.u.buffer_from_host.drvbuf.client_context = msg_context; ++ ++ /* buffer header */ ++ m.u.buffer_from_host.buffer_header.cmd = 0; ++ m.u.buffer_from_host.buffer_header.data = buf->buffer; ++ m.u.buffer_from_host.buffer_header.alloc_size = buf->buffer_size; ++ m.u.buffer_from_host.buffer_header.length = 0; /* nothing used yet */ ++ m.u.buffer_from_host.buffer_header.offset = 0; /* no offset */ ++ m.u.buffer_from_host.buffer_header.flags = 0; /* no flags */ ++ m.u.buffer_from_host.buffer_header.pts = MMAL_TIME_UNKNOWN; ++ m.u.buffer_from_host.buffer_header.dts = MMAL_TIME_UNKNOWN; ++ ++ /* clear buffer type sepecific data */ ++ memset(&m.u.buffer_from_host.buffer_header_type_specific, 0, ++ sizeof(m.u.buffer_from_host.buffer_header_type_specific)); ++ ++ /* no payload in message */ ++ m.u.buffer_from_host.payload_in_message = 0; ++ ++ vchi_service_use(instance->handle); ++ ++ ret = vchi_msg_queue(instance->handle, &m, ++ sizeof(struct mmal_msg_header) + ++ sizeof(m.u.buffer_from_host), ++ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); ++ ++ if (ret != 0) { ++ release_msg_context(msg_context); ++ /* todo: is this correct error value? */ ++ } ++ ++ vchi_service_release(instance->handle); ++ ++ mutex_unlock(&instance->bulk_mutex); ++ ++ return ret; ++} ++ ++/* submit a buffer to the mmal sevice ++ * ++ * the buffer_from_host uses size data from the ports next available ++ * mmal_buffer and deals with there being no buffer available by ++ * incrementing the underflow for later ++ */ ++static int port_buffer_from_host(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port) ++{ ++ int ret; ++ struct mmal_buffer *buf; ++ unsigned long flags = 0; ++ ++ if (!port->enabled) ++ return -EINVAL; ++ ++ /* peek buffer from queue */ ++ spin_lock_irqsave(&port->slock, flags); ++ if (list_empty(&port->buffers)) { ++ port->buffer_underflow++; ++ spin_unlock_irqrestore(&port->slock, flags); ++ return -ENOSPC; ++ } ++ ++ buf = list_entry(port->buffers.next, struct mmal_buffer, list); ++ ++ spin_unlock_irqrestore(&port->slock, flags); ++ ++ /* issue buffer to mmal service */ ++ ret = buffer_from_host(instance, port, buf); ++ if (ret) { ++ pr_err("adding buffer header failed\n"); ++ /* todo: how should this be dealt with */ ++ } ++ ++ return ret; ++} ++ ++/* deals with receipt of buffer to host message */ ++static void buffer_to_host_cb(struct vchiq_mmal_instance *instance, ++ struct mmal_msg *msg, u32 msg_len) ++{ ++ struct mmal_msg_context *msg_context; ++ ++ pr_debug("buffer_to_host_cb: instance:%p msg:%p msg_len:%d\n", ++ instance, msg, msg_len); ++ ++ if (msg->u.buffer_from_host.drvbuf.magic == MMAL_MAGIC) { ++ msg_context = msg->u.buffer_from_host.drvbuf.client_context; ++ } else { ++ pr_err("MMAL_MSG_TYPE_BUFFER_TO_HOST with bad magic\n"); ++ return; ++ } ++ ++ if (msg->h.status != MMAL_MSG_STATUS_SUCCESS) { ++ /* message reception had an error */ ++ pr_warn("error %d in reply\n", msg->h.status); ++ ++ msg_context->u.bulk.status = msg->h.status; ++ ++ } else if (msg->u.buffer_from_host.buffer_header.length == 0) { ++ /* empty buffer */ ++ if (msg->u.buffer_from_host.buffer_header.flags & ++ MMAL_BUFFER_HEADER_FLAG_EOS) { ++ msg_context->u.bulk.status = ++ dummy_bulk_receive(instance, msg_context); ++ if (msg_context->u.bulk.status == 0) ++ return; /* successful bulk submission, bulk ++ * completion will trigger callback ++ */ ++ } else { ++ /* do callback with empty buffer - not EOS though */ ++ msg_context->u.bulk.status = 0; ++ msg_context->u.bulk.buffer_used = 0; ++ } ++ } else if (msg->u.buffer_from_host.payload_in_message == 0) { ++ /* data is not in message, queue a bulk receive */ ++ msg_context->u.bulk.status = ++ bulk_receive(instance, msg, msg_context); ++ if (msg_context->u.bulk.status == 0) ++ return; /* successful bulk submission, bulk ++ * completion will trigger callback ++ */ ++ ++ /* failed to submit buffer, this will end badly */ ++ pr_err("error %d on bulk submission\n", ++ msg_context->u.bulk.status); ++ ++ } else if (msg->u.buffer_from_host.payload_in_message <= ++ MMAL_VC_SHORT_DATA) { ++ /* data payload within message */ ++ msg_context->u.bulk.status = inline_receive(instance, msg, ++ msg_context); ++ } else { ++ pr_err("message with invalid short payload\n"); ++ ++ /* signal error */ ++ msg_context->u.bulk.status = -EINVAL; ++ msg_context->u.bulk.buffer_used = ++ msg->u.buffer_from_host.payload_in_message; ++ } ++ ++ /* replace the buffer header */ ++ port_buffer_from_host(instance, msg_context->u.bulk.port); ++ ++ /* schedule the port callback */ ++ schedule_work(&msg_context->u.bulk.work); ++} ++ ++static void bulk_receive_cb(struct vchiq_mmal_instance *instance, ++ struct mmal_msg_context *msg_context) ++{ ++ /* bulk receive operation complete */ ++ mutex_unlock(&msg_context->u.bulk.instance->bulk_mutex); ++ ++ /* replace the buffer header */ ++ port_buffer_from_host(msg_context->u.bulk.instance, ++ msg_context->u.bulk.port); ++ ++ msg_context->u.bulk.status = 0; ++ ++ /* schedule the port callback */ ++ schedule_work(&msg_context->u.bulk.work); ++} ++ ++static void bulk_abort_cb(struct vchiq_mmal_instance *instance, ++ struct mmal_msg_context *msg_context) ++{ ++ pr_err("%s: bulk ABORTED msg_context:%p\n", __func__, msg_context); ++ ++ /* bulk receive operation complete */ ++ mutex_unlock(&msg_context->u.bulk.instance->bulk_mutex); ++ ++ /* replace the buffer header */ ++ port_buffer_from_host(msg_context->u.bulk.instance, ++ msg_context->u.bulk.port); ++ ++ msg_context->u.bulk.status = -EINTR; ++ ++ schedule_work(&msg_context->u.bulk.work); ++} ++ ++/* incoming event service callback */ ++static void service_callback(void *param, ++ const VCHI_CALLBACK_REASON_T reason, ++ void *bulk_ctx) ++{ ++ struct vchiq_mmal_instance *instance = param; ++ int status; ++ u32 msg_len; ++ struct mmal_msg *msg; ++ VCHI_HELD_MSG_T msg_handle; ++ ++ if (!instance) { ++ pr_err("Message callback passed NULL instance\n"); ++ return; ++ } ++ ++ switch (reason) { ++ case VCHI_CALLBACK_MSG_AVAILABLE: ++ status = vchi_msg_hold(instance->handle, (void **)&msg, ++ &msg_len, VCHI_FLAGS_NONE, &msg_handle); ++ if (status) { ++ pr_err("Unable to dequeue a message (%d)\n", status); ++ break; ++ } ++ ++ DBG_DUMP_MSG(msg, msg_len, "<<< reply message"); ++ ++ /* handling is different for buffer messages */ ++ switch (msg->h.type) { ++ ++ case MMAL_MSG_TYPE_BUFFER_FROM_HOST: ++ vchi_held_msg_release(&msg_handle); ++ break; ++ ++ case MMAL_MSG_TYPE_EVENT_TO_HOST: ++ event_to_host_cb(instance, msg, msg_len); ++ vchi_held_msg_release(&msg_handle); ++ ++ break; ++ ++ case MMAL_MSG_TYPE_BUFFER_TO_HOST: ++ buffer_to_host_cb(instance, msg, msg_len); ++ vchi_held_msg_release(&msg_handle); ++ break; ++ ++ default: ++ /* messages dependant on header context to complete */ ++ ++ /* todo: the msg.context really ought to be sanity ++ * checked before we just use it, afaict it comes back ++ * and is used raw from the videocore. Perhaps it ++ * should be verified the address lies in the kernel ++ * address space. ++ */ ++ if (msg->h.context == NULL) { ++ pr_err("received message context was null!\n"); ++ vchi_held_msg_release(&msg_handle); ++ break; ++ } ++ ++ /* fill in context values */ ++ msg->h.context->u.sync.msg_handle = msg_handle; ++ msg->h.context->u.sync.msg = msg; ++ msg->h.context->u.sync.msg_len = msg_len; ++ ++ /* todo: should this check (completion_done() ++ * == 1) for no one waiting? or do we need a ++ * flag to tell us the completion has been ++ * interrupted so we can free the message and ++ * its context. This probably also solves the ++ * message arriving after interruption todo ++ * below ++ */ ++ ++ /* complete message so caller knows it happened */ ++ complete(&msg->h.context->u.sync.cmplt); ++ break; ++ } ++ ++ break; ++ ++ case VCHI_CALLBACK_BULK_RECEIVED: ++ bulk_receive_cb(instance, bulk_ctx); ++ break; ++ ++ case VCHI_CALLBACK_BULK_RECEIVE_ABORTED: ++ bulk_abort_cb(instance, bulk_ctx); ++ break; ++ ++ case VCHI_CALLBACK_SERVICE_CLOSED: ++ /* TODO: consider if this requires action if received when ++ * driver is not explicitly closing the service ++ */ ++ break; ++ ++ default: ++ pr_err("Received unhandled message reason %d\n", reason); ++ break; ++ } ++} ++ ++static int send_synchronous_mmal_msg(struct vchiq_mmal_instance *instance, ++ struct mmal_msg *msg, ++ unsigned int payload_len, ++ struct mmal_msg **msg_out, ++ VCHI_HELD_MSG_T *msg_handle_out) ++{ ++ struct mmal_msg_context msg_context; ++ int ret; ++ ++ /* payload size must not cause message to exceed max size */ ++ if (payload_len > ++ (MMAL_MSG_MAX_SIZE - sizeof(struct mmal_msg_header))) { ++ pr_err("payload length %d exceeds max:%d\n", payload_len, ++ (MMAL_MSG_MAX_SIZE - sizeof(struct mmal_msg_header))); ++ return -EINVAL; ++ } ++ ++ init_completion(&msg_context.u.sync.cmplt); ++ ++ msg->h.magic = MMAL_MAGIC; ++ msg->h.context = &msg_context; ++ msg->h.status = 0; ++ ++ DBG_DUMP_MSG(msg, (sizeof(struct mmal_msg_header) + payload_len), ++ ">>> sync message"); ++ ++ vchi_service_use(instance->handle); ++ ++ ret = vchi_msg_queue(instance->handle, ++ msg, ++ sizeof(struct mmal_msg_header) + payload_len, ++ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); ++ ++ vchi_service_release(instance->handle); ++ ++ if (ret) { ++ pr_err("error %d queuing message\n", ret); ++ return ret; ++ } ++ ++ ret = wait_for_completion_timeout(&msg_context.u.sync.cmplt, HZ); ++ if (ret <= 0) { ++ pr_err("error %d waiting for sync completion\n", ret); ++ if (ret == 0) ++ ret = -ETIME; ++ /* todo: what happens if the message arrives after aborting */ ++ return ret; ++ } ++ ++ *msg_out = msg_context.u.sync.msg; ++ *msg_handle_out = msg_context.u.sync.msg_handle; ++ ++ return 0; ++} ++ ++static void dump_port_info(struct vchiq_mmal_port *port) ++{ ++ pr_debug("port handle:0x%x enabled:%d\n", port->handle, port->enabled); ++ ++ pr_debug("buffer minimum num:%d size:%d align:%d\n", ++ port->minimum_buffer.num, ++ port->minimum_buffer.size, port->minimum_buffer.alignment); ++ ++ pr_debug("buffer recommended num:%d size:%d align:%d\n", ++ port->recommended_buffer.num, ++ port->recommended_buffer.size, ++ port->recommended_buffer.alignment); ++ ++ pr_debug("buffer current values num:%d size:%d align:%d\n", ++ port->current_buffer.num, ++ port->current_buffer.size, port->current_buffer.alignment); ++ ++ pr_debug("elementry stream: type:%d encoding:0x%x varient:0x%x\n", ++ port->format.type, ++ port->format.encoding, port->format.encoding_variant); ++ ++ pr_debug(" bitrate:%d flags:0x%x\n", ++ port->format.bitrate, port->format.flags); ++ ++ if (port->format.type == MMAL_ES_TYPE_VIDEO) { ++ pr_debug ++ ("es video format: width:%d height:%d colourspace:0x%x\n", ++ port->es.video.width, port->es.video.height, ++ port->es.video.color_space); ++ ++ pr_debug(" : crop xywh %d,%d,%d,%d\n", ++ port->es.video.crop.x, ++ port->es.video.crop.y, ++ port->es.video.crop.width, port->es.video.crop.height); ++ pr_debug(" : framerate %d/%d aspect %d/%d\n", ++ port->es.video.frame_rate.num, ++ port->es.video.frame_rate.den, ++ port->es.video.par.num, port->es.video.par.den); ++ } ++} ++ ++static void port_to_mmal_msg(struct vchiq_mmal_port *port, struct mmal_port *p) ++{ ++ ++ /* todo do readonly fields need setting at all? */ ++ p->type = port->type; ++ p->index = port->index; ++ p->index_all = 0; ++ p->is_enabled = port->enabled; ++ p->buffer_num_min = port->minimum_buffer.num; ++ p->buffer_size_min = port->minimum_buffer.size; ++ p->buffer_alignment_min = port->minimum_buffer.alignment; ++ p->buffer_num_recommended = port->recommended_buffer.num; ++ p->buffer_size_recommended = port->recommended_buffer.size; ++ ++ /* only three writable fields in a port */ ++ p->buffer_num = port->current_buffer.num; ++ p->buffer_size = port->current_buffer.size; ++ p->userdata = port; ++} ++ ++static int port_info_set(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port) ++{ ++ int ret; ++ struct mmal_msg m; ++ struct mmal_msg *rmsg; ++ VCHI_HELD_MSG_T rmsg_handle; ++ ++ pr_debug("setting port info port %p\n", port); ++ if (!port) ++ return -1; ++ dump_port_info(port); ++ ++ m.h.type = MMAL_MSG_TYPE_PORT_INFO_SET; ++ ++ m.u.port_info_set.component_handle = port->component->handle; ++ m.u.port_info_set.port_type = port->type; ++ m.u.port_info_set.port_index = port->index; ++ ++ port_to_mmal_msg(port, &m.u.port_info_set.port); ++ ++ /* elementry stream format setup */ ++ m.u.port_info_set.format.type = port->format.type; ++ m.u.port_info_set.format.encoding = port->format.encoding; ++ m.u.port_info_set.format.encoding_variant = ++ port->format.encoding_variant; ++ m.u.port_info_set.format.bitrate = port->format.bitrate; ++ m.u.port_info_set.format.flags = port->format.flags; ++ ++ memcpy(&m.u.port_info_set.es, &port->es, ++ sizeof(union mmal_es_specific_format)); ++ ++ m.u.port_info_set.format.extradata_size = port->format.extradata_size; ++ memcpy(rmsg->u.port_info_set.extradata, port->format.extradata, ++ port->format.extradata_size); ++ ++ ret = send_synchronous_mmal_msg(instance, &m, ++ sizeof(m.u.port_info_set), ++ &rmsg, &rmsg_handle); ++ if (ret) ++ return ret; ++ ++ if (rmsg->h.type != MMAL_MSG_TYPE_PORT_INFO_SET) { ++ /* got an unexpected message type in reply */ ++ ret = -EINVAL; ++ goto release_msg; ++ } ++ ++ /* return operation status */ ++ ret = -rmsg->u.port_info_get_reply.status; ++ ++ pr_debug("%s:result:%d component:0x%x port:%d\n", __func__, ret, ++ port->component->handle, port->handle); ++ ++release_msg: ++ vchi_held_msg_release(&rmsg_handle); ++ ++ return ret; ++ ++} ++ ++/* use port info get message to retrive port information */ ++static int port_info_get(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port) ++{ ++ int ret; ++ struct mmal_msg m; ++ struct mmal_msg *rmsg; ++ VCHI_HELD_MSG_T rmsg_handle; ++ ++ /* port info time */ ++ m.h.type = MMAL_MSG_TYPE_PORT_INFO_GET; ++ m.u.port_info_get.component_handle = port->component->handle; ++ m.u.port_info_get.port_type = port->type; ++ m.u.port_info_get.index = port->index; ++ ++ ret = send_synchronous_mmal_msg(instance, &m, ++ sizeof(m.u.port_info_get), ++ &rmsg, &rmsg_handle); ++ if (ret) ++ return ret; ++ ++ if (rmsg->h.type != MMAL_MSG_TYPE_PORT_INFO_GET) { ++ /* got an unexpected message type in reply */ ++ ret = -EINVAL; ++ goto release_msg; ++ } ++ ++ /* return operation status */ ++ ret = -rmsg->u.port_info_get_reply.status; ++ if (ret != MMAL_MSG_STATUS_SUCCESS) ++ goto release_msg; ++ ++ if (rmsg->u.port_info_get_reply.port.is_enabled == 0) ++ port->enabled = false; ++ else ++ port->enabled = true; ++ ++ /* copy the values out of the message */ ++ port->handle = rmsg->u.port_info_get_reply.port_handle; ++ ++ /* port type and index cached to use on port info set becuase ++ * it does not use a port handle ++ */ ++ port->type = rmsg->u.port_info_get_reply.port_type; ++ port->index = rmsg->u.port_info_get_reply.port_index; ++ ++ port->minimum_buffer.num = ++ rmsg->u.port_info_get_reply.port.buffer_num_min; ++ port->minimum_buffer.size = ++ rmsg->u.port_info_get_reply.port.buffer_size_min; ++ port->minimum_buffer.alignment = ++ rmsg->u.port_info_get_reply.port.buffer_alignment_min; ++ ++ port->recommended_buffer.alignment = ++ rmsg->u.port_info_get_reply.port.buffer_alignment_min; ++ port->recommended_buffer.num = ++ rmsg->u.port_info_get_reply.port.buffer_num_recommended; ++ ++ port->current_buffer.num = rmsg->u.port_info_get_reply.port.buffer_num; ++ port->current_buffer.size = ++ rmsg->u.port_info_get_reply.port.buffer_size; ++ ++ /* stream format */ ++ port->format.type = rmsg->u.port_info_get_reply.format.type; ++ port->format.encoding = rmsg->u.port_info_get_reply.format.encoding; ++ port->format.encoding_variant = ++ rmsg->u.port_info_get_reply.format.encoding_variant; ++ port->format.bitrate = rmsg->u.port_info_get_reply.format.bitrate; ++ port->format.flags = rmsg->u.port_info_get_reply.format.flags; ++ ++ /* elementry stream format */ ++ memcpy(&port->es, ++ &rmsg->u.port_info_get_reply.es, ++ sizeof(union mmal_es_specific_format)); ++ port->format.es = &port->es; ++ ++ port->format.extradata_size = ++ rmsg->u.port_info_get_reply.format.extradata_size; ++ memcpy(port->format.extradata, ++ rmsg->u.port_info_get_reply.extradata, ++ port->format.extradata_size); ++ ++ pr_debug("received port info\n"); ++ dump_port_info(port); ++ ++release_msg: ++ ++ pr_debug("%s:result:%d component:0x%x port:%d\n", ++ __func__, ret, port->component->handle, port->handle); ++ ++ vchi_held_msg_release(&rmsg_handle); ++ ++ return ret; ++} ++ ++/* create comonent on vc */ ++static int create_component(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_component *component, ++ const char *name) ++{ ++ int ret; ++ struct mmal_msg m; ++ struct mmal_msg *rmsg; ++ VCHI_HELD_MSG_T rmsg_handle; ++ ++ /* build component create message */ ++ m.h.type = MMAL_MSG_TYPE_COMPONENT_CREATE; ++ m.u.component_create.client_component = component; ++ strncpy(m.u.component_create.name, name, ++ sizeof(m.u.component_create.name)); ++ ++ ret = send_synchronous_mmal_msg(instance, &m, ++ sizeof(m.u.component_create), ++ &rmsg, &rmsg_handle); ++ if (ret) ++ return ret; ++ ++ if (rmsg->h.type != m.h.type) { ++ /* got an unexpected message type in reply */ ++ ret = -EINVAL; ++ goto release_msg; ++ } ++ ++ ret = -rmsg->u.component_create_reply.status; ++ if (ret != MMAL_MSG_STATUS_SUCCESS) ++ goto release_msg; ++ ++ /* a valid component response received */ ++ component->handle = rmsg->u.component_create_reply.component_handle; ++ component->inputs = rmsg->u.component_create_reply.input_num; ++ component->outputs = rmsg->u.component_create_reply.output_num; ++ component->clocks = rmsg->u.component_create_reply.clock_num; ++ ++ pr_debug("Component handle:0x%x in:%d out:%d clock:%d\n", ++ component->handle, ++ component->inputs, component->outputs, component->clocks); ++ ++release_msg: ++ vchi_held_msg_release(&rmsg_handle); ++ ++ return ret; ++} ++ ++/* destroys a component on vc */ ++static int destroy_component(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_component *component) ++{ ++ int ret; ++ struct mmal_msg m; ++ struct mmal_msg *rmsg; ++ VCHI_HELD_MSG_T rmsg_handle; ++ ++ m.h.type = MMAL_MSG_TYPE_COMPONENT_DESTROY; ++ m.u.component_destroy.component_handle = component->handle; ++ ++ ret = send_synchronous_mmal_msg(instance, &m, ++ sizeof(m.u.component_destroy), ++ &rmsg, &rmsg_handle); ++ if (ret) ++ return ret; ++ ++ if (rmsg->h.type != m.h.type) { ++ /* got an unexpected message type in reply */ ++ ret = -EINVAL; ++ goto release_msg; ++ } ++ ++ ret = -rmsg->u.component_destroy_reply.status; ++ ++release_msg: ++ ++ vchi_held_msg_release(&rmsg_handle); ++ ++ return ret; ++} ++ ++/* enable a component on vc */ ++static int enable_component(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_component *component) ++{ ++ int ret; ++ struct mmal_msg m; ++ struct mmal_msg *rmsg; ++ VCHI_HELD_MSG_T rmsg_handle; ++ ++ m.h.type = MMAL_MSG_TYPE_COMPONENT_ENABLE; ++ m.u.component_enable.component_handle = component->handle; ++ ++ ret = send_synchronous_mmal_msg(instance, &m, ++ sizeof(m.u.component_enable), ++ &rmsg, &rmsg_handle); ++ if (ret) ++ return ret; ++ ++ if (rmsg->h.type != m.h.type) { ++ /* got an unexpected message type in reply */ ++ ret = -EINVAL; ++ goto release_msg; ++ } ++ ++ ret = -rmsg->u.component_enable_reply.status; ++ ++release_msg: ++ vchi_held_msg_release(&rmsg_handle); ++ ++ return ret; ++} ++ ++/* disable a component on vc */ ++static int disable_component(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_component *component) ++{ ++ int ret; ++ struct mmal_msg m; ++ struct mmal_msg *rmsg; ++ VCHI_HELD_MSG_T rmsg_handle; ++ ++ m.h.type = MMAL_MSG_TYPE_COMPONENT_DISABLE; ++ m.u.component_disable.component_handle = component->handle; ++ ++ ret = send_synchronous_mmal_msg(instance, &m, ++ sizeof(m.u.component_disable), ++ &rmsg, &rmsg_handle); ++ if (ret) ++ return ret; ++ ++ if (rmsg->h.type != m.h.type) { ++ /* got an unexpected message type in reply */ ++ ret = -EINVAL; ++ goto release_msg; ++ } ++ ++ ret = -rmsg->u.component_disable_reply.status; ++ ++release_msg: ++ ++ vchi_held_msg_release(&rmsg_handle); ++ ++ return ret; ++} ++ ++/* get version of mmal implementation */ ++static int get_version(struct vchiq_mmal_instance *instance, ++ u32 *major_out, u32 *minor_out) ++{ ++ int ret; ++ struct mmal_msg m; ++ struct mmal_msg *rmsg; ++ VCHI_HELD_MSG_T rmsg_handle; ++ ++ m.h.type = MMAL_MSG_TYPE_GET_VERSION; ++ ++ ret = send_synchronous_mmal_msg(instance, &m, ++ sizeof(m.u.version), ++ &rmsg, &rmsg_handle); ++ if (ret) ++ return ret; ++ ++ if (rmsg->h.type != m.h.type) { ++ /* got an unexpected message type in reply */ ++ ret = -EINVAL; ++ goto release_msg; ++ } ++ ++ *major_out = rmsg->u.version.major; ++ *minor_out = rmsg->u.version.minor; ++ ++release_msg: ++ vchi_held_msg_release(&rmsg_handle); ++ ++ return ret; ++} ++ ++/* do a port action with a port as a parameter */ ++static int port_action_port(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ enum mmal_msg_port_action_type action_type) ++{ ++ int ret; ++ struct mmal_msg m; ++ struct mmal_msg *rmsg; ++ VCHI_HELD_MSG_T rmsg_handle; ++ ++ m.h.type = MMAL_MSG_TYPE_PORT_ACTION; ++ m.u.port_action_port.component_handle = port->component->handle; ++ m.u.port_action_port.port_handle = port->handle; ++ m.u.port_action_port.action = action_type; ++ ++ port_to_mmal_msg(port, &m.u.port_action_port.port); ++ ++ ret = send_synchronous_mmal_msg(instance, &m, ++ sizeof(m.u.port_action_port), ++ &rmsg, &rmsg_handle); ++ if (ret) ++ return ret; ++ ++ if (rmsg->h.type != MMAL_MSG_TYPE_PORT_ACTION) { ++ /* got an unexpected message type in reply */ ++ ret = -EINVAL; ++ goto release_msg; ++ } ++ ++ ret = -rmsg->u.port_action_reply.status; ++ ++ pr_debug("%s:result:%d component:0x%x port:%d action:%s(%d)\n", ++ __func__, ++ ret, port->component->handle, port->handle, ++ port_action_type_names[action_type], action_type); ++ ++release_msg: ++ vchi_held_msg_release(&rmsg_handle); ++ ++ return ret; ++} ++ ++/* do a port action with handles as parameters */ ++static int port_action_handle(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ enum mmal_msg_port_action_type action_type, ++ u32 connect_component_handle, ++ u32 connect_port_handle) ++{ ++ int ret; ++ struct mmal_msg m; ++ struct mmal_msg *rmsg; ++ VCHI_HELD_MSG_T rmsg_handle; ++ ++ m.h.type = MMAL_MSG_TYPE_PORT_ACTION; ++ ++ m.u.port_action_handle.component_handle = port->component->handle; ++ m.u.port_action_handle.port_handle = port->handle; ++ m.u.port_action_handle.action = action_type; ++ ++ m.u.port_action_handle.connect_component_handle = ++ connect_component_handle; ++ m.u.port_action_handle.connect_port_handle = connect_port_handle; ++ ++ ret = send_synchronous_mmal_msg(instance, &m, ++ sizeof(m.u.port_action_handle), ++ &rmsg, &rmsg_handle); ++ if (ret) ++ return ret; ++ ++ if (rmsg->h.type != MMAL_MSG_TYPE_PORT_ACTION) { ++ /* got an unexpected message type in reply */ ++ ret = -EINVAL; ++ goto release_msg; ++ } ++ ++ ret = -rmsg->u.port_action_reply.status; ++ ++ pr_debug("%s:result:%d component:0x%x port:%d action:%s(%d)" \ ++ " connect component:0x%x connect port:%d\n", ++ __func__, ++ ret, port->component->handle, port->handle, ++ port_action_type_names[action_type], ++ action_type, connect_component_handle, connect_port_handle); ++ ++release_msg: ++ vchi_held_msg_release(&rmsg_handle); ++ ++ return ret; ++} ++ ++static int port_parameter_set(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ u32 parameter_id, void *value, u32 value_size) ++{ ++ int ret; ++ struct mmal_msg m; ++ struct mmal_msg *rmsg; ++ VCHI_HELD_MSG_T rmsg_handle; ++ ++ m.h.type = MMAL_MSG_TYPE_PORT_PARAMETER_SET; ++ ++ m.u.port_parameter_set.component_handle = port->component->handle; ++ m.u.port_parameter_set.port_handle = port->handle; ++ m.u.port_parameter_set.id = parameter_id; ++ m.u.port_parameter_set.size = (2 * sizeof(u32)) + value_size; ++ memcpy(&m.u.port_parameter_set.value, value, value_size); ++ ++ ret = send_synchronous_mmal_msg(instance, &m, ++ (4 * sizeof(u32)) + value_size, ++ &rmsg, &rmsg_handle); ++ if (ret) ++ return ret; ++ ++ if (rmsg->h.type != MMAL_MSG_TYPE_PORT_PARAMETER_SET) { ++ /* got an unexpected message type in reply */ ++ ret = -EINVAL; ++ goto release_msg; ++ } ++ ++ ret = -rmsg->u.port_parameter_set_reply.status; ++ ++ pr_debug("%s:result:%d component:0x%x port:%d parameter:%d\n", ++ __func__, ++ ret, port->component->handle, port->handle, parameter_id); ++ ++release_msg: ++ vchi_held_msg_release(&rmsg_handle); ++ ++ return ret; ++} ++ ++static int port_parameter_get(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ u32 parameter_id, void *value, u32 *value_size) ++{ ++ int ret; ++ struct mmal_msg m; ++ struct mmal_msg *rmsg; ++ VCHI_HELD_MSG_T rmsg_handle; ++ ++ m.h.type = MMAL_MSG_TYPE_PORT_PARAMETER_GET; ++ ++ m.u.port_parameter_get.component_handle = port->component->handle; ++ m.u.port_parameter_get.port_handle = port->handle; ++ m.u.port_parameter_get.id = parameter_id; ++ m.u.port_parameter_get.size = (2 * sizeof(u32)) + *value_size; ++ ++ ret = send_synchronous_mmal_msg(instance, &m, ++ sizeof(struct ++ mmal_msg_port_parameter_get), ++ &rmsg, &rmsg_handle); ++ if (ret) ++ return ret; ++ ++ if (rmsg->h.type != MMAL_MSG_TYPE_PORT_PARAMETER_GET) { ++ /* got an unexpected message type in reply */ ++ pr_err("Incorrect reply type %d\n", rmsg->h.type); ++ ret = -EINVAL; ++ goto release_msg; ++ } ++ ++ ret = -rmsg->u.port_parameter_get_reply.status; ++ if (ret) { ++ /* Copy only as much as we have space for ++ * but report true size of parameter ++ */ ++ memcpy(value, &rmsg->u.port_parameter_get_reply.value, ++ *value_size); ++ *value_size = rmsg->u.port_parameter_get_reply.size; ++ } else ++ memcpy(value, &rmsg->u.port_parameter_get_reply.value, ++ rmsg->u.port_parameter_get_reply.size); ++ ++ pr_info("%s:result:%d component:0x%x port:%d parameter:%d\n", __func__, ++ ret, port->component->handle, port->handle, parameter_id); ++ ++release_msg: ++ vchi_held_msg_release(&rmsg_handle); ++ ++ return ret; ++} ++ ++/* disables a port and drains buffers from it */ ++static int port_disable(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port) ++{ ++ int ret; ++ struct list_head *q, *buf_head; ++ unsigned long flags = 0; ++ ++ if (!port->enabled) ++ return 0; ++ ++ port->enabled = false; ++ ++ ret = port_action_port(instance, port, ++ MMAL_MSG_PORT_ACTION_TYPE_DISABLE); ++ if (ret == 0) { ++ ++ /* drain all queued buffers on port */ ++ spin_lock_irqsave(&port->slock, flags); ++ ++ list_for_each_safe(buf_head, q, &port->buffers) { ++ struct mmal_buffer *mmalbuf; ++ mmalbuf = list_entry(buf_head, struct mmal_buffer, ++ list); ++ list_del(buf_head); ++ if (port->buffer_cb) ++ port->buffer_cb(instance, ++ port, 0, mmalbuf, 0, 0, ++ MMAL_TIME_UNKNOWN, ++ MMAL_TIME_UNKNOWN); ++ } ++ ++ spin_unlock_irqrestore(&port->slock, flags); ++ ++ ret = port_info_get(instance, port); ++ } ++ ++ return ret; ++} ++ ++/* enable a port */ ++static int port_enable(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port) ++{ ++ unsigned int hdr_count; ++ struct list_head *buf_head; ++ int ret; ++ ++ if (port->enabled) ++ return 0; ++ ++ /* ensure there are enough buffers queued to cover the buffer headers */ ++ if (port->buffer_cb != NULL) { ++ hdr_count = 0; ++ list_for_each(buf_head, &port->buffers) { ++ hdr_count++; ++ } ++ if (hdr_count < port->current_buffer.num) ++ return -ENOSPC; ++ } ++ ++ ret = port_action_port(instance, port, ++ MMAL_MSG_PORT_ACTION_TYPE_ENABLE); ++ if (ret) ++ goto done; ++ ++ port->enabled = true; ++ ++ if (port->buffer_cb) { ++ /* send buffer headers to videocore */ ++ hdr_count = 1; ++ list_for_each(buf_head, &port->buffers) { ++ struct mmal_buffer *mmalbuf; ++ mmalbuf = list_entry(buf_head, struct mmal_buffer, ++ list); ++ ret = buffer_from_host(instance, port, mmalbuf); ++ if (ret) ++ goto done; ++ ++ hdr_count++; ++ if (hdr_count > port->current_buffer.num) ++ break; ++ } ++ } ++ ++ ret = port_info_get(instance, port); ++ ++done: ++ return ret; ++} ++ ++/* ------------------------------------------------------------------ ++ * Exported API ++ *------------------------------------------------------------------*/ ++ ++int vchiq_mmal_port_set_format(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ ret = port_info_set(instance, port); ++ if (ret) ++ goto release_unlock; ++ ++ /* read what has actually been set */ ++ ret = port_info_get(instance, port); ++ ++release_unlock: ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return ret; ++ ++} ++ ++int vchiq_mmal_port_parameter_set(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ u32 parameter, void *value, u32 value_size) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ ret = port_parameter_set(instance, port, parameter, value, value_size); ++ ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return ret; ++} ++ ++int vchiq_mmal_port_parameter_get(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ u32 parameter, void *value, u32 *value_size) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ ret = port_parameter_get(instance, port, parameter, value, value_size); ++ ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return ret; ++} ++ ++/* enable a port ++ * ++ * enables a port and queues buffers for satisfying callbacks if we ++ * provide a callback handler ++ */ ++int vchiq_mmal_port_enable(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ vchiq_mmal_buffer_cb buffer_cb) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ /* already enabled - noop */ ++ if (port->enabled) { ++ ret = 0; ++ goto unlock; ++ } ++ ++ port->buffer_cb = buffer_cb; ++ ++ ret = port_enable(instance, port); ++ ++unlock: ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return ret; ++} ++ ++int vchiq_mmal_port_disable(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ if (!port->enabled) { ++ mutex_unlock(&instance->vchiq_mutex); ++ return 0; ++ } ++ ++ ret = port_disable(instance, port); ++ ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return ret; ++} ++ ++/* ports will be connected in a tunneled manner so data buffers ++ * are not handled by client. ++ */ ++int vchiq_mmal_port_connect_tunnel(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *src, ++ struct vchiq_mmal_port *dst) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ /* disconnect ports if connected */ ++ if (src->connected != NULL) { ++ ret = port_disable(instance, src); ++ if (ret) { ++ pr_err("failed disabling src port(%d)\n", ret); ++ goto release_unlock; ++ } ++ ++ /* do not need to disable the destination port as they ++ * are connected and it is done automatically ++ */ ++ ++ ret = port_action_handle(instance, src, ++ MMAL_MSG_PORT_ACTION_TYPE_DISCONNECT, ++ src->connected->component->handle, ++ src->connected->handle); ++ if (ret < 0) { ++ pr_err("failed disconnecting src port\n"); ++ goto release_unlock; ++ } ++ src->connected->enabled = false; ++ src->connected = NULL; ++ } ++ ++ if (dst == NULL) { ++ /* do not make new connection */ ++ ret = 0; ++ pr_debug("not making new connection\n"); ++ goto release_unlock; ++ } ++ ++ /* copy src port format to dst */ ++ dst->format.encoding = src->format.encoding; ++ dst->es.video.width = src->es.video.width; ++ dst->es.video.height = src->es.video.height; ++ dst->es.video.crop.x = src->es.video.crop.x; ++ dst->es.video.crop.y = src->es.video.crop.y; ++ dst->es.video.crop.width = src->es.video.crop.width; ++ dst->es.video.crop.height = src->es.video.crop.height; ++ dst->es.video.frame_rate.num = src->es.video.frame_rate.num; ++ dst->es.video.frame_rate.den = src->es.video.frame_rate.den; ++ ++ /* set new format */ ++ ret = port_info_set(instance, dst); ++ if (ret) { ++ pr_debug("setting port info failed\n"); ++ goto release_unlock; ++ } ++ ++ /* read what has actually been set */ ++ ret = port_info_get(instance, dst); ++ if (ret) { ++ pr_debug("read back port info failed\n"); ++ goto release_unlock; ++ } ++ ++ /* connect two ports together */ ++ ret = port_action_handle(instance, src, ++ MMAL_MSG_PORT_ACTION_TYPE_CONNECT, ++ dst->component->handle, dst->handle); ++ if (ret < 0) { ++ pr_debug("connecting port %d:%d to %d:%d failed\n", ++ src->component->handle, src->handle, ++ dst->component->handle, dst->handle); ++ goto release_unlock; ++ } ++ src->connected = dst; ++ ++release_unlock: ++ ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return ret; ++} ++ ++int vchiq_mmal_submit_buffer(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ struct mmal_buffer *buffer) ++{ ++ unsigned long flags = 0; ++ ++ spin_lock_irqsave(&port->slock, flags); ++ list_add_tail(&buffer->list, &port->buffers); ++ spin_unlock_irqrestore(&port->slock, flags); ++ ++ /* the port previously underflowed because it was missing a ++ * mmal_buffer which has just been added, submit that buffer ++ * to the mmal service. ++ */ ++ if (port->buffer_underflow) { ++ port_buffer_from_host(instance, port); ++ port->buffer_underflow--; ++ } ++ ++ return 0; ++} ++ ++/* Initialise a mmal component and its ports ++ * ++ */ ++int vchiq_mmal_component_init(struct vchiq_mmal_instance *instance, ++ const char *name, ++ struct vchiq_mmal_component **component_out) ++{ ++ int ret; ++ int idx; /* port index */ ++ struct vchiq_mmal_component *component; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ if (instance->component_idx == VCHIQ_MMAL_MAX_COMPONENTS) { ++ ret = -EINVAL; /* todo is this correct error? */ ++ goto unlock; ++ } ++ ++ component = &instance->component[instance->component_idx]; ++ ++ ret = create_component(instance, component, name); ++ if (ret < 0) ++ goto unlock; ++ ++ /* ports info needs gathering */ ++ component->control.type = MMAL_PORT_TYPE_CONTROL; ++ component->control.index = 0; ++ component->control.component = component; ++ spin_lock_init(&component->control.slock); ++ INIT_LIST_HEAD(&component->control.buffers); ++ ret = port_info_get(instance, &component->control); ++ if (ret < 0) ++ goto release_component; ++ ++ for (idx = 0; idx < component->inputs; idx++) { ++ component->input[idx].type = MMAL_PORT_TYPE_INPUT; ++ component->input[idx].index = idx; ++ component->input[idx].component = component; ++ spin_lock_init(&component->input[idx].slock); ++ INIT_LIST_HEAD(&component->input[idx].buffers); ++ ret = port_info_get(instance, &component->input[idx]); ++ if (ret < 0) ++ goto release_component; ++ } ++ ++ for (idx = 0; idx < component->outputs; idx++) { ++ component->output[idx].type = MMAL_PORT_TYPE_OUTPUT; ++ component->output[idx].index = idx; ++ component->output[idx].component = component; ++ spin_lock_init(&component->output[idx].slock); ++ INIT_LIST_HEAD(&component->output[idx].buffers); ++ ret = port_info_get(instance, &component->output[idx]); ++ if (ret < 0) ++ goto release_component; ++ } ++ ++ for (idx = 0; idx < component->clocks; idx++) { ++ component->clock[idx].type = MMAL_PORT_TYPE_CLOCK; ++ component->clock[idx].index = idx; ++ component->clock[idx].component = component; ++ spin_lock_init(&component->clock[idx].slock); ++ INIT_LIST_HEAD(&component->clock[idx].buffers); ++ ret = port_info_get(instance, &component->clock[idx]); ++ if (ret < 0) ++ goto release_component; ++ } ++ ++ instance->component_idx++; ++ ++ *component_out = component; ++ ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return 0; ++ ++release_component: ++ destroy_component(instance, component); ++unlock: ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return ret; ++} ++ ++/* ++ * cause a mmal component to be destroyed ++ */ ++int vchiq_mmal_component_finalise(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_component *component) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ if (component->enabled) ++ ret = disable_component(instance, component); ++ ++ ret = destroy_component(instance, component); ++ ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return ret; ++} ++ ++/* ++ * cause a mmal component to be enabled ++ */ ++int vchiq_mmal_component_enable(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_component *component) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ if (component->enabled) { ++ mutex_unlock(&instance->vchiq_mutex); ++ return 0; ++ } ++ ++ ret = enable_component(instance, component); ++ if (ret == 0) ++ component->enabled = true; ++ ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return ret; ++} ++ ++/* ++ * cause a mmal component to be enabled ++ */ ++int vchiq_mmal_component_disable(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_component *component) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ if (!component->enabled) { ++ mutex_unlock(&instance->vchiq_mutex); ++ return 0; ++ } ++ ++ ret = disable_component(instance, component); ++ if (ret == 0) ++ component->enabled = false; ++ ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return ret; ++} ++ ++int vchiq_mmal_version(struct vchiq_mmal_instance *instance, ++ u32 *major_out, u32 *minor_out) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ ret = get_version(instance, major_out, minor_out); ++ ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return ret; ++} ++ ++int vchiq_mmal_finalise(struct vchiq_mmal_instance *instance) ++{ ++ int status = 0; ++ ++ if (instance == NULL) ++ return -EINVAL; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ vchi_service_use(instance->handle); ++ ++ status = vchi_service_close(instance->handle); ++ if (status != 0) ++ pr_err("mmal-vchiq: VCHIQ close failed"); ++ ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ vfree(instance->bulk_scratch); ++ ++ kfree(instance); ++ ++ return status; ++} ++ ++int vchiq_mmal_init(struct vchiq_mmal_instance **out_instance) ++{ ++ int status; ++ struct vchiq_mmal_instance *instance; ++ static VCHI_CONNECTION_T *vchi_connection; ++ static VCHI_INSTANCE_T vchi_instance; ++ SERVICE_CREATION_T params = { ++ VCHI_VERSION_EX(VC_MMAL_VER, VC_MMAL_MIN_VER), ++ VC_MMAL_SERVER_NAME, ++ vchi_connection, ++ 0, /* rx fifo size (unused) */ ++ 0, /* tx fifo size (unused) */ ++ service_callback, ++ NULL, /* service callback parameter */ ++ 1, /* unaligned bulk receives */ ++ 1, /* unaligned bulk transmits */ ++ 0 /* want crc check on bulk transfers */ ++ }; ++ ++ /* compile time checks to ensure structure size as they are ++ * directly (de)serialised from memory. ++ */ ++ ++ /* ensure the header structure has packed to the correct size */ ++ BUILD_BUG_ON(sizeof(struct mmal_msg_header) != 24); ++ ++ /* ensure message structure does not exceed maximum length */ ++ BUILD_BUG_ON(sizeof(struct mmal_msg) > MMAL_MSG_MAX_SIZE); ++ ++ /* mmal port struct is correct size */ ++ BUILD_BUG_ON(sizeof(struct mmal_port) != 64); ++ ++ /* create a vchi instance */ ++ status = vchi_initialise(&vchi_instance); ++ if (status) { ++ pr_err("Failed to initialise VCHI instance (status=%d)\n", ++ status); ++ return -EIO; ++ } ++ ++ status = vchi_connect(NULL, 0, vchi_instance); ++ if (status) { ++ pr_err("Failed to connect VCHI instance (status=%d)\n", status); ++ return -EIO; ++ } ++ ++ instance = kmalloc(sizeof(*instance), GFP_KERNEL); ++ memset(instance, 0, sizeof(*instance)); ++ ++ mutex_init(&instance->vchiq_mutex); ++ mutex_init(&instance->bulk_mutex); ++ ++ instance->bulk_scratch = vmalloc(PAGE_SIZE); ++ ++ params.callback_param = instance; ++ ++ status = vchi_service_open(vchi_instance, ¶ms, &instance->handle); ++ if (status) { ++ pr_err("Failed to open VCHI service connection (status=%d)\n", ++ status); ++ goto err_close_services; ++ } ++ ++ vchi_service_release(instance->handle); ++ ++ *out_instance = instance; ++ ++ return 0; ++ ++err_close_services: ++ ++ vchi_service_close(instance->handle); ++ vfree(instance->bulk_scratch); ++ kfree(instance); ++ return -ENODEV; ++} +diff --git a/drivers/media/platform/bcm2835/mmal-vchiq.h b/drivers/media/platform/bcm2835/mmal-vchiq.h +new file mode 100644 +index 0000000..9d1d11e +--- /dev/null ++++ b/drivers/media/platform/bcm2835/mmal-vchiq.h +@@ -0,0 +1,178 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders <vincent.sanders@collabora.co.uk> ++ * Dave Stevenson <dsteve@broadcom.com> ++ * Simon Mellor <simellor@broadcom.com> ++ * Luke Diamand <luked@broadcom.com> ++ * ++ * MMAL interface to VCHIQ message passing ++ */ ++ ++#ifndef MMAL_VCHIQ_H ++#define MMAL_VCHIQ_H ++ ++#include "mmal-msg-format.h" ++ ++#define MAX_PORT_COUNT 4 ++ ++/* Maximum size of the format extradata. */ ++#define MMAL_FORMAT_EXTRADATA_MAX_SIZE 128 ++ ++struct vchiq_mmal_instance; ++ ++enum vchiq_mmal_es_type { ++ MMAL_ES_TYPE_UNKNOWN, /**< Unknown elementary stream type */ ++ MMAL_ES_TYPE_CONTROL, /**< Elementary stream of control commands */ ++ MMAL_ES_TYPE_AUDIO, /**< Audio elementary stream */ ++ MMAL_ES_TYPE_VIDEO, /**< Video elementary stream */ ++ MMAL_ES_TYPE_SUBPICTURE /**< Sub-picture elementary stream */ ++}; ++ ++/* rectangle, used lots so it gets its own struct */ ++struct vchiq_mmal_rect { ++ s32 x; ++ s32 y; ++ s32 width; ++ s32 height; ++}; ++ ++struct vchiq_mmal_port_buffer { ++ unsigned int num; /* number of buffers */ ++ u32 size; /* size of buffers */ ++ u32 alignment; /* alignment of buffers */ ++}; ++ ++struct vchiq_mmal_port; ++ ++typedef void (*vchiq_mmal_buffer_cb)( ++ struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ int status, struct mmal_buffer *buffer, ++ unsigned long length, u32 mmal_flags, s64 dts, s64 pts); ++ ++struct vchiq_mmal_port { ++ bool enabled; ++ u32 handle; ++ u32 type; /* port type, cached to use on port info set */ ++ u32 index; /* port index, cached to use on port info set */ ++ ++ /* component port belongs to, allows simple deref */ ++ struct vchiq_mmal_component *component; ++ ++ struct vchiq_mmal_port *connected; /* port conencted to */ ++ ++ /* buffer info */ ++ struct vchiq_mmal_port_buffer minimum_buffer; ++ struct vchiq_mmal_port_buffer recommended_buffer; ++ struct vchiq_mmal_port_buffer current_buffer; ++ ++ /* stream format */ ++ struct mmal_es_format format; ++ /* elementry stream format */ ++ union mmal_es_specific_format es; ++ ++ /* data buffers to fill */ ++ struct list_head buffers; ++ /* lock to serialise adding and removing buffers from list */ ++ spinlock_t slock; ++ /* count of how many buffer header refils have failed because ++ * there was no buffer to satisfy them ++ */ ++ int buffer_underflow; ++ /* callback on buffer completion */ ++ vchiq_mmal_buffer_cb buffer_cb; ++ /* callback context */ ++ void *cb_ctx; ++}; ++ ++struct vchiq_mmal_component { ++ bool enabled; ++ u32 handle; /* VideoCore handle for component */ ++ u32 inputs; /* Number of input ports */ ++ u32 outputs; /* Number of output ports */ ++ u32 clocks; /* Number of clock ports */ ++ struct vchiq_mmal_port control; /* control port */ ++ struct vchiq_mmal_port input[MAX_PORT_COUNT]; /* input ports */ ++ struct vchiq_mmal_port output[MAX_PORT_COUNT]; /* output ports */ ++ struct vchiq_mmal_port clock[MAX_PORT_COUNT]; /* clock ports */ ++}; ++ ++ ++int vchiq_mmal_init(struct vchiq_mmal_instance **out_instance); ++int vchiq_mmal_finalise(struct vchiq_mmal_instance *instance); ++ ++/* Initialise a mmal component and its ports ++* ++*/ ++int vchiq_mmal_component_init( ++ struct vchiq_mmal_instance *instance, ++ const char *name, ++ struct vchiq_mmal_component **component_out); ++ ++int vchiq_mmal_component_finalise( ++ struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_component *component); ++ ++int vchiq_mmal_component_enable( ++ struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_component *component); ++ ++int vchiq_mmal_component_disable( ++ struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_component *component); ++ ++ ++ ++/* enable a mmal port ++ * ++ * enables a port and if a buffer callback provided enque buffer ++ * headers as apropriate for the port. ++ */ ++int vchiq_mmal_port_enable( ++ struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ vchiq_mmal_buffer_cb buffer_cb); ++ ++/* disable a port ++ * ++ * disable a port will dequeue any pending buffers ++ */ ++int vchiq_mmal_port_disable(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port); ++ ++ ++int vchiq_mmal_port_parameter_set(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ u32 parameter, ++ void *value, ++ u32 value_size); ++ ++int vchiq_mmal_port_parameter_get(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ u32 parameter, ++ void *value, ++ u32 *value_size); ++ ++int vchiq_mmal_port_set_format(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port); ++ ++int vchiq_mmal_port_connect_tunnel(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *src, ++ struct vchiq_mmal_port *dst); ++ ++int vchiq_mmal_version(struct vchiq_mmal_instance *instance, ++ u32 *major_out, ++ u32 *minor_out); ++ ++int vchiq_mmal_submit_buffer(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ struct mmal_buffer *buf); ++ ++#endif /* MMAL_VCHIQ_H */ +-- +1.9.1 + |