All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v10 00/21] V4L2 clock and asynchronous probing
@ 2013-06-11  8:23 ` Guennadi Liakhovetski
  0 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-11  8:23 UTC (permalink / raw)
  To: linux-media
  Cc: Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer,
	Guennadi Liakhovetski

3.11 is approaching... Here comes v10 of the V4L2 clock helper and 
asynchronous probing patch set. In this series I included 3 groups of 
patches, that actually aren't that strongly related.

1. patches 1-11 split soc-camera subdevice linking procedure into two 
steps: (a) turning on the master clock and (b) actually linking a (client) 
subdevice to a host. This is necessary to keep the V4L2 clock API free 
from any device context.
2. patches 12-13 are adding device tree support to the 
sh_mobile_ceu_camera soc-camera host driver. They aren't really related, 
but following V4L2 async support for this driver has been developed on top 
of this code, so, I would prefer to keep the order.
3. patches 14-21 are the actual clock and async probing code. They contain 
core support and an example conversion of the soc-camera framework and one 
its platform (an AP4EVB board) to the new APIs.

Thanks for all the reviews and suggestions.

Guennadi Liakhovetski (21):
  soc-camera: move common code to soc_camera.c
  soc-camera: add host clock callbacks to start and stop the master
    clock
  pxa-camera: move interface activation and deactivation to clock
    callbacks
  omap1-camera: move interface activation and deactivation to clock
    callbacks
  atmel-isi: move interface activation and deactivation to clock
    callbacks
  mx3-camera: move interface activation and deactivation to clock
    callbacks
  mx2-camera: move interface activation and deactivation to clock
    callbacks
  mx1-camera: move interface activation and deactivation to clock
    callbacks
  sh-mobile-ceu-camera: move interface activation and deactivation to
    clock callbacks
  soc-camera: make .clock_{start,stop} compulsory, .add / .remove
    optional
  soc-camera: don't attach the client to the host during probing
  sh-mobile-ceu-camera: add primitive OF support
  sh-mobile-ceu-driver: support max width and height in DT
  V4L2: add temporary clock helpers
  V4L2: add a device pointer to struct v4l2_subdev
  V4L2: support asynchronous subdevice registration
  soc-camera: switch I2C subdevice drivers to use v4l2-clk
  soc-camera: add V4L2-async support
  sh_mobile_ceu_camera: add asynchronous subdevice probing support
  imx074: support asynchronous probing
  ARM: shmobile: convert ap4evb to asynchronously register camera
    subdevices

 .../devicetree/bindings/media/sh_mobile_ceu.txt    |   18 +
 arch/arm/mach-shmobile/board-ap4evb.c              |  103 ++--
 arch/arm/mach-shmobile/clock-sh7372.c              |    1 +
 drivers/media/i2c/soc_camera/imx074.c              |   32 +-
 drivers/media/i2c/soc_camera/mt9m001.c             |   17 +-
 drivers/media/i2c/soc_camera/mt9m111.c             |   20 +-
 drivers/media/i2c/soc_camera/mt9t031.c             |   19 +-
 drivers/media/i2c/soc_camera/mt9t112.c             |   25 +-
 drivers/media/i2c/soc_camera/mt9v022.c             |   17 +-
 drivers/media/i2c/soc_camera/ov2640.c              |   19 +-
 drivers/media/i2c/soc_camera/ov5642.c              |   20 +-
 drivers/media/i2c/soc_camera/ov6650.c              |   17 +-
 drivers/media/i2c/soc_camera/ov772x.c              |   15 +-
 drivers/media/i2c/soc_camera/ov9640.c              |   17 +-
 drivers/media/i2c/soc_camera/ov9640.h              |    1 +
 drivers/media/i2c/soc_camera/ov9740.c              |   18 +-
 drivers/media/i2c/soc_camera/rj54n1cb0c.c          |   17 +-
 drivers/media/i2c/soc_camera/tw9910.c              |   24 +-
 drivers/media/platform/soc_camera/atmel-isi.c      |   38 +-
 drivers/media/platform/soc_camera/mx1_camera.c     |   48 +-
 drivers/media/platform/soc_camera/mx2_camera.c     |   41 +-
 drivers/media/platform/soc_camera/mx3_camera.c     |   44 +-
 drivers/media/platform/soc_camera/omap1_camera.c   |   41 +-
 drivers/media/platform/soc_camera/pxa_camera.c     |   46 +-
 .../platform/soc_camera/sh_mobile_ceu_camera.c     |  243 +++++--
 drivers/media/platform/soc_camera/sh_mobile_csi2.c |  153 +++--
 drivers/media/platform/soc_camera/soc_camera.c     |  707 +++++++++++++++++---
 .../platform/soc_camera/soc_camera_platform.c      |    2 +-
 drivers/media/v4l2-core/Makefile                   |    3 +-
 drivers/media/v4l2-core/v4l2-async.c               |  280 ++++++++
 drivers/media/v4l2-core/v4l2-clk.c                 |  242 +++++++
 drivers/media/v4l2-core/v4l2-common.c              |    2 +
 include/media/sh_mobile_ceu.h                      |    2 +
 include/media/sh_mobile_csi2.h                     |    2 +-
 include/media/soc_camera.h                         |   39 +-
 include/media/v4l2-async.h                         |  105 +++
 include/media/v4l2-clk.h                           |   54 ++
 include/media/v4l2-subdev.h                        |   10 +
 38 files changed, 2035 insertions(+), 467 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/media/sh_mobile_ceu.txt
 create mode 100644 drivers/media/v4l2-core/v4l2-async.c
 create mode 100644 drivers/media/v4l2-core/v4l2-clk.c
 create mode 100644 include/media/v4l2-async.h
 create mode 100644 include/media/v4l2-clk.h

-- 
1.7.2.5

Thanks
Guennadi
---
Guennadi Liakhovetski, Ph.D.
Freelance Open-Source Software Developer
http://www.open-technology.de/

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v10 00/21] V4L2 clock and asynchronous probing
@ 2013-06-11  8:23 ` Guennadi Liakhovetski
  0 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-11  8:23 UTC (permalink / raw)
  To: linux-media
  Cc: Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer,
	Guennadi Liakhovetski

3.11 is approaching... Here comes v10 of the V4L2 clock helper and 
asynchronous probing patch set. In this series I included 3 groups of 
patches, that actually aren't that strongly related.

1. patches 1-11 split soc-camera subdevice linking procedure into two 
steps: (a) turning on the master clock and (b) actually linking a (client) 
subdevice to a host. This is necessary to keep the V4L2 clock API free 
from any device context.
2. patches 12-13 are adding device tree support to the 
sh_mobile_ceu_camera soc-camera host driver. They aren't really related, 
but following V4L2 async support for this driver has been developed on top 
of this code, so, I would prefer to keep the order.
3. patches 14-21 are the actual clock and async probing code. They contain 
core support and an example conversion of the soc-camera framework and one 
its platform (an AP4EVB board) to the new APIs.

Thanks for all the reviews and suggestions.

Guennadi Liakhovetski (21):
  soc-camera: move common code to soc_camera.c
  soc-camera: add host clock callbacks to start and stop the master
    clock
  pxa-camera: move interface activation and deactivation to clock
    callbacks
  omap1-camera: move interface activation and deactivation to clock
    callbacks
  atmel-isi: move interface activation and deactivation to clock
    callbacks
  mx3-camera: move interface activation and deactivation to clock
    callbacks
  mx2-camera: move interface activation and deactivation to clock
    callbacks
  mx1-camera: move interface activation and deactivation to clock
    callbacks
  sh-mobile-ceu-camera: move interface activation and deactivation to
    clock callbacks
  soc-camera: make .clock_{start,stop} compulsory, .add / .remove
    optional
  soc-camera: don't attach the client to the host during probing
  sh-mobile-ceu-camera: add primitive OF support
  sh-mobile-ceu-driver: support max width and height in DT
  V4L2: add temporary clock helpers
  V4L2: add a device pointer to struct v4l2_subdev
  V4L2: support asynchronous subdevice registration
  soc-camera: switch I2C subdevice drivers to use v4l2-clk
  soc-camera: add V4L2-async support
  sh_mobile_ceu_camera: add asynchronous subdevice probing support
  imx074: support asynchronous probing
  ARM: shmobile: convert ap4evb to asynchronously register camera
    subdevices

 .../devicetree/bindings/media/sh_mobile_ceu.txt    |   18 +
 arch/arm/mach-shmobile/board-ap4evb.c              |  103 ++--
 arch/arm/mach-shmobile/clock-sh7372.c              |    1 +
 drivers/media/i2c/soc_camera/imx074.c              |   32 +-
 drivers/media/i2c/soc_camera/mt9m001.c             |   17 +-
 drivers/media/i2c/soc_camera/mt9m111.c             |   20 +-
 drivers/media/i2c/soc_camera/mt9t031.c             |   19 +-
 drivers/media/i2c/soc_camera/mt9t112.c             |   25 +-
 drivers/media/i2c/soc_camera/mt9v022.c             |   17 +-
 drivers/media/i2c/soc_camera/ov2640.c              |   19 +-
 drivers/media/i2c/soc_camera/ov5642.c              |   20 +-
 drivers/media/i2c/soc_camera/ov6650.c              |   17 +-
 drivers/media/i2c/soc_camera/ov772x.c              |   15 +-
 drivers/media/i2c/soc_camera/ov9640.c              |   17 +-
 drivers/media/i2c/soc_camera/ov9640.h              |    1 +
 drivers/media/i2c/soc_camera/ov9740.c              |   18 +-
 drivers/media/i2c/soc_camera/rj54n1cb0c.c          |   17 +-
 drivers/media/i2c/soc_camera/tw9910.c              |   24 +-
 drivers/media/platform/soc_camera/atmel-isi.c      |   38 +-
 drivers/media/platform/soc_camera/mx1_camera.c     |   48 +-
 drivers/media/platform/soc_camera/mx2_camera.c     |   41 +-
 drivers/media/platform/soc_camera/mx3_camera.c     |   44 +-
 drivers/media/platform/soc_camera/omap1_camera.c   |   41 +-
 drivers/media/platform/soc_camera/pxa_camera.c     |   46 +-
 .../platform/soc_camera/sh_mobile_ceu_camera.c     |  243 +++++--
 drivers/media/platform/soc_camera/sh_mobile_csi2.c |  153 +++--
 drivers/media/platform/soc_camera/soc_camera.c     |  707 +++++++++++++++++---
 .../platform/soc_camera/soc_camera_platform.c      |    2 +-
 drivers/media/v4l2-core/Makefile                   |    3 +-
 drivers/media/v4l2-core/v4l2-async.c               |  280 ++++++++
 drivers/media/v4l2-core/v4l2-clk.c                 |  242 +++++++
 drivers/media/v4l2-core/v4l2-common.c              |    2 +
 include/media/sh_mobile_ceu.h                      |    2 +
 include/media/sh_mobile_csi2.h                     |    2 +-
 include/media/soc_camera.h                         |   39 +-
 include/media/v4l2-async.h                         |  105 +++
 include/media/v4l2-clk.h                           |   54 ++
 include/media/v4l2-subdev.h                        |   10 +
 38 files changed, 2035 insertions(+), 467 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/media/sh_mobile_ceu.txt
 create mode 100644 drivers/media/v4l2-core/v4l2-async.c
 create mode 100644 drivers/media/v4l2-core/v4l2-clk.c
 create mode 100644 include/media/v4l2-async.h
 create mode 100644 include/media/v4l2-clk.h

-- 
1.7.2.5

Thanks
Guennadi
---
Guennadi Liakhovetski, Ph.D.
Freelance Open-Source Software Developer
http://www.open-technology.de/

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v10 01/21] soc-camera: move common code to soc_camera.c
  2013-06-11  8:23 ` Guennadi Liakhovetski
@ 2013-06-11  8:23   ` Guennadi Liakhovetski
  -1 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-11  8:23 UTC (permalink / raw)
  To: linux-media
  Cc: Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer,
	Guennadi Liakhovetski

All soc-camera host drivers include a pointer to an soc-camera device in
their host private struct to check, that only one client is connected.
Move this common code to soc_camera.c.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
 drivers/media/platform/soc_camera/atmel-isi.c      |   10 +-----
 drivers/media/platform/soc_camera/mx1_camera.c     |   20 +++--------
 drivers/media/platform/soc_camera/mx2_camera.c     |   13 +------
 drivers/media/platform/soc_camera/mx3_camera.c     |    9 -----
 drivers/media/platform/soc_camera/omap1_camera.c   |   14 +------
 drivers/media/platform/soc_camera/pxa_camera.c     |   18 ++-------
 .../platform/soc_camera/sh_mobile_ceu_camera.c     |   13 +------
 drivers/media/platform/soc_camera/soc_camera.c     |   38 ++++++++++++++++---
 include/media/soc_camera.h                         |    1 +
 9 files changed, 49 insertions(+), 87 deletions(-)

diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c
index 1abbb36..c9e080a 100644
--- a/drivers/media/platform/soc_camera/atmel-isi.c
+++ b/drivers/media/platform/soc_camera/atmel-isi.c
@@ -102,7 +102,6 @@ struct atmel_isi {
 	struct list_head		video_buffer_list;
 	struct frame_buffer		*active;
 
-	struct soc_camera_device	*icd;
 	struct soc_camera_host		soc_host;
 };
 
@@ -367,7 +366,7 @@ static void start_dma(struct atmel_isi *isi, struct frame_buffer *buffer)
 
 	/* Check if already in a frame */
 	if (isi_readl(isi, ISI_STATUS) & ISI_CTRL_CDC) {
-		dev_err(isi->icd->parent, "Already in frame handling.\n");
+		dev_err(isi->soc_host.icd->parent, "Already in frame handling.\n");
 		return;
 	}
 
@@ -753,9 +752,6 @@ static int isi_camera_add_device(struct soc_camera_device *icd)
 	struct atmel_isi *isi = ici->priv;
 	int ret;
 
-	if (isi->icd)
-		return -EBUSY;
-
 	ret = clk_enable(isi->pclk);
 	if (ret)
 		return ret;
@@ -766,7 +762,6 @@ static int isi_camera_add_device(struct soc_camera_device *icd)
 		return ret;
 	}
 
-	isi->icd = icd;
 	dev_dbg(icd->parent, "Atmel ISI Camera driver attached to camera %d\n",
 		 icd->devnum);
 	return 0;
@@ -777,11 +772,8 @@ static void isi_camera_remove_device(struct soc_camera_device *icd)
 	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct atmel_isi *isi = ici->priv;
 
-	BUG_ON(icd != isi->icd);
-
 	clk_disable(isi->mck);
 	clk_disable(isi->pclk);
-	isi->icd = NULL;
 
 	dev_dbg(icd->parent, "Atmel ISI Camera driver detached from camera %d\n",
 		 icd->devnum);
diff --git a/drivers/media/platform/soc_camera/mx1_camera.c b/drivers/media/platform/soc_camera/mx1_camera.c
index a3fd8d6..5f9ec8e 100644
--- a/drivers/media/platform/soc_camera/mx1_camera.c
+++ b/drivers/media/platform/soc_camera/mx1_camera.c
@@ -104,7 +104,6 @@ struct mx1_buffer {
  */
 struct mx1_camera_dev {
 	struct soc_camera_host		soc_host;
-	struct soc_camera_device	*icd;
 	struct mx1_camera_pdata		*pdata;
 	struct mx1_buffer		*active;
 	struct resource			*res;
@@ -220,7 +219,7 @@ out:
 static int mx1_camera_setup_dma(struct mx1_camera_dev *pcdev)
 {
 	struct videobuf_buffer *vbuf = &pcdev->active->vb;
-	struct device *dev = pcdev->icd->parent;
+	struct device *dev = pcdev->soc_host.icd->parent;
 	int ret;
 
 	if (unlikely(!pcdev->active)) {
@@ -331,7 +330,7 @@ static void mx1_camera_wakeup(struct mx1_camera_dev *pcdev,
 static void mx1_camera_dma_irq(int channel, void *data)
 {
 	struct mx1_camera_dev *pcdev = data;
-	struct device *dev = pcdev->icd->parent;
+	struct device *dev = pcdev->soc_host.icd->parent;
 	struct mx1_buffer *buf;
 	struct videobuf_buffer *vb;
 	unsigned long flags;
@@ -389,7 +388,7 @@ static int mclk_get_divisor(struct mx1_camera_dev *pcdev)
 	 */
 	div = (lcdclk + 2 * mclk - 1) / (2 * mclk) - 1;
 
-	dev_dbg(pcdev->icd->parent,
+	dev_dbg(pcdev->soc_host.icd->parent,
 		"System clock %lukHz, target freq %dkHz, divisor %lu\n",
 		lcdclk / 1000, mclk / 1000, div);
 
@@ -400,7 +399,7 @@ static void mx1_camera_activate(struct mx1_camera_dev *pcdev)
 {
 	unsigned int csicr1 = CSICR1_EN;
 
-	dev_dbg(pcdev->icd->parent, "Activate device\n");
+	dev_dbg(pcdev->soc_host.icd->parent, "Activate device\n");
 
 	clk_prepare_enable(pcdev->clk);
 
@@ -416,7 +415,7 @@ static void mx1_camera_activate(struct mx1_camera_dev *pcdev)
 
 static void mx1_camera_deactivate(struct mx1_camera_dev *pcdev)
 {
-	dev_dbg(pcdev->icd->parent, "Deactivate device\n");
+	dev_dbg(pcdev->soc_host.icd->parent, "Deactivate device\n");
 
 	/* Disable all CSI interface */
 	__raw_writel(0x00, pcdev->base + CSICR1);
@@ -433,16 +432,11 @@ static int mx1_camera_add_device(struct soc_camera_device *icd)
 	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct mx1_camera_dev *pcdev = ici->priv;
 
-	if (pcdev->icd)
-		return -EBUSY;
-
 	dev_info(icd->parent, "MX1 Camera driver attached to camera %d\n",
 		 icd->devnum);
 
 	mx1_camera_activate(pcdev);
 
-	pcdev->icd = icd;
-
 	return 0;
 }
 
@@ -452,8 +446,6 @@ static void mx1_camera_remove_device(struct soc_camera_device *icd)
 	struct mx1_camera_dev *pcdev = ici->priv;
 	unsigned int csicr1;
 
-	BUG_ON(icd != pcdev->icd);
-
 	/* disable interrupts */
 	csicr1 = __raw_readl(pcdev->base + CSICR1) & ~CSI_IRQ_MASK;
 	__raw_writel(csicr1, pcdev->base + CSICR1);
@@ -465,8 +457,6 @@ static void mx1_camera_remove_device(struct soc_camera_device *icd)
 		 icd->devnum);
 
 	mx1_camera_deactivate(pcdev);
-
-	pcdev->icd = NULL;
 }
 
 static int mx1_camera_set_bus_param(struct soc_camera_device *icd)
diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c
index 5bbeb43..772e071 100644
--- a/drivers/media/platform/soc_camera/mx2_camera.c
+++ b/drivers/media/platform/soc_camera/mx2_camera.c
@@ -236,7 +236,6 @@ enum mx2_camera_type {
 struct mx2_camera_dev {
 	struct device		*dev;
 	struct soc_camera_host	soc_host;
-	struct soc_camera_device *icd;
 	struct clk		*clk_emma_ahb, *clk_emma_ipg;
 	struct clk		*clk_csi_ahb, *clk_csi_per;
 
@@ -394,8 +393,8 @@ static void mx27_update_emma_buf(struct mx2_camera_dev *pcdev,
 		writel(phys, pcdev->base_emma +
 			PRP_DEST_Y_PTR - 0x14 * bufnum);
 		if (prp->out_fmt = V4L2_PIX_FMT_YUV420) {
-			u32 imgsize = pcdev->icd->user_height *
-					pcdev->icd->user_width;
+			u32 imgsize = pcdev->soc_host.icd->user_height *
+					pcdev->soc_host.icd->user_width;
 
 			writel(phys + imgsize, pcdev->base_emma +
 				PRP_DEST_CB_PTR - 0x14 * bufnum);
@@ -424,9 +423,6 @@ static int mx2_camera_add_device(struct soc_camera_device *icd)
 	int ret;
 	u32 csicr1;
 
-	if (pcdev->icd)
-		return -EBUSY;
-
 	ret = clk_prepare_enable(pcdev->clk_csi_ahb);
 	if (ret < 0)
 		return ret;
@@ -441,7 +437,6 @@ static int mx2_camera_add_device(struct soc_camera_device *icd)
 	pcdev->csicr1 = csicr1;
 	writel(pcdev->csicr1, pcdev->base_csi + CSICR1);
 
-	pcdev->icd = icd;
 	pcdev->frame_count = 0;
 
 	dev_info(icd->parent, "Camera driver attached to camera %d\n",
@@ -460,14 +455,10 @@ static void mx2_camera_remove_device(struct soc_camera_device *icd)
 	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct mx2_camera_dev *pcdev = ici->priv;
 
-	BUG_ON(icd != pcdev->icd);
-
 	dev_info(icd->parent, "Camera driver detached from camera %d\n",
 		 icd->devnum);
 
 	mx2_camera_deactivate(pcdev);
-
-	pcdev->icd = NULL;
 }
 
 /*
diff --git a/drivers/media/platform/soc_camera/mx3_camera.c b/drivers/media/platform/soc_camera/mx3_camera.c
index 5da3377..71b9b19 100644
--- a/drivers/media/platform/soc_camera/mx3_camera.c
+++ b/drivers/media/platform/soc_camera/mx3_camera.c
@@ -94,7 +94,6 @@ struct mx3_camera_dev {
 	 * Interface. If anyone ever builds hardware to enable more than one
 	 * camera _simultaneously_, they will have to modify this driver too
 	 */
-	struct soc_camera_device *icd;
 	struct clk		*clk;
 
 	void __iomem		*base;
@@ -517,13 +516,9 @@ static int mx3_camera_add_device(struct soc_camera_device *icd)
 	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct mx3_camera_dev *mx3_cam = ici->priv;
 
-	if (mx3_cam->icd)
-		return -EBUSY;
-
 	mx3_camera_activate(mx3_cam, icd);
 
 	mx3_cam->buf_total = 0;
-	mx3_cam->icd = icd;
 
 	dev_info(icd->parent, "MX3 Camera driver attached to camera %d\n",
 		 icd->devnum);
@@ -538,8 +533,6 @@ static void mx3_camera_remove_device(struct soc_camera_device *icd)
 	struct mx3_camera_dev *mx3_cam = ici->priv;
 	struct idmac_channel **ichan = &mx3_cam->idmac_channel[0];
 
-	BUG_ON(icd != mx3_cam->icd);
-
 	if (*ichan) {
 		dma_release_channel(&(*ichan)->dma_chan);
 		*ichan = NULL;
@@ -547,8 +540,6 @@ static void mx3_camera_remove_device(struct soc_camera_device *icd)
 
 	clk_disable_unprepare(mx3_cam->clk);
 
-	mx3_cam->icd = NULL;
-
 	dev_info(icd->parent, "MX3 Camera driver detached from camera %d\n",
 		 icd->devnum);
 }
diff --git a/drivers/media/platform/soc_camera/omap1_camera.c b/drivers/media/platform/soc_camera/omap1_camera.c
index 9689a6e..c42c23e 100644
--- a/drivers/media/platform/soc_camera/omap1_camera.c
+++ b/drivers/media/platform/soc_camera/omap1_camera.c
@@ -150,7 +150,6 @@ struct omap1_cam_buf {
 
 struct omap1_cam_dev {
 	struct soc_camera_host		soc_host;
-	struct soc_camera_device	*icd;
 	struct clk			*clk;
 
 	unsigned int			irq;
@@ -564,7 +563,7 @@ static void videobuf_done(struct omap1_cam_dev *pcdev,
 {
 	struct omap1_cam_buf *buf = pcdev->active;
 	struct videobuf_buffer *vb;
-	struct device *dev = pcdev->icd->parent;
+	struct device *dev = pcdev->soc_host.icd->parent;
 
 	if (WARN_ON(!buf)) {
 		suspend_capture(pcdev);
@@ -790,7 +789,7 @@ out:
 static irqreturn_t cam_isr(int irq, void *data)
 {
 	struct omap1_cam_dev *pcdev = data;
-	struct device *dev = pcdev->icd->parent;
+	struct device *dev = pcdev->soc_host.icd->parent;
 	struct omap1_cam_buf *buf = pcdev->active;
 	u32 it_status;
 	unsigned long flags;
@@ -904,9 +903,6 @@ static int omap1_cam_add_device(struct soc_camera_device *icd)
 	struct omap1_cam_dev *pcdev = ici->priv;
 	u32 ctrlclock;
 
-	if (pcdev->icd)
-		return -EBUSY;
-
 	clk_enable(pcdev->clk);
 
 	/* setup sensor clock */
@@ -941,8 +937,6 @@ static int omap1_cam_add_device(struct soc_camera_device *icd)
 
 	sensor_reset(pcdev, false);
 
-	pcdev->icd = icd;
-
 	dev_dbg(icd->parent, "OMAP1 Camera driver attached to camera %d\n",
 			icd->devnum);
 	return 0;
@@ -954,8 +948,6 @@ static void omap1_cam_remove_device(struct soc_camera_device *icd)
 	struct omap1_cam_dev *pcdev = ici->priv;
 	u32 ctrlclock;
 
-	BUG_ON(icd != pcdev->icd);
-
 	suspend_capture(pcdev);
 	disable_capture(pcdev);
 
@@ -974,8 +966,6 @@ static void omap1_cam_remove_device(struct soc_camera_device *icd)
 
 	clk_disable(pcdev->clk);
 
-	pcdev->icd = NULL;
-
 	dev_dbg(icd->parent,
 		"OMAP1 Camera driver detached from camera %d\n", icd->devnum);
 }
diff --git a/drivers/media/platform/soc_camera/pxa_camera.c b/drivers/media/platform/soc_camera/pxa_camera.c
index d665242..686edf7 100644
--- a/drivers/media/platform/soc_camera/pxa_camera.c
+++ b/drivers/media/platform/soc_camera/pxa_camera.c
@@ -200,7 +200,6 @@ struct pxa_camera_dev {
 	 * interface. If anyone ever builds hardware to enable more than
 	 * one camera, they will have to modify this driver too
 	 */
-	struct soc_camera_device *icd;
 	struct clk		*clk;
 
 	unsigned int		irq;
@@ -966,13 +965,8 @@ static int pxa_camera_add_device(struct soc_camera_device *icd)
 	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct pxa_camera_dev *pcdev = ici->priv;
 
-	if (pcdev->icd)
-		return -EBUSY;
-
 	pxa_camera_activate(pcdev);
 
-	pcdev->icd = icd;
-
 	dev_info(icd->parent, "PXA Camera driver attached to camera %d\n",
 		 icd->devnum);
 
@@ -985,8 +979,6 @@ static void pxa_camera_remove_device(struct soc_camera_device *icd)
 	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct pxa_camera_dev *pcdev = ici->priv;
 
-	BUG_ON(icd != pcdev->icd);
-
 	dev_info(icd->parent, "PXA Camera driver detached from camera %d\n",
 		 icd->devnum);
 
@@ -999,8 +991,6 @@ static void pxa_camera_remove_device(struct soc_camera_device *icd)
 	DCSR(pcdev->dma_chans[2]) = 0;
 
 	pxa_camera_deactivate(pcdev);
-
-	pcdev->icd = NULL;
 }
 
 static int test_platform_param(struct pxa_camera_dev *pcdev,
@@ -1596,8 +1586,8 @@ static int pxa_camera_suspend(struct device *dev)
 	pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR3);
 	pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR4);
 
-	if (pcdev->icd) {
-		struct v4l2_subdev *sd = soc_camera_to_subdev(pcdev->icd);
+	if (pcdev->soc_host.icd) {
+		struct v4l2_subdev *sd = soc_camera_to_subdev(pcdev->soc_host.icd);
 		ret = v4l2_subdev_call(sd, core, s_power, 0);
 		if (ret = -ENOIOCTLCMD)
 			ret = 0;
@@ -1622,8 +1612,8 @@ static int pxa_camera_resume(struct device *dev)
 	__raw_writel(pcdev->save_cicr[i++], pcdev->base + CICR3);
 	__raw_writel(pcdev->save_cicr[i++], pcdev->base + CICR4);
 
-	if (pcdev->icd) {
-		struct v4l2_subdev *sd = soc_camera_to_subdev(pcdev->icd);
+	if (pcdev->soc_host.icd) {
+		struct v4l2_subdev *sd = soc_camera_to_subdev(pcdev->soc_host.icd);
 		ret = v4l2_subdev_call(sd, core, s_power, 1);
 		if (ret = -ENOIOCTLCMD)
 			ret = 0;
diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
index 143d29fe..5b7d8e1 100644
--- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
+++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
@@ -95,7 +95,6 @@ struct sh_mobile_ceu_buffer {
 
 struct sh_mobile_ceu_dev {
 	struct soc_camera_host ici;
-	struct soc_camera_device *icd;
 	struct platform_device *csi2_pdev;
 
 	unsigned int irq;
@@ -163,7 +162,7 @@ static u32 ceu_read(struct sh_mobile_ceu_dev *priv, unsigned long reg_offs)
 static int sh_mobile_ceu_soft_reset(struct sh_mobile_ceu_dev *pcdev)
 {
 	int i, success = 0;
-	struct soc_camera_device *icd = pcdev->icd;
+	struct soc_camera_device *icd = pcdev->ici.icd;
 
 	ceu_write(pcdev, CAPSR, 1 << 16); /* reset */
 
@@ -277,7 +276,7 @@ static int sh_mobile_ceu_videobuf_setup(struct vb2_queue *vq,
  */
 static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev)
 {
-	struct soc_camera_device *icd = pcdev->icd;
+	struct soc_camera_device *icd = pcdev->ici.icd;
 	dma_addr_t phys_addr_top, phys_addr_bottom;
 	unsigned long top1, top2;
 	unsigned long bottom1, bottom2;
@@ -552,9 +551,6 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd)
 	struct v4l2_subdev *csi2_sd;
 	int ret;
 
-	if (pcdev->icd)
-		return -EBUSY;
-
 	dev_info(icd->parent,
 		 "SuperH Mobile CEU driver attached to camera %d\n",
 		 icd->devnum);
@@ -583,7 +579,6 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd)
 	 */
 	if (ret = -ENODEV && csi2_sd)
 		csi2_sd->grp_id = 0;
-	pcdev->icd = icd;
 
 	return 0;
 }
@@ -595,8 +590,6 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd)
 	struct sh_mobile_ceu_dev *pcdev = ici->priv;
 	struct v4l2_subdev *csi2_sd = find_csi2(pcdev);
 
-	BUG_ON(icd != pcdev->icd);
-
 	v4l2_subdev_call(csi2_sd, core, s_power, 0);
 	if (csi2_sd)
 		csi2_sd->grp_id = 0;
@@ -618,8 +611,6 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd)
 	dev_info(icd->parent,
 		 "SuperH Mobile CEU driver detached from camera %d\n",
 		 icd->devnum);
-
-	pcdev->icd = NULL;
 }
 
 /*
diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c
index 3a4efbd..832f059 100644
--- a/drivers/media/platform/soc_camera/soc_camera.c
+++ b/drivers/media/platform/soc_camera/soc_camera.c
@@ -505,6 +505,32 @@ static int soc_camera_set_fmt(struct soc_camera_device *icd,
 	return ici->ops->set_bus_param(icd);
 }
 
+static int soc_camera_add_device(struct soc_camera_device *icd)
+{
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+	int ret;
+
+	if (ici->icd)
+		return -EBUSY;
+
+	ret = ici->ops->add(icd);
+	if (!ret)
+		ici->icd = icd;
+
+	return ret;
+}
+
+static void soc_camera_remove_device(struct soc_camera_device *icd)
+{
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+
+	if (WARN_ON(icd != ici->icd))
+		return;
+
+	ici->ops->remove(icd);
+	ici->icd = NULL;
+}
+
 static int soc_camera_open(struct file *file)
 {
 	struct video_device *vdev = video_devdata(file);
@@ -568,7 +594,7 @@ static int soc_camera_open(struct file *file)
 		if (sdesc->subdev_desc.reset)
 			sdesc->subdev_desc.reset(icd->pdev);
 
-		ret = ici->ops->add(icd);
+		ret = soc_camera_add_device(icd);
 		if (ret < 0) {
 			dev_err(icd->pdev, "Couldn't activate the camera: %d\n", ret);
 			goto eiciadd;
@@ -619,7 +645,7 @@ esfmt:
 eresume:
 	__soc_camera_power_off(icd);
 epower:
-	ici->ops->remove(icd);
+	soc_camera_remove_device(icd);
 eiciadd:
 	icd->use_count--;
 	mutex_unlock(&ici->host_lock);
@@ -645,7 +671,7 @@ static int soc_camera_close(struct file *file)
 			vb2_queue_release(&icd->vb2_vidq);
 		__soc_camera_power_off(icd);
 
-		ici->ops->remove(icd);
+		soc_camera_remove_device(icd);
 	}
 
 	if (icd->streamer = file)
@@ -1167,7 +1193,7 @@ static int soc_camera_probe(struct soc_camera_device *icd)
 		ssdd->reset(icd->pdev);
 
 	mutex_lock(&ici->host_lock);
-	ret = ici->ops->add(icd);
+	ret = soc_camera_add_device(icd);
 	mutex_unlock(&ici->host_lock);
 	if (ret < 0)
 		goto eadd;
@@ -1240,7 +1266,7 @@ static int soc_camera_probe(struct soc_camera_device *icd)
 		icd->field		= mf.field;
 	}
 
-	ici->ops->remove(icd);
+	soc_camera_remove_device(icd);
 
 	mutex_unlock(&ici->host_lock);
 
@@ -1263,7 +1289,7 @@ eadddev:
 	icd->vdev = NULL;
 evdc:
 	mutex_lock(&ici->host_lock);
-	ici->ops->remove(icd);
+	soc_camera_remove_device(icd);
 	mutex_unlock(&ici->host_lock);
 eadd:
 	v4l2_ctrl_handler_free(&icd->ctrl_handler);
diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h
index ff77d08..5a46ce2 100644
--- a/include/media/soc_camera.h
+++ b/include/media/soc_camera.h
@@ -64,6 +64,7 @@ struct soc_camera_host {
 	struct mutex host_lock;		/* Protect pipeline modifications */
 	unsigned char nr;		/* Host number */
 	u32 capabilities;
+	struct soc_camera_device *icd;	/* Currently attached client */
 	void *priv;
 	const char *drv_name;
 	struct soc_camera_host_ops *ops;
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v10 01/21] soc-camera: move common code to soc_camera.c
@ 2013-06-11  8:23   ` Guennadi Liakhovetski
  0 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-11  8:23 UTC (permalink / raw)
  To: linux-media
  Cc: Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer,
	Guennadi Liakhovetski

All soc-camera host drivers include a pointer to an soc-camera device in
their host private struct to check, that only one client is connected.
Move this common code to soc_camera.c.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
 drivers/media/platform/soc_camera/atmel-isi.c      |   10 +-----
 drivers/media/platform/soc_camera/mx1_camera.c     |   20 +++--------
 drivers/media/platform/soc_camera/mx2_camera.c     |   13 +------
 drivers/media/platform/soc_camera/mx3_camera.c     |    9 -----
 drivers/media/platform/soc_camera/omap1_camera.c   |   14 +------
 drivers/media/platform/soc_camera/pxa_camera.c     |   18 ++-------
 .../platform/soc_camera/sh_mobile_ceu_camera.c     |   13 +------
 drivers/media/platform/soc_camera/soc_camera.c     |   38 ++++++++++++++++---
 include/media/soc_camera.h                         |    1 +
 9 files changed, 49 insertions(+), 87 deletions(-)

diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c
index 1abbb36..c9e080a 100644
--- a/drivers/media/platform/soc_camera/atmel-isi.c
+++ b/drivers/media/platform/soc_camera/atmel-isi.c
@@ -102,7 +102,6 @@ struct atmel_isi {
 	struct list_head		video_buffer_list;
 	struct frame_buffer		*active;
 
-	struct soc_camera_device	*icd;
 	struct soc_camera_host		soc_host;
 };
 
@@ -367,7 +366,7 @@ static void start_dma(struct atmel_isi *isi, struct frame_buffer *buffer)
 
 	/* Check if already in a frame */
 	if (isi_readl(isi, ISI_STATUS) & ISI_CTRL_CDC) {
-		dev_err(isi->icd->parent, "Already in frame handling.\n");
+		dev_err(isi->soc_host.icd->parent, "Already in frame handling.\n");
 		return;
 	}
 
@@ -753,9 +752,6 @@ static int isi_camera_add_device(struct soc_camera_device *icd)
 	struct atmel_isi *isi = ici->priv;
 	int ret;
 
-	if (isi->icd)
-		return -EBUSY;
-
 	ret = clk_enable(isi->pclk);
 	if (ret)
 		return ret;
@@ -766,7 +762,6 @@ static int isi_camera_add_device(struct soc_camera_device *icd)
 		return ret;
 	}
 
-	isi->icd = icd;
 	dev_dbg(icd->parent, "Atmel ISI Camera driver attached to camera %d\n",
 		 icd->devnum);
 	return 0;
@@ -777,11 +772,8 @@ static void isi_camera_remove_device(struct soc_camera_device *icd)
 	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct atmel_isi *isi = ici->priv;
 
-	BUG_ON(icd != isi->icd);
-
 	clk_disable(isi->mck);
 	clk_disable(isi->pclk);
-	isi->icd = NULL;
 
 	dev_dbg(icd->parent, "Atmel ISI Camera driver detached from camera %d\n",
 		 icd->devnum);
diff --git a/drivers/media/platform/soc_camera/mx1_camera.c b/drivers/media/platform/soc_camera/mx1_camera.c
index a3fd8d6..5f9ec8e 100644
--- a/drivers/media/platform/soc_camera/mx1_camera.c
+++ b/drivers/media/platform/soc_camera/mx1_camera.c
@@ -104,7 +104,6 @@ struct mx1_buffer {
  */
 struct mx1_camera_dev {
 	struct soc_camera_host		soc_host;
-	struct soc_camera_device	*icd;
 	struct mx1_camera_pdata		*pdata;
 	struct mx1_buffer		*active;
 	struct resource			*res;
@@ -220,7 +219,7 @@ out:
 static int mx1_camera_setup_dma(struct mx1_camera_dev *pcdev)
 {
 	struct videobuf_buffer *vbuf = &pcdev->active->vb;
-	struct device *dev = pcdev->icd->parent;
+	struct device *dev = pcdev->soc_host.icd->parent;
 	int ret;
 
 	if (unlikely(!pcdev->active)) {
@@ -331,7 +330,7 @@ static void mx1_camera_wakeup(struct mx1_camera_dev *pcdev,
 static void mx1_camera_dma_irq(int channel, void *data)
 {
 	struct mx1_camera_dev *pcdev = data;
-	struct device *dev = pcdev->icd->parent;
+	struct device *dev = pcdev->soc_host.icd->parent;
 	struct mx1_buffer *buf;
 	struct videobuf_buffer *vb;
 	unsigned long flags;
@@ -389,7 +388,7 @@ static int mclk_get_divisor(struct mx1_camera_dev *pcdev)
 	 */
 	div = (lcdclk + 2 * mclk - 1) / (2 * mclk) - 1;
 
-	dev_dbg(pcdev->icd->parent,
+	dev_dbg(pcdev->soc_host.icd->parent,
 		"System clock %lukHz, target freq %dkHz, divisor %lu\n",
 		lcdclk / 1000, mclk / 1000, div);
 
@@ -400,7 +399,7 @@ static void mx1_camera_activate(struct mx1_camera_dev *pcdev)
 {
 	unsigned int csicr1 = CSICR1_EN;
 
-	dev_dbg(pcdev->icd->parent, "Activate device\n");
+	dev_dbg(pcdev->soc_host.icd->parent, "Activate device\n");
 
 	clk_prepare_enable(pcdev->clk);
 
@@ -416,7 +415,7 @@ static void mx1_camera_activate(struct mx1_camera_dev *pcdev)
 
 static void mx1_camera_deactivate(struct mx1_camera_dev *pcdev)
 {
-	dev_dbg(pcdev->icd->parent, "Deactivate device\n");
+	dev_dbg(pcdev->soc_host.icd->parent, "Deactivate device\n");
 
 	/* Disable all CSI interface */
 	__raw_writel(0x00, pcdev->base + CSICR1);
@@ -433,16 +432,11 @@ static int mx1_camera_add_device(struct soc_camera_device *icd)
 	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct mx1_camera_dev *pcdev = ici->priv;
 
-	if (pcdev->icd)
-		return -EBUSY;
-
 	dev_info(icd->parent, "MX1 Camera driver attached to camera %d\n",
 		 icd->devnum);
 
 	mx1_camera_activate(pcdev);
 
-	pcdev->icd = icd;
-
 	return 0;
 }
 
@@ -452,8 +446,6 @@ static void mx1_camera_remove_device(struct soc_camera_device *icd)
 	struct mx1_camera_dev *pcdev = ici->priv;
 	unsigned int csicr1;
 
-	BUG_ON(icd != pcdev->icd);
-
 	/* disable interrupts */
 	csicr1 = __raw_readl(pcdev->base + CSICR1) & ~CSI_IRQ_MASK;
 	__raw_writel(csicr1, pcdev->base + CSICR1);
@@ -465,8 +457,6 @@ static void mx1_camera_remove_device(struct soc_camera_device *icd)
 		 icd->devnum);
 
 	mx1_camera_deactivate(pcdev);
-
-	pcdev->icd = NULL;
 }
 
 static int mx1_camera_set_bus_param(struct soc_camera_device *icd)
diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c
index 5bbeb43..772e071 100644
--- a/drivers/media/platform/soc_camera/mx2_camera.c
+++ b/drivers/media/platform/soc_camera/mx2_camera.c
@@ -236,7 +236,6 @@ enum mx2_camera_type {
 struct mx2_camera_dev {
 	struct device		*dev;
 	struct soc_camera_host	soc_host;
-	struct soc_camera_device *icd;
 	struct clk		*clk_emma_ahb, *clk_emma_ipg;
 	struct clk		*clk_csi_ahb, *clk_csi_per;
 
@@ -394,8 +393,8 @@ static void mx27_update_emma_buf(struct mx2_camera_dev *pcdev,
 		writel(phys, pcdev->base_emma +
 			PRP_DEST_Y_PTR - 0x14 * bufnum);
 		if (prp->out_fmt == V4L2_PIX_FMT_YUV420) {
-			u32 imgsize = pcdev->icd->user_height *
-					pcdev->icd->user_width;
+			u32 imgsize = pcdev->soc_host.icd->user_height *
+					pcdev->soc_host.icd->user_width;
 
 			writel(phys + imgsize, pcdev->base_emma +
 				PRP_DEST_CB_PTR - 0x14 * bufnum);
@@ -424,9 +423,6 @@ static int mx2_camera_add_device(struct soc_camera_device *icd)
 	int ret;
 	u32 csicr1;
 
-	if (pcdev->icd)
-		return -EBUSY;
-
 	ret = clk_prepare_enable(pcdev->clk_csi_ahb);
 	if (ret < 0)
 		return ret;
@@ -441,7 +437,6 @@ static int mx2_camera_add_device(struct soc_camera_device *icd)
 	pcdev->csicr1 = csicr1;
 	writel(pcdev->csicr1, pcdev->base_csi + CSICR1);
 
-	pcdev->icd = icd;
 	pcdev->frame_count = 0;
 
 	dev_info(icd->parent, "Camera driver attached to camera %d\n",
@@ -460,14 +455,10 @@ static void mx2_camera_remove_device(struct soc_camera_device *icd)
 	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct mx2_camera_dev *pcdev = ici->priv;
 
-	BUG_ON(icd != pcdev->icd);
-
 	dev_info(icd->parent, "Camera driver detached from camera %d\n",
 		 icd->devnum);
 
 	mx2_camera_deactivate(pcdev);
-
-	pcdev->icd = NULL;
 }
 
 /*
diff --git a/drivers/media/platform/soc_camera/mx3_camera.c b/drivers/media/platform/soc_camera/mx3_camera.c
index 5da3377..71b9b19 100644
--- a/drivers/media/platform/soc_camera/mx3_camera.c
+++ b/drivers/media/platform/soc_camera/mx3_camera.c
@@ -94,7 +94,6 @@ struct mx3_camera_dev {
 	 * Interface. If anyone ever builds hardware to enable more than one
 	 * camera _simultaneously_, they will have to modify this driver too
 	 */
-	struct soc_camera_device *icd;
 	struct clk		*clk;
 
 	void __iomem		*base;
@@ -517,13 +516,9 @@ static int mx3_camera_add_device(struct soc_camera_device *icd)
 	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct mx3_camera_dev *mx3_cam = ici->priv;
 
-	if (mx3_cam->icd)
-		return -EBUSY;
-
 	mx3_camera_activate(mx3_cam, icd);
 
 	mx3_cam->buf_total = 0;
-	mx3_cam->icd = icd;
 
 	dev_info(icd->parent, "MX3 Camera driver attached to camera %d\n",
 		 icd->devnum);
@@ -538,8 +533,6 @@ static void mx3_camera_remove_device(struct soc_camera_device *icd)
 	struct mx3_camera_dev *mx3_cam = ici->priv;
 	struct idmac_channel **ichan = &mx3_cam->idmac_channel[0];
 
-	BUG_ON(icd != mx3_cam->icd);
-
 	if (*ichan) {
 		dma_release_channel(&(*ichan)->dma_chan);
 		*ichan = NULL;
@@ -547,8 +540,6 @@ static void mx3_camera_remove_device(struct soc_camera_device *icd)
 
 	clk_disable_unprepare(mx3_cam->clk);
 
-	mx3_cam->icd = NULL;
-
 	dev_info(icd->parent, "MX3 Camera driver detached from camera %d\n",
 		 icd->devnum);
 }
diff --git a/drivers/media/platform/soc_camera/omap1_camera.c b/drivers/media/platform/soc_camera/omap1_camera.c
index 9689a6e..c42c23e 100644
--- a/drivers/media/platform/soc_camera/omap1_camera.c
+++ b/drivers/media/platform/soc_camera/omap1_camera.c
@@ -150,7 +150,6 @@ struct omap1_cam_buf {
 
 struct omap1_cam_dev {
 	struct soc_camera_host		soc_host;
-	struct soc_camera_device	*icd;
 	struct clk			*clk;
 
 	unsigned int			irq;
@@ -564,7 +563,7 @@ static void videobuf_done(struct omap1_cam_dev *pcdev,
 {
 	struct omap1_cam_buf *buf = pcdev->active;
 	struct videobuf_buffer *vb;
-	struct device *dev = pcdev->icd->parent;
+	struct device *dev = pcdev->soc_host.icd->parent;
 
 	if (WARN_ON(!buf)) {
 		suspend_capture(pcdev);
@@ -790,7 +789,7 @@ out:
 static irqreturn_t cam_isr(int irq, void *data)
 {
 	struct omap1_cam_dev *pcdev = data;
-	struct device *dev = pcdev->icd->parent;
+	struct device *dev = pcdev->soc_host.icd->parent;
 	struct omap1_cam_buf *buf = pcdev->active;
 	u32 it_status;
 	unsigned long flags;
@@ -904,9 +903,6 @@ static int omap1_cam_add_device(struct soc_camera_device *icd)
 	struct omap1_cam_dev *pcdev = ici->priv;
 	u32 ctrlclock;
 
-	if (pcdev->icd)
-		return -EBUSY;
-
 	clk_enable(pcdev->clk);
 
 	/* setup sensor clock */
@@ -941,8 +937,6 @@ static int omap1_cam_add_device(struct soc_camera_device *icd)
 
 	sensor_reset(pcdev, false);
 
-	pcdev->icd = icd;
-
 	dev_dbg(icd->parent, "OMAP1 Camera driver attached to camera %d\n",
 			icd->devnum);
 	return 0;
@@ -954,8 +948,6 @@ static void omap1_cam_remove_device(struct soc_camera_device *icd)
 	struct omap1_cam_dev *pcdev = ici->priv;
 	u32 ctrlclock;
 
-	BUG_ON(icd != pcdev->icd);
-
 	suspend_capture(pcdev);
 	disable_capture(pcdev);
 
@@ -974,8 +966,6 @@ static void omap1_cam_remove_device(struct soc_camera_device *icd)
 
 	clk_disable(pcdev->clk);
 
-	pcdev->icd = NULL;
-
 	dev_dbg(icd->parent,
 		"OMAP1 Camera driver detached from camera %d\n", icd->devnum);
 }
diff --git a/drivers/media/platform/soc_camera/pxa_camera.c b/drivers/media/platform/soc_camera/pxa_camera.c
index d665242..686edf7 100644
--- a/drivers/media/platform/soc_camera/pxa_camera.c
+++ b/drivers/media/platform/soc_camera/pxa_camera.c
@@ -200,7 +200,6 @@ struct pxa_camera_dev {
 	 * interface. If anyone ever builds hardware to enable more than
 	 * one camera, they will have to modify this driver too
 	 */
-	struct soc_camera_device *icd;
 	struct clk		*clk;
 
 	unsigned int		irq;
@@ -966,13 +965,8 @@ static int pxa_camera_add_device(struct soc_camera_device *icd)
 	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct pxa_camera_dev *pcdev = ici->priv;
 
-	if (pcdev->icd)
-		return -EBUSY;
-
 	pxa_camera_activate(pcdev);
 
-	pcdev->icd = icd;
-
 	dev_info(icd->parent, "PXA Camera driver attached to camera %d\n",
 		 icd->devnum);
 
@@ -985,8 +979,6 @@ static void pxa_camera_remove_device(struct soc_camera_device *icd)
 	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct pxa_camera_dev *pcdev = ici->priv;
 
-	BUG_ON(icd != pcdev->icd);
-
 	dev_info(icd->parent, "PXA Camera driver detached from camera %d\n",
 		 icd->devnum);
 
@@ -999,8 +991,6 @@ static void pxa_camera_remove_device(struct soc_camera_device *icd)
 	DCSR(pcdev->dma_chans[2]) = 0;
 
 	pxa_camera_deactivate(pcdev);
-
-	pcdev->icd = NULL;
 }
 
 static int test_platform_param(struct pxa_camera_dev *pcdev,
@@ -1596,8 +1586,8 @@ static int pxa_camera_suspend(struct device *dev)
 	pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR3);
 	pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR4);
 
-	if (pcdev->icd) {
-		struct v4l2_subdev *sd = soc_camera_to_subdev(pcdev->icd);
+	if (pcdev->soc_host.icd) {
+		struct v4l2_subdev *sd = soc_camera_to_subdev(pcdev->soc_host.icd);
 		ret = v4l2_subdev_call(sd, core, s_power, 0);
 		if (ret == -ENOIOCTLCMD)
 			ret = 0;
@@ -1622,8 +1612,8 @@ static int pxa_camera_resume(struct device *dev)
 	__raw_writel(pcdev->save_cicr[i++], pcdev->base + CICR3);
 	__raw_writel(pcdev->save_cicr[i++], pcdev->base + CICR4);
 
-	if (pcdev->icd) {
-		struct v4l2_subdev *sd = soc_camera_to_subdev(pcdev->icd);
+	if (pcdev->soc_host.icd) {
+		struct v4l2_subdev *sd = soc_camera_to_subdev(pcdev->soc_host.icd);
 		ret = v4l2_subdev_call(sd, core, s_power, 1);
 		if (ret == -ENOIOCTLCMD)
 			ret = 0;
diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
index 143d29fe..5b7d8e1 100644
--- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
+++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
@@ -95,7 +95,6 @@ struct sh_mobile_ceu_buffer {
 
 struct sh_mobile_ceu_dev {
 	struct soc_camera_host ici;
-	struct soc_camera_device *icd;
 	struct platform_device *csi2_pdev;
 
 	unsigned int irq;
@@ -163,7 +162,7 @@ static u32 ceu_read(struct sh_mobile_ceu_dev *priv, unsigned long reg_offs)
 static int sh_mobile_ceu_soft_reset(struct sh_mobile_ceu_dev *pcdev)
 {
 	int i, success = 0;
-	struct soc_camera_device *icd = pcdev->icd;
+	struct soc_camera_device *icd = pcdev->ici.icd;
 
 	ceu_write(pcdev, CAPSR, 1 << 16); /* reset */
 
@@ -277,7 +276,7 @@ static int sh_mobile_ceu_videobuf_setup(struct vb2_queue *vq,
  */
 static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev)
 {
-	struct soc_camera_device *icd = pcdev->icd;
+	struct soc_camera_device *icd = pcdev->ici.icd;
 	dma_addr_t phys_addr_top, phys_addr_bottom;
 	unsigned long top1, top2;
 	unsigned long bottom1, bottom2;
@@ -552,9 +551,6 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd)
 	struct v4l2_subdev *csi2_sd;
 	int ret;
 
-	if (pcdev->icd)
-		return -EBUSY;
-
 	dev_info(icd->parent,
 		 "SuperH Mobile CEU driver attached to camera %d\n",
 		 icd->devnum);
@@ -583,7 +579,6 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd)
 	 */
 	if (ret == -ENODEV && csi2_sd)
 		csi2_sd->grp_id = 0;
-	pcdev->icd = icd;
 
 	return 0;
 }
@@ -595,8 +590,6 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd)
 	struct sh_mobile_ceu_dev *pcdev = ici->priv;
 	struct v4l2_subdev *csi2_sd = find_csi2(pcdev);
 
-	BUG_ON(icd != pcdev->icd);
-
 	v4l2_subdev_call(csi2_sd, core, s_power, 0);
 	if (csi2_sd)
 		csi2_sd->grp_id = 0;
@@ -618,8 +611,6 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd)
 	dev_info(icd->parent,
 		 "SuperH Mobile CEU driver detached from camera %d\n",
 		 icd->devnum);
-
-	pcdev->icd = NULL;
 }
 
 /*
diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c
index 3a4efbd..832f059 100644
--- a/drivers/media/platform/soc_camera/soc_camera.c
+++ b/drivers/media/platform/soc_camera/soc_camera.c
@@ -505,6 +505,32 @@ static int soc_camera_set_fmt(struct soc_camera_device *icd,
 	return ici->ops->set_bus_param(icd);
 }
 
+static int soc_camera_add_device(struct soc_camera_device *icd)
+{
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+	int ret;
+
+	if (ici->icd)
+		return -EBUSY;
+
+	ret = ici->ops->add(icd);
+	if (!ret)
+		ici->icd = icd;
+
+	return ret;
+}
+
+static void soc_camera_remove_device(struct soc_camera_device *icd)
+{
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+
+	if (WARN_ON(icd != ici->icd))
+		return;
+
+	ici->ops->remove(icd);
+	ici->icd = NULL;
+}
+
 static int soc_camera_open(struct file *file)
 {
 	struct video_device *vdev = video_devdata(file);
@@ -568,7 +594,7 @@ static int soc_camera_open(struct file *file)
 		if (sdesc->subdev_desc.reset)
 			sdesc->subdev_desc.reset(icd->pdev);
 
-		ret = ici->ops->add(icd);
+		ret = soc_camera_add_device(icd);
 		if (ret < 0) {
 			dev_err(icd->pdev, "Couldn't activate the camera: %d\n", ret);
 			goto eiciadd;
@@ -619,7 +645,7 @@ esfmt:
 eresume:
 	__soc_camera_power_off(icd);
 epower:
-	ici->ops->remove(icd);
+	soc_camera_remove_device(icd);
 eiciadd:
 	icd->use_count--;
 	mutex_unlock(&ici->host_lock);
@@ -645,7 +671,7 @@ static int soc_camera_close(struct file *file)
 			vb2_queue_release(&icd->vb2_vidq);
 		__soc_camera_power_off(icd);
 
-		ici->ops->remove(icd);
+		soc_camera_remove_device(icd);
 	}
 
 	if (icd->streamer == file)
@@ -1167,7 +1193,7 @@ static int soc_camera_probe(struct soc_camera_device *icd)
 		ssdd->reset(icd->pdev);
 
 	mutex_lock(&ici->host_lock);
-	ret = ici->ops->add(icd);
+	ret = soc_camera_add_device(icd);
 	mutex_unlock(&ici->host_lock);
 	if (ret < 0)
 		goto eadd;
@@ -1240,7 +1266,7 @@ static int soc_camera_probe(struct soc_camera_device *icd)
 		icd->field		= mf.field;
 	}
 
-	ici->ops->remove(icd);
+	soc_camera_remove_device(icd);
 
 	mutex_unlock(&ici->host_lock);
 
@@ -1263,7 +1289,7 @@ eadddev:
 	icd->vdev = NULL;
 evdc:
 	mutex_lock(&ici->host_lock);
-	ici->ops->remove(icd);
+	soc_camera_remove_device(icd);
 	mutex_unlock(&ici->host_lock);
 eadd:
 	v4l2_ctrl_handler_free(&icd->ctrl_handler);
diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h
index ff77d08..5a46ce2 100644
--- a/include/media/soc_camera.h
+++ b/include/media/soc_camera.h
@@ -64,6 +64,7 @@ struct soc_camera_host {
 	struct mutex host_lock;		/* Protect pipeline modifications */
 	unsigned char nr;		/* Host number */
 	u32 capabilities;
+	struct soc_camera_device *icd;	/* Currently attached client */
 	void *priv;
 	const char *drv_name;
 	struct soc_camera_host_ops *ops;
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v10 02/21] soc-camera: add host clock callbacks to start and stop the master clock
  2013-06-11  8:23 ` Guennadi Liakhovetski
@ 2013-06-11  8:23   ` Guennadi Liakhovetski
  -1 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-11  8:23 UTC (permalink / raw)
  To: linux-media
  Cc: Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer,
	Guennadi Liakhovetski

Currently soc-camera uses a single camera host callback to activate the
interface master clock and to configure the interface for a specific
client. However, during probing we might not have the information about
a client, we just need to activate the clock. Add new camera host driver
callbacks to only start and stop the clock without and client-specific
configuration.

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

diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c
index 832f059..df90565 100644
--- a/drivers/media/platform/soc_camera/soc_camera.c
+++ b/drivers/media/platform/soc_camera/soc_camera.c
@@ -513,10 +513,23 @@ static int soc_camera_add_device(struct soc_camera_device *icd)
 	if (ici->icd)
 		return -EBUSY;
 
+	if (ici->ops->clock_start) {
+		ret = ici->ops->clock_start(ici);
+		if (ret < 0)
+			return ret;
+	}
+
 	ret = ici->ops->add(icd);
-	if (!ret)
-		ici->icd = icd;
+	if (ret < 0)
+		goto eadd;
+
+	ici->icd = icd;
 
+	return 0;
+
+eadd:
+	if (ici->ops->clock_stop)
+		ici->ops->clock_stop(ici);
 	return ret;
 }
 
@@ -528,6 +541,8 @@ static void soc_camera_remove_device(struct soc_camera_device *icd)
 		return;
 
 	ici->ops->remove(icd);
+	if (ici->ops->clock_stop)
+		ici->ops->clock_stop(ici);
 	ici->icd = NULL;
 }
 
diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h
index 5a46ce2..64415ee 100644
--- a/include/media/soc_camera.h
+++ b/include/media/soc_camera.h
@@ -74,6 +74,8 @@ struct soc_camera_host_ops {
 	struct module *owner;
 	int (*add)(struct soc_camera_device *);
 	void (*remove)(struct soc_camera_device *);
+	int (*clock_start)(struct soc_camera_host *);
+	void (*clock_stop)(struct soc_camera_host *);
 	/*
 	 * .get_formats() is called for each client device format, but
 	 * .put_formats() is only called once. Further, if any of the calls to
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v10 02/21] soc-camera: add host clock callbacks to start and stop the master clock
@ 2013-06-11  8:23   ` Guennadi Liakhovetski
  0 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-11  8:23 UTC (permalink / raw)
  To: linux-media
  Cc: Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer,
	Guennadi Liakhovetski

Currently soc-camera uses a single camera host callback to activate the
interface master clock and to configure the interface for a specific
client. However, during probing we might not have the information about
a client, we just need to activate the clock. Add new camera host driver
callbacks to only start and stop the clock without and client-specific
configuration.

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

diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c
index 832f059..df90565 100644
--- a/drivers/media/platform/soc_camera/soc_camera.c
+++ b/drivers/media/platform/soc_camera/soc_camera.c
@@ -513,10 +513,23 @@ static int soc_camera_add_device(struct soc_camera_device *icd)
 	if (ici->icd)
 		return -EBUSY;
 
+	if (ici->ops->clock_start) {
+		ret = ici->ops->clock_start(ici);
+		if (ret < 0)
+			return ret;
+	}
+
 	ret = ici->ops->add(icd);
-	if (!ret)
-		ici->icd = icd;
+	if (ret < 0)
+		goto eadd;
+
+	ici->icd = icd;
 
+	return 0;
+
+eadd:
+	if (ici->ops->clock_stop)
+		ici->ops->clock_stop(ici);
 	return ret;
 }
 
@@ -528,6 +541,8 @@ static void soc_camera_remove_device(struct soc_camera_device *icd)
 		return;
 
 	ici->ops->remove(icd);
+	if (ici->ops->clock_stop)
+		ici->ops->clock_stop(ici);
 	ici->icd = NULL;
 }
 
diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h
index 5a46ce2..64415ee 100644
--- a/include/media/soc_camera.h
+++ b/include/media/soc_camera.h
@@ -74,6 +74,8 @@ struct soc_camera_host_ops {
 	struct module *owner;
 	int (*add)(struct soc_camera_device *);
 	void (*remove)(struct soc_camera_device *);
+	int (*clock_start)(struct soc_camera_host *);
+	void (*clock_stop)(struct soc_camera_host *);
 	/*
 	 * .get_formats() is called for each client device format, but
 	 * .put_formats() is only called once. Further, if any of the calls to
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v10 03/21] pxa-camera: move interface activation and deactivation to clock callbacks
  2013-06-11  8:23 ` Guennadi Liakhovetski
@ 2013-06-11  8:23   ` Guennadi Liakhovetski
  -1 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-11  8:23 UTC (permalink / raw)
  To: linux-media
  Cc: Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer,
	Guennadi Liakhovetski

When adding and removing a client, the pxa-camera driver only activates
and deactivates its camera interface respectively, which doesn't include
any client-specific actions. Move this functionality into .clock_start()
and .clock_stop() callbacks.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
 drivers/media/platform/soc_camera/pxa_camera.c |   28 +++++++++++++++--------
 1 files changed, 18 insertions(+), 10 deletions(-)

diff --git a/drivers/media/platform/soc_camera/pxa_camera.c b/drivers/media/platform/soc_camera/pxa_camera.c
index 686edf7..d4df305 100644
--- a/drivers/media/platform/soc_camera/pxa_camera.c
+++ b/drivers/media/platform/soc_camera/pxa_camera.c
@@ -955,33 +955,39 @@ static irqreturn_t pxa_camera_irq(int irq, void *data)
 	return IRQ_HANDLED;
 }
 
+static int pxa_camera_add_device(struct soc_camera_device *icd)
+{
+	dev_info(icd->parent, "PXA Camera driver attached to camera %d\n",
+		 icd->devnum);
+
+	return 0;
+}
+
+static void pxa_camera_remove_device(struct soc_camera_device *icd)
+{
+	dev_info(icd->parent, "PXA Camera driver detached from camera %d\n",
+		 icd->devnum);
+}
+
 /*
  * The following two functions absolutely depend on the fact, that
  * there can be only one camera on PXA quick capture interface
  * Called with .host_lock held
  */
-static int pxa_camera_add_device(struct soc_camera_device *icd)
+static int pxa_camera_clock_start(struct soc_camera_host *ici)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct pxa_camera_dev *pcdev = ici->priv;
 
 	pxa_camera_activate(pcdev);
 
-	dev_info(icd->parent, "PXA Camera driver attached to camera %d\n",
-		 icd->devnum);
-
 	return 0;
 }
 
 /* Called with .host_lock held */
-static void pxa_camera_remove_device(struct soc_camera_device *icd)
+static void pxa_camera_clock_stop(struct soc_camera_host *ici)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct pxa_camera_dev *pcdev = ici->priv;
 
-	dev_info(icd->parent, "PXA Camera driver detached from camera %d\n",
-		 icd->devnum);
-
 	/* disable capture, disable interrupts */
 	__raw_writel(0x3ff, pcdev->base + CICR0);
 
@@ -1630,6 +1636,8 @@ static struct soc_camera_host_ops pxa_soc_camera_host_ops = {
 	.owner		= THIS_MODULE,
 	.add		= pxa_camera_add_device,
 	.remove		= pxa_camera_remove_device,
+	.clock_start	= pxa_camera_clock_start,
+	.clock_stop	= pxa_camera_clock_stop,
 	.set_crop	= pxa_camera_set_crop,
 	.get_formats	= pxa_camera_get_formats,
 	.put_formats	= pxa_camera_put_formats,
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v10 03/21] pxa-camera: move interface activation and deactivation to clock callbacks
@ 2013-06-11  8:23   ` Guennadi Liakhovetski
  0 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-11  8:23 UTC (permalink / raw)
  To: linux-media
  Cc: Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer,
	Guennadi Liakhovetski

When adding and removing a client, the pxa-camera driver only activates
and deactivates its camera interface respectively, which doesn't include
any client-specific actions. Move this functionality into .clock_start()
and .clock_stop() callbacks.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
 drivers/media/platform/soc_camera/pxa_camera.c |   28 +++++++++++++++--------
 1 files changed, 18 insertions(+), 10 deletions(-)

diff --git a/drivers/media/platform/soc_camera/pxa_camera.c b/drivers/media/platform/soc_camera/pxa_camera.c
index 686edf7..d4df305 100644
--- a/drivers/media/platform/soc_camera/pxa_camera.c
+++ b/drivers/media/platform/soc_camera/pxa_camera.c
@@ -955,33 +955,39 @@ static irqreturn_t pxa_camera_irq(int irq, void *data)
 	return IRQ_HANDLED;
 }
 
+static int pxa_camera_add_device(struct soc_camera_device *icd)
+{
+	dev_info(icd->parent, "PXA Camera driver attached to camera %d\n",
+		 icd->devnum);
+
+	return 0;
+}
+
+static void pxa_camera_remove_device(struct soc_camera_device *icd)
+{
+	dev_info(icd->parent, "PXA Camera driver detached from camera %d\n",
+		 icd->devnum);
+}
+
 /*
  * The following two functions absolutely depend on the fact, that
  * there can be only one camera on PXA quick capture interface
  * Called with .host_lock held
  */
-static int pxa_camera_add_device(struct soc_camera_device *icd)
+static int pxa_camera_clock_start(struct soc_camera_host *ici)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct pxa_camera_dev *pcdev = ici->priv;
 
 	pxa_camera_activate(pcdev);
 
-	dev_info(icd->parent, "PXA Camera driver attached to camera %d\n",
-		 icd->devnum);
-
 	return 0;
 }
 
 /* Called with .host_lock held */
-static void pxa_camera_remove_device(struct soc_camera_device *icd)
+static void pxa_camera_clock_stop(struct soc_camera_host *ici)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct pxa_camera_dev *pcdev = ici->priv;
 
-	dev_info(icd->parent, "PXA Camera driver detached from camera %d\n",
-		 icd->devnum);
-
 	/* disable capture, disable interrupts */
 	__raw_writel(0x3ff, pcdev->base + CICR0);
 
@@ -1630,6 +1636,8 @@ static struct soc_camera_host_ops pxa_soc_camera_host_ops = {
 	.owner		= THIS_MODULE,
 	.add		= pxa_camera_add_device,
 	.remove		= pxa_camera_remove_device,
+	.clock_start	= pxa_camera_clock_start,
+	.clock_stop	= pxa_camera_clock_stop,
 	.set_crop	= pxa_camera_set_crop,
 	.get_formats	= pxa_camera_get_formats,
 	.put_formats	= pxa_camera_put_formats,
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v10 04/21] omap1-camera: move interface activation and deactivation to clock callbacks
  2013-06-11  8:23 ` Guennadi Liakhovetski
@ 2013-06-11  8:23   ` Guennadi Liakhovetski
  -1 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-11  8:23 UTC (permalink / raw)
  To: linux-media
  Cc: Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer,
	Guennadi Liakhovetski

When adding and removing a client, the omap1-camera driver only activates
and deactivates its camera interface respectively, which doesn't include
any client-specific actions. Move this functionality into .clock_start()
and .clock_stop() callbacks.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
 drivers/media/platform/soc_camera/omap1_camera.c |   27 ++++++++++++++-------
 1 files changed, 18 insertions(+), 9 deletions(-)

diff --git a/drivers/media/platform/soc_camera/omap1_camera.c b/drivers/media/platform/soc_camera/omap1_camera.c
index c42c23e..6769193 100644
--- a/drivers/media/platform/soc_camera/omap1_camera.c
+++ b/drivers/media/platform/soc_camera/omap1_camera.c
@@ -893,13 +893,26 @@ static void sensor_reset(struct omap1_cam_dev *pcdev, bool reset)
 		CAM_WRITE(pcdev, GPIO, !reset);
 }
 
+static int omap1_cam_add_device(struct soc_camera_device *icd)
+{
+	dev_dbg(icd->parent, "OMAP1 Camera driver attached to camera %d\n",
+			icd->devnum);
+
+	return 0;
+}
+
+static void omap1_cam_remove_device(struct soc_camera_device *icd)
+{
+	dev_dbg(icd->parent,
+		"OMAP1 Camera driver detached from camera %d\n", icd->devnum);
+}
+
 /*
  * The following two functions absolutely depend on the fact, that
  * there can be only one camera on OMAP1 camera sensor interface
  */
-static int omap1_cam_add_device(struct soc_camera_device *icd)
+static int omap1_cam_clock_start(struct soc_camera_host *ici)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct omap1_cam_dev *pcdev = ici->priv;
 	u32 ctrlclock;
 
@@ -937,14 +950,11 @@ static int omap1_cam_add_device(struct soc_camera_device *icd)
 
 	sensor_reset(pcdev, false);
 
-	dev_dbg(icd->parent, "OMAP1 Camera driver attached to camera %d\n",
-			icd->devnum);
 	return 0;
 }
 
-static void omap1_cam_remove_device(struct soc_camera_device *icd)
+static void omap1_cam_clock_stop(struct soc_camera_host *ici)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct omap1_cam_dev *pcdev = ici->priv;
 	u32 ctrlclock;
 
@@ -965,9 +975,6 @@ static void omap1_cam_remove_device(struct soc_camera_device *icd)
 	CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~MCLK_EN);
 
 	clk_disable(pcdev->clk);
-
-	dev_dbg(icd->parent,
-		"OMAP1 Camera driver detached from camera %d\n", icd->devnum);
 }
 
 /* Duplicate standard formats based on host capability of byte swapping */
@@ -1525,6 +1532,8 @@ static struct soc_camera_host_ops omap1_host_ops = {
 	.owner		= THIS_MODULE,
 	.add		= omap1_cam_add_device,
 	.remove		= omap1_cam_remove_device,
+	.clock_start	= omap1_cam_clock_start,
+	.clock_stop	= omap1_cam_clock_stop,
 	.get_formats	= omap1_cam_get_formats,
 	.set_crop	= omap1_cam_set_crop,
 	.set_fmt	= omap1_cam_set_fmt,
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v10 04/21] omap1-camera: move interface activation and deactivation to clock callbacks
@ 2013-06-11  8:23   ` Guennadi Liakhovetski
  0 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-11  8:23 UTC (permalink / raw)
  To: linux-media
  Cc: Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer,
	Guennadi Liakhovetski

When adding and removing a client, the omap1-camera driver only activates
and deactivates its camera interface respectively, which doesn't include
any client-specific actions. Move this functionality into .clock_start()
and .clock_stop() callbacks.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
 drivers/media/platform/soc_camera/omap1_camera.c |   27 ++++++++++++++-------
 1 files changed, 18 insertions(+), 9 deletions(-)

diff --git a/drivers/media/platform/soc_camera/omap1_camera.c b/drivers/media/platform/soc_camera/omap1_camera.c
index c42c23e..6769193 100644
--- a/drivers/media/platform/soc_camera/omap1_camera.c
+++ b/drivers/media/platform/soc_camera/omap1_camera.c
@@ -893,13 +893,26 @@ static void sensor_reset(struct omap1_cam_dev *pcdev, bool reset)
 		CAM_WRITE(pcdev, GPIO, !reset);
 }
 
+static int omap1_cam_add_device(struct soc_camera_device *icd)
+{
+	dev_dbg(icd->parent, "OMAP1 Camera driver attached to camera %d\n",
+			icd->devnum);
+
+	return 0;
+}
+
+static void omap1_cam_remove_device(struct soc_camera_device *icd)
+{
+	dev_dbg(icd->parent,
+		"OMAP1 Camera driver detached from camera %d\n", icd->devnum);
+}
+
 /*
  * The following two functions absolutely depend on the fact, that
  * there can be only one camera on OMAP1 camera sensor interface
  */
-static int omap1_cam_add_device(struct soc_camera_device *icd)
+static int omap1_cam_clock_start(struct soc_camera_host *ici)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct omap1_cam_dev *pcdev = ici->priv;
 	u32 ctrlclock;
 
@@ -937,14 +950,11 @@ static int omap1_cam_add_device(struct soc_camera_device *icd)
 
 	sensor_reset(pcdev, false);
 
-	dev_dbg(icd->parent, "OMAP1 Camera driver attached to camera %d\n",
-			icd->devnum);
 	return 0;
 }
 
-static void omap1_cam_remove_device(struct soc_camera_device *icd)
+static void omap1_cam_clock_stop(struct soc_camera_host *ici)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct omap1_cam_dev *pcdev = ici->priv;
 	u32 ctrlclock;
 
@@ -965,9 +975,6 @@ static void omap1_cam_remove_device(struct soc_camera_device *icd)
 	CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~MCLK_EN);
 
 	clk_disable(pcdev->clk);
-
-	dev_dbg(icd->parent,
-		"OMAP1 Camera driver detached from camera %d\n", icd->devnum);
 }
 
 /* Duplicate standard formats based on host capability of byte swapping */
@@ -1525,6 +1532,8 @@ static struct soc_camera_host_ops omap1_host_ops = {
 	.owner		= THIS_MODULE,
 	.add		= omap1_cam_add_device,
 	.remove		= omap1_cam_remove_device,
+	.clock_start	= omap1_cam_clock_start,
+	.clock_stop	= omap1_cam_clock_stop,
 	.get_formats	= omap1_cam_get_formats,
 	.set_crop	= omap1_cam_set_crop,
 	.set_fmt	= omap1_cam_set_fmt,
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v10 05/21] atmel-isi: move interface activation and deactivation to clock callbacks
  2013-06-11  8:23 ` Guennadi Liakhovetski
@ 2013-06-11  8:23   ` Guennadi Liakhovetski
  -1 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-11  8:23 UTC (permalink / raw)
  To: linux-media
  Cc: Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer,
	Guennadi Liakhovetski

When adding and removing a client, the atmel-isi camera host driver only
activates and deactivates its camera interface respectively, which doesn't
include any client-specific actions. Move this functionality into
.clock_start() and .clock_stop() callbacks.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
 drivers/media/platform/soc_camera/atmel-isi.c |   28 +++++++++++++++++--------
 1 files changed, 19 insertions(+), 9 deletions(-)

diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c
index c9e080a..1044856 100644
--- a/drivers/media/platform/soc_camera/atmel-isi.c
+++ b/drivers/media/platform/soc_camera/atmel-isi.c
@@ -745,10 +745,23 @@ static int isi_camera_get_formats(struct soc_camera_device *icd,
 	return formats;
 }
 
-/* Called with .host_lock held */
 static int isi_camera_add_device(struct soc_camera_device *icd)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+	dev_dbg(icd->parent, "Atmel ISI Camera driver attached to camera %d\n",
+		 icd->devnum);
+
+	return 0;
+}
+
+static void isi_camera_remove_device(struct soc_camera_device *icd)
+{
+	dev_dbg(icd->parent, "Atmel ISI Camera driver detached from camera %d\n",
+		 icd->devnum);
+}
+
+/* Called with .host_lock held */
+static int isi_camera_clock_start(struct soc_camera_host *ici)
+{
 	struct atmel_isi *isi = ici->priv;
 	int ret;
 
@@ -762,21 +775,16 @@ static int isi_camera_add_device(struct soc_camera_device *icd)
 		return ret;
 	}
 
-	dev_dbg(icd->parent, "Atmel ISI Camera driver attached to camera %d\n",
-		 icd->devnum);
 	return 0;
 }
+
 /* Called with .host_lock held */
-static void isi_camera_remove_device(struct soc_camera_device *icd)
+static void isi_camera_clock_stop(struct soc_camera_host *ici)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct atmel_isi *isi = ici->priv;
 
 	clk_disable(isi->mck);
 	clk_disable(isi->pclk);
-
-	dev_dbg(icd->parent, "Atmel ISI Camera driver detached from camera %d\n",
-		 icd->devnum);
 }
 
 static unsigned int isi_camera_poll(struct file *file, poll_table *pt)
@@ -880,6 +888,8 @@ static struct soc_camera_host_ops isi_soc_camera_host_ops = {
 	.owner		= THIS_MODULE,
 	.add		= isi_camera_add_device,
 	.remove		= isi_camera_remove_device,
+	.clock_start	= isi_camera_clock_start,
+	.clock_stop	= isi_camera_clock_stop,
 	.set_fmt	= isi_camera_set_fmt,
 	.try_fmt	= isi_camera_try_fmt,
 	.get_formats	= isi_camera_get_formats,
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v10 05/21] atmel-isi: move interface activation and deactivation to clock callbacks
@ 2013-06-11  8:23   ` Guennadi Liakhovetski
  0 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-11  8:23 UTC (permalink / raw)
  To: linux-media
  Cc: Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer,
	Guennadi Liakhovetski

When adding and removing a client, the atmel-isi camera host driver only
activates and deactivates its camera interface respectively, which doesn't
include any client-specific actions. Move this functionality into
.clock_start() and .clock_stop() callbacks.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
 drivers/media/platform/soc_camera/atmel-isi.c |   28 +++++++++++++++++--------
 1 files changed, 19 insertions(+), 9 deletions(-)

diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c
index c9e080a..1044856 100644
--- a/drivers/media/platform/soc_camera/atmel-isi.c
+++ b/drivers/media/platform/soc_camera/atmel-isi.c
@@ -745,10 +745,23 @@ static int isi_camera_get_formats(struct soc_camera_device *icd,
 	return formats;
 }
 
-/* Called with .host_lock held */
 static int isi_camera_add_device(struct soc_camera_device *icd)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+	dev_dbg(icd->parent, "Atmel ISI Camera driver attached to camera %d\n",
+		 icd->devnum);
+
+	return 0;
+}
+
+static void isi_camera_remove_device(struct soc_camera_device *icd)
+{
+	dev_dbg(icd->parent, "Atmel ISI Camera driver detached from camera %d\n",
+		 icd->devnum);
+}
+
+/* Called with .host_lock held */
+static int isi_camera_clock_start(struct soc_camera_host *ici)
+{
 	struct atmel_isi *isi = ici->priv;
 	int ret;
 
@@ -762,21 +775,16 @@ static int isi_camera_add_device(struct soc_camera_device *icd)
 		return ret;
 	}
 
-	dev_dbg(icd->parent, "Atmel ISI Camera driver attached to camera %d\n",
-		 icd->devnum);
 	return 0;
 }
+
 /* Called with .host_lock held */
-static void isi_camera_remove_device(struct soc_camera_device *icd)
+static void isi_camera_clock_stop(struct soc_camera_host *ici)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct atmel_isi *isi = ici->priv;
 
 	clk_disable(isi->mck);
 	clk_disable(isi->pclk);
-
-	dev_dbg(icd->parent, "Atmel ISI Camera driver detached from camera %d\n",
-		 icd->devnum);
 }
 
 static unsigned int isi_camera_poll(struct file *file, poll_table *pt)
@@ -880,6 +888,8 @@ static struct soc_camera_host_ops isi_soc_camera_host_ops = {
 	.owner		= THIS_MODULE,
 	.add		= isi_camera_add_device,
 	.remove		= isi_camera_remove_device,
+	.clock_start	= isi_camera_clock_start,
+	.clock_stop	= isi_camera_clock_stop,
 	.set_fmt	= isi_camera_set_fmt,
 	.try_fmt	= isi_camera_try_fmt,
 	.get_formats	= isi_camera_get_formats,
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v10 06/21] mx3-camera: move interface activation and deactivation to clock callbacks
  2013-06-11  8:23 ` Guennadi Liakhovetski
@ 2013-06-11  8:23   ` Guennadi Liakhovetski
  -1 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-11  8:23 UTC (permalink / raw)
  To: linux-media
  Cc: Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer,
	Guennadi Liakhovetski

When adding and removing a client, the mx3-camera driver only activates
and deactivates its camera interface respectively, which doesn't include
any client-specific actions. Move this functionality into .clock_start()
and .clock_stop() callbacks.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
 drivers/media/platform/soc_camera/mx3_camera.c |   35 ++++++++++++++---------
 1 files changed, 21 insertions(+), 14 deletions(-)

diff --git a/drivers/media/platform/soc_camera/mx3_camera.c b/drivers/media/platform/soc_camera/mx3_camera.c
index 71b9b19..1047e3e 100644
--- a/drivers/media/platform/soc_camera/mx3_camera.c
+++ b/drivers/media/platform/soc_camera/mx3_camera.c
@@ -460,8 +460,7 @@ static int mx3_camera_init_videobuf(struct vb2_queue *q,
 }
 
 /* First part of ipu_csi_init_interface() */
-static void mx3_camera_activate(struct mx3_camera_dev *mx3_cam,
-				struct soc_camera_device *icd)
+static void mx3_camera_activate(struct mx3_camera_dev *mx3_cam)
 {
 	u32 conf;
 	long rate;
@@ -505,31 +504,40 @@ static void mx3_camera_activate(struct mx3_camera_dev *mx3_cam,
 
 	clk_prepare_enable(mx3_cam->clk);
 	rate = clk_round_rate(mx3_cam->clk, mx3_cam->mclk);
-	dev_dbg(icd->parent, "Set SENS_CONF to %x, rate %ld\n", conf, rate);
+	dev_dbg(mx3_cam->soc_host.v4l2_dev.dev, "Set SENS_CONF to %x, rate %ld\n", conf, rate);
 	if (rate)
 		clk_set_rate(mx3_cam->clk, rate);
 }
 
-/* Called with .host_lock held */
 static int mx3_camera_add_device(struct soc_camera_device *icd)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+	dev_info(icd->parent, "MX3 Camera driver attached to camera %d\n",
+		 icd->devnum);
+
+	return 0;
+}
+
+static void mx3_camera_remove_device(struct soc_camera_device *icd)
+{
+	dev_info(icd->parent, "MX3 Camera driver detached from camera %d\n",
+		 icd->devnum);
+}
+
+/* Called with .host_lock held */
+static int mx3_camera_clock_start(struct soc_camera_host *ici)
+{
 	struct mx3_camera_dev *mx3_cam = ici->priv;
 
-	mx3_camera_activate(mx3_cam, icd);
+	mx3_camera_activate(mx3_cam);
 
 	mx3_cam->buf_total = 0;
 
-	dev_info(icd->parent, "MX3 Camera driver attached to camera %d\n",
-		 icd->devnum);
-
 	return 0;
 }
 
 /* Called with .host_lock held */
-static void mx3_camera_remove_device(struct soc_camera_device *icd)
+static void mx3_camera_clock_stop(struct soc_camera_host *ici)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct mx3_camera_dev *mx3_cam = ici->priv;
 	struct idmac_channel **ichan = &mx3_cam->idmac_channel[0];
 
@@ -539,9 +547,6 @@ static void mx3_camera_remove_device(struct soc_camera_device *icd)
 	}
 
 	clk_disable_unprepare(mx3_cam->clk);
-
-	dev_info(icd->parent, "MX3 Camera driver detached from camera %d\n",
-		 icd->devnum);
 }
 
 static int test_platform_param(struct mx3_camera_dev *mx3_cam,
@@ -1124,6 +1129,8 @@ static struct soc_camera_host_ops mx3_soc_camera_host_ops = {
 	.owner		= THIS_MODULE,
 	.add		= mx3_camera_add_device,
 	.remove		= mx3_camera_remove_device,
+	.clock_start	= mx3_camera_clock_start,
+	.clock_stop	= mx3_camera_clock_stop,
 	.set_crop	= mx3_camera_set_crop,
 	.set_fmt	= mx3_camera_set_fmt,
 	.try_fmt	= mx3_camera_try_fmt,
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v10 06/21] mx3-camera: move interface activation and deactivation to clock callbacks
@ 2013-06-11  8:23   ` Guennadi Liakhovetski
  0 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-11  8:23 UTC (permalink / raw)
  To: linux-media
  Cc: Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer,
	Guennadi Liakhovetski

When adding and removing a client, the mx3-camera driver only activates
and deactivates its camera interface respectively, which doesn't include
any client-specific actions. Move this functionality into .clock_start()
and .clock_stop() callbacks.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
 drivers/media/platform/soc_camera/mx3_camera.c |   35 ++++++++++++++---------
 1 files changed, 21 insertions(+), 14 deletions(-)

diff --git a/drivers/media/platform/soc_camera/mx3_camera.c b/drivers/media/platform/soc_camera/mx3_camera.c
index 71b9b19..1047e3e 100644
--- a/drivers/media/platform/soc_camera/mx3_camera.c
+++ b/drivers/media/platform/soc_camera/mx3_camera.c
@@ -460,8 +460,7 @@ static int mx3_camera_init_videobuf(struct vb2_queue *q,
 }
 
 /* First part of ipu_csi_init_interface() */
-static void mx3_camera_activate(struct mx3_camera_dev *mx3_cam,
-				struct soc_camera_device *icd)
+static void mx3_camera_activate(struct mx3_camera_dev *mx3_cam)
 {
 	u32 conf;
 	long rate;
@@ -505,31 +504,40 @@ static void mx3_camera_activate(struct mx3_camera_dev *mx3_cam,
 
 	clk_prepare_enable(mx3_cam->clk);
 	rate = clk_round_rate(mx3_cam->clk, mx3_cam->mclk);
-	dev_dbg(icd->parent, "Set SENS_CONF to %x, rate %ld\n", conf, rate);
+	dev_dbg(mx3_cam->soc_host.v4l2_dev.dev, "Set SENS_CONF to %x, rate %ld\n", conf, rate);
 	if (rate)
 		clk_set_rate(mx3_cam->clk, rate);
 }
 
-/* Called with .host_lock held */
 static int mx3_camera_add_device(struct soc_camera_device *icd)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+	dev_info(icd->parent, "MX3 Camera driver attached to camera %d\n",
+		 icd->devnum);
+
+	return 0;
+}
+
+static void mx3_camera_remove_device(struct soc_camera_device *icd)
+{
+	dev_info(icd->parent, "MX3 Camera driver detached from camera %d\n",
+		 icd->devnum);
+}
+
+/* Called with .host_lock held */
+static int mx3_camera_clock_start(struct soc_camera_host *ici)
+{
 	struct mx3_camera_dev *mx3_cam = ici->priv;
 
-	mx3_camera_activate(mx3_cam, icd);
+	mx3_camera_activate(mx3_cam);
 
 	mx3_cam->buf_total = 0;
 
-	dev_info(icd->parent, "MX3 Camera driver attached to camera %d\n",
-		 icd->devnum);
-
 	return 0;
 }
 
 /* Called with .host_lock held */
-static void mx3_camera_remove_device(struct soc_camera_device *icd)
+static void mx3_camera_clock_stop(struct soc_camera_host *ici)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct mx3_camera_dev *mx3_cam = ici->priv;
 	struct idmac_channel **ichan = &mx3_cam->idmac_channel[0];
 
@@ -539,9 +547,6 @@ static void mx3_camera_remove_device(struct soc_camera_device *icd)
 	}
 
 	clk_disable_unprepare(mx3_cam->clk);
-
-	dev_info(icd->parent, "MX3 Camera driver detached from camera %d\n",
-		 icd->devnum);
 }
 
 static int test_platform_param(struct mx3_camera_dev *mx3_cam,
@@ -1124,6 +1129,8 @@ static struct soc_camera_host_ops mx3_soc_camera_host_ops = {
 	.owner		= THIS_MODULE,
 	.add		= mx3_camera_add_device,
 	.remove		= mx3_camera_remove_device,
+	.clock_start	= mx3_camera_clock_start,
+	.clock_stop	= mx3_camera_clock_stop,
 	.set_crop	= mx3_camera_set_crop,
 	.set_fmt	= mx3_camera_set_fmt,
 	.try_fmt	= mx3_camera_try_fmt,
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v10 07/21] mx2-camera: move interface activation and deactivation to clock callbacks
  2013-06-11  8:23 ` Guennadi Liakhovetski
@ 2013-06-11  8:23   ` Guennadi Liakhovetski
  -1 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-11  8:23 UTC (permalink / raw)
  To: linux-media
  Cc: Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer,
	Guennadi Liakhovetski

When adding and removing a client, the mx2-camera driver only activates
and deactivates its camera interface respectively, which doesn't include
any client-specific actions. Move this functionality into .clock_start()
and .clock_stop() callbacks.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
 drivers/media/platform/soc_camera/mx2_camera.c |   28 +++++++++++++++--------
 1 files changed, 18 insertions(+), 10 deletions(-)

diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c
index 772e071..45a0276 100644
--- a/drivers/media/platform/soc_camera/mx2_camera.c
+++ b/drivers/media/platform/soc_camera/mx2_camera.c
@@ -412,13 +412,26 @@ static void mx2_camera_deactivate(struct mx2_camera_dev *pcdev)
 	writel(0, pcdev->base_emma + PRP_CNTL);
 }
 
+static int mx2_camera_add_device(struct soc_camera_device *icd)
+{
+	dev_info(icd->parent, "Camera driver attached to camera %d\n",
+		 icd->devnum);
+
+	return 0;
+}
+
+static void mx2_camera_remove_device(struct soc_camera_device *icd)
+{
+	dev_info(icd->parent, "Camera driver detached from camera %d\n",
+		 icd->devnum);
+}
+
 /*
  * The following two functions absolutely depend on the fact, that
  * there can be only one camera on mx2 camera sensor interface
  */
-static int mx2_camera_add_device(struct soc_camera_device *icd)
+static int mx2_camera_clock_start(struct soc_camera_host *ici)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct mx2_camera_dev *pcdev = ici->priv;
 	int ret;
 	u32 csicr1;
@@ -439,9 +452,6 @@ static int mx2_camera_add_device(struct soc_camera_device *icd)
 
 	pcdev->frame_count = 0;
 
-	dev_info(icd->parent, "Camera driver attached to camera %d\n",
-		 icd->devnum);
-
 	return 0;
 
 exit_csi_ahb:
@@ -450,14 +460,10 @@ exit_csi_ahb:
 	return ret;
 }
 
-static void mx2_camera_remove_device(struct soc_camera_device *icd)
+static void mx2_camera_clock_stop(struct soc_camera_host *ici)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct mx2_camera_dev *pcdev = ici->priv;
 
-	dev_info(icd->parent, "Camera driver detached from camera %d\n",
-		 icd->devnum);
-
 	mx2_camera_deactivate(pcdev);
 }
 
@@ -1271,6 +1277,8 @@ static struct soc_camera_host_ops mx2_soc_camera_host_ops = {
 	.owner		= THIS_MODULE,
 	.add		= mx2_camera_add_device,
 	.remove		= mx2_camera_remove_device,
+	.clock_start	= mx2_camera_clock_start,
+	.clock_stop	= mx2_camera_clock_stop,
 	.set_fmt	= mx2_camera_set_fmt,
 	.set_crop	= mx2_camera_set_crop,
 	.get_formats	= mx2_camera_get_formats,
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v10 07/21] mx2-camera: move interface activation and deactivation to clock callbacks
@ 2013-06-11  8:23   ` Guennadi Liakhovetski
  0 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-11  8:23 UTC (permalink / raw)
  To: linux-media
  Cc: Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer,
	Guennadi Liakhovetski

When adding and removing a client, the mx2-camera driver only activates
and deactivates its camera interface respectively, which doesn't include
any client-specific actions. Move this functionality into .clock_start()
and .clock_stop() callbacks.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
 drivers/media/platform/soc_camera/mx2_camera.c |   28 +++++++++++++++--------
 1 files changed, 18 insertions(+), 10 deletions(-)

diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c
index 772e071..45a0276 100644
--- a/drivers/media/platform/soc_camera/mx2_camera.c
+++ b/drivers/media/platform/soc_camera/mx2_camera.c
@@ -412,13 +412,26 @@ static void mx2_camera_deactivate(struct mx2_camera_dev *pcdev)
 	writel(0, pcdev->base_emma + PRP_CNTL);
 }
 
+static int mx2_camera_add_device(struct soc_camera_device *icd)
+{
+	dev_info(icd->parent, "Camera driver attached to camera %d\n",
+		 icd->devnum);
+
+	return 0;
+}
+
+static void mx2_camera_remove_device(struct soc_camera_device *icd)
+{
+	dev_info(icd->parent, "Camera driver detached from camera %d\n",
+		 icd->devnum);
+}
+
 /*
  * The following two functions absolutely depend on the fact, that
  * there can be only one camera on mx2 camera sensor interface
  */
-static int mx2_camera_add_device(struct soc_camera_device *icd)
+static int mx2_camera_clock_start(struct soc_camera_host *ici)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct mx2_camera_dev *pcdev = ici->priv;
 	int ret;
 	u32 csicr1;
@@ -439,9 +452,6 @@ static int mx2_camera_add_device(struct soc_camera_device *icd)
 
 	pcdev->frame_count = 0;
 
-	dev_info(icd->parent, "Camera driver attached to camera %d\n",
-		 icd->devnum);
-
 	return 0;
 
 exit_csi_ahb:
@@ -450,14 +460,10 @@ exit_csi_ahb:
 	return ret;
 }
 
-static void mx2_camera_remove_device(struct soc_camera_device *icd)
+static void mx2_camera_clock_stop(struct soc_camera_host *ici)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct mx2_camera_dev *pcdev = ici->priv;
 
-	dev_info(icd->parent, "Camera driver detached from camera %d\n",
-		 icd->devnum);
-
 	mx2_camera_deactivate(pcdev);
 }
 
@@ -1271,6 +1277,8 @@ static struct soc_camera_host_ops mx2_soc_camera_host_ops = {
 	.owner		= THIS_MODULE,
 	.add		= mx2_camera_add_device,
 	.remove		= mx2_camera_remove_device,
+	.clock_start	= mx2_camera_clock_start,
+	.clock_stop	= mx2_camera_clock_stop,
 	.set_fmt	= mx2_camera_set_fmt,
 	.set_crop	= mx2_camera_set_crop,
 	.get_formats	= mx2_camera_get_formats,
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v10 08/21] mx1-camera: move interface activation and deactivation to clock callbacks
  2013-06-11  8:23 ` Guennadi Liakhovetski
@ 2013-06-11  8:23   ` Guennadi Liakhovetski
  -1 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-11  8:23 UTC (permalink / raw)
  To: linux-media
  Cc: Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer,
	Guennadi Liakhovetski

When adding and removing a client, the mx1-camera driver only activates
and deactivates its camera interface respectively, which doesn't include
any client-specific actions. Move this functionality into .clock_start()
and .clock_stop() callbacks.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
 drivers/media/platform/soc_camera/mx1_camera.c |   32 +++++++++++++++---------
 1 files changed, 20 insertions(+), 12 deletions(-)

diff --git a/drivers/media/platform/soc_camera/mx1_camera.c b/drivers/media/platform/soc_camera/mx1_camera.c
index 5f9ec8e..fea3e61 100644
--- a/drivers/media/platform/soc_camera/mx1_camera.c
+++ b/drivers/media/platform/soc_camera/mx1_camera.c
@@ -399,7 +399,7 @@ static void mx1_camera_activate(struct mx1_camera_dev *pcdev)
 {
 	unsigned int csicr1 = CSICR1_EN;
 
-	dev_dbg(pcdev->soc_host.icd->parent, "Activate device\n");
+	dev_dbg(pcdev->soc_host.v4l2_dev.dev, "Activate device\n");
 
 	clk_prepare_enable(pcdev->clk);
 
@@ -415,7 +415,7 @@ static void mx1_camera_activate(struct mx1_camera_dev *pcdev)
 
 static void mx1_camera_deactivate(struct mx1_camera_dev *pcdev)
 {
-	dev_dbg(pcdev->soc_host.icd->parent, "Deactivate device\n");
+	dev_dbg(pcdev->soc_host.v4l2_dev.dev, "Deactivate device\n");
 
 	/* Disable all CSI interface */
 	__raw_writel(0x00, pcdev->base + CSICR1);
@@ -423,26 +423,35 @@ static void mx1_camera_deactivate(struct mx1_camera_dev *pcdev)
 	clk_disable_unprepare(pcdev->clk);
 }
 
+static int mx1_camera_add_device(struct soc_camera_device *icd)
+{
+	dev_info(icd->parent, "MX1 Camera driver attached to camera %d\n",
+		 icd->devnum);
+
+	return 0;
+}
+
+static void mx1_camera_remove_device(struct soc_camera_device *icd)
+{
+	dev_info(icd->parent, "MX1 Camera driver detached from camera %d\n",
+		 icd->devnum);
+}
+
 /*
  * The following two functions absolutely depend on the fact, that
  * there can be only one camera on i.MX1/i.MXL camera sensor interface
  */
-static int mx1_camera_add_device(struct soc_camera_device *icd)
+static int mx1_camera_clock_start(struct soc_camera_host *ici)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct mx1_camera_dev *pcdev = ici->priv;
 
-	dev_info(icd->parent, "MX1 Camera driver attached to camera %d\n",
-		 icd->devnum);
-
 	mx1_camera_activate(pcdev);
 
 	return 0;
 }
 
-static void mx1_camera_remove_device(struct soc_camera_device *icd)
+static void mx1_camera_clock_stop(struct soc_camera_host *ici)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct mx1_camera_dev *pcdev = ici->priv;
 	unsigned int csicr1;
 
@@ -453,9 +462,6 @@ static void mx1_camera_remove_device(struct soc_camera_device *icd)
 	/* Stop DMA engine */
 	imx_dma_disable(pcdev->dma_chan);
 
-	dev_info(icd->parent, "MX1 Camera driver detached from camera %d\n",
-		 icd->devnum);
-
 	mx1_camera_deactivate(pcdev);
 }
 
@@ -669,6 +675,8 @@ static struct soc_camera_host_ops mx1_soc_camera_host_ops = {
 	.owner		= THIS_MODULE,
 	.add		= mx1_camera_add_device,
 	.remove		= mx1_camera_remove_device,
+	.clock_start	= mx1_camera_clock_start,
+	.clock_stop	= mx1_camera_clock_stop,
 	.set_bus_param	= mx1_camera_set_bus_param,
 	.set_fmt	= mx1_camera_set_fmt,
 	.try_fmt	= mx1_camera_try_fmt,
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v10 08/21] mx1-camera: move interface activation and deactivation to clock callbacks
@ 2013-06-11  8:23   ` Guennadi Liakhovetski
  0 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-11  8:23 UTC (permalink / raw)
  To: linux-media
  Cc: Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer,
	Guennadi Liakhovetski

When adding and removing a client, the mx1-camera driver only activates
and deactivates its camera interface respectively, which doesn't include
any client-specific actions. Move this functionality into .clock_start()
and .clock_stop() callbacks.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
 drivers/media/platform/soc_camera/mx1_camera.c |   32 +++++++++++++++---------
 1 files changed, 20 insertions(+), 12 deletions(-)

diff --git a/drivers/media/platform/soc_camera/mx1_camera.c b/drivers/media/platform/soc_camera/mx1_camera.c
index 5f9ec8e..fea3e61 100644
--- a/drivers/media/platform/soc_camera/mx1_camera.c
+++ b/drivers/media/platform/soc_camera/mx1_camera.c
@@ -399,7 +399,7 @@ static void mx1_camera_activate(struct mx1_camera_dev *pcdev)
 {
 	unsigned int csicr1 = CSICR1_EN;
 
-	dev_dbg(pcdev->soc_host.icd->parent, "Activate device\n");
+	dev_dbg(pcdev->soc_host.v4l2_dev.dev, "Activate device\n");
 
 	clk_prepare_enable(pcdev->clk);
 
@@ -415,7 +415,7 @@ static void mx1_camera_activate(struct mx1_camera_dev *pcdev)
 
 static void mx1_camera_deactivate(struct mx1_camera_dev *pcdev)
 {
-	dev_dbg(pcdev->soc_host.icd->parent, "Deactivate device\n");
+	dev_dbg(pcdev->soc_host.v4l2_dev.dev, "Deactivate device\n");
 
 	/* Disable all CSI interface */
 	__raw_writel(0x00, pcdev->base + CSICR1);
@@ -423,26 +423,35 @@ static void mx1_camera_deactivate(struct mx1_camera_dev *pcdev)
 	clk_disable_unprepare(pcdev->clk);
 }
 
+static int mx1_camera_add_device(struct soc_camera_device *icd)
+{
+	dev_info(icd->parent, "MX1 Camera driver attached to camera %d\n",
+		 icd->devnum);
+
+	return 0;
+}
+
+static void mx1_camera_remove_device(struct soc_camera_device *icd)
+{
+	dev_info(icd->parent, "MX1 Camera driver detached from camera %d\n",
+		 icd->devnum);
+}
+
 /*
  * The following two functions absolutely depend on the fact, that
  * there can be only one camera on i.MX1/i.MXL camera sensor interface
  */
-static int mx1_camera_add_device(struct soc_camera_device *icd)
+static int mx1_camera_clock_start(struct soc_camera_host *ici)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct mx1_camera_dev *pcdev = ici->priv;
 
-	dev_info(icd->parent, "MX1 Camera driver attached to camera %d\n",
-		 icd->devnum);
-
 	mx1_camera_activate(pcdev);
 
 	return 0;
 }
 
-static void mx1_camera_remove_device(struct soc_camera_device *icd)
+static void mx1_camera_clock_stop(struct soc_camera_host *ici)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct mx1_camera_dev *pcdev = ici->priv;
 	unsigned int csicr1;
 
@@ -453,9 +462,6 @@ static void mx1_camera_remove_device(struct soc_camera_device *icd)
 	/* Stop DMA engine */
 	imx_dma_disable(pcdev->dma_chan);
 
-	dev_info(icd->parent, "MX1 Camera driver detached from camera %d\n",
-		 icd->devnum);
-
 	mx1_camera_deactivate(pcdev);
 }
 
@@ -669,6 +675,8 @@ static struct soc_camera_host_ops mx1_soc_camera_host_ops = {
 	.owner		= THIS_MODULE,
 	.add		= mx1_camera_add_device,
 	.remove		= mx1_camera_remove_device,
+	.clock_start	= mx1_camera_clock_start,
+	.clock_stop	= mx1_camera_clock_stop,
 	.set_bus_param	= mx1_camera_set_bus_param,
 	.set_fmt	= mx1_camera_set_fmt,
 	.try_fmt	= mx1_camera_try_fmt,
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v10 09/21] sh-mobile-ceu-camera: move interface activation and deactivation to clock callback
  2013-06-11  8:23 ` Guennadi Liakhovetski
@ 2013-06-11  8:23   ` Guennadi Liakhovetski
  -1 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-11  8:23 UTC (permalink / raw)
  To: linux-media
  Cc: Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer,
	Guennadi Liakhovetski

When adding and removing a client, the sh-mobile-ceu-camera driver activates
and, respectively, deactivates its camera interface and, if necessary, the
CSI2 controller. Only handling of the CSI2 interface is client-specific and
is only needed, when a data-exchange with the client is taking place. Move
the rest to .clock_start() and .clock_stop() callbacks.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
 .../platform/soc_camera/sh_mobile_ceu_camera.c     |   58 ++++++++++++--------
 1 files changed, 35 insertions(+), 23 deletions(-)

diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
index 5b7d8e1..9037472 100644
--- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
+++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
@@ -162,7 +162,6 @@ static u32 ceu_read(struct sh_mobile_ceu_dev *priv, unsigned long reg_offs)
 static int sh_mobile_ceu_soft_reset(struct sh_mobile_ceu_dev *pcdev)
 {
 	int i, success = 0;
-	struct soc_camera_device *icd = pcdev->ici.icd;
 
 	ceu_write(pcdev, CAPSR, 1 << 16); /* reset */
 
@@ -186,7 +185,7 @@ static int sh_mobile_ceu_soft_reset(struct sh_mobile_ceu_dev *pcdev)
 
 
 	if (2 != success) {
-		dev_warn(icd->pdev, "soft reset time out\n");
+		dev_warn(pcdev->ici.v4l2_dev.dev, "soft reset time out\n");
 		return -EIO;
 	}
 
@@ -543,35 +542,21 @@ static struct v4l2_subdev *find_csi2(struct sh_mobile_ceu_dev *pcdev)
 	return NULL;
 }
 
-/* Called with .host_lock held */
 static int sh_mobile_ceu_add_device(struct soc_camera_device *icd)
 {
 	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct sh_mobile_ceu_dev *pcdev = ici->priv;
-	struct v4l2_subdev *csi2_sd;
+	struct v4l2_subdev *csi2_sd = find_csi2(pcdev);
 	int ret;
 
-	dev_info(icd->parent,
-		 "SuperH Mobile CEU driver attached to camera %d\n",
-		 icd->devnum);
-
-	pm_runtime_get_sync(ici->v4l2_dev.dev);
-
-	pcdev->buf_total = 0;
-
-	ret = sh_mobile_ceu_soft_reset(pcdev);
-
-	csi2_sd = find_csi2(pcdev);
 	if (csi2_sd) {
 		csi2_sd->grp_id = soc_camera_grp_id(icd);
 		v4l2_set_subdev_hostdata(csi2_sd, icd);
 	}
 
 	ret = v4l2_subdev_call(csi2_sd, core, s_power, 1);
-	if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) {
-		pm_runtime_put(ici->v4l2_dev.dev);
+	if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
 		return ret;
-	}
 
 	/*
 	 * -ENODEV is special: either csi2_sd = NULL or the CSI-2 driver
@@ -580,19 +565,48 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd)
 	if (ret = -ENODEV && csi2_sd)
 		csi2_sd->grp_id = 0;
 
+	dev_info(icd->parent,
+		 "SuperH Mobile CEU driver attached to camera %d\n",
+		 icd->devnum);
+
 	return 0;
 }
 
-/* Called with .host_lock held */
 static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd)
 {
 	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct sh_mobile_ceu_dev *pcdev = ici->priv;
 	struct v4l2_subdev *csi2_sd = find_csi2(pcdev);
 
+	dev_info(icd->parent,
+		 "SuperH Mobile CEU driver detached from camera %d\n",
+		 icd->devnum);
+
 	v4l2_subdev_call(csi2_sd, core, s_power, 0);
 	if (csi2_sd)
 		csi2_sd->grp_id = 0;
+}
+
+/* Called with .host_lock held */
+static int sh_mobile_ceu_clock_start(struct soc_camera_host *ici)
+{
+	struct sh_mobile_ceu_dev *pcdev = ici->priv;
+	int ret;
+
+	pm_runtime_get_sync(ici->v4l2_dev.dev);
+
+	pcdev->buf_total = 0;
+
+	ret = sh_mobile_ceu_soft_reset(pcdev);
+
+	return 0;
+}
+
+/* Called with .host_lock held */
+static void sh_mobile_ceu_clock_stop(struct soc_camera_host *ici)
+{
+	struct sh_mobile_ceu_dev *pcdev = ici->priv;
+
 	/* disable capture, disable interrupts */
 	ceu_write(pcdev, CEIER, 0);
 	sh_mobile_ceu_soft_reset(pcdev);
@@ -607,10 +621,6 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd)
 	spin_unlock_irq(&pcdev->lock);
 
 	pm_runtime_put(ici->v4l2_dev.dev);
-
-	dev_info(icd->parent,
-		 "SuperH Mobile CEU driver detached from camera %d\n",
-		 icd->devnum);
 }
 
 /*
@@ -2027,6 +2037,8 @@ static struct soc_camera_host_ops sh_mobile_ceu_host_ops = {
 	.owner		= THIS_MODULE,
 	.add		= sh_mobile_ceu_add_device,
 	.remove		= sh_mobile_ceu_remove_device,
+	.clock_start	= sh_mobile_ceu_clock_start,
+	.clock_stop	= sh_mobile_ceu_clock_stop,
 	.get_formats	= sh_mobile_ceu_get_formats,
 	.put_formats	= sh_mobile_ceu_put_formats,
 	.get_crop	= sh_mobile_ceu_get_crop,
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v10 09/21] sh-mobile-ceu-camera: move interface activation and deactivation to clock callbacks
@ 2013-06-11  8:23   ` Guennadi Liakhovetski
  0 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-11  8:23 UTC (permalink / raw)
  To: linux-media
  Cc: Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer,
	Guennadi Liakhovetski

When adding and removing a client, the sh-mobile-ceu-camera driver activates
and, respectively, deactivates its camera interface and, if necessary, the
CSI2 controller. Only handling of the CSI2 interface is client-specific and
is only needed, when a data-exchange with the client is taking place. Move
the rest to .clock_start() and .clock_stop() callbacks.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
 .../platform/soc_camera/sh_mobile_ceu_camera.c     |   58 ++++++++++++--------
 1 files changed, 35 insertions(+), 23 deletions(-)

diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
index 5b7d8e1..9037472 100644
--- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
+++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
@@ -162,7 +162,6 @@ static u32 ceu_read(struct sh_mobile_ceu_dev *priv, unsigned long reg_offs)
 static int sh_mobile_ceu_soft_reset(struct sh_mobile_ceu_dev *pcdev)
 {
 	int i, success = 0;
-	struct soc_camera_device *icd = pcdev->ici.icd;
 
 	ceu_write(pcdev, CAPSR, 1 << 16); /* reset */
 
@@ -186,7 +185,7 @@ static int sh_mobile_ceu_soft_reset(struct sh_mobile_ceu_dev *pcdev)
 
 
 	if (2 != success) {
-		dev_warn(icd->pdev, "soft reset time out\n");
+		dev_warn(pcdev->ici.v4l2_dev.dev, "soft reset time out\n");
 		return -EIO;
 	}
 
@@ -543,35 +542,21 @@ static struct v4l2_subdev *find_csi2(struct sh_mobile_ceu_dev *pcdev)
 	return NULL;
 }
 
-/* Called with .host_lock held */
 static int sh_mobile_ceu_add_device(struct soc_camera_device *icd)
 {
 	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct sh_mobile_ceu_dev *pcdev = ici->priv;
-	struct v4l2_subdev *csi2_sd;
+	struct v4l2_subdev *csi2_sd = find_csi2(pcdev);
 	int ret;
 
-	dev_info(icd->parent,
-		 "SuperH Mobile CEU driver attached to camera %d\n",
-		 icd->devnum);
-
-	pm_runtime_get_sync(ici->v4l2_dev.dev);
-
-	pcdev->buf_total = 0;
-
-	ret = sh_mobile_ceu_soft_reset(pcdev);
-
-	csi2_sd = find_csi2(pcdev);
 	if (csi2_sd) {
 		csi2_sd->grp_id = soc_camera_grp_id(icd);
 		v4l2_set_subdev_hostdata(csi2_sd, icd);
 	}
 
 	ret = v4l2_subdev_call(csi2_sd, core, s_power, 1);
-	if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) {
-		pm_runtime_put(ici->v4l2_dev.dev);
+	if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
 		return ret;
-	}
 
 	/*
 	 * -ENODEV is special: either csi2_sd == NULL or the CSI-2 driver
@@ -580,19 +565,48 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd)
 	if (ret == -ENODEV && csi2_sd)
 		csi2_sd->grp_id = 0;
 
+	dev_info(icd->parent,
+		 "SuperH Mobile CEU driver attached to camera %d\n",
+		 icd->devnum);
+
 	return 0;
 }
 
-/* Called with .host_lock held */
 static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd)
 {
 	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct sh_mobile_ceu_dev *pcdev = ici->priv;
 	struct v4l2_subdev *csi2_sd = find_csi2(pcdev);
 
+	dev_info(icd->parent,
+		 "SuperH Mobile CEU driver detached from camera %d\n",
+		 icd->devnum);
+
 	v4l2_subdev_call(csi2_sd, core, s_power, 0);
 	if (csi2_sd)
 		csi2_sd->grp_id = 0;
+}
+
+/* Called with .host_lock held */
+static int sh_mobile_ceu_clock_start(struct soc_camera_host *ici)
+{
+	struct sh_mobile_ceu_dev *pcdev = ici->priv;
+	int ret;
+
+	pm_runtime_get_sync(ici->v4l2_dev.dev);
+
+	pcdev->buf_total = 0;
+
+	ret = sh_mobile_ceu_soft_reset(pcdev);
+
+	return 0;
+}
+
+/* Called with .host_lock held */
+static void sh_mobile_ceu_clock_stop(struct soc_camera_host *ici)
+{
+	struct sh_mobile_ceu_dev *pcdev = ici->priv;
+
 	/* disable capture, disable interrupts */
 	ceu_write(pcdev, CEIER, 0);
 	sh_mobile_ceu_soft_reset(pcdev);
@@ -607,10 +621,6 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd)
 	spin_unlock_irq(&pcdev->lock);
 
 	pm_runtime_put(ici->v4l2_dev.dev);
-
-	dev_info(icd->parent,
-		 "SuperH Mobile CEU driver detached from camera %d\n",
-		 icd->devnum);
 }
 
 /*
@@ -2027,6 +2037,8 @@ static struct soc_camera_host_ops sh_mobile_ceu_host_ops = {
 	.owner		= THIS_MODULE,
 	.add		= sh_mobile_ceu_add_device,
 	.remove		= sh_mobile_ceu_remove_device,
+	.clock_start	= sh_mobile_ceu_clock_start,
+	.clock_stop	= sh_mobile_ceu_clock_stop,
 	.get_formats	= sh_mobile_ceu_get_formats,
 	.put_formats	= sh_mobile_ceu_put_formats,
 	.get_crop	= sh_mobile_ceu_get_crop,
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v10 10/21] soc-camera: make .clock_{start,stop} compulsory, .add / .remove optional
  2013-06-11  8:23 ` Guennadi Liakhovetski
@ 2013-06-11  8:23   ` Guennadi Liakhovetski
  -1 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-11  8:23 UTC (permalink / raw)
  To: linux-media
  Cc: Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer,
	Guennadi Liakhovetski

All existing soc-camera host drivers use .clock_start() and .clock_stop()
callbacks to activate and deactivate their camera interfaces, whereas
.add() and .remove() callbacks are usually dummy. Make the former two
compulsory and the latter two optional.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
 drivers/media/platform/soc_camera/soc_camera.c |   27 +++++++++++------------
 1 files changed, 13 insertions(+), 14 deletions(-)

diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c
index df90565..e503f03 100644
--- a/drivers/media/platform/soc_camera/soc_camera.c
+++ b/drivers/media/platform/soc_camera/soc_camera.c
@@ -513,23 +513,22 @@ static int soc_camera_add_device(struct soc_camera_device *icd)
 	if (ici->icd)
 		return -EBUSY;
 
-	if (ici->ops->clock_start) {
-		ret = ici->ops->clock_start(ici);
+	ret = ici->ops->clock_start(ici);
+	if (ret < 0)
+		return ret;
+
+	if (ici->ops->add) {
+		ret = ici->ops->add(icd);
 		if (ret < 0)
-			return ret;
+			goto eadd;
 	}
 
-	ret = ici->ops->add(icd);
-	if (ret < 0)
-		goto eadd;
-
 	ici->icd = icd;
 
 	return 0;
 
 eadd:
-	if (ici->ops->clock_stop)
-		ici->ops->clock_stop(ici);
+	ici->ops->clock_stop(ici);
 	return ret;
 }
 
@@ -540,9 +539,9 @@ static void soc_camera_remove_device(struct soc_camera_device *icd)
 	if (WARN_ON(icd != ici->icd))
 		return;
 
-	ici->ops->remove(icd);
-	if (ici->ops->clock_stop)
-		ici->ops->clock_stop(ici);
+	if (ici->ops->remove)
+		ici->ops->remove(icd);
+	ici->ops->clock_stop(ici);
 	ici->icd = NULL;
 }
 
@@ -1413,8 +1412,8 @@ int soc_camera_host_register(struct soc_camera_host *ici)
 	    ((!ici->ops->init_videobuf ||
 	      !ici->ops->reqbufs) &&
 	     !ici->ops->init_videobuf2) ||
-	    !ici->ops->add ||
-	    !ici->ops->remove ||
+	    !ici->ops->clock_start ||
+	    !ici->ops->clock_stop ||
 	    !ici->ops->poll ||
 	    !ici->v4l2_dev.dev)
 		return -EINVAL;
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v10 10/21] soc-camera: make .clock_{start,stop} compulsory, .add / .remove optional
@ 2013-06-11  8:23   ` Guennadi Liakhovetski
  0 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-11  8:23 UTC (permalink / raw)
  To: linux-media
  Cc: Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer,
	Guennadi Liakhovetski

All existing soc-camera host drivers use .clock_start() and .clock_stop()
callbacks to activate and deactivate their camera interfaces, whereas
.add() and .remove() callbacks are usually dummy. Make the former two
compulsory and the latter two optional.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
 drivers/media/platform/soc_camera/soc_camera.c |   27 +++++++++++------------
 1 files changed, 13 insertions(+), 14 deletions(-)

diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c
index df90565..e503f03 100644
--- a/drivers/media/platform/soc_camera/soc_camera.c
+++ b/drivers/media/platform/soc_camera/soc_camera.c
@@ -513,23 +513,22 @@ static int soc_camera_add_device(struct soc_camera_device *icd)
 	if (ici->icd)
 		return -EBUSY;
 
-	if (ici->ops->clock_start) {
-		ret = ici->ops->clock_start(ici);
+	ret = ici->ops->clock_start(ici);
+	if (ret < 0)
+		return ret;
+
+	if (ici->ops->add) {
+		ret = ici->ops->add(icd);
 		if (ret < 0)
-			return ret;
+			goto eadd;
 	}
 
-	ret = ici->ops->add(icd);
-	if (ret < 0)
-		goto eadd;
-
 	ici->icd = icd;
 
 	return 0;
 
 eadd:
-	if (ici->ops->clock_stop)
-		ici->ops->clock_stop(ici);
+	ici->ops->clock_stop(ici);
 	return ret;
 }
 
@@ -540,9 +539,9 @@ static void soc_camera_remove_device(struct soc_camera_device *icd)
 	if (WARN_ON(icd != ici->icd))
 		return;
 
-	ici->ops->remove(icd);
-	if (ici->ops->clock_stop)
-		ici->ops->clock_stop(ici);
+	if (ici->ops->remove)
+		ici->ops->remove(icd);
+	ici->ops->clock_stop(ici);
 	ici->icd = NULL;
 }
 
@@ -1413,8 +1412,8 @@ int soc_camera_host_register(struct soc_camera_host *ici)
 	    ((!ici->ops->init_videobuf ||
 	      !ici->ops->reqbufs) &&
 	     !ici->ops->init_videobuf2) ||
-	    !ici->ops->add ||
-	    !ici->ops->remove ||
+	    !ici->ops->clock_start ||
+	    !ici->ops->clock_stop ||
 	    !ici->ops->poll ||
 	    !ici->v4l2_dev.dev)
 		return -EINVAL;
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v10 11/21] soc-camera: don't attach the client to the host during probing
  2013-06-11  8:23 ` Guennadi Liakhovetski
@ 2013-06-11  8:23   ` Guennadi Liakhovetski
  -1 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-11  8:23 UTC (permalink / raw)
  To: linux-media
  Cc: Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer,
	Guennadi Liakhovetski

During client probing we only have to turn on the host's clock, no need to
actually attach the client to the host.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
 drivers/media/platform/soc_camera/soc_camera.c |    6 +++---
 1 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c
index e503f03..ac98bcb 100644
--- a/drivers/media/platform/soc_camera/soc_camera.c
+++ b/drivers/media/platform/soc_camera/soc_camera.c
@@ -1207,7 +1207,7 @@ static int soc_camera_probe(struct soc_camera_device *icd)
 		ssdd->reset(icd->pdev);
 
 	mutex_lock(&ici->host_lock);
-	ret = soc_camera_add_device(icd);
+	ret = ici->ops->clock_start(ici);
 	mutex_unlock(&ici->host_lock);
 	if (ret < 0)
 		goto eadd;
@@ -1280,7 +1280,7 @@ static int soc_camera_probe(struct soc_camera_device *icd)
 		icd->field		= mf.field;
 	}
 
-	soc_camera_remove_device(icd);
+	ici->ops->clock_stop(ici);
 
 	mutex_unlock(&ici->host_lock);
 
@@ -1303,7 +1303,7 @@ eadddev:
 	icd->vdev = NULL;
 evdc:
 	mutex_lock(&ici->host_lock);
-	soc_camera_remove_device(icd);
+	ici->ops->clock_stop(ici);
 	mutex_unlock(&ici->host_lock);
 eadd:
 	v4l2_ctrl_handler_free(&icd->ctrl_handler);
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v10 11/21] soc-camera: don't attach the client to the host during probing
@ 2013-06-11  8:23   ` Guennadi Liakhovetski
  0 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-11  8:23 UTC (permalink / raw)
  To: linux-media
  Cc: Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer,
	Guennadi Liakhovetski

During client probing we only have to turn on the host's clock, no need to
actually attach the client to the host.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
 drivers/media/platform/soc_camera/soc_camera.c |    6 +++---
 1 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c
index e503f03..ac98bcb 100644
--- a/drivers/media/platform/soc_camera/soc_camera.c
+++ b/drivers/media/platform/soc_camera/soc_camera.c
@@ -1207,7 +1207,7 @@ static int soc_camera_probe(struct soc_camera_device *icd)
 		ssdd->reset(icd->pdev);
 
 	mutex_lock(&ici->host_lock);
-	ret = soc_camera_add_device(icd);
+	ret = ici->ops->clock_start(ici);
 	mutex_unlock(&ici->host_lock);
 	if (ret < 0)
 		goto eadd;
@@ -1280,7 +1280,7 @@ static int soc_camera_probe(struct soc_camera_device *icd)
 		icd->field		= mf.field;
 	}
 
-	soc_camera_remove_device(icd);
+	ici->ops->clock_stop(ici);
 
 	mutex_unlock(&ici->host_lock);
 
@@ -1303,7 +1303,7 @@ eadddev:
 	icd->vdev = NULL;
 evdc:
 	mutex_lock(&ici->host_lock);
-	soc_camera_remove_device(icd);
+	ici->ops->clock_stop(ici);
 	mutex_unlock(&ici->host_lock);
 eadd:
 	v4l2_ctrl_handler_free(&icd->ctrl_handler);
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v10 12/21] sh-mobile-ceu-camera: add primitive OF support
  2013-06-11  8:23 ` Guennadi Liakhovetski
@ 2013-06-11  8:23   ` Guennadi Liakhovetski
  -1 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-11  8:23 UTC (permalink / raw)
  To: linux-media
  Cc: Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer,
	Guennadi Liakhovetski

Add an OF hook to sh_mobile_ceu_camera.c, no properties so far. Booting
with DT also requires platform data to be optional.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
 .../platform/soc_camera/sh_mobile_ceu_camera.c     |   33 ++++++++++++++------
 1 files changed, 23 insertions(+), 10 deletions(-)

diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
index 9037472..fcc13d8 100644
--- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
+++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
@@ -27,6 +27,7 @@
 #include <linux/kernel.h>
 #include <linux/mm.h>
 #include <linux/moduleparam.h>
+#include <linux/of.h>
 #include <linux/time.h>
 #include <linux/slab.h>
 #include <linux/device.h>
@@ -118,6 +119,7 @@ struct sh_mobile_ceu_dev {
 
 	enum v4l2_field field;
 	int sequence;
+	unsigned long flags;
 
 	unsigned int image_mode:1;
 	unsigned int is_16bit:1;
@@ -706,7 +708,7 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd)
 	}
 
 	/* CSI2 special configuration */
-	if (pcdev->pdata->csi2) {
+	if (pcdev->csi2_pdev) {
 		in_width = ((in_width - 2) * 2);
 		left_offset *= 2;
 	}
@@ -810,7 +812,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd)
 	/* Make choises, based on platform preferences */
 	if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
 	    (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
-		if (pcdev->pdata->flags & SH_CEU_FLAG_HSYNC_LOW)
+		if (pcdev->flags & SH_CEU_FLAG_HSYNC_LOW)
 			common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
 		else
 			common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
@@ -818,7 +820,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd)
 
 	if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
 	    (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
-		if (pcdev->pdata->flags & SH_CEU_FLAG_VSYNC_LOW)
+		if (pcdev->flags & SH_CEU_FLAG_VSYNC_LOW)
 			common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
 		else
 			common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
@@ -873,11 +875,11 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd)
 	value |= common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW ? 1 << 1 : 0;
 	value |= common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW ? 1 << 0 : 0;
 
-	if (pcdev->pdata->csi2) /* CSI2 mode */
+	if (pcdev->csi2_pdev) /* CSI2 mode */
 		value |= 3 << 12;
 	else if (pcdev->is_16bit)
 		value |= 1 << 12;
-	else if (pcdev->pdata->flags & SH_CEU_FLAG_LOWER_8BIT)
+	else if (pcdev->flags & SH_CEU_FLAG_LOWER_8BIT)
 		value |= 2 << 12;
 
 	ceu_write(pcdev, CAMCR, value);
@@ -1052,7 +1054,7 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int
 		return 0;
 	}
 
-	if (!pcdev->pdata->csi2) {
+	if (!pcdev->pdata || !pcdev->pdata->csi2) {
 		/* Are there any restrictions in the CSI-2 case? */
 		ret = sh_mobile_ceu_try_bus_param(icd, fmt->bits_per_sample);
 		if (ret < 0)
@@ -2107,13 +2109,17 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev)
 	init_completion(&pcdev->complete);
 
 	pcdev->pdata = pdev->dev.platform_data;
-	if (!pcdev->pdata) {
+	if (!pcdev->pdata && !pdev->dev.of_node) {
 		dev_err(&pdev->dev, "CEU platform data not set.\n");
 		return -EINVAL;
 	}
 
-	pcdev->max_width = pcdev->pdata->max_width ? : 2560;
-	pcdev->max_height = pcdev->pdata->max_height ? : 1920;
+	/* TODO: implement per-device bus flags */
+	if (pcdev->pdata) {
+		pcdev->max_width = pcdev->pdata->max_width ? : 2560;
+		pcdev->max_height = pcdev->pdata->max_height ? : 1920;
+		pcdev->flags = pcdev->pdata->flags;
+	}
 
 	base = devm_ioremap_resource(&pdev->dev, res);
 	if (IS_ERR(base))
@@ -2168,7 +2174,7 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev)
 		goto exit_free_ctx;
 
 	/* CSI2 interfacing */
-	csi2 = pcdev->pdata->csi2;
+	csi2 = pcdev->pdata ? pcdev->pdata->csi2 : NULL;
 	if (csi2) {
 		struct platform_device *csi2_pdev  			platform_device_alloc("sh-mobile-csi2", csi2->id);
@@ -2290,10 +2296,17 @@ static const struct dev_pm_ops sh_mobile_ceu_dev_pm_ops = {
 	.runtime_resume = sh_mobile_ceu_runtime_nop,
 };
 
+static const struct of_device_id sh_mobile_ceu_of_match[] = {
+	{ .compatible = "renesas,sh-mobile-ceu" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, sh_mobile_ceu_of_match);
+
 static struct platform_driver sh_mobile_ceu_driver = {
 	.driver		= {
 		.name	= "sh_mobile_ceu",
 		.pm	= &sh_mobile_ceu_dev_pm_ops,
+		.of_match_table = sh_mobile_ceu_of_match,
 	},
 	.probe		= sh_mobile_ceu_probe,
 	.remove		= sh_mobile_ceu_remove,
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v10 12/21] sh-mobile-ceu-camera: add primitive OF support
@ 2013-06-11  8:23   ` Guennadi Liakhovetski
  0 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-11  8:23 UTC (permalink / raw)
  To: linux-media
  Cc: Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer,
	Guennadi Liakhovetski

Add an OF hook to sh_mobile_ceu_camera.c, no properties so far. Booting
with DT also requires platform data to be optional.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
 .../platform/soc_camera/sh_mobile_ceu_camera.c     |   33 ++++++++++++++------
 1 files changed, 23 insertions(+), 10 deletions(-)

diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
index 9037472..fcc13d8 100644
--- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
+++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
@@ -27,6 +27,7 @@
 #include <linux/kernel.h>
 #include <linux/mm.h>
 #include <linux/moduleparam.h>
+#include <linux/of.h>
 #include <linux/time.h>
 #include <linux/slab.h>
 #include <linux/device.h>
@@ -118,6 +119,7 @@ struct sh_mobile_ceu_dev {
 
 	enum v4l2_field field;
 	int sequence;
+	unsigned long flags;
 
 	unsigned int image_mode:1;
 	unsigned int is_16bit:1;
@@ -706,7 +708,7 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd)
 	}
 
 	/* CSI2 special configuration */
-	if (pcdev->pdata->csi2) {
+	if (pcdev->csi2_pdev) {
 		in_width = ((in_width - 2) * 2);
 		left_offset *= 2;
 	}
@@ -810,7 +812,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd)
 	/* Make choises, based on platform preferences */
 	if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
 	    (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
-		if (pcdev->pdata->flags & SH_CEU_FLAG_HSYNC_LOW)
+		if (pcdev->flags & SH_CEU_FLAG_HSYNC_LOW)
 			common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
 		else
 			common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
@@ -818,7 +820,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd)
 
 	if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
 	    (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
-		if (pcdev->pdata->flags & SH_CEU_FLAG_VSYNC_LOW)
+		if (pcdev->flags & SH_CEU_FLAG_VSYNC_LOW)
 			common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
 		else
 			common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
@@ -873,11 +875,11 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd)
 	value |= common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW ? 1 << 1 : 0;
 	value |= common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW ? 1 << 0 : 0;
 
-	if (pcdev->pdata->csi2) /* CSI2 mode */
+	if (pcdev->csi2_pdev) /* CSI2 mode */
 		value |= 3 << 12;
 	else if (pcdev->is_16bit)
 		value |= 1 << 12;
-	else if (pcdev->pdata->flags & SH_CEU_FLAG_LOWER_8BIT)
+	else if (pcdev->flags & SH_CEU_FLAG_LOWER_8BIT)
 		value |= 2 << 12;
 
 	ceu_write(pcdev, CAMCR, value);
@@ -1052,7 +1054,7 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int
 		return 0;
 	}
 
-	if (!pcdev->pdata->csi2) {
+	if (!pcdev->pdata || !pcdev->pdata->csi2) {
 		/* Are there any restrictions in the CSI-2 case? */
 		ret = sh_mobile_ceu_try_bus_param(icd, fmt->bits_per_sample);
 		if (ret < 0)
@@ -2107,13 +2109,17 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev)
 	init_completion(&pcdev->complete);
 
 	pcdev->pdata = pdev->dev.platform_data;
-	if (!pcdev->pdata) {
+	if (!pcdev->pdata && !pdev->dev.of_node) {
 		dev_err(&pdev->dev, "CEU platform data not set.\n");
 		return -EINVAL;
 	}
 
-	pcdev->max_width = pcdev->pdata->max_width ? : 2560;
-	pcdev->max_height = pcdev->pdata->max_height ? : 1920;
+	/* TODO: implement per-device bus flags */
+	if (pcdev->pdata) {
+		pcdev->max_width = pcdev->pdata->max_width ? : 2560;
+		pcdev->max_height = pcdev->pdata->max_height ? : 1920;
+		pcdev->flags = pcdev->pdata->flags;
+	}
 
 	base = devm_ioremap_resource(&pdev->dev, res);
 	if (IS_ERR(base))
@@ -2168,7 +2174,7 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev)
 		goto exit_free_ctx;
 
 	/* CSI2 interfacing */
-	csi2 = pcdev->pdata->csi2;
+	csi2 = pcdev->pdata ? pcdev->pdata->csi2 : NULL;
 	if (csi2) {
 		struct platform_device *csi2_pdev =
 			platform_device_alloc("sh-mobile-csi2", csi2->id);
@@ -2290,10 +2296,17 @@ static const struct dev_pm_ops sh_mobile_ceu_dev_pm_ops = {
 	.runtime_resume = sh_mobile_ceu_runtime_nop,
 };
 
+static const struct of_device_id sh_mobile_ceu_of_match[] = {
+	{ .compatible = "renesas,sh-mobile-ceu" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, sh_mobile_ceu_of_match);
+
 static struct platform_driver sh_mobile_ceu_driver = {
 	.driver		= {
 		.name	= "sh_mobile_ceu",
 		.pm	= &sh_mobile_ceu_dev_pm_ops,
+		.of_match_table = sh_mobile_ceu_of_match,
 	},
 	.probe		= sh_mobile_ceu_probe,
 	.remove		= sh_mobile_ceu_remove,
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v10 13/21] sh-mobile-ceu-driver: support max width and height in DT
  2013-06-11  8:23 ` Guennadi Liakhovetski
@ 2013-06-11  8:23   ` Guennadi Liakhovetski
  -1 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-11  8:23 UTC (permalink / raw)
  To: linux-media
  Cc: Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer,
	Guennadi Liakhovetski

Some CEU implementations have non-standard (larger) maximum supported
width and height values. Add two OF properties to specify them.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
 .../devicetree/bindings/media/sh_mobile_ceu.txt    |   18 +++++++++++++++
 .../platform/soc_camera/sh_mobile_ceu_camera.c     |   23 ++++++++++++++++++-
 2 files changed, 39 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/media/sh_mobile_ceu.txt

diff --git a/Documentation/devicetree/bindings/media/sh_mobile_ceu.txt b/Documentation/devicetree/bindings/media/sh_mobile_ceu.txt
new file mode 100644
index 0000000..1ce4e46
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/sh_mobile_ceu.txt
@@ -0,0 +1,18 @@
+Bindings, specific for the sh_mobile_ceu_camera.c driver:
+ - compatible: Should be "renesas,sh-mobile-ceu"
+ - reg: register base and size
+ - interrupts: the interrupt number
+ - interrupt-parent: the interrupt controller
+ - renesas,max-width: maximum image width, supported on this SoC
+ - renesas,max-height: maximum image height, supported on this SoC
+
+Example:
+
+ceu0: ceu@0xfe910000 {
+	compatible = "renesas,sh-mobile-ceu";
+	reg = <0xfe910000 0xa0>;
+	interrupt-parent = <&intcs>;
+	interrupts = <0x880>;
+	renesas,max-width = <8188>;
+	renesas,max-height = <8188>;
+};
diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
index fcc13d8..b0f0995 100644
--- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
+++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
@@ -2116,11 +2116,30 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev)
 
 	/* TODO: implement per-device bus flags */
 	if (pcdev->pdata) {
-		pcdev->max_width = pcdev->pdata->max_width ? : 2560;
-		pcdev->max_height = pcdev->pdata->max_height ? : 1920;
+		pcdev->max_width = pcdev->pdata->max_width;
+		pcdev->max_height = pcdev->pdata->max_height;
 		pcdev->flags = pcdev->pdata->flags;
 	}
 
+	if (!pcdev->max_width) {
+		unsigned int v;
+		err = of_property_read_u32(pdev->dev.of_node, "renesas,max-width", &v);
+		if (!err)
+			pcdev->max_width = v;
+
+		if (!pcdev->max_width)
+			pcdev->max_width = 2560;
+	}
+	if (!pcdev->max_height) {
+		unsigned int v;
+		err = of_property_read_u32(pdev->dev.of_node, "renesas,max-height", &v);
+		if (!err)
+			pcdev->max_height = v;
+
+		if (!pcdev->max_height)
+			pcdev->max_height = 1920;
+	}
+
 	base = devm_ioremap_resource(&pdev->dev, res);
 	if (IS_ERR(base))
 		return PTR_ERR(base);
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v10 13/21] sh-mobile-ceu-driver: support max width and height in DT
@ 2013-06-11  8:23   ` Guennadi Liakhovetski
  0 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-11  8:23 UTC (permalink / raw)
  To: linux-media
  Cc: Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer,
	Guennadi Liakhovetski

Some CEU implementations have non-standard (larger) maximum supported
width and height values. Add two OF properties to specify them.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
 .../devicetree/bindings/media/sh_mobile_ceu.txt    |   18 +++++++++++++++
 .../platform/soc_camera/sh_mobile_ceu_camera.c     |   23 ++++++++++++++++++-
 2 files changed, 39 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/media/sh_mobile_ceu.txt

diff --git a/Documentation/devicetree/bindings/media/sh_mobile_ceu.txt b/Documentation/devicetree/bindings/media/sh_mobile_ceu.txt
new file mode 100644
index 0000000..1ce4e46
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/sh_mobile_ceu.txt
@@ -0,0 +1,18 @@
+Bindings, specific for the sh_mobile_ceu_camera.c driver:
+ - compatible: Should be "renesas,sh-mobile-ceu"
+ - reg: register base and size
+ - interrupts: the interrupt number
+ - interrupt-parent: the interrupt controller
+ - renesas,max-width: maximum image width, supported on this SoC
+ - renesas,max-height: maximum image height, supported on this SoC
+
+Example:
+
+ceu0: ceu@0xfe910000 {
+	compatible = "renesas,sh-mobile-ceu";
+	reg = <0xfe910000 0xa0>;
+	interrupt-parent = <&intcs>;
+	interrupts = <0x880>;
+	renesas,max-width = <8188>;
+	renesas,max-height = <8188>;
+};
diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
index fcc13d8..b0f0995 100644
--- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
+++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
@@ -2116,11 +2116,30 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev)
 
 	/* TODO: implement per-device bus flags */
 	if (pcdev->pdata) {
-		pcdev->max_width = pcdev->pdata->max_width ? : 2560;
-		pcdev->max_height = pcdev->pdata->max_height ? : 1920;
+		pcdev->max_width = pcdev->pdata->max_width;
+		pcdev->max_height = pcdev->pdata->max_height;
 		pcdev->flags = pcdev->pdata->flags;
 	}
 
+	if (!pcdev->max_width) {
+		unsigned int v;
+		err = of_property_read_u32(pdev->dev.of_node, "renesas,max-width", &v);
+		if (!err)
+			pcdev->max_width = v;
+
+		if (!pcdev->max_width)
+			pcdev->max_width = 2560;
+	}
+	if (!pcdev->max_height) {
+		unsigned int v;
+		err = of_property_read_u32(pdev->dev.of_node, "renesas,max-height", &v);
+		if (!err)
+			pcdev->max_height = v;
+
+		if (!pcdev->max_height)
+			pcdev->max_height = 1920;
+	}
+
 	base = devm_ioremap_resource(&pdev->dev, res);
 	if (IS_ERR(base))
 		return PTR_ERR(base);
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v10 14/21] V4L2: add temporary clock helpers
  2013-06-11  8:23 ` Guennadi Liakhovetski
@ 2013-06-11  8:23   ` Guennadi Liakhovetski
  -1 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-11  8:23 UTC (permalink / raw)
  To: linux-media
  Cc: Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer,
	Guennadi Liakhovetski

Typical video devices like camera sensors require an external clock source.
Many such devices cannot even access their hardware registers without a
running clock. These clock sources should be controlled by their consumers.
This should be performed, using the generic clock framework. Unfortunately
so far only very few systems have been ported to that framework. This patch
adds a set of temporary helpers, mimicking the generic clock API, to V4L2.
Platforms, adopting the clock API, should switch to using it. Eventually
this temporary API should be removed.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---

v10: lock the driver only between enable / disable to avoid a circular 
dependency and allow driver unloading.

 drivers/media/v4l2-core/Makefile   |    2 +-
 drivers/media/v4l2-core/v4l2-clk.c |  242 ++++++++++++++++++++++++++++++++++++
 include/media/v4l2-clk.h           |   54 ++++++++
 3 files changed, 297 insertions(+), 1 deletions(-)
 create mode 100644 drivers/media/v4l2-core/v4l2-clk.c
 create mode 100644 include/media/v4l2-clk.h

diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile
index aa50c46..628c630 100644
--- a/drivers/media/v4l2-core/Makefile
+++ b/drivers/media/v4l2-core/Makefile
@@ -5,7 +5,7 @@
 tuner-objs	:=	tuner-core.o
 
 videodev-objs	:=	v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-fh.o \
-			v4l2-event.o v4l2-ctrls.o v4l2-subdev.o
+			v4l2-event.o v4l2-ctrls.o v4l2-subdev.o v4l2-clk.o
 ifeq ($(CONFIG_COMPAT),y)
   videodev-objs += v4l2-compat-ioctl32.o
 endif
diff --git a/drivers/media/v4l2-core/v4l2-clk.c b/drivers/media/v4l2-core/v4l2-clk.c
new file mode 100644
index 0000000..b67de86
--- /dev/null
+++ b/drivers/media/v4l2-core/v4l2-clk.c
@@ -0,0 +1,242 @@
+/*
+ * V4L2 clock service
+ *
+ * Copyright (C) 2012-2013, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/atomic.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include <media/v4l2-clk.h>
+#include <media/v4l2-subdev.h>
+
+static DEFINE_MUTEX(clk_lock);
+static LIST_HEAD(clk_list);
+
+static struct v4l2_clk *v4l2_clk_find(const char *dev_id, const char *id)
+{
+	struct v4l2_clk *clk;
+
+	list_for_each_entry(clk, &clk_list, list) {
+		if (strcmp(dev_id, clk->dev_id))
+			continue;
+
+		if (!id || !clk->id || !strcmp(clk->id, id))
+			return clk;
+	}
+
+	return ERR_PTR(-ENODEV);
+}
+
+struct v4l2_clk *v4l2_clk_get(struct device *dev, const char *id)
+{
+	struct v4l2_clk *clk;
+
+	mutex_lock(&clk_lock);
+	clk = v4l2_clk_find(dev_name(dev), id);
+
+	if (!IS_ERR(clk))
+		atomic_inc(&clk->use_count);
+	mutex_unlock(&clk_lock);
+
+	return clk;
+}
+EXPORT_SYMBOL(v4l2_clk_get);
+
+void v4l2_clk_put(struct v4l2_clk *clk)
+{
+	struct v4l2_clk *tmp;
+
+	if (IS_ERR(clk))
+		return;
+
+	mutex_lock(&clk_lock);
+
+	list_for_each_entry(tmp, &clk_list, list)
+		if (tmp = clk)
+			atomic_dec(&clk->use_count);
+
+	mutex_unlock(&clk_lock);
+}
+EXPORT_SYMBOL(v4l2_clk_put);
+
+static int v4l2_clk_lock_driver(struct v4l2_clk *clk)
+{
+	struct v4l2_clk *tmp;
+	int ret = -ENODEV;
+
+	mutex_lock(&clk_lock);
+
+	list_for_each_entry(tmp, &clk_list, list)
+		if (tmp = clk) {
+			ret = !try_module_get(clk->ops->owner);
+			if (ret)
+				ret = -EFAULT;
+			break;
+		}
+
+	mutex_unlock(&clk_lock);
+
+	return ret;
+}
+
+static void v4l2_clk_unlock_driver(struct v4l2_clk *clk)
+{
+	module_put(clk->ops->owner);
+}
+
+int v4l2_clk_enable(struct v4l2_clk *clk)
+{
+	int ret = v4l2_clk_lock_driver(clk);
+
+	if (ret < 0)
+		return ret;
+
+	mutex_lock(&clk->lock);
+
+	if (++clk->enable = 1 && clk->ops->enable) {
+		ret = clk->ops->enable(clk);
+		if (ret < 0)
+			clk->enable--;
+	}
+
+	mutex_unlock(&clk->lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(v4l2_clk_enable);
+
+/*
+ * You might Oops if you try to disabled a disabled clock, because then the
+ * driver isn't locked and could have been unloaded by now, so, don't do that
+ */
+void v4l2_clk_disable(struct v4l2_clk *clk)
+{
+	int enable;
+
+	mutex_lock(&clk->lock);
+
+	enable = --clk->enable;
+	if (WARN(enable < 0, "Unbalanced %s() on %s:%s!\n", __func__,
+		 clk->dev_id, clk->id))
+		clk->enable++;
+	else if (!enable && clk->ops->disable)
+		clk->ops->disable(clk);
+
+	mutex_unlock(&clk->lock);
+
+	v4l2_clk_unlock_driver(clk);
+}
+EXPORT_SYMBOL(v4l2_clk_disable);
+
+unsigned long v4l2_clk_get_rate(struct v4l2_clk *clk)
+{
+	int ret = v4l2_clk_lock_driver(clk);
+
+	if (ret < 0)
+		return ret;
+
+	mutex_lock(&clk->lock);
+	if (!clk->ops->get_rate)
+		ret = -ENOSYS;
+	else
+		ret = clk->ops->get_rate(clk);
+	mutex_unlock(&clk->lock);
+
+	v4l2_clk_unlock_driver(clk);
+
+	return ret;
+}
+EXPORT_SYMBOL(v4l2_clk_get_rate);
+
+int v4l2_clk_set_rate(struct v4l2_clk *clk, unsigned long rate)
+{
+	int ret = v4l2_clk_lock_driver(clk);
+
+	if (ret < 0)
+		return ret;
+
+	mutex_lock(&clk->lock);
+	if (!clk->ops->set_rate)
+		ret = -ENOSYS;
+	else
+		ret = clk->ops->set_rate(clk, rate);
+	mutex_unlock(&clk->lock);
+
+	v4l2_clk_unlock_driver(clk);
+
+	return ret;
+}
+EXPORT_SYMBOL(v4l2_clk_set_rate);
+
+struct v4l2_clk *v4l2_clk_register(const struct v4l2_clk_ops *ops,
+				   const char *dev_id,
+				   const char *id, void *priv)
+{
+	struct v4l2_clk *clk;
+	int ret;
+
+	if (!ops || !dev_id)
+		return ERR_PTR(-EINVAL);
+
+	clk = kzalloc(sizeof(struct v4l2_clk), GFP_KERNEL);
+	if (!clk)
+		return ERR_PTR(-ENOMEM);
+
+	clk->id = kstrdup(id, GFP_KERNEL);
+	clk->dev_id = kstrdup(dev_id, GFP_KERNEL);
+	if ((id && !clk->id) || !clk->dev_id) {
+		ret = -ENOMEM;
+		goto ealloc;
+	}
+	clk->ops = ops;
+	clk->priv = priv;
+	atomic_set(&clk->use_count, 0);
+	mutex_init(&clk->lock);
+
+	mutex_lock(&clk_lock);
+	if (!IS_ERR(v4l2_clk_find(dev_id, id))) {
+		mutex_unlock(&clk_lock);
+		ret = -EEXIST;
+		goto eexist;
+	}
+	list_add_tail(&clk->list, &clk_list);
+	mutex_unlock(&clk_lock);
+
+	return clk;
+
+eexist:
+ealloc:
+	kfree(clk->id);
+	kfree(clk->dev_id);
+	kfree(clk);
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL(v4l2_clk_register);
+
+void v4l2_clk_unregister(struct v4l2_clk *clk)
+{
+	if (WARN(atomic_read(&clk->use_count),
+		 "%s(): Refusing to unregister ref-counted %s:%s clock!\n",
+		 __func__, clk->dev_id, clk->id))
+		return;
+
+	mutex_lock(&clk_lock);
+	list_del(&clk->list);
+	mutex_unlock(&clk_lock);
+
+	kfree(clk->id);
+	kfree(clk->dev_id);
+	kfree(clk);
+}
+EXPORT_SYMBOL(v4l2_clk_unregister);
diff --git a/include/media/v4l2-clk.h b/include/media/v4l2-clk.h
new file mode 100644
index 0000000..0503a90
--- /dev/null
+++ b/include/media/v4l2-clk.h
@@ -0,0 +1,54 @@
+/*
+ * V4L2 clock service
+ *
+ * Copyright (C) 2012-2013, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * ATTENTION: This is a temporary API and it shall be replaced by the generic
+ * clock API, when the latter becomes widely available.
+ */
+
+#ifndef MEDIA_V4L2_CLK_H
+#define MEDIA_V4L2_CLK_H
+
+#include <linux/atomic.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+
+struct module;
+struct device;
+
+struct v4l2_clk {
+	struct list_head list;
+	const struct v4l2_clk_ops *ops;
+	const char *dev_id;
+	const char *id;
+	int enable;
+	struct mutex lock; /* Protect the enable count */
+	atomic_t use_count;
+	void *priv;
+};
+
+struct v4l2_clk_ops {
+	struct module	*owner;
+	int		(*enable)(struct v4l2_clk *clk);
+	void		(*disable)(struct v4l2_clk *clk);
+	unsigned long	(*get_rate)(struct v4l2_clk *clk);
+	int		(*set_rate)(struct v4l2_clk *clk, unsigned long);
+};
+
+struct v4l2_clk *v4l2_clk_register(const struct v4l2_clk_ops *ops,
+				   const char *dev_name,
+				   const char *name, void *priv);
+void v4l2_clk_unregister(struct v4l2_clk *clk);
+struct v4l2_clk *v4l2_clk_get(struct device *dev, const char *id);
+void v4l2_clk_put(struct v4l2_clk *clk);
+int v4l2_clk_enable(struct v4l2_clk *clk);
+void v4l2_clk_disable(struct v4l2_clk *clk);
+unsigned long v4l2_clk_get_rate(struct v4l2_clk *clk);
+int v4l2_clk_set_rate(struct v4l2_clk *clk, unsigned long rate);
+
+#endif
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v10 14/21] V4L2: add temporary clock helpers
@ 2013-06-11  8:23   ` Guennadi Liakhovetski
  0 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-11  8:23 UTC (permalink / raw)
  To: linux-media
  Cc: Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer,
	Guennadi Liakhovetski

Typical video devices like camera sensors require an external clock source.
Many such devices cannot even access their hardware registers without a
running clock. These clock sources should be controlled by their consumers.
This should be performed, using the generic clock framework. Unfortunately
so far only very few systems have been ported to that framework. This patch
adds a set of temporary helpers, mimicking the generic clock API, to V4L2.
Platforms, adopting the clock API, should switch to using it. Eventually
this temporary API should be removed.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---

v10: lock the driver only between enable / disable to avoid a circular 
dependency and allow driver unloading.

 drivers/media/v4l2-core/Makefile   |    2 +-
 drivers/media/v4l2-core/v4l2-clk.c |  242 ++++++++++++++++++++++++++++++++++++
 include/media/v4l2-clk.h           |   54 ++++++++
 3 files changed, 297 insertions(+), 1 deletions(-)
 create mode 100644 drivers/media/v4l2-core/v4l2-clk.c
 create mode 100644 include/media/v4l2-clk.h

diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile
index aa50c46..628c630 100644
--- a/drivers/media/v4l2-core/Makefile
+++ b/drivers/media/v4l2-core/Makefile
@@ -5,7 +5,7 @@
 tuner-objs	:=	tuner-core.o
 
 videodev-objs	:=	v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-fh.o \
-			v4l2-event.o v4l2-ctrls.o v4l2-subdev.o
+			v4l2-event.o v4l2-ctrls.o v4l2-subdev.o v4l2-clk.o
 ifeq ($(CONFIG_COMPAT),y)
   videodev-objs += v4l2-compat-ioctl32.o
 endif
diff --git a/drivers/media/v4l2-core/v4l2-clk.c b/drivers/media/v4l2-core/v4l2-clk.c
new file mode 100644
index 0000000..b67de86
--- /dev/null
+++ b/drivers/media/v4l2-core/v4l2-clk.c
@@ -0,0 +1,242 @@
+/*
+ * V4L2 clock service
+ *
+ * Copyright (C) 2012-2013, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/atomic.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include <media/v4l2-clk.h>
+#include <media/v4l2-subdev.h>
+
+static DEFINE_MUTEX(clk_lock);
+static LIST_HEAD(clk_list);
+
+static struct v4l2_clk *v4l2_clk_find(const char *dev_id, const char *id)
+{
+	struct v4l2_clk *clk;
+
+	list_for_each_entry(clk, &clk_list, list) {
+		if (strcmp(dev_id, clk->dev_id))
+			continue;
+
+		if (!id || !clk->id || !strcmp(clk->id, id))
+			return clk;
+	}
+
+	return ERR_PTR(-ENODEV);
+}
+
+struct v4l2_clk *v4l2_clk_get(struct device *dev, const char *id)
+{
+	struct v4l2_clk *clk;
+
+	mutex_lock(&clk_lock);
+	clk = v4l2_clk_find(dev_name(dev), id);
+
+	if (!IS_ERR(clk))
+		atomic_inc(&clk->use_count);
+	mutex_unlock(&clk_lock);
+
+	return clk;
+}
+EXPORT_SYMBOL(v4l2_clk_get);
+
+void v4l2_clk_put(struct v4l2_clk *clk)
+{
+	struct v4l2_clk *tmp;
+
+	if (IS_ERR(clk))
+		return;
+
+	mutex_lock(&clk_lock);
+
+	list_for_each_entry(tmp, &clk_list, list)
+		if (tmp == clk)
+			atomic_dec(&clk->use_count);
+
+	mutex_unlock(&clk_lock);
+}
+EXPORT_SYMBOL(v4l2_clk_put);
+
+static int v4l2_clk_lock_driver(struct v4l2_clk *clk)
+{
+	struct v4l2_clk *tmp;
+	int ret = -ENODEV;
+
+	mutex_lock(&clk_lock);
+
+	list_for_each_entry(tmp, &clk_list, list)
+		if (tmp == clk) {
+			ret = !try_module_get(clk->ops->owner);
+			if (ret)
+				ret = -EFAULT;
+			break;
+		}
+
+	mutex_unlock(&clk_lock);
+
+	return ret;
+}
+
+static void v4l2_clk_unlock_driver(struct v4l2_clk *clk)
+{
+	module_put(clk->ops->owner);
+}
+
+int v4l2_clk_enable(struct v4l2_clk *clk)
+{
+	int ret = v4l2_clk_lock_driver(clk);
+
+	if (ret < 0)
+		return ret;
+
+	mutex_lock(&clk->lock);
+
+	if (++clk->enable == 1 && clk->ops->enable) {
+		ret = clk->ops->enable(clk);
+		if (ret < 0)
+			clk->enable--;
+	}
+
+	mutex_unlock(&clk->lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(v4l2_clk_enable);
+
+/*
+ * You might Oops if you try to disabled a disabled clock, because then the
+ * driver isn't locked and could have been unloaded by now, so, don't do that
+ */
+void v4l2_clk_disable(struct v4l2_clk *clk)
+{
+	int enable;
+
+	mutex_lock(&clk->lock);
+
+	enable = --clk->enable;
+	if (WARN(enable < 0, "Unbalanced %s() on %s:%s!\n", __func__,
+		 clk->dev_id, clk->id))
+		clk->enable++;
+	else if (!enable && clk->ops->disable)
+		clk->ops->disable(clk);
+
+	mutex_unlock(&clk->lock);
+
+	v4l2_clk_unlock_driver(clk);
+}
+EXPORT_SYMBOL(v4l2_clk_disable);
+
+unsigned long v4l2_clk_get_rate(struct v4l2_clk *clk)
+{
+	int ret = v4l2_clk_lock_driver(clk);
+
+	if (ret < 0)
+		return ret;
+
+	mutex_lock(&clk->lock);
+	if (!clk->ops->get_rate)
+		ret = -ENOSYS;
+	else
+		ret = clk->ops->get_rate(clk);
+	mutex_unlock(&clk->lock);
+
+	v4l2_clk_unlock_driver(clk);
+
+	return ret;
+}
+EXPORT_SYMBOL(v4l2_clk_get_rate);
+
+int v4l2_clk_set_rate(struct v4l2_clk *clk, unsigned long rate)
+{
+	int ret = v4l2_clk_lock_driver(clk);
+
+	if (ret < 0)
+		return ret;
+
+	mutex_lock(&clk->lock);
+	if (!clk->ops->set_rate)
+		ret = -ENOSYS;
+	else
+		ret = clk->ops->set_rate(clk, rate);
+	mutex_unlock(&clk->lock);
+
+	v4l2_clk_unlock_driver(clk);
+
+	return ret;
+}
+EXPORT_SYMBOL(v4l2_clk_set_rate);
+
+struct v4l2_clk *v4l2_clk_register(const struct v4l2_clk_ops *ops,
+				   const char *dev_id,
+				   const char *id, void *priv)
+{
+	struct v4l2_clk *clk;
+	int ret;
+
+	if (!ops || !dev_id)
+		return ERR_PTR(-EINVAL);
+
+	clk = kzalloc(sizeof(struct v4l2_clk), GFP_KERNEL);
+	if (!clk)
+		return ERR_PTR(-ENOMEM);
+
+	clk->id = kstrdup(id, GFP_KERNEL);
+	clk->dev_id = kstrdup(dev_id, GFP_KERNEL);
+	if ((id && !clk->id) || !clk->dev_id) {
+		ret = -ENOMEM;
+		goto ealloc;
+	}
+	clk->ops = ops;
+	clk->priv = priv;
+	atomic_set(&clk->use_count, 0);
+	mutex_init(&clk->lock);
+
+	mutex_lock(&clk_lock);
+	if (!IS_ERR(v4l2_clk_find(dev_id, id))) {
+		mutex_unlock(&clk_lock);
+		ret = -EEXIST;
+		goto eexist;
+	}
+	list_add_tail(&clk->list, &clk_list);
+	mutex_unlock(&clk_lock);
+
+	return clk;
+
+eexist:
+ealloc:
+	kfree(clk->id);
+	kfree(clk->dev_id);
+	kfree(clk);
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL(v4l2_clk_register);
+
+void v4l2_clk_unregister(struct v4l2_clk *clk)
+{
+	if (WARN(atomic_read(&clk->use_count),
+		 "%s(): Refusing to unregister ref-counted %s:%s clock!\n",
+		 __func__, clk->dev_id, clk->id))
+		return;
+
+	mutex_lock(&clk_lock);
+	list_del(&clk->list);
+	mutex_unlock(&clk_lock);
+
+	kfree(clk->id);
+	kfree(clk->dev_id);
+	kfree(clk);
+}
+EXPORT_SYMBOL(v4l2_clk_unregister);
diff --git a/include/media/v4l2-clk.h b/include/media/v4l2-clk.h
new file mode 100644
index 0000000..0503a90
--- /dev/null
+++ b/include/media/v4l2-clk.h
@@ -0,0 +1,54 @@
+/*
+ * V4L2 clock service
+ *
+ * Copyright (C) 2012-2013, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * ATTENTION: This is a temporary API and it shall be replaced by the generic
+ * clock API, when the latter becomes widely available.
+ */
+
+#ifndef MEDIA_V4L2_CLK_H
+#define MEDIA_V4L2_CLK_H
+
+#include <linux/atomic.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+
+struct module;
+struct device;
+
+struct v4l2_clk {
+	struct list_head list;
+	const struct v4l2_clk_ops *ops;
+	const char *dev_id;
+	const char *id;
+	int enable;
+	struct mutex lock; /* Protect the enable count */
+	atomic_t use_count;
+	void *priv;
+};
+
+struct v4l2_clk_ops {
+	struct module	*owner;
+	int		(*enable)(struct v4l2_clk *clk);
+	void		(*disable)(struct v4l2_clk *clk);
+	unsigned long	(*get_rate)(struct v4l2_clk *clk);
+	int		(*set_rate)(struct v4l2_clk *clk, unsigned long);
+};
+
+struct v4l2_clk *v4l2_clk_register(const struct v4l2_clk_ops *ops,
+				   const char *dev_name,
+				   const char *name, void *priv);
+void v4l2_clk_unregister(struct v4l2_clk *clk);
+struct v4l2_clk *v4l2_clk_get(struct device *dev, const char *id);
+void v4l2_clk_put(struct v4l2_clk *clk);
+int v4l2_clk_enable(struct v4l2_clk *clk);
+void v4l2_clk_disable(struct v4l2_clk *clk);
+unsigned long v4l2_clk_get_rate(struct v4l2_clk *clk);
+int v4l2_clk_set_rate(struct v4l2_clk *clk, unsigned long rate);
+
+#endif
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v10 15/21] V4L2: add a device pointer to struct v4l2_subdev
  2013-06-11  8:23 ` Guennadi Liakhovetski
@ 2013-06-11  8:23   ` Guennadi Liakhovetski
  -1 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-11  8:23 UTC (permalink / raw)
  To: linux-media
  Cc: Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer,
	Guennadi Liakhovetski

It is often useful to have simple means to get from a subdevice to the
underlying physical device. This patch adds such a pointer to struct
v4l2_subdev and sets it accordingly in the I2C and SPI cases.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
 drivers/media/v4l2-core/v4l2-common.c |    2 ++
 include/media/v4l2-subdev.h           |    2 ++
 2 files changed, 4 insertions(+), 0 deletions(-)

diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c
index 3fed63f..accfec6 100644
--- a/drivers/media/v4l2-core/v4l2-common.c
+++ b/drivers/media/v4l2-core/v4l2-common.c
@@ -291,6 +291,7 @@ void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client,
 	sd->flags |= V4L2_SUBDEV_FL_IS_I2C;
 	/* the owner is the same as the i2c_client's driver owner */
 	sd->owner = client->driver->driver.owner;
+	sd->dev = &client->dev;
 	/* i2c_client and v4l2_subdev point to one another */
 	v4l2_set_subdevdata(sd, client);
 	i2c_set_clientdata(client, sd);
@@ -426,6 +427,7 @@ void v4l2_spi_subdev_init(struct v4l2_subdev *sd, struct spi_device *spi,
 	sd->flags |= V4L2_SUBDEV_FL_IS_SPI;
 	/* the owner is the same as the spi_device's driver owner */
 	sd->owner = spi->dev.driver->owner;
+	sd->dev = &spi->dev;
 	/* spi_device and v4l2_subdev point to one another */
 	v4l2_set_subdevdata(sd, spi);
 	spi_set_drvdata(spi, sd);
diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h
index 5298d67..d8756fa 100644
--- a/include/media/v4l2-subdev.h
+++ b/include/media/v4l2-subdev.h
@@ -585,6 +585,8 @@ struct v4l2_subdev {
 	void *host_priv;
 	/* subdev device node */
 	struct video_device *devnode;
+	/* pointer to the physical device */
+	struct device *dev;
 };
 
 #define media_entity_to_v4l2_subdev(ent) \
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v10 15/21] V4L2: add a device pointer to struct v4l2_subdev
@ 2013-06-11  8:23   ` Guennadi Liakhovetski
  0 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-11  8:23 UTC (permalink / raw)
  To: linux-media
  Cc: Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer,
	Guennadi Liakhovetski

It is often useful to have simple means to get from a subdevice to the
underlying physical device. This patch adds such a pointer to struct
v4l2_subdev and sets it accordingly in the I2C and SPI cases.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
 drivers/media/v4l2-core/v4l2-common.c |    2 ++
 include/media/v4l2-subdev.h           |    2 ++
 2 files changed, 4 insertions(+), 0 deletions(-)

diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c
index 3fed63f..accfec6 100644
--- a/drivers/media/v4l2-core/v4l2-common.c
+++ b/drivers/media/v4l2-core/v4l2-common.c
@@ -291,6 +291,7 @@ void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client,
 	sd->flags |= V4L2_SUBDEV_FL_IS_I2C;
 	/* the owner is the same as the i2c_client's driver owner */
 	sd->owner = client->driver->driver.owner;
+	sd->dev = &client->dev;
 	/* i2c_client and v4l2_subdev point to one another */
 	v4l2_set_subdevdata(sd, client);
 	i2c_set_clientdata(client, sd);
@@ -426,6 +427,7 @@ void v4l2_spi_subdev_init(struct v4l2_subdev *sd, struct spi_device *spi,
 	sd->flags |= V4L2_SUBDEV_FL_IS_SPI;
 	/* the owner is the same as the spi_device's driver owner */
 	sd->owner = spi->dev.driver->owner;
+	sd->dev = &spi->dev;
 	/* spi_device and v4l2_subdev point to one another */
 	v4l2_set_subdevdata(sd, spi);
 	spi_set_drvdata(spi, sd);
diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h
index 5298d67..d8756fa 100644
--- a/include/media/v4l2-subdev.h
+++ b/include/media/v4l2-subdev.h
@@ -585,6 +585,8 @@ struct v4l2_subdev {
 	void *host_priv;
 	/* subdev device node */
 	struct video_device *devnode;
+	/* pointer to the physical device */
+	struct device *dev;
 };
 
 #define media_entity_to_v4l2_subdev(ent) \
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v10 16/21] V4L2: support asynchronous subdevice registration
  2013-06-11  8:23 ` Guennadi Liakhovetski
@ 2013-06-11  8:23   ` Guennadi Liakhovetski
  -1 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-11  8:23 UTC (permalink / raw)
  To: linux-media
  Cc: Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer,
	Guennadi Liakhovetski

Currently bridge device drivers register devices for all subdevices
synchronously, tupically, during their probing. E.g. if an I2C CMOS sensor
is attached to a video bridge device, the bridge driver will create an I2C
device and wait for the respective I2C driver to probe. This makes linking
of devices straight forward, but this approach cannot be used with
intrinsically asynchronous and unordered device registration systems like
the Flattened Device Tree. To support such systems this patch adds an
asynchronous subdevice registration framework to V4L2. To use it respective
(e.g. I2C) subdevice drivers must register themselves with the framework.
A bridge driver on the other hand must register notification callbacks,
that will be called upon various related events.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---

v10:
  (a) remove dynamic dev[] allocation
  (b) merge struct v4l2_async_hw_info into struct v4l2_async_subdev
  (c) change .bound(), .unbind() prototypes - thanks to Laurent for a patch
  (d) improve struct comment
  (e) remove an unused variable in v4l2_async_unregister_subdev()
  (f) lock the device, when dereferencing dev->driver
  (g) fix a false success in v4l2_async_belongs()

 drivers/media/v4l2-core/Makefile     |    3 +-
 drivers/media/v4l2-core/v4l2-async.c |  280 ++++++++++++++++++++++++++++++++++
 include/media/v4l2-async.h           |  105 +++++++++++++
 include/media/v4l2-subdev.h          |    8 +
 4 files changed, 395 insertions(+), 1 deletions(-)
 create mode 100644 drivers/media/v4l2-core/v4l2-async.c
 create mode 100644 include/media/v4l2-async.h

diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile
index 628c630..4c33b8d6 100644
--- a/drivers/media/v4l2-core/Makefile
+++ b/drivers/media/v4l2-core/Makefile
@@ -5,7 +5,8 @@
 tuner-objs	:=	tuner-core.o
 
 videodev-objs	:=	v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-fh.o \
-			v4l2-event.o v4l2-ctrls.o v4l2-subdev.o v4l2-clk.o
+			v4l2-event.o v4l2-ctrls.o v4l2-subdev.o v4l2-clk.o \
+			v4l2-async.o
 ifeq ($(CONFIG_COMPAT),y)
   videodev-objs += v4l2-compat-ioctl32.o
 endif
diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
new file mode 100644
index 0000000..3590ccc
--- /dev/null
+++ b/drivers/media/v4l2-core/v4l2-async.c
@@ -0,0 +1,280 @@
+/*
+ * V4L2 asynchronous subdevice registration API
+ *
+ * Copyright (C) 2012-2013, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include <media/v4l2-async.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+static bool match_i2c(struct device *dev, struct v4l2_async_subdev *asd)
+{
+	struct i2c_client *client = i2c_verify_client(dev);
+	return client &&
+		asd->bus_type = V4L2_ASYNC_BUS_I2C &&
+		asd->match.i2c.adapter_id = client->adapter->nr &&
+		asd->match.i2c.address = client->addr;
+}
+
+static bool match_platform(struct device *dev, struct v4l2_async_subdev *asd)
+{
+	return asd->bus_type = V4L2_ASYNC_BUS_PLATFORM &&
+		!strcmp(asd->match.platform.name, dev_name(dev));
+}
+
+static LIST_HEAD(subdev_list);
+static LIST_HEAD(notifier_list);
+static DEFINE_MUTEX(list_lock);
+
+static struct v4l2_async_subdev *v4l2_async_belongs(struct v4l2_async_notifier *notifier,
+						    struct v4l2_async_subdev_list *asdl)
+{
+	struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl);
+	struct v4l2_async_subdev *asd;
+	bool (*match)(struct device *,
+		      struct v4l2_async_subdev *);
+
+	list_for_each_entry(asd, &notifier->waiting, list) {
+		/* bus_type has been verified valid before */
+		switch (asd->bus_type) {
+		case V4L2_ASYNC_BUS_CUSTOM:
+			match = asd->match.custom.match;
+			if (!match)
+				/* Match always */
+				return asd;
+			break;
+		case V4L2_ASYNC_BUS_PLATFORM:
+			match = match_platform;
+			break;
+		case V4L2_ASYNC_BUS_I2C:
+			match = match_i2c;
+			break;
+		default:
+			/* Cannot happen, unless someone breaks us */
+			WARN_ON(true);
+			return NULL;
+		}
+
+		/* match cannot be NULL here */
+		if (match(sd->dev, asd))
+			return asd;
+	}
+
+	return NULL;
+}
+
+static int v4l2_async_test_notify(struct v4l2_async_notifier *notifier,
+				  struct v4l2_async_subdev_list *asdl,
+				  struct v4l2_async_subdev *asd)
+{
+	struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl);
+	int ret;
+
+	/* Remove from the waiting list */
+	list_del(&asd->list);
+	asdl->asd = asd;
+	asdl->notifier = notifier;
+
+	if (notifier->bound) {
+		ret = notifier->bound(notifier, sd, asd);
+		if (ret < 0)
+			return ret;
+	}
+	/* Move from the global subdevice list to notifier's done */
+	list_move(&asdl->list, &notifier->done);
+
+	ret = v4l2_device_register_subdev(notifier->v4l2_dev, sd);
+	if (ret < 0) {
+		if (notifier->unbind)
+			notifier->unbind(notifier, sd, asd);
+		return ret;
+	}
+
+	if (list_empty(&notifier->waiting) && notifier->complete)
+		return notifier->complete(notifier);
+
+	return 0;
+}
+
+static void v4l2_async_cleanup(struct v4l2_async_subdev_list *asdl)
+{
+	struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl);
+
+	v4l2_device_unregister_subdev(sd);
+	/* Subdevice driver will reprobe and put asdl back onto the list */
+	list_del_init(&asdl->list);
+	asdl->asd = NULL;
+	sd->dev = NULL;
+}
+
+int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
+				 struct v4l2_async_notifier *notifier)
+{
+	struct v4l2_async_subdev_list *asdl, *tmp;
+	struct v4l2_async_subdev *asd;
+	int i;
+
+	if (!notifier->subdev_num || notifier->subdev_num > V4L2_MAX_SUBDEVS)
+		return -EINVAL;
+
+	notifier->v4l2_dev = v4l2_dev;
+	INIT_LIST_HEAD(&notifier->waiting);
+	INIT_LIST_HEAD(&notifier->done);
+
+	for (i = 0; i < notifier->subdev_num; i++) {
+		asd = notifier->subdev[i];
+
+		switch (asd->bus_type) {
+		case V4L2_ASYNC_BUS_CUSTOM:
+		case V4L2_ASYNC_BUS_PLATFORM:
+		case V4L2_ASYNC_BUS_I2C:
+			break;
+		default:
+			dev_err(notifier->v4l2_dev ? notifier->v4l2_dev->dev : NULL,
+				"Invalid bus-type %u on %p\n",
+				asd->bus_type, asd);
+			return -EINVAL;
+		}
+		list_add_tail(&asd->list, &notifier->waiting);
+	}
+
+	mutex_lock(&list_lock);
+
+	/* Keep also completed notifiers on the list */
+	list_add(&notifier->list, &notifier_list);
+
+	list_for_each_entry_safe(asdl, tmp, &subdev_list, list) {
+		int ret;
+
+		asd = v4l2_async_belongs(notifier, asdl);
+		if (!asd)
+			continue;
+
+		ret = v4l2_async_test_notify(notifier, asdl, asd);
+		if (ret < 0) {
+			mutex_unlock(&list_lock);
+			return ret;
+		}
+	}
+
+	mutex_unlock(&list_lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(v4l2_async_notifier_register);
+
+void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier)
+{
+	struct v4l2_async_subdev_list *asdl, *tmp;
+	unsigned int notif_n_subdev = notifier->subdev_num;
+	unsigned int n_subdev = min(notif_n_subdev, V4L2_MAX_SUBDEVS);
+	struct device *dev[n_subdev];
+	int i = 0;
+
+	mutex_lock(&list_lock);
+
+	list_del(&notifier->list);
+
+	list_for_each_entry_safe(asdl, tmp, &notifier->done, list) {
+		struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl);
+
+		dev[i] = get_device(sd->dev);
+
+		v4l2_async_cleanup(asdl);
+
+		/* If we handled USB devices, we'd have to lock the parent too */
+		device_release_driver(dev[i++]);
+
+		if (notifier->unbind)
+			notifier->unbind(notifier, sd, sd->asdl.asd);
+	}
+
+	mutex_unlock(&list_lock);
+
+	while (i--) {
+		struct device *d = dev[i];
+
+		if (d && device_attach(d) < 0) {
+			const char *name = "(none)";
+			int lock = device_trylock(d);
+
+			if (lock && d->driver)
+				name = d->driver->name;
+			dev_err(d, "Failed to re-probe to %s\n", name);
+			if (lock)
+				device_unlock(d);
+		}
+		put_device(d);
+	}
+	/*
+	 * Don't care about the waiting list, it is initialised and populated
+	 * upon notifier registration.
+	 */
+}
+EXPORT_SYMBOL(v4l2_async_notifier_unregister);
+
+int v4l2_async_register_subdev(struct v4l2_subdev *sd)
+{
+	struct v4l2_async_subdev_list *asdl = &sd->asdl;
+	struct v4l2_async_notifier *notifier;
+
+	mutex_lock(&list_lock);
+
+	INIT_LIST_HEAD(&asdl->list);
+
+	list_for_each_entry(notifier, &notifier_list, list) {
+		struct v4l2_async_subdev *asd = v4l2_async_belongs(notifier, asdl);
+		if (asd) {
+			int ret = v4l2_async_test_notify(notifier, asdl, asd);
+			mutex_unlock(&list_lock);
+			return ret;
+		}
+	}
+
+	/* None matched, wait for hot-plugging */
+	list_add(&asdl->list, &subdev_list);
+
+	mutex_unlock(&list_lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(v4l2_async_register_subdev);
+
+void v4l2_async_unregister_subdev(struct v4l2_subdev *sd)
+{
+	struct v4l2_async_subdev_list *asdl = &sd->asdl;
+	struct v4l2_async_notifier *notifier = asdl->notifier;
+
+	if (!asdl->asd) {
+		if (!list_empty(&asdl->list))
+			v4l2_async_cleanup(asdl);
+		return;
+	}
+
+	mutex_lock(&list_lock);
+
+	list_add(&asdl->asd->list, &notifier->waiting);
+
+	v4l2_async_cleanup(asdl);
+
+	if (notifier->unbind)
+		notifier->unbind(notifier, sd, sd->asdl.asd);
+
+	mutex_unlock(&list_lock);
+}
+EXPORT_SYMBOL(v4l2_async_unregister_subdev);
diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h
new file mode 100644
index 0000000..12bd39c
--- /dev/null
+++ b/include/media/v4l2-async.h
@@ -0,0 +1,105 @@
+/*
+ * V4L2 asynchronous subdevice registration API
+ *
+ * Copyright (C) 2012-2013, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef V4L2_ASYNC_H
+#define V4L2_ASYNC_H
+
+#include <linux/list.h>
+#include <linux/mutex.h>
+
+struct device;
+struct v4l2_device;
+struct v4l2_subdev;
+struct v4l2_async_notifier;
+
+/* A random max subdevice number, used to allocate an array on stack */
+#define V4L2_MAX_SUBDEVS 128U
+
+enum v4l2_async_bus_type {
+	V4L2_ASYNC_BUS_CUSTOM,
+	V4L2_ASYNC_BUS_PLATFORM,
+	V4L2_ASYNC_BUS_I2C,
+};
+
+/**
+ * struct v4l2_async_subdev - sub-device descriptor, as known to a bridge
+ * @bus_type:	subdevice bus type to select the appropriate matching method
+ * @match:	union of per-bus type matching data sets
+ * @list:	used to link struct v4l2_async_subdev objects, waiting to be
+ *		probed, to a notifier->waiting list
+ */
+struct v4l2_async_subdev {
+	enum v4l2_async_bus_type bus_type;
+	union {
+		struct {
+			const char *name;
+		} platform;
+		struct {
+			int adapter_id;
+			unsigned short address;
+		} i2c;
+		struct {
+			bool (*match)(struct device *,
+				      struct v4l2_async_subdev *);
+			void *priv;
+		} custom;
+	} match;
+
+	/* v4l2-async core private: not to be used by drivers */
+	struct list_head list;
+};
+
+/**
+ * v4l2_async_subdev_list - provided by subdevices
+ * @list:	links struct v4l2_async_subdev_list objects to a global list
+ *		before probing, and onto notifier->done after probing
+ * @asd:	pointer to respective struct v4l2_async_subdev
+ * @notifier:	pointer to managing notifier
+ */
+struct v4l2_async_subdev_list {
+	struct list_head list;
+	struct v4l2_async_subdev *asd;
+	struct v4l2_async_notifier *notifier;
+};
+
+/**
+ * v4l2_async_notifier - v4l2_device notifier data
+ * @subdev_num:	number of subdevices
+ * @subdev:	array of pointers to subdevices
+ * @v4l2_dev:	pointer to struct v4l2_device
+ * @waiting:	list of struct v4l2_async_subdev, waiting for their drivers
+ * @done:	list of struct v4l2_async_subdev_list, already probed
+ * @list:	member in a global list of notifiers
+ * @bound:	a subdevice driver has successfully probed one of subdevices
+ * @complete:	all subdevices have been probed successfully
+ * @unbind:	a subdevice is leaving
+ */
+struct v4l2_async_notifier {
+	unsigned int subdev_num;
+	struct v4l2_async_subdev **subdev;
+	struct v4l2_device *v4l2_dev;
+	struct list_head waiting;
+	struct list_head done;
+	struct list_head list;
+	int (*bound)(struct v4l2_async_notifier *notifier,
+		     struct v4l2_subdev *subdev,
+		     struct v4l2_async_subdev *asd);
+	int (*complete)(struct v4l2_async_notifier *notifier);
+	void (*unbind)(struct v4l2_async_notifier *notifier,
+		       struct v4l2_subdev *subdev,
+		       struct v4l2_async_subdev *asd);
+};
+
+int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
+				 struct v4l2_async_notifier *notifier);
+void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier);
+int v4l2_async_register_subdev(struct v4l2_subdev *sd);
+void v4l2_async_unregister_subdev(struct v4l2_subdev *sd);
+#endif
diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h
index d8756fa..21174af 100644
--- a/include/media/v4l2-subdev.h
+++ b/include/media/v4l2-subdev.h
@@ -24,6 +24,7 @@
 #include <linux/types.h>
 #include <linux/v4l2-subdev.h>
 #include <media/media-entity.h>
+#include <media/v4l2-async.h>
 #include <media/v4l2-common.h>
 #include <media/v4l2-dev.h>
 #include <media/v4l2-fh.h>
@@ -587,8 +588,15 @@ struct v4l2_subdev {
 	struct video_device *devnode;
 	/* pointer to the physical device */
 	struct device *dev;
+	struct v4l2_async_subdev_list asdl;
 };
 
+static inline struct v4l2_subdev *v4l2_async_to_subdev(
+			struct v4l2_async_subdev_list *asdl)
+{
+	return container_of(asdl, struct v4l2_subdev, asdl);
+}
+
 #define media_entity_to_v4l2_subdev(ent) \
 	container_of(ent, struct v4l2_subdev, entity)
 #define vdev_to_v4l2_subdev(vdev) \
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v10 16/21] V4L2: support asynchronous subdevice registration
@ 2013-06-11  8:23   ` Guennadi Liakhovetski
  0 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-11  8:23 UTC (permalink / raw)
  To: linux-media
  Cc: Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer,
	Guennadi Liakhovetski

Currently bridge device drivers register devices for all subdevices
synchronously, tupically, during their probing. E.g. if an I2C CMOS sensor
is attached to a video bridge device, the bridge driver will create an I2C
device and wait for the respective I2C driver to probe. This makes linking
of devices straight forward, but this approach cannot be used with
intrinsically asynchronous and unordered device registration systems like
the Flattened Device Tree. To support such systems this patch adds an
asynchronous subdevice registration framework to V4L2. To use it respective
(e.g. I2C) subdevice drivers must register themselves with the framework.
A bridge driver on the other hand must register notification callbacks,
that will be called upon various related events.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---

v10:
  (a) remove dynamic dev[] allocation
  (b) merge struct v4l2_async_hw_info into struct v4l2_async_subdev
  (c) change .bound(), .unbind() prototypes - thanks to Laurent for a patch
  (d) improve struct comment
  (e) remove an unused variable in v4l2_async_unregister_subdev()
  (f) lock the device, when dereferencing dev->driver
  (g) fix a false success in v4l2_async_belongs()

 drivers/media/v4l2-core/Makefile     |    3 +-
 drivers/media/v4l2-core/v4l2-async.c |  280 ++++++++++++++++++++++++++++++++++
 include/media/v4l2-async.h           |  105 +++++++++++++
 include/media/v4l2-subdev.h          |    8 +
 4 files changed, 395 insertions(+), 1 deletions(-)
 create mode 100644 drivers/media/v4l2-core/v4l2-async.c
 create mode 100644 include/media/v4l2-async.h

diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile
index 628c630..4c33b8d6 100644
--- a/drivers/media/v4l2-core/Makefile
+++ b/drivers/media/v4l2-core/Makefile
@@ -5,7 +5,8 @@
 tuner-objs	:=	tuner-core.o
 
 videodev-objs	:=	v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-fh.o \
-			v4l2-event.o v4l2-ctrls.o v4l2-subdev.o v4l2-clk.o
+			v4l2-event.o v4l2-ctrls.o v4l2-subdev.o v4l2-clk.o \
+			v4l2-async.o
 ifeq ($(CONFIG_COMPAT),y)
   videodev-objs += v4l2-compat-ioctl32.o
 endif
diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
new file mode 100644
index 0000000..3590ccc
--- /dev/null
+++ b/drivers/media/v4l2-core/v4l2-async.c
@@ -0,0 +1,280 @@
+/*
+ * V4L2 asynchronous subdevice registration API
+ *
+ * Copyright (C) 2012-2013, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include <media/v4l2-async.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+static bool match_i2c(struct device *dev, struct v4l2_async_subdev *asd)
+{
+	struct i2c_client *client = i2c_verify_client(dev);
+	return client &&
+		asd->bus_type == V4L2_ASYNC_BUS_I2C &&
+		asd->match.i2c.adapter_id == client->adapter->nr &&
+		asd->match.i2c.address == client->addr;
+}
+
+static bool match_platform(struct device *dev, struct v4l2_async_subdev *asd)
+{
+	return asd->bus_type == V4L2_ASYNC_BUS_PLATFORM &&
+		!strcmp(asd->match.platform.name, dev_name(dev));
+}
+
+static LIST_HEAD(subdev_list);
+static LIST_HEAD(notifier_list);
+static DEFINE_MUTEX(list_lock);
+
+static struct v4l2_async_subdev *v4l2_async_belongs(struct v4l2_async_notifier *notifier,
+						    struct v4l2_async_subdev_list *asdl)
+{
+	struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl);
+	struct v4l2_async_subdev *asd;
+	bool (*match)(struct device *,
+		      struct v4l2_async_subdev *);
+
+	list_for_each_entry(asd, &notifier->waiting, list) {
+		/* bus_type has been verified valid before */
+		switch (asd->bus_type) {
+		case V4L2_ASYNC_BUS_CUSTOM:
+			match = asd->match.custom.match;
+			if (!match)
+				/* Match always */
+				return asd;
+			break;
+		case V4L2_ASYNC_BUS_PLATFORM:
+			match = match_platform;
+			break;
+		case V4L2_ASYNC_BUS_I2C:
+			match = match_i2c;
+			break;
+		default:
+			/* Cannot happen, unless someone breaks us */
+			WARN_ON(true);
+			return NULL;
+		}
+
+		/* match cannot be NULL here */
+		if (match(sd->dev, asd))
+			return asd;
+	}
+
+	return NULL;
+}
+
+static int v4l2_async_test_notify(struct v4l2_async_notifier *notifier,
+				  struct v4l2_async_subdev_list *asdl,
+				  struct v4l2_async_subdev *asd)
+{
+	struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl);
+	int ret;
+
+	/* Remove from the waiting list */
+	list_del(&asd->list);
+	asdl->asd = asd;
+	asdl->notifier = notifier;
+
+	if (notifier->bound) {
+		ret = notifier->bound(notifier, sd, asd);
+		if (ret < 0)
+			return ret;
+	}
+	/* Move from the global subdevice list to notifier's done */
+	list_move(&asdl->list, &notifier->done);
+
+	ret = v4l2_device_register_subdev(notifier->v4l2_dev, sd);
+	if (ret < 0) {
+		if (notifier->unbind)
+			notifier->unbind(notifier, sd, asd);
+		return ret;
+	}
+
+	if (list_empty(&notifier->waiting) && notifier->complete)
+		return notifier->complete(notifier);
+
+	return 0;
+}
+
+static void v4l2_async_cleanup(struct v4l2_async_subdev_list *asdl)
+{
+	struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl);
+
+	v4l2_device_unregister_subdev(sd);
+	/* Subdevice driver will reprobe and put asdl back onto the list */
+	list_del_init(&asdl->list);
+	asdl->asd = NULL;
+	sd->dev = NULL;
+}
+
+int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
+				 struct v4l2_async_notifier *notifier)
+{
+	struct v4l2_async_subdev_list *asdl, *tmp;
+	struct v4l2_async_subdev *asd;
+	int i;
+
+	if (!notifier->subdev_num || notifier->subdev_num > V4L2_MAX_SUBDEVS)
+		return -EINVAL;
+
+	notifier->v4l2_dev = v4l2_dev;
+	INIT_LIST_HEAD(&notifier->waiting);
+	INIT_LIST_HEAD(&notifier->done);
+
+	for (i = 0; i < notifier->subdev_num; i++) {
+		asd = notifier->subdev[i];
+
+		switch (asd->bus_type) {
+		case V4L2_ASYNC_BUS_CUSTOM:
+		case V4L2_ASYNC_BUS_PLATFORM:
+		case V4L2_ASYNC_BUS_I2C:
+			break;
+		default:
+			dev_err(notifier->v4l2_dev ? notifier->v4l2_dev->dev : NULL,
+				"Invalid bus-type %u on %p\n",
+				asd->bus_type, asd);
+			return -EINVAL;
+		}
+		list_add_tail(&asd->list, &notifier->waiting);
+	}
+
+	mutex_lock(&list_lock);
+
+	/* Keep also completed notifiers on the list */
+	list_add(&notifier->list, &notifier_list);
+
+	list_for_each_entry_safe(asdl, tmp, &subdev_list, list) {
+		int ret;
+
+		asd = v4l2_async_belongs(notifier, asdl);
+		if (!asd)
+			continue;
+
+		ret = v4l2_async_test_notify(notifier, asdl, asd);
+		if (ret < 0) {
+			mutex_unlock(&list_lock);
+			return ret;
+		}
+	}
+
+	mutex_unlock(&list_lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(v4l2_async_notifier_register);
+
+void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier)
+{
+	struct v4l2_async_subdev_list *asdl, *tmp;
+	unsigned int notif_n_subdev = notifier->subdev_num;
+	unsigned int n_subdev = min(notif_n_subdev, V4L2_MAX_SUBDEVS);
+	struct device *dev[n_subdev];
+	int i = 0;
+
+	mutex_lock(&list_lock);
+
+	list_del(&notifier->list);
+
+	list_for_each_entry_safe(asdl, tmp, &notifier->done, list) {
+		struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl);
+
+		dev[i] = get_device(sd->dev);
+
+		v4l2_async_cleanup(asdl);
+
+		/* If we handled USB devices, we'd have to lock the parent too */
+		device_release_driver(dev[i++]);
+
+		if (notifier->unbind)
+			notifier->unbind(notifier, sd, sd->asdl.asd);
+	}
+
+	mutex_unlock(&list_lock);
+
+	while (i--) {
+		struct device *d = dev[i];
+
+		if (d && device_attach(d) < 0) {
+			const char *name = "(none)";
+			int lock = device_trylock(d);
+
+			if (lock && d->driver)
+				name = d->driver->name;
+			dev_err(d, "Failed to re-probe to %s\n", name);
+			if (lock)
+				device_unlock(d);
+		}
+		put_device(d);
+	}
+	/*
+	 * Don't care about the waiting list, it is initialised and populated
+	 * upon notifier registration.
+	 */
+}
+EXPORT_SYMBOL(v4l2_async_notifier_unregister);
+
+int v4l2_async_register_subdev(struct v4l2_subdev *sd)
+{
+	struct v4l2_async_subdev_list *asdl = &sd->asdl;
+	struct v4l2_async_notifier *notifier;
+
+	mutex_lock(&list_lock);
+
+	INIT_LIST_HEAD(&asdl->list);
+
+	list_for_each_entry(notifier, &notifier_list, list) {
+		struct v4l2_async_subdev *asd = v4l2_async_belongs(notifier, asdl);
+		if (asd) {
+			int ret = v4l2_async_test_notify(notifier, asdl, asd);
+			mutex_unlock(&list_lock);
+			return ret;
+		}
+	}
+
+	/* None matched, wait for hot-plugging */
+	list_add(&asdl->list, &subdev_list);
+
+	mutex_unlock(&list_lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(v4l2_async_register_subdev);
+
+void v4l2_async_unregister_subdev(struct v4l2_subdev *sd)
+{
+	struct v4l2_async_subdev_list *asdl = &sd->asdl;
+	struct v4l2_async_notifier *notifier = asdl->notifier;
+
+	if (!asdl->asd) {
+		if (!list_empty(&asdl->list))
+			v4l2_async_cleanup(asdl);
+		return;
+	}
+
+	mutex_lock(&list_lock);
+
+	list_add(&asdl->asd->list, &notifier->waiting);
+
+	v4l2_async_cleanup(asdl);
+
+	if (notifier->unbind)
+		notifier->unbind(notifier, sd, sd->asdl.asd);
+
+	mutex_unlock(&list_lock);
+}
+EXPORT_SYMBOL(v4l2_async_unregister_subdev);
diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h
new file mode 100644
index 0000000..12bd39c
--- /dev/null
+++ b/include/media/v4l2-async.h
@@ -0,0 +1,105 @@
+/*
+ * V4L2 asynchronous subdevice registration API
+ *
+ * Copyright (C) 2012-2013, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef V4L2_ASYNC_H
+#define V4L2_ASYNC_H
+
+#include <linux/list.h>
+#include <linux/mutex.h>
+
+struct device;
+struct v4l2_device;
+struct v4l2_subdev;
+struct v4l2_async_notifier;
+
+/* A random max subdevice number, used to allocate an array on stack */
+#define V4L2_MAX_SUBDEVS 128U
+
+enum v4l2_async_bus_type {
+	V4L2_ASYNC_BUS_CUSTOM,
+	V4L2_ASYNC_BUS_PLATFORM,
+	V4L2_ASYNC_BUS_I2C,
+};
+
+/**
+ * struct v4l2_async_subdev - sub-device descriptor, as known to a bridge
+ * @bus_type:	subdevice bus type to select the appropriate matching method
+ * @match:	union of per-bus type matching data sets
+ * @list:	used to link struct v4l2_async_subdev objects, waiting to be
+ *		probed, to a notifier->waiting list
+ */
+struct v4l2_async_subdev {
+	enum v4l2_async_bus_type bus_type;
+	union {
+		struct {
+			const char *name;
+		} platform;
+		struct {
+			int adapter_id;
+			unsigned short address;
+		} i2c;
+		struct {
+			bool (*match)(struct device *,
+				      struct v4l2_async_subdev *);
+			void *priv;
+		} custom;
+	} match;
+
+	/* v4l2-async core private: not to be used by drivers */
+	struct list_head list;
+};
+
+/**
+ * v4l2_async_subdev_list - provided by subdevices
+ * @list:	links struct v4l2_async_subdev_list objects to a global list
+ *		before probing, and onto notifier->done after probing
+ * @asd:	pointer to respective struct v4l2_async_subdev
+ * @notifier:	pointer to managing notifier
+ */
+struct v4l2_async_subdev_list {
+	struct list_head list;
+	struct v4l2_async_subdev *asd;
+	struct v4l2_async_notifier *notifier;
+};
+
+/**
+ * v4l2_async_notifier - v4l2_device notifier data
+ * @subdev_num:	number of subdevices
+ * @subdev:	array of pointers to subdevices
+ * @v4l2_dev:	pointer to struct v4l2_device
+ * @waiting:	list of struct v4l2_async_subdev, waiting for their drivers
+ * @done:	list of struct v4l2_async_subdev_list, already probed
+ * @list:	member in a global list of notifiers
+ * @bound:	a subdevice driver has successfully probed one of subdevices
+ * @complete:	all subdevices have been probed successfully
+ * @unbind:	a subdevice is leaving
+ */
+struct v4l2_async_notifier {
+	unsigned int subdev_num;
+	struct v4l2_async_subdev **subdev;
+	struct v4l2_device *v4l2_dev;
+	struct list_head waiting;
+	struct list_head done;
+	struct list_head list;
+	int (*bound)(struct v4l2_async_notifier *notifier,
+		     struct v4l2_subdev *subdev,
+		     struct v4l2_async_subdev *asd);
+	int (*complete)(struct v4l2_async_notifier *notifier);
+	void (*unbind)(struct v4l2_async_notifier *notifier,
+		       struct v4l2_subdev *subdev,
+		       struct v4l2_async_subdev *asd);
+};
+
+int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
+				 struct v4l2_async_notifier *notifier);
+void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier);
+int v4l2_async_register_subdev(struct v4l2_subdev *sd);
+void v4l2_async_unregister_subdev(struct v4l2_subdev *sd);
+#endif
diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h
index d8756fa..21174af 100644
--- a/include/media/v4l2-subdev.h
+++ b/include/media/v4l2-subdev.h
@@ -24,6 +24,7 @@
 #include <linux/types.h>
 #include <linux/v4l2-subdev.h>
 #include <media/media-entity.h>
+#include <media/v4l2-async.h>
 #include <media/v4l2-common.h>
 #include <media/v4l2-dev.h>
 #include <media/v4l2-fh.h>
@@ -587,8 +588,15 @@ struct v4l2_subdev {
 	struct video_device *devnode;
 	/* pointer to the physical device */
 	struct device *dev;
+	struct v4l2_async_subdev_list asdl;
 };
 
+static inline struct v4l2_subdev *v4l2_async_to_subdev(
+			struct v4l2_async_subdev_list *asdl)
+{
+	return container_of(asdl, struct v4l2_subdev, asdl);
+}
+
 #define media_entity_to_v4l2_subdev(ent) \
 	container_of(ent, struct v4l2_subdev, entity)
 #define vdev_to_v4l2_subdev(vdev) \
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v10 17/21] soc-camera: switch I2C subdevice drivers to use v4l2-clk
  2013-06-11  8:23 ` Guennadi Liakhovetski
@ 2013-06-11  8:23   ` Guennadi Liakhovetski
  -1 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-11  8:23 UTC (permalink / raw)
  To: linux-media
  Cc: Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer,
	Guennadi Liakhovetski

Instead of centrally enabling and disabling subdevice master clocks in
soc-camera core, let subdevice drivers do that themselves, using the
V4L2 clock API and soc-camera convenience wrappers.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
 drivers/media/i2c/soc_camera/imx074.c              |   18 ++-
 drivers/media/i2c/soc_camera/mt9m001.c             |   17 ++-
 drivers/media/i2c/soc_camera/mt9m111.c             |   20 ++-
 drivers/media/i2c/soc_camera/mt9t031.c             |   19 ++-
 drivers/media/i2c/soc_camera/mt9t112.c             |   25 +++-
 drivers/media/i2c/soc_camera/mt9v022.c             |   17 ++-
 drivers/media/i2c/soc_camera/ov2640.c              |   19 ++-
 drivers/media/i2c/soc_camera/ov5642.c              |   20 ++-
 drivers/media/i2c/soc_camera/ov6650.c              |   17 ++-
 drivers/media/i2c/soc_camera/ov772x.c              |   15 ++-
 drivers/media/i2c/soc_camera/ov9640.c              |   17 ++-
 drivers/media/i2c/soc_camera/ov9640.h              |    1 +
 drivers/media/i2c/soc_camera/ov9740.c              |   18 ++-
 drivers/media/i2c/soc_camera/rj54n1cb0c.c          |   17 ++-
 drivers/media/i2c/soc_camera/tw9910.c              |   24 +++-
 drivers/media/platform/soc_camera/soc_camera.c     |  162 +++++++++++++++-----
 .../platform/soc_camera/soc_camera_platform.c      |    2 +-
 include/media/soc_camera.h                         |   13 +-
 18 files changed, 362 insertions(+), 79 deletions(-)

diff --git a/drivers/media/i2c/soc_camera/imx074.c b/drivers/media/i2c/soc_camera/imx074.c
index a2a5cbb..a6a5060 100644
--- a/drivers/media/i2c/soc_camera/imx074.c
+++ b/drivers/media/i2c/soc_camera/imx074.c
@@ -18,6 +18,7 @@
 #include <linux/module.h>
 
 #include <media/soc_camera.h>
+#include <media/v4l2-clk.h>
 #include <media/v4l2-subdev.h>
 #include <media/v4l2-chip-ident.h>
 
@@ -77,6 +78,7 @@ struct imx074_datafmt {
 struct imx074 {
 	struct v4l2_subdev		subdev;
 	const struct imx074_datafmt	*fmt;
+	struct v4l2_clk			*clk;
 };
 
 static const struct imx074_datafmt imx074_colour_fmts[] = {
@@ -272,8 +274,9 @@ static int imx074_s_power(struct v4l2_subdev *sd, int on)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+	struct imx074 *priv = to_imx074(client);
 
-	return soc_camera_set_power(&client->dev, ssdd, on);
+	return soc_camera_set_power(&client->dev, ssdd, priv->clk, on);
 }
 
 static int imx074_g_mbus_config(struct v4l2_subdev *sd,
@@ -431,6 +434,7 @@ static int imx074_probe(struct i2c_client *client,
 	struct imx074 *priv;
 	struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+	int ret;
 
 	if (!ssdd) {
 		dev_err(&client->dev, "IMX074: missing platform data!\n");
@@ -451,13 +455,23 @@ static int imx074_probe(struct i2c_client *client,
 
 	priv->fmt	= &imx074_colour_fmts[0];
 
-	return imx074_video_probe(client);
+	priv->clk = v4l2_clk_get(&client->dev, "mclk");
+	if (IS_ERR(priv->clk))
+		return PTR_ERR(priv->clk);
+
+	ret = imx074_video_probe(client);
+	if (ret < 0)
+		v4l2_clk_put(priv->clk);
+
+	return ret;
 }
 
 static int imx074_remove(struct i2c_client *client)
 {
 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+	struct imx074 *priv = to_imx074(client);
 
+	v4l2_clk_put(priv->clk);
 	if (ssdd->free_bus)
 		ssdd->free_bus(ssdd);
 
diff --git a/drivers/media/i2c/soc_camera/mt9m001.c b/drivers/media/i2c/soc_camera/mt9m001.c
index dd90898..620f9df 100644
--- a/drivers/media/i2c/soc_camera/mt9m001.c
+++ b/drivers/media/i2c/soc_camera/mt9m001.c
@@ -16,6 +16,7 @@
 
 #include <media/soc_camera.h>
 #include <media/soc_mediabus.h>
+#include <media/v4l2-clk.h>
 #include <media/v4l2-subdev.h>
 #include <media/v4l2-chip-ident.h>
 #include <media/v4l2-ctrls.h>
@@ -94,6 +95,7 @@ struct mt9m001 {
 		struct v4l2_ctrl *exposure;
 	};
 	struct v4l2_rect rect;	/* Sensor window */
+	struct v4l2_clk *clk;
 	const struct mt9m001_datafmt *fmt;
 	const struct mt9m001_datafmt *fmts;
 	int num_fmts;
@@ -381,8 +383,9 @@ static int mt9m001_s_power(struct v4l2_subdev *sd, int on)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+	struct mt9m001 *mt9m001 = to_mt9m001(client);
 
-	return soc_camera_set_power(&client->dev, ssdd, on);
+	return soc_camera_set_power(&client->dev, ssdd, mt9m001->clk, on);
 }
 
 static int mt9m001_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
@@ -710,9 +713,18 @@ static int mt9m001_probe(struct i2c_client *client,
 	mt9m001->rect.width	= MT9M001_MAX_WIDTH;
 	mt9m001->rect.height	= MT9M001_MAX_HEIGHT;
 
+	mt9m001->clk = v4l2_clk_get(&client->dev, "mclk");
+	if (IS_ERR(mt9m001->clk)) {
+		ret = PTR_ERR(mt9m001->clk);
+		goto eclkget;
+	}
+
 	ret = mt9m001_video_probe(ssdd, client);
-	if (ret)
+	if (ret) {
+		v4l2_clk_put(mt9m001->clk);
+eclkget:
 		v4l2_ctrl_handler_free(&mt9m001->hdl);
+	}
 
 	return ret;
 }
@@ -722,6 +734,7 @@ static int mt9m001_remove(struct i2c_client *client)
 	struct mt9m001 *mt9m001 = to_mt9m001(client);
 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
 
+	v4l2_clk_put(mt9m001->clk);
 	v4l2_device_unregister_subdev(&mt9m001->subdev);
 	v4l2_ctrl_handler_free(&mt9m001->hdl);
 	mt9m001_video_remove(ssdd);
diff --git a/drivers/media/i2c/soc_camera/mt9m111.c b/drivers/media/i2c/soc_camera/mt9m111.c
index 8bd4e0d..5fa9372 100644
--- a/drivers/media/i2c/soc_camera/mt9m111.c
+++ b/drivers/media/i2c/soc_camera/mt9m111.c
@@ -17,6 +17,7 @@
 #include <linux/module.h>
 
 #include <media/soc_camera.h>
+#include <media/v4l2-clk.h>
 #include <media/v4l2-common.h>
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-chip-ident.h>
@@ -209,6 +210,7 @@ struct mt9m111 {
 			 * from v4l2-chip-ident.h */
 	struct mt9m111_context *ctx;
 	struct v4l2_rect rect;	/* cropping rectangle */
+	struct v4l2_clk *clk;
 	int width;		/* output */
 	int height;		/* sizes */
 	struct mutex power_lock; /* lock to protect power_count */
@@ -801,14 +803,14 @@ static int mt9m111_power_on(struct mt9m111 *mt9m111)
 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
 	int ret;
 
-	ret = soc_camera_power_on(&client->dev, ssdd);
+	ret = soc_camera_power_on(&client->dev, ssdd, mt9m111->clk);
 	if (ret < 0)
 		return ret;
 
 	ret = mt9m111_resume(mt9m111);
 	if (ret < 0) {
 		dev_err(&client->dev, "Failed to resume the sensor: %d\n", ret);
-		soc_camera_power_off(&client->dev, ssdd);
+		soc_camera_power_off(&client->dev, ssdd, mt9m111->clk);
 	}
 
 	return ret;
@@ -820,7 +822,7 @@ static void mt9m111_power_off(struct mt9m111 *mt9m111)
 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
 
 	mt9m111_suspend(mt9m111);
-	soc_camera_power_off(&client->dev, ssdd);
+	soc_camera_power_off(&client->dev, ssdd, mt9m111->clk);
 }
 
 static int mt9m111_s_power(struct v4l2_subdev *sd, int on)
@@ -1002,9 +1004,18 @@ static int mt9m111_probe(struct i2c_client *client,
 	mt9m111->lastpage	= -1;
 	mutex_init(&mt9m111->power_lock);
 
+	mt9m111->clk = v4l2_clk_get(&client->dev, "mclk");
+	if (IS_ERR(mt9m111->clk)) {
+		ret = PTR_ERR(mt9m111->clk);
+		goto eclkget;
+	}
+
 	ret = mt9m111_video_probe(client);
-	if (ret)
+	if (ret) {
+		v4l2_clk_put(mt9m111->clk);
+eclkget:
 		v4l2_ctrl_handler_free(&mt9m111->hdl);
+	}
 
 	return ret;
 }
@@ -1013,6 +1024,7 @@ static int mt9m111_remove(struct i2c_client *client)
 {
 	struct mt9m111 *mt9m111 = to_mt9m111(client);
 
+	v4l2_clk_put(mt9m111->clk);
 	v4l2_device_unregister_subdev(&mt9m111->subdev);
 	v4l2_ctrl_handler_free(&mt9m111->hdl);
 
diff --git a/drivers/media/i2c/soc_camera/mt9t031.c b/drivers/media/i2c/soc_camera/mt9t031.c
index 191e3f1..1740f43 100644
--- a/drivers/media/i2c/soc_camera/mt9t031.c
+++ b/drivers/media/i2c/soc_camera/mt9t031.c
@@ -19,6 +19,7 @@
 
 #include <media/soc_camera.h>
 #include <media/v4l2-chip-ident.h>
+#include <media/v4l2-clk.h>
 #include <media/v4l2-subdev.h>
 #include <media/v4l2-ctrls.h>
 
@@ -76,6 +77,7 @@ struct mt9t031 {
 		struct v4l2_ctrl *exposure;
 	};
 	struct v4l2_rect rect;	/* Sensor window */
+	struct v4l2_clk *clk;
 	int model;	/* V4L2_IDENT_MT9T031* codes from v4l2-chip-ident.h */
 	u16 xskip;
 	u16 yskip;
@@ -610,16 +612,17 @@ static int mt9t031_s_power(struct v4l2_subdev *sd, int on)
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
 	struct video_device *vdev = soc_camera_i2c_to_vdev(client);
+	struct mt9t031 *mt9t031 = to_mt9t031(client);
 	int ret;
 
 	if (on) {
-		ret = soc_camera_power_on(&client->dev, ssdd);
+		ret = soc_camera_power_on(&client->dev, ssdd, mt9t031->clk);
 		if (ret < 0)
 			return ret;
 		vdev->dev.type = &mt9t031_dev_type;
 	} else {
 		vdev->dev.type = NULL;
-		soc_camera_power_off(&client->dev, ssdd);
+		soc_camera_power_off(&client->dev, ssdd, mt9t031->clk);
 	}
 
 	return 0;
@@ -812,9 +815,18 @@ static int mt9t031_probe(struct i2c_client *client,
 	mt9t031->xskip = 1;
 	mt9t031->yskip = 1;
 
+	mt9t031->clk = v4l2_clk_get(&client->dev, "mclk");
+	if (IS_ERR(mt9t031->clk)) {
+		ret = PTR_ERR(mt9t031->clk);
+		goto eclkget;
+	}
+
 	ret = mt9t031_video_probe(client);
-	if (ret)
+	if (ret) {
+		v4l2_clk_put(mt9t031->clk);
+eclkget:
 		v4l2_ctrl_handler_free(&mt9t031->hdl);
+	}
 
 	return ret;
 }
@@ -823,6 +835,7 @@ static int mt9t031_remove(struct i2c_client *client)
 {
 	struct mt9t031 *mt9t031 = to_mt9t031(client);
 
+	v4l2_clk_put(mt9t031->clk);
 	v4l2_device_unregister_subdev(&mt9t031->subdev);
 	v4l2_ctrl_handler_free(&mt9t031->hdl);
 
diff --git a/drivers/media/i2c/soc_camera/mt9t112.c b/drivers/media/i2c/soc_camera/mt9t112.c
index 0af29a4..577cdea 100644
--- a/drivers/media/i2c/soc_camera/mt9t112.c
+++ b/drivers/media/i2c/soc_camera/mt9t112.c
@@ -28,6 +28,7 @@
 #include <media/mt9t112.h>
 #include <media/soc_camera.h>
 #include <media/v4l2-chip-ident.h>
+#include <media/v4l2-clk.h>
 #include <media/v4l2-common.h>
 
 /* you can check PLL/clock info */
@@ -90,6 +91,7 @@ struct mt9t112_priv {
 	struct mt9t112_camera_info	*info;
 	struct i2c_client		*client;
 	struct v4l2_rect		 frame;
+	struct v4l2_clk			*clk;
 	const struct mt9t112_format	*format;
 	int				 model;
 	int				 num_formats;
@@ -781,8 +783,9 @@ static int mt9t112_s_power(struct v4l2_subdev *sd, int on)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+	struct mt9t112_priv *priv = to_mt9t112(client);
 
-	return soc_camera_set_power(&client->dev, ssdd, on);
+	return soc_camera_set_power(&client->dev, ssdd, priv->clk, on);
 }
 
 static struct v4l2_subdev_core_ops mt9t112_subdev_core_ops = {
@@ -1108,16 +1111,29 @@ static int mt9t112_probe(struct i2c_client *client,
 
 	v4l2_i2c_subdev_init(&priv->subdev, client, &mt9t112_subdev_ops);
 
+	priv->clk = v4l2_clk_get(&client->dev, "mclk");
+	if (IS_ERR(priv->clk))
+		return PTR_ERR(priv->clk);
+
 	ret = mt9t112_camera_probe(client);
-	if (ret)
-		return ret;
 
 	/* Cannot fail: using the default supported pixel code */
-	mt9t112_set_params(priv, &rect, V4L2_MBUS_FMT_UYVY8_2X8);
+	if (!ret)
+		mt9t112_set_params(priv, &rect, V4L2_MBUS_FMT_UYVY8_2X8);
+	else
+		v4l2_clk_put(priv->clk);
 
 	return ret;
 }
 
+static int mt9t112_remove(struct i2c_client *client)
+{
+	struct mt9t112_priv *priv = to_mt9t112(client);
+
+	v4l2_clk_put(priv->clk);
+	return 0;
+}
+
 static const struct i2c_device_id mt9t112_id[] = {
 	{ "mt9t112", 0 },
 	{ }
@@ -1129,6 +1145,7 @@ static struct i2c_driver mt9t112_i2c_driver = {
 		.name = "mt9t112",
 	},
 	.probe    = mt9t112_probe,
+	.remove   = mt9t112_remove,
 	.id_table = mt9t112_id,
 };
 
diff --git a/drivers/media/i2c/soc_camera/mt9v022.c b/drivers/media/i2c/soc_camera/mt9v022.c
index a295e59..3164f56 100644
--- a/drivers/media/i2c/soc_camera/mt9v022.c
+++ b/drivers/media/i2c/soc_camera/mt9v022.c
@@ -20,6 +20,7 @@
 #include <media/soc_mediabus.h>
 #include <media/v4l2-subdev.h>
 #include <media/v4l2-chip-ident.h>
+#include <media/v4l2-clk.h>
 #include <media/v4l2-ctrls.h>
 
 /*
@@ -149,6 +150,7 @@ struct mt9v022 {
 	struct v4l2_ctrl *hblank;
 	struct v4l2_ctrl *vblank;
 	struct v4l2_rect rect;	/* Sensor window */
+	struct v4l2_clk *clk;
 	const struct mt9v022_datafmt *fmt;
 	const struct mt9v022_datafmt *fmts;
 	const struct mt9v02x_register *reg;
@@ -518,8 +520,9 @@ static int mt9v022_s_power(struct v4l2_subdev *sd, int on)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+	struct mt9v022 *mt9v022 = to_mt9v022(client);
 
-	return soc_camera_set_power(&client->dev, ssdd, on);
+	return soc_camera_set_power(&client->dev, ssdd, mt9v022->clk, on);
 }
 
 static int mt9v022_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
@@ -957,9 +960,18 @@ static int mt9v022_probe(struct i2c_client *client,
 	mt9v022->rect.width	= MT9V022_MAX_WIDTH;
 	mt9v022->rect.height	= MT9V022_MAX_HEIGHT;
 
+	mt9v022->clk = v4l2_clk_get(&client->dev, "mclk");
+	if (IS_ERR(mt9v022->clk)) {
+		ret = PTR_ERR(mt9v022->clk);
+		goto eclkget;
+	}
+
 	ret = mt9v022_video_probe(client);
-	if (ret)
+	if (ret) {
+		v4l2_clk_put(mt9v022->clk);
+eclkget:
 		v4l2_ctrl_handler_free(&mt9v022->hdl);
+	}
 
 	return ret;
 }
@@ -969,6 +981,7 @@ static int mt9v022_remove(struct i2c_client *client)
 	struct mt9v022 *mt9v022 = to_mt9v022(client);
 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
 
+	v4l2_clk_put(mt9v022->clk);
 	v4l2_device_unregister_subdev(&mt9v022->subdev);
 	if (ssdd->free_bus)
 		ssdd->free_bus(ssdd);
diff --git a/drivers/media/i2c/soc_camera/ov2640.c b/drivers/media/i2c/soc_camera/ov2640.c
index e316842..0c97b8c 100644
--- a/drivers/media/i2c/soc_camera/ov2640.c
+++ b/drivers/media/i2c/soc_camera/ov2640.c
@@ -23,6 +23,7 @@
 
 #include <media/soc_camera.h>
 #include <media/v4l2-chip-ident.h>
+#include <media/v4l2-clk.h>
 #include <media/v4l2-subdev.h>
 #include <media/v4l2-ctrls.h>
 
@@ -303,6 +304,7 @@ struct ov2640_priv {
 	struct v4l2_subdev		subdev;
 	struct v4l2_ctrl_handler	hdl;
 	enum v4l2_mbus_pixelcode	cfmt_code;
+	struct v4l2_clk			*clk;
 	const struct ov2640_win_size	*win;
 	int				model;
 };
@@ -772,8 +774,9 @@ static int ov2640_s_power(struct v4l2_subdev *sd, int on)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+	struct ov2640_priv *priv = to_ov2640(client);
 
-	return soc_camera_set_power(&client->dev, ssdd, on);
+	return soc_camera_set_power(&client->dev, ssdd, priv->clk, on);
 }
 
 /* Select the nearest higher resolution for capture */
@@ -1113,11 +1116,20 @@ static int ov2640_probe(struct i2c_client *client,
 	if (priv->hdl.error)
 		return priv->hdl.error;
 
+	priv->clk = v4l2_clk_get(&client->dev, "mclk");
+	if (IS_ERR(priv->clk)) {
+		ret = PTR_ERR(priv->clk);
+		goto eclkget;
+	}
+
 	ret = ov2640_video_probe(client);
-	if (ret)
+	if (ret) {
+		v4l2_clk_put(priv->clk);
+eclkget:
 		v4l2_ctrl_handler_free(&priv->hdl);
-	else
+	} else {
 		dev_info(&adapter->dev, "OV2640 Probed\n");
+	}
 
 	return ret;
 }
@@ -1126,6 +1138,7 @@ static int ov2640_remove(struct i2c_client *client)
 {
 	struct ov2640_priv       *priv = to_ov2640(client);
 
+	v4l2_clk_put(priv->clk);
 	v4l2_device_unregister_subdev(&priv->subdev);
 	v4l2_ctrl_handler_free(&priv->hdl);
 	return 0;
diff --git a/drivers/media/i2c/soc_camera/ov5642.c b/drivers/media/i2c/soc_camera/ov5642.c
index 9aa56de..8ffc8f9 100644
--- a/drivers/media/i2c/soc_camera/ov5642.c
+++ b/drivers/media/i2c/soc_camera/ov5642.c
@@ -25,6 +25,7 @@
 
 #include <media/soc_camera.h>
 #include <media/v4l2-chip-ident.h>
+#include <media/v4l2-clk.h>
 #include <media/v4l2-subdev.h>
 
 /* OV5642 registers */
@@ -610,6 +611,7 @@ struct ov5642 {
 	struct v4l2_subdev		subdev;
 	const struct ov5642_datafmt	*fmt;
 	struct v4l2_rect                crop_rect;
+	struct v4l2_clk			*clk;
 
 	/* blanking information */
 	int total_width;
@@ -935,12 +937,13 @@ static int ov5642_s_power(struct v4l2_subdev *sd, int on)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+	struct ov5642 *priv = to_ov5642(client);
 	int ret;
 
 	if (!on)
-		return soc_camera_power_off(&client->dev, ssdd);
+		return soc_camera_power_off(&client->dev, ssdd, priv->clk);
 
-	ret = soc_camera_power_on(&client->dev, ssdd);
+	ret = soc_camera_power_on(&client->dev, ssdd, priv->clk);
 	if (ret < 0)
 		return ret;
 
@@ -1021,6 +1024,7 @@ static int ov5642_probe(struct i2c_client *client,
 {
 	struct ov5642 *priv;
 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+	int ret;
 
 	if (!ssdd) {
 		dev_err(&client->dev, "OV5642: missing platform data!\n");
@@ -1042,13 +1046,23 @@ static int ov5642_probe(struct i2c_client *client,
 	priv->total_width = OV5642_DEFAULT_WIDTH + BLANKING_EXTRA_WIDTH;
 	priv->total_height = BLANKING_MIN_HEIGHT;
 
-	return ov5642_video_probe(client);
+	priv->clk = v4l2_clk_get(&client->dev, "mclk");
+	if (IS_ERR(priv->clk))
+		return PTR_ERR(priv->clk);
+
+	ret = ov5642_video_probe(client);
+	if (ret < 0)
+		v4l2_clk_put(priv->clk);
+
+	return ret;
 }
 
 static int ov5642_remove(struct i2c_client *client)
 {
 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+	struct ov5642 *priv = to_ov5642(client);
 
+	v4l2_clk_put(priv->clk);
 	if (ssdd->free_bus)
 		ssdd->free_bus(ssdd);
 
diff --git a/drivers/media/i2c/soc_camera/ov6650.c b/drivers/media/i2c/soc_camera/ov6650.c
index 991202d..2389aa2 100644
--- a/drivers/media/i2c/soc_camera/ov6650.c
+++ b/drivers/media/i2c/soc_camera/ov6650.c
@@ -33,6 +33,7 @@
 
 #include <media/soc_camera.h>
 #include <media/v4l2-chip-ident.h>
+#include <media/v4l2-clk.h>
 #include <media/v4l2-ctrls.h>
 
 /* Register definitions */
@@ -196,6 +197,7 @@ struct ov6650 {
 		struct v4l2_ctrl *blue;
 		struct v4l2_ctrl *red;
 	};
+	struct v4l2_clk		*clk;
 	bool			half_scale;	/* scale down output by 2 */
 	struct v4l2_rect	rect;		/* sensor cropping window */
 	unsigned long		pclk_limit;	/* from host */
@@ -436,8 +438,9 @@ static int ov6650_s_power(struct v4l2_subdev *sd, int on)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+	struct ov6650 *priv = to_ov6650(client);
 
-	return soc_camera_set_power(&client->dev, ssdd, on);
+	return soc_camera_set_power(&client->dev, ssdd, priv->clk, on);
 }
 
 static int ov6650_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
@@ -1025,9 +1028,18 @@ static int ov6650_probe(struct i2c_client *client,
 	priv->code	  = V4L2_MBUS_FMT_YUYV8_2X8;
 	priv->colorspace  = V4L2_COLORSPACE_JPEG;
 
+	priv->clk = v4l2_clk_get(&client->dev, "mclk");
+	if (IS_ERR(priv->clk)) {
+		ret = PTR_ERR(priv->clk);
+		goto eclkget;
+	}
+
 	ret = ov6650_video_probe(client);
-	if (ret)
+	if (ret) {
+		v4l2_clk_put(priv->clk);
+eclkget:
 		v4l2_ctrl_handler_free(&priv->hdl);
+	}
 
 	return ret;
 }
@@ -1036,6 +1048,7 @@ static int ov6650_remove(struct i2c_client *client)
 {
 	struct ov6650 *priv = to_ov6650(client);
 
+	v4l2_clk_put(priv->clk);
 	v4l2_device_unregister_subdev(&priv->subdev);
 	v4l2_ctrl_handler_free(&priv->hdl);
 	return 0;
diff --git a/drivers/media/i2c/soc_camera/ov772x.c b/drivers/media/i2c/soc_camera/ov772x.c
index 713d62e..d643b69 100644
--- a/drivers/media/i2c/soc_camera/ov772x.c
+++ b/drivers/media/i2c/soc_camera/ov772x.c
@@ -26,6 +26,7 @@
 
 #include <media/ov772x.h>
 #include <media/soc_camera.h>
+#include <media/v4l2-clk.h>
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-chip-ident.h>
 #include <media/v4l2-subdev.h>
@@ -396,6 +397,7 @@ struct ov772x_win_size {
 struct ov772x_priv {
 	struct v4l2_subdev                subdev;
 	struct v4l2_ctrl_handler	  hdl;
+	struct v4l2_clk			 *clk;
 	struct ov772x_camera_info        *info;
 	const struct ov772x_color_format *cfmt;
 	const struct ov772x_win_size     *win;
@@ -668,8 +670,9 @@ static int ov772x_s_power(struct v4l2_subdev *sd, int on)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+	struct ov772x_priv *priv = to_ov772x(sd);
 
-	return soc_camera_set_power(&client->dev, ssdd, on);
+	return soc_camera_set_power(&client->dev, ssdd, priv->clk, on);
 }
 
 static const struct ov772x_win_size *ov772x_select_win(u32 width, u32 height)
@@ -1088,13 +1091,22 @@ static int ov772x_probe(struct i2c_client *client,
 	if (priv->hdl.error)
 		return priv->hdl.error;
 
+	priv->clk = v4l2_clk_get(&client->dev, "mclk");
+	if (IS_ERR(priv->clk)) {
+		ret = PTR_ERR(priv->clk);
+		goto eclkget;
+	}
+
 	ret = ov772x_video_probe(priv);
 	if (ret < 0) {
+		v4l2_clk_put(priv->clk);
+eclkget:
 		v4l2_ctrl_handler_free(&priv->hdl);
 	} else {
 		priv->cfmt = &ov772x_cfmts[0];
 		priv->win = &ov772x_win_sizes[0];
 	}
+
 	return ret;
 }
 
@@ -1102,6 +1114,7 @@ static int ov772x_remove(struct i2c_client *client)
 {
 	struct ov772x_priv *priv = to_ov772x(i2c_get_clientdata(client));
 
+	v4l2_clk_put(priv->clk);
 	v4l2_device_unregister_subdev(&priv->subdev);
 	v4l2_ctrl_handler_free(&priv->hdl);
 	return 0;
diff --git a/drivers/media/i2c/soc_camera/ov9640.c b/drivers/media/i2c/soc_camera/ov9640.c
index f4eba1b..681ed7e 100644
--- a/drivers/media/i2c/soc_camera/ov9640.c
+++ b/drivers/media/i2c/soc_camera/ov9640.c
@@ -29,6 +29,7 @@
 
 #include <media/soc_camera.h>
 #include <media/v4l2-chip-ident.h>
+#include <media/v4l2-clk.h>
 #include <media/v4l2-common.h>
 #include <media/v4l2-ctrls.h>
 
@@ -337,8 +338,9 @@ static int ov9640_s_power(struct v4l2_subdev *sd, int on)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+	struct ov9640_priv *priv = to_ov9640_sensor(sd);
 
-	return soc_camera_set_power(&client->dev, ssdd, on);
+	return soc_camera_set_power(&client->dev, ssdd, priv->clk, on);
 }
 
 /* select nearest higher resolution for capture */
@@ -716,10 +718,18 @@ static int ov9640_probe(struct i2c_client *client,
 	if (priv->hdl.error)
 		return priv->hdl.error;
 
-	ret = ov9640_video_probe(client);
+	priv->clk = v4l2_clk_get(&client->dev, "mclk");
+	if (IS_ERR(priv->clk)) {
+		ret = PTR_ERR(priv->clk);
+		goto eclkget;
+	}
 
-	if (ret)
+	ret = ov9640_video_probe(client);
+	if (ret) {
+		v4l2_clk_put(priv->clk);
+eclkget:
 		v4l2_ctrl_handler_free(&priv->hdl);
+	}
 
 	return ret;
 }
@@ -729,6 +739,7 @@ static int ov9640_remove(struct i2c_client *client)
 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
 	struct ov9640_priv *priv = to_ov9640_sensor(sd);
 
+	v4l2_clk_put(priv->clk);
 	v4l2_device_unregister_subdev(&priv->subdev);
 	v4l2_ctrl_handler_free(&priv->hdl);
 	return 0;
diff --git a/drivers/media/i2c/soc_camera/ov9640.h b/drivers/media/i2c/soc_camera/ov9640.h
index 6b33a97..65d13ff 100644
--- a/drivers/media/i2c/soc_camera/ov9640.h
+++ b/drivers/media/i2c/soc_camera/ov9640.h
@@ -199,6 +199,7 @@ struct ov9640_reg {
 struct ov9640_priv {
 	struct v4l2_subdev		subdev;
 	struct v4l2_ctrl_handler	hdl;
+	struct v4l2_clk			*clk;
 
 	int				model;
 	int				revision;
diff --git a/drivers/media/i2c/soc_camera/ov9740.c b/drivers/media/i2c/soc_camera/ov9740.c
index 012bd62..6a5e164 100644
--- a/drivers/media/i2c/soc_camera/ov9740.c
+++ b/drivers/media/i2c/soc_camera/ov9740.c
@@ -18,6 +18,7 @@
 
 #include <media/soc_camera.h>
 #include <media/v4l2-chip-ident.h>
+#include <media/v4l2-clk.h>
 #include <media/v4l2-ctrls.h>
 
 #define to_ov9740(sd)		container_of(sd, struct ov9740_priv, subdev)
@@ -196,6 +197,7 @@ struct ov9740_reg {
 struct ov9740_priv {
 	struct v4l2_subdev		subdev;
 	struct v4l2_ctrl_handler	hdl;
+	struct v4l2_clk			*clk;
 
 	int				ident;
 	u16				model;
@@ -792,7 +794,7 @@ static int ov9740_s_power(struct v4l2_subdev *sd, int on)
 	int ret;
 
 	if (on) {
-		ret = soc_camera_power_on(&client->dev, ssdd);
+		ret = soc_camera_power_on(&client->dev, ssdd, priv->clk);
 		if (ret < 0)
 			return ret;
 
@@ -806,7 +808,7 @@ static int ov9740_s_power(struct v4l2_subdev *sd, int on)
 			priv->current_enable = true;
 		}
 
-		soc_camera_power_off(&client->dev, ssdd);
+		soc_camera_power_off(&client->dev, ssdd, priv->clk);
 	}
 
 	return 0;
@@ -975,9 +977,18 @@ static int ov9740_probe(struct i2c_client *client,
 	if (priv->hdl.error)
 		return priv->hdl.error;
 
+	priv->clk = v4l2_clk_get(&client->dev, "mclk");
+	if (IS_ERR(priv->clk)) {
+		ret = PTR_ERR(priv->clk);
+		goto eclkget;
+	}
+
 	ret = ov9740_video_probe(client);
-	if (ret < 0)
+	if (ret < 0) {
+		v4l2_clk_put(priv->clk);
+eclkget:
 		v4l2_ctrl_handler_free(&priv->hdl);
+	}
 
 	return ret;
 }
@@ -986,6 +997,7 @@ static int ov9740_remove(struct i2c_client *client)
 {
 	struct ov9740_priv *priv = i2c_get_clientdata(client);
 
+	v4l2_clk_put(priv->clk);
 	v4l2_device_unregister_subdev(&priv->subdev);
 	v4l2_ctrl_handler_free(&priv->hdl);
 	return 0;
diff --git a/drivers/media/i2c/soc_camera/rj54n1cb0c.c b/drivers/media/i2c/soc_camera/rj54n1cb0c.c
index 1f9ec3b..93db388 100644
--- a/drivers/media/i2c/soc_camera/rj54n1cb0c.c
+++ b/drivers/media/i2c/soc_camera/rj54n1cb0c.c
@@ -17,6 +17,7 @@
 
 #include <media/rj54n1cb0c.h>
 #include <media/soc_camera.h>
+#include <media/v4l2-clk.h>
 #include <media/v4l2-subdev.h>
 #include <media/v4l2-chip-ident.h>
 #include <media/v4l2-ctrls.h>
@@ -151,6 +152,7 @@ struct rj54n1_clock_div {
 struct rj54n1 {
 	struct v4l2_subdev subdev;
 	struct v4l2_ctrl_handler hdl;
+	struct v4l2_clk *clk;
 	struct rj54n1_clock_div clk_div;
 	const struct rj54n1_datafmt *fmt;
 	struct v4l2_rect rect;	/* Sensor window */
@@ -1184,8 +1186,9 @@ static int rj54n1_s_power(struct v4l2_subdev *sd, int on)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+	struct rj54n1 *rj54n1 = to_rj54n1(client);
 
-	return soc_camera_set_power(&client->dev, ssdd, on);
+	return soc_camera_set_power(&client->dev, ssdd, rj54n1->clk, on);
 }
 
 static int rj54n1_s_ctrl(struct v4l2_ctrl *ctrl)
@@ -1382,9 +1385,18 @@ static int rj54n1_probe(struct i2c_client *client,
 	rj54n1->tgclk_mhz	= (rj54n1_priv->mclk_freq / PLL_L * PLL_N) /
 		(clk_div.ratio_tg + 1) / (clk_div.ratio_t + 1);
 
+	rj54n1->clk = v4l2_clk_get(&client->dev, "mclk");
+	if (IS_ERR(rj54n1->clk)) {
+		ret = PTR_ERR(rj54n1->clk);
+		goto eclkget;
+	}
+
 	ret = rj54n1_video_probe(client, rj54n1_priv);
-	if (ret < 0)
+	if (ret < 0) {
+		v4l2_clk_put(rj54n1->clk);
+eclkget:
 		v4l2_ctrl_handler_free(&rj54n1->hdl);
+	}
 
 	return ret;
 }
@@ -1394,6 +1406,7 @@ static int rj54n1_remove(struct i2c_client *client)
 	struct rj54n1 *rj54n1 = to_rj54n1(client);
 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
 
+	v4l2_clk_put(rj54n1->clk);
 	v4l2_device_unregister_subdev(&rj54n1->subdev);
 	if (ssdd->free_bus)
 		ssdd->free_bus(ssdd);
diff --git a/drivers/media/i2c/soc_camera/tw9910.c b/drivers/media/i2c/soc_camera/tw9910.c
index 6b9cc2a..fe43591 100644
--- a/drivers/media/i2c/soc_camera/tw9910.c
+++ b/drivers/media/i2c/soc_camera/tw9910.c
@@ -28,6 +28,7 @@
 #include <media/soc_camera.h>
 #include <media/tw9910.h>
 #include <media/v4l2-chip-ident.h>
+#include <media/v4l2-clk.h>
 #include <media/v4l2-subdev.h>
 
 #define GET_ID(val)  ((val & 0xF8) >> 3)
@@ -228,6 +229,7 @@ struct tw9910_scale_ctrl {
 
 struct tw9910_priv {
 	struct v4l2_subdev		subdev;
+	struct v4l2_clk			*clk;
 	struct tw9910_video_info	*info;
 	const struct tw9910_scale_ctrl	*scale;
 	v4l2_std_id			norm;
@@ -570,8 +572,9 @@ static int tw9910_s_power(struct v4l2_subdev *sd, int on)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+	struct tw9910_priv *priv = to_tw9910(client);
 
-	return soc_camera_set_power(&client->dev, ssdd, on);
+	return soc_camera_set_power(&client->dev, ssdd, priv->clk, on);
 }
 
 static int tw9910_set_frame(struct v4l2_subdev *sd, u32 *width, u32 *height)
@@ -912,6 +915,7 @@ static int tw9910_probe(struct i2c_client *client,
 	struct i2c_adapter		*adapter  		to_i2c_adapter(client->dev.parent);
 	struct soc_camera_subdev_desc	*ssdd = soc_camera_i2c_to_desc(client);
+	int ret;
 
 	if (!ssdd || !ssdd->drv_priv) {
 		dev_err(&client->dev, "TW9910: missing platform data!\n");
@@ -935,7 +939,22 @@ static int tw9910_probe(struct i2c_client *client,
 
 	v4l2_i2c_subdev_init(&priv->subdev, client, &tw9910_subdev_ops);
 
-	return tw9910_video_probe(client);
+	priv->clk = v4l2_clk_get(&client->dev, "mclk");
+	if (IS_ERR(priv->clk))
+		return PTR_ERR(priv->clk);
+
+	ret = tw9910_video_probe(client);
+	if (ret < 0)
+		v4l2_clk_put(priv->clk);
+
+	return ret;
+}
+
+static int tw9910_remove(struct i2c_client *client)
+{
+	struct tw9910_priv *priv = to_tw9910(client);
+	v4l2_clk_put(priv->clk);
+	return 0;
 }
 
 static const struct i2c_device_id tw9910_id[] = {
@@ -949,6 +968,7 @@ static struct i2c_driver tw9910_i2c_driver = {
 		.name = "tw9910",
 	},
 	.probe    = tw9910_probe,
+	.remove   = tw9910_remove,
 	.id_table = tw9910_id,
 };
 
diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c
index ac98bcb..5845916 100644
--- a/drivers/media/platform/soc_camera/soc_camera.c
+++ b/drivers/media/platform/soc_camera/soc_camera.c
@@ -30,6 +30,7 @@
 #include <linux/vmalloc.h>
 
 #include <media/soc_camera.h>
+#include <media/v4l2-clk.h>
 #include <media/v4l2-common.h>
 #include <media/v4l2-ioctl.h>
 #include <media/v4l2-dev.h>
@@ -50,13 +51,19 @@ static LIST_HEAD(hosts);
 static LIST_HEAD(devices);
 static DEFINE_MUTEX(list_lock);		/* Protects the list of hosts */
 
-int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd)
+int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd,
+			struct v4l2_clk *clk)
 {
-	int ret = regulator_bulk_enable(ssdd->num_regulators,
+	int ret = clk ? v4l2_clk_enable(clk) : 0;
+	if (ret < 0) {
+		dev_err(dev, "Cannot enable clock\n");
+		return ret;
+	}
+	ret = regulator_bulk_enable(ssdd->num_regulators,
 					ssdd->regulators);
 	if (ret < 0) {
 		dev_err(dev, "Cannot enable regulators\n");
-		return ret;
+		goto eregenable;;
 	}
 
 	if (ssdd->power) {
@@ -64,16 +71,25 @@ int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd)
 		if (ret < 0) {
 			dev_err(dev,
 				"Platform failed to power-on the camera.\n");
-			regulator_bulk_disable(ssdd->num_regulators,
-					       ssdd->regulators);
+			goto epwron;
 		}
 	}
 
+	return 0;
+
+epwron:
+	regulator_bulk_disable(ssdd->num_regulators,
+			       ssdd->regulators);
+eregenable:
+	if (clk)
+		v4l2_clk_disable(clk);
+
 	return ret;
 }
 EXPORT_SYMBOL(soc_camera_power_on);
 
-int soc_camera_power_off(struct device *dev, struct soc_camera_subdev_desc *ssdd)
+int soc_camera_power_off(struct device *dev, struct soc_camera_subdev_desc *ssdd,
+			 struct v4l2_clk *clk)
 {
 	int ret = 0;
 	int err;
@@ -94,6 +110,9 @@ int soc_camera_power_off(struct device *dev, struct soc_camera_subdev_desc *ssdd
 		ret = ret ? : err;
 	}
 
+	if (clk)
+		v4l2_clk_disable(clk);
+
 	return ret;
 }
 EXPORT_SYMBOL(soc_camera_power_off);
@@ -513,9 +532,11 @@ static int soc_camera_add_device(struct soc_camera_device *icd)
 	if (ici->icd)
 		return -EBUSY;
 
-	ret = ici->ops->clock_start(ici);
-	if (ret < 0)
-		return ret;
+	if (!icd->clk) {
+		ret = ici->ops->clock_start(ici);
+		if (ret < 0)
+			return ret;
+	}
 
 	if (ici->ops->add) {
 		ret = ici->ops->add(icd);
@@ -528,7 +549,8 @@ static int soc_camera_add_device(struct soc_camera_device *icd)
 	return 0;
 
 eadd:
-	ici->ops->clock_stop(ici);
+	if (!icd->clk)
+		ici->ops->clock_stop(ici);
 	return ret;
 }
 
@@ -541,7 +563,8 @@ static void soc_camera_remove_device(struct soc_camera_device *icd)
 
 	if (ici->ops->remove)
 		ici->ops->remove(icd);
-	ici->ops->clock_stop(ici);
+	if (!icd->clk)
+		ici->ops->clock_stop(ici);
 	ici->icd = NULL;
 }
 
@@ -1124,6 +1147,57 @@ static void scan_add_host(struct soc_camera_host *ici)
 	mutex_unlock(&list_lock);
 }
 
+/*
+ * It is invalid to call v4l2_clk_enable() after a successful probing
+ * asynchronously outside of V4L2 operations, i.e. with .host_lock not held.
+ */
+static int soc_camera_clk_enable(struct v4l2_clk *clk)
+{
+	struct soc_camera_device *icd = clk->priv;
+	struct soc_camera_host *ici;
+
+	if (!icd || !icd->parent)
+		return -ENODEV;
+
+	ici = to_soc_camera_host(icd->parent);
+
+	if (!try_module_get(ici->ops->owner))
+		return -ENODEV;
+
+	/*
+	 * If a different client is currently being probed, the host will tell
+	 * you to go
+	 */
+	return ici->ops->clock_start(ici);
+}
+
+static void soc_camera_clk_disable(struct v4l2_clk *clk)
+{
+	struct soc_camera_device *icd = clk->priv;
+	struct soc_camera_host *ici;
+
+	if (!icd || !icd->parent)
+		return;
+
+	ici = to_soc_camera_host(icd->parent);
+
+	ici->ops->clock_stop(ici);
+
+	module_put(ici->ops->owner);
+}
+
+/*
+ * Eventually, it would be more logical to make the respective host the clock
+ * owner, but then we would have to copy this struct for each ici. Besides, it
+ * would introduce the circular dependency problem, unless we port all client
+ * drivers to release the clock, when not in use.
+ */
+static const struct v4l2_clk_ops soc_camera_clk_ops = {
+	.owner = THIS_MODULE,
+	.enable = soc_camera_clk_enable,
+	.disable = soc_camera_clk_disable,
+};
+
 #ifdef CONFIG_I2C_BOARDINFO
 static int soc_camera_init_i2c(struct soc_camera_device *icd,
 			       struct soc_camera_desc *sdesc)
@@ -1133,19 +1207,32 @@ static int soc_camera_init_i2c(struct soc_camera_device *icd,
 	struct soc_camera_host_desc *shd = &sdesc->host_desc;
 	struct i2c_adapter *adap = i2c_get_adapter(shd->i2c_adapter_id);
 	struct v4l2_subdev *subdev;
+	char clk_name[V4L2_SUBDEV_NAME_SIZE];
+	int ret;
 
 	if (!adap) {
 		dev_err(icd->pdev, "Cannot get I2C adapter #%d. No driver?\n",
 			shd->i2c_adapter_id);
-		goto ei2cga;
+		return -ENODEV;
 	}
 
 	shd->board_info->platform_data = &sdesc->subdev_desc;
 
+	snprintf(clk_name, sizeof(clk_name), "%d-%04x",
+		 shd->i2c_adapter_id, shd->board_info->addr);
+
+	icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, "mclk", icd);
+	if (IS_ERR(icd->clk)) {
+		ret = PTR_ERR(icd->clk);
+		goto eclkreg;
+	}
+
 	subdev = v4l2_i2c_new_subdev_board(&ici->v4l2_dev, adap,
 				shd->board_info, NULL);
-	if (!subdev)
+	if (!subdev) {
+		ret = -ENODEV;
 		goto ei2cnd;
+	}
 
 	client = v4l2_get_subdevdata(subdev);
 
@@ -1154,9 +1241,11 @@ static int soc_camera_init_i2c(struct soc_camera_device *icd,
 
 	return 0;
 ei2cnd:
+	v4l2_clk_unregister(icd->clk);
+	icd->clk = NULL;
+eclkreg:
 	i2c_put_adapter(adap);
-ei2cga:
-	return -ENODEV;
+	return ret;
 }
 
 static void soc_camera_free_i2c(struct soc_camera_device *icd)
@@ -1169,6 +1258,8 @@ static void soc_camera_free_i2c(struct soc_camera_device *icd)
 	v4l2_device_unregister_subdev(i2c_get_clientdata(client));
 	i2c_unregister_device(client);
 	i2c_put_adapter(adap);
+	v4l2_clk_unregister(icd->clk);
+	icd->clk = NULL;
 }
 #else
 #define soc_camera_init_i2c(icd, sdesc)	(-ENODEV)
@@ -1206,26 +1297,31 @@ static int soc_camera_probe(struct soc_camera_device *icd)
 	if (ssdd->reset)
 		ssdd->reset(icd->pdev);
 
-	mutex_lock(&ici->host_lock);
-	ret = ici->ops->clock_start(ici);
-	mutex_unlock(&ici->host_lock);
-	if (ret < 0)
-		goto eadd;
-
 	/* Must have icd->vdev before registering the device */
 	ret = video_dev_create(icd);
 	if (ret < 0)
 		goto evdc;
 
+	/*
+	 * ..._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 also during client probing.
+	 */
+	mutex_lock(&ici->host_lock);
+
 	/* Non-i2c cameras, e.g., soc_camera_platform, have no board_info */
 	if (shd->board_info) {
 		ret = soc_camera_init_i2c(icd, sdesc);
 		if (ret < 0)
-			goto eadddev;
+			goto eadd;
 	} else if (!shd->add_device || !shd->del_device) {
 		ret = -EINVAL;
-		goto eadddev;
+		goto eadd;
 	} else {
+		ret = ici->ops->clock_start(ici);
+		if (ret < 0)
+			goto eadd;
+
 		if (shd->module_name)
 			ret = request_module(shd->module_name);
 
@@ -1261,13 +1357,6 @@ static int soc_camera_probe(struct soc_camera_device *icd)
 
 	icd->field = V4L2_FIELD_ANY;
 
-	/*
-	 * ..._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(&ici->host_lock);
-
 	ret = soc_camera_video_start(icd);
 	if (ret < 0)
 		goto evidstart;
@@ -1280,14 +1369,14 @@ static int soc_camera_probe(struct soc_camera_device *icd)
 		icd->field		= mf.field;
 	}
 
-	ici->ops->clock_stop(ici);
+	if (!shd->board_info)
+		ici->ops->clock_stop(ici);
 
 	mutex_unlock(&ici->host_lock);
 
 	return 0;
 
 evidstart:
-	mutex_unlock(&ici->host_lock);
 	soc_camera_free_user_formats(icd);
 eiufmt:
 ectrl:
@@ -1296,16 +1385,15 @@ ectrl:
 	} else {
 		shd->del_device(icd);
 		module_put(control->driver->owner);
-	}
 enodrv:
 eadddev:
+		ici->ops->clock_stop(ici);
+	}
+eadd:
 	video_device_release(icd->vdev);
 	icd->vdev = NULL;
-evdc:
-	mutex_lock(&ici->host_lock);
-	ici->ops->clock_stop(ici);
 	mutex_unlock(&ici->host_lock);
-eadd:
+evdc:
 	v4l2_ctrl_handler_free(&icd->ctrl_handler);
 	return ret;
 }
diff --git a/drivers/media/platform/soc_camera/soc_camera_platform.c b/drivers/media/platform/soc_camera/soc_camera_platform.c
index bdf909d..ceaddfb 100644
--- a/drivers/media/platform/soc_camera/soc_camera_platform.c
+++ b/drivers/media/platform/soc_camera/soc_camera_platform.c
@@ -54,7 +54,7 @@ static int soc_camera_platform_s_power(struct v4l2_subdev *sd, int on)
 {
 	struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
 
-	return soc_camera_set_power(p->icd->control, &p->icd->sdesc->subdev_desc, on);
+	return soc_camera_set_power(p->icd->control, &p->icd->sdesc->subdev_desc, NULL, on);
 }
 
 static struct v4l2_subdev_core_ops platform_subdev_core_ops = {
diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h
index 64415ee..f8c1ffe 100644
--- a/include/media/soc_camera.h
+++ b/include/media/soc_camera.h
@@ -49,6 +49,7 @@ struct soc_camera_device {
 	/* soc_camera.c private count. Only accessed with .host_lock held */
 	int use_count;
 	struct file *streamer;		/* stream owner */
+	struct v4l2_clk *clk;
 	union {
 		struct videobuf_queue vb_vidq;
 		struct vb2_queue vb2_vidq;
@@ -325,14 +326,16 @@ static inline void soc_camera_limit_side(int *start, int *length,
 unsigned long soc_camera_apply_board_flags(struct soc_camera_subdev_desc *ssdd,
 					   const struct v4l2_mbus_config *cfg);
 
-int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd);
-int soc_camera_power_off(struct device *dev, struct soc_camera_subdev_desc *ssdd);
+int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd,
+			struct v4l2_clk *clk);
+int soc_camera_power_off(struct device *dev, struct soc_camera_subdev_desc *ssdd,
+			 struct v4l2_clk *clk);
 
 static inline int soc_camera_set_power(struct device *dev,
-				struct soc_camera_subdev_desc *ssdd, bool on)
+		struct soc_camera_subdev_desc *ssdd, struct v4l2_clk *clk, bool on)
 {
-	return on ? soc_camera_power_on(dev, ssdd)
-		  : soc_camera_power_off(dev, ssdd);
+	return on ? soc_camera_power_on(dev, ssdd, clk)
+		  : soc_camera_power_off(dev, ssdd, clk);
 }
 
 /* This is only temporary here - until v4l2-subdev begins to link to video_device */
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v10 17/21] soc-camera: switch I2C subdevice drivers to use v4l2-clk
@ 2013-06-11  8:23   ` Guennadi Liakhovetski
  0 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-11  8:23 UTC (permalink / raw)
  To: linux-media
  Cc: Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer,
	Guennadi Liakhovetski

Instead of centrally enabling and disabling subdevice master clocks in
soc-camera core, let subdevice drivers do that themselves, using the
V4L2 clock API and soc-camera convenience wrappers.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
 drivers/media/i2c/soc_camera/imx074.c              |   18 ++-
 drivers/media/i2c/soc_camera/mt9m001.c             |   17 ++-
 drivers/media/i2c/soc_camera/mt9m111.c             |   20 ++-
 drivers/media/i2c/soc_camera/mt9t031.c             |   19 ++-
 drivers/media/i2c/soc_camera/mt9t112.c             |   25 +++-
 drivers/media/i2c/soc_camera/mt9v022.c             |   17 ++-
 drivers/media/i2c/soc_camera/ov2640.c              |   19 ++-
 drivers/media/i2c/soc_camera/ov5642.c              |   20 ++-
 drivers/media/i2c/soc_camera/ov6650.c              |   17 ++-
 drivers/media/i2c/soc_camera/ov772x.c              |   15 ++-
 drivers/media/i2c/soc_camera/ov9640.c              |   17 ++-
 drivers/media/i2c/soc_camera/ov9640.h              |    1 +
 drivers/media/i2c/soc_camera/ov9740.c              |   18 ++-
 drivers/media/i2c/soc_camera/rj54n1cb0c.c          |   17 ++-
 drivers/media/i2c/soc_camera/tw9910.c              |   24 +++-
 drivers/media/platform/soc_camera/soc_camera.c     |  162 +++++++++++++++-----
 .../platform/soc_camera/soc_camera_platform.c      |    2 +-
 include/media/soc_camera.h                         |   13 +-
 18 files changed, 362 insertions(+), 79 deletions(-)

diff --git a/drivers/media/i2c/soc_camera/imx074.c b/drivers/media/i2c/soc_camera/imx074.c
index a2a5cbb..a6a5060 100644
--- a/drivers/media/i2c/soc_camera/imx074.c
+++ b/drivers/media/i2c/soc_camera/imx074.c
@@ -18,6 +18,7 @@
 #include <linux/module.h>
 
 #include <media/soc_camera.h>
+#include <media/v4l2-clk.h>
 #include <media/v4l2-subdev.h>
 #include <media/v4l2-chip-ident.h>
 
@@ -77,6 +78,7 @@ struct imx074_datafmt {
 struct imx074 {
 	struct v4l2_subdev		subdev;
 	const struct imx074_datafmt	*fmt;
+	struct v4l2_clk			*clk;
 };
 
 static const struct imx074_datafmt imx074_colour_fmts[] = {
@@ -272,8 +274,9 @@ static int imx074_s_power(struct v4l2_subdev *sd, int on)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+	struct imx074 *priv = to_imx074(client);
 
-	return soc_camera_set_power(&client->dev, ssdd, on);
+	return soc_camera_set_power(&client->dev, ssdd, priv->clk, on);
 }
 
 static int imx074_g_mbus_config(struct v4l2_subdev *sd,
@@ -431,6 +434,7 @@ static int imx074_probe(struct i2c_client *client,
 	struct imx074 *priv;
 	struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+	int ret;
 
 	if (!ssdd) {
 		dev_err(&client->dev, "IMX074: missing platform data!\n");
@@ -451,13 +455,23 @@ static int imx074_probe(struct i2c_client *client,
 
 	priv->fmt	= &imx074_colour_fmts[0];
 
-	return imx074_video_probe(client);
+	priv->clk = v4l2_clk_get(&client->dev, "mclk");
+	if (IS_ERR(priv->clk))
+		return PTR_ERR(priv->clk);
+
+	ret = imx074_video_probe(client);
+	if (ret < 0)
+		v4l2_clk_put(priv->clk);
+
+	return ret;
 }
 
 static int imx074_remove(struct i2c_client *client)
 {
 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+	struct imx074 *priv = to_imx074(client);
 
+	v4l2_clk_put(priv->clk);
 	if (ssdd->free_bus)
 		ssdd->free_bus(ssdd);
 
diff --git a/drivers/media/i2c/soc_camera/mt9m001.c b/drivers/media/i2c/soc_camera/mt9m001.c
index dd90898..620f9df 100644
--- a/drivers/media/i2c/soc_camera/mt9m001.c
+++ b/drivers/media/i2c/soc_camera/mt9m001.c
@@ -16,6 +16,7 @@
 
 #include <media/soc_camera.h>
 #include <media/soc_mediabus.h>
+#include <media/v4l2-clk.h>
 #include <media/v4l2-subdev.h>
 #include <media/v4l2-chip-ident.h>
 #include <media/v4l2-ctrls.h>
@@ -94,6 +95,7 @@ struct mt9m001 {
 		struct v4l2_ctrl *exposure;
 	};
 	struct v4l2_rect rect;	/* Sensor window */
+	struct v4l2_clk *clk;
 	const struct mt9m001_datafmt *fmt;
 	const struct mt9m001_datafmt *fmts;
 	int num_fmts;
@@ -381,8 +383,9 @@ static int mt9m001_s_power(struct v4l2_subdev *sd, int on)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+	struct mt9m001 *mt9m001 = to_mt9m001(client);
 
-	return soc_camera_set_power(&client->dev, ssdd, on);
+	return soc_camera_set_power(&client->dev, ssdd, mt9m001->clk, on);
 }
 
 static int mt9m001_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
@@ -710,9 +713,18 @@ static int mt9m001_probe(struct i2c_client *client,
 	mt9m001->rect.width	= MT9M001_MAX_WIDTH;
 	mt9m001->rect.height	= MT9M001_MAX_HEIGHT;
 
+	mt9m001->clk = v4l2_clk_get(&client->dev, "mclk");
+	if (IS_ERR(mt9m001->clk)) {
+		ret = PTR_ERR(mt9m001->clk);
+		goto eclkget;
+	}
+
 	ret = mt9m001_video_probe(ssdd, client);
-	if (ret)
+	if (ret) {
+		v4l2_clk_put(mt9m001->clk);
+eclkget:
 		v4l2_ctrl_handler_free(&mt9m001->hdl);
+	}
 
 	return ret;
 }
@@ -722,6 +734,7 @@ static int mt9m001_remove(struct i2c_client *client)
 	struct mt9m001 *mt9m001 = to_mt9m001(client);
 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
 
+	v4l2_clk_put(mt9m001->clk);
 	v4l2_device_unregister_subdev(&mt9m001->subdev);
 	v4l2_ctrl_handler_free(&mt9m001->hdl);
 	mt9m001_video_remove(ssdd);
diff --git a/drivers/media/i2c/soc_camera/mt9m111.c b/drivers/media/i2c/soc_camera/mt9m111.c
index 8bd4e0d..5fa9372 100644
--- a/drivers/media/i2c/soc_camera/mt9m111.c
+++ b/drivers/media/i2c/soc_camera/mt9m111.c
@@ -17,6 +17,7 @@
 #include <linux/module.h>
 
 #include <media/soc_camera.h>
+#include <media/v4l2-clk.h>
 #include <media/v4l2-common.h>
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-chip-ident.h>
@@ -209,6 +210,7 @@ struct mt9m111 {
 			 * from v4l2-chip-ident.h */
 	struct mt9m111_context *ctx;
 	struct v4l2_rect rect;	/* cropping rectangle */
+	struct v4l2_clk *clk;
 	int width;		/* output */
 	int height;		/* sizes */
 	struct mutex power_lock; /* lock to protect power_count */
@@ -801,14 +803,14 @@ static int mt9m111_power_on(struct mt9m111 *mt9m111)
 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
 	int ret;
 
-	ret = soc_camera_power_on(&client->dev, ssdd);
+	ret = soc_camera_power_on(&client->dev, ssdd, mt9m111->clk);
 	if (ret < 0)
 		return ret;
 
 	ret = mt9m111_resume(mt9m111);
 	if (ret < 0) {
 		dev_err(&client->dev, "Failed to resume the sensor: %d\n", ret);
-		soc_camera_power_off(&client->dev, ssdd);
+		soc_camera_power_off(&client->dev, ssdd, mt9m111->clk);
 	}
 
 	return ret;
@@ -820,7 +822,7 @@ static void mt9m111_power_off(struct mt9m111 *mt9m111)
 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
 
 	mt9m111_suspend(mt9m111);
-	soc_camera_power_off(&client->dev, ssdd);
+	soc_camera_power_off(&client->dev, ssdd, mt9m111->clk);
 }
 
 static int mt9m111_s_power(struct v4l2_subdev *sd, int on)
@@ -1002,9 +1004,18 @@ static int mt9m111_probe(struct i2c_client *client,
 	mt9m111->lastpage	= -1;
 	mutex_init(&mt9m111->power_lock);
 
+	mt9m111->clk = v4l2_clk_get(&client->dev, "mclk");
+	if (IS_ERR(mt9m111->clk)) {
+		ret = PTR_ERR(mt9m111->clk);
+		goto eclkget;
+	}
+
 	ret = mt9m111_video_probe(client);
-	if (ret)
+	if (ret) {
+		v4l2_clk_put(mt9m111->clk);
+eclkget:
 		v4l2_ctrl_handler_free(&mt9m111->hdl);
+	}
 
 	return ret;
 }
@@ -1013,6 +1024,7 @@ static int mt9m111_remove(struct i2c_client *client)
 {
 	struct mt9m111 *mt9m111 = to_mt9m111(client);
 
+	v4l2_clk_put(mt9m111->clk);
 	v4l2_device_unregister_subdev(&mt9m111->subdev);
 	v4l2_ctrl_handler_free(&mt9m111->hdl);
 
diff --git a/drivers/media/i2c/soc_camera/mt9t031.c b/drivers/media/i2c/soc_camera/mt9t031.c
index 191e3f1..1740f43 100644
--- a/drivers/media/i2c/soc_camera/mt9t031.c
+++ b/drivers/media/i2c/soc_camera/mt9t031.c
@@ -19,6 +19,7 @@
 
 #include <media/soc_camera.h>
 #include <media/v4l2-chip-ident.h>
+#include <media/v4l2-clk.h>
 #include <media/v4l2-subdev.h>
 #include <media/v4l2-ctrls.h>
 
@@ -76,6 +77,7 @@ struct mt9t031 {
 		struct v4l2_ctrl *exposure;
 	};
 	struct v4l2_rect rect;	/* Sensor window */
+	struct v4l2_clk *clk;
 	int model;	/* V4L2_IDENT_MT9T031* codes from v4l2-chip-ident.h */
 	u16 xskip;
 	u16 yskip;
@@ -610,16 +612,17 @@ static int mt9t031_s_power(struct v4l2_subdev *sd, int on)
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
 	struct video_device *vdev = soc_camera_i2c_to_vdev(client);
+	struct mt9t031 *mt9t031 = to_mt9t031(client);
 	int ret;
 
 	if (on) {
-		ret = soc_camera_power_on(&client->dev, ssdd);
+		ret = soc_camera_power_on(&client->dev, ssdd, mt9t031->clk);
 		if (ret < 0)
 			return ret;
 		vdev->dev.type = &mt9t031_dev_type;
 	} else {
 		vdev->dev.type = NULL;
-		soc_camera_power_off(&client->dev, ssdd);
+		soc_camera_power_off(&client->dev, ssdd, mt9t031->clk);
 	}
 
 	return 0;
@@ -812,9 +815,18 @@ static int mt9t031_probe(struct i2c_client *client,
 	mt9t031->xskip = 1;
 	mt9t031->yskip = 1;
 
+	mt9t031->clk = v4l2_clk_get(&client->dev, "mclk");
+	if (IS_ERR(mt9t031->clk)) {
+		ret = PTR_ERR(mt9t031->clk);
+		goto eclkget;
+	}
+
 	ret = mt9t031_video_probe(client);
-	if (ret)
+	if (ret) {
+		v4l2_clk_put(mt9t031->clk);
+eclkget:
 		v4l2_ctrl_handler_free(&mt9t031->hdl);
+	}
 
 	return ret;
 }
@@ -823,6 +835,7 @@ static int mt9t031_remove(struct i2c_client *client)
 {
 	struct mt9t031 *mt9t031 = to_mt9t031(client);
 
+	v4l2_clk_put(mt9t031->clk);
 	v4l2_device_unregister_subdev(&mt9t031->subdev);
 	v4l2_ctrl_handler_free(&mt9t031->hdl);
 
diff --git a/drivers/media/i2c/soc_camera/mt9t112.c b/drivers/media/i2c/soc_camera/mt9t112.c
index 0af29a4..577cdea 100644
--- a/drivers/media/i2c/soc_camera/mt9t112.c
+++ b/drivers/media/i2c/soc_camera/mt9t112.c
@@ -28,6 +28,7 @@
 #include <media/mt9t112.h>
 #include <media/soc_camera.h>
 #include <media/v4l2-chip-ident.h>
+#include <media/v4l2-clk.h>
 #include <media/v4l2-common.h>
 
 /* you can check PLL/clock info */
@@ -90,6 +91,7 @@ struct mt9t112_priv {
 	struct mt9t112_camera_info	*info;
 	struct i2c_client		*client;
 	struct v4l2_rect		 frame;
+	struct v4l2_clk			*clk;
 	const struct mt9t112_format	*format;
 	int				 model;
 	int				 num_formats;
@@ -781,8 +783,9 @@ static int mt9t112_s_power(struct v4l2_subdev *sd, int on)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+	struct mt9t112_priv *priv = to_mt9t112(client);
 
-	return soc_camera_set_power(&client->dev, ssdd, on);
+	return soc_camera_set_power(&client->dev, ssdd, priv->clk, on);
 }
 
 static struct v4l2_subdev_core_ops mt9t112_subdev_core_ops = {
@@ -1108,16 +1111,29 @@ static int mt9t112_probe(struct i2c_client *client,
 
 	v4l2_i2c_subdev_init(&priv->subdev, client, &mt9t112_subdev_ops);
 
+	priv->clk = v4l2_clk_get(&client->dev, "mclk");
+	if (IS_ERR(priv->clk))
+		return PTR_ERR(priv->clk);
+
 	ret = mt9t112_camera_probe(client);
-	if (ret)
-		return ret;
 
 	/* Cannot fail: using the default supported pixel code */
-	mt9t112_set_params(priv, &rect, V4L2_MBUS_FMT_UYVY8_2X8);
+	if (!ret)
+		mt9t112_set_params(priv, &rect, V4L2_MBUS_FMT_UYVY8_2X8);
+	else
+		v4l2_clk_put(priv->clk);
 
 	return ret;
 }
 
+static int mt9t112_remove(struct i2c_client *client)
+{
+	struct mt9t112_priv *priv = to_mt9t112(client);
+
+	v4l2_clk_put(priv->clk);
+	return 0;
+}
+
 static const struct i2c_device_id mt9t112_id[] = {
 	{ "mt9t112", 0 },
 	{ }
@@ -1129,6 +1145,7 @@ static struct i2c_driver mt9t112_i2c_driver = {
 		.name = "mt9t112",
 	},
 	.probe    = mt9t112_probe,
+	.remove   = mt9t112_remove,
 	.id_table = mt9t112_id,
 };
 
diff --git a/drivers/media/i2c/soc_camera/mt9v022.c b/drivers/media/i2c/soc_camera/mt9v022.c
index a295e59..3164f56 100644
--- a/drivers/media/i2c/soc_camera/mt9v022.c
+++ b/drivers/media/i2c/soc_camera/mt9v022.c
@@ -20,6 +20,7 @@
 #include <media/soc_mediabus.h>
 #include <media/v4l2-subdev.h>
 #include <media/v4l2-chip-ident.h>
+#include <media/v4l2-clk.h>
 #include <media/v4l2-ctrls.h>
 
 /*
@@ -149,6 +150,7 @@ struct mt9v022 {
 	struct v4l2_ctrl *hblank;
 	struct v4l2_ctrl *vblank;
 	struct v4l2_rect rect;	/* Sensor window */
+	struct v4l2_clk *clk;
 	const struct mt9v022_datafmt *fmt;
 	const struct mt9v022_datafmt *fmts;
 	const struct mt9v02x_register *reg;
@@ -518,8 +520,9 @@ static int mt9v022_s_power(struct v4l2_subdev *sd, int on)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+	struct mt9v022 *mt9v022 = to_mt9v022(client);
 
-	return soc_camera_set_power(&client->dev, ssdd, on);
+	return soc_camera_set_power(&client->dev, ssdd, mt9v022->clk, on);
 }
 
 static int mt9v022_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
@@ -957,9 +960,18 @@ static int mt9v022_probe(struct i2c_client *client,
 	mt9v022->rect.width	= MT9V022_MAX_WIDTH;
 	mt9v022->rect.height	= MT9V022_MAX_HEIGHT;
 
+	mt9v022->clk = v4l2_clk_get(&client->dev, "mclk");
+	if (IS_ERR(mt9v022->clk)) {
+		ret = PTR_ERR(mt9v022->clk);
+		goto eclkget;
+	}
+
 	ret = mt9v022_video_probe(client);
-	if (ret)
+	if (ret) {
+		v4l2_clk_put(mt9v022->clk);
+eclkget:
 		v4l2_ctrl_handler_free(&mt9v022->hdl);
+	}
 
 	return ret;
 }
@@ -969,6 +981,7 @@ static int mt9v022_remove(struct i2c_client *client)
 	struct mt9v022 *mt9v022 = to_mt9v022(client);
 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
 
+	v4l2_clk_put(mt9v022->clk);
 	v4l2_device_unregister_subdev(&mt9v022->subdev);
 	if (ssdd->free_bus)
 		ssdd->free_bus(ssdd);
diff --git a/drivers/media/i2c/soc_camera/ov2640.c b/drivers/media/i2c/soc_camera/ov2640.c
index e316842..0c97b8c 100644
--- a/drivers/media/i2c/soc_camera/ov2640.c
+++ b/drivers/media/i2c/soc_camera/ov2640.c
@@ -23,6 +23,7 @@
 
 #include <media/soc_camera.h>
 #include <media/v4l2-chip-ident.h>
+#include <media/v4l2-clk.h>
 #include <media/v4l2-subdev.h>
 #include <media/v4l2-ctrls.h>
 
@@ -303,6 +304,7 @@ struct ov2640_priv {
 	struct v4l2_subdev		subdev;
 	struct v4l2_ctrl_handler	hdl;
 	enum v4l2_mbus_pixelcode	cfmt_code;
+	struct v4l2_clk			*clk;
 	const struct ov2640_win_size	*win;
 	int				model;
 };
@@ -772,8 +774,9 @@ static int ov2640_s_power(struct v4l2_subdev *sd, int on)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+	struct ov2640_priv *priv = to_ov2640(client);
 
-	return soc_camera_set_power(&client->dev, ssdd, on);
+	return soc_camera_set_power(&client->dev, ssdd, priv->clk, on);
 }
 
 /* Select the nearest higher resolution for capture */
@@ -1113,11 +1116,20 @@ static int ov2640_probe(struct i2c_client *client,
 	if (priv->hdl.error)
 		return priv->hdl.error;
 
+	priv->clk = v4l2_clk_get(&client->dev, "mclk");
+	if (IS_ERR(priv->clk)) {
+		ret = PTR_ERR(priv->clk);
+		goto eclkget;
+	}
+
 	ret = ov2640_video_probe(client);
-	if (ret)
+	if (ret) {
+		v4l2_clk_put(priv->clk);
+eclkget:
 		v4l2_ctrl_handler_free(&priv->hdl);
-	else
+	} else {
 		dev_info(&adapter->dev, "OV2640 Probed\n");
+	}
 
 	return ret;
 }
@@ -1126,6 +1138,7 @@ static int ov2640_remove(struct i2c_client *client)
 {
 	struct ov2640_priv       *priv = to_ov2640(client);
 
+	v4l2_clk_put(priv->clk);
 	v4l2_device_unregister_subdev(&priv->subdev);
 	v4l2_ctrl_handler_free(&priv->hdl);
 	return 0;
diff --git a/drivers/media/i2c/soc_camera/ov5642.c b/drivers/media/i2c/soc_camera/ov5642.c
index 9aa56de..8ffc8f9 100644
--- a/drivers/media/i2c/soc_camera/ov5642.c
+++ b/drivers/media/i2c/soc_camera/ov5642.c
@@ -25,6 +25,7 @@
 
 #include <media/soc_camera.h>
 #include <media/v4l2-chip-ident.h>
+#include <media/v4l2-clk.h>
 #include <media/v4l2-subdev.h>
 
 /* OV5642 registers */
@@ -610,6 +611,7 @@ struct ov5642 {
 	struct v4l2_subdev		subdev;
 	const struct ov5642_datafmt	*fmt;
 	struct v4l2_rect                crop_rect;
+	struct v4l2_clk			*clk;
 
 	/* blanking information */
 	int total_width;
@@ -935,12 +937,13 @@ static int ov5642_s_power(struct v4l2_subdev *sd, int on)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+	struct ov5642 *priv = to_ov5642(client);
 	int ret;
 
 	if (!on)
-		return soc_camera_power_off(&client->dev, ssdd);
+		return soc_camera_power_off(&client->dev, ssdd, priv->clk);
 
-	ret = soc_camera_power_on(&client->dev, ssdd);
+	ret = soc_camera_power_on(&client->dev, ssdd, priv->clk);
 	if (ret < 0)
 		return ret;
 
@@ -1021,6 +1024,7 @@ static int ov5642_probe(struct i2c_client *client,
 {
 	struct ov5642 *priv;
 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+	int ret;
 
 	if (!ssdd) {
 		dev_err(&client->dev, "OV5642: missing platform data!\n");
@@ -1042,13 +1046,23 @@ static int ov5642_probe(struct i2c_client *client,
 	priv->total_width = OV5642_DEFAULT_WIDTH + BLANKING_EXTRA_WIDTH;
 	priv->total_height = BLANKING_MIN_HEIGHT;
 
-	return ov5642_video_probe(client);
+	priv->clk = v4l2_clk_get(&client->dev, "mclk");
+	if (IS_ERR(priv->clk))
+		return PTR_ERR(priv->clk);
+
+	ret = ov5642_video_probe(client);
+	if (ret < 0)
+		v4l2_clk_put(priv->clk);
+
+	return ret;
 }
 
 static int ov5642_remove(struct i2c_client *client)
 {
 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+	struct ov5642 *priv = to_ov5642(client);
 
+	v4l2_clk_put(priv->clk);
 	if (ssdd->free_bus)
 		ssdd->free_bus(ssdd);
 
diff --git a/drivers/media/i2c/soc_camera/ov6650.c b/drivers/media/i2c/soc_camera/ov6650.c
index 991202d..2389aa2 100644
--- a/drivers/media/i2c/soc_camera/ov6650.c
+++ b/drivers/media/i2c/soc_camera/ov6650.c
@@ -33,6 +33,7 @@
 
 #include <media/soc_camera.h>
 #include <media/v4l2-chip-ident.h>
+#include <media/v4l2-clk.h>
 #include <media/v4l2-ctrls.h>
 
 /* Register definitions */
@@ -196,6 +197,7 @@ struct ov6650 {
 		struct v4l2_ctrl *blue;
 		struct v4l2_ctrl *red;
 	};
+	struct v4l2_clk		*clk;
 	bool			half_scale;	/* scale down output by 2 */
 	struct v4l2_rect	rect;		/* sensor cropping window */
 	unsigned long		pclk_limit;	/* from host */
@@ -436,8 +438,9 @@ static int ov6650_s_power(struct v4l2_subdev *sd, int on)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+	struct ov6650 *priv = to_ov6650(client);
 
-	return soc_camera_set_power(&client->dev, ssdd, on);
+	return soc_camera_set_power(&client->dev, ssdd, priv->clk, on);
 }
 
 static int ov6650_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
@@ -1025,9 +1028,18 @@ static int ov6650_probe(struct i2c_client *client,
 	priv->code	  = V4L2_MBUS_FMT_YUYV8_2X8;
 	priv->colorspace  = V4L2_COLORSPACE_JPEG;
 
+	priv->clk = v4l2_clk_get(&client->dev, "mclk");
+	if (IS_ERR(priv->clk)) {
+		ret = PTR_ERR(priv->clk);
+		goto eclkget;
+	}
+
 	ret = ov6650_video_probe(client);
-	if (ret)
+	if (ret) {
+		v4l2_clk_put(priv->clk);
+eclkget:
 		v4l2_ctrl_handler_free(&priv->hdl);
+	}
 
 	return ret;
 }
@@ -1036,6 +1048,7 @@ static int ov6650_remove(struct i2c_client *client)
 {
 	struct ov6650 *priv = to_ov6650(client);
 
+	v4l2_clk_put(priv->clk);
 	v4l2_device_unregister_subdev(&priv->subdev);
 	v4l2_ctrl_handler_free(&priv->hdl);
 	return 0;
diff --git a/drivers/media/i2c/soc_camera/ov772x.c b/drivers/media/i2c/soc_camera/ov772x.c
index 713d62e..d643b69 100644
--- a/drivers/media/i2c/soc_camera/ov772x.c
+++ b/drivers/media/i2c/soc_camera/ov772x.c
@@ -26,6 +26,7 @@
 
 #include <media/ov772x.h>
 #include <media/soc_camera.h>
+#include <media/v4l2-clk.h>
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-chip-ident.h>
 #include <media/v4l2-subdev.h>
@@ -396,6 +397,7 @@ struct ov772x_win_size {
 struct ov772x_priv {
 	struct v4l2_subdev                subdev;
 	struct v4l2_ctrl_handler	  hdl;
+	struct v4l2_clk			 *clk;
 	struct ov772x_camera_info        *info;
 	const struct ov772x_color_format *cfmt;
 	const struct ov772x_win_size     *win;
@@ -668,8 +670,9 @@ static int ov772x_s_power(struct v4l2_subdev *sd, int on)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+	struct ov772x_priv *priv = to_ov772x(sd);
 
-	return soc_camera_set_power(&client->dev, ssdd, on);
+	return soc_camera_set_power(&client->dev, ssdd, priv->clk, on);
 }
 
 static const struct ov772x_win_size *ov772x_select_win(u32 width, u32 height)
@@ -1088,13 +1091,22 @@ static int ov772x_probe(struct i2c_client *client,
 	if (priv->hdl.error)
 		return priv->hdl.error;
 
+	priv->clk = v4l2_clk_get(&client->dev, "mclk");
+	if (IS_ERR(priv->clk)) {
+		ret = PTR_ERR(priv->clk);
+		goto eclkget;
+	}
+
 	ret = ov772x_video_probe(priv);
 	if (ret < 0) {
+		v4l2_clk_put(priv->clk);
+eclkget:
 		v4l2_ctrl_handler_free(&priv->hdl);
 	} else {
 		priv->cfmt = &ov772x_cfmts[0];
 		priv->win = &ov772x_win_sizes[0];
 	}
+
 	return ret;
 }
 
@@ -1102,6 +1114,7 @@ static int ov772x_remove(struct i2c_client *client)
 {
 	struct ov772x_priv *priv = to_ov772x(i2c_get_clientdata(client));
 
+	v4l2_clk_put(priv->clk);
 	v4l2_device_unregister_subdev(&priv->subdev);
 	v4l2_ctrl_handler_free(&priv->hdl);
 	return 0;
diff --git a/drivers/media/i2c/soc_camera/ov9640.c b/drivers/media/i2c/soc_camera/ov9640.c
index f4eba1b..681ed7e 100644
--- a/drivers/media/i2c/soc_camera/ov9640.c
+++ b/drivers/media/i2c/soc_camera/ov9640.c
@@ -29,6 +29,7 @@
 
 #include <media/soc_camera.h>
 #include <media/v4l2-chip-ident.h>
+#include <media/v4l2-clk.h>
 #include <media/v4l2-common.h>
 #include <media/v4l2-ctrls.h>
 
@@ -337,8 +338,9 @@ static int ov9640_s_power(struct v4l2_subdev *sd, int on)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+	struct ov9640_priv *priv = to_ov9640_sensor(sd);
 
-	return soc_camera_set_power(&client->dev, ssdd, on);
+	return soc_camera_set_power(&client->dev, ssdd, priv->clk, on);
 }
 
 /* select nearest higher resolution for capture */
@@ -716,10 +718,18 @@ static int ov9640_probe(struct i2c_client *client,
 	if (priv->hdl.error)
 		return priv->hdl.error;
 
-	ret = ov9640_video_probe(client);
+	priv->clk = v4l2_clk_get(&client->dev, "mclk");
+	if (IS_ERR(priv->clk)) {
+		ret = PTR_ERR(priv->clk);
+		goto eclkget;
+	}
 
-	if (ret)
+	ret = ov9640_video_probe(client);
+	if (ret) {
+		v4l2_clk_put(priv->clk);
+eclkget:
 		v4l2_ctrl_handler_free(&priv->hdl);
+	}
 
 	return ret;
 }
@@ -729,6 +739,7 @@ static int ov9640_remove(struct i2c_client *client)
 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
 	struct ov9640_priv *priv = to_ov9640_sensor(sd);
 
+	v4l2_clk_put(priv->clk);
 	v4l2_device_unregister_subdev(&priv->subdev);
 	v4l2_ctrl_handler_free(&priv->hdl);
 	return 0;
diff --git a/drivers/media/i2c/soc_camera/ov9640.h b/drivers/media/i2c/soc_camera/ov9640.h
index 6b33a97..65d13ff 100644
--- a/drivers/media/i2c/soc_camera/ov9640.h
+++ b/drivers/media/i2c/soc_camera/ov9640.h
@@ -199,6 +199,7 @@ struct ov9640_reg {
 struct ov9640_priv {
 	struct v4l2_subdev		subdev;
 	struct v4l2_ctrl_handler	hdl;
+	struct v4l2_clk			*clk;
 
 	int				model;
 	int				revision;
diff --git a/drivers/media/i2c/soc_camera/ov9740.c b/drivers/media/i2c/soc_camera/ov9740.c
index 012bd62..6a5e164 100644
--- a/drivers/media/i2c/soc_camera/ov9740.c
+++ b/drivers/media/i2c/soc_camera/ov9740.c
@@ -18,6 +18,7 @@
 
 #include <media/soc_camera.h>
 #include <media/v4l2-chip-ident.h>
+#include <media/v4l2-clk.h>
 #include <media/v4l2-ctrls.h>
 
 #define to_ov9740(sd)		container_of(sd, struct ov9740_priv, subdev)
@@ -196,6 +197,7 @@ struct ov9740_reg {
 struct ov9740_priv {
 	struct v4l2_subdev		subdev;
 	struct v4l2_ctrl_handler	hdl;
+	struct v4l2_clk			*clk;
 
 	int				ident;
 	u16				model;
@@ -792,7 +794,7 @@ static int ov9740_s_power(struct v4l2_subdev *sd, int on)
 	int ret;
 
 	if (on) {
-		ret = soc_camera_power_on(&client->dev, ssdd);
+		ret = soc_camera_power_on(&client->dev, ssdd, priv->clk);
 		if (ret < 0)
 			return ret;
 
@@ -806,7 +808,7 @@ static int ov9740_s_power(struct v4l2_subdev *sd, int on)
 			priv->current_enable = true;
 		}
 
-		soc_camera_power_off(&client->dev, ssdd);
+		soc_camera_power_off(&client->dev, ssdd, priv->clk);
 	}
 
 	return 0;
@@ -975,9 +977,18 @@ static int ov9740_probe(struct i2c_client *client,
 	if (priv->hdl.error)
 		return priv->hdl.error;
 
+	priv->clk = v4l2_clk_get(&client->dev, "mclk");
+	if (IS_ERR(priv->clk)) {
+		ret = PTR_ERR(priv->clk);
+		goto eclkget;
+	}
+
 	ret = ov9740_video_probe(client);
-	if (ret < 0)
+	if (ret < 0) {
+		v4l2_clk_put(priv->clk);
+eclkget:
 		v4l2_ctrl_handler_free(&priv->hdl);
+	}
 
 	return ret;
 }
@@ -986,6 +997,7 @@ static int ov9740_remove(struct i2c_client *client)
 {
 	struct ov9740_priv *priv = i2c_get_clientdata(client);
 
+	v4l2_clk_put(priv->clk);
 	v4l2_device_unregister_subdev(&priv->subdev);
 	v4l2_ctrl_handler_free(&priv->hdl);
 	return 0;
diff --git a/drivers/media/i2c/soc_camera/rj54n1cb0c.c b/drivers/media/i2c/soc_camera/rj54n1cb0c.c
index 1f9ec3b..93db388 100644
--- a/drivers/media/i2c/soc_camera/rj54n1cb0c.c
+++ b/drivers/media/i2c/soc_camera/rj54n1cb0c.c
@@ -17,6 +17,7 @@
 
 #include <media/rj54n1cb0c.h>
 #include <media/soc_camera.h>
+#include <media/v4l2-clk.h>
 #include <media/v4l2-subdev.h>
 #include <media/v4l2-chip-ident.h>
 #include <media/v4l2-ctrls.h>
@@ -151,6 +152,7 @@ struct rj54n1_clock_div {
 struct rj54n1 {
 	struct v4l2_subdev subdev;
 	struct v4l2_ctrl_handler hdl;
+	struct v4l2_clk *clk;
 	struct rj54n1_clock_div clk_div;
 	const struct rj54n1_datafmt *fmt;
 	struct v4l2_rect rect;	/* Sensor window */
@@ -1184,8 +1186,9 @@ static int rj54n1_s_power(struct v4l2_subdev *sd, int on)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+	struct rj54n1 *rj54n1 = to_rj54n1(client);
 
-	return soc_camera_set_power(&client->dev, ssdd, on);
+	return soc_camera_set_power(&client->dev, ssdd, rj54n1->clk, on);
 }
 
 static int rj54n1_s_ctrl(struct v4l2_ctrl *ctrl)
@@ -1382,9 +1385,18 @@ static int rj54n1_probe(struct i2c_client *client,
 	rj54n1->tgclk_mhz	= (rj54n1_priv->mclk_freq / PLL_L * PLL_N) /
 		(clk_div.ratio_tg + 1) / (clk_div.ratio_t + 1);
 
+	rj54n1->clk = v4l2_clk_get(&client->dev, "mclk");
+	if (IS_ERR(rj54n1->clk)) {
+		ret = PTR_ERR(rj54n1->clk);
+		goto eclkget;
+	}
+
 	ret = rj54n1_video_probe(client, rj54n1_priv);
-	if (ret < 0)
+	if (ret < 0) {
+		v4l2_clk_put(rj54n1->clk);
+eclkget:
 		v4l2_ctrl_handler_free(&rj54n1->hdl);
+	}
 
 	return ret;
 }
@@ -1394,6 +1406,7 @@ static int rj54n1_remove(struct i2c_client *client)
 	struct rj54n1 *rj54n1 = to_rj54n1(client);
 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
 
+	v4l2_clk_put(rj54n1->clk);
 	v4l2_device_unregister_subdev(&rj54n1->subdev);
 	if (ssdd->free_bus)
 		ssdd->free_bus(ssdd);
diff --git a/drivers/media/i2c/soc_camera/tw9910.c b/drivers/media/i2c/soc_camera/tw9910.c
index 6b9cc2a..fe43591 100644
--- a/drivers/media/i2c/soc_camera/tw9910.c
+++ b/drivers/media/i2c/soc_camera/tw9910.c
@@ -28,6 +28,7 @@
 #include <media/soc_camera.h>
 #include <media/tw9910.h>
 #include <media/v4l2-chip-ident.h>
+#include <media/v4l2-clk.h>
 #include <media/v4l2-subdev.h>
 
 #define GET_ID(val)  ((val & 0xF8) >> 3)
@@ -228,6 +229,7 @@ struct tw9910_scale_ctrl {
 
 struct tw9910_priv {
 	struct v4l2_subdev		subdev;
+	struct v4l2_clk			*clk;
 	struct tw9910_video_info	*info;
 	const struct tw9910_scale_ctrl	*scale;
 	v4l2_std_id			norm;
@@ -570,8 +572,9 @@ static int tw9910_s_power(struct v4l2_subdev *sd, int on)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+	struct tw9910_priv *priv = to_tw9910(client);
 
-	return soc_camera_set_power(&client->dev, ssdd, on);
+	return soc_camera_set_power(&client->dev, ssdd, priv->clk, on);
 }
 
 static int tw9910_set_frame(struct v4l2_subdev *sd, u32 *width, u32 *height)
@@ -912,6 +915,7 @@ static int tw9910_probe(struct i2c_client *client,
 	struct i2c_adapter		*adapter =
 		to_i2c_adapter(client->dev.parent);
 	struct soc_camera_subdev_desc	*ssdd = soc_camera_i2c_to_desc(client);
+	int ret;
 
 	if (!ssdd || !ssdd->drv_priv) {
 		dev_err(&client->dev, "TW9910: missing platform data!\n");
@@ -935,7 +939,22 @@ static int tw9910_probe(struct i2c_client *client,
 
 	v4l2_i2c_subdev_init(&priv->subdev, client, &tw9910_subdev_ops);
 
-	return tw9910_video_probe(client);
+	priv->clk = v4l2_clk_get(&client->dev, "mclk");
+	if (IS_ERR(priv->clk))
+		return PTR_ERR(priv->clk);
+
+	ret = tw9910_video_probe(client);
+	if (ret < 0)
+		v4l2_clk_put(priv->clk);
+
+	return ret;
+}
+
+static int tw9910_remove(struct i2c_client *client)
+{
+	struct tw9910_priv *priv = to_tw9910(client);
+	v4l2_clk_put(priv->clk);
+	return 0;
 }
 
 static const struct i2c_device_id tw9910_id[] = {
@@ -949,6 +968,7 @@ static struct i2c_driver tw9910_i2c_driver = {
 		.name = "tw9910",
 	},
 	.probe    = tw9910_probe,
+	.remove   = tw9910_remove,
 	.id_table = tw9910_id,
 };
 
diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c
index ac98bcb..5845916 100644
--- a/drivers/media/platform/soc_camera/soc_camera.c
+++ b/drivers/media/platform/soc_camera/soc_camera.c
@@ -30,6 +30,7 @@
 #include <linux/vmalloc.h>
 
 #include <media/soc_camera.h>
+#include <media/v4l2-clk.h>
 #include <media/v4l2-common.h>
 #include <media/v4l2-ioctl.h>
 #include <media/v4l2-dev.h>
@@ -50,13 +51,19 @@ static LIST_HEAD(hosts);
 static LIST_HEAD(devices);
 static DEFINE_MUTEX(list_lock);		/* Protects the list of hosts */
 
-int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd)
+int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd,
+			struct v4l2_clk *clk)
 {
-	int ret = regulator_bulk_enable(ssdd->num_regulators,
+	int ret = clk ? v4l2_clk_enable(clk) : 0;
+	if (ret < 0) {
+		dev_err(dev, "Cannot enable clock\n");
+		return ret;
+	}
+	ret = regulator_bulk_enable(ssdd->num_regulators,
 					ssdd->regulators);
 	if (ret < 0) {
 		dev_err(dev, "Cannot enable regulators\n");
-		return ret;
+		goto eregenable;;
 	}
 
 	if (ssdd->power) {
@@ -64,16 +71,25 @@ int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd)
 		if (ret < 0) {
 			dev_err(dev,
 				"Platform failed to power-on the camera.\n");
-			regulator_bulk_disable(ssdd->num_regulators,
-					       ssdd->regulators);
+			goto epwron;
 		}
 	}
 
+	return 0;
+
+epwron:
+	regulator_bulk_disable(ssdd->num_regulators,
+			       ssdd->regulators);
+eregenable:
+	if (clk)
+		v4l2_clk_disable(clk);
+
 	return ret;
 }
 EXPORT_SYMBOL(soc_camera_power_on);
 
-int soc_camera_power_off(struct device *dev, struct soc_camera_subdev_desc *ssdd)
+int soc_camera_power_off(struct device *dev, struct soc_camera_subdev_desc *ssdd,
+			 struct v4l2_clk *clk)
 {
 	int ret = 0;
 	int err;
@@ -94,6 +110,9 @@ int soc_camera_power_off(struct device *dev, struct soc_camera_subdev_desc *ssdd
 		ret = ret ? : err;
 	}
 
+	if (clk)
+		v4l2_clk_disable(clk);
+
 	return ret;
 }
 EXPORT_SYMBOL(soc_camera_power_off);
@@ -513,9 +532,11 @@ static int soc_camera_add_device(struct soc_camera_device *icd)
 	if (ici->icd)
 		return -EBUSY;
 
-	ret = ici->ops->clock_start(ici);
-	if (ret < 0)
-		return ret;
+	if (!icd->clk) {
+		ret = ici->ops->clock_start(ici);
+		if (ret < 0)
+			return ret;
+	}
 
 	if (ici->ops->add) {
 		ret = ici->ops->add(icd);
@@ -528,7 +549,8 @@ static int soc_camera_add_device(struct soc_camera_device *icd)
 	return 0;
 
 eadd:
-	ici->ops->clock_stop(ici);
+	if (!icd->clk)
+		ici->ops->clock_stop(ici);
 	return ret;
 }
 
@@ -541,7 +563,8 @@ static void soc_camera_remove_device(struct soc_camera_device *icd)
 
 	if (ici->ops->remove)
 		ici->ops->remove(icd);
-	ici->ops->clock_stop(ici);
+	if (!icd->clk)
+		ici->ops->clock_stop(ici);
 	ici->icd = NULL;
 }
 
@@ -1124,6 +1147,57 @@ static void scan_add_host(struct soc_camera_host *ici)
 	mutex_unlock(&list_lock);
 }
 
+/*
+ * It is invalid to call v4l2_clk_enable() after a successful probing
+ * asynchronously outside of V4L2 operations, i.e. with .host_lock not held.
+ */
+static int soc_camera_clk_enable(struct v4l2_clk *clk)
+{
+	struct soc_camera_device *icd = clk->priv;
+	struct soc_camera_host *ici;
+
+	if (!icd || !icd->parent)
+		return -ENODEV;
+
+	ici = to_soc_camera_host(icd->parent);
+
+	if (!try_module_get(ici->ops->owner))
+		return -ENODEV;
+
+	/*
+	 * If a different client is currently being probed, the host will tell
+	 * you to go
+	 */
+	return ici->ops->clock_start(ici);
+}
+
+static void soc_camera_clk_disable(struct v4l2_clk *clk)
+{
+	struct soc_camera_device *icd = clk->priv;
+	struct soc_camera_host *ici;
+
+	if (!icd || !icd->parent)
+		return;
+
+	ici = to_soc_camera_host(icd->parent);
+
+	ici->ops->clock_stop(ici);
+
+	module_put(ici->ops->owner);
+}
+
+/*
+ * Eventually, it would be more logical to make the respective host the clock
+ * owner, but then we would have to copy this struct for each ici. Besides, it
+ * would introduce the circular dependency problem, unless we port all client
+ * drivers to release the clock, when not in use.
+ */
+static const struct v4l2_clk_ops soc_camera_clk_ops = {
+	.owner = THIS_MODULE,
+	.enable = soc_camera_clk_enable,
+	.disable = soc_camera_clk_disable,
+};
+
 #ifdef CONFIG_I2C_BOARDINFO
 static int soc_camera_init_i2c(struct soc_camera_device *icd,
 			       struct soc_camera_desc *sdesc)
@@ -1133,19 +1207,32 @@ static int soc_camera_init_i2c(struct soc_camera_device *icd,
 	struct soc_camera_host_desc *shd = &sdesc->host_desc;
 	struct i2c_adapter *adap = i2c_get_adapter(shd->i2c_adapter_id);
 	struct v4l2_subdev *subdev;
+	char clk_name[V4L2_SUBDEV_NAME_SIZE];
+	int ret;
 
 	if (!adap) {
 		dev_err(icd->pdev, "Cannot get I2C adapter #%d. No driver?\n",
 			shd->i2c_adapter_id);
-		goto ei2cga;
+		return -ENODEV;
 	}
 
 	shd->board_info->platform_data = &sdesc->subdev_desc;
 
+	snprintf(clk_name, sizeof(clk_name), "%d-%04x",
+		 shd->i2c_adapter_id, shd->board_info->addr);
+
+	icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, "mclk", icd);
+	if (IS_ERR(icd->clk)) {
+		ret = PTR_ERR(icd->clk);
+		goto eclkreg;
+	}
+
 	subdev = v4l2_i2c_new_subdev_board(&ici->v4l2_dev, adap,
 				shd->board_info, NULL);
-	if (!subdev)
+	if (!subdev) {
+		ret = -ENODEV;
 		goto ei2cnd;
+	}
 
 	client = v4l2_get_subdevdata(subdev);
 
@@ -1154,9 +1241,11 @@ static int soc_camera_init_i2c(struct soc_camera_device *icd,
 
 	return 0;
 ei2cnd:
+	v4l2_clk_unregister(icd->clk);
+	icd->clk = NULL;
+eclkreg:
 	i2c_put_adapter(adap);
-ei2cga:
-	return -ENODEV;
+	return ret;
 }
 
 static void soc_camera_free_i2c(struct soc_camera_device *icd)
@@ -1169,6 +1258,8 @@ static void soc_camera_free_i2c(struct soc_camera_device *icd)
 	v4l2_device_unregister_subdev(i2c_get_clientdata(client));
 	i2c_unregister_device(client);
 	i2c_put_adapter(adap);
+	v4l2_clk_unregister(icd->clk);
+	icd->clk = NULL;
 }
 #else
 #define soc_camera_init_i2c(icd, sdesc)	(-ENODEV)
@@ -1206,26 +1297,31 @@ static int soc_camera_probe(struct soc_camera_device *icd)
 	if (ssdd->reset)
 		ssdd->reset(icd->pdev);
 
-	mutex_lock(&ici->host_lock);
-	ret = ici->ops->clock_start(ici);
-	mutex_unlock(&ici->host_lock);
-	if (ret < 0)
-		goto eadd;
-
 	/* Must have icd->vdev before registering the device */
 	ret = video_dev_create(icd);
 	if (ret < 0)
 		goto evdc;
 
+	/*
+	 * ..._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 also during client probing.
+	 */
+	mutex_lock(&ici->host_lock);
+
 	/* Non-i2c cameras, e.g., soc_camera_platform, have no board_info */
 	if (shd->board_info) {
 		ret = soc_camera_init_i2c(icd, sdesc);
 		if (ret < 0)
-			goto eadddev;
+			goto eadd;
 	} else if (!shd->add_device || !shd->del_device) {
 		ret = -EINVAL;
-		goto eadddev;
+		goto eadd;
 	} else {
+		ret = ici->ops->clock_start(ici);
+		if (ret < 0)
+			goto eadd;
+
 		if (shd->module_name)
 			ret = request_module(shd->module_name);
 
@@ -1261,13 +1357,6 @@ static int soc_camera_probe(struct soc_camera_device *icd)
 
 	icd->field = V4L2_FIELD_ANY;
 
-	/*
-	 * ..._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(&ici->host_lock);
-
 	ret = soc_camera_video_start(icd);
 	if (ret < 0)
 		goto evidstart;
@@ -1280,14 +1369,14 @@ static int soc_camera_probe(struct soc_camera_device *icd)
 		icd->field		= mf.field;
 	}
 
-	ici->ops->clock_stop(ici);
+	if (!shd->board_info)
+		ici->ops->clock_stop(ici);
 
 	mutex_unlock(&ici->host_lock);
 
 	return 0;
 
 evidstart:
-	mutex_unlock(&ici->host_lock);
 	soc_camera_free_user_formats(icd);
 eiufmt:
 ectrl:
@@ -1296,16 +1385,15 @@ ectrl:
 	} else {
 		shd->del_device(icd);
 		module_put(control->driver->owner);
-	}
 enodrv:
 eadddev:
+		ici->ops->clock_stop(ici);
+	}
+eadd:
 	video_device_release(icd->vdev);
 	icd->vdev = NULL;
-evdc:
-	mutex_lock(&ici->host_lock);
-	ici->ops->clock_stop(ici);
 	mutex_unlock(&ici->host_lock);
-eadd:
+evdc:
 	v4l2_ctrl_handler_free(&icd->ctrl_handler);
 	return ret;
 }
diff --git a/drivers/media/platform/soc_camera/soc_camera_platform.c b/drivers/media/platform/soc_camera/soc_camera_platform.c
index bdf909d..ceaddfb 100644
--- a/drivers/media/platform/soc_camera/soc_camera_platform.c
+++ b/drivers/media/platform/soc_camera/soc_camera_platform.c
@@ -54,7 +54,7 @@ static int soc_camera_platform_s_power(struct v4l2_subdev *sd, int on)
 {
 	struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
 
-	return soc_camera_set_power(p->icd->control, &p->icd->sdesc->subdev_desc, on);
+	return soc_camera_set_power(p->icd->control, &p->icd->sdesc->subdev_desc, NULL, on);
 }
 
 static struct v4l2_subdev_core_ops platform_subdev_core_ops = {
diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h
index 64415ee..f8c1ffe 100644
--- a/include/media/soc_camera.h
+++ b/include/media/soc_camera.h
@@ -49,6 +49,7 @@ struct soc_camera_device {
 	/* soc_camera.c private count. Only accessed with .host_lock held */
 	int use_count;
 	struct file *streamer;		/* stream owner */
+	struct v4l2_clk *clk;
 	union {
 		struct videobuf_queue vb_vidq;
 		struct vb2_queue vb2_vidq;
@@ -325,14 +326,16 @@ static inline void soc_camera_limit_side(int *start, int *length,
 unsigned long soc_camera_apply_board_flags(struct soc_camera_subdev_desc *ssdd,
 					   const struct v4l2_mbus_config *cfg);
 
-int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd);
-int soc_camera_power_off(struct device *dev, struct soc_camera_subdev_desc *ssdd);
+int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd,
+			struct v4l2_clk *clk);
+int soc_camera_power_off(struct device *dev, struct soc_camera_subdev_desc *ssdd,
+			 struct v4l2_clk *clk);
 
 static inline int soc_camera_set_power(struct device *dev,
-				struct soc_camera_subdev_desc *ssdd, bool on)
+		struct soc_camera_subdev_desc *ssdd, struct v4l2_clk *clk, bool on)
 {
-	return on ? soc_camera_power_on(dev, ssdd)
-		  : soc_camera_power_off(dev, ssdd);
+	return on ? soc_camera_power_on(dev, ssdd, clk)
+		  : soc_camera_power_off(dev, ssdd, clk);
 }
 
 /* This is only temporary here - until v4l2-subdev begins to link to video_device */
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v10 18/21] soc-camera: add V4L2-async support
  2013-06-11  8:23 ` Guennadi Liakhovetski
@ 2013-06-11  8:23   ` Guennadi Liakhovetski
  -1 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-11  8:23 UTC (permalink / raw)
  To: linux-media
  Cc: Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer,
	Guennadi Liakhovetski

Add support for asynchronous subdevice probing, using the v4l2-async API.
The legacy synchronous mode is still supported too, which allows to
gradually update drivers and platforms. The selected approach adds a
notifier for each struct soc_camera_device instance, i.e. for each video
device node, even when there are multiple such instances registered with a
single soc-camera host simultaneously.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
 drivers/media/platform/soc_camera/soc_camera.c |  527 ++++++++++++++++++++----
 include/media/soc_camera.h                     |   23 +-
 2 files changed, 464 insertions(+), 86 deletions(-)

diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c
index 5845916..725e49d 100644
--- a/drivers/media/platform/soc_camera/soc_camera.c
+++ b/drivers/media/platform/soc_camera/soc_camera.c
@@ -21,22 +21,23 @@
 #include <linux/i2c.h>
 #include <linux/init.h>
 #include <linux/list.h>
-#include <linux/mutex.h>
 #include <linux/module.h>
+#include <linux/mutex.h>
 #include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
 #include <linux/regulator/consumer.h>
 #include <linux/slab.h>
-#include <linux/pm_runtime.h>
 #include <linux/vmalloc.h>
 
 #include <media/soc_camera.h>
+#include <media/soc_mediabus.h>
+#include <media/v4l2-async.h>
 #include <media/v4l2-clk.h>
 #include <media/v4l2-common.h>
 #include <media/v4l2-ioctl.h>
 #include <media/v4l2-dev.h>
 #include <media/videobuf-core.h>
 #include <media/videobuf2-core.h>
-#include <media/soc_mediabus.h>
 
 /* Default to VGA resolution */
 #define DEFAULT_WIDTH	640
@@ -47,23 +48,39 @@
 	 (icd)->vb_vidq.streaming :			\
 	 vb2_is_streaming(&(icd)->vb2_vidq))
 
+#define MAP_MAX_NUM 32
+static DECLARE_BITMAP(device_map, MAP_MAX_NUM);
 static LIST_HEAD(hosts);
 static LIST_HEAD(devices);
-static DEFINE_MUTEX(list_lock);		/* Protects the list of hosts */
+/*
+ * Protects lists and bitmaps of hosts and devices.
+ * Lock nesting: Ok to take ->host_lock under list_lock.
+ */
+static DEFINE_MUTEX(list_lock);
+
+struct soc_camera_async_client {
+	struct v4l2_async_subdev *sensor;
+	struct v4l2_async_notifier notifier;
+	struct platform_device *pdev;
+	struct list_head list;		/* needed for clean up */
+};
+
+static int soc_camera_video_start(struct soc_camera_device *icd);
+static int video_dev_create(struct soc_camera_device *icd);
 
 int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd,
 			struct v4l2_clk *clk)
 {
 	int ret = clk ? v4l2_clk_enable(clk) : 0;
 	if (ret < 0) {
-		dev_err(dev, "Cannot enable clock\n");
+		dev_err(dev, "Cannot enable clock: %d\n", ret);
 		return ret;
 	}
 	ret = regulator_bulk_enable(ssdd->num_regulators,
 					ssdd->regulators);
 	if (ret < 0) {
 		dev_err(dev, "Cannot enable regulators\n");
-		goto eregenable;;
+		goto eregenable;
 	}
 
 	if (ssdd->power) {
@@ -117,6 +134,14 @@ int soc_camera_power_off(struct device *dev, struct soc_camera_subdev_desc *ssdd
 }
 EXPORT_SYMBOL(soc_camera_power_off);
 
+int soc_camera_power_init(struct device *dev, struct soc_camera_subdev_desc *ssdd)
+{
+
+	return devm_regulator_bulk_get(dev, ssdd->num_regulators,
+				       ssdd->regulators);
+}
+EXPORT_SYMBOL(soc_camera_power_init);
+
 static int __soc_camera_power_on(struct soc_camera_device *icd)
 {
 	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
@@ -533,7 +558,9 @@ static int soc_camera_add_device(struct soc_camera_device *icd)
 		return -EBUSY;
 
 	if (!icd->clk) {
+		mutex_lock(&ici->clk_lock);
 		ret = ici->ops->clock_start(ici);
+		mutex_unlock(&ici->clk_lock);
 		if (ret < 0)
 			return ret;
 	}
@@ -549,8 +576,11 @@ static int soc_camera_add_device(struct soc_camera_device *icd)
 	return 0;
 
 eadd:
-	if (!icd->clk)
+	if (!icd->clk) {
+		mutex_lock(&ici->clk_lock);
 		ici->ops->clock_stop(ici);
+		mutex_unlock(&ici->clk_lock);
+	}
 	return ret;
 }
 
@@ -563,8 +593,11 @@ static void soc_camera_remove_device(struct soc_camera_device *icd)
 
 	if (ici->ops->remove)
 		ici->ops->remove(icd);
-	if (!icd->clk)
+	if (!icd->clk) {
+		mutex_lock(&ici->clk_lock);
 		ici->ops->clock_stop(ici);
+		mutex_unlock(&ici->clk_lock);
+	}
 	ici->icd = NULL;
 }
 
@@ -673,8 +706,8 @@ static int soc_camera_open(struct file *file)
 	return 0;
 
 	/*
-	 * First four errors are entered with the .host_lock held
-	 * and use_count = 1
+	 * All errors are entered with the .host_lock held, first four also
+	 * with use_count = 1
 	 */
 einitvb:
 esfmt:
@@ -1128,7 +1161,8 @@ static int soc_camera_s_register(struct file *file, void *fh,
 }
 #endif
 
-static int soc_camera_probe(struct soc_camera_device *icd);
+static int soc_camera_probe(struct soc_camera_host *ici,
+			    struct soc_camera_device *icd);
 
 /* So far this function cannot fail */
 static void scan_add_host(struct soc_camera_host *ici)
@@ -1137,12 +1171,20 @@ static void scan_add_host(struct soc_camera_host *ici)
 
 	mutex_lock(&list_lock);
 
-	list_for_each_entry(icd, &devices, list) {
+	list_for_each_entry(icd, &devices, list)
 		if (icd->iface = ici->nr) {
+			struct soc_camera_desc *sdesc = to_soc_camera_desc(icd);
+			struct soc_camera_subdev_desc *ssdd = &sdesc->subdev_desc;
+
+			/* The camera could have been already on, try to reset */
+			if (ssdd->reset)
+				ssdd->reset(icd->pdev);
+
 			icd->parent = ici->v4l2_dev.dev;
-			soc_camera_probe(icd);
+
+			/* Ignore errors */
+			soc_camera_probe(ici, icd);
 		}
-	}
 
 	mutex_unlock(&list_lock);
 }
@@ -1155,6 +1197,7 @@ static int soc_camera_clk_enable(struct v4l2_clk *clk)
 {
 	struct soc_camera_device *icd = clk->priv;
 	struct soc_camera_host *ici;
+	int ret;
 
 	if (!icd || !icd->parent)
 		return -ENODEV;
@@ -1168,7 +1211,10 @@ static int soc_camera_clk_enable(struct v4l2_clk *clk)
 	 * If a different client is currently being probed, the host will tell
 	 * you to go
 	 */
-	return ici->ops->clock_start(ici);
+	mutex_lock(&ici->clk_lock);
+	ret = ici->ops->clock_start(ici);
+	mutex_unlock(&ici->clk_lock);
+	return ret;
 }
 
 static void soc_camera_clk_disable(struct v4l2_clk *clk)
@@ -1181,7 +1227,9 @@ static void soc_camera_clk_disable(struct v4l2_clk *clk)
 
 	ici = to_soc_camera_host(icd->parent);
 
+	mutex_lock(&ici->clk_lock);
 	ici->ops->clock_stop(ici);
+	mutex_unlock(&ici->clk_lock);
 
 	module_put(ici->ops->owner);
 }
@@ -1198,18 +1246,117 @@ static const struct v4l2_clk_ops soc_camera_clk_ops = {
 	.disable = soc_camera_clk_disable,
 };
 
+static int soc_camera_dyn_pdev(struct soc_camera_desc *sdesc,
+			       struct soc_camera_async_client *sasc)
+{
+	struct platform_device *pdev;
+	int ret, i;
+
+	mutex_lock(&list_lock);
+	i = find_first_zero_bit(device_map, MAP_MAX_NUM);
+	if (i < MAP_MAX_NUM)
+		set_bit(i, device_map);
+	mutex_unlock(&list_lock);
+	if (i >= MAP_MAX_NUM)
+		return -ENOMEM;
+
+	pdev = platform_device_alloc("soc-camera-pdrv", i);
+	if (!pdev)
+		return -ENOMEM;
+
+	ret = platform_device_add_data(pdev, sdesc, sizeof(*sdesc));
+	if (ret < 0) {
+		platform_device_put(pdev);
+		return ret;
+	}
+
+	sasc->pdev = pdev;
+
+	return 0;
+}
+
+static struct soc_camera_device *soc_camera_add_pdev(struct soc_camera_async_client *sasc)
+{
+	struct platform_device *pdev = sasc->pdev;
+	int ret;
+
+	ret = platform_device_add(pdev);
+	if (ret < 0 || !pdev->dev.driver)
+		return NULL;
+
+	return platform_get_drvdata(pdev);
+}
+
+/* Locking: called with .host_lock held */
+static int soc_camera_probe_finish(struct soc_camera_device *icd)
+{
+	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+	struct v4l2_mbus_framefmt mf;
+	int ret;
+
+	sd->grp_id = soc_camera_grp_id(icd);
+	v4l2_set_subdev_hostdata(sd, icd);
+
+	ret = v4l2_ctrl_add_handler(&icd->ctrl_handler, sd->ctrl_handler, NULL);
+	if (ret < 0)
+		return ret;
+
+	ret = soc_camera_add_device(icd);
+	if (ret < 0) {
+		dev_err(icd->pdev, "Couldn't activate the camera: %d\n", ret);
+		return ret;
+	}
+
+	/* At this point client .probe() should have run already */
+	ret = soc_camera_init_user_formats(icd);
+	if (ret < 0)
+		goto eusrfmt;
+
+	icd->field = V4L2_FIELD_ANY;
+
+	ret = soc_camera_video_start(icd);
+	if (ret < 0)
+		goto evidstart;
+
+	/* Try to improve our guess of a reasonable window format */
+	if (!v4l2_subdev_call(sd, video, g_mbus_fmt, &mf)) {
+		icd->user_width		= mf.width;
+		icd->user_height	= mf.height;
+		icd->colorspace		= mf.colorspace;
+		icd->field		= mf.field;
+	}
+	soc_camera_remove_device(icd);
+
+	return 0;
+
+evidstart:
+	soc_camera_free_user_formats(icd);
+eusrfmt:
+	soc_camera_remove_device(icd);
+
+	return ret;
+}
+
 #ifdef CONFIG_I2C_BOARDINFO
-static int soc_camera_init_i2c(struct soc_camera_device *icd,
+static int soc_camera_i2c_init(struct soc_camera_device *icd,
 			       struct soc_camera_desc *sdesc)
 {
 	struct i2c_client *client;
-	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+	struct soc_camera_host *ici;
 	struct soc_camera_host_desc *shd = &sdesc->host_desc;
-	struct i2c_adapter *adap = i2c_get_adapter(shd->i2c_adapter_id);
+	struct i2c_adapter *adap;
 	struct v4l2_subdev *subdev;
 	char clk_name[V4L2_SUBDEV_NAME_SIZE];
 	int ret;
 
+	/* First find out how we link the main client */
+	if (icd->sasc) {
+		/* Async non-OF probing handled by the subdevice list */
+		return -EPROBE_DEFER;
+	}
+
+	ici = to_soc_camera_host(icd->parent);
+	adap = i2c_get_adapter(shd->i2c_adapter_id);
 	if (!adap) {
 		dev_err(icd->pdev, "Cannot get I2C adapter #%d. No driver?\n",
 			shd->i2c_adapter_id);
@@ -1242,42 +1389,202 @@ static int soc_camera_init_i2c(struct soc_camera_device *icd,
 	return 0;
 ei2cnd:
 	v4l2_clk_unregister(icd->clk);
-	icd->clk = NULL;
 eclkreg:
+	icd->clk = NULL;
 	i2c_put_adapter(adap);
 	return ret;
 }
 
-static void soc_camera_free_i2c(struct soc_camera_device *icd)
+static void soc_camera_i2c_free(struct soc_camera_device *icd)
 {
 	struct i2c_client *client  		to_i2c_client(to_soc_camera_control(icd));
-	struct i2c_adapter *adap = client->adapter;
+	struct i2c_adapter *adap;
 
 	icd->control = NULL;
+	if (icd->sasc)
+		return;
+
+	adap = client->adapter;
 	v4l2_device_unregister_subdev(i2c_get_clientdata(client));
 	i2c_unregister_device(client);
 	i2c_put_adapter(adap);
 	v4l2_clk_unregister(icd->clk);
 	icd->clk = NULL;
 }
+
+/*
+ * V4L2 asynchronous notifier callbacks. They are all called under a v4l2-async
+ * internal global mutex, therefore cannot race against other asynchronous
+ * events. Until notifier->complete() (soc_camera_async_complete()) is called,
+ * the video device node is not registered and no V4L fops can occur. Unloading
+ * of the host driver also calls a v4l2-async function, so also there we're
+ * protected.
+ */
+static int soc_camera_async_bound(struct v4l2_async_notifier *notifier,
+				  struct v4l2_subdev *sd,
+				  struct v4l2_async_subdev *asd)
+{
+	struct soc_camera_async_client *sasc = container_of(notifier,
+					struct soc_camera_async_client, notifier);
+	struct soc_camera_device *icd = platform_get_drvdata(sasc->pdev);
+
+	if (asd = sasc->sensor && !WARN_ON(icd->control)) {
+		struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+		/*
+		 * Only now we get subdevice-specific information like
+		 * regulators, flags, callbacks, etc.
+		 */
+		if (client) {
+			struct soc_camera_desc *sdesc = to_soc_camera_desc(icd);
+			struct soc_camera_subdev_desc *ssdd +				soc_camera_i2c_to_desc(client);
+			if (ssdd) {
+				memcpy(&sdesc->subdev_desc, ssdd,
+				       sizeof(sdesc->subdev_desc));
+				if (ssdd->reset)
+					ssdd->reset(icd->pdev);
+			}
+
+			icd->control = &client->dev;
+		}
+	}
+
+	return 0;
+}
+
+static void soc_camera_async_unbind(struct v4l2_async_notifier *notifier,
+				    struct v4l2_subdev *sd,
+				    struct v4l2_async_subdev *asd)
+{
+	struct soc_camera_async_client *sasc = container_of(notifier,
+					struct soc_camera_async_client, notifier);
+	struct soc_camera_device *icd = platform_get_drvdata(sasc->pdev);
+
+	if (icd->clk) {
+		v4l2_clk_unregister(icd->clk);
+		icd->clk = NULL;
+	}
+}
+
+static int soc_camera_async_complete(struct v4l2_async_notifier *notifier)
+{
+	struct soc_camera_async_client *sasc = container_of(notifier,
+					struct soc_camera_async_client, notifier);
+	struct soc_camera_device *icd = platform_get_drvdata(sasc->pdev);
+
+	if (to_soc_camera_control(icd)) {
+		struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+		int ret;
+
+		mutex_lock(&list_lock);
+		ret = soc_camera_probe(ici, icd);
+		mutex_unlock(&list_lock);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int scan_async_group(struct soc_camera_host *ici,
+			    struct v4l2_async_subdev **asd, int size)
+{
+	struct soc_camera_async_subdev *sasd;
+	struct soc_camera_async_client *sasc;
+	struct soc_camera_device *icd;
+	struct soc_camera_desc sdesc = {.host_desc.bus_id = ici->nr,};
+	char clk_name[V4L2_SUBDEV_NAME_SIZE];
+	int ret, i;
+
+	/* First look for a sensor */
+	for (i = 0; i < size; i++) {
+		sasd = container_of(asd[i], struct soc_camera_async_subdev, asd);
+		if (sasd->role = SOCAM_SUBDEV_DATA_SOURCE)
+			break;
+	}
+
+	if (i = size || asd[i]->bus_type != V4L2_ASYNC_BUS_I2C) {
+		/* All useless */
+		dev_err(ici->v4l2_dev.dev, "No I2C data source found!\n");
+		return -ENODEV;
+	}
+
+	/* Or shall this be managed by the soc-camera device? */
+	sasc = devm_kzalloc(ici->v4l2_dev.dev, sizeof(*sasc), GFP_KERNEL);
+	if (!sasc)
+		return -ENOMEM;
+
+	/* HACK: just need a != NULL */
+	sdesc.host_desc.board_info = ERR_PTR(-ENODATA);
+
+	ret = soc_camera_dyn_pdev(&sdesc, sasc);
+	if (ret < 0)
+		return ret;
+
+	sasc->sensor = &sasd->asd;
+
+	icd = soc_camera_add_pdev(sasc);
+	if (!icd) {
+		platform_device_put(sasc->pdev);
+		return -ENOMEM;
+	}
+
+	sasc->notifier.subdev = asd;
+	sasc->notifier.subdev_num = size;
+	sasc->notifier.bound = soc_camera_async_bound;
+	sasc->notifier.unbind = soc_camera_async_unbind;
+	sasc->notifier.complete = soc_camera_async_complete;
+
+	icd->sasc = sasc;
+	icd->parent = ici->v4l2_dev.dev;
+
+	snprintf(clk_name, sizeof(clk_name), "%d-%04x",
+		 sasd->asd.match.i2c.adapter_id, sasd->asd.match.i2c.address);
+
+	icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, "mclk", icd);
+	if (IS_ERR(icd->clk)) {
+		ret = PTR_ERR(icd->clk);
+		goto eclkreg;
+	}
+
+	ret = v4l2_async_notifier_register(&ici->v4l2_dev, &sasc->notifier);
+	if (!ret)
+		return 0;
+
+	v4l2_clk_unregister(icd->clk);
+eclkreg:
+	icd->clk = NULL;
+	platform_device_unregister(sasc->pdev);
+	dev_err(ici->v4l2_dev.dev, "group probe failed: %d\n", ret);
+
+	return ret;
+}
+
+static void scan_async_host(struct soc_camera_host *ici)
+{
+	struct v4l2_async_subdev **asd;
+	int j;
+
+	for (j = 0, asd = ici->asd; ici->asd_sizes[j]; j++) {
+		scan_async_group(ici, asd, ici->asd_sizes[j]);
+		asd += ici->asd_sizes[j];
+	}
+}
 #else
-#define soc_camera_init_i2c(icd, sdesc)	(-ENODEV)
-#define soc_camera_free_i2c(icd)	do {} while (0)
+#define soc_camera_i2c_init(icd, sdesc)	(-ENODEV)
+#define soc_camera_i2c_free(icd)	do {} while (0)
+#define scan_async_host(ici)		do {} while (0)
 #endif
 
-static int soc_camera_video_start(struct soc_camera_device *icd);
-static int video_dev_create(struct soc_camera_device *icd);
 /* Called during host-driver probe */
-static int soc_camera_probe(struct soc_camera_device *icd)
+static int soc_camera_probe(struct soc_camera_host *ici,
+			    struct soc_camera_device *icd)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct soc_camera_desc *sdesc = to_soc_camera_desc(icd);
 	struct soc_camera_host_desc *shd = &sdesc->host_desc;
-	struct soc_camera_subdev_desc *ssdd = &sdesc->subdev_desc;
 	struct device *control = NULL;
-	struct v4l2_subdev *sd;
-	struct v4l2_mbus_framefmt mf;
 	int ret;
 
 	dev_info(icd->pdev, "Probing %s\n", dev_name(icd->pdev));
@@ -1293,10 +1600,6 @@ static int soc_camera_probe(struct soc_camera_device *icd)
 	if (ret < 0)
 		return ret;
 
-	/* The camera could have been already on, try to reset */
-	if (ssdd->reset)
-		ssdd->reset(icd->pdev);
-
 	/* Must have icd->vdev before registering the device */
 	ret = video_dev_create(icd);
 	if (ret < 0)
@@ -1307,18 +1610,19 @@ static int soc_camera_probe(struct soc_camera_device *icd)
 	 * itself is protected against concurrent open() calls, but we also have
 	 * to protect our data also during client probing.
 	 */
-	mutex_lock(&ici->host_lock);
 
 	/* Non-i2c cameras, e.g., soc_camera_platform, have no board_info */
 	if (shd->board_info) {
-		ret = soc_camera_init_i2c(icd, sdesc);
-		if (ret < 0)
+		ret = soc_camera_i2c_init(icd, sdesc);
+		if (ret < 0 && ret != -EPROBE_DEFER)
 			goto eadd;
 	} else if (!shd->add_device || !shd->del_device) {
 		ret = -EINVAL;
 		goto eadd;
 	} else {
+		mutex_lock(&ici->clk_lock);
 		ret = ici->ops->clock_start(ici);
+		mutex_unlock(&ici->clk_lock);
 		if (ret < 0)
 			goto eadd;
 
@@ -1342,57 +1646,33 @@ static int soc_camera_probe(struct soc_camera_device *icd)
 		}
 	}
 
-	sd = soc_camera_to_subdev(icd);
-	sd->grp_id = soc_camera_grp_id(icd);
-	v4l2_set_subdev_hostdata(sd, icd);
-
-	ret = v4l2_ctrl_add_handler(&icd->ctrl_handler, sd->ctrl_handler, NULL);
-	if (ret < 0)
-		goto ectrl;
-
-	/* At this point client .probe() should have run already */
-	ret = soc_camera_init_user_formats(icd);
-	if (ret < 0)
-		goto eiufmt;
-
-	icd->field = V4L2_FIELD_ANY;
-
-	ret = soc_camera_video_start(icd);
-	if (ret < 0)
-		goto evidstart;
-
-	/* Try to improve our guess of a reasonable window format */
-	if (!v4l2_subdev_call(sd, video, g_mbus_fmt, &mf)) {
-		icd->user_width		= mf.width;
-		icd->user_height	= mf.height;
-		icd->colorspace		= mf.colorspace;
-		icd->field		= mf.field;
-	}
-
-	if (!shd->board_info)
-		ici->ops->clock_stop(ici);
-
+	mutex_lock(&ici->host_lock);
+	ret = soc_camera_probe_finish(icd);
 	mutex_unlock(&ici->host_lock);
+	if (ret < 0)
+		goto efinish;
 
 	return 0;
 
-evidstart:
-	soc_camera_free_user_formats(icd);
-eiufmt:
-ectrl:
+efinish:
 	if (shd->board_info) {
-		soc_camera_free_i2c(icd);
+		soc_camera_i2c_free(icd);
 	} else {
 		shd->del_device(icd);
 		module_put(control->driver->owner);
 enodrv:
 eadddev:
+		mutex_lock(&ici->clk_lock);
 		ici->ops->clock_stop(ici);
+		mutex_unlock(&ici->clk_lock);
 	}
 eadd:
 	video_device_release(icd->vdev);
 	icd->vdev = NULL;
-	mutex_unlock(&ici->host_lock);
+	if (icd->vdev) {
+		video_device_release(icd->vdev);
+		icd->vdev = NULL;
+	}
 evdc:
 	v4l2_ctrl_handler_free(&icd->ctrl_handler);
 	return ret;
@@ -1400,15 +1680,15 @@ evdc:
 
 /*
  * This is called on device_unregister, which only means we have to disconnect
- * from the host, but not remove ourselves from the device list
+ * from the host, but not remove ourselves from the device list. With
+ * asynchronous client probing this can also be called without
+ * soc_camera_probe_finish() having run. Careful with clean up.
  */
 static int soc_camera_remove(struct soc_camera_device *icd)
 {
 	struct soc_camera_desc *sdesc = to_soc_camera_desc(icd);
 	struct video_device *vdev = icd->vdev;
 
-	BUG_ON(!icd->parent);
-
 	v4l2_ctrl_handler_free(&icd->ctrl_handler);
 	if (vdev) {
 		video_unregister_device(vdev);
@@ -1416,15 +1696,27 @@ static int soc_camera_remove(struct soc_camera_device *icd)
 	}
 
 	if (sdesc->host_desc.board_info) {
-		soc_camera_free_i2c(icd);
+		soc_camera_i2c_free(icd);
 	} else {
-		struct device_driver *drv = to_soc_camera_control(icd)->driver;
+		struct device *dev = to_soc_camera_control(icd);
+		struct device_driver *drv = dev ? dev->driver : NULL;
 		if (drv) {
 			sdesc->host_desc.del_device(icd);
 			module_put(drv->owner);
 		}
 	}
-	soc_camera_free_user_formats(icd);
+
+	if (icd->num_user_formats)
+		soc_camera_free_user_formats(icd);
+
+	if (icd->clk) {
+		/* For the synchronous case */
+		v4l2_clk_unregister(icd->clk);
+		icd->clk = NULL;
+	}
+
+	if (icd->sasc)
+		platform_device_unregister(icd->sasc->pdev);
 
 	return 0;
 }
@@ -1535,7 +1827,18 @@ int soc_camera_host_register(struct soc_camera_host *ici)
 	mutex_unlock(&list_lock);
 
 	mutex_init(&ici->host_lock);
-	scan_add_host(ici);
+	mutex_init(&ici->clk_lock);
+
+	if (ici->asd_sizes)
+		/*
+		 * No OF, host with a list of subdevices. Don't try to mix
+		 * modes by initialising some groups statically and some
+		 * dynamically!
+		 */
+		scan_async_host(ici);
+	else
+		/* Legacy: static platform devices from board data */
+		scan_add_host(ici);
 
 	return 0;
 
@@ -1548,13 +1851,30 @@ EXPORT_SYMBOL(soc_camera_host_register);
 /* Unregister all clients! */
 void soc_camera_host_unregister(struct soc_camera_host *ici)
 {
-	struct soc_camera_device *icd;
+	struct soc_camera_device *icd, *tmp;
+	struct soc_camera_async_client *sasc;
+	LIST_HEAD(notifiers);
 
 	mutex_lock(&list_lock);
-
 	list_del(&ici->list);
 	list_for_each_entry(icd, &devices, list)
-		if (icd->iface = ici->nr && to_soc_camera_control(icd))
+		if (icd->iface = ici->nr && icd->sasc) {
+			/* as long as we hold the device, sasc won't be freed */
+			get_device(icd->pdev);
+			list_add(&icd->sasc->list, &notifiers);
+		}
+	mutex_unlock(&list_lock);
+
+	list_for_each_entry(sasc, &notifiers, list) {
+		/* Must call unlocked to avoid AB-BA dead-lock */
+		v4l2_async_notifier_unregister(&sasc->notifier);
+		put_device(&sasc->pdev->dev);
+	}
+
+	mutex_lock(&list_lock);
+
+	list_for_each_entry_safe(icd, tmp, &devices, list)
+		if (icd->iface = ici->nr)
 			soc_camera_remove(icd);
 
 	mutex_unlock(&list_lock);
@@ -1569,6 +1889,7 @@ static int soc_camera_device_register(struct soc_camera_device *icd)
 	struct soc_camera_device *ix;
 	int num = -1, i;
 
+	mutex_lock(&list_lock);
 	for (i = 0; i < 256 && num < 0; i++) {
 		num = i;
 		/* Check if this index is available on this interface */
@@ -1580,18 +1901,34 @@ static int soc_camera_device_register(struct soc_camera_device *icd)
 		}
 	}
 
-	if (num < 0)
+	if (num < 0) {
 		/*
 		 * ok, we have 256 cameras on this host...
 		 * man, stay reasonable...
 		 */
+		mutex_unlock(&list_lock);
 		return -ENOMEM;
+	}
 
 	icd->devnum		= num;
 	icd->use_count		= 0;
 	icd->host_priv		= NULL;
 
+	/*
+	 * Dynamically allocated devices set the bit earlier, but it doesn't hurt setting
+	 * it again
+	 */
+	i = to_platform_device(icd->pdev)->id;
+	if (i < 0)
+		/* One static (legacy) soc-camera platform device */
+		i = 0;
+	if (i >= MAP_MAX_NUM) {
+		mutex_unlock(&list_lock);
+		return -EBUSY;
+	}
+	set_bit(i, device_map);
 	list_add_tail(&icd->list, &devices);
+	mutex_unlock(&list_lock);
 
 	return 0;
 }
@@ -1691,6 +2028,12 @@ static int soc_camera_pdrv_probe(struct platform_device *pdev)
 	if (!icd)
 		return -ENOMEM;
 
+	/*
+	 * In the asynchronous case ssdd->num_regulators = 0 yet, so, the below
+	 * regulator allocation is a dummy. They will be really requested later
+	 * in soc_camera_async_bind(). Also note, that in that case regulators
+	 * are attached to the I2C device and not to the camera platform device.
+	 */
 	ret = devm_regulator_bulk_get(&pdev->dev, ssdd->num_regulators,
 				      ssdd->regulators);
 	if (ret < 0)
@@ -1715,11 +2058,25 @@ static int soc_camera_pdrv_probe(struct platform_device *pdev)
 static int soc_camera_pdrv_remove(struct platform_device *pdev)
 {
 	struct soc_camera_device *icd = platform_get_drvdata(pdev);
+	int i;
 
 	if (!icd)
 		return -EINVAL;
 
-	list_del(&icd->list);
+	i = pdev->id;
+	if (i < 0)
+		i = 0;
+
+	/*
+	 * In synchronous mode with static platform devices this is called in a
+	 * loop from drivers/base/dd.c::driver_detach(), no parallel execution,
+	 * no need to lock. In asynchronous case the caller -
+	 * soc_camera_host_unregister() - already holds the lock
+	 */
+	if (test_bit(i, device_map)) {
+		clear_bit(i, device_map);
+		list_del(&icd->list);
+	}
 
 	return 0;
 }
diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h
index f8c1ffe..2d3c939 100644
--- a/include/media/soc_camera.h
+++ b/include/media/soc_camera.h
@@ -19,11 +19,13 @@
 #include <linux/videodev2.h>
 #include <media/videobuf-core.h>
 #include <media/videobuf2-core.h>
+#include <media/v4l2-async.h>
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-device.h>
 
 struct file;
 struct soc_camera_desc;
+struct soc_camera_async_client;
 
 struct soc_camera_device {
 	struct list_head list;		/* list of all registered devices */
@@ -50,6 +52,9 @@ struct soc_camera_device {
 	int use_count;
 	struct file *streamer;		/* stream owner */
 	struct v4l2_clk *clk;
+	/* Asynchronous subdevice management */
+	struct soc_camera_async_client *sasc;
+	/* video buffer queue */
 	union {
 		struct videobuf_queue vb_vidq;
 		struct vb2_queue vb2_vidq;
@@ -59,16 +64,30 @@ struct soc_camera_device {
 /* Host supports programmable stride */
 #define SOCAM_HOST_CAP_STRIDE		(1 << 0)
 
+enum soc_camera_subdev_role {
+	SOCAM_SUBDEV_DATA_SOURCE = 1,
+	SOCAM_SUBDEV_DATA_SINK,
+	SOCAM_SUBDEV_DATA_PROCESSOR,
+};
+
+struct soc_camera_async_subdev {
+	struct v4l2_async_subdev asd;
+	enum soc_camera_subdev_role role;
+};
+
 struct soc_camera_host {
 	struct v4l2_device v4l2_dev;
 	struct list_head list;
-	struct mutex host_lock;		/* Protect pipeline modifications */
+	struct mutex host_lock;		/* Main synchronisation lock */
+	struct mutex clk_lock;		/* Protect pipeline modifications */
 	unsigned char nr;		/* Host number */
 	u32 capabilities;
 	struct soc_camera_device *icd;	/* Currently attached client */
 	void *priv;
 	const char *drv_name;
 	struct soc_camera_host_ops *ops;
+	struct v4l2_async_subdev **asd;	/* Flat array, arranged in groups */
+	int *asd_sizes;			/* 0-terminated array of asd group sizes */
 };
 
 struct soc_camera_host_ops {
@@ -161,6 +180,7 @@ struct soc_camera_host_desc {
 };
 
 /*
+ * Platform data for "soc-camera-pdrv"
  * This MUST be kept binary-identical to struct soc_camera_link below, until
  * it is completely replaced by this one, after which we can split it into its
  * two components.
@@ -326,6 +346,7 @@ static inline void soc_camera_limit_side(int *start, int *length,
 unsigned long soc_camera_apply_board_flags(struct soc_camera_subdev_desc *ssdd,
 					   const struct v4l2_mbus_config *cfg);
 
+int soc_camera_power_init(struct device *dev, struct soc_camera_subdev_desc *ssdd);
 int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd,
 			struct v4l2_clk *clk);
 int soc_camera_power_off(struct device *dev, struct soc_camera_subdev_desc *ssdd,
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v10 18/21] soc-camera: add V4L2-async support
@ 2013-06-11  8:23   ` Guennadi Liakhovetski
  0 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-11  8:23 UTC (permalink / raw)
  To: linux-media
  Cc: Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer,
	Guennadi Liakhovetski

Add support for asynchronous subdevice probing, using the v4l2-async API.
The legacy synchronous mode is still supported too, which allows to
gradually update drivers and platforms. The selected approach adds a
notifier for each struct soc_camera_device instance, i.e. for each video
device node, even when there are multiple such instances registered with a
single soc-camera host simultaneously.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
 drivers/media/platform/soc_camera/soc_camera.c |  527 ++++++++++++++++++++----
 include/media/soc_camera.h                     |   23 +-
 2 files changed, 464 insertions(+), 86 deletions(-)

diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c
index 5845916..725e49d 100644
--- a/drivers/media/platform/soc_camera/soc_camera.c
+++ b/drivers/media/platform/soc_camera/soc_camera.c
@@ -21,22 +21,23 @@
 #include <linux/i2c.h>
 #include <linux/init.h>
 #include <linux/list.h>
-#include <linux/mutex.h>
 #include <linux/module.h>
+#include <linux/mutex.h>
 #include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
 #include <linux/regulator/consumer.h>
 #include <linux/slab.h>
-#include <linux/pm_runtime.h>
 #include <linux/vmalloc.h>
 
 #include <media/soc_camera.h>
+#include <media/soc_mediabus.h>
+#include <media/v4l2-async.h>
 #include <media/v4l2-clk.h>
 #include <media/v4l2-common.h>
 #include <media/v4l2-ioctl.h>
 #include <media/v4l2-dev.h>
 #include <media/videobuf-core.h>
 #include <media/videobuf2-core.h>
-#include <media/soc_mediabus.h>
 
 /* Default to VGA resolution */
 #define DEFAULT_WIDTH	640
@@ -47,23 +48,39 @@
 	 (icd)->vb_vidq.streaming :			\
 	 vb2_is_streaming(&(icd)->vb2_vidq))
 
+#define MAP_MAX_NUM 32
+static DECLARE_BITMAP(device_map, MAP_MAX_NUM);
 static LIST_HEAD(hosts);
 static LIST_HEAD(devices);
-static DEFINE_MUTEX(list_lock);		/* Protects the list of hosts */
+/*
+ * Protects lists and bitmaps of hosts and devices.
+ * Lock nesting: Ok to take ->host_lock under list_lock.
+ */
+static DEFINE_MUTEX(list_lock);
+
+struct soc_camera_async_client {
+	struct v4l2_async_subdev *sensor;
+	struct v4l2_async_notifier notifier;
+	struct platform_device *pdev;
+	struct list_head list;		/* needed for clean up */
+};
+
+static int soc_camera_video_start(struct soc_camera_device *icd);
+static int video_dev_create(struct soc_camera_device *icd);
 
 int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd,
 			struct v4l2_clk *clk)
 {
 	int ret = clk ? v4l2_clk_enable(clk) : 0;
 	if (ret < 0) {
-		dev_err(dev, "Cannot enable clock\n");
+		dev_err(dev, "Cannot enable clock: %d\n", ret);
 		return ret;
 	}
 	ret = regulator_bulk_enable(ssdd->num_regulators,
 					ssdd->regulators);
 	if (ret < 0) {
 		dev_err(dev, "Cannot enable regulators\n");
-		goto eregenable;;
+		goto eregenable;
 	}
 
 	if (ssdd->power) {
@@ -117,6 +134,14 @@ int soc_camera_power_off(struct device *dev, struct soc_camera_subdev_desc *ssdd
 }
 EXPORT_SYMBOL(soc_camera_power_off);
 
+int soc_camera_power_init(struct device *dev, struct soc_camera_subdev_desc *ssdd)
+{
+
+	return devm_regulator_bulk_get(dev, ssdd->num_regulators,
+				       ssdd->regulators);
+}
+EXPORT_SYMBOL(soc_camera_power_init);
+
 static int __soc_camera_power_on(struct soc_camera_device *icd)
 {
 	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
@@ -533,7 +558,9 @@ static int soc_camera_add_device(struct soc_camera_device *icd)
 		return -EBUSY;
 
 	if (!icd->clk) {
+		mutex_lock(&ici->clk_lock);
 		ret = ici->ops->clock_start(ici);
+		mutex_unlock(&ici->clk_lock);
 		if (ret < 0)
 			return ret;
 	}
@@ -549,8 +576,11 @@ static int soc_camera_add_device(struct soc_camera_device *icd)
 	return 0;
 
 eadd:
-	if (!icd->clk)
+	if (!icd->clk) {
+		mutex_lock(&ici->clk_lock);
 		ici->ops->clock_stop(ici);
+		mutex_unlock(&ici->clk_lock);
+	}
 	return ret;
 }
 
@@ -563,8 +593,11 @@ static void soc_camera_remove_device(struct soc_camera_device *icd)
 
 	if (ici->ops->remove)
 		ici->ops->remove(icd);
-	if (!icd->clk)
+	if (!icd->clk) {
+		mutex_lock(&ici->clk_lock);
 		ici->ops->clock_stop(ici);
+		mutex_unlock(&ici->clk_lock);
+	}
 	ici->icd = NULL;
 }
 
@@ -673,8 +706,8 @@ static int soc_camera_open(struct file *file)
 	return 0;
 
 	/*
-	 * First four errors are entered with the .host_lock held
-	 * and use_count == 1
+	 * All errors are entered with the .host_lock held, first four also
+	 * with use_count == 1
 	 */
 einitvb:
 esfmt:
@@ -1128,7 +1161,8 @@ static int soc_camera_s_register(struct file *file, void *fh,
 }
 #endif
 
-static int soc_camera_probe(struct soc_camera_device *icd);
+static int soc_camera_probe(struct soc_camera_host *ici,
+			    struct soc_camera_device *icd);
 
 /* So far this function cannot fail */
 static void scan_add_host(struct soc_camera_host *ici)
@@ -1137,12 +1171,20 @@ static void scan_add_host(struct soc_camera_host *ici)
 
 	mutex_lock(&list_lock);
 
-	list_for_each_entry(icd, &devices, list) {
+	list_for_each_entry(icd, &devices, list)
 		if (icd->iface == ici->nr) {
+			struct soc_camera_desc *sdesc = to_soc_camera_desc(icd);
+			struct soc_camera_subdev_desc *ssdd = &sdesc->subdev_desc;
+
+			/* The camera could have been already on, try to reset */
+			if (ssdd->reset)
+				ssdd->reset(icd->pdev);
+
 			icd->parent = ici->v4l2_dev.dev;
-			soc_camera_probe(icd);
+
+			/* Ignore errors */
+			soc_camera_probe(ici, icd);
 		}
-	}
 
 	mutex_unlock(&list_lock);
 }
@@ -1155,6 +1197,7 @@ static int soc_camera_clk_enable(struct v4l2_clk *clk)
 {
 	struct soc_camera_device *icd = clk->priv;
 	struct soc_camera_host *ici;
+	int ret;
 
 	if (!icd || !icd->parent)
 		return -ENODEV;
@@ -1168,7 +1211,10 @@ static int soc_camera_clk_enable(struct v4l2_clk *clk)
 	 * If a different client is currently being probed, the host will tell
 	 * you to go
 	 */
-	return ici->ops->clock_start(ici);
+	mutex_lock(&ici->clk_lock);
+	ret = ici->ops->clock_start(ici);
+	mutex_unlock(&ici->clk_lock);
+	return ret;
 }
 
 static void soc_camera_clk_disable(struct v4l2_clk *clk)
@@ -1181,7 +1227,9 @@ static void soc_camera_clk_disable(struct v4l2_clk *clk)
 
 	ici = to_soc_camera_host(icd->parent);
 
+	mutex_lock(&ici->clk_lock);
 	ici->ops->clock_stop(ici);
+	mutex_unlock(&ici->clk_lock);
 
 	module_put(ici->ops->owner);
 }
@@ -1198,18 +1246,117 @@ static const struct v4l2_clk_ops soc_camera_clk_ops = {
 	.disable = soc_camera_clk_disable,
 };
 
+static int soc_camera_dyn_pdev(struct soc_camera_desc *sdesc,
+			       struct soc_camera_async_client *sasc)
+{
+	struct platform_device *pdev;
+	int ret, i;
+
+	mutex_lock(&list_lock);
+	i = find_first_zero_bit(device_map, MAP_MAX_NUM);
+	if (i < MAP_MAX_NUM)
+		set_bit(i, device_map);
+	mutex_unlock(&list_lock);
+	if (i >= MAP_MAX_NUM)
+		return -ENOMEM;
+
+	pdev = platform_device_alloc("soc-camera-pdrv", i);
+	if (!pdev)
+		return -ENOMEM;
+
+	ret = platform_device_add_data(pdev, sdesc, sizeof(*sdesc));
+	if (ret < 0) {
+		platform_device_put(pdev);
+		return ret;
+	}
+
+	sasc->pdev = pdev;
+
+	return 0;
+}
+
+static struct soc_camera_device *soc_camera_add_pdev(struct soc_camera_async_client *sasc)
+{
+	struct platform_device *pdev = sasc->pdev;
+	int ret;
+
+	ret = platform_device_add(pdev);
+	if (ret < 0 || !pdev->dev.driver)
+		return NULL;
+
+	return platform_get_drvdata(pdev);
+}
+
+/* Locking: called with .host_lock held */
+static int soc_camera_probe_finish(struct soc_camera_device *icd)
+{
+	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+	struct v4l2_mbus_framefmt mf;
+	int ret;
+
+	sd->grp_id = soc_camera_grp_id(icd);
+	v4l2_set_subdev_hostdata(sd, icd);
+
+	ret = v4l2_ctrl_add_handler(&icd->ctrl_handler, sd->ctrl_handler, NULL);
+	if (ret < 0)
+		return ret;
+
+	ret = soc_camera_add_device(icd);
+	if (ret < 0) {
+		dev_err(icd->pdev, "Couldn't activate the camera: %d\n", ret);
+		return ret;
+	}
+
+	/* At this point client .probe() should have run already */
+	ret = soc_camera_init_user_formats(icd);
+	if (ret < 0)
+		goto eusrfmt;
+
+	icd->field = V4L2_FIELD_ANY;
+
+	ret = soc_camera_video_start(icd);
+	if (ret < 0)
+		goto evidstart;
+
+	/* Try to improve our guess of a reasonable window format */
+	if (!v4l2_subdev_call(sd, video, g_mbus_fmt, &mf)) {
+		icd->user_width		= mf.width;
+		icd->user_height	= mf.height;
+		icd->colorspace		= mf.colorspace;
+		icd->field		= mf.field;
+	}
+	soc_camera_remove_device(icd);
+
+	return 0;
+
+evidstart:
+	soc_camera_free_user_formats(icd);
+eusrfmt:
+	soc_camera_remove_device(icd);
+
+	return ret;
+}
+
 #ifdef CONFIG_I2C_BOARDINFO
-static int soc_camera_init_i2c(struct soc_camera_device *icd,
+static int soc_camera_i2c_init(struct soc_camera_device *icd,
 			       struct soc_camera_desc *sdesc)
 {
 	struct i2c_client *client;
-	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+	struct soc_camera_host *ici;
 	struct soc_camera_host_desc *shd = &sdesc->host_desc;
-	struct i2c_adapter *adap = i2c_get_adapter(shd->i2c_adapter_id);
+	struct i2c_adapter *adap;
 	struct v4l2_subdev *subdev;
 	char clk_name[V4L2_SUBDEV_NAME_SIZE];
 	int ret;
 
+	/* First find out how we link the main client */
+	if (icd->sasc) {
+		/* Async non-OF probing handled by the subdevice list */
+		return -EPROBE_DEFER;
+	}
+
+	ici = to_soc_camera_host(icd->parent);
+	adap = i2c_get_adapter(shd->i2c_adapter_id);
 	if (!adap) {
 		dev_err(icd->pdev, "Cannot get I2C adapter #%d. No driver?\n",
 			shd->i2c_adapter_id);
@@ -1242,42 +1389,202 @@ static int soc_camera_init_i2c(struct soc_camera_device *icd,
 	return 0;
 ei2cnd:
 	v4l2_clk_unregister(icd->clk);
-	icd->clk = NULL;
 eclkreg:
+	icd->clk = NULL;
 	i2c_put_adapter(adap);
 	return ret;
 }
 
-static void soc_camera_free_i2c(struct soc_camera_device *icd)
+static void soc_camera_i2c_free(struct soc_camera_device *icd)
 {
 	struct i2c_client *client =
 		to_i2c_client(to_soc_camera_control(icd));
-	struct i2c_adapter *adap = client->adapter;
+	struct i2c_adapter *adap;
 
 	icd->control = NULL;
+	if (icd->sasc)
+		return;
+
+	adap = client->adapter;
 	v4l2_device_unregister_subdev(i2c_get_clientdata(client));
 	i2c_unregister_device(client);
 	i2c_put_adapter(adap);
 	v4l2_clk_unregister(icd->clk);
 	icd->clk = NULL;
 }
+
+/*
+ * V4L2 asynchronous notifier callbacks. They are all called under a v4l2-async
+ * internal global mutex, therefore cannot race against other asynchronous
+ * events. Until notifier->complete() (soc_camera_async_complete()) is called,
+ * the video device node is not registered and no V4L fops can occur. Unloading
+ * of the host driver also calls a v4l2-async function, so also there we're
+ * protected.
+ */
+static int soc_camera_async_bound(struct v4l2_async_notifier *notifier,
+				  struct v4l2_subdev *sd,
+				  struct v4l2_async_subdev *asd)
+{
+	struct soc_camera_async_client *sasc = container_of(notifier,
+					struct soc_camera_async_client, notifier);
+	struct soc_camera_device *icd = platform_get_drvdata(sasc->pdev);
+
+	if (asd == sasc->sensor && !WARN_ON(icd->control)) {
+		struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+		/*
+		 * Only now we get subdevice-specific information like
+		 * regulators, flags, callbacks, etc.
+		 */
+		if (client) {
+			struct soc_camera_desc *sdesc = to_soc_camera_desc(icd);
+			struct soc_camera_subdev_desc *ssdd =
+				soc_camera_i2c_to_desc(client);
+			if (ssdd) {
+				memcpy(&sdesc->subdev_desc, ssdd,
+				       sizeof(sdesc->subdev_desc));
+				if (ssdd->reset)
+					ssdd->reset(icd->pdev);
+			}
+
+			icd->control = &client->dev;
+		}
+	}
+
+	return 0;
+}
+
+static void soc_camera_async_unbind(struct v4l2_async_notifier *notifier,
+				    struct v4l2_subdev *sd,
+				    struct v4l2_async_subdev *asd)
+{
+	struct soc_camera_async_client *sasc = container_of(notifier,
+					struct soc_camera_async_client, notifier);
+	struct soc_camera_device *icd = platform_get_drvdata(sasc->pdev);
+
+	if (icd->clk) {
+		v4l2_clk_unregister(icd->clk);
+		icd->clk = NULL;
+	}
+}
+
+static int soc_camera_async_complete(struct v4l2_async_notifier *notifier)
+{
+	struct soc_camera_async_client *sasc = container_of(notifier,
+					struct soc_camera_async_client, notifier);
+	struct soc_camera_device *icd = platform_get_drvdata(sasc->pdev);
+
+	if (to_soc_camera_control(icd)) {
+		struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+		int ret;
+
+		mutex_lock(&list_lock);
+		ret = soc_camera_probe(ici, icd);
+		mutex_unlock(&list_lock);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int scan_async_group(struct soc_camera_host *ici,
+			    struct v4l2_async_subdev **asd, int size)
+{
+	struct soc_camera_async_subdev *sasd;
+	struct soc_camera_async_client *sasc;
+	struct soc_camera_device *icd;
+	struct soc_camera_desc sdesc = {.host_desc.bus_id = ici->nr,};
+	char clk_name[V4L2_SUBDEV_NAME_SIZE];
+	int ret, i;
+
+	/* First look for a sensor */
+	for (i = 0; i < size; i++) {
+		sasd = container_of(asd[i], struct soc_camera_async_subdev, asd);
+		if (sasd->role == SOCAM_SUBDEV_DATA_SOURCE)
+			break;
+	}
+
+	if (i == size || asd[i]->bus_type != V4L2_ASYNC_BUS_I2C) {
+		/* All useless */
+		dev_err(ici->v4l2_dev.dev, "No I2C data source found!\n");
+		return -ENODEV;
+	}
+
+	/* Or shall this be managed by the soc-camera device? */
+	sasc = devm_kzalloc(ici->v4l2_dev.dev, sizeof(*sasc), GFP_KERNEL);
+	if (!sasc)
+		return -ENOMEM;
+
+	/* HACK: just need a != NULL */
+	sdesc.host_desc.board_info = ERR_PTR(-ENODATA);
+
+	ret = soc_camera_dyn_pdev(&sdesc, sasc);
+	if (ret < 0)
+		return ret;
+
+	sasc->sensor = &sasd->asd;
+
+	icd = soc_camera_add_pdev(sasc);
+	if (!icd) {
+		platform_device_put(sasc->pdev);
+		return -ENOMEM;
+	}
+
+	sasc->notifier.subdev = asd;
+	sasc->notifier.subdev_num = size;
+	sasc->notifier.bound = soc_camera_async_bound;
+	sasc->notifier.unbind = soc_camera_async_unbind;
+	sasc->notifier.complete = soc_camera_async_complete;
+
+	icd->sasc = sasc;
+	icd->parent = ici->v4l2_dev.dev;
+
+	snprintf(clk_name, sizeof(clk_name), "%d-%04x",
+		 sasd->asd.match.i2c.adapter_id, sasd->asd.match.i2c.address);
+
+	icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, "mclk", icd);
+	if (IS_ERR(icd->clk)) {
+		ret = PTR_ERR(icd->clk);
+		goto eclkreg;
+	}
+
+	ret = v4l2_async_notifier_register(&ici->v4l2_dev, &sasc->notifier);
+	if (!ret)
+		return 0;
+
+	v4l2_clk_unregister(icd->clk);
+eclkreg:
+	icd->clk = NULL;
+	platform_device_unregister(sasc->pdev);
+	dev_err(ici->v4l2_dev.dev, "group probe failed: %d\n", ret);
+
+	return ret;
+}
+
+static void scan_async_host(struct soc_camera_host *ici)
+{
+	struct v4l2_async_subdev **asd;
+	int j;
+
+	for (j = 0, asd = ici->asd; ici->asd_sizes[j]; j++) {
+		scan_async_group(ici, asd, ici->asd_sizes[j]);
+		asd += ici->asd_sizes[j];
+	}
+}
 #else
-#define soc_camera_init_i2c(icd, sdesc)	(-ENODEV)
-#define soc_camera_free_i2c(icd)	do {} while (0)
+#define soc_camera_i2c_init(icd, sdesc)	(-ENODEV)
+#define soc_camera_i2c_free(icd)	do {} while (0)
+#define scan_async_host(ici)		do {} while (0)
 #endif
 
-static int soc_camera_video_start(struct soc_camera_device *icd);
-static int video_dev_create(struct soc_camera_device *icd);
 /* Called during host-driver probe */
-static int soc_camera_probe(struct soc_camera_device *icd)
+static int soc_camera_probe(struct soc_camera_host *ici,
+			    struct soc_camera_device *icd)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct soc_camera_desc *sdesc = to_soc_camera_desc(icd);
 	struct soc_camera_host_desc *shd = &sdesc->host_desc;
-	struct soc_camera_subdev_desc *ssdd = &sdesc->subdev_desc;
 	struct device *control = NULL;
-	struct v4l2_subdev *sd;
-	struct v4l2_mbus_framefmt mf;
 	int ret;
 
 	dev_info(icd->pdev, "Probing %s\n", dev_name(icd->pdev));
@@ -1293,10 +1600,6 @@ static int soc_camera_probe(struct soc_camera_device *icd)
 	if (ret < 0)
 		return ret;
 
-	/* The camera could have been already on, try to reset */
-	if (ssdd->reset)
-		ssdd->reset(icd->pdev);
-
 	/* Must have icd->vdev before registering the device */
 	ret = video_dev_create(icd);
 	if (ret < 0)
@@ -1307,18 +1610,19 @@ static int soc_camera_probe(struct soc_camera_device *icd)
 	 * itself is protected against concurrent open() calls, but we also have
 	 * to protect our data also during client probing.
 	 */
-	mutex_lock(&ici->host_lock);
 
 	/* Non-i2c cameras, e.g., soc_camera_platform, have no board_info */
 	if (shd->board_info) {
-		ret = soc_camera_init_i2c(icd, sdesc);
-		if (ret < 0)
+		ret = soc_camera_i2c_init(icd, sdesc);
+		if (ret < 0 && ret != -EPROBE_DEFER)
 			goto eadd;
 	} else if (!shd->add_device || !shd->del_device) {
 		ret = -EINVAL;
 		goto eadd;
 	} else {
+		mutex_lock(&ici->clk_lock);
 		ret = ici->ops->clock_start(ici);
+		mutex_unlock(&ici->clk_lock);
 		if (ret < 0)
 			goto eadd;
 
@@ -1342,57 +1646,33 @@ static int soc_camera_probe(struct soc_camera_device *icd)
 		}
 	}
 
-	sd = soc_camera_to_subdev(icd);
-	sd->grp_id = soc_camera_grp_id(icd);
-	v4l2_set_subdev_hostdata(sd, icd);
-
-	ret = v4l2_ctrl_add_handler(&icd->ctrl_handler, sd->ctrl_handler, NULL);
-	if (ret < 0)
-		goto ectrl;
-
-	/* At this point client .probe() should have run already */
-	ret = soc_camera_init_user_formats(icd);
-	if (ret < 0)
-		goto eiufmt;
-
-	icd->field = V4L2_FIELD_ANY;
-
-	ret = soc_camera_video_start(icd);
-	if (ret < 0)
-		goto evidstart;
-
-	/* Try to improve our guess of a reasonable window format */
-	if (!v4l2_subdev_call(sd, video, g_mbus_fmt, &mf)) {
-		icd->user_width		= mf.width;
-		icd->user_height	= mf.height;
-		icd->colorspace		= mf.colorspace;
-		icd->field		= mf.field;
-	}
-
-	if (!shd->board_info)
-		ici->ops->clock_stop(ici);
-
+	mutex_lock(&ici->host_lock);
+	ret = soc_camera_probe_finish(icd);
 	mutex_unlock(&ici->host_lock);
+	if (ret < 0)
+		goto efinish;
 
 	return 0;
 
-evidstart:
-	soc_camera_free_user_formats(icd);
-eiufmt:
-ectrl:
+efinish:
 	if (shd->board_info) {
-		soc_camera_free_i2c(icd);
+		soc_camera_i2c_free(icd);
 	} else {
 		shd->del_device(icd);
 		module_put(control->driver->owner);
 enodrv:
 eadddev:
+		mutex_lock(&ici->clk_lock);
 		ici->ops->clock_stop(ici);
+		mutex_unlock(&ici->clk_lock);
 	}
 eadd:
 	video_device_release(icd->vdev);
 	icd->vdev = NULL;
-	mutex_unlock(&ici->host_lock);
+	if (icd->vdev) {
+		video_device_release(icd->vdev);
+		icd->vdev = NULL;
+	}
 evdc:
 	v4l2_ctrl_handler_free(&icd->ctrl_handler);
 	return ret;
@@ -1400,15 +1680,15 @@ evdc:
 
 /*
  * This is called on device_unregister, which only means we have to disconnect
- * from the host, but not remove ourselves from the device list
+ * from the host, but not remove ourselves from the device list. With
+ * asynchronous client probing this can also be called without
+ * soc_camera_probe_finish() having run. Careful with clean up.
  */
 static int soc_camera_remove(struct soc_camera_device *icd)
 {
 	struct soc_camera_desc *sdesc = to_soc_camera_desc(icd);
 	struct video_device *vdev = icd->vdev;
 
-	BUG_ON(!icd->parent);
-
 	v4l2_ctrl_handler_free(&icd->ctrl_handler);
 	if (vdev) {
 		video_unregister_device(vdev);
@@ -1416,15 +1696,27 @@ static int soc_camera_remove(struct soc_camera_device *icd)
 	}
 
 	if (sdesc->host_desc.board_info) {
-		soc_camera_free_i2c(icd);
+		soc_camera_i2c_free(icd);
 	} else {
-		struct device_driver *drv = to_soc_camera_control(icd)->driver;
+		struct device *dev = to_soc_camera_control(icd);
+		struct device_driver *drv = dev ? dev->driver : NULL;
 		if (drv) {
 			sdesc->host_desc.del_device(icd);
 			module_put(drv->owner);
 		}
 	}
-	soc_camera_free_user_formats(icd);
+
+	if (icd->num_user_formats)
+		soc_camera_free_user_formats(icd);
+
+	if (icd->clk) {
+		/* For the synchronous case */
+		v4l2_clk_unregister(icd->clk);
+		icd->clk = NULL;
+	}
+
+	if (icd->sasc)
+		platform_device_unregister(icd->sasc->pdev);
 
 	return 0;
 }
@@ -1535,7 +1827,18 @@ int soc_camera_host_register(struct soc_camera_host *ici)
 	mutex_unlock(&list_lock);
 
 	mutex_init(&ici->host_lock);
-	scan_add_host(ici);
+	mutex_init(&ici->clk_lock);
+
+	if (ici->asd_sizes)
+		/*
+		 * No OF, host with a list of subdevices. Don't try to mix
+		 * modes by initialising some groups statically and some
+		 * dynamically!
+		 */
+		scan_async_host(ici);
+	else
+		/* Legacy: static platform devices from board data */
+		scan_add_host(ici);
 
 	return 0;
 
@@ -1548,13 +1851,30 @@ EXPORT_SYMBOL(soc_camera_host_register);
 /* Unregister all clients! */
 void soc_camera_host_unregister(struct soc_camera_host *ici)
 {
-	struct soc_camera_device *icd;
+	struct soc_camera_device *icd, *tmp;
+	struct soc_camera_async_client *sasc;
+	LIST_HEAD(notifiers);
 
 	mutex_lock(&list_lock);
-
 	list_del(&ici->list);
 	list_for_each_entry(icd, &devices, list)
-		if (icd->iface == ici->nr && to_soc_camera_control(icd))
+		if (icd->iface == ici->nr && icd->sasc) {
+			/* as long as we hold the device, sasc won't be freed */
+			get_device(icd->pdev);
+			list_add(&icd->sasc->list, &notifiers);
+		}
+	mutex_unlock(&list_lock);
+
+	list_for_each_entry(sasc, &notifiers, list) {
+		/* Must call unlocked to avoid AB-BA dead-lock */
+		v4l2_async_notifier_unregister(&sasc->notifier);
+		put_device(&sasc->pdev->dev);
+	}
+
+	mutex_lock(&list_lock);
+
+	list_for_each_entry_safe(icd, tmp, &devices, list)
+		if (icd->iface == ici->nr)
 			soc_camera_remove(icd);
 
 	mutex_unlock(&list_lock);
@@ -1569,6 +1889,7 @@ static int soc_camera_device_register(struct soc_camera_device *icd)
 	struct soc_camera_device *ix;
 	int num = -1, i;
 
+	mutex_lock(&list_lock);
 	for (i = 0; i < 256 && num < 0; i++) {
 		num = i;
 		/* Check if this index is available on this interface */
@@ -1580,18 +1901,34 @@ static int soc_camera_device_register(struct soc_camera_device *icd)
 		}
 	}
 
-	if (num < 0)
+	if (num < 0) {
 		/*
 		 * ok, we have 256 cameras on this host...
 		 * man, stay reasonable...
 		 */
+		mutex_unlock(&list_lock);
 		return -ENOMEM;
+	}
 
 	icd->devnum		= num;
 	icd->use_count		= 0;
 	icd->host_priv		= NULL;
 
+	/*
+	 * Dynamically allocated devices set the bit earlier, but it doesn't hurt setting
+	 * it again
+	 */
+	i = to_platform_device(icd->pdev)->id;
+	if (i < 0)
+		/* One static (legacy) soc-camera platform device */
+		i = 0;
+	if (i >= MAP_MAX_NUM) {
+		mutex_unlock(&list_lock);
+		return -EBUSY;
+	}
+	set_bit(i, device_map);
 	list_add_tail(&icd->list, &devices);
+	mutex_unlock(&list_lock);
 
 	return 0;
 }
@@ -1691,6 +2028,12 @@ static int soc_camera_pdrv_probe(struct platform_device *pdev)
 	if (!icd)
 		return -ENOMEM;
 
+	/*
+	 * In the asynchronous case ssdd->num_regulators == 0 yet, so, the below
+	 * regulator allocation is a dummy. They will be really requested later
+	 * in soc_camera_async_bind(). Also note, that in that case regulators
+	 * are attached to the I2C device and not to the camera platform device.
+	 */
 	ret = devm_regulator_bulk_get(&pdev->dev, ssdd->num_regulators,
 				      ssdd->regulators);
 	if (ret < 0)
@@ -1715,11 +2058,25 @@ static int soc_camera_pdrv_probe(struct platform_device *pdev)
 static int soc_camera_pdrv_remove(struct platform_device *pdev)
 {
 	struct soc_camera_device *icd = platform_get_drvdata(pdev);
+	int i;
 
 	if (!icd)
 		return -EINVAL;
 
-	list_del(&icd->list);
+	i = pdev->id;
+	if (i < 0)
+		i = 0;
+
+	/*
+	 * In synchronous mode with static platform devices this is called in a
+	 * loop from drivers/base/dd.c::driver_detach(), no parallel execution,
+	 * no need to lock. In asynchronous case the caller -
+	 * soc_camera_host_unregister() - already holds the lock
+	 */
+	if (test_bit(i, device_map)) {
+		clear_bit(i, device_map);
+		list_del(&icd->list);
+	}
 
 	return 0;
 }
diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h
index f8c1ffe..2d3c939 100644
--- a/include/media/soc_camera.h
+++ b/include/media/soc_camera.h
@@ -19,11 +19,13 @@
 #include <linux/videodev2.h>
 #include <media/videobuf-core.h>
 #include <media/videobuf2-core.h>
+#include <media/v4l2-async.h>
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-device.h>
 
 struct file;
 struct soc_camera_desc;
+struct soc_camera_async_client;
 
 struct soc_camera_device {
 	struct list_head list;		/* list of all registered devices */
@@ -50,6 +52,9 @@ struct soc_camera_device {
 	int use_count;
 	struct file *streamer;		/* stream owner */
 	struct v4l2_clk *clk;
+	/* Asynchronous subdevice management */
+	struct soc_camera_async_client *sasc;
+	/* video buffer queue */
 	union {
 		struct videobuf_queue vb_vidq;
 		struct vb2_queue vb2_vidq;
@@ -59,16 +64,30 @@ struct soc_camera_device {
 /* Host supports programmable stride */
 #define SOCAM_HOST_CAP_STRIDE		(1 << 0)
 
+enum soc_camera_subdev_role {
+	SOCAM_SUBDEV_DATA_SOURCE = 1,
+	SOCAM_SUBDEV_DATA_SINK,
+	SOCAM_SUBDEV_DATA_PROCESSOR,
+};
+
+struct soc_camera_async_subdev {
+	struct v4l2_async_subdev asd;
+	enum soc_camera_subdev_role role;
+};
+
 struct soc_camera_host {
 	struct v4l2_device v4l2_dev;
 	struct list_head list;
-	struct mutex host_lock;		/* Protect pipeline modifications */
+	struct mutex host_lock;		/* Main synchronisation lock */
+	struct mutex clk_lock;		/* Protect pipeline modifications */
 	unsigned char nr;		/* Host number */
 	u32 capabilities;
 	struct soc_camera_device *icd;	/* Currently attached client */
 	void *priv;
 	const char *drv_name;
 	struct soc_camera_host_ops *ops;
+	struct v4l2_async_subdev **asd;	/* Flat array, arranged in groups */
+	int *asd_sizes;			/* 0-terminated array of asd group sizes */
 };
 
 struct soc_camera_host_ops {
@@ -161,6 +180,7 @@ struct soc_camera_host_desc {
 };
 
 /*
+ * Platform data for "soc-camera-pdrv"
  * This MUST be kept binary-identical to struct soc_camera_link below, until
  * it is completely replaced by this one, after which we can split it into its
  * two components.
@@ -326,6 +346,7 @@ static inline void soc_camera_limit_side(int *start, int *length,
 unsigned long soc_camera_apply_board_flags(struct soc_camera_subdev_desc *ssdd,
 					   const struct v4l2_mbus_config *cfg);
 
+int soc_camera_power_init(struct device *dev, struct soc_camera_subdev_desc *ssdd);
 int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd,
 			struct v4l2_clk *clk);
 int soc_camera_power_off(struct device *dev, struct soc_camera_subdev_desc *ssdd,
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v10 19/21] sh_mobile_ceu_camera: add asynchronous subdevice probing support
  2013-06-11  8:23 ` Guennadi Liakhovetski
@ 2013-06-11  8:23   ` Guennadi Liakhovetski
  -1 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-11  8:23 UTC (permalink / raw)
  To: linux-media
  Cc: Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer,
	Guennadi Liakhovetski

Use the v4l2-async API to support asynchronous subdevice probing,
including the CSI2 subdevice. Synchronous probing is still supported too.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
 .../platform/soc_camera/sh_mobile_ceu_camera.c     |  134 +++++++++++++-----
 drivers/media/platform/soc_camera/sh_mobile_csi2.c |  153 ++++++++++++--------
 include/media/sh_mobile_ceu.h                      |    2 +
 include/media/sh_mobile_csi2.h                     |    2 +-
 4 files changed, 190 insertions(+), 101 deletions(-)

diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
index b0f0995..d1b410b 100644
--- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
+++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
@@ -36,6 +36,7 @@
 #include <linux/pm_runtime.h>
 #include <linux/sched.h>
 
+#include <media/v4l2-async.h>
 #include <media/v4l2-common.h>
 #include <media/v4l2-dev.h>
 #include <media/soc_camera.h>
@@ -96,6 +97,10 @@ struct sh_mobile_ceu_buffer {
 
 struct sh_mobile_ceu_dev {
 	struct soc_camera_host ici;
+	/* Asynchronous CSI2 linking */
+	struct v4l2_async_subdev *csi2_asd;
+	struct v4l2_subdev *csi2_sd;
+	/* Synchronous probing compatibility */
 	struct platform_device *csi2_pdev;
 
 	unsigned int irq;
@@ -185,7 +190,6 @@ static int sh_mobile_ceu_soft_reset(struct sh_mobile_ceu_dev *pcdev)
 		udelay(1);
 	}
 
-
 	if (2 != success) {
 		dev_warn(pcdev->ici.v4l2_dev.dev, "soft reset time out\n");
 		return -EIO;
@@ -534,16 +538,29 @@ static struct v4l2_subdev *find_csi2(struct sh_mobile_ceu_dev *pcdev)
 {
 	struct v4l2_subdev *sd;
 
-	if (!pcdev->csi2_pdev)
-		return NULL;
+	if (pcdev->csi2_sd)
+		return pcdev->csi2_sd;
 
-	v4l2_device_for_each_subdev(sd, &pcdev->ici.v4l2_dev)
-		if (&pcdev->csi2_pdev->dev = v4l2_get_subdevdata(sd))
-			return sd;
+	if (pcdev->csi2_asd) {
+		char name[] = "sh-mobile-csi2";
+		v4l2_device_for_each_subdev(sd, &pcdev->ici.v4l2_dev)
+			if (!strncmp(name, sd->name, sizeof(name) - 1)) {
+				pcdev->csi2_sd = sd;
+				return sd;
+			}
+	}
 
 	return NULL;
 }
 
+static struct v4l2_subdev *csi2_subdev(struct sh_mobile_ceu_dev *pcdev,
+				       struct soc_camera_device *icd)
+{
+	struct v4l2_subdev *sd = pcdev->csi2_sd;
+
+	return sd && sd->grp_id = soc_camera_grp_id(icd) ? sd : NULL;
+}
+
 static int sh_mobile_ceu_add_device(struct soc_camera_device *icd)
 {
 	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
@@ -564,12 +581,12 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd)
 	 * -ENODEV is special: either csi2_sd = NULL or the CSI-2 driver
 	 * has not found this soc-camera device among its clients
 	 */
-	if (ret = -ENODEV && csi2_sd)
+	if (csi2_sd && ret = -ENODEV)
 		csi2_sd->grp_id = 0;
 
 	dev_info(icd->parent,
-		 "SuperH Mobile CEU driver attached to camera %d\n",
-		 icd->devnum);
+		 "SuperH Mobile CEU%s driver attached to camera %d\n",
+		 csi2_sd && csi2_sd->grp_id ? "/CSI-2" : "", icd->devnum);
 
 	return 0;
 }
@@ -585,8 +602,6 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd)
 		 icd->devnum);
 
 	v4l2_subdev_call(csi2_sd, core, s_power, 0);
-	if (csi2_sd)
-		csi2_sd->grp_id = 0;
 }
 
 /* Called with .host_lock held */
@@ -708,7 +723,7 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd)
 	}
 
 	/* CSI2 special configuration */
-	if (pcdev->csi2_pdev) {
+	if (csi2_subdev(pcdev, icd)) {
 		in_width = ((in_width - 2) * 2);
 		left_offset *= 2;
 	}
@@ -765,13 +780,7 @@ static void capture_restore(struct sh_mobile_ceu_dev *pcdev, u32 capsr)
 static struct v4l2_subdev *find_bus_subdev(struct sh_mobile_ceu_dev *pcdev,
 					   struct soc_camera_device *icd)
 {
-	if (pcdev->csi2_pdev) {
-		struct v4l2_subdev *csi2_sd = find_csi2(pcdev);
-		if (csi2_sd && csi2_sd->grp_id = soc_camera_grp_id(icd))
-			return csi2_sd;
-	}
-
-	return soc_camera_to_subdev(icd);
+	return csi2_subdev(pcdev, icd) ? : soc_camera_to_subdev(icd);
 }
 
 #define CEU_BUS_FLAGS (V4L2_MBUS_MASTER |	\
@@ -875,7 +884,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd)
 	value |= common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW ? 1 << 1 : 0;
 	value |= common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW ? 1 << 0 : 0;
 
-	if (pcdev->csi2_pdev) /* CSI2 mode */
+	if (csi2_subdev(pcdev, icd)) /* CSI2 mode */
 		value |= 3 << 12;
 	else if (pcdev->is_16bit)
 		value |= 1 << 12;
@@ -1054,7 +1063,7 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int
 		return 0;
 	}
 
-	if (!pcdev->pdata || !pcdev->pdata->csi2) {
+	if (!csi2_subdev(pcdev, icd)) {
 		/* Are there any restrictions in the CSI-2 case? */
 		ret = sh_mobile_ceu_try_bus_param(icd, fmt->bits_per_sample);
 		if (ret < 0)
@@ -2084,7 +2093,7 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev)
 	struct resource *res;
 	void __iomem *base;
 	unsigned int irq;
-	int err = 0;
+	int err, i;
 	struct bus_wait wait = {
 		.completion = COMPLETION_INITIALIZER_ONSTACK(wait.completion),
 		.notifier.notifier_call = bus_notify,
@@ -2188,31 +2197,60 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev)
 		goto exit_free_clk;
 	}
 
-	err = soc_camera_host_register(&pcdev->ici);
-	if (err)
-		goto exit_free_ctx;
+	if (pcdev->pdata && pcdev->pdata->asd_sizes) {
+		struct v4l2_async_subdev **asd;
+		char name[] = "sh-mobile-csi2";
+		int j;
+
+		/*
+		 * CSI2 interfacing: several groups can use CSI2, pick up the
+		 * first one
+		 */
+		asd = pcdev->pdata->asd;
+		for (j = 0; pcdev->pdata->asd_sizes[j]; j++) {
+			for (i = 0; i < pcdev->pdata->asd_sizes[j]; i++, asd++) {
+				dev_dbg(&pdev->dev, "%s(): subdev #%d, type %u\n",
+					__func__, i, (*asd)->bus_type);
+				if ((*asd)->bus_type = V4L2_ASYNC_BUS_PLATFORM &&
+				    !strncmp(name, (*asd)->match.platform.name,
+					     sizeof(name) - 1)) {
+					pcdev->csi2_asd = *asd;
+					break;
+				}
+			}
+			if (pcdev->csi2_asd)
+				break;
+		}
+
+		pcdev->ici.asd = pcdev->pdata->asd;
+		pcdev->ici.asd_sizes = pcdev->pdata->asd_sizes;
+	}
 
-	/* CSI2 interfacing */
+	/* Legacy CSI2 interfacing */
 	csi2 = pcdev->pdata ? pcdev->pdata->csi2 : NULL;
 	if (csi2) {
+		/*
+		 * TODO: remove this once all users are converted to
+		 * asynchronous CSI2 probing. If it has to be kept, csi2
+		 * platform device resources have to be added, using
+		 * platform_device_add_resources()
+		 */
 		struct platform_device *csi2_pdev  			platform_device_alloc("sh-mobile-csi2", csi2->id);
 		struct sh_csi2_pdata *csi2_pdata = csi2->platform_data;
 
 		if (!csi2_pdev) {
 			err = -ENOMEM;
-			goto exit_host_unregister;
+			goto exit_free_ctx;
 		}
 
 		pcdev->csi2_pdev		= csi2_pdev;
 
-		err = platform_device_add_data(csi2_pdev, csi2_pdata, sizeof(*csi2_pdata));
+		err = platform_device_add_data(csi2_pdev, csi2_pdata,
+					       sizeof(*csi2_pdata));
 		if (err < 0)
 			goto exit_pdev_put;
 
-		csi2_pdata			= csi2_pdev->dev.platform_data;
-		csi2_pdata->v4l2_dev		= &pcdev->ici.v4l2_dev;
-
 		csi2_pdev->resource		= csi2->resource;
 		csi2_pdev->num_resources	= csi2->num_resources;
 
@@ -2254,17 +2292,38 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev)
 			err = -ENODEV;
 			goto exit_pdev_unregister;
 		}
+
+		pcdev->csi2_sd = platform_get_drvdata(csi2_pdev);
+	}
+
+	err = soc_camera_host_register(&pcdev->ici);
+	if (err)
+		goto exit_csi2_unregister;
+
+	if (csi2) {
+		err = v4l2_device_register_subdev(&pcdev->ici.v4l2_dev,
+						  pcdev->csi2_sd);
+		dev_dbg(&pdev->dev, "%s(): ret(register_subdev) = %d\n",
+			__func__, err);
+		if (err < 0)
+			goto exit_host_unregister;
+		/* v4l2_device_register_subdev() took a reference too */
+		module_put(pcdev->csi2_sd->owner);
 	}
 
 	return 0;
 
-exit_pdev_unregister:
-	platform_device_del(pcdev->csi2_pdev);
-exit_pdev_put:
-	pcdev->csi2_pdev->resource = NULL;
-	platform_device_put(pcdev->csi2_pdev);
 exit_host_unregister:
 	soc_camera_host_unregister(&pcdev->ici);
+exit_csi2_unregister:
+	if (csi2) {
+		module_put(pcdev->csi2_pdev->dev.driver->owner);
+exit_pdev_unregister:
+		platform_device_del(pcdev->csi2_pdev);
+exit_pdev_put:
+		pcdev->csi2_pdev->resource = NULL;
+		platform_device_put(pcdev->csi2_pdev);
+	}
 exit_free_ctx:
 	vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
 exit_free_clk:
@@ -2324,6 +2383,7 @@ MODULE_DEVICE_TABLE(of, sh_mobile_ceu_of_match);
 static struct platform_driver sh_mobile_ceu_driver = {
 	.driver		= {
 		.name	= "sh_mobile_ceu",
+		.owner	= THIS_MODULE,
 		.pm	= &sh_mobile_ceu_dev_pm_ops,
 		.of_match_table = sh_mobile_ceu_of_match,
 	},
@@ -2349,5 +2409,5 @@ module_exit(sh_mobile_ceu_exit);
 MODULE_DESCRIPTION("SuperH Mobile CEU driver");
 MODULE_AUTHOR("Magnus Damm");
 MODULE_LICENSE("GPL");
-MODULE_VERSION("0.0.6");
+MODULE_VERSION("0.1.0");
 MODULE_ALIAS("platform:sh_mobile_ceu");
diff --git a/drivers/media/platform/soc_camera/sh_mobile_csi2.c b/drivers/media/platform/soc_camera/sh_mobile_csi2.c
index 13a1f8f..05dd21a 100644
--- a/drivers/media/platform/soc_camera/sh_mobile_csi2.c
+++ b/drivers/media/platform/soc_camera/sh_mobile_csi2.c
@@ -36,7 +36,6 @@
 
 struct sh_csi2 {
 	struct v4l2_subdev		subdev;
-	struct list_head		list;
 	unsigned int			irq;
 	unsigned long			mipi_flags;
 	void __iomem			*base;
@@ -44,6 +43,8 @@ struct sh_csi2 {
 	struct sh_csi2_client_config	*client;
 };
 
+static void sh_csi2_hwinit(struct sh_csi2 *priv);
+
 static int sh_csi2_try_fmt(struct v4l2_subdev *sd,
 			   struct v4l2_mbus_framefmt *mf)
 {
@@ -132,10 +133,58 @@ static int sh_csi2_s_fmt(struct v4l2_subdev *sd,
 static int sh_csi2_g_mbus_config(struct v4l2_subdev *sd,
 				 struct v4l2_mbus_config *cfg)
 {
-	cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING |
-		V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH |
-		V4L2_MBUS_MASTER | V4L2_MBUS_DATA_ACTIVE_HIGH;
-	cfg->type = V4L2_MBUS_PARALLEL;
+	struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev);
+
+	if (!priv->mipi_flags) {
+		struct soc_camera_device *icd = v4l2_get_subdev_hostdata(sd);
+		struct v4l2_subdev *client_sd = soc_camera_to_subdev(icd);
+		struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data;
+		unsigned long common_flags, csi2_flags;
+		struct v4l2_mbus_config client_cfg = {.type = V4L2_MBUS_CSI2,};
+		int ret;
+
+		/* Check if we can support this camera */
+		csi2_flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK |
+			V4L2_MBUS_CSI2_1_LANE;
+
+		switch (pdata->type) {
+		case SH_CSI2C:
+			if (priv->client->lanes != 1)
+				csi2_flags |= V4L2_MBUS_CSI2_2_LANE;
+			break;
+		case SH_CSI2I:
+			switch (priv->client->lanes) {
+			default:
+				csi2_flags |= V4L2_MBUS_CSI2_4_LANE;
+			case 3:
+				csi2_flags |= V4L2_MBUS_CSI2_3_LANE;
+			case 2:
+				csi2_flags |= V4L2_MBUS_CSI2_2_LANE;
+			}
+		}
+
+		ret = v4l2_subdev_call(client_sd, video, g_mbus_config, &client_cfg);
+		if (ret = -ENOIOCTLCMD)
+			common_flags = csi2_flags;
+		else if (!ret)
+			common_flags = soc_mbus_config_compatible(&client_cfg,
+								  csi2_flags);
+		else
+			common_flags = 0;
+
+		if (!common_flags)
+			return -EINVAL;
+
+		/* All good: camera MIPI configuration supported */
+		priv->mipi_flags = common_flags;
+	}
+
+	if (cfg) {
+		cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING |
+			V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH |
+			V4L2_MBUS_MASTER | V4L2_MBUS_DATA_ACTIVE_HIGH;
+		cfg->type = V4L2_MBUS_PARALLEL;
+	}
 
 	return 0;
 }
@@ -146,8 +195,17 @@ static int sh_csi2_s_mbus_config(struct v4l2_subdev *sd,
 	struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev);
 	struct soc_camera_device *icd = v4l2_get_subdev_hostdata(sd);
 	struct v4l2_subdev *client_sd = soc_camera_to_subdev(icd);
-	struct v4l2_mbus_config client_cfg = {.type = V4L2_MBUS_CSI2,
-					      .flags = priv->mipi_flags};
+	struct v4l2_mbus_config client_cfg = {.type = V4L2_MBUS_CSI2,};
+	int ret = sh_csi2_g_mbus_config(sd, NULL);
+
+	if (ret < 0)
+		return ret;
+
+	pm_runtime_get_sync(&priv->pdev->dev);
+
+	sh_csi2_hwinit(priv);
+
+	client_cfg.flags = priv->mipi_flags;
 
 	return v4l2_subdev_call(client_sd, video, s_mbus_config, &client_cfg);
 }
@@ -202,19 +260,19 @@ static void sh_csi2_hwinit(struct sh_csi2 *priv)
 
 static int sh_csi2_client_connect(struct sh_csi2 *priv)
 {
-	struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data;
-	struct soc_camera_device *icd = v4l2_get_subdev_hostdata(&priv->subdev);
-	struct v4l2_subdev *client_sd = soc_camera_to_subdev(icd);
 	struct device *dev = v4l2_get_subdevdata(&priv->subdev);
-	struct v4l2_mbus_config cfg;
-	unsigned long common_flags, csi2_flags;
-	int i, ret;
+	struct sh_csi2_pdata *pdata = dev->platform_data;
+	struct soc_camera_device *icd = v4l2_get_subdev_hostdata(&priv->subdev);
+	int i;
 
 	if (priv->client)
 		return -EBUSY;
 
 	for (i = 0; i < pdata->num_clients; i++)
-		if (&pdata->clients[i].pdev->dev = icd->pdev)
+		if ((pdata->clients[i].pdev &&
+		     &pdata->clients[i].pdev->dev = icd->pdev) ||
+		    (icd->control &&
+		     strcmp(pdata->clients[i].name, dev_name(icd->control))))
 			break;
 
 	dev_dbg(dev, "%s(%p): found #%d\n", __func__, dev, i);
@@ -222,46 +280,8 @@ static int sh_csi2_client_connect(struct sh_csi2 *priv)
 	if (i = pdata->num_clients)
 		return -ENODEV;
 
-	/* Check if we can support this camera */
-	csi2_flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK | V4L2_MBUS_CSI2_1_LANE;
-
-	switch (pdata->type) {
-	case SH_CSI2C:
-		if (pdata->clients[i].lanes != 1)
-			csi2_flags |= V4L2_MBUS_CSI2_2_LANE;
-		break;
-	case SH_CSI2I:
-		switch (pdata->clients[i].lanes) {
-		default:
-			csi2_flags |= V4L2_MBUS_CSI2_4_LANE;
-		case 3:
-			csi2_flags |= V4L2_MBUS_CSI2_3_LANE;
-		case 2:
-			csi2_flags |= V4L2_MBUS_CSI2_2_LANE;
-		}
-	}
-
-	cfg.type = V4L2_MBUS_CSI2;
-	ret = v4l2_subdev_call(client_sd, video, g_mbus_config, &cfg);
-	if (ret = -ENOIOCTLCMD)
-		common_flags = csi2_flags;
-	else if (!ret)
-		common_flags = soc_mbus_config_compatible(&cfg,
-							  csi2_flags);
-	else
-		common_flags = 0;
-
-	if (!common_flags)
-		return -EINVAL;
-
-	/* All good: camera MIPI configuration supported */
-	priv->mipi_flags = common_flags;
 	priv->client = pdata->clients + i;
 
-	pm_runtime_get_sync(dev);
-
-	sh_csi2_hwinit(priv);
-
 	return 0;
 }
 
@@ -304,11 +324,18 @@ static int sh_csi2_probe(struct platform_device *pdev)
 	/* Platform data specify the PHY, lanes, ECC, CRC */
 	struct sh_csi2_pdata *pdata = pdev->dev.platform_data;
 
+	if (!pdata)
+		return -EINVAL;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(struct sh_csi2), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	/* Interrupt unused so far */
 	irq = platform_get_irq(pdev, 0);
 
-	if (!res || (int)irq <= 0 || !pdata) {
+	if (!res || (int)irq <= 0) {
 		dev_err(&pdev->dev, "Not enough CSI2 platform resources.\n");
 		return -ENODEV;
 	}
@@ -319,10 +346,6 @@ static int sh_csi2_probe(struct platform_device *pdev)
 		return -EINVAL;
 	}
 
-	priv = devm_kzalloc(&pdev->dev, sizeof(struct sh_csi2), GFP_KERNEL);
-	if (!priv)
-		return -ENOMEM;
-
 	priv->irq = irq;
 
 	priv->base = devm_ioremap_resource(&pdev->dev, res);
@@ -330,15 +353,17 @@ static int sh_csi2_probe(struct platform_device *pdev)
 		return PTR_ERR(priv->base);
 
 	priv->pdev = pdev;
-	platform_set_drvdata(pdev, priv);
+	priv->subdev.owner = THIS_MODULE;
+	priv->subdev.dev = &pdev->dev;
+	platform_set_drvdata(pdev, &priv->subdev);
 
 	v4l2_subdev_init(&priv->subdev, &sh_csi2_subdev_ops);
 	v4l2_set_subdevdata(&priv->subdev, &pdev->dev);
 
 	snprintf(priv->subdev.name, V4L2_SUBDEV_NAME_SIZE, "%s.mipi-csi",
-		 dev_name(pdata->v4l2_dev->dev));
-	ret = v4l2_device_register_subdev(pdata->v4l2_dev, &priv->subdev);
-	dev_dbg(&pdev->dev, "%s(%p): ret(register_subdev) = %d\n", __func__, priv, ret);
+		 dev_name(&pdev->dev));
+
+	ret = v4l2_async_register_subdev(&priv->subdev);
 	if (ret < 0)
 		return ret;
 
@@ -351,9 +376,11 @@ static int sh_csi2_probe(struct platform_device *pdev)
 
 static int sh_csi2_remove(struct platform_device *pdev)
 {
-	struct sh_csi2 *priv = platform_get_drvdata(pdev);
+	struct v4l2_subdev *subdev = platform_get_drvdata(pdev);
+	struct sh_csi2 *priv = container_of(subdev, struct sh_csi2, subdev);
 
-	v4l2_device_unregister_subdev(&priv->subdev);
+	v4l2_async_unregister_subdev(&priv->subdev);
+	v4l2_device_unregister_subdev(subdev);
 	pm_runtime_disable(&pdev->dev);
 
 	return 0;
diff --git a/include/media/sh_mobile_ceu.h b/include/media/sh_mobile_ceu.h
index 6fdb6ad..8937241 100644
--- a/include/media/sh_mobile_ceu.h
+++ b/include/media/sh_mobile_ceu.h
@@ -22,6 +22,8 @@ struct sh_mobile_ceu_info {
 	int max_width;
 	int max_height;
 	struct sh_mobile_ceu_companion *csi2;
+	struct v4l2_async_subdev **asd;	/* Flat array, arranged in groups */
+	int *asd_sizes;			/* 0-terminated array pf asd group sizes */
 };
 
 #endif /* __ASM_SH_MOBILE_CEU_H__ */
diff --git a/include/media/sh_mobile_csi2.h b/include/media/sh_mobile_csi2.h
index c586c4f..14030db 100644
--- a/include/media/sh_mobile_csi2.h
+++ b/include/media/sh_mobile_csi2.h
@@ -33,6 +33,7 @@ struct sh_csi2_client_config {
 	unsigned char lanes;		/* bitmask[3:0] */
 	unsigned char channel;		/* 0..3 */
 	struct platform_device *pdev;	/* client platform device */
+	const char *name;		/* async matching: client name */
 };
 
 struct v4l2_device;
@@ -42,7 +43,6 @@ struct sh_csi2_pdata {
 	unsigned int flags;
 	struct sh_csi2_client_config *clients;
 	int num_clients;
-	struct v4l2_device *v4l2_dev;
 };
 
 #endif
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v10 19/21] sh_mobile_ceu_camera: add asynchronous subdevice probing support
@ 2013-06-11  8:23   ` Guennadi Liakhovetski
  0 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-11  8:23 UTC (permalink / raw)
  To: linux-media
  Cc: Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer,
	Guennadi Liakhovetski

Use the v4l2-async API to support asynchronous subdevice probing,
including the CSI2 subdevice. Synchronous probing is still supported too.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
 .../platform/soc_camera/sh_mobile_ceu_camera.c     |  134 +++++++++++++-----
 drivers/media/platform/soc_camera/sh_mobile_csi2.c |  153 ++++++++++++--------
 include/media/sh_mobile_ceu.h                      |    2 +
 include/media/sh_mobile_csi2.h                     |    2 +-
 4 files changed, 190 insertions(+), 101 deletions(-)

diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
index b0f0995..d1b410b 100644
--- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
+++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
@@ -36,6 +36,7 @@
 #include <linux/pm_runtime.h>
 #include <linux/sched.h>
 
+#include <media/v4l2-async.h>
 #include <media/v4l2-common.h>
 #include <media/v4l2-dev.h>
 #include <media/soc_camera.h>
@@ -96,6 +97,10 @@ struct sh_mobile_ceu_buffer {
 
 struct sh_mobile_ceu_dev {
 	struct soc_camera_host ici;
+	/* Asynchronous CSI2 linking */
+	struct v4l2_async_subdev *csi2_asd;
+	struct v4l2_subdev *csi2_sd;
+	/* Synchronous probing compatibility */
 	struct platform_device *csi2_pdev;
 
 	unsigned int irq;
@@ -185,7 +190,6 @@ static int sh_mobile_ceu_soft_reset(struct sh_mobile_ceu_dev *pcdev)
 		udelay(1);
 	}
 
-
 	if (2 != success) {
 		dev_warn(pcdev->ici.v4l2_dev.dev, "soft reset time out\n");
 		return -EIO;
@@ -534,16 +538,29 @@ static struct v4l2_subdev *find_csi2(struct sh_mobile_ceu_dev *pcdev)
 {
 	struct v4l2_subdev *sd;
 
-	if (!pcdev->csi2_pdev)
-		return NULL;
+	if (pcdev->csi2_sd)
+		return pcdev->csi2_sd;
 
-	v4l2_device_for_each_subdev(sd, &pcdev->ici.v4l2_dev)
-		if (&pcdev->csi2_pdev->dev == v4l2_get_subdevdata(sd))
-			return sd;
+	if (pcdev->csi2_asd) {
+		char name[] = "sh-mobile-csi2";
+		v4l2_device_for_each_subdev(sd, &pcdev->ici.v4l2_dev)
+			if (!strncmp(name, sd->name, sizeof(name) - 1)) {
+				pcdev->csi2_sd = sd;
+				return sd;
+			}
+	}
 
 	return NULL;
 }
 
+static struct v4l2_subdev *csi2_subdev(struct sh_mobile_ceu_dev *pcdev,
+				       struct soc_camera_device *icd)
+{
+	struct v4l2_subdev *sd = pcdev->csi2_sd;
+
+	return sd && sd->grp_id == soc_camera_grp_id(icd) ? sd : NULL;
+}
+
 static int sh_mobile_ceu_add_device(struct soc_camera_device *icd)
 {
 	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
@@ -564,12 +581,12 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd)
 	 * -ENODEV is special: either csi2_sd == NULL or the CSI-2 driver
 	 * has not found this soc-camera device among its clients
 	 */
-	if (ret == -ENODEV && csi2_sd)
+	if (csi2_sd && ret == -ENODEV)
 		csi2_sd->grp_id = 0;
 
 	dev_info(icd->parent,
-		 "SuperH Mobile CEU driver attached to camera %d\n",
-		 icd->devnum);
+		 "SuperH Mobile CEU%s driver attached to camera %d\n",
+		 csi2_sd && csi2_sd->grp_id ? "/CSI-2" : "", icd->devnum);
 
 	return 0;
 }
@@ -585,8 +602,6 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd)
 		 icd->devnum);
 
 	v4l2_subdev_call(csi2_sd, core, s_power, 0);
-	if (csi2_sd)
-		csi2_sd->grp_id = 0;
 }
 
 /* Called with .host_lock held */
@@ -708,7 +723,7 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd)
 	}
 
 	/* CSI2 special configuration */
-	if (pcdev->csi2_pdev) {
+	if (csi2_subdev(pcdev, icd)) {
 		in_width = ((in_width - 2) * 2);
 		left_offset *= 2;
 	}
@@ -765,13 +780,7 @@ static void capture_restore(struct sh_mobile_ceu_dev *pcdev, u32 capsr)
 static struct v4l2_subdev *find_bus_subdev(struct sh_mobile_ceu_dev *pcdev,
 					   struct soc_camera_device *icd)
 {
-	if (pcdev->csi2_pdev) {
-		struct v4l2_subdev *csi2_sd = find_csi2(pcdev);
-		if (csi2_sd && csi2_sd->grp_id == soc_camera_grp_id(icd))
-			return csi2_sd;
-	}
-
-	return soc_camera_to_subdev(icd);
+	return csi2_subdev(pcdev, icd) ? : soc_camera_to_subdev(icd);
 }
 
 #define CEU_BUS_FLAGS (V4L2_MBUS_MASTER |	\
@@ -875,7 +884,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd)
 	value |= common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW ? 1 << 1 : 0;
 	value |= common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW ? 1 << 0 : 0;
 
-	if (pcdev->csi2_pdev) /* CSI2 mode */
+	if (csi2_subdev(pcdev, icd)) /* CSI2 mode */
 		value |= 3 << 12;
 	else if (pcdev->is_16bit)
 		value |= 1 << 12;
@@ -1054,7 +1063,7 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int
 		return 0;
 	}
 
-	if (!pcdev->pdata || !pcdev->pdata->csi2) {
+	if (!csi2_subdev(pcdev, icd)) {
 		/* Are there any restrictions in the CSI-2 case? */
 		ret = sh_mobile_ceu_try_bus_param(icd, fmt->bits_per_sample);
 		if (ret < 0)
@@ -2084,7 +2093,7 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev)
 	struct resource *res;
 	void __iomem *base;
 	unsigned int irq;
-	int err = 0;
+	int err, i;
 	struct bus_wait wait = {
 		.completion = COMPLETION_INITIALIZER_ONSTACK(wait.completion),
 		.notifier.notifier_call = bus_notify,
@@ -2188,31 +2197,60 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev)
 		goto exit_free_clk;
 	}
 
-	err = soc_camera_host_register(&pcdev->ici);
-	if (err)
-		goto exit_free_ctx;
+	if (pcdev->pdata && pcdev->pdata->asd_sizes) {
+		struct v4l2_async_subdev **asd;
+		char name[] = "sh-mobile-csi2";
+		int j;
+
+		/*
+		 * CSI2 interfacing: several groups can use CSI2, pick up the
+		 * first one
+		 */
+		asd = pcdev->pdata->asd;
+		for (j = 0; pcdev->pdata->asd_sizes[j]; j++) {
+			for (i = 0; i < pcdev->pdata->asd_sizes[j]; i++, asd++) {
+				dev_dbg(&pdev->dev, "%s(): subdev #%d, type %u\n",
+					__func__, i, (*asd)->bus_type);
+				if ((*asd)->bus_type == V4L2_ASYNC_BUS_PLATFORM &&
+				    !strncmp(name, (*asd)->match.platform.name,
+					     sizeof(name) - 1)) {
+					pcdev->csi2_asd = *asd;
+					break;
+				}
+			}
+			if (pcdev->csi2_asd)
+				break;
+		}
+
+		pcdev->ici.asd = pcdev->pdata->asd;
+		pcdev->ici.asd_sizes = pcdev->pdata->asd_sizes;
+	}
 
-	/* CSI2 interfacing */
+	/* Legacy CSI2 interfacing */
 	csi2 = pcdev->pdata ? pcdev->pdata->csi2 : NULL;
 	if (csi2) {
+		/*
+		 * TODO: remove this once all users are converted to
+		 * asynchronous CSI2 probing. If it has to be kept, csi2
+		 * platform device resources have to be added, using
+		 * platform_device_add_resources()
+		 */
 		struct platform_device *csi2_pdev =
 			platform_device_alloc("sh-mobile-csi2", csi2->id);
 		struct sh_csi2_pdata *csi2_pdata = csi2->platform_data;
 
 		if (!csi2_pdev) {
 			err = -ENOMEM;
-			goto exit_host_unregister;
+			goto exit_free_ctx;
 		}
 
 		pcdev->csi2_pdev		= csi2_pdev;
 
-		err = platform_device_add_data(csi2_pdev, csi2_pdata, sizeof(*csi2_pdata));
+		err = platform_device_add_data(csi2_pdev, csi2_pdata,
+					       sizeof(*csi2_pdata));
 		if (err < 0)
 			goto exit_pdev_put;
 
-		csi2_pdata			= csi2_pdev->dev.platform_data;
-		csi2_pdata->v4l2_dev		= &pcdev->ici.v4l2_dev;
-
 		csi2_pdev->resource		= csi2->resource;
 		csi2_pdev->num_resources	= csi2->num_resources;
 
@@ -2254,17 +2292,38 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev)
 			err = -ENODEV;
 			goto exit_pdev_unregister;
 		}
+
+		pcdev->csi2_sd = platform_get_drvdata(csi2_pdev);
+	}
+
+	err = soc_camera_host_register(&pcdev->ici);
+	if (err)
+		goto exit_csi2_unregister;
+
+	if (csi2) {
+		err = v4l2_device_register_subdev(&pcdev->ici.v4l2_dev,
+						  pcdev->csi2_sd);
+		dev_dbg(&pdev->dev, "%s(): ret(register_subdev) = %d\n",
+			__func__, err);
+		if (err < 0)
+			goto exit_host_unregister;
+		/* v4l2_device_register_subdev() took a reference too */
+		module_put(pcdev->csi2_sd->owner);
 	}
 
 	return 0;
 
-exit_pdev_unregister:
-	platform_device_del(pcdev->csi2_pdev);
-exit_pdev_put:
-	pcdev->csi2_pdev->resource = NULL;
-	platform_device_put(pcdev->csi2_pdev);
 exit_host_unregister:
 	soc_camera_host_unregister(&pcdev->ici);
+exit_csi2_unregister:
+	if (csi2) {
+		module_put(pcdev->csi2_pdev->dev.driver->owner);
+exit_pdev_unregister:
+		platform_device_del(pcdev->csi2_pdev);
+exit_pdev_put:
+		pcdev->csi2_pdev->resource = NULL;
+		platform_device_put(pcdev->csi2_pdev);
+	}
 exit_free_ctx:
 	vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
 exit_free_clk:
@@ -2324,6 +2383,7 @@ MODULE_DEVICE_TABLE(of, sh_mobile_ceu_of_match);
 static struct platform_driver sh_mobile_ceu_driver = {
 	.driver		= {
 		.name	= "sh_mobile_ceu",
+		.owner	= THIS_MODULE,
 		.pm	= &sh_mobile_ceu_dev_pm_ops,
 		.of_match_table = sh_mobile_ceu_of_match,
 	},
@@ -2349,5 +2409,5 @@ module_exit(sh_mobile_ceu_exit);
 MODULE_DESCRIPTION("SuperH Mobile CEU driver");
 MODULE_AUTHOR("Magnus Damm");
 MODULE_LICENSE("GPL");
-MODULE_VERSION("0.0.6");
+MODULE_VERSION("0.1.0");
 MODULE_ALIAS("platform:sh_mobile_ceu");
diff --git a/drivers/media/platform/soc_camera/sh_mobile_csi2.c b/drivers/media/platform/soc_camera/sh_mobile_csi2.c
index 13a1f8f..05dd21a 100644
--- a/drivers/media/platform/soc_camera/sh_mobile_csi2.c
+++ b/drivers/media/platform/soc_camera/sh_mobile_csi2.c
@@ -36,7 +36,6 @@
 
 struct sh_csi2 {
 	struct v4l2_subdev		subdev;
-	struct list_head		list;
 	unsigned int			irq;
 	unsigned long			mipi_flags;
 	void __iomem			*base;
@@ -44,6 +43,8 @@ struct sh_csi2 {
 	struct sh_csi2_client_config	*client;
 };
 
+static void sh_csi2_hwinit(struct sh_csi2 *priv);
+
 static int sh_csi2_try_fmt(struct v4l2_subdev *sd,
 			   struct v4l2_mbus_framefmt *mf)
 {
@@ -132,10 +133,58 @@ static int sh_csi2_s_fmt(struct v4l2_subdev *sd,
 static int sh_csi2_g_mbus_config(struct v4l2_subdev *sd,
 				 struct v4l2_mbus_config *cfg)
 {
-	cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING |
-		V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH |
-		V4L2_MBUS_MASTER | V4L2_MBUS_DATA_ACTIVE_HIGH;
-	cfg->type = V4L2_MBUS_PARALLEL;
+	struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev);
+
+	if (!priv->mipi_flags) {
+		struct soc_camera_device *icd = v4l2_get_subdev_hostdata(sd);
+		struct v4l2_subdev *client_sd = soc_camera_to_subdev(icd);
+		struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data;
+		unsigned long common_flags, csi2_flags;
+		struct v4l2_mbus_config client_cfg = {.type = V4L2_MBUS_CSI2,};
+		int ret;
+
+		/* Check if we can support this camera */
+		csi2_flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK |
+			V4L2_MBUS_CSI2_1_LANE;
+
+		switch (pdata->type) {
+		case SH_CSI2C:
+			if (priv->client->lanes != 1)
+				csi2_flags |= V4L2_MBUS_CSI2_2_LANE;
+			break;
+		case SH_CSI2I:
+			switch (priv->client->lanes) {
+			default:
+				csi2_flags |= V4L2_MBUS_CSI2_4_LANE;
+			case 3:
+				csi2_flags |= V4L2_MBUS_CSI2_3_LANE;
+			case 2:
+				csi2_flags |= V4L2_MBUS_CSI2_2_LANE;
+			}
+		}
+
+		ret = v4l2_subdev_call(client_sd, video, g_mbus_config, &client_cfg);
+		if (ret == -ENOIOCTLCMD)
+			common_flags = csi2_flags;
+		else if (!ret)
+			common_flags = soc_mbus_config_compatible(&client_cfg,
+								  csi2_flags);
+		else
+			common_flags = 0;
+
+		if (!common_flags)
+			return -EINVAL;
+
+		/* All good: camera MIPI configuration supported */
+		priv->mipi_flags = common_flags;
+	}
+
+	if (cfg) {
+		cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING |
+			V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH |
+			V4L2_MBUS_MASTER | V4L2_MBUS_DATA_ACTIVE_HIGH;
+		cfg->type = V4L2_MBUS_PARALLEL;
+	}
 
 	return 0;
 }
@@ -146,8 +195,17 @@ static int sh_csi2_s_mbus_config(struct v4l2_subdev *sd,
 	struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev);
 	struct soc_camera_device *icd = v4l2_get_subdev_hostdata(sd);
 	struct v4l2_subdev *client_sd = soc_camera_to_subdev(icd);
-	struct v4l2_mbus_config client_cfg = {.type = V4L2_MBUS_CSI2,
-					      .flags = priv->mipi_flags};
+	struct v4l2_mbus_config client_cfg = {.type = V4L2_MBUS_CSI2,};
+	int ret = sh_csi2_g_mbus_config(sd, NULL);
+
+	if (ret < 0)
+		return ret;
+
+	pm_runtime_get_sync(&priv->pdev->dev);
+
+	sh_csi2_hwinit(priv);
+
+	client_cfg.flags = priv->mipi_flags;
 
 	return v4l2_subdev_call(client_sd, video, s_mbus_config, &client_cfg);
 }
@@ -202,19 +260,19 @@ static void sh_csi2_hwinit(struct sh_csi2 *priv)
 
 static int sh_csi2_client_connect(struct sh_csi2 *priv)
 {
-	struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data;
-	struct soc_camera_device *icd = v4l2_get_subdev_hostdata(&priv->subdev);
-	struct v4l2_subdev *client_sd = soc_camera_to_subdev(icd);
 	struct device *dev = v4l2_get_subdevdata(&priv->subdev);
-	struct v4l2_mbus_config cfg;
-	unsigned long common_flags, csi2_flags;
-	int i, ret;
+	struct sh_csi2_pdata *pdata = dev->platform_data;
+	struct soc_camera_device *icd = v4l2_get_subdev_hostdata(&priv->subdev);
+	int i;
 
 	if (priv->client)
 		return -EBUSY;
 
 	for (i = 0; i < pdata->num_clients; i++)
-		if (&pdata->clients[i].pdev->dev == icd->pdev)
+		if ((pdata->clients[i].pdev &&
+		     &pdata->clients[i].pdev->dev == icd->pdev) ||
+		    (icd->control &&
+		     strcmp(pdata->clients[i].name, dev_name(icd->control))))
 			break;
 
 	dev_dbg(dev, "%s(%p): found #%d\n", __func__, dev, i);
@@ -222,46 +280,8 @@ static int sh_csi2_client_connect(struct sh_csi2 *priv)
 	if (i == pdata->num_clients)
 		return -ENODEV;
 
-	/* Check if we can support this camera */
-	csi2_flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK | V4L2_MBUS_CSI2_1_LANE;
-
-	switch (pdata->type) {
-	case SH_CSI2C:
-		if (pdata->clients[i].lanes != 1)
-			csi2_flags |= V4L2_MBUS_CSI2_2_LANE;
-		break;
-	case SH_CSI2I:
-		switch (pdata->clients[i].lanes) {
-		default:
-			csi2_flags |= V4L2_MBUS_CSI2_4_LANE;
-		case 3:
-			csi2_flags |= V4L2_MBUS_CSI2_3_LANE;
-		case 2:
-			csi2_flags |= V4L2_MBUS_CSI2_2_LANE;
-		}
-	}
-
-	cfg.type = V4L2_MBUS_CSI2;
-	ret = v4l2_subdev_call(client_sd, video, g_mbus_config, &cfg);
-	if (ret == -ENOIOCTLCMD)
-		common_flags = csi2_flags;
-	else if (!ret)
-		common_flags = soc_mbus_config_compatible(&cfg,
-							  csi2_flags);
-	else
-		common_flags = 0;
-
-	if (!common_flags)
-		return -EINVAL;
-
-	/* All good: camera MIPI configuration supported */
-	priv->mipi_flags = common_flags;
 	priv->client = pdata->clients + i;
 
-	pm_runtime_get_sync(dev);
-
-	sh_csi2_hwinit(priv);
-
 	return 0;
 }
 
@@ -304,11 +324,18 @@ static int sh_csi2_probe(struct platform_device *pdev)
 	/* Platform data specify the PHY, lanes, ECC, CRC */
 	struct sh_csi2_pdata *pdata = pdev->dev.platform_data;
 
+	if (!pdata)
+		return -EINVAL;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(struct sh_csi2), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	/* Interrupt unused so far */
 	irq = platform_get_irq(pdev, 0);
 
-	if (!res || (int)irq <= 0 || !pdata) {
+	if (!res || (int)irq <= 0) {
 		dev_err(&pdev->dev, "Not enough CSI2 platform resources.\n");
 		return -ENODEV;
 	}
@@ -319,10 +346,6 @@ static int sh_csi2_probe(struct platform_device *pdev)
 		return -EINVAL;
 	}
 
-	priv = devm_kzalloc(&pdev->dev, sizeof(struct sh_csi2), GFP_KERNEL);
-	if (!priv)
-		return -ENOMEM;
-
 	priv->irq = irq;
 
 	priv->base = devm_ioremap_resource(&pdev->dev, res);
@@ -330,15 +353,17 @@ static int sh_csi2_probe(struct platform_device *pdev)
 		return PTR_ERR(priv->base);
 
 	priv->pdev = pdev;
-	platform_set_drvdata(pdev, priv);
+	priv->subdev.owner = THIS_MODULE;
+	priv->subdev.dev = &pdev->dev;
+	platform_set_drvdata(pdev, &priv->subdev);
 
 	v4l2_subdev_init(&priv->subdev, &sh_csi2_subdev_ops);
 	v4l2_set_subdevdata(&priv->subdev, &pdev->dev);
 
 	snprintf(priv->subdev.name, V4L2_SUBDEV_NAME_SIZE, "%s.mipi-csi",
-		 dev_name(pdata->v4l2_dev->dev));
-	ret = v4l2_device_register_subdev(pdata->v4l2_dev, &priv->subdev);
-	dev_dbg(&pdev->dev, "%s(%p): ret(register_subdev) = %d\n", __func__, priv, ret);
+		 dev_name(&pdev->dev));
+
+	ret = v4l2_async_register_subdev(&priv->subdev);
 	if (ret < 0)
 		return ret;
 
@@ -351,9 +376,11 @@ static int sh_csi2_probe(struct platform_device *pdev)
 
 static int sh_csi2_remove(struct platform_device *pdev)
 {
-	struct sh_csi2 *priv = platform_get_drvdata(pdev);
+	struct v4l2_subdev *subdev = platform_get_drvdata(pdev);
+	struct sh_csi2 *priv = container_of(subdev, struct sh_csi2, subdev);
 
-	v4l2_device_unregister_subdev(&priv->subdev);
+	v4l2_async_unregister_subdev(&priv->subdev);
+	v4l2_device_unregister_subdev(subdev);
 	pm_runtime_disable(&pdev->dev);
 
 	return 0;
diff --git a/include/media/sh_mobile_ceu.h b/include/media/sh_mobile_ceu.h
index 6fdb6ad..8937241 100644
--- a/include/media/sh_mobile_ceu.h
+++ b/include/media/sh_mobile_ceu.h
@@ -22,6 +22,8 @@ struct sh_mobile_ceu_info {
 	int max_width;
 	int max_height;
 	struct sh_mobile_ceu_companion *csi2;
+	struct v4l2_async_subdev **asd;	/* Flat array, arranged in groups */
+	int *asd_sizes;			/* 0-terminated array pf asd group sizes */
 };
 
 #endif /* __ASM_SH_MOBILE_CEU_H__ */
diff --git a/include/media/sh_mobile_csi2.h b/include/media/sh_mobile_csi2.h
index c586c4f..14030db 100644
--- a/include/media/sh_mobile_csi2.h
+++ b/include/media/sh_mobile_csi2.h
@@ -33,6 +33,7 @@ struct sh_csi2_client_config {
 	unsigned char lanes;		/* bitmask[3:0] */
 	unsigned char channel;		/* 0..3 */
 	struct platform_device *pdev;	/* client platform device */
+	const char *name;		/* async matching: client name */
 };
 
 struct v4l2_device;
@@ -42,7 +43,6 @@ struct sh_csi2_pdata {
 	unsigned int flags;
 	struct sh_csi2_client_config *clients;
 	int num_clients;
-	struct v4l2_device *v4l2_dev;
 };
 
 #endif
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v10 20/21] imx074: support asynchronous probing
  2013-06-11  8:23 ` Guennadi Liakhovetski
@ 2013-06-11  8:23   ` Guennadi Liakhovetski
  -1 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-11  8:23 UTC (permalink / raw)
  To: linux-media
  Cc: Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer,
	Guennadi Liakhovetski

Both synchronous and asynchronous imx074 subdevice probing is supported by
this patch.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
 drivers/media/i2c/soc_camera/imx074.c |   20 +++++++++++++++++---
 1 files changed, 17 insertions(+), 3 deletions(-)

diff --git a/drivers/media/i2c/soc_camera/imx074.c b/drivers/media/i2c/soc_camera/imx074.c
index a6a5060..84245e1 100644
--- a/drivers/media/i2c/soc_camera/imx074.c
+++ b/drivers/media/i2c/soc_camera/imx074.c
@@ -18,6 +18,7 @@
 #include <linux/module.h>
 
 #include <media/soc_camera.h>
+#include <media/v4l2-async.h>
 #include <media/v4l2-clk.h>
 #include <media/v4l2-subdev.h>
 #include <media/v4l2-chip-ident.h>
@@ -456,13 +457,24 @@ static int imx074_probe(struct i2c_client *client,
 	priv->fmt	= &imx074_colour_fmts[0];
 
 	priv->clk = v4l2_clk_get(&client->dev, "mclk");
-	if (IS_ERR(priv->clk))
-		return PTR_ERR(priv->clk);
+	if (IS_ERR(priv->clk)) {
+		dev_info(&client->dev, "Error %ld getting clock\n", PTR_ERR(priv->clk));
+		return -EPROBE_DEFER;
+	}
+
+	ret = soc_camera_power_init(&client->dev, ssdd);
+	if (ret < 0)
+		goto epwrinit;
 
 	ret = imx074_video_probe(client);
 	if (ret < 0)
-		v4l2_clk_put(priv->clk);
+		goto eprobe;
 
+	return v4l2_async_register_subdev(&priv->subdev);
+
+epwrinit:
+eprobe:
+	v4l2_clk_put(priv->clk);
 	return ret;
 }
 
@@ -471,7 +483,9 @@ static int imx074_remove(struct i2c_client *client)
 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
 	struct imx074 *priv = to_imx074(client);
 
+	v4l2_async_unregister_subdev(&priv->subdev);
 	v4l2_clk_put(priv->clk);
+
 	if (ssdd->free_bus)
 		ssdd->free_bus(ssdd);
 
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v10 20/21] imx074: support asynchronous probing
@ 2013-06-11  8:23   ` Guennadi Liakhovetski
  0 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-11  8:23 UTC (permalink / raw)
  To: linux-media
  Cc: Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer,
	Guennadi Liakhovetski

Both synchronous and asynchronous imx074 subdevice probing is supported by
this patch.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
 drivers/media/i2c/soc_camera/imx074.c |   20 +++++++++++++++++---
 1 files changed, 17 insertions(+), 3 deletions(-)

diff --git a/drivers/media/i2c/soc_camera/imx074.c b/drivers/media/i2c/soc_camera/imx074.c
index a6a5060..84245e1 100644
--- a/drivers/media/i2c/soc_camera/imx074.c
+++ b/drivers/media/i2c/soc_camera/imx074.c
@@ -18,6 +18,7 @@
 #include <linux/module.h>
 
 #include <media/soc_camera.h>
+#include <media/v4l2-async.h>
 #include <media/v4l2-clk.h>
 #include <media/v4l2-subdev.h>
 #include <media/v4l2-chip-ident.h>
@@ -456,13 +457,24 @@ static int imx074_probe(struct i2c_client *client,
 	priv->fmt	= &imx074_colour_fmts[0];
 
 	priv->clk = v4l2_clk_get(&client->dev, "mclk");
-	if (IS_ERR(priv->clk))
-		return PTR_ERR(priv->clk);
+	if (IS_ERR(priv->clk)) {
+		dev_info(&client->dev, "Error %ld getting clock\n", PTR_ERR(priv->clk));
+		return -EPROBE_DEFER;
+	}
+
+	ret = soc_camera_power_init(&client->dev, ssdd);
+	if (ret < 0)
+		goto epwrinit;
 
 	ret = imx074_video_probe(client);
 	if (ret < 0)
-		v4l2_clk_put(priv->clk);
+		goto eprobe;
 
+	return v4l2_async_register_subdev(&priv->subdev);
+
+epwrinit:
+eprobe:
+	v4l2_clk_put(priv->clk);
 	return ret;
 }
 
@@ -471,7 +483,9 @@ static int imx074_remove(struct i2c_client *client)
 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
 	struct imx074 *priv = to_imx074(client);
 
+	v4l2_async_unregister_subdev(&priv->subdev);
 	v4l2_clk_put(priv->clk);
+
 	if (ssdd->free_bus)
 		ssdd->free_bus(ssdd);
 
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v10 21/21] ARM: shmobile: convert ap4evb to asynchronously register camera subdevices
  2013-06-11  8:23 ` Guennadi Liakhovetski
@ 2013-06-11  8:23   ` Guennadi Liakhovetski
  -1 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-11  8:23 UTC (permalink / raw)
  To: linux-media
  Cc: Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer,
	Guennadi Liakhovetski

Register the imx074 camera I2C and the CSI-2 platform devices directly
in board platform data instead of letting the sh_mobile_ceu_camera driver
and the soc-camera framework register them at their run-time. This uses
the V4L2 asynchronous subdevice probing capability.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
 arch/arm/mach-shmobile/board-ap4evb.c |  103 +++++++++++++++++++-------------
 arch/arm/mach-shmobile/clock-sh7372.c |    1 +
 2 files changed, 62 insertions(+), 42 deletions(-)

diff --git a/arch/arm/mach-shmobile/board-ap4evb.c b/arch/arm/mach-shmobile/board-ap4evb.c
index 45f78ca..f0beec0 100644
--- a/arch/arm/mach-shmobile/board-ap4evb.c
+++ b/arch/arm/mach-shmobile/board-ap4evb.c
@@ -51,6 +51,7 @@
 #include <media/sh_mobile_ceu.h>
 #include <media/sh_mobile_csi2.h>
 #include <media/soc_camera.h>
+#include <media/v4l2-async.h>
 
 #include <sound/sh_fsi.h>
 #include <sound/simple_card.h>
@@ -872,22 +873,32 @@ static struct platform_device leds_device = {
 	},
 };
 
-static struct i2c_board_info imx074_info = {
-	I2C_BOARD_INFO("imx074", 0x1a),
+/* I2C */
+static struct soc_camera_subdev_desc imx074_desc;
+static struct i2c_board_info i2c0_devices[] = {
+	{
+		I2C_BOARD_INFO("ak4643", 0x13),
+	}, {
+		I2C_BOARD_INFO("imx074", 0x1a),
+		.platform_data = &imx074_desc,
+	},
 };
 
-static struct soc_camera_link imx074_link = {
-	.bus_id		= 0,
-	.board_info	= &imx074_info,
-	.i2c_adapter_id	= 0,
-	.module_name	= "imx074",
+static struct i2c_board_info i2c1_devices[] = {
+	{
+		I2C_BOARD_INFO("r2025sd", 0x32),
+	},
 };
 
-static struct platform_device ap4evb_camera = {
-	.name   = "soc-camera-pdrv",
-	.id     = 0,
-	.dev    = {
-		.platform_data = &imx074_link,
+static struct resource csi2_resources[] = {
+	{
+		.name	= "CSI2",
+		.start	= 0xffc90000,
+		.end	= 0xffc90fff,
+		.flags	= IORESOURCE_MEM,
+	}, {
+		.start	= intcs_evt2irq(0x17a0),
+		.flags  = IORESOURCE_IRQ,
 	},
 };
 
@@ -896,7 +907,7 @@ static struct sh_csi2_client_config csi2_clients[] = {
 		.phy		= SH_CSI2_PHY_MAIN,
 		.lanes		= 0,		/* default: 2 lanes */
 		.channel	= 0,
-		.pdev		= &ap4evb_camera,
+		.name		= "imx074",
 	},
 };
 
@@ -907,31 +918,50 @@ static struct sh_csi2_pdata csi2_info = {
 	.flags		= SH_CSI2_ECC | SH_CSI2_CRC,
 };
 
-static struct resource csi2_resources[] = {
-	[0] = {
-		.name	= "CSI2",
-		.start	= 0xffc90000,
-		.end	= 0xffc90fff,
-		.flags	= IORESOURCE_MEM,
+static struct platform_device csi2_device = {
+	.name		= "sh-mobile-csi2",
+	.id		= 0,
+	.num_resources	= ARRAY_SIZE(csi2_resources),
+	.resource	= csi2_resources,
+	.dev		= {
+		.platform_data = &csi2_info,
 	},
-	[1] = {
-		.start	= intcs_evt2irq(0x17a0),
-		.flags  = IORESOURCE_IRQ,
+};
+
+static struct soc_camera_async_subdev csi2_sd = {
+	.asd = {
+		.bus_type = V4L2_ASYNC_BUS_PLATFORM,
+		.match.platform.name = "sh-mobile-csi2.0",
 	},
+	.role = SOCAM_SUBDEV_DATA_PROCESSOR,
 };
 
-static struct sh_mobile_ceu_companion csi2 = {
-	.id		= 0,
-	.num_resources	= ARRAY_SIZE(csi2_resources),
-	.resource	= csi2_resources,
-	.platform_data	= &csi2_info,
+static struct soc_camera_async_subdev imx074_sd = {
+	.asd = {
+		.bus_type = V4L2_ASYNC_BUS_I2C,
+		.match.i2c = {
+			.adapter_id = 0,
+			.address = 0x1a,
+		},
+	},
+	.role = SOCAM_SUBDEV_DATA_SOURCE,
 };
 
+static struct v4l2_async_subdev *ceu_subdevs[] = {
+	/* Single 2-element group */
+	&csi2_sd.asd,
+	&imx074_sd.asd,
+};
+
+/* 0-terminated array of group-sizes */
+static int ceu_subdev_sizes[] = {ARRAY_SIZE(ceu_subdevs), 0};
+
 static struct sh_mobile_ceu_info sh_mobile_ceu_info = {
 	.flags = SH_CEU_FLAG_USE_8BIT_BUS,
 	.max_width = 8188,
 	.max_height = 8188,
-	.csi2 = &csi2,
+	.asd = ceu_subdevs,
+	.asd_sizes = ceu_subdev_sizes,
 };
 
 static struct resource ceu_resources[] = {
@@ -976,7 +1006,7 @@ static struct platform_device *ap4evb_devices[] __initdata = {
 	&lcdc_device,
 	&lcdc1_device,
 	&ceu_device,
-	&ap4evb_camera,
+	&csi2_device,
 	&meram_device,
 };
 
@@ -1071,19 +1101,6 @@ static struct i2c_board_info tsc_device = {
 	/*.irq is selected on ap4evb_init */
 };
 
-/* I2C */
-static struct i2c_board_info i2c0_devices[] = {
-	{
-		I2C_BOARD_INFO("ak4643", 0x13),
-	},
-};
-
-static struct i2c_board_info i2c1_devices[] = {
-	{
-		I2C_BOARD_INFO("r2025sd", 0x32),
-	},
-};
-
 
 static const struct pinctrl_map ap4evb_pinctrl_map[] = {
 	/* MMCIF */
@@ -1120,6 +1137,7 @@ static void __init ap4evb_init(void)
 		{ "A3SP", &sdhi0_device, },
 		{ "A3SP", &sdhi1_device, },
 		{ "A4R", &ceu_device, },
+		{ "A4R", &csi2_device, },
 	};
 	u32 srcr4;
 	struct clk *clk;
@@ -1319,6 +1337,7 @@ static void __init ap4evb_init(void)
 	sh7372_pm_init();
 	pm_clk_add(&fsi_device.dev, "spu2");
 	pm_clk_add(&lcdc1_device.dev, "hdmi");
+	pm_clk_add(&csi2_device.dev, "csir");
 }
 
 MACHINE_START(AP4EVB, "ap4evb")
diff --git a/arch/arm/mach-shmobile/clock-sh7372.c b/arch/arm/mach-shmobile/clock-sh7372.c
index 7e10593..0d6dcd3 100644
--- a/arch/arm/mach-shmobile/clock-sh7372.c
+++ b/arch/arm/mach-shmobile/clock-sh7372.c
@@ -584,6 +584,7 @@ static struct clk_lookup lookups[] = {
 	CLKDEV_ICK_ID("divb", "sh_fsi2", &fsidivs[FSIDIV_B]),
 	CLKDEV_ICK_ID("xcka", "sh_fsi2", &fsiack_clk),
 	CLKDEV_ICK_ID("xckb", "sh_fsi2", &fsibck_clk),
+	CLKDEV_ICK_ID("csir", "sh-mobile-csi2.0", &div4_clks[DIV4_CSIR]),
 };
 
 void __init sh7372_clock_init(void)
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v10 21/21] ARM: shmobile: convert ap4evb to asynchronously register camera subdevices
@ 2013-06-11  8:23   ` Guennadi Liakhovetski
  0 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-11  8:23 UTC (permalink / raw)
  To: linux-media
  Cc: Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer,
	Guennadi Liakhovetski

Register the imx074 camera I2C and the CSI-2 platform devices directly
in board platform data instead of letting the sh_mobile_ceu_camera driver
and the soc-camera framework register them at their run-time. This uses
the V4L2 asynchronous subdevice probing capability.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
 arch/arm/mach-shmobile/board-ap4evb.c |  103 +++++++++++++++++++-------------
 arch/arm/mach-shmobile/clock-sh7372.c |    1 +
 2 files changed, 62 insertions(+), 42 deletions(-)

diff --git a/arch/arm/mach-shmobile/board-ap4evb.c b/arch/arm/mach-shmobile/board-ap4evb.c
index 45f78ca..f0beec0 100644
--- a/arch/arm/mach-shmobile/board-ap4evb.c
+++ b/arch/arm/mach-shmobile/board-ap4evb.c
@@ -51,6 +51,7 @@
 #include <media/sh_mobile_ceu.h>
 #include <media/sh_mobile_csi2.h>
 #include <media/soc_camera.h>
+#include <media/v4l2-async.h>
 
 #include <sound/sh_fsi.h>
 #include <sound/simple_card.h>
@@ -872,22 +873,32 @@ static struct platform_device leds_device = {
 	},
 };
 
-static struct i2c_board_info imx074_info = {
-	I2C_BOARD_INFO("imx074", 0x1a),
+/* I2C */
+static struct soc_camera_subdev_desc imx074_desc;
+static struct i2c_board_info i2c0_devices[] = {
+	{
+		I2C_BOARD_INFO("ak4643", 0x13),
+	}, {
+		I2C_BOARD_INFO("imx074", 0x1a),
+		.platform_data = &imx074_desc,
+	},
 };
 
-static struct soc_camera_link imx074_link = {
-	.bus_id		= 0,
-	.board_info	= &imx074_info,
-	.i2c_adapter_id	= 0,
-	.module_name	= "imx074",
+static struct i2c_board_info i2c1_devices[] = {
+	{
+		I2C_BOARD_INFO("r2025sd", 0x32),
+	},
 };
 
-static struct platform_device ap4evb_camera = {
-	.name   = "soc-camera-pdrv",
-	.id     = 0,
-	.dev    = {
-		.platform_data = &imx074_link,
+static struct resource csi2_resources[] = {
+	{
+		.name	= "CSI2",
+		.start	= 0xffc90000,
+		.end	= 0xffc90fff,
+		.flags	= IORESOURCE_MEM,
+	}, {
+		.start	= intcs_evt2irq(0x17a0),
+		.flags  = IORESOURCE_IRQ,
 	},
 };
 
@@ -896,7 +907,7 @@ static struct sh_csi2_client_config csi2_clients[] = {
 		.phy		= SH_CSI2_PHY_MAIN,
 		.lanes		= 0,		/* default: 2 lanes */
 		.channel	= 0,
-		.pdev		= &ap4evb_camera,
+		.name		= "imx074",
 	},
 };
 
@@ -907,31 +918,50 @@ static struct sh_csi2_pdata csi2_info = {
 	.flags		= SH_CSI2_ECC | SH_CSI2_CRC,
 };
 
-static struct resource csi2_resources[] = {
-	[0] = {
-		.name	= "CSI2",
-		.start	= 0xffc90000,
-		.end	= 0xffc90fff,
-		.flags	= IORESOURCE_MEM,
+static struct platform_device csi2_device = {
+	.name		= "sh-mobile-csi2",
+	.id		= 0,
+	.num_resources	= ARRAY_SIZE(csi2_resources),
+	.resource	= csi2_resources,
+	.dev		= {
+		.platform_data = &csi2_info,
 	},
-	[1] = {
-		.start	= intcs_evt2irq(0x17a0),
-		.flags  = IORESOURCE_IRQ,
+};
+
+static struct soc_camera_async_subdev csi2_sd = {
+	.asd = {
+		.bus_type = V4L2_ASYNC_BUS_PLATFORM,
+		.match.platform.name = "sh-mobile-csi2.0",
 	},
+	.role = SOCAM_SUBDEV_DATA_PROCESSOR,
 };
 
-static struct sh_mobile_ceu_companion csi2 = {
-	.id		= 0,
-	.num_resources	= ARRAY_SIZE(csi2_resources),
-	.resource	= csi2_resources,
-	.platform_data	= &csi2_info,
+static struct soc_camera_async_subdev imx074_sd = {
+	.asd = {
+		.bus_type = V4L2_ASYNC_BUS_I2C,
+		.match.i2c = {
+			.adapter_id = 0,
+			.address = 0x1a,
+		},
+	},
+	.role = SOCAM_SUBDEV_DATA_SOURCE,
 };
 
+static struct v4l2_async_subdev *ceu_subdevs[] = {
+	/* Single 2-element group */
+	&csi2_sd.asd,
+	&imx074_sd.asd,
+};
+
+/* 0-terminated array of group-sizes */
+static int ceu_subdev_sizes[] = {ARRAY_SIZE(ceu_subdevs), 0};
+
 static struct sh_mobile_ceu_info sh_mobile_ceu_info = {
 	.flags = SH_CEU_FLAG_USE_8BIT_BUS,
 	.max_width = 8188,
 	.max_height = 8188,
-	.csi2 = &csi2,
+	.asd = ceu_subdevs,
+	.asd_sizes = ceu_subdev_sizes,
 };
 
 static struct resource ceu_resources[] = {
@@ -976,7 +1006,7 @@ static struct platform_device *ap4evb_devices[] __initdata = {
 	&lcdc_device,
 	&lcdc1_device,
 	&ceu_device,
-	&ap4evb_camera,
+	&csi2_device,
 	&meram_device,
 };
 
@@ -1071,19 +1101,6 @@ static struct i2c_board_info tsc_device = {
 	/*.irq is selected on ap4evb_init */
 };
 
-/* I2C */
-static struct i2c_board_info i2c0_devices[] = {
-	{
-		I2C_BOARD_INFO("ak4643", 0x13),
-	},
-};
-
-static struct i2c_board_info i2c1_devices[] = {
-	{
-		I2C_BOARD_INFO("r2025sd", 0x32),
-	},
-};
-
 
 static const struct pinctrl_map ap4evb_pinctrl_map[] = {
 	/* MMCIF */
@@ -1120,6 +1137,7 @@ static void __init ap4evb_init(void)
 		{ "A3SP", &sdhi0_device, },
 		{ "A3SP", &sdhi1_device, },
 		{ "A4R", &ceu_device, },
+		{ "A4R", &csi2_device, },
 	};
 	u32 srcr4;
 	struct clk *clk;
@@ -1319,6 +1337,7 @@ static void __init ap4evb_init(void)
 	sh7372_pm_init();
 	pm_clk_add(&fsi_device.dev, "spu2");
 	pm_clk_add(&lcdc1_device.dev, "hdmi");
+	pm_clk_add(&csi2_device.dev, "csir");
 }
 
 MACHINE_START(AP4EVB, "ap4evb")
diff --git a/arch/arm/mach-shmobile/clock-sh7372.c b/arch/arm/mach-shmobile/clock-sh7372.c
index 7e10593..0d6dcd3 100644
--- a/arch/arm/mach-shmobile/clock-sh7372.c
+++ b/arch/arm/mach-shmobile/clock-sh7372.c
@@ -584,6 +584,7 @@ static struct clk_lookup lookups[] = {
 	CLKDEV_ICK_ID("divb", "sh_fsi2", &fsidivs[FSIDIV_B]),
 	CLKDEV_ICK_ID("xcka", "sh_fsi2", &fsiack_clk),
 	CLKDEV_ICK_ID("xckb", "sh_fsi2", &fsibck_clk),
+	CLKDEV_ICK_ID("csir", "sh-mobile-csi2.0", &div4_clks[DIV4_CSIR]),
 };
 
 void __init sh7372_clock_init(void)
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* Re: [PATCH v10 15/21] V4L2: add a device pointer to struct v4l2_subdev
  2013-06-11  8:23   ` Guennadi Liakhovetski
@ 2013-06-12 17:28     ` Prabhakar Lad
  -1 siblings, 0 replies; 68+ messages in thread
From: Prabhakar Lad @ 2013-06-12 17:16 UTC (permalink / raw)
  To: Guennadi Liakhovetski
  Cc: linux-media, Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil,
	linux-sh, Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer

On Tue, Jun 11, 2013 at 1:53 PM, Guennadi Liakhovetski
<g.liakhovetski@gmx.de> wrote:
> It is often useful to have simple means to get from a subdevice to the
> underlying physical device. This patch adds such a pointer to struct
> v4l2_subdev and sets it accordingly in the I2C and SPI cases.
>
> Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>

Acked-by: Lad, Prabhakar <prabhakar.csengg@gmail.com>

Regards,
--Prabhakar Lad

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v10 16/21] V4L2: support asynchronous subdevice registration
  2013-06-11  8:23   ` Guennadi Liakhovetski
@ 2013-06-12 17:30     ` Prabhakar Lad
  -1 siblings, 0 replies; 68+ messages in thread
From: Prabhakar Lad @ 2013-06-12 17:18 UTC (permalink / raw)
  To: Guennadi Liakhovetski
  Cc: linux-media, Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil,
	linux-sh, Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer

Hi Guennadi,

Thanks for the patch. Hopefully we get this in for 3.11

On Tue, Jun 11, 2013 at 1:53 PM, Guennadi Liakhovetski
<g.liakhovetski@gmx.de> wrote:
> Currently bridge device drivers register devices for all subdevices
> synchronously, tupically, during their probing. E.g. if an I2C CMOS sensor
> is attached to a video bridge device, the bridge driver will create an I2C
> device and wait for the respective I2C driver to probe. This makes linking
> of devices straight forward, but this approach cannot be used with
> intrinsically asynchronous and unordered device registration systems like
> the Flattened Device Tree. To support such systems this patch adds an
> asynchronous subdevice registration framework to V4L2. To use it respective
> (e.g. I2C) subdevice drivers must register themselves with the framework.
> A bridge driver on the other hand must register notification callbacks,
> that will be called upon various related events.
>
> Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>

Acked-by: Lad, Prabhakar <prabhakar.csengg@gmail.com>
Tested-by: Lad, Prabhakar <prabhakar.csengg@gmail.com>

Regards,
--Prabhakar Lad

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v10 15/21] V4L2: add a device pointer to struct v4l2_subdev
@ 2013-06-12 17:28     ` Prabhakar Lad
  0 siblings, 0 replies; 68+ messages in thread
From: Prabhakar Lad @ 2013-06-12 17:28 UTC (permalink / raw)
  To: Guennadi Liakhovetski
  Cc: linux-media, Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil,
	linux-sh, Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer

On Tue, Jun 11, 2013 at 1:53 PM, Guennadi Liakhovetski
<g.liakhovetski@gmx.de> wrote:
> It is often useful to have simple means to get from a subdevice to the
> underlying physical device. This patch adds such a pointer to struct
> v4l2_subdev and sets it accordingly in the I2C and SPI cases.
>
> Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>

Acked-by: Lad, Prabhakar <prabhakar.csengg@gmail.com>

Regards,
--Prabhakar Lad

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v10 16/21] V4L2: support asynchronous subdevice registration
@ 2013-06-12 17:30     ` Prabhakar Lad
  0 siblings, 0 replies; 68+ messages in thread
From: Prabhakar Lad @ 2013-06-12 17:30 UTC (permalink / raw)
  To: Guennadi Liakhovetski
  Cc: linux-media, Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil,
	linux-sh, Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer

Hi Guennadi,

Thanks for the patch. Hopefully we get this in for 3.11

On Tue, Jun 11, 2013 at 1:53 PM, Guennadi Liakhovetski
<g.liakhovetski@gmx.de> wrote:
> Currently bridge device drivers register devices for all subdevices
> synchronously, tupically, during their probing. E.g. if an I2C CMOS sensor
> is attached to a video bridge device, the bridge driver will create an I2C
> device and wait for the respective I2C driver to probe. This makes linking
> of devices straight forward, but this approach cannot be used with
> intrinsically asynchronous and unordered device registration systems like
> the Flattened Device Tree. To support such systems this patch adds an
> asynchronous subdevice registration framework to V4L2. To use it respective
> (e.g. I2C) subdevice drivers must register themselves with the framework.
> A bridge driver on the other hand must register notification callbacks,
> that will be called upon various related events.
>
> Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>

Acked-by: Lad, Prabhakar <prabhakar.csengg@gmail.com>
Tested-by: Lad, Prabhakar <prabhakar.csengg@gmail.com>

Regards,
--Prabhakar Lad

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v10 15/21] V4L2: add a device pointer to struct v4l2_subdev
  2013-06-11  8:23   ` Guennadi Liakhovetski
@ 2013-06-13 10:32     ` Hans Verkuil
  -1 siblings, 0 replies; 68+ messages in thread
From: Hans Verkuil @ 2013-06-13 10:32 UTC (permalink / raw)
  To: Guennadi Liakhovetski
  Cc: linux-media, Laurent Pinchart, Sylwester Nawrocki, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer

On Tue 11 June 2013 10:23:42 Guennadi Liakhovetski wrote:
> It is often useful to have simple means to get from a subdevice to the
> underlying physical device. This patch adds such a pointer to struct
> v4l2_subdev and sets it accordingly in the I2C and SPI cases.
> 
> Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
> ---
>  drivers/media/v4l2-core/v4l2-common.c |    2 ++
>  include/media/v4l2-subdev.h           |    2 ++
>  2 files changed, 4 insertions(+), 0 deletions(-)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c
> index 3fed63f..accfec6 100644
> --- a/drivers/media/v4l2-core/v4l2-common.c
> +++ b/drivers/media/v4l2-core/v4l2-common.c
> @@ -291,6 +291,7 @@ void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client,
>  	sd->flags |= V4L2_SUBDEV_FL_IS_I2C;
>  	/* the owner is the same as the i2c_client's driver owner */
>  	sd->owner = client->driver->driver.owner;
> +	sd->dev = &client->dev;
>  	/* i2c_client and v4l2_subdev point to one another */
>  	v4l2_set_subdevdata(sd, client);
>  	i2c_set_clientdata(client, sd);
> @@ -426,6 +427,7 @@ void v4l2_spi_subdev_init(struct v4l2_subdev *sd, struct spi_device *spi,
>  	sd->flags |= V4L2_SUBDEV_FL_IS_SPI;
>  	/* the owner is the same as the spi_device's driver owner */
>  	sd->owner = spi->dev.driver->owner;
> +	sd->dev = &spi->dev;
>  	/* spi_device and v4l2_subdev point to one another */
>  	v4l2_set_subdevdata(sd, spi);
>  	spi_set_drvdata(spi, sd);
> diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h
> index 5298d67..d8756fa 100644
> --- a/include/media/v4l2-subdev.h
> +++ b/include/media/v4l2-subdev.h
> @@ -585,6 +585,8 @@ struct v4l2_subdev {
>  	void *host_priv;
>  	/* subdev device node */
>  	struct video_device *devnode;
> +	/* pointer to the physical device */

Can you change this comment to:

	/* pointer to the physical device, if any */

It's a small change, but this makes it clear that it's OK to leave it to
NULL for subdevs that don't have an underlying device struct.

Thanks!

	Hans

> +	struct device *dev;
>  };
>  
>  #define media_entity_to_v4l2_subdev(ent) \
> 

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v10 15/21] V4L2: add a device pointer to struct v4l2_subdev
@ 2013-06-13 10:32     ` Hans Verkuil
  0 siblings, 0 replies; 68+ messages in thread
From: Hans Verkuil @ 2013-06-13 10:32 UTC (permalink / raw)
  To: Guennadi Liakhovetski
  Cc: linux-media, Laurent Pinchart, Sylwester Nawrocki, linux-sh,
	Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer

On Tue 11 June 2013 10:23:42 Guennadi Liakhovetski wrote:
> It is often useful to have simple means to get from a subdevice to the
> underlying physical device. This patch adds such a pointer to struct
> v4l2_subdev and sets it accordingly in the I2C and SPI cases.
> 
> Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
> ---
>  drivers/media/v4l2-core/v4l2-common.c |    2 ++
>  include/media/v4l2-subdev.h           |    2 ++
>  2 files changed, 4 insertions(+), 0 deletions(-)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c
> index 3fed63f..accfec6 100644
> --- a/drivers/media/v4l2-core/v4l2-common.c
> +++ b/drivers/media/v4l2-core/v4l2-common.c
> @@ -291,6 +291,7 @@ void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client,
>  	sd->flags |= V4L2_SUBDEV_FL_IS_I2C;
>  	/* the owner is the same as the i2c_client's driver owner */
>  	sd->owner = client->driver->driver.owner;
> +	sd->dev = &client->dev;
>  	/* i2c_client and v4l2_subdev point to one another */
>  	v4l2_set_subdevdata(sd, client);
>  	i2c_set_clientdata(client, sd);
> @@ -426,6 +427,7 @@ void v4l2_spi_subdev_init(struct v4l2_subdev *sd, struct spi_device *spi,
>  	sd->flags |= V4L2_SUBDEV_FL_IS_SPI;
>  	/* the owner is the same as the spi_device's driver owner */
>  	sd->owner = spi->dev.driver->owner;
> +	sd->dev = &spi->dev;
>  	/* spi_device and v4l2_subdev point to one another */
>  	v4l2_set_subdevdata(sd, spi);
>  	spi_set_drvdata(spi, sd);
> diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h
> index 5298d67..d8756fa 100644
> --- a/include/media/v4l2-subdev.h
> +++ b/include/media/v4l2-subdev.h
> @@ -585,6 +585,8 @@ struct v4l2_subdev {
>  	void *host_priv;
>  	/* subdev device node */
>  	struct video_device *devnode;
> +	/* pointer to the physical device */

Can you change this comment to:

	/* pointer to the physical device, if any */

It's a small change, but this makes it clear that it's OK to leave it to
NULL for subdevs that don't have an underlying device struct.

Thanks!

	Hans

> +	struct device *dev;
>  };
>  
>  #define media_entity_to_v4l2_subdev(ent) \
> 

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v10 16/21] V4L2: support asynchronous subdevice registration
  2013-06-11  8:23   ` Guennadi Liakhovetski
@ 2013-06-13 21:37     ` Sylwester Nawrocki
  -1 siblings, 0 replies; 68+ messages in thread
From: Sylwester Nawrocki @ 2013-06-13 21:37 UTC (permalink / raw)
  To: Guennadi Liakhovetski
  Cc: linux-media, Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil,
	linux-sh, Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer

Hi Guennadi,

Overall it looks quite neat at this v10. :)

On 06/11/2013 10:23 AM, Guennadi Liakhovetski wrote:
> Currently bridge device drivers register devices for all subdevices
> synchronously, tupically, during their probing. E.g. if an I2C CMOS sensor

s/tupically/typically

> is attached to a video bridge device, the bridge driver will create an I2C

> +/**
> + * v4l2_async_subdev_list - provided by subdevices
> + * @list:	links struct v4l2_async_subdev_list objects to a global list
> + *		before probing, and onto notifier->done after probing
> + * @asd:	pointer to respective struct v4l2_async_subdev
> + * @notifier:	pointer to managing notifier
> + */
> +struct v4l2_async_subdev_list {
> +	struct list_head list;
> +	struct v4l2_async_subdev *asd;
> +	struct v4l2_async_notifier *notifier;
> +};

I have a patch for this patch, which embeds members of this struct directly
into struct v4l2_subdev. My felling is that the code is simpler and easier
to follow this way, I might be missing some important details though.

> +/**
> + * v4l2_async_notifier - v4l2_device notifier data
> + * @subdev_num:	number of subdevices
> + * @subdev:	array of pointers to subdevices

How about changing this to:

       @subdevs: array of pointers to the subdevice descriptors

I think it would be more immediately clear this is the actual subdevs array
pointer, and perhaps we could have subdev_num renamed to num_subdevs ?

> + * @v4l2_dev:	pointer to struct v4l2_device
> + * @waiting:	list of struct v4l2_async_subdev, waiting for their drivers
> + * @done:	list of struct v4l2_async_subdev_list, already probed
> + * @list:	member in a global list of notifiers
> + * @bound:	a subdevice driver has successfully probed one of subdevices
> + * @complete:	all subdevices have been probed successfully
> + * @unbind:	a subdevice is leaving
> + */
> +struct v4l2_async_notifier {
> +	unsigned int subdev_num;
> +	struct v4l2_async_subdev **subdev;
> +	struct v4l2_device *v4l2_dev;
> +	struct list_head waiting;
> +	struct list_head done;
> +	struct list_head list;
> +	int (*bound)(struct v4l2_async_notifier *notifier,
> +		     struct v4l2_subdev *subdev,
> +		     struct v4l2_async_subdev *asd);
> +	int (*complete)(struct v4l2_async_notifier *notifier);
> +	void (*unbind)(struct v4l2_async_notifier *notifier,
> +		       struct v4l2_subdev *subdev,
> +		       struct v4l2_async_subdev *asd);
> +};
> +
> +int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
> +				 struct v4l2_async_notifier *notifier);
> +void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier);
> +int v4l2_async_register_subdev(struct v4l2_subdev *sd);
> +void v4l2_async_unregister_subdev(struct v4l2_subdev *sd);

I still think "async_" in this public API is unnecessary, since we register/
unregister a subdev with the core and notifiers are intrinsically 
asynchronous.
But your preference seems be otherwise, what could I do... :) At most it 
just
means one less happy user of this interface.

So except this bikeshedding I don't really have other comments, I'm 
going to
test this series with the s3c-camif/ov9650 drivers and will report back 
soon.

It would have been a shame to not have this series in 3.11. I guess three
kernel cycles, since the initial implementation, time frame is sufficient
for having finally working camera devices on a device tree enabled system
in mainline.

Thanks for the idea and patience during reviews! :)


Regards,
Sylwester

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v10 16/21] V4L2: support asynchronous subdevice registration
@ 2013-06-13 21:37     ` Sylwester Nawrocki
  0 siblings, 0 replies; 68+ messages in thread
From: Sylwester Nawrocki @ 2013-06-13 21:37 UTC (permalink / raw)
  To: Guennadi Liakhovetski
  Cc: linux-media, Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil,
	linux-sh, Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer

Hi Guennadi,

Overall it looks quite neat at this v10. :)

On 06/11/2013 10:23 AM, Guennadi Liakhovetski wrote:
> Currently bridge device drivers register devices for all subdevices
> synchronously, tupically, during their probing. E.g. if an I2C CMOS sensor

s/tupically/typically

> is attached to a video bridge device, the bridge driver will create an I2C

> +/**
> + * v4l2_async_subdev_list - provided by subdevices
> + * @list:	links struct v4l2_async_subdev_list objects to a global list
> + *		before probing, and onto notifier->done after probing
> + * @asd:	pointer to respective struct v4l2_async_subdev
> + * @notifier:	pointer to managing notifier
> + */
> +struct v4l2_async_subdev_list {
> +	struct list_head list;
> +	struct v4l2_async_subdev *asd;
> +	struct v4l2_async_notifier *notifier;
> +};

I have a patch for this patch, which embeds members of this struct directly
into struct v4l2_subdev. My felling is that the code is simpler and easier
to follow this way, I might be missing some important details though.

> +/**
> + * v4l2_async_notifier - v4l2_device notifier data
> + * @subdev_num:	number of subdevices
> + * @subdev:	array of pointers to subdevices

How about changing this to:

       @subdevs: array of pointers to the subdevice descriptors

I think it would be more immediately clear this is the actual subdevs array
pointer, and perhaps we could have subdev_num renamed to num_subdevs ?

> + * @v4l2_dev:	pointer to struct v4l2_device
> + * @waiting:	list of struct v4l2_async_subdev, waiting for their drivers
> + * @done:	list of struct v4l2_async_subdev_list, already probed
> + * @list:	member in a global list of notifiers
> + * @bound:	a subdevice driver has successfully probed one of subdevices
> + * @complete:	all subdevices have been probed successfully
> + * @unbind:	a subdevice is leaving
> + */
> +struct v4l2_async_notifier {
> +	unsigned int subdev_num;
> +	struct v4l2_async_subdev **subdev;
> +	struct v4l2_device *v4l2_dev;
> +	struct list_head waiting;
> +	struct list_head done;
> +	struct list_head list;
> +	int (*bound)(struct v4l2_async_notifier *notifier,
> +		     struct v4l2_subdev *subdev,
> +		     struct v4l2_async_subdev *asd);
> +	int (*complete)(struct v4l2_async_notifier *notifier);
> +	void (*unbind)(struct v4l2_async_notifier *notifier,
> +		       struct v4l2_subdev *subdev,
> +		       struct v4l2_async_subdev *asd);
> +};
> +
> +int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
> +				 struct v4l2_async_notifier *notifier);
> +void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier);
> +int v4l2_async_register_subdev(struct v4l2_subdev *sd);
> +void v4l2_async_unregister_subdev(struct v4l2_subdev *sd);

I still think "async_" in this public API is unnecessary, since we register/
unregister a subdev with the core and notifiers are intrinsically 
asynchronous.
But your preference seems be otherwise, what could I do... :) At most it 
just
means one less happy user of this interface.

So except this bikeshedding I don't really have other comments, I'm 
going to
test this series with the s3c-camif/ov9650 drivers and will report back 
soon.

It would have been a shame to not have this series in 3.11. I guess three
kernel cycles, since the initial implementation, time frame is sufficient
for having finally working camera devices on a device tree enabled system
in mainline.

Thanks for the idea and patience during reviews! :)


Regards,
Sylwester

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH] V4L2: Merge struct v4l2_async_subdev_list with struct v4l2_subdev
  2013-06-11  8:23   ` Guennadi Liakhovetski
@ 2013-06-13 21:39     ` Sylwester Nawrocki
  -1 siblings, 0 replies; 68+ messages in thread
From: Sylwester Nawrocki @ 2013-06-13 21:39 UTC (permalink / raw)
  To: g.liakhovetski
  Cc: linux-media, laurent.pinchart, sakari.ailus, magnus.damm,
	prabhakar.csengg, s.hauer, linux-sh, Sylwester Nawrocki

From: Sylwester Nawrocki <s.nawrocki@samsung.com>

Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
---

Please consider squashing such change or similar into patch
[PATCH v10 16/21] V4L2: support asynchronous subdevice registration

This patch has not been tested yet.
---
 drivers/media/v4l2-core/v4l2-async.c |   67 +++++++++++++++-------------------
 include/media/v4l2-async.h           |   15 +-------
 include/media/v4l2-subdev.h          |   13 +++----
 3 files changed, 36 insertions(+), 59 deletions(-)

diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
index 3590ccc..53f741d 100644
--- a/drivers/media/v4l2-core/v4l2-async.c
+++ b/drivers/media/v4l2-core/v4l2-async.c
@@ -42,12 +42,10 @@ static LIST_HEAD(notifier_list);
 static DEFINE_MUTEX(list_lock);
 
 static struct v4l2_async_subdev *v4l2_async_belongs(struct v4l2_async_notifier *notifier,
-						    struct v4l2_async_subdev_list *asdl)
+						    struct v4l2_subdev *sd)
 {
-	struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl);
 	struct v4l2_async_subdev *asd;
-	bool (*match)(struct device *,
-		      struct v4l2_async_subdev *);
+	bool (*match)(struct device *, struct v4l2_async_subdev *);
 
 	list_for_each_entry(asd, &notifier->waiting, list) {
 		/* bus_type has been verified valid before */
@@ -79,16 +77,15 @@ static struct v4l2_async_subdev *v4l2_async_belongs(struct v4l2_async_notifier *
 }
 
 static int v4l2_async_test_notify(struct v4l2_async_notifier *notifier,
-				  struct v4l2_async_subdev_list *asdl,
+				  struct v4l2_subdev *sd,
 				  struct v4l2_async_subdev *asd)
 {
-	struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl);
 	int ret;
 
 	/* Remove from the waiting list */
 	list_del(&asd->list);
-	asdl->asd = asd;
-	asdl->notifier = notifier;
+	sd->asd = asd;
+	sd->notifier = notifier;
 
 	if (notifier->bound) {
 		ret = notifier->bound(notifier, sd, asd);
@@ -96,7 +93,7 @@ static int v4l2_async_test_notify(struct v4l2_async_notifier *notifier,
 			return ret;
 	}
 	/* Move from the global subdevice list to notifier's done */
-	list_move(&asdl->list, &notifier->done);
+	list_move(&sd->async_list, &notifier->done);
 
 	ret = v4l2_device_register_subdev(notifier->v4l2_dev, sd);
 	if (ret < 0) {
@@ -111,21 +108,19 @@ static int v4l2_async_test_notify(struct v4l2_async_notifier *notifier,
 	return 0;
 }
 
-static void v4l2_async_cleanup(struct v4l2_async_subdev_list *asdl)
+static void v4l2_async_cleanup(struct v4l2_subdev *sd)
 {
-	struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl);
-
 	v4l2_device_unregister_subdev(sd);
-	/* Subdevice driver will reprobe and put asdl back onto the list */
-	list_del_init(&asdl->list);
-	asdl->asd = NULL;
+	/* Subdevice driver will reprobe and put the subdev back onto the list */
+	list_del_init(&sd->async_list);
+	sd->asd = NULL;
 	sd->dev = NULL;
 }
 
 int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
 				 struct v4l2_async_notifier *notifier)
 {
-	struct v4l2_async_subdev_list *asdl, *tmp;
+	struct v4l2_subdev *sd, *tmp;
 	struct v4l2_async_subdev *asd;
 	int i;
 
@@ -158,14 +153,14 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
 	/* Keep also completed notifiers on the list */
 	list_add(&notifier->list, &notifier_list);
 
-	list_for_each_entry_safe(asdl, tmp, &subdev_list, list) {
+	list_for_each_entry_safe(sd, tmp, &subdev_list, list) {
 		int ret;
 
-		asd = v4l2_async_belongs(notifier, asdl);
+		asd = v4l2_async_belongs(notifier, sd);
 		if (!asd)
 			continue;
 
-		ret = v4l2_async_test_notify(notifier, asdl, asd);
+		ret = v4l2_async_test_notify(notifier, sd, asd);
 		if (ret < 0) {
 			mutex_unlock(&list_lock);
 			return ret;
@@ -180,7 +175,7 @@ EXPORT_SYMBOL(v4l2_async_notifier_register);
 
 void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier)
 {
-	struct v4l2_async_subdev_list *asdl, *tmp;
+	struct v4l2_subdev *sd, *tmp;
 	unsigned int notif_n_subdev = notifier->subdev_num;
 	unsigned int n_subdev = min(notif_n_subdev, V4L2_MAX_SUBDEVS);
 	struct device *dev[n_subdev];
@@ -190,18 +185,16 @@ void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier)
 
 	list_del(&notifier->list);
 
-	list_for_each_entry_safe(asdl, tmp, &notifier->done, list) {
-		struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl);
-
+	list_for_each_entry_safe(sd, tmp, &notifier->done, list) {
 		dev[i] = get_device(sd->dev);
 
-		v4l2_async_cleanup(asdl);
+		v4l2_async_cleanup(sd);
 
 		/* If we handled USB devices, we'd have to lock the parent too */
 		device_release_driver(dev[i++]);
 
 		if (notifier->unbind)
-			notifier->unbind(notifier, sd, sd->asdl.asd);
+			notifier->unbind(notifier, sd, sd->asd);
 	}
 
 	mutex_unlock(&list_lock);
@@ -230,24 +223,23 @@ EXPORT_SYMBOL(v4l2_async_notifier_unregister);
 
 int v4l2_async_register_subdev(struct v4l2_subdev *sd)
 {
-	struct v4l2_async_subdev_list *asdl = &sd->asdl;
 	struct v4l2_async_notifier *notifier;
 
 	mutex_lock(&list_lock);
 
-	INIT_LIST_HEAD(&asdl->list);
+	INIT_LIST_HEAD(&sd->async_list);
 
 	list_for_each_entry(notifier, &notifier_list, list) {
-		struct v4l2_async_subdev *asd = v4l2_async_belongs(notifier, asdl);
+		struct v4l2_async_subdev *asd = v4l2_async_belongs(notifier, sd);
 		if (asd) {
-			int ret = v4l2_async_test_notify(notifier, asdl, asd);
+			int ret = v4l2_async_test_notify(notifier, sd, asd);
 			mutex_unlock(&list_lock);
 			return ret;
 		}
 	}
 
 	/* None matched, wait for hot-plugging */
-	list_add(&asdl->list, &subdev_list);
+	list_add(&sd->async_list, &subdev_list);
 
 	mutex_unlock(&list_lock);
 
@@ -257,23 +249,22 @@ EXPORT_SYMBOL(v4l2_async_register_subdev);
 
 void v4l2_async_unregister_subdev(struct v4l2_subdev *sd)
 {
-	struct v4l2_async_subdev_list *asdl = &sd->asdl;
-	struct v4l2_async_notifier *notifier = asdl->notifier;
+	struct v4l2_async_notifier *notifier = sd->notifier;
 
-	if (!asdl->asd) {
-		if (!list_empty(&asdl->list))
-			v4l2_async_cleanup(asdl);
+	if (!sd->asd) {
+		if (!list_empty(&sd->async_list))
+			v4l2_async_cleanup(sd);
 		return;
 	}
 
 	mutex_lock(&list_lock);
 
-	list_add(&asdl->asd->list, &notifier->waiting);
+	list_add(&sd->asd->list, &notifier->waiting);
 
-	v4l2_async_cleanup(asdl);
+	v4l2_async_cleanup(sd);
 
 	if (notifier->unbind)
-		notifier->unbind(notifier, sd, sd->asdl.asd);
+		notifier->unbind(notifier, sd, sd->asd);
 
 	mutex_unlock(&list_lock);
 }
diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h
index 12bd39c..ba91c3b 100644
--- a/include/media/v4l2-async.h
+++ b/include/media/v4l2-async.h
@@ -57,25 +57,12 @@ struct v4l2_async_subdev {
 };
 
 /**
- * v4l2_async_subdev_list - provided by subdevices
- * @list:	links struct v4l2_async_subdev_list objects to a global list
- *		before probing, and onto notifier->done after probing
- * @asd:	pointer to respective struct v4l2_async_subdev
- * @notifier:	pointer to managing notifier
- */
-struct v4l2_async_subdev_list {
-	struct list_head list;
-	struct v4l2_async_subdev *asd;
-	struct v4l2_async_notifier *notifier;
-};
-
-/**
  * v4l2_async_notifier - v4l2_device notifier data
  * @subdev_num:	number of subdevices
  * @subdev:	array of pointers to subdevices
  * @v4l2_dev:	pointer to struct v4l2_device
  * @waiting:	list of struct v4l2_async_subdev, waiting for their drivers
- * @done:	list of struct v4l2_async_subdev_list, already probed
+ * @done:	list of struct v4l2_subdev, already probed
  * @list:	member in a global list of notifiers
  * @bound:	a subdevice driver has successfully probed one of subdevices
  * @complete:	all subdevices have been probed successfully
diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h
index 21174af..45aa155 100644
--- a/include/media/v4l2-subdev.h
+++ b/include/media/v4l2-subdev.h
@@ -588,15 +588,14 @@ struct v4l2_subdev {
 	struct video_device *devnode;
 	/* pointer to the physical device */
 	struct device *dev;
-	struct v4l2_async_subdev_list asdl;
+	/* Links this subdev to a global subdev_list or @notifier->done list. */
+	struct list_head async_list;
+	/* Pointer to respective struct v4l2_async_subdev. */
+	struct v4l2_async_subdev *asd;
+	/* Pointer to the managing notifier. */
+	struct v4l2_async_notifier *notifier;
 };
 
-static inline struct v4l2_subdev *v4l2_async_to_subdev(
-			struct v4l2_async_subdev_list *asdl)
-{
-	return container_of(asdl, struct v4l2_subdev, asdl);
-}
-
 #define media_entity_to_v4l2_subdev(ent) \
 	container_of(ent, struct v4l2_subdev, entity)
 #define vdev_to_v4l2_subdev(vdev) \
-- 
1.7.4.1


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH] V4L2: Merge struct v4l2_async_subdev_list with struct v4l2_subdev
@ 2013-06-13 21:39     ` Sylwester Nawrocki
  0 siblings, 0 replies; 68+ messages in thread
From: Sylwester Nawrocki @ 2013-06-13 21:39 UTC (permalink / raw)
  To: g.liakhovetski
  Cc: linux-media, laurent.pinchart, sakari.ailus, magnus.damm,
	prabhakar.csengg, s.hauer, linux-sh, Sylwester Nawrocki

From: Sylwester Nawrocki <s.nawrocki@samsung.com>

Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
---

Please consider squashing such change or similar into patch
[PATCH v10 16/21] V4L2: support asynchronous subdevice registration

This patch has not been tested yet.
---
 drivers/media/v4l2-core/v4l2-async.c |   67 +++++++++++++++-------------------
 include/media/v4l2-async.h           |   15 +-------
 include/media/v4l2-subdev.h          |   13 +++----
 3 files changed, 36 insertions(+), 59 deletions(-)

diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
index 3590ccc..53f741d 100644
--- a/drivers/media/v4l2-core/v4l2-async.c
+++ b/drivers/media/v4l2-core/v4l2-async.c
@@ -42,12 +42,10 @@ static LIST_HEAD(notifier_list);
 static DEFINE_MUTEX(list_lock);
 
 static struct v4l2_async_subdev *v4l2_async_belongs(struct v4l2_async_notifier *notifier,
-						    struct v4l2_async_subdev_list *asdl)
+						    struct v4l2_subdev *sd)
 {
-	struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl);
 	struct v4l2_async_subdev *asd;
-	bool (*match)(struct device *,
-		      struct v4l2_async_subdev *);
+	bool (*match)(struct device *, struct v4l2_async_subdev *);
 
 	list_for_each_entry(asd, &notifier->waiting, list) {
 		/* bus_type has been verified valid before */
@@ -79,16 +77,15 @@ static struct v4l2_async_subdev *v4l2_async_belongs(struct v4l2_async_notifier *
 }
 
 static int v4l2_async_test_notify(struct v4l2_async_notifier *notifier,
-				  struct v4l2_async_subdev_list *asdl,
+				  struct v4l2_subdev *sd,
 				  struct v4l2_async_subdev *asd)
 {
-	struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl);
 	int ret;
 
 	/* Remove from the waiting list */
 	list_del(&asd->list);
-	asdl->asd = asd;
-	asdl->notifier = notifier;
+	sd->asd = asd;
+	sd->notifier = notifier;
 
 	if (notifier->bound) {
 		ret = notifier->bound(notifier, sd, asd);
@@ -96,7 +93,7 @@ static int v4l2_async_test_notify(struct v4l2_async_notifier *notifier,
 			return ret;
 	}
 	/* Move from the global subdevice list to notifier's done */
-	list_move(&asdl->list, &notifier->done);
+	list_move(&sd->async_list, &notifier->done);
 
 	ret = v4l2_device_register_subdev(notifier->v4l2_dev, sd);
 	if (ret < 0) {
@@ -111,21 +108,19 @@ static int v4l2_async_test_notify(struct v4l2_async_notifier *notifier,
 	return 0;
 }
 
-static void v4l2_async_cleanup(struct v4l2_async_subdev_list *asdl)
+static void v4l2_async_cleanup(struct v4l2_subdev *sd)
 {
-	struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl);
-
 	v4l2_device_unregister_subdev(sd);
-	/* Subdevice driver will reprobe and put asdl back onto the list */
-	list_del_init(&asdl->list);
-	asdl->asd = NULL;
+	/* Subdevice driver will reprobe and put the subdev back onto the list */
+	list_del_init(&sd->async_list);
+	sd->asd = NULL;
 	sd->dev = NULL;
 }
 
 int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
 				 struct v4l2_async_notifier *notifier)
 {
-	struct v4l2_async_subdev_list *asdl, *tmp;
+	struct v4l2_subdev *sd, *tmp;
 	struct v4l2_async_subdev *asd;
 	int i;
 
@@ -158,14 +153,14 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
 	/* Keep also completed notifiers on the list */
 	list_add(&notifier->list, &notifier_list);
 
-	list_for_each_entry_safe(asdl, tmp, &subdev_list, list) {
+	list_for_each_entry_safe(sd, tmp, &subdev_list, list) {
 		int ret;
 
-		asd = v4l2_async_belongs(notifier, asdl);
+		asd = v4l2_async_belongs(notifier, sd);
 		if (!asd)
 			continue;
 
-		ret = v4l2_async_test_notify(notifier, asdl, asd);
+		ret = v4l2_async_test_notify(notifier, sd, asd);
 		if (ret < 0) {
 			mutex_unlock(&list_lock);
 			return ret;
@@ -180,7 +175,7 @@ EXPORT_SYMBOL(v4l2_async_notifier_register);
 
 void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier)
 {
-	struct v4l2_async_subdev_list *asdl, *tmp;
+	struct v4l2_subdev *sd, *tmp;
 	unsigned int notif_n_subdev = notifier->subdev_num;
 	unsigned int n_subdev = min(notif_n_subdev, V4L2_MAX_SUBDEVS);
 	struct device *dev[n_subdev];
@@ -190,18 +185,16 @@ void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier)
 
 	list_del(&notifier->list);
 
-	list_for_each_entry_safe(asdl, tmp, &notifier->done, list) {
-		struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl);
-
+	list_for_each_entry_safe(sd, tmp, &notifier->done, list) {
 		dev[i] = get_device(sd->dev);
 
-		v4l2_async_cleanup(asdl);
+		v4l2_async_cleanup(sd);
 
 		/* If we handled USB devices, we'd have to lock the parent too */
 		device_release_driver(dev[i++]);
 
 		if (notifier->unbind)
-			notifier->unbind(notifier, sd, sd->asdl.asd);
+			notifier->unbind(notifier, sd, sd->asd);
 	}
 
 	mutex_unlock(&list_lock);
@@ -230,24 +223,23 @@ EXPORT_SYMBOL(v4l2_async_notifier_unregister);
 
 int v4l2_async_register_subdev(struct v4l2_subdev *sd)
 {
-	struct v4l2_async_subdev_list *asdl = &sd->asdl;
 	struct v4l2_async_notifier *notifier;
 
 	mutex_lock(&list_lock);
 
-	INIT_LIST_HEAD(&asdl->list);
+	INIT_LIST_HEAD(&sd->async_list);
 
 	list_for_each_entry(notifier, &notifier_list, list) {
-		struct v4l2_async_subdev *asd = v4l2_async_belongs(notifier, asdl);
+		struct v4l2_async_subdev *asd = v4l2_async_belongs(notifier, sd);
 		if (asd) {
-			int ret = v4l2_async_test_notify(notifier, asdl, asd);
+			int ret = v4l2_async_test_notify(notifier, sd, asd);
 			mutex_unlock(&list_lock);
 			return ret;
 		}
 	}
 
 	/* None matched, wait for hot-plugging */
-	list_add(&asdl->list, &subdev_list);
+	list_add(&sd->async_list, &subdev_list);
 
 	mutex_unlock(&list_lock);
 
@@ -257,23 +249,22 @@ EXPORT_SYMBOL(v4l2_async_register_subdev);
 
 void v4l2_async_unregister_subdev(struct v4l2_subdev *sd)
 {
-	struct v4l2_async_subdev_list *asdl = &sd->asdl;
-	struct v4l2_async_notifier *notifier = asdl->notifier;
+	struct v4l2_async_notifier *notifier = sd->notifier;
 
-	if (!asdl->asd) {
-		if (!list_empty(&asdl->list))
-			v4l2_async_cleanup(asdl);
+	if (!sd->asd) {
+		if (!list_empty(&sd->async_list))
+			v4l2_async_cleanup(sd);
 		return;
 	}
 
 	mutex_lock(&list_lock);
 
-	list_add(&asdl->asd->list, &notifier->waiting);
+	list_add(&sd->asd->list, &notifier->waiting);
 
-	v4l2_async_cleanup(asdl);
+	v4l2_async_cleanup(sd);
 
 	if (notifier->unbind)
-		notifier->unbind(notifier, sd, sd->asdl.asd);
+		notifier->unbind(notifier, sd, sd->asd);
 
 	mutex_unlock(&list_lock);
 }
diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h
index 12bd39c..ba91c3b 100644
--- a/include/media/v4l2-async.h
+++ b/include/media/v4l2-async.h
@@ -57,25 +57,12 @@ struct v4l2_async_subdev {
 };
 
 /**
- * v4l2_async_subdev_list - provided by subdevices
- * @list:	links struct v4l2_async_subdev_list objects to a global list
- *		before probing, and onto notifier->done after probing
- * @asd:	pointer to respective struct v4l2_async_subdev
- * @notifier:	pointer to managing notifier
- */
-struct v4l2_async_subdev_list {
-	struct list_head list;
-	struct v4l2_async_subdev *asd;
-	struct v4l2_async_notifier *notifier;
-};
-
-/**
  * v4l2_async_notifier - v4l2_device notifier data
  * @subdev_num:	number of subdevices
  * @subdev:	array of pointers to subdevices
  * @v4l2_dev:	pointer to struct v4l2_device
  * @waiting:	list of struct v4l2_async_subdev, waiting for their drivers
- * @done:	list of struct v4l2_async_subdev_list, already probed
+ * @done:	list of struct v4l2_subdev, already probed
  * @list:	member in a global list of notifiers
  * @bound:	a subdevice driver has successfully probed one of subdevices
  * @complete:	all subdevices have been probed successfully
diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h
index 21174af..45aa155 100644
--- a/include/media/v4l2-subdev.h
+++ b/include/media/v4l2-subdev.h
@@ -588,15 +588,14 @@ struct v4l2_subdev {
 	struct video_device *devnode;
 	/* pointer to the physical device */
 	struct device *dev;
-	struct v4l2_async_subdev_list asdl;
+	/* Links this subdev to a global subdev_list or @notifier->done list. */
+	struct list_head async_list;
+	/* Pointer to respective struct v4l2_async_subdev. */
+	struct v4l2_async_subdev *asd;
+	/* Pointer to the managing notifier. */
+	struct v4l2_async_notifier *notifier;
 };
 
-static inline struct v4l2_subdev *v4l2_async_to_subdev(
-			struct v4l2_async_subdev_list *asdl)
-{
-	return container_of(asdl, struct v4l2_subdev, asdl);
-}
-
 #define media_entity_to_v4l2_subdev(ent) \
 	container_of(ent, struct v4l2_subdev, entity)
 #define vdev_to_v4l2_subdev(vdev) \
-- 
1.7.4.1


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* Re: [PATCH v10 16/21] V4L2: support asynchronous subdevice registration
  2013-06-13 21:37     ` Sylwester Nawrocki
@ 2013-06-14  7:14       ` Guennadi Liakhovetski
  -1 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-14  7:14 UTC (permalink / raw)
  To: Sylwester Nawrocki
  Cc: linux-media, Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil,
	linux-sh, Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer

Hi Sylwester

On Thu, 13 Jun 2013, Sylwester Nawrocki wrote:

> Hi Guennadi,
> 
> Overall it looks quite neat at this v10. :)

Thanks :)

> On 06/11/2013 10:23 AM, Guennadi Liakhovetski wrote:
> > Currently bridge device drivers register devices for all subdevices
> > synchronously, tupically, during their probing. E.g. if an I2C CMOS sensor
> 
> s/tupically/typically
> 
> > is attached to a video bridge device, the bridge driver will create an I2C
> 
> > +/**
> > + * v4l2_async_subdev_list - provided by subdevices
> > + * @list:	links struct v4l2_async_subdev_list objects to a global list
> > + *		before probing, and onto notifier->done after probing
> > + * @asd:	pointer to respective struct v4l2_async_subdev
> > + * @notifier:	pointer to managing notifier
> > + */
> > +struct v4l2_async_subdev_list {
> > +	struct list_head list;
> > +	struct v4l2_async_subdev *asd;
> > +	struct v4l2_async_notifier *notifier;
> > +};
> 
> I have a patch for this patch, which embeds members of this struct directly
> into struct v4l2_subdev. My felling is that the code is simpler and easier
> to follow this way, I might be missing some important details though.

Thanks, saw it. In principle I have nothing against it. I think, it's just 
principle approach to this work, which seems to differ slightly from how 
others see it. I tried to as little intrusive as possible, touching 
current APIs only if absolutely necessary, keeping the async stuff largely 
separated from the rest. If however the common feeling is, that we should 
inject it directly in V4L2 core, I have nothing against it either. Still, 
I would prefer to keep the .c and .h files separate for now at least to 
reduce merge conflicts etc.

> > +/**
> > + * v4l2_async_notifier - v4l2_device notifier data
> > + * @subdev_num:	number of subdevices
> > + * @subdev:	array of pointers to subdevices
> 
> How about changing this to:
> 
>       @subdevs: array of pointers to the subdevice descriptors

I'm sure every single line of comments and code in these (and all other) 
patches can be improved :)

> I think it would be more immediately clear this is the actual subdevs array
> pointer, and perhaps we could have subdev_num renamed to num_subdevs ?

Sure, why not :)

> > + * @v4l2_dev:	pointer to struct v4l2_device
> > + * @waiting:	list of struct v4l2_async_subdev, waiting for their drivers
> > + * @done:	list of struct v4l2_async_subdev_list, already probed
> > + * @list:	member in a global list of notifiers
> > + * @bound:	a subdevice driver has successfully probed one of subdevices
> > + * @complete:	all subdevices have been probed successfully
> > + * @unbind:	a subdevice is leaving
> > + */
> > +struct v4l2_async_notifier {
> > +	unsigned int subdev_num;
> > +	struct v4l2_async_subdev **subdev;
> > +	struct v4l2_device *v4l2_dev;
> > +	struct list_head waiting;
> > +	struct list_head done;
> > +	struct list_head list;
> > +	int (*bound)(struct v4l2_async_notifier *notifier,
> > +		     struct v4l2_subdev *subdev,
> > +		     struct v4l2_async_subdev *asd);
> > +	int (*complete)(struct v4l2_async_notifier *notifier);
> > +	void (*unbind)(struct v4l2_async_notifier *notifier,
> > +		       struct v4l2_subdev *subdev,
> > +		       struct v4l2_async_subdev *asd);
> > +};
> > +
> > +int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
> > +				 struct v4l2_async_notifier *notifier);
> > +void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier);
> > +int v4l2_async_register_subdev(struct v4l2_subdev *sd);
> > +void v4l2_async_unregister_subdev(struct v4l2_subdev *sd);
> 
> I still think "async_" in this public API is unnecessary, since we register/
> unregister a subdev with the core and notifiers are intrinsically
> asynchronous.
> But your preference seems be otherwise, what could I do... :) At most it just
> means one less happy user of this interface.

See above :) And another point - this is your opinion, which I certainly 
respect and take into account. I think, Laurent somehow softly inclined in 
the same direction. But I didn't hear any other opinions, so, in the end I 
have to make a decision - is everyone more likely to like what has been 
proposed by this specific reviewer, or would everyone object :) There are 
obvious things - fixes etc., and there are less obvious ones - naming, 
formulating and such. So, here again - I just would prefer all methods, 
comprising this API to share the same namespace. To make it clearer, that 
if you register subdevices asynchronously, you also need notifiers on the 
host and the other way round. To make it easier to authors to match these 
methods. Just my 2p :)

> So except this bikeshedding I don't really have other comments, I'm going to
> test this series with the s3c-camif/ov9650 drivers and will report back soon.
> 
> It would have been a shame to not have this series in 3.11. I guess three
> kernel cycles, since the initial implementation, time frame is sufficient
> for having finally working camera devices on a device tree enabled system
> in mainline.

Great! So, let's get 1 or 2 opinions more, then I can make a v11 with just 
a couple of cosmetic changes and kindly ask Mauro to pull this in :)

Thanks
Guennadi
---
Guennadi Liakhovetski, Ph.D.
Freelance Open-Source Software Developer
http://www.open-technology.de/

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v10 16/21] V4L2: support asynchronous subdevice registration
@ 2013-06-14  7:14       ` Guennadi Liakhovetski
  0 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-14  7:14 UTC (permalink / raw)
  To: Sylwester Nawrocki
  Cc: linux-media, Laurent Pinchart, Sylwester Nawrocki, Hans Verkuil,
	linux-sh, Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer

Hi Sylwester

On Thu, 13 Jun 2013, Sylwester Nawrocki wrote:

> Hi Guennadi,
> 
> Overall it looks quite neat at this v10. :)

Thanks :)

> On 06/11/2013 10:23 AM, Guennadi Liakhovetski wrote:
> > Currently bridge device drivers register devices for all subdevices
> > synchronously, tupically, during their probing. E.g. if an I2C CMOS sensor
> 
> s/tupically/typically
> 
> > is attached to a video bridge device, the bridge driver will create an I2C
> 
> > +/**
> > + * v4l2_async_subdev_list - provided by subdevices
> > + * @list:	links struct v4l2_async_subdev_list objects to a global list
> > + *		before probing, and onto notifier->done after probing
> > + * @asd:	pointer to respective struct v4l2_async_subdev
> > + * @notifier:	pointer to managing notifier
> > + */
> > +struct v4l2_async_subdev_list {
> > +	struct list_head list;
> > +	struct v4l2_async_subdev *asd;
> > +	struct v4l2_async_notifier *notifier;
> > +};
> 
> I have a patch for this patch, which embeds members of this struct directly
> into struct v4l2_subdev. My felling is that the code is simpler and easier
> to follow this way, I might be missing some important details though.

Thanks, saw it. In principle I have nothing against it. I think, it's just 
principle approach to this work, which seems to differ slightly from how 
others see it. I tried to as little intrusive as possible, touching 
current APIs only if absolutely necessary, keeping the async stuff largely 
separated from the rest. If however the common feeling is, that we should 
inject it directly in V4L2 core, I have nothing against it either. Still, 
I would prefer to keep the .c and .h files separate for now at least to 
reduce merge conflicts etc.

> > +/**
> > + * v4l2_async_notifier - v4l2_device notifier data
> > + * @subdev_num:	number of subdevices
> > + * @subdev:	array of pointers to subdevices
> 
> How about changing this to:
> 
>       @subdevs: array of pointers to the subdevice descriptors

I'm sure every single line of comments and code in these (and all other) 
patches can be improved :)

> I think it would be more immediately clear this is the actual subdevs array
> pointer, and perhaps we could have subdev_num renamed to num_subdevs ?

Sure, why not :)

> > + * @v4l2_dev:	pointer to struct v4l2_device
> > + * @waiting:	list of struct v4l2_async_subdev, waiting for their drivers
> > + * @done:	list of struct v4l2_async_subdev_list, already probed
> > + * @list:	member in a global list of notifiers
> > + * @bound:	a subdevice driver has successfully probed one of subdevices
> > + * @complete:	all subdevices have been probed successfully
> > + * @unbind:	a subdevice is leaving
> > + */
> > +struct v4l2_async_notifier {
> > +	unsigned int subdev_num;
> > +	struct v4l2_async_subdev **subdev;
> > +	struct v4l2_device *v4l2_dev;
> > +	struct list_head waiting;
> > +	struct list_head done;
> > +	struct list_head list;
> > +	int (*bound)(struct v4l2_async_notifier *notifier,
> > +		     struct v4l2_subdev *subdev,
> > +		     struct v4l2_async_subdev *asd);
> > +	int (*complete)(struct v4l2_async_notifier *notifier);
> > +	void (*unbind)(struct v4l2_async_notifier *notifier,
> > +		       struct v4l2_subdev *subdev,
> > +		       struct v4l2_async_subdev *asd);
> > +};
> > +
> > +int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
> > +				 struct v4l2_async_notifier *notifier);
> > +void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier);
> > +int v4l2_async_register_subdev(struct v4l2_subdev *sd);
> > +void v4l2_async_unregister_subdev(struct v4l2_subdev *sd);
> 
> I still think "async_" in this public API is unnecessary, since we register/
> unregister a subdev with the core and notifiers are intrinsically
> asynchronous.
> But your preference seems be otherwise, what could I do... :) At most it just
> means one less happy user of this interface.

See above :) And another point - this is your opinion, which I certainly 
respect and take into account. I think, Laurent somehow softly inclined in 
the same direction. But I didn't hear any other opinions, so, in the end I 
have to make a decision - is everyone more likely to like what has been 
proposed by this specific reviewer, or would everyone object :) There are 
obvious things - fixes etc., and there are less obvious ones - naming, 
formulating and such. So, here again - I just would prefer all methods, 
comprising this API to share the same namespace. To make it clearer, that 
if you register subdevices asynchronously, you also need notifiers on the 
host and the other way round. To make it easier to authors to match these 
methods. Just my 2p :)

> So except this bikeshedding I don't really have other comments, I'm going to
> test this series with the s3c-camif/ov9650 drivers and will report back soon.
> 
> It would have been a shame to not have this series in 3.11. I guess three
> kernel cycles, since the initial implementation, time frame is sufficient
> for having finally working camera devices on a device tree enabled system
> in mainline.

Great! So, let's get 1 or 2 opinions more, then I can make a v11 with just 
a couple of cosmetic changes and kindly ask Mauro to pull this in :)

Thanks
Guennadi
---
Guennadi Liakhovetski, Ph.D.
Freelance Open-Source Software Developer
http://www.open-technology.de/

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v10 16/21] V4L2: support asynchronous subdevice registration
  2013-06-14  7:14       ` Guennadi Liakhovetski
@ 2013-06-14  9:07         ` Hans Verkuil
  -1 siblings, 0 replies; 68+ messages in thread
From: Hans Verkuil @ 2013-06-14  9:07 UTC (permalink / raw)
  To: Guennadi Liakhovetski
  Cc: Sylwester Nawrocki, linux-media, Laurent Pinchart,
	Sylwester Nawrocki, linux-sh, Magnus Damm, Sakari Ailus,
	Prabhakar Lad, Sascha Hauer

On Fri 14 June 2013 09:14:48 Guennadi Liakhovetski wrote:
> Hi Sylwester
> 
> On Thu, 13 Jun 2013, Sylwester Nawrocki wrote:
> 
> > Hi Guennadi,
> > 
> > Overall it looks quite neat at this v10. :)
> 
> Thanks :)
> 
> > On 06/11/2013 10:23 AM, Guennadi Liakhovetski wrote:
> > > Currently bridge device drivers register devices for all subdevices
> > > synchronously, tupically, during their probing. E.g. if an I2C CMOS sensor
> > 
> > s/tupically/typically
> > 
> > > is attached to a video bridge device, the bridge driver will create an I2C
> > 
> > > +/**
> > > + * v4l2_async_subdev_list - provided by subdevices
> > > + * @list:	links struct v4l2_async_subdev_list objects to a global list
> > > + *		before probing, and onto notifier->done after probing
> > > + * @asd:	pointer to respective struct v4l2_async_subdev
> > > + * @notifier:	pointer to managing notifier
> > > + */
> > > +struct v4l2_async_subdev_list {
> > > +	struct list_head list;
> > > +	struct v4l2_async_subdev *asd;
> > > +	struct v4l2_async_notifier *notifier;
> > > +};
> > 
> > I have a patch for this patch, which embeds members of this struct directly
> > into struct v4l2_subdev. My felling is that the code is simpler and easier
> > to follow this way, I might be missing some important details though.
> 
> Thanks, saw it. In principle I have nothing against it. I think, it's just 
> principle approach to this work, which seems to differ slightly from how 
> others see it. I tried to as little intrusive as possible, touching 
> current APIs only if absolutely necessary, keeping the async stuff largely 
> separated from the rest. If however the common feeling is, that we should 
> inject it directly in V4L2 core, I have nothing against it either. Still, 
> I would prefer to keep the .c and .h files separate for now at least to 
> reduce merge conflicts etc.

I think it makes sense to move this to the core, but keep the .c and .h files
separate.

A general note: my experience is that if being being intrusive to existing
APIs/data structures will simplify your code, then that's probable a good idea.
Core APIs and data structures are not 'holy' and it is quite OK to change them.
They will need careful code review, but other than that it is perfectly fine.

> 
> > > +/**
> > > + * v4l2_async_notifier - v4l2_device notifier data
> > > + * @subdev_num:	number of subdevices
> > > + * @subdev:	array of pointers to subdevices
> > 
> > How about changing this to:
> > 
> >       @subdevs: array of pointers to the subdevice descriptors
> 
> I'm sure every single line of comments and code in these (and all other) 
> patches can be improved :)
> 
> > I think it would be more immediately clear this is the actual subdevs array
> > pointer, and perhaps we could have subdev_num renamed to num_subdevs ?
> 
> Sure, why not :)
> 
> > > + * @v4l2_dev:	pointer to struct v4l2_device
> > > + * @waiting:	list of struct v4l2_async_subdev, waiting for their drivers
> > > + * @done:	list of struct v4l2_async_subdev_list, already probed
> > > + * @list:	member in a global list of notifiers
> > > + * @bound:	a subdevice driver has successfully probed one of subdevices
> > > + * @complete:	all subdevices have been probed successfully
> > > + * @unbind:	a subdevice is leaving
> > > + */
> > > +struct v4l2_async_notifier {
> > > +	unsigned int subdev_num;
> > > +	struct v4l2_async_subdev **subdev;
> > > +	struct v4l2_device *v4l2_dev;
> > > +	struct list_head waiting;
> > > +	struct list_head done;
> > > +	struct list_head list;
> > > +	int (*bound)(struct v4l2_async_notifier *notifier,
> > > +		     struct v4l2_subdev *subdev,
> > > +		     struct v4l2_async_subdev *asd);
> > > +	int (*complete)(struct v4l2_async_notifier *notifier);
> > > +	void (*unbind)(struct v4l2_async_notifier *notifier,
> > > +		       struct v4l2_subdev *subdev,
> > > +		       struct v4l2_async_subdev *asd);
> > > +};
> > > +
> > > +int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
> > > +				 struct v4l2_async_notifier *notifier);
> > > +void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier);
> > > +int v4l2_async_register_subdev(struct v4l2_subdev *sd);
> > > +void v4l2_async_unregister_subdev(struct v4l2_subdev *sd);
> > 
> > I still think "async_" in this public API is unnecessary, since we register/
> > unregister a subdev with the core and notifiers are intrinsically
> > asynchronous.
> > But your preference seems be otherwise, what could I do... :) At most it just
> > means one less happy user of this interface.

I think v4l2_register_subdev looks awfully similar to v4l2_device_register_subdev.
It becomes very confusing naming it like that. I prefer v4l2_async where 'async'
refers to the v4l2-async module.

> 
> See above :) And another point - this is your opinion, which I certainly 
> respect and take into account. I think, Laurent somehow softly inclined in 
> the same direction. But I didn't hear any other opinions, so, in the end I 
> have to make a decision - is everyone more likely to like what has been 
> proposed by this specific reviewer, or would everyone object :) There are 
> obvious things - fixes etc., and there are less obvious ones - naming, 
> formulating and such. So, here again - I just would prefer all methods, 
> comprising this API to share the same namespace. To make it clearer, that 
> if you register subdevices asynchronously, you also need notifiers on the 
> host and the other way round. To make it easier to authors to match these 
> methods. Just my 2p :)
> 
> > So except this bikeshedding I don't really have other comments, I'm going to
> > test this series with the s3c-camif/ov9650 drivers and will report back soon.
> > 
> > It would have been a shame to not have this series in 3.11. I guess three
> > kernel cycles, since the initial implementation, time frame is sufficient
> > for having finally working camera devices on a device tree enabled system
> > in mainline.
> 
> Great! So, let's get 1 or 2 opinions more, then I can make a v11 with just 
> a couple of cosmetic changes and kindly ask Mauro to pull this in :)

There is one last thing that also needs to be done: document this API in
Documentation/video4linux/v4l2-framework.txt.

Regards,

	Hans

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v10 16/21] V4L2: support asynchronous subdevice registration
@ 2013-06-14  9:07         ` Hans Verkuil
  0 siblings, 0 replies; 68+ messages in thread
From: Hans Verkuil @ 2013-06-14  9:07 UTC (permalink / raw)
  To: Guennadi Liakhovetski
  Cc: Sylwester Nawrocki, linux-media, Laurent Pinchart,
	Sylwester Nawrocki, linux-sh, Magnus Damm, Sakari Ailus,
	Prabhakar Lad, Sascha Hauer

On Fri 14 June 2013 09:14:48 Guennadi Liakhovetski wrote:
> Hi Sylwester
> 
> On Thu, 13 Jun 2013, Sylwester Nawrocki wrote:
> 
> > Hi Guennadi,
> > 
> > Overall it looks quite neat at this v10. :)
> 
> Thanks :)
> 
> > On 06/11/2013 10:23 AM, Guennadi Liakhovetski wrote:
> > > Currently bridge device drivers register devices for all subdevices
> > > synchronously, tupically, during their probing. E.g. if an I2C CMOS sensor
> > 
> > s/tupically/typically
> > 
> > > is attached to a video bridge device, the bridge driver will create an I2C
> > 
> > > +/**
> > > + * v4l2_async_subdev_list - provided by subdevices
> > > + * @list:	links struct v4l2_async_subdev_list objects to a global list
> > > + *		before probing, and onto notifier->done after probing
> > > + * @asd:	pointer to respective struct v4l2_async_subdev
> > > + * @notifier:	pointer to managing notifier
> > > + */
> > > +struct v4l2_async_subdev_list {
> > > +	struct list_head list;
> > > +	struct v4l2_async_subdev *asd;
> > > +	struct v4l2_async_notifier *notifier;
> > > +};
> > 
> > I have a patch for this patch, which embeds members of this struct directly
> > into struct v4l2_subdev. My felling is that the code is simpler and easier
> > to follow this way, I might be missing some important details though.
> 
> Thanks, saw it. In principle I have nothing against it. I think, it's just 
> principle approach to this work, which seems to differ slightly from how 
> others see it. I tried to as little intrusive as possible, touching 
> current APIs only if absolutely necessary, keeping the async stuff largely 
> separated from the rest. If however the common feeling is, that we should 
> inject it directly in V4L2 core, I have nothing against it either. Still, 
> I would prefer to keep the .c and .h files separate for now at least to 
> reduce merge conflicts etc.

I think it makes sense to move this to the core, but keep the .c and .h files
separate.

A general note: my experience is that if being being intrusive to existing
APIs/data structures will simplify your code, then that's probable a good idea.
Core APIs and data structures are not 'holy' and it is quite OK to change them.
They will need careful code review, but other than that it is perfectly fine.

> 
> > > +/**
> > > + * v4l2_async_notifier - v4l2_device notifier data
> > > + * @subdev_num:	number of subdevices
> > > + * @subdev:	array of pointers to subdevices
> > 
> > How about changing this to:
> > 
> >       @subdevs: array of pointers to the subdevice descriptors
> 
> I'm sure every single line of comments and code in these (and all other) 
> patches can be improved :)
> 
> > I think it would be more immediately clear this is the actual subdevs array
> > pointer, and perhaps we could have subdev_num renamed to num_subdevs ?
> 
> Sure, why not :)
> 
> > > + * @v4l2_dev:	pointer to struct v4l2_device
> > > + * @waiting:	list of struct v4l2_async_subdev, waiting for their drivers
> > > + * @done:	list of struct v4l2_async_subdev_list, already probed
> > > + * @list:	member in a global list of notifiers
> > > + * @bound:	a subdevice driver has successfully probed one of subdevices
> > > + * @complete:	all subdevices have been probed successfully
> > > + * @unbind:	a subdevice is leaving
> > > + */
> > > +struct v4l2_async_notifier {
> > > +	unsigned int subdev_num;
> > > +	struct v4l2_async_subdev **subdev;
> > > +	struct v4l2_device *v4l2_dev;
> > > +	struct list_head waiting;
> > > +	struct list_head done;
> > > +	struct list_head list;
> > > +	int (*bound)(struct v4l2_async_notifier *notifier,
> > > +		     struct v4l2_subdev *subdev,
> > > +		     struct v4l2_async_subdev *asd);
> > > +	int (*complete)(struct v4l2_async_notifier *notifier);
> > > +	void (*unbind)(struct v4l2_async_notifier *notifier,
> > > +		       struct v4l2_subdev *subdev,
> > > +		       struct v4l2_async_subdev *asd);
> > > +};
> > > +
> > > +int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
> > > +				 struct v4l2_async_notifier *notifier);
> > > +void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier);
> > > +int v4l2_async_register_subdev(struct v4l2_subdev *sd);
> > > +void v4l2_async_unregister_subdev(struct v4l2_subdev *sd);
> > 
> > I still think "async_" in this public API is unnecessary, since we register/
> > unregister a subdev with the core and notifiers are intrinsically
> > asynchronous.
> > But your preference seems be otherwise, what could I do... :) At most it just
> > means one less happy user of this interface.

I think v4l2_register_subdev looks awfully similar to v4l2_device_register_subdev.
It becomes very confusing naming it like that. I prefer v4l2_async where 'async'
refers to the v4l2-async module.

> 
> See above :) And another point - this is your opinion, which I certainly 
> respect and take into account. I think, Laurent somehow softly inclined in 
> the same direction. But I didn't hear any other opinions, so, in the end I 
> have to make a decision - is everyone more likely to like what has been 
> proposed by this specific reviewer, or would everyone object :) There are 
> obvious things - fixes etc., and there are less obvious ones - naming, 
> formulating and such. So, here again - I just would prefer all methods, 
> comprising this API to share the same namespace. To make it clearer, that 
> if you register subdevices asynchronously, you also need notifiers on the 
> host and the other way round. To make it easier to authors to match these 
> methods. Just my 2p :)
> 
> > So except this bikeshedding I don't really have other comments, I'm going to
> > test this series with the s3c-camif/ov9650 drivers and will report back soon.
> > 
> > It would have been a shame to not have this series in 3.11. I guess three
> > kernel cycles, since the initial implementation, time frame is sufficient
> > for having finally working camera devices on a device tree enabled system
> > in mainline.
> 
> Great! So, let's get 1 or 2 opinions more, then I can make a v11 with just 
> a couple of cosmetic changes and kindly ask Mauro to pull this in :)

There is one last thing that also needs to be done: document this API in
Documentation/video4linux/v4l2-framework.txt.

Regards,

	Hans

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v10 16/21] V4L2: support asynchronous subdevice registration
  2013-06-14  9:07         ` Hans Verkuil
@ 2013-06-14  9:14           ` Guennadi Liakhovetski
  -1 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-14  9:14 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: Sylwester Nawrocki, linux-media, Laurent Pinchart,
	Sylwester Nawrocki, linux-sh, Magnus Damm, Sakari Ailus,
	Prabhakar Lad, Sascha Hauer

Hi Hans

On Fri, 14 Jun 2013, Hans Verkuil wrote:

> On Fri 14 June 2013 09:14:48 Guennadi Liakhovetski wrote:
> > Hi Sylwester
> > 
> > On Thu, 13 Jun 2013, Sylwester Nawrocki wrote:
> > 
> > > Hi Guennadi,
> > > 
> > > Overall it looks quite neat at this v10. :)
> > 
> > Thanks :)
> > 
> > > On 06/11/2013 10:23 AM, Guennadi Liakhovetski wrote:
> > > > Currently bridge device drivers register devices for all subdevices
> > > > synchronously, tupically, during their probing. E.g. if an I2C CMOS sensor
> > > 
> > > s/tupically/typically
> > > 
> > > > is attached to a video bridge device, the bridge driver will create an I2C
> > > 
> > > > +/**
> > > > + * v4l2_async_subdev_list - provided by subdevices
> > > > + * @list:	links struct v4l2_async_subdev_list objects to a global list
> > > > + *		before probing, and onto notifier->done after probing
> > > > + * @asd:	pointer to respective struct v4l2_async_subdev
> > > > + * @notifier:	pointer to managing notifier
> > > > + */
> > > > +struct v4l2_async_subdev_list {
> > > > +	struct list_head list;
> > > > +	struct v4l2_async_subdev *asd;
> > > > +	struct v4l2_async_notifier *notifier;
> > > > +};
> > > 
> > > I have a patch for this patch, which embeds members of this struct directly
> > > into struct v4l2_subdev. My felling is that the code is simpler and easier
> > > to follow this way, I might be missing some important details though.
> > 
> > Thanks, saw it. In principle I have nothing against it. I think, it's just 
> > principle approach to this work, which seems to differ slightly from how 
> > others see it. I tried to as little intrusive as possible, touching 
> > current APIs only if absolutely necessary, keeping the async stuff largely 
> > separated from the rest. If however the common feeling is, that we should 
> > inject it directly in V4L2 core, I have nothing against it either. Still, 
> > I would prefer to keep the .c and .h files separate for now at least to 
> > reduce merge conflicts etc.
> 
> I think it makes sense to move this to the core, but keep the .c and .h files
> separate.

Ok, we can apply Sylwester's patch on top of my series, sure.

> A general note: my experience is that if being being intrusive to existing
> APIs/data structures will simplify your code, then that's probable a good idea.
> Core APIs and data structures are not 'holy' and it is quite OK to change them.
> They will need careful code review, but other than that it is perfectly fine.
> 
> > 
> > > > +/**
> > > > + * v4l2_async_notifier - v4l2_device notifier data
> > > > + * @subdev_num:	number of subdevices
> > > > + * @subdev:	array of pointers to subdevices
> > > 
> > > How about changing this to:
> > > 
> > >       @subdevs: array of pointers to the subdevice descriptors
> > 
> > I'm sure every single line of comments and code in these (and all other) 
> > patches can be improved :)
> > 
> > > I think it would be more immediately clear this is the actual subdevs array
> > > pointer, and perhaps we could have subdev_num renamed to num_subdevs ?
> > 
> > Sure, why not :)
> > 
> > > > + * @v4l2_dev:	pointer to struct v4l2_device
> > > > + * @waiting:	list of struct v4l2_async_subdev, waiting for their drivers
> > > > + * @done:	list of struct v4l2_async_subdev_list, already probed
> > > > + * @list:	member in a global list of notifiers
> > > > + * @bound:	a subdevice driver has successfully probed one of subdevices
> > > > + * @complete:	all subdevices have been probed successfully
> > > > + * @unbind:	a subdevice is leaving
> > > > + */
> > > > +struct v4l2_async_notifier {
> > > > +	unsigned int subdev_num;
> > > > +	struct v4l2_async_subdev **subdev;
> > > > +	struct v4l2_device *v4l2_dev;
> > > > +	struct list_head waiting;
> > > > +	struct list_head done;
> > > > +	struct list_head list;
> > > > +	int (*bound)(struct v4l2_async_notifier *notifier,
> > > > +		     struct v4l2_subdev *subdev,
> > > > +		     struct v4l2_async_subdev *asd);
> > > > +	int (*complete)(struct v4l2_async_notifier *notifier);
> > > > +	void (*unbind)(struct v4l2_async_notifier *notifier,
> > > > +		       struct v4l2_subdev *subdev,
> > > > +		       struct v4l2_async_subdev *asd);
> > > > +};
> > > > +
> > > > +int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
> > > > +				 struct v4l2_async_notifier *notifier);
> > > > +void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier);
> > > > +int v4l2_async_register_subdev(struct v4l2_subdev *sd);
> > > > +void v4l2_async_unregister_subdev(struct v4l2_subdev *sd);
> > > 
> > > I still think "async_" in this public API is unnecessary, since we register/
> > > unregister a subdev with the core and notifiers are intrinsically
> > > asynchronous.
> > > But your preference seems be otherwise, what could I do... :) At most it just
> > > means one less happy user of this interface.
> 
> I think v4l2_register_subdev looks awfully similar to v4l2_device_register_subdev.
> It becomes very confusing naming it like that. I prefer v4l2_async where 'async'
> refers to the v4l2-async module.

And v4l2(_async)_notifier_(un)register()?

> > See above :) And another point - this is your opinion, which I certainly 
> > respect and take into account. I think, Laurent somehow softly inclined in 
> > the same direction. But I didn't hear any other opinions, so, in the end I 
> > have to make a decision - is everyone more likely to like what has been 
> > proposed by this specific reviewer, or would everyone object :) There are 
> > obvious things - fixes etc., and there are less obvious ones - naming, 
> > formulating and such. So, here again - I just would prefer all methods, 
> > comprising this API to share the same namespace. To make it clearer, that 
> > if you register subdevices asynchronously, you also need notifiers on the 
> > host and the other way round. To make it easier to authors to match these 
> > methods. Just my 2p :)
> > 
> > > So except this bikeshedding I don't really have other comments, I'm going to
> > > test this series with the s3c-camif/ov9650 drivers and will report back soon.
> > > 
> > > It would have been a shame to not have this series in 3.11. I guess three
> > > kernel cycles, since the initial implementation, time frame is sufficient
> > > for having finally working camera devices on a device tree enabled system
> > > in mainline.
> > 
> > Great! So, let's get 1 or 2 opinions more, then I can make a v11 with just 
> > a couple of cosmetic changes and kindly ask Mauro to pull this in :)
> 
> There is one last thing that also needs to be done: document this API in
> Documentation/video4linux/v4l2-framework.txt.

Right, can do that too.

Thanks
Guennadi
---
Guennadi Liakhovetski, Ph.D.
Freelance Open-Source Software Developer
http://www.open-technology.de/

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v10 16/21] V4L2: support asynchronous subdevice registration
@ 2013-06-14  9:14           ` Guennadi Liakhovetski
  0 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-14  9:14 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: Sylwester Nawrocki, linux-media, Laurent Pinchart,
	Sylwester Nawrocki, linux-sh, Magnus Damm, Sakari Ailus,
	Prabhakar Lad, Sascha Hauer

Hi Hans

On Fri, 14 Jun 2013, Hans Verkuil wrote:

> On Fri 14 June 2013 09:14:48 Guennadi Liakhovetski wrote:
> > Hi Sylwester
> > 
> > On Thu, 13 Jun 2013, Sylwester Nawrocki wrote:
> > 
> > > Hi Guennadi,
> > > 
> > > Overall it looks quite neat at this v10. :)
> > 
> > Thanks :)
> > 
> > > On 06/11/2013 10:23 AM, Guennadi Liakhovetski wrote:
> > > > Currently bridge device drivers register devices for all subdevices
> > > > synchronously, tupically, during their probing. E.g. if an I2C CMOS sensor
> > > 
> > > s/tupically/typically
> > > 
> > > > is attached to a video bridge device, the bridge driver will create an I2C
> > > 
> > > > +/**
> > > > + * v4l2_async_subdev_list - provided by subdevices
> > > > + * @list:	links struct v4l2_async_subdev_list objects to a global list
> > > > + *		before probing, and onto notifier->done after probing
> > > > + * @asd:	pointer to respective struct v4l2_async_subdev
> > > > + * @notifier:	pointer to managing notifier
> > > > + */
> > > > +struct v4l2_async_subdev_list {
> > > > +	struct list_head list;
> > > > +	struct v4l2_async_subdev *asd;
> > > > +	struct v4l2_async_notifier *notifier;
> > > > +};
> > > 
> > > I have a patch for this patch, which embeds members of this struct directly
> > > into struct v4l2_subdev. My felling is that the code is simpler and easier
> > > to follow this way, I might be missing some important details though.
> > 
> > Thanks, saw it. In principle I have nothing against it. I think, it's just 
> > principle approach to this work, which seems to differ slightly from how 
> > others see it. I tried to as little intrusive as possible, touching 
> > current APIs only if absolutely necessary, keeping the async stuff largely 
> > separated from the rest. If however the common feeling is, that we should 
> > inject it directly in V4L2 core, I have nothing against it either. Still, 
> > I would prefer to keep the .c and .h files separate for now at least to 
> > reduce merge conflicts etc.
> 
> I think it makes sense to move this to the core, but keep the .c and .h files
> separate.

Ok, we can apply Sylwester's patch on top of my series, sure.

> A general note: my experience is that if being being intrusive to existing
> APIs/data structures will simplify your code, then that's probable a good idea.
> Core APIs and data structures are not 'holy' and it is quite OK to change them.
> They will need careful code review, but other than that it is perfectly fine.
> 
> > 
> > > > +/**
> > > > + * v4l2_async_notifier - v4l2_device notifier data
> > > > + * @subdev_num:	number of subdevices
> > > > + * @subdev:	array of pointers to subdevices
> > > 
> > > How about changing this to:
> > > 
> > >       @subdevs: array of pointers to the subdevice descriptors
> > 
> > I'm sure every single line of comments and code in these (and all other) 
> > patches can be improved :)
> > 
> > > I think it would be more immediately clear this is the actual subdevs array
> > > pointer, and perhaps we could have subdev_num renamed to num_subdevs ?
> > 
> > Sure, why not :)
> > 
> > > > + * @v4l2_dev:	pointer to struct v4l2_device
> > > > + * @waiting:	list of struct v4l2_async_subdev, waiting for their drivers
> > > > + * @done:	list of struct v4l2_async_subdev_list, already probed
> > > > + * @list:	member in a global list of notifiers
> > > > + * @bound:	a subdevice driver has successfully probed one of subdevices
> > > > + * @complete:	all subdevices have been probed successfully
> > > > + * @unbind:	a subdevice is leaving
> > > > + */
> > > > +struct v4l2_async_notifier {
> > > > +	unsigned int subdev_num;
> > > > +	struct v4l2_async_subdev **subdev;
> > > > +	struct v4l2_device *v4l2_dev;
> > > > +	struct list_head waiting;
> > > > +	struct list_head done;
> > > > +	struct list_head list;
> > > > +	int (*bound)(struct v4l2_async_notifier *notifier,
> > > > +		     struct v4l2_subdev *subdev,
> > > > +		     struct v4l2_async_subdev *asd);
> > > > +	int (*complete)(struct v4l2_async_notifier *notifier);
> > > > +	void (*unbind)(struct v4l2_async_notifier *notifier,
> > > > +		       struct v4l2_subdev *subdev,
> > > > +		       struct v4l2_async_subdev *asd);
> > > > +};
> > > > +
> > > > +int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
> > > > +				 struct v4l2_async_notifier *notifier);
> > > > +void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier);
> > > > +int v4l2_async_register_subdev(struct v4l2_subdev *sd);
> > > > +void v4l2_async_unregister_subdev(struct v4l2_subdev *sd);
> > > 
> > > I still think "async_" in this public API is unnecessary, since we register/
> > > unregister a subdev with the core and notifiers are intrinsically
> > > asynchronous.
> > > But your preference seems be otherwise, what could I do... :) At most it just
> > > means one less happy user of this interface.
> 
> I think v4l2_register_subdev looks awfully similar to v4l2_device_register_subdev.
> It becomes very confusing naming it like that. I prefer v4l2_async where 'async'
> refers to the v4l2-async module.

And v4l2(_async)_notifier_(un)register()?

> > See above :) And another point - this is your opinion, which I certainly 
> > respect and take into account. I think, Laurent somehow softly inclined in 
> > the same direction. But I didn't hear any other opinions, so, in the end I 
> > have to make a decision - is everyone more likely to like what has been 
> > proposed by this specific reviewer, or would everyone object :) There are 
> > obvious things - fixes etc., and there are less obvious ones - naming, 
> > formulating and such. So, here again - I just would prefer all methods, 
> > comprising this API to share the same namespace. To make it clearer, that 
> > if you register subdevices asynchronously, you also need notifiers on the 
> > host and the other way round. To make it easier to authors to match these 
> > methods. Just my 2p :)
> > 
> > > So except this bikeshedding I don't really have other comments, I'm going to
> > > test this series with the s3c-camif/ov9650 drivers and will report back soon.
> > > 
> > > It would have been a shame to not have this series in 3.11. I guess three
> > > kernel cycles, since the initial implementation, time frame is sufficient
> > > for having finally working camera devices on a device tree enabled system
> > > in mainline.
> > 
> > Great! So, let's get 1 or 2 opinions more, then I can make a v11 with just 
> > a couple of cosmetic changes and kindly ask Mauro to pull this in :)
> 
> There is one last thing that also needs to be done: document this API in
> Documentation/video4linux/v4l2-framework.txt.

Right, can do that too.

Thanks
Guennadi
---
Guennadi Liakhovetski, Ph.D.
Freelance Open-Source Software Developer
http://www.open-technology.de/

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v10 16/21] V4L2: support asynchronous subdevice registration
  2013-06-14  9:14           ` Guennadi Liakhovetski
@ 2013-06-14  9:40             ` Sylwester Nawrocki
  -1 siblings, 0 replies; 68+ messages in thread
From: Sylwester Nawrocki @ 2013-06-14  9:40 UTC (permalink / raw)
  To: Guennadi Liakhovetski, Hans Verkuil
  Cc: Sylwester Nawrocki, linux-media, Laurent Pinchart,
	Sylwester Nawrocki, linux-sh, Magnus Damm, Sakari Ailus,
	Prabhakar Lad, Sascha Hauer

Hi,

On 06/14/2013 11:14 AM, Guennadi Liakhovetski wrote:
> On Fri, 14 Jun 2013, Hans Verkuil wrote:
>> On Fri 14 June 2013 09:14:48 Guennadi Liakhovetski wrote:
>>> On Thu, 13 Jun 2013, Sylwester Nawrocki wrote:
>>>> On 06/11/2013 10:23 AM, Guennadi Liakhovetski wrote:
[...]
>>>>> + * @v4l2_dev:	pointer to struct v4l2_device
>>>>> + * @waiting:	list of struct v4l2_async_subdev, waiting for their drivers
>>>>> + * @done:	list of struct v4l2_async_subdev_list, already probed
>>>>> + * @list:	member in a global list of notifiers
>>>>> + * @bound:	a subdevice driver has successfully probed one of subdevices
>>>>> + * @complete:	all subdevices have been probed successfully
>>>>> + * @unbind:	a subdevice is leaving
>>>>> + */
>>>>> +struct v4l2_async_notifier {
>>>>> +	unsigned int subdev_num;
>>>>> +	struct v4l2_async_subdev **subdev;
>>>>> +	struct v4l2_device *v4l2_dev;
>>>>> +	struct list_head waiting;
>>>>> +	struct list_head done;
>>>>> +	struct list_head list;
>>>>> +	int (*bound)(struct v4l2_async_notifier *notifier,
>>>>> +		     struct v4l2_subdev *subdev,
>>>>> +		     struct v4l2_async_subdev *asd);
>>>>> +	int (*complete)(struct v4l2_async_notifier *notifier);
>>>>> +	void (*unbind)(struct v4l2_async_notifier *notifier,
>>>>> +		       struct v4l2_subdev *subdev,
>>>>> +		       struct v4l2_async_subdev *asd);
>>>>> +};
>>>>> +
>>>>> +int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
>>>>> +				 struct v4l2_async_notifier *notifier);
>>>>> +void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier);
>>>>> +int v4l2_async_register_subdev(struct v4l2_subdev *sd);
>>>>> +void v4l2_async_unregister_subdev(struct v4l2_subdev *sd);
>>>>
>>>> I still think "async_" in this public API is unnecessary, since we register/
>>>> unregister a subdev with the core and notifiers are intrinsically
>>>> asynchronous.
>>>> But your preference seems be otherwise, what could I do... :) At most it just
>>>> means one less happy user of this interface.
>>
>> I think v4l2_register_subdev looks awfully similar to v4l2_device_register_subdev.
>> It becomes very confusing naming it like that. I prefer v4l2_async where 'async'
>> refers to the v4l2-async module.

Ok, let's leave v4l2_async then.

> And v4l2(_async)_notifier_(un)register()?

I guess it would be better to have all or none of the functions
with that prefix. So either:

v4l2_async_notifier_register
v4l2_async_notifier_unregister
v4l2_async_register_subdev
v4l2_async_unregister_subdev

or

v4l2_subdev_notifier_register
v4l2_subdev_notifier_unregister
v4l2_subdev_register
v4l2_subdev_unregister

Thanks,
Sylwester

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v10 16/21] V4L2: support asynchronous subdevice registration
@ 2013-06-14  9:40             ` Sylwester Nawrocki
  0 siblings, 0 replies; 68+ messages in thread
From: Sylwester Nawrocki @ 2013-06-14  9:40 UTC (permalink / raw)
  To: Guennadi Liakhovetski, Hans Verkuil
  Cc: Sylwester Nawrocki, linux-media, Laurent Pinchart,
	Sylwester Nawrocki, linux-sh, Magnus Damm, Sakari Ailus,
	Prabhakar Lad, Sascha Hauer

Hi,

On 06/14/2013 11:14 AM, Guennadi Liakhovetski wrote:
> On Fri, 14 Jun 2013, Hans Verkuil wrote:
>> On Fri 14 June 2013 09:14:48 Guennadi Liakhovetski wrote:
>>> On Thu, 13 Jun 2013, Sylwester Nawrocki wrote:
>>>> On 06/11/2013 10:23 AM, Guennadi Liakhovetski wrote:
[...]
>>>>> + * @v4l2_dev:	pointer to struct v4l2_device
>>>>> + * @waiting:	list of struct v4l2_async_subdev, waiting for their drivers
>>>>> + * @done:	list of struct v4l2_async_subdev_list, already probed
>>>>> + * @list:	member in a global list of notifiers
>>>>> + * @bound:	a subdevice driver has successfully probed one of subdevices
>>>>> + * @complete:	all subdevices have been probed successfully
>>>>> + * @unbind:	a subdevice is leaving
>>>>> + */
>>>>> +struct v4l2_async_notifier {
>>>>> +	unsigned int subdev_num;
>>>>> +	struct v4l2_async_subdev **subdev;
>>>>> +	struct v4l2_device *v4l2_dev;
>>>>> +	struct list_head waiting;
>>>>> +	struct list_head done;
>>>>> +	struct list_head list;
>>>>> +	int (*bound)(struct v4l2_async_notifier *notifier,
>>>>> +		     struct v4l2_subdev *subdev,
>>>>> +		     struct v4l2_async_subdev *asd);
>>>>> +	int (*complete)(struct v4l2_async_notifier *notifier);
>>>>> +	void (*unbind)(struct v4l2_async_notifier *notifier,
>>>>> +		       struct v4l2_subdev *subdev,
>>>>> +		       struct v4l2_async_subdev *asd);
>>>>> +};
>>>>> +
>>>>> +int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
>>>>> +				 struct v4l2_async_notifier *notifier);
>>>>> +void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier);
>>>>> +int v4l2_async_register_subdev(struct v4l2_subdev *sd);
>>>>> +void v4l2_async_unregister_subdev(struct v4l2_subdev *sd);
>>>>
>>>> I still think "async_" in this public API is unnecessary, since we register/
>>>> unregister a subdev with the core and notifiers are intrinsically
>>>> asynchronous.
>>>> But your preference seems be otherwise, what could I do... :) At most it just
>>>> means one less happy user of this interface.
>>
>> I think v4l2_register_subdev looks awfully similar to v4l2_device_register_subdev.
>> It becomes very confusing naming it like that. I prefer v4l2_async where 'async'
>> refers to the v4l2-async module.

Ok, let's leave v4l2_async then.

> And v4l2(_async)_notifier_(un)register()?

I guess it would be better to have all or none of the functions
with that prefix. So either:

v4l2_async_notifier_register
v4l2_async_notifier_unregister
v4l2_async_register_subdev
v4l2_async_unregister_subdev

or

v4l2_subdev_notifier_register
v4l2_subdev_notifier_unregister
v4l2_subdev_register
v4l2_subdev_unregister

Thanks,
Sylwester

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v10 16/21] V4L2: support asynchronous subdevice registration
  2013-06-14  9:40             ` Sylwester Nawrocki
@ 2013-06-14  9:44               ` Guennadi Liakhovetski
  -1 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-14  9:44 UTC (permalink / raw)
  To: Sylwester Nawrocki
  Cc: Hans Verkuil, Sylwester Nawrocki, linux-media, Laurent Pinchart,
	linux-sh, Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer

On Fri, 14 Jun 2013, Sylwester Nawrocki wrote:

> Hi,
> 
> On 06/14/2013 11:14 AM, Guennadi Liakhovetski wrote:
> > On Fri, 14 Jun 2013, Hans Verkuil wrote:
> >> On Fri 14 June 2013 09:14:48 Guennadi Liakhovetski wrote:
> >>> On Thu, 13 Jun 2013, Sylwester Nawrocki wrote:
> >>>> On 06/11/2013 10:23 AM, Guennadi Liakhovetski wrote:
> [...]
> >>>>> + * @v4l2_dev:	pointer to struct v4l2_device
> >>>>> + * @waiting:	list of struct v4l2_async_subdev, waiting for their drivers
> >>>>> + * @done:	list of struct v4l2_async_subdev_list, already probed
> >>>>> + * @list:	member in a global list of notifiers
> >>>>> + * @bound:	a subdevice driver has successfully probed one of subdevices
> >>>>> + * @complete:	all subdevices have been probed successfully
> >>>>> + * @unbind:	a subdevice is leaving
> >>>>> + */
> >>>>> +struct v4l2_async_notifier {
> >>>>> +	unsigned int subdev_num;
> >>>>> +	struct v4l2_async_subdev **subdev;
> >>>>> +	struct v4l2_device *v4l2_dev;
> >>>>> +	struct list_head waiting;
> >>>>> +	struct list_head done;
> >>>>> +	struct list_head list;
> >>>>> +	int (*bound)(struct v4l2_async_notifier *notifier,
> >>>>> +		     struct v4l2_subdev *subdev,
> >>>>> +		     struct v4l2_async_subdev *asd);
> >>>>> +	int (*complete)(struct v4l2_async_notifier *notifier);
> >>>>> +	void (*unbind)(struct v4l2_async_notifier *notifier,
> >>>>> +		       struct v4l2_subdev *subdev,
> >>>>> +		       struct v4l2_async_subdev *asd);
> >>>>> +};
> >>>>> +
> >>>>> +int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
> >>>>> +				 struct v4l2_async_notifier *notifier);
> >>>>> +void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier);
> >>>>> +int v4l2_async_register_subdev(struct v4l2_subdev *sd);
> >>>>> +void v4l2_async_unregister_subdev(struct v4l2_subdev *sd);
> >>>>
> >>>> I still think "async_" in this public API is unnecessary, since we register/
> >>>> unregister a subdev with the core and notifiers are intrinsically
> >>>> asynchronous.
> >>>> But your preference seems be otherwise, what could I do... :) At most it just
> >>>> means one less happy user of this interface.
> >>
> >> I think v4l2_register_subdev looks awfully similar to v4l2_device_register_subdev.
> >> It becomes very confusing naming it like that. I prefer v4l2_async where 'async'
> >> refers to the v4l2-async module.
> 
> Ok, let's leave v4l2_async then.
> 
> > And v4l2(_async)_notifier_(un)register()?
> 
> I guess it would be better to have all or none of the functions
> with that prefix. So either:

Exactly.

Thanks
Guennadi

> v4l2_async_notifier_register
> v4l2_async_notifier_unregister
> v4l2_async_register_subdev
> v4l2_async_unregister_subdev
> 
> or
> 
> v4l2_subdev_notifier_register
> v4l2_subdev_notifier_unregister
> v4l2_subdev_register
> v4l2_subdev_unregister
> 
> Thanks,
> Sylwester
> 

---
Guennadi Liakhovetski, Ph.D.
Freelance Open-Source Software Developer
http://www.open-technology.de/

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v10 16/21] V4L2: support asynchronous subdevice registration
@ 2013-06-14  9:44               ` Guennadi Liakhovetski
  0 siblings, 0 replies; 68+ messages in thread
From: Guennadi Liakhovetski @ 2013-06-14  9:44 UTC (permalink / raw)
  To: Sylwester Nawrocki
  Cc: Hans Verkuil, Sylwester Nawrocki, linux-media, Laurent Pinchart,
	linux-sh, Magnus Damm, Sakari Ailus, Prabhakar Lad, Sascha Hauer

On Fri, 14 Jun 2013, Sylwester Nawrocki wrote:

> Hi,
> 
> On 06/14/2013 11:14 AM, Guennadi Liakhovetski wrote:
> > On Fri, 14 Jun 2013, Hans Verkuil wrote:
> >> On Fri 14 June 2013 09:14:48 Guennadi Liakhovetski wrote:
> >>> On Thu, 13 Jun 2013, Sylwester Nawrocki wrote:
> >>>> On 06/11/2013 10:23 AM, Guennadi Liakhovetski wrote:
> [...]
> >>>>> + * @v4l2_dev:	pointer to struct v4l2_device
> >>>>> + * @waiting:	list of struct v4l2_async_subdev, waiting for their drivers
> >>>>> + * @done:	list of struct v4l2_async_subdev_list, already probed
> >>>>> + * @list:	member in a global list of notifiers
> >>>>> + * @bound:	a subdevice driver has successfully probed one of subdevices
> >>>>> + * @complete:	all subdevices have been probed successfully
> >>>>> + * @unbind:	a subdevice is leaving
> >>>>> + */
> >>>>> +struct v4l2_async_notifier {
> >>>>> +	unsigned int subdev_num;
> >>>>> +	struct v4l2_async_subdev **subdev;
> >>>>> +	struct v4l2_device *v4l2_dev;
> >>>>> +	struct list_head waiting;
> >>>>> +	struct list_head done;
> >>>>> +	struct list_head list;
> >>>>> +	int (*bound)(struct v4l2_async_notifier *notifier,
> >>>>> +		     struct v4l2_subdev *subdev,
> >>>>> +		     struct v4l2_async_subdev *asd);
> >>>>> +	int (*complete)(struct v4l2_async_notifier *notifier);
> >>>>> +	void (*unbind)(struct v4l2_async_notifier *notifier,
> >>>>> +		       struct v4l2_subdev *subdev,
> >>>>> +		       struct v4l2_async_subdev *asd);
> >>>>> +};
> >>>>> +
> >>>>> +int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
> >>>>> +				 struct v4l2_async_notifier *notifier);
> >>>>> +void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier);
> >>>>> +int v4l2_async_register_subdev(struct v4l2_subdev *sd);
> >>>>> +void v4l2_async_unregister_subdev(struct v4l2_subdev *sd);
> >>>>
> >>>> I still think "async_" in this public API is unnecessary, since we register/
> >>>> unregister a subdev with the core and notifiers are intrinsically
> >>>> asynchronous.
> >>>> But your preference seems be otherwise, what could I do... :) At most it just
> >>>> means one less happy user of this interface.
> >>
> >> I think v4l2_register_subdev looks awfully similar to v4l2_device_register_subdev.
> >> It becomes very confusing naming it like that. I prefer v4l2_async where 'async'
> >> refers to the v4l2-async module.
> 
> Ok, let's leave v4l2_async then.
> 
> > And v4l2(_async)_notifier_(un)register()?
> 
> I guess it would be better to have all or none of the functions
> with that prefix. So either:

Exactly.

Thanks
Guennadi

> v4l2_async_notifier_register
> v4l2_async_notifier_unregister
> v4l2_async_register_subdev
> v4l2_async_unregister_subdev
> 
> or
> 
> v4l2_subdev_notifier_register
> v4l2_subdev_notifier_unregister
> v4l2_subdev_register
> v4l2_subdev_unregister
> 
> Thanks,
> Sylwester
> 

---
Guennadi Liakhovetski, Ph.D.
Freelance Open-Source Software Developer
http://www.open-technology.de/

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v10 16/21] V4L2: support asynchronous subdevice registration
  2013-06-14  9:40             ` Sylwester Nawrocki
@ 2013-06-14 12:58               ` Laurent Pinchart
  -1 siblings, 0 replies; 68+ messages in thread
From: Laurent Pinchart @ 2013-06-14 12:58 UTC (permalink / raw)
  To: Sylwester Nawrocki
  Cc: Guennadi Liakhovetski, Hans Verkuil, Sylwester Nawrocki,
	linux-media, linux-sh, Magnus Damm, Sakari Ailus, Prabhakar Lad,
	Sascha Hauer

Hi Sylwester,

On Friday 14 June 2013 11:40:12 Sylwester Nawrocki wrote:
> On 06/14/2013 11:14 AM, Guennadi Liakhovetski wrote:
> > On Fri, 14 Jun 2013, Hans Verkuil wrote:
> >> On Fri 14 June 2013 09:14:48 Guennadi Liakhovetski wrote:
> >>> On Thu, 13 Jun 2013, Sylwester Nawrocki wrote:
> >>>> On 06/11/2013 10:23 AM, Guennadi Liakhovetski wrote:
> [...]
> 
> >>>>> + * @v4l2_dev:	pointer to struct v4l2_device
> >>>>> + * @waiting:	list of struct v4l2_async_subdev, waiting for their
> >>>>> drivers
> >>>>> + * @done:	list of struct v4l2_async_subdev_list, already probed
> >>>>> + * @list:	member in a global list of notifiers
> >>>>> + * @bound:	a subdevice driver has successfully probed one of
> >>>>> subdevices
> >>>>> + * @complete:	all subdevices have been probed successfully
> >>>>> + * @unbind:	a subdevice is leaving
> >>>>> + */
> >>>>> +struct v4l2_async_notifier {
> >>>>> +	unsigned int subdev_num;
> >>>>> +	struct v4l2_async_subdev **subdev;
> >>>>> +	struct v4l2_device *v4l2_dev;
> >>>>> +	struct list_head waiting;
> >>>>> +	struct list_head done;
> >>>>> +	struct list_head list;
> >>>>> +	int (*bound)(struct v4l2_async_notifier *notifier,
> >>>>> +		     struct v4l2_subdev *subdev,
> >>>>> +		     struct v4l2_async_subdev *asd);
> >>>>> +	int (*complete)(struct v4l2_async_notifier *notifier);
> >>>>> +	void (*unbind)(struct v4l2_async_notifier *notifier,
> >>>>> +		       struct v4l2_subdev *subdev,
> >>>>> +		       struct v4l2_async_subdev *asd);
> >>>>> +};
> >>>>> +
> >>>>> +int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
> >>>>> +				 struct v4l2_async_notifier *notifier);
> >>>>> +void v4l2_async_notifier_unregister(struct v4l2_async_notifier
> >>>>> *notifier);
> >>>>> +int v4l2_async_register_subdev(struct v4l2_subdev *sd);
> >>>>> +void v4l2_async_unregister_subdev(struct v4l2_subdev *sd);
> >>>> 
> >>>> I still think "async_" in this public API is unnecessary, since we
> >>>> register/ unregister a subdev with the core and notifiers are
> >>>> intrinsically
> >>>> asynchronous.
> >>>> But your preference seems be otherwise, what could I do... :) At most
> >>>> it just means one less happy user of this interface.
> >> 
> >> I think v4l2_register_subdev looks awfully similar to
> >> v4l2_device_register_subdev. It becomes very confusing naming it like
> >> that. I prefer v4l2_async where 'async' refers to the v4l2-async module.
> 
> Ok, let's leave v4l2_async then.
> 
> > And v4l2(_async)_notifier_(un)register()?
> 
> I guess it would be better to have all or none of the functions
> with that prefix. So either:
> 
> v4l2_async_notifier_register
> v4l2_async_notifier_unregister
> v4l2_async_register_subdev
> v4l2_async_unregister_subdev
> 
> or
> 
> v4l2_subdev_notifier_register
> v4l2_subdev_notifier_unregister
> v4l2_subdev_register
> v4l2_subdev_unregister

I prefer the section option, but I'm fine with the first one for now. As Hans 
pointed out, it would be pretty easy to confuse v4l2_subdev_register with 
v4l2_device_register_subdev. In the long term we want to move all drivers to 
asynchronous registration. When that happens it won't be difficult to revisit 
the API to remove unused functions and rename v4l2_aysnc_* to just v4l2_*.

-- 
Regards,

Laurent Pinchart


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v10 16/21] V4L2: support asynchronous subdevice registration
@ 2013-06-14 12:58               ` Laurent Pinchart
  0 siblings, 0 replies; 68+ messages in thread
From: Laurent Pinchart @ 2013-06-14 12:58 UTC (permalink / raw)
  To: Sylwester Nawrocki
  Cc: Guennadi Liakhovetski, Hans Verkuil, Sylwester Nawrocki,
	linux-media, linux-sh, Magnus Damm, Sakari Ailus, Prabhakar Lad,
	Sascha Hauer

Hi Sylwester,

On Friday 14 June 2013 11:40:12 Sylwester Nawrocki wrote:
> On 06/14/2013 11:14 AM, Guennadi Liakhovetski wrote:
> > On Fri, 14 Jun 2013, Hans Verkuil wrote:
> >> On Fri 14 June 2013 09:14:48 Guennadi Liakhovetski wrote:
> >>> On Thu, 13 Jun 2013, Sylwester Nawrocki wrote:
> >>>> On 06/11/2013 10:23 AM, Guennadi Liakhovetski wrote:
> [...]
> 
> >>>>> + * @v4l2_dev:	pointer to struct v4l2_device
> >>>>> + * @waiting:	list of struct v4l2_async_subdev, waiting for their
> >>>>> drivers
> >>>>> + * @done:	list of struct v4l2_async_subdev_list, already probed
> >>>>> + * @list:	member in a global list of notifiers
> >>>>> + * @bound:	a subdevice driver has successfully probed one of
> >>>>> subdevices
> >>>>> + * @complete:	all subdevices have been probed successfully
> >>>>> + * @unbind:	a subdevice is leaving
> >>>>> + */
> >>>>> +struct v4l2_async_notifier {
> >>>>> +	unsigned int subdev_num;
> >>>>> +	struct v4l2_async_subdev **subdev;
> >>>>> +	struct v4l2_device *v4l2_dev;
> >>>>> +	struct list_head waiting;
> >>>>> +	struct list_head done;
> >>>>> +	struct list_head list;
> >>>>> +	int (*bound)(struct v4l2_async_notifier *notifier,
> >>>>> +		     struct v4l2_subdev *subdev,
> >>>>> +		     struct v4l2_async_subdev *asd);
> >>>>> +	int (*complete)(struct v4l2_async_notifier *notifier);
> >>>>> +	void (*unbind)(struct v4l2_async_notifier *notifier,
> >>>>> +		       struct v4l2_subdev *subdev,
> >>>>> +		       struct v4l2_async_subdev *asd);
> >>>>> +};
> >>>>> +
> >>>>> +int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
> >>>>> +				 struct v4l2_async_notifier *notifier);
> >>>>> +void v4l2_async_notifier_unregister(struct v4l2_async_notifier
> >>>>> *notifier);
> >>>>> +int v4l2_async_register_subdev(struct v4l2_subdev *sd);
> >>>>> +void v4l2_async_unregister_subdev(struct v4l2_subdev *sd);
> >>>> 
> >>>> I still think "async_" in this public API is unnecessary, since we
> >>>> register/ unregister a subdev with the core and notifiers are
> >>>> intrinsically
> >>>> asynchronous.
> >>>> But your preference seems be otherwise, what could I do... :) At most
> >>>> it just means one less happy user of this interface.
> >> 
> >> I think v4l2_register_subdev looks awfully similar to
> >> v4l2_device_register_subdev. It becomes very confusing naming it like
> >> that. I prefer v4l2_async where 'async' refers to the v4l2-async module.
> 
> Ok, let's leave v4l2_async then.
> 
> > And v4l2(_async)_notifier_(un)register()?
> 
> I guess it would be better to have all or none of the functions
> with that prefix. So either:
> 
> v4l2_async_notifier_register
> v4l2_async_notifier_unregister
> v4l2_async_register_subdev
> v4l2_async_unregister_subdev
> 
> or
> 
> v4l2_subdev_notifier_register
> v4l2_subdev_notifier_unregister
> v4l2_subdev_register
> v4l2_subdev_unregister

I prefer the section option, but I'm fine with the first one for now. As Hans 
pointed out, it would be pretty easy to confuse v4l2_subdev_register with 
v4l2_device_register_subdev. In the long term we want to move all drivers to 
asynchronous registration. When that happens it won't be difficult to revisit 
the API to remove unused functions and rename v4l2_aysnc_* to just v4l2_*.

-- 
Regards,

Laurent Pinchart


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v10 16/21] V4L2: support asynchronous subdevice registration
  2013-06-14  7:14       ` Guennadi Liakhovetski
@ 2013-06-14 13:03         ` Laurent Pinchart
  -1 siblings, 0 replies; 68+ messages in thread
From: Laurent Pinchart @ 2013-06-14 13:03 UTC (permalink / raw)
  To: Guennadi Liakhovetski
  Cc: Sylwester Nawrocki, linux-media, Sylwester Nawrocki,
	Hans Verkuil, linux-sh, Magnus Damm, Sakari Ailus, Prabhakar Lad,
	Sascha Hauer

Hi Guennadi,

On Friday 14 June 2013 09:14:48 Guennadi Liakhovetski wrote:
> On Thu, 13 Jun 2013, Sylwester Nawrocki wrote:
> > Hi Guennadi,
> > 
> > Overall it looks quite neat at this v10. :)
> 
> Thanks :)
> 
> > On 06/11/2013 10:23 AM, Guennadi Liakhovetski wrote:
> > > Currently bridge device drivers register devices for all subdevices
> > > synchronously, tupically, during their probing. E.g. if an I2C CMOS
> > > sensor
> > 
> > s/tupically/typically
> > 
> > > is attached to a video bridge device, the bridge driver will create an
> > > I2C
> > > 
> > > +/**
> > > + * v4l2_async_subdev_list - provided by subdevices
> > > + * @list:	links struct v4l2_async_subdev_list objects to a global list
> > > + *		before probing, and onto notifier->done after probing
> > > + * @asd:	pointer to respective struct v4l2_async_subdev
> > > + * @notifier:	pointer to managing notifier
> > > + */
> > > +struct v4l2_async_subdev_list {
> > > +	struct list_head list;
> > > +	struct v4l2_async_subdev *asd;
> > > +	struct v4l2_async_notifier *notifier;
> > > +};
> > 
> > I have a patch for this patch, which embeds members of this struct
> > directly into struct v4l2_subdev. My felling is that the code is simpler
> > and easier to follow this way, I might be missing some important details
> > though.
> 
> Thanks, saw it. In principle I have nothing against it. I think, it's just
> principle approach to this work, which seems to differ slightly from how
> others see it. I tried to as little intrusive as possible, touching
> current APIs only if absolutely necessary, keeping the async stuff largely
> separated from the rest. If however the common feeling is, that we should
> inject it directly in V4L2 core, I have nothing against it either. Still,
> I would prefer to keep the .c and .h files separate for now at least to
> reduce merge conflicts etc.
> 
> > > +/**
> > > + * v4l2_async_notifier - v4l2_device notifier data
> > > + * @subdev_num:	number of subdevices
> > > + * @subdev:	array of pointers to subdevices
> > 
> > How about changing this to:
> >       @subdevs: array of pointers to the subdevice descriptors
> 
> I'm sure every single line of comments and code in these (and all other)
> patches can be improved :)
> 
> > I think it would be more immediately clear this is the actual subdevs
> > array pointer, and perhaps we could have subdev_num renamed to num_subdevs
> > ?
> 
> Sure, why not :)
> 
> > > + * @v4l2_dev:	pointer to struct v4l2_device
> > > + * @waiting:	list of struct v4l2_async_subdev, waiting for their
> > > drivers
> > > + * @done:	list of struct v4l2_async_subdev_list, already probed
> > > + * @list:	member in a global list of notifiers
> > > + * @bound:	a subdevice driver has successfully probed one of subdevices
> > > + * @complete:	all subdevices have been probed successfully
> > > + * @unbind:	a subdevice is leaving
> > > + */
> > > +struct v4l2_async_notifier {
> > > +	unsigned int subdev_num;
> > > +	struct v4l2_async_subdev **subdev;
> > > +	struct v4l2_device *v4l2_dev;
> > > +	struct list_head waiting;
> > > +	struct list_head done;
> > > +	struct list_head list;
> > > +	int (*bound)(struct v4l2_async_notifier *notifier,
> > > +		     struct v4l2_subdev *subdev,
> > > +		     struct v4l2_async_subdev *asd);
> > > +	int (*complete)(struct v4l2_async_notifier *notifier);
> > > +	void (*unbind)(struct v4l2_async_notifier *notifier,
> > > +		       struct v4l2_subdev *subdev,
> > > +		       struct v4l2_async_subdev *asd);
> > > +};
> > > +
> > > +int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
> > > +				 struct v4l2_async_notifier *notifier);
> > > +void v4l2_async_notifier_unregister(struct v4l2_async_notifier
> > > *notifier);
> > > +int v4l2_async_register_subdev(struct v4l2_subdev *sd);
> > > +void v4l2_async_unregister_subdev(struct v4l2_subdev *sd);
> > 
> > I still think "async_" in this public API is unnecessary, since we
> > register/ unregister a subdev with the core and notifiers are
> > intrinsically asynchronous.
> > But your preference seems be otherwise, what could I do... :) At most it
> > just means one less happy user of this interface.
> 
> See above :) And another point - this is your opinion, which I certainly
> respect and take into account. I think, Laurent somehow softly inclined in
> the same direction. But I didn't hear any other opinions, so, in the end I
> have to make a decision - is everyone more likely to like what has been
> proposed by this specific reviewer, or would everyone object :) There are
> obvious things - fixes etc., and there are less obvious ones - naming,
> formulating and such. So, here again - I just would prefer all methods,
> comprising this API to share the same namespace. To make it clearer, that
> if you register subdevices asynchronously, you also need notifiers on the
> host and the other way round. To make it easier to authors to match these
> methods. Just my 2p :)
> 
> > So except this bikeshedding I don't really have other comments, I'm going
> > to test this series with the s3c-camif/ov9650 drivers and will report
> > back soon.
> > 
> > It would have been a shame to not have this series in 3.11. I guess three
> > kernel cycles, since the initial implementation, time frame is sufficient
> > for having finally working camera devices on a device tree enabled system
> > in mainline.
> 
> Great! So, let's get 1 or 2 opinions more, then I can make a v11 with just
> a couple of cosmetic changes and kindly ask Mauro to pull this in :)

I have no further comment than those already posted by Hans and Sylwester on 
v10. I'll ack v11 (possibly with comments on the documentation though ;-)).

-- 
Regards,

Laurent Pinchart


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v10 16/21] V4L2: support asynchronous subdevice registration
@ 2013-06-14 13:03         ` Laurent Pinchart
  0 siblings, 0 replies; 68+ messages in thread
From: Laurent Pinchart @ 2013-06-14 13:03 UTC (permalink / raw)
  To: Guennadi Liakhovetski
  Cc: Sylwester Nawrocki, linux-media, Sylwester Nawrocki,
	Hans Verkuil, linux-sh, Magnus Damm, Sakari Ailus, Prabhakar Lad,
	Sascha Hauer

Hi Guennadi,

On Friday 14 June 2013 09:14:48 Guennadi Liakhovetski wrote:
> On Thu, 13 Jun 2013, Sylwester Nawrocki wrote:
> > Hi Guennadi,
> > 
> > Overall it looks quite neat at this v10. :)
> 
> Thanks :)
> 
> > On 06/11/2013 10:23 AM, Guennadi Liakhovetski wrote:
> > > Currently bridge device drivers register devices for all subdevices
> > > synchronously, tupically, during their probing. E.g. if an I2C CMOS
> > > sensor
> > 
> > s/tupically/typically
> > 
> > > is attached to a video bridge device, the bridge driver will create an
> > > I2C
> > > 
> > > +/**
> > > + * v4l2_async_subdev_list - provided by subdevices
> > > + * @list:	links struct v4l2_async_subdev_list objects to a global list
> > > + *		before probing, and onto notifier->done after probing
> > > + * @asd:	pointer to respective struct v4l2_async_subdev
> > > + * @notifier:	pointer to managing notifier
> > > + */
> > > +struct v4l2_async_subdev_list {
> > > +	struct list_head list;
> > > +	struct v4l2_async_subdev *asd;
> > > +	struct v4l2_async_notifier *notifier;
> > > +};
> > 
> > I have a patch for this patch, which embeds members of this struct
> > directly into struct v4l2_subdev. My felling is that the code is simpler
> > and easier to follow this way, I might be missing some important details
> > though.
> 
> Thanks, saw it. In principle I have nothing against it. I think, it's just
> principle approach to this work, which seems to differ slightly from how
> others see it. I tried to as little intrusive as possible, touching
> current APIs only if absolutely necessary, keeping the async stuff largely
> separated from the rest. If however the common feeling is, that we should
> inject it directly in V4L2 core, I have nothing against it either. Still,
> I would prefer to keep the .c and .h files separate for now at least to
> reduce merge conflicts etc.
> 
> > > +/**
> > > + * v4l2_async_notifier - v4l2_device notifier data
> > > + * @subdev_num:	number of subdevices
> > > + * @subdev:	array of pointers to subdevices
> > 
> > How about changing this to:
> >       @subdevs: array of pointers to the subdevice descriptors
> 
> I'm sure every single line of comments and code in these (and all other)
> patches can be improved :)
> 
> > I think it would be more immediately clear this is the actual subdevs
> > array pointer, and perhaps we could have subdev_num renamed to num_subdevs
> > ?
> 
> Sure, why not :)
> 
> > > + * @v4l2_dev:	pointer to struct v4l2_device
> > > + * @waiting:	list of struct v4l2_async_subdev, waiting for their
> > > drivers
> > > + * @done:	list of struct v4l2_async_subdev_list, already probed
> > > + * @list:	member in a global list of notifiers
> > > + * @bound:	a subdevice driver has successfully probed one of subdevices
> > > + * @complete:	all subdevices have been probed successfully
> > > + * @unbind:	a subdevice is leaving
> > > + */
> > > +struct v4l2_async_notifier {
> > > +	unsigned int subdev_num;
> > > +	struct v4l2_async_subdev **subdev;
> > > +	struct v4l2_device *v4l2_dev;
> > > +	struct list_head waiting;
> > > +	struct list_head done;
> > > +	struct list_head list;
> > > +	int (*bound)(struct v4l2_async_notifier *notifier,
> > > +		     struct v4l2_subdev *subdev,
> > > +		     struct v4l2_async_subdev *asd);
> > > +	int (*complete)(struct v4l2_async_notifier *notifier);
> > > +	void (*unbind)(struct v4l2_async_notifier *notifier,
> > > +		       struct v4l2_subdev *subdev,
> > > +		       struct v4l2_async_subdev *asd);
> > > +};
> > > +
> > > +int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
> > > +				 struct v4l2_async_notifier *notifier);
> > > +void v4l2_async_notifier_unregister(struct v4l2_async_notifier
> > > *notifier);
> > > +int v4l2_async_register_subdev(struct v4l2_subdev *sd);
> > > +void v4l2_async_unregister_subdev(struct v4l2_subdev *sd);
> > 
> > I still think "async_" in this public API is unnecessary, since we
> > register/ unregister a subdev with the core and notifiers are
> > intrinsically asynchronous.
> > But your preference seems be otherwise, what could I do... :) At most it
> > just means one less happy user of this interface.
> 
> See above :) And another point - this is your opinion, which I certainly
> respect and take into account. I think, Laurent somehow softly inclined in
> the same direction. But I didn't hear any other opinions, so, in the end I
> have to make a decision - is everyone more likely to like what has been
> proposed by this specific reviewer, or would everyone object :) There are
> obvious things - fixes etc., and there are less obvious ones - naming,
> formulating and such. So, here again - I just would prefer all methods,
> comprising this API to share the same namespace. To make it clearer, that
> if you register subdevices asynchronously, you also need notifiers on the
> host and the other way round. To make it easier to authors to match these
> methods. Just my 2p :)
> 
> > So except this bikeshedding I don't really have other comments, I'm going
> > to test this series with the s3c-camif/ov9650 drivers and will report
> > back soon.
> > 
> > It would have been a shame to not have this series in 3.11. I guess three
> > kernel cycles, since the initial implementation, time frame is sufficient
> > for having finally working camera devices on a device tree enabled system
> > in mainline.
> 
> Great! So, let's get 1 or 2 opinions more, then I can make a v11 with just
> a couple of cosmetic changes and kindly ask Mauro to pull this in :)

I have no further comment than those already posted by Hans and Sylwester on 
v10. I'll ack v11 (possibly with comments on the documentation though ;-)).

-- 
Regards,

Laurent Pinchart


^ permalink raw reply	[flat|nested] 68+ messages in thread

end of thread, other threads:[~2013-06-14 13:03 UTC | newest]

Thread overview: 68+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-06-11  8:23 [PATCH v10 00/21] V4L2 clock and asynchronous probing Guennadi Liakhovetski
2013-06-11  8:23 ` Guennadi Liakhovetski
2013-06-11  8:23 ` [PATCH v10 01/21] soc-camera: move common code to soc_camera.c Guennadi Liakhovetski
2013-06-11  8:23   ` Guennadi Liakhovetski
2013-06-11  8:23 ` [PATCH v10 02/21] soc-camera: add host clock callbacks to start and stop the master clock Guennadi Liakhovetski
2013-06-11  8:23   ` Guennadi Liakhovetski
2013-06-11  8:23 ` [PATCH v10 03/21] pxa-camera: move interface activation and deactivation to clock callbacks Guennadi Liakhovetski
2013-06-11  8:23   ` Guennadi Liakhovetski
2013-06-11  8:23 ` [PATCH v10 04/21] omap1-camera: " Guennadi Liakhovetski
2013-06-11  8:23   ` Guennadi Liakhovetski
2013-06-11  8:23 ` [PATCH v10 05/21] atmel-isi: " Guennadi Liakhovetski
2013-06-11  8:23   ` Guennadi Liakhovetski
2013-06-11  8:23 ` [PATCH v10 06/21] mx3-camera: " Guennadi Liakhovetski
2013-06-11  8:23   ` Guennadi Liakhovetski
2013-06-11  8:23 ` [PATCH v10 07/21] mx2-camera: " Guennadi Liakhovetski
2013-06-11  8:23   ` Guennadi Liakhovetski
2013-06-11  8:23 ` [PATCH v10 08/21] mx1-camera: " Guennadi Liakhovetski
2013-06-11  8:23   ` Guennadi Liakhovetski
2013-06-11  8:23 ` [PATCH v10 09/21] sh-mobile-ceu-camera: move interface activation and deactivation to clock callback Guennadi Liakhovetski
2013-06-11  8:23   ` [PATCH v10 09/21] sh-mobile-ceu-camera: move interface activation and deactivation to clock callbacks Guennadi Liakhovetski
2013-06-11  8:23 ` [PATCH v10 10/21] soc-camera: make .clock_{start,stop} compulsory, .add / .remove optional Guennadi Liakhovetski
2013-06-11  8:23   ` Guennadi Liakhovetski
2013-06-11  8:23 ` [PATCH v10 11/21] soc-camera: don't attach the client to the host during probing Guennadi Liakhovetski
2013-06-11  8:23   ` Guennadi Liakhovetski
2013-06-11  8:23 ` [PATCH v10 12/21] sh-mobile-ceu-camera: add primitive OF support Guennadi Liakhovetski
2013-06-11  8:23   ` Guennadi Liakhovetski
2013-06-11  8:23 ` [PATCH v10 13/21] sh-mobile-ceu-driver: support max width and height in DT Guennadi Liakhovetski
2013-06-11  8:23   ` Guennadi Liakhovetski
2013-06-11  8:23 ` [PATCH v10 14/21] V4L2: add temporary clock helpers Guennadi Liakhovetski
2013-06-11  8:23   ` Guennadi Liakhovetski
2013-06-11  8:23 ` [PATCH v10 15/21] V4L2: add a device pointer to struct v4l2_subdev Guennadi Liakhovetski
2013-06-11  8:23   ` Guennadi Liakhovetski
2013-06-12 17:16   ` Prabhakar Lad
2013-06-12 17:28     ` Prabhakar Lad
2013-06-13 10:32   ` Hans Verkuil
2013-06-13 10:32     ` Hans Verkuil
2013-06-11  8:23 ` [PATCH v10 16/21] V4L2: support asynchronous subdevice registration Guennadi Liakhovetski
2013-06-11  8:23   ` Guennadi Liakhovetski
2013-06-12 17:18   ` Prabhakar Lad
2013-06-12 17:30     ` Prabhakar Lad
2013-06-13 21:37   ` Sylwester Nawrocki
2013-06-13 21:37     ` Sylwester Nawrocki
2013-06-14  7:14     ` Guennadi Liakhovetski
2013-06-14  7:14       ` Guennadi Liakhovetski
2013-06-14  9:07       ` Hans Verkuil
2013-06-14  9:07         ` Hans Verkuil
2013-06-14  9:14         ` Guennadi Liakhovetski
2013-06-14  9:14           ` Guennadi Liakhovetski
2013-06-14  9:40           ` Sylwester Nawrocki
2013-06-14  9:40             ` Sylwester Nawrocki
2013-06-14  9:44             ` Guennadi Liakhovetski
2013-06-14  9:44               ` Guennadi Liakhovetski
2013-06-14 12:58             ` Laurent Pinchart
2013-06-14 12:58               ` Laurent Pinchart
2013-06-14 13:03       ` Laurent Pinchart
2013-06-14 13:03         ` Laurent Pinchart
2013-06-13 21:39   ` [PATCH] V4L2: Merge struct v4l2_async_subdev_list with struct v4l2_subdev Sylwester Nawrocki
2013-06-13 21:39     ` Sylwester Nawrocki
2013-06-11  8:23 ` [PATCH v10 17/21] soc-camera: switch I2C subdevice drivers to use v4l2-clk Guennadi Liakhovetski
2013-06-11  8:23   ` Guennadi Liakhovetski
2013-06-11  8:23 ` [PATCH v10 18/21] soc-camera: add V4L2-async support Guennadi Liakhovetski
2013-06-11  8:23   ` Guennadi Liakhovetski
2013-06-11  8:23 ` [PATCH v10 19/21] sh_mobile_ceu_camera: add asynchronous subdevice probing support Guennadi Liakhovetski
2013-06-11  8:23   ` Guennadi Liakhovetski
2013-06-11  8:23 ` [PATCH v10 20/21] imx074: support asynchronous probing Guennadi Liakhovetski
2013-06-11  8:23   ` Guennadi Liakhovetski
2013-06-11  8:23 ` [PATCH v10 21/21] ARM: shmobile: convert ap4evb to asynchronously register camera subdevices Guennadi Liakhovetski
2013-06-11  8:23   ` Guennadi Liakhovetski

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.