]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/media/video/soc_camera.c
Merge tag 'v2.6.38' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[mv-sheeva.git] / drivers / media / video / soc_camera.c
index 052bd6dfa5a787033d445401ddb114bb40796732..a66811b43710a690c2a5d7ee77967ce40af1a6d4 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/mutex.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
 #include <linux/slab.h>
 #include <linux/pm_runtime.h>
 #include <linux/vmalloc.h>
@@ -43,6 +44,51 @@ static LIST_HEAD(hosts);
 static LIST_HEAD(devices);
 static DEFINE_MUTEX(list_lock);                /* Protects the list of hosts */
 
+static int soc_camera_power_set(struct soc_camera_device *icd,
+                               struct soc_camera_link *icl,
+                               int power_on)
+{
+       int ret;
+
+       if (power_on) {
+               ret = regulator_bulk_enable(icl->num_regulators,
+                                           icl->regulators);
+               if (ret < 0) {
+                       dev_err(&icd->dev, "Cannot enable regulators\n");
+                       return ret;
+               }
+
+               if (icl->power)
+                       ret = icl->power(icd->pdev, power_on);
+               if (ret < 0) {
+                       dev_err(&icd->dev,
+                               "Platform failed to power-on the camera.\n");
+
+                       regulator_bulk_disable(icl->num_regulators,
+                                              icl->regulators);
+                       return ret;
+               }
+       } else {
+               ret = 0;
+               if (icl->power)
+                       ret = icl->power(icd->pdev, 0);
+               if (ret < 0) {
+                       dev_err(&icd->dev,
+                               "Platform failed to power-off the camera.\n");
+                       return ret;
+               }
+
+               ret = regulator_bulk_disable(icl->num_regulators,
+                                            icl->regulators);
+               if (ret < 0) {
+                       dev_err(&icd->dev, "Cannot disable regulators\n");
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
 const struct soc_camera_format_xlate *soc_camera_xlate_by_fourcc(
        struct soc_camera_device *icd, unsigned int fourcc)
 {
@@ -352,12 +398,6 @@ static int soc_camera_open(struct file *file)
                return -EINVAL;
        }
 
-       /*
-        * Protect against icd->ops->remove() until we module_get() both
-        * drivers.
-        */
-       mutex_lock(&icd->video_lock);
-
        icd->use_count++;
 
        /* Now we really have to activate the camera */
@@ -375,11 +415,9 @@ static int soc_camera_open(struct file *file)
                        },
                };
 
-               if (icl->power) {
-                       ret = icl->power(icd->pdev, 1);
-                       if (ret < 0)
-                               goto epower;
-               }
+               ret = soc_camera_power_set(icd, icl, 1);
+               if (ret < 0)
+                       goto epower;
 
                /* The camera could have been already on, try to reset */
                if (icl->reset)
@@ -412,8 +450,6 @@ static int soc_camera_open(struct file *file)
        file->private_data = icd;
        dev_dbg(&icd->dev, "camera device open\n");
 
-       mutex_unlock(&icd->video_lock);
-
        return 0;
 
        /*
@@ -425,11 +461,9 @@ esfmt:
 eresume:
        ici->ops->remove(icd);
 eiciadd:
-       if (icl->power)
-               icl->power(icd->pdev, 0);
+       soc_camera_power_set(icd, icl, 0);
 epower:
        icd->use_count--;
-       mutex_unlock(&icd->video_lock);
        module_put(ici->ops->owner);
 
        return ret;
@@ -440,7 +474,6 @@ static int soc_camera_close(struct file *file)
        struct soc_camera_device *icd = file->private_data;
        struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
 
-       mutex_lock(&icd->video_lock);
        icd->use_count--;
        if (!icd->use_count) {
                struct soc_camera_link *icl = to_soc_camera_link(icd);
@@ -450,15 +483,12 @@ static int soc_camera_close(struct file *file)
 
                ici->ops->remove(icd);
 
-               if (icl->power)
-                       icl->power(icd->pdev, 0);
+               soc_camera_power_set(icd, icl, 0);
        }
 
        if (icd->streamer == file)
                icd->streamer = NULL;
 
-       mutex_unlock(&icd->video_lock);
-
        module_put(ici->ops->owner);
 
        dev_dbg(&icd->dev, "camera device close\n");
@@ -517,7 +547,7 @@ static struct v4l2_file_operations soc_camera_fops = {
        .owner          = THIS_MODULE,
        .open           = soc_camera_open,
        .release        = soc_camera_close,
-       .ioctl          = video_ioctl2,
+       .unlocked_ioctl = video_ioctl2,
        .read           = soc_camera_read,
        .mmap           = soc_camera_mmap,
        .poll           = soc_camera_poll,
@@ -534,12 +564,9 @@ static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv,
        if (icd->streamer && icd->streamer != file)
                return -EBUSY;
 
-       mutex_lock(&icd->vb_vidq.vb_lock);
-
        if (icd->vb_vidq.bufs[0]) {
                dev_err(&icd->dev, "S_FMT denied: queue initialised\n");
-               ret = -EBUSY;
-               goto unlock;
+               return -EBUSY;
        }
 
        ret = soc_camera_set_fmt(icd, f);
@@ -547,9 +574,6 @@ static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv,
        if (!ret && !icd->streamer)
                icd->streamer = file;
 
-unlock:
-       mutex_unlock(&icd->vb_vidq.vb_lock);
-
        return ret;
 }
 
@@ -622,15 +646,11 @@ static int soc_camera_streamon(struct file *file, void *priv,
        if (icd->streamer != file)
                return -EBUSY;
 
-       mutex_lock(&icd->video_lock);
-
        v4l2_subdev_call(sd, video, s_stream, 1);
 
        /* This calls buf_queue from host driver's videobuf_queue_ops */
        ret = videobuf_streamon(&icd->vb_vidq);
 
-       mutex_unlock(&icd->video_lock);
-
        return ret;
 }
 
