From 9ce484a50a4f556a4803f0a7d7061ce50c079c5e Mon Sep 17 00:00:00 2001
From: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
Date: Wed, 3 Jun 2009 11:48:49 +0200
Subject: [PATCH 19/33] soc-camera: put pixel format initialisation back in probe, add .put_formats()

The move of format translation initialisation into soc_camera_open() was
temporary for the soc-camera as platform driver intermediate step, put it back
into soc_camera_probe(). Also add a .put_formats() method to
soc_camera_host_ops to free any resources host driver might have allocated in
.get_formats().

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
 drivers/media/video/soc_camera.c |   50 ++++++++++++++++++++++++++-----------
 include/media/soc_camera.h       |    7 +++++
 2 files changed, 42 insertions(+), 15 deletions(-)

diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c
index 7d20175..83868a5 100644
--- a/drivers/media/video/soc_camera.c
+++ b/drivers/media/video/soc_camera.c
@@ -211,7 +211,7 @@ static int soc_camera_dqbuf(struct file *file, void *priv,
 static int soc_camera_init_user_formats(struct soc_camera_device *icd)
 {
 	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
-	int i, fmts = 0;
+	int i, fmts = 0, ret;
 
 	if (!ici->ops->get_formats)
 		/*
@@ -224,8 +224,12 @@ static int soc_camera_init_user_formats(struct soc_camera_device *icd)
 		 * First pass - only count formats this host-sensor
 		 * configuration can provide
 		 */
-		for (i = 0; i < icd->num_formats; i++)
-			fmts += ici->ops->get_formats(icd, i, NULL);
+		for (i = 0; i < icd->num_formats; i++) {
+			ret = ici->ops->get_formats(icd, i, NULL);
+			if (ret < 0)
+				return ret;
+			fmts += ret;
+		}
 
 	if (!fmts)
 		return -ENXIO;
@@ -247,19 +251,32 @@ static int soc_camera_init_user_formats(struct soc_camera_device *icd)
 			icd->user_formats[i].cam_fmt = icd->formats + i;
 			icd->user_formats[i].buswidth = icd->formats[i].depth;
 		} else {
-			fmts += ici->ops->get_formats(icd, i,
-						      &icd->user_formats[fmts]);
+			ret = ici->ops->get_formats(icd, i,
+						    &icd->user_formats[fmts]);
+			if (ret < 0)
+				goto egfmt;
+			fmts += ret;
 		}
 
 	icd->current_fmt = icd->user_formats[0].host_fmt;
 
 	return 0;
+
+egfmt:
+	icd->num_user_formats = 0;
+	vfree(icd->user_formats);
+	return ret;
 }
 
 /* Always entered with .video_lock held */
 static void soc_camera_free_user_formats(struct soc_camera_device *icd)
 {
+	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+
+	if (ici->ops->put_formats)
+		ici->ops->put_formats(icd);
 	icd->current_fmt = NULL;
+	icd->num_user_formats = 0;
 	vfree(icd->user_formats);
 	icd->user_formats = NULL;
 }
@@ -344,16 +361,11 @@ static int soc_camera_open(struct file *file)
 				.width		= icd->rect_current.width,
 				.height		= icd->rect_current.height,
 				.field		= icd->field,
+				.pixelformat	= icd->current_fmt->fourcc,
+				.colorspace	= icd->current_fmt->colorspace,
 			},
 		};
 
-		ret = soc_camera_init_user_formats(icd);
-		if (ret < 0)
-			goto eiufmt;
-
-		f.fmt.pix.pixelformat	= icd->current_fmt->fourcc;
-		f.fmt.pix.colorspace	= icd->current_fmt->colorspace;
-
 		if (icl->power) {
 			ret = icl->power(icd->pdev, 1);
 			if (ret < 0)
@@ -404,8 +416,6 @@ eiciadd:
 	if (icl->power)
 		icl->power(icd->pdev, 0);
 epower:
-	soc_camera_free_user_formats(icd);
-eiufmt:
 	icd->use_count--;
 	mutex_unlock(&icd->video_lock);
 	module_put(ici->ops->owner);
@@ -431,7 +441,6 @@ static int soc_camera_close(struct file *file)
 		ici->ops->remove(icd);
 		if (icl->power)
 			icl->power(icd->pdev, 0);
-		soc_camera_free_user_formats(icd);
 	}
 
 	mutex_unlock(&icd->video_lock);
@@ -954,6 +963,14 @@ static int soc_camera_probe(struct device *dev)
 		}
 	}
 
+	/* At this point client .probe() should have run already */
+	ret = soc_camera_init_user_formats(icd);
+	if (ret < 0)
+		goto eiufmt;
+
+	icd->rect_current = icd->rect_max;
+	icd->field = V4L2_FIELD_ANY;
+
 	/* ..._video_start() will create a device node, so we have to protect */
 	mutex_lock(&icd->video_lock);
 
@@ -978,6 +995,8 @@ static int soc_camera_probe(struct device *dev)
 
 evidstart:
 	mutex_unlock(&icd->video_lock);
+	soc_camera_free_user_formats(icd);
+eiufmt:
 	if (icl->board_info) {
 		soc_camera_free_i2c(icd);
 	} else {
@@ -1023,6 +1042,7 @@ static int soc_camera_remove(struct device *dev)
 			module_put(drv->owner);
 		}
 	}
+	soc_camera_free_user_formats(icd);
 
 	return 0;
 }
diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h
index f623c01..2b7a8c6 100644
--- a/include/media/soc_camera.h
+++ b/include/media/soc_camera.h
@@ -67,8 +67,15 @@ struct soc_camera_host_ops {
 	void (*remove)(struct soc_camera_device *);
 	int (*suspend)(struct soc_camera_device *, pm_message_t);
 	int (*resume)(struct soc_camera_device *);
+	/*
+	 * .get_formats() is called for each client device format, but
+	 * .put_formats() is only called once. Further, if any of the calls to
+	 * .get_formats() fail, .put_formats() will not be called at all, the
+	 * failing .get_formats() must then clean up internally.
+	 */
 	int (*get_formats)(struct soc_camera_device *, int,
 			   struct soc_camera_format_xlate *);
+	void (*put_formats)(struct soc_camera_device *);
 	int (*set_crop)(struct soc_camera_device *, struct v4l2_rect *);
 	int (*set_fmt)(struct soc_camera_device *, struct v4l2_format *);
 	int (*try_fmt)(struct soc_camera_device *, struct v4l2_format *);
-- 
1.6.2.4

