Add an soc-camera host livecrop operation to implement live zoom. If
a host driver implements it, it should take care to preserve output
frame format, then live crop doesn't break streaming.

Signed-off-by: Guennadi Liakhovetski <g.liakhovet...@gmx.de>
---
 drivers/media/video/soc_camera.c |   20 ++++++++++++++------
 include/media/soc_camera.h       |    5 +++++
 2 files changed, 19 insertions(+), 6 deletions(-)

diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c
index 2f0fd2f..11f0f1e 100644
--- a/drivers/media/video/soc_camera.c
+++ b/drivers/media/video/soc_camera.c
@@ -41,6 +41,11 @@
 #define DEFAULT_WIDTH  640
 #define DEFAULT_HEIGHT 480
 
+#define is_streaming(ici, icd)                         \
+       (((ici)->ops->init_videobuf) ?                  \
+        (icd)->vb_vidq.streaming :                     \
+        vb2_is_streaming(&(icd)->vb2_vidq))
+
 static LIST_HEAD(hosts);
 static LIST_HEAD(devices);
 static DEFINE_MUTEX(list_lock);                /* Protects the list of hosts */
@@ -626,7 +631,7 @@ static int soc_camera_s_fmt_vid_cap(struct file *file, void 
*priv,
        if (icd->streamer && icd->streamer != file)
                return -EBUSY;
 
-       if (icd->vb_vidq.bufs[0]) {
+       if (is_streaming(to_soc_camera_host(icd->dev.parent), icd)) {
                dev_err(&icd->dev, "S_FMT denied: queue initialised\n");
                return -EBUSY;
        }
@@ -867,14 +872,17 @@ static int soc_camera_s_crop(struct file *file, void *fh,
        if (ret < 0) {
                dev_err(&icd->dev,
                        "S_CROP denied: getting current crop failed\n");
-       } else if (icd->vb_vidq.bufs[0] &&
-                  (a->c.width != current_crop.c.width ||
-                   a->c.height != current_crop.c.height)) {
+       } else if ((a->c.width == current_crop.c.width &&
+                   a->c.height == current_crop.c.height) ||
+                  !is_streaming(ici, icd)) {
+               /* same size or not streaming - use .set_crop() */
+               ret = ici->ops->set_crop(icd, a);
+       } else if (ici->ops->set_livecrop) {
+               ret = ici->ops->set_livecrop(icd, a);
+       } else {
                dev_err(&icd->dev,
                        "S_CROP denied: queue initialised and sizes differ\n");
                ret = -EBUSY;
-       } else {
-               ret = ici->ops->set_crop(icd, a);
        }
 
        return ret;
diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h
index f80b537..844cd09 100644
--- a/include/media/soc_camera.h
+++ b/include/media/soc_camera.h
@@ -80,6 +80,11 @@ struct soc_camera_host_ops {
        int (*cropcap)(struct soc_camera_device *, struct v4l2_cropcap *);
        int (*get_crop)(struct soc_camera_device *, struct v4l2_crop *);
        int (*set_crop)(struct soc_camera_device *, struct v4l2_crop *);
+       /*
+        * The difference to .set_crop() is, that .set_livecrop is not allowed
+        * to change the output sizes
+        */
+       int (*set_livecrop)(struct soc_camera_device *, struct v4l2_crop *);
        int (*set_fmt)(struct soc_camera_device *, struct v4l2_format *);
        int (*try_fmt)(struct soc_camera_device *, struct v4l2_format *);
        void (*init_videobuf)(struct videobuf_queue *,
-- 
1.7.2.5

--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to