/*
* camera image capture (abstract) bus driver
*
* Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
*
* This driver provides an interface between platform-specific camera
* busses and camera devices. It should be used if the camera is
* connected not over a "proper" bus like PCI or USB, but over a
* special bus, like, for example, the Quick Capture interface on PXA270
* SoCs. Later it should also be used for i.MX31 SoCs from Freescale.
* It can handle multiple cameras and / or multiple busses, which can
* be used, e.g., in stereo-vision applications.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/list.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/vmalloc.h>
#include <media/v4l2-common.h>
#include <media/v4l2-dev.h>
#include <media/soc_camera.h>
static LIST_HEAD(hosts);
static LIST_HEAD(devices);
static DEFINE_MUTEX(list_lock);
static DEFINE_MUTEX(video_lock);
const static struct soc_camera_data_format*
format_by_fourcc(struct soc_camera_device *icd, unsigned int fourcc)
{
unsigned int i;
for (i = 0; i < icd->num_formats; i++)
if (icd->formats[i].fourcc == fourcc)
return icd->formats + i;
return NULL;
}
static int soc_camera_try_fmt_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
struct soc_camera_host *ici =
to_soc_camera_host(icd->dev.parent);
enum v4l2_field field;
const struct soc_camera_data_format *fmt;
int ret;
WARN_ON(priv != file->private_data);
fmt = format_by_fourcc(icd, f->fmt.pix.pixelformat);
if (!fmt) {
dev_dbg(&icd->dev, "invalid format 0x%08x\n",
f->fmt.pix.pixelformat);
return -EINVAL;
}
dev_dbg(&icd->dev, "fmt: 0x%08x\n", fmt->fourcc);
field = f->fmt.pix.field;
if (field == V4L2_FIELD_ANY) {
field = V4L2_FIELD_NONE;
} else if (V4L2_FIELD_NONE != field) {
dev_err(&icd->dev, "Field type invalid.\n");
return -EINVAL;
}
/* test physical bus parameters */
ret = ici->ops->try_bus_param(icd, f->fmt.pix.pixelformat);
if (ret)
return ret;
/* limit format to hardware capabilities */
ret = ici->ops->try_fmt_cap(icd, f);
/* calculate missing fields */
f->fmt.pix.field = field;
f->fmt.pix.bytesperline =
(f->fmt.pix.width * fmt->depth) >> 3;
f->fmt.pix.sizeimage =
f->fmt.pix.height * f->fmt.pix.bytesperline;
return ret;
}
static int soc_camera_enum_input(struct file *file, void *priv,
struct v4l2_input *inp)
{
if (inp->index != 0)
return -EINVAL;
inp->type = V4L2_INPUT_TYPE_CAMERA;
inp->std = V4L2_STD_UNKNOWN;
strcpy(inp->name, "Camera");
return 0;
}
static int soc_camera_g_input(struct file *file, void *priv, unsigned int *i)
{
*i = 0;
return 0;
}
static int soc_camera_s_input(struct file *file, void *priv, unsigned int i)
{
if (i > 0)
return -EINVAL;
return 0;
}
static int soc_camera_s_std(struct file *file, void *priv, v4l2_std_id *a)
{
return 0;
}
static int soc_camera_reqbufs(struct file *file, void *priv,
struct v4l2_requestbuffers *p)
{
int ret;
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
struct soc_camera_host