aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/soc_camera.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/video/soc_camera.c')
-rw-r--r--drivers/media/video/soc_camera.c141
1 files changed, 91 insertions, 50 deletions
diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c
index fcb05f06de8f..0e890cc23377 100644
--- a/drivers/media/video/soc_camera.c
+++ b/drivers/media/video/soc_camera.c
@@ -30,6 +30,10 @@
#include <media/videobuf-core.h>
#include <media/soc_camera.h>
+/* Default to VGA resolution */
+#define DEFAULT_WIDTH 640
+#define DEFAULT_HEIGHT 480
+
static LIST_HEAD(hosts);
static LIST_HEAD(devices);
static DEFINE_MUTEX(list_lock);
@@ -256,6 +260,46 @@ static void soc_camera_free_user_formats(struct soc_camera_device *icd)
vfree(icd->user_formats);
}
+/* Called with .vb_lock held */
+static int soc_camera_set_fmt(struct soc_camera_file *icf,
+ struct v4l2_format *f)
+{
+ struct soc_camera_device *icd = icf->icd;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ int ret;
+
+ /* We always call try_fmt() before set_fmt() or set_crop() */
+ ret = ici->ops->try_fmt(icd, f);
+ if (ret < 0)
+ return ret;
+
+ ret = ici->ops->set_fmt(icd, f);
+ if (ret < 0) {
+ return ret;
+ } else if (!icd->current_fmt ||
+ icd->current_fmt->fourcc != pix->pixelformat) {
+ dev_err(&ici->dev,
+ "Host driver hasn't set up current format correctly!\n");
+ return -EINVAL;
+ }
+
+ icd->width = pix->width;
+ icd->height = pix->height;
+ icf->vb_vidq.field =
+ icd->field = pix->field;
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ dev_warn(&icd->dev, "Attention! Wrong buf-type %d\n",
+ f->type);
+
+ dev_dbg(&icd->dev, "set width: %d height: %d\n",
+ icd->width, icd->height);
+
+ /* set physical bus parameters */
+ return ici->ops->set_bus_param(icd, pix->pixelformat);
+}
+
static int soc_camera_open(struct file *file)
{
struct video_device *vdev;
@@ -297,14 +341,28 @@ static int soc_camera_open(struct file *file)
/* Now we really have to activate the camera */
if (icd->use_count == 1) {
- ret = soc_camera_init_user_formats(icd);
- if (ret < 0)
- goto eiufmt;
+ /* Restore parameters before the last close() per V4L2 API */
+ struct v4l2_format f = {
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .fmt.pix = {
+ .width = icd->width,
+ .height = icd->height,
+ .field = icd->field,
+ .pixelformat = icd->current_fmt->fourcc,
+ .colorspace = icd->current_fmt->colorspace,
+ },
+ };
+
ret = ici->ops->add(icd);
if (ret < 0) {
dev_err(&icd->dev, "Couldn't activate the camera: %d\n", ret);
goto eiciadd;
}
+
+ /* Try to configure with default parameters */
+ ret = soc_camera_set_fmt(icf, &f);
+ if (ret < 0)
+ goto esfmt;
}
mutex_unlock(&icd->video_lock);
@@ -316,10 +374,13 @@ static int soc_camera_open(struct file *file)
return 0;
- /* First two errors are entered with the .video_lock held */
+ /*
+ * First three errors are entered with the .video_lock held
+ * and use_count == 1
+ */
+esfmt:
+ ici->ops->remove(icd);
eiciadd:
- soc_camera_free_user_formats(icd);
-eiufmt:
icd->use_count--;
mutex_unlock(&icd->video_lock);
module_put(ici->ops->owner);
@@ -339,10 +400,9 @@ static int soc_camera_close(struct file *file)
mutex_lock(&icd->video_lock);
icd->use_count--;
- if (!icd->use_count) {
+ if (!icd->use_count)
ici->ops->remove(icd);
- soc_camera_free_user_formats(icd);
- }
+
mutex_unlock(&icd->video_lock);
module_put(icd->ops->owner);
@@ -415,18 +475,10 @@ static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv,
{
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);
- struct v4l2_pix_format *pix = &f->fmt.pix;
- __u32 pixfmt = pix->pixelformat;
int ret;
- struct v4l2_rect rect;
WARN_ON(priv != file->private_data);
- ret = soc_camera_try_fmt_vid_cap(file, priv, f);
- if (ret < 0)
- return ret;
-
mutex_lock(&icf->vb_vidq.vb_lock);
if (videobuf_queue_is_busy(&icf->vb_vidq)) {
@@ -435,33 +487,7 @@ static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv,
goto unlock;
}
- rect.left = icd->x_current;
- rect.top = icd->y_current;
- rect.width = pix->width;
- rect.height = pix->height;
- ret = ici->ops->set_fmt(icd, pix->pixelformat, &rect);
- if (ret < 0) {
- goto unlock;
- } else if (!icd->current_fmt ||
- icd->current_fmt->fourcc != pixfmt) {
- dev_err(&ici->dev,
- "Host driver hasn't set up current format correctly!\n");
- ret = -EINVAL;
- goto unlock;
- }
-
- icd->width = rect.width;
- icd->height = rect.height;
- icf->vb_vidq.field = pix->field;
- if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- dev_warn(&icd->dev, "Attention! Wrong buf-type %d\n",
- f->type);
-
- dev_dbg(&icd->dev, "set width: %d height: %d\n",
- icd->width, icd->height);
-
- /* set physical bus parameters */
- ret = ici->ops->set_bus_param(icd, pixfmt);
+ ret = soc_camera_set_fmt(icf, f);
unlock:
mutex_unlock(&icf->vb_vidq.vb_lock);
@@ -648,8 +674,8 @@ static int soc_camera_cropcap(struct file *file, void *fh,
a->bounds.height = icd->height_max;
a->defrect.left = icd->x_min;
a->defrect.top = icd->y_min;
- a->defrect.width = 640;
- a->defrect.height = 480;
+ a->defrect.width = DEFAULT_WIDTH;
+ a->defrect.height = DEFAULT_HEIGHT;
a->pixelaspect.numerator = 1;
a->pixelaspect.denominator = 1;
@@ -685,7 +711,7 @@ static int soc_camera_s_crop(struct file *file, void *fh,
/* Cropping is allowed during a running capture, guard consistency */
mutex_lock(&icf->vb_vidq.vb_lock);
- ret = ici->ops->set_fmt(icd, 0, &a->c);
+ ret = ici->ops->set_crop(icd, &a->c);
if (!ret) {
icd->width = a->c.width;
icd->height = a->c.height;
@@ -738,7 +764,10 @@ static int soc_camera_s_register(struct file *file, void *fh,
static int device_register_link(struct soc_camera_device *icd)
{
- int ret = device_register(&icd->dev);
+ int ret = dev_set_name(&icd->dev, "%u-%u", icd->iface, icd->devnum);
+
+ if (!ret)
+ ret = device_register(&icd->dev);
if (ret < 0) {
/* Prevent calling device_unregister() */
@@ -844,9 +873,18 @@ static int soc_camera_probe(struct device *dev)
qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_EXPOSURE);
icd->exposure = qctrl ? qctrl->default_value :
(unsigned short)~0;
+
+ ret = soc_camera_init_user_formats(icd);
+ if (ret < 0)
+ goto eiufmt;
+
+ icd->height = DEFAULT_HEIGHT;
+ icd->width = DEFAULT_WIDTH;
+ icd->field = V4L2_FIELD_ANY;
}
- ici->ops->remove(icd);
+eiufmt:
+ ici->ops->remove(icd);
eiadd:
mutex_unlock(&icd->video_lock);
module_put(ici->ops->owner);
@@ -865,6 +903,8 @@ static int soc_camera_remove(struct device *dev)
if (icd->ops->remove)
icd->ops->remove(icd);
+ soc_camera_free_user_formats(icd);
+
return 0;
}
@@ -918,6 +958,7 @@ int soc_camera_host_register(struct soc_camera_host *ici)
if (!ici || !ici->ops ||
!ici->ops->try_fmt ||
!ici->ops->set_fmt ||
+ !ici->ops->set_crop ||
!ici->ops->set_bus_param ||
!ici->ops->querycap ||
!ici->ops->init_videobuf ||
@@ -998,6 +1039,7 @@ int soc_camera_device_register(struct soc_camera_device *icd)
!icd->ops->release ||
!icd->ops->start_capture ||
!icd->ops->stop_capture ||
+ !icd->ops->set_crop ||
!icd->ops->set_fmt ||
!icd->ops->try_fmt ||
!icd->ops->query_bus_param ||
@@ -1021,7 +1063,6 @@ int soc_camera_device_register(struct soc_camera_device *icd)
icd->devnum = num;
icd->dev.bus = &soc_camera_bus_type;
- dev_set_name(&icd->dev, "%u-%u", icd->iface, icd->devnum);
icd->dev.release = dummy_release;
icd->use_count = 0;