]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/media/video/soc_camera.c
V4L/DVB (11408): gspca - m5602-s5k83a: Add led support to the s5k83a sensor.
[linux-2.6-omap-h63xx.git] / drivers / media / video / soc_camera.c
index fcb05f06de8ffb7d73e564820110ca78100a7a07..0e890cc233776d85c3cba061299761e9402e7047 100644 (file)
 #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;