@@ -648,8 +668,6 @@ static int soc_camera_streamoff(struct file *file, void *priv,
        if (icd->streamer != file)
                return -EBUSY;
 
-       mutex_lock(&icd->video_lock);
-
        /*
         * This calls buf_release from host driver's videobuf_queue_ops for all
         * remaining buffers. When the last buffer is freed, stop capture
@@ -658,8 +676,6 @@ static int soc_camera_streamoff(struct file *file, void *priv,
 
        v4l2_subdev_call(sd, video, s_stream, 0);
 
-       mutex_unlock(&icd->video_lock);
-
        return 0;
 }
 
@@ -748,9 +764,7 @@ static int soc_camera_g_crop(struct file *file, void *fh,
        struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
        int ret;
 
-       mutex_lock(&icd->vb_vidq.vb_lock);
        ret = ici->ops->get_crop(icd, a);
-       mutex_unlock(&icd->vb_vidq.vb_lock);
 
        return ret;
 }
@@ -775,9 +789,6 @@ static int soc_camera_s_crop(struct file *file, void *fh,
        dev_dbg(&icd->dev, "S_CROP(%ux%u@%u:%u)\n",
                rect->width, rect->height, rect->left, rect->top);
 
-       /* Cropping is allowed during a running capture, guard consistency */
-       mutex_lock(&icd->vb_vidq.vb_lock);
-
        /* If get_crop fails, we'll let host and / or client drivers decide */
        ret = ici->ops->get_crop(icd, &current_crop);
 
@@ -795,8 +806,6 @@ static int soc_camera_s_crop(struct file *file, void *fh,
                ret = ici->ops->set_crop(icd, a);
        }
 
-       mutex_unlock(&icd->vb_vidq.vb_lock);
-
        return ret;
 }
 
@@ -941,14 +950,14 @@ static int soc_camera_probe(struct device *dev)
 
        dev_info(dev, "Probing %s\n", dev_name(dev));
 
-       if (icl->power) {
-               ret = icl->power(icd->pdev, 1);
-               if (ret < 0) {
-                       dev_err(dev,
-                               "Platform failed to power-on the camera.\n");
-                       goto epower;
-               }
-       }
+       ret = regulator_bulk_get(icd->pdev, icl->num_regulators,
+                                icl->regulators);
+       if (ret < 0)
+               goto ereg;
+
+       ret = soc_camera_power_set(icd, icl, 1);
+       if (ret < 0)
+               goto epower;
 
        /* The camera could have been already on, try to reset */
        if (icl->reset)
@@ -998,7 +1007,13 @@ static int soc_camera_probe(struct device *dev)
 
        icd->field = V4L2_FIELD_ANY;
 
-       /* ..._video_start() will create a device node, so we have to protect */
+       icd->vdev->lock = &icd->video_lock;
+
+       /*
+        * ..._video_start() will create a device node, video_register_device()
+        * itself is protected against concurrent open() calls, but we also have
+        * to protect our data.
+        */
        mutex_lock(&icd->video_lock);
 
        ret = soc_camera_video_start(icd);
@@ -1021,8 +1036,7 @@ static int soc_camera_probe(struct device *dev)
 
        ici->ops->remove(icd);
 
-       if (icl->power)
-               icl->power(icd->pdev, 0);
+       soc_camera_power_set(icd, icl, 0);
 
        mutex_unlock(&icd->video_lock);
 
@@ -1044,9 +1058,10 @@ eadddev:
 evdc:
        ici->ops->remove(icd);
 eadd:
-       if (icl->power)
-               icl->power(icd->pdev, 0);
+       soc_camera_power_set(icd, icl, 0);
 epower:
+       regulator_bulk_free(icl->num_regulators, icl->regulators);
+ereg:
        return ret;
 }
 
@@ -1063,10 +1078,8 @@ static int soc_camera_remove(struct device *dev)
        BUG_ON(!dev->parent);
 
        if (vdev) {
-               mutex_lock(&icd->video_lock);
                video_unregister_device(vdev);
                icd->vdev = NULL;
-               mutex_unlock(&icd->video_lock);
        }
 
        if (icl->board_info) {
@@ -1081,6 +1094,8 @@ static int soc_camera_remove(struct device *dev)
        }
        soc_camera_free_user_formats(icd);
 
+       regulator_bulk_free(icl->num_regulators, icl->regulators);
+
        return 0;
 }