All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 00/11] v4l2: OMAP4 ISS driver + Sensor + Board support
@ 2011-12-01  0:14 ` Sergio Aguirre
  0 siblings, 0 replies; 65+ messages in thread
From: Sergio Aguirre @ 2011-12-01  0:14 UTC (permalink / raw)
  To: linux-media; +Cc: linux-omap, laurent.pinchart, sakari.ailus, Sergio Aguirre

Hi everyone,

This is the second version of the OMAP4 ISS driver,
now ported to the Media Controller framework AND supporting
videobuf2 framework.

This patchset should apply cleanly on top of v3.2-rc3 kernel tag.

This driver attempts to provide an fully open source solution to
control the OMAP4 Imaging SubSystem (a.k.a. ISS).

Starts with just CSI2-A interface support, and pretends to be
ready for expansion to add support to the many ISS block modules
as possible.

Please see newly added documentation for more details:

Documentation/video4linux/omap4_camera.txt

Any comments/complaints are welcome. :)

Changes since v1:
- Simplification of auxclk handling in board files. (Pointed out by: Roger Quadros)
- Cleanup of Camera support enablement for 4430sdp & panda. (Pointed out by: Roger Quadros)
- Use of HWMOD declaration for assisted platform_device creation. (Pointed out by: Felipe Balbi)
- Videobuf2 migration (Removal of custom iss_queue buffer handling driver)
- Proper GPO3 handling for CAM_SEL in 4430sdp.

Sergio Aguirre (10):
  TWL6030: Add mapping for auxiliary regs
  mfd: twl6040: Fix wrong TWL6040_GPO3 bitfield value
  OMAP4: hwmod: Include CSI2A and CSIPHY1 memory sections
  OMAP4: Add base addresses for ISS
  v4l: Add support for omap4iss driver
  v4l: Add support for ov5640 sensor
  v4l: Add support for ov5650 sensor
  arm: omap4430sdp: Add support for omap4iss camera
  arm: omap4panda: Add support for omap4iss camera
  arm: Add support for CMA for omap4iss driver

Stanimir Varbanov (1):
  v4l: Introduce sensor operation for getting interface configuration

 Documentation/video4linux/omap4_camera.txt    |   60 ++
 arch/arm/mach-omap2/Kconfig                   |   54 +
 arch/arm/mach-omap2/Makefile                  |    3 +
 arch/arm/mach-omap2/board-4430sdp-camera.c    |  221 ++++
 arch/arm/mach-omap2/board-omap4panda-camera.c |  198 ++++
 arch/arm/mach-omap2/devices.c                 |   40 +
 arch/arm/mach-omap2/omap_hwmod_44xx_data.c    |   16 +-
 arch/arm/plat-omap/include/plat/omap4-iss.h   |   42 +
 arch/arm/plat-omap/include/plat/omap44xx.h    |    9 +
 drivers/media/video/Kconfig                   |   25 +
 drivers/media/video/Makefile                  |    3 +
 drivers/media/video/omap4iss/Makefile         |    6 +
 drivers/media/video/omap4iss/iss.c            | 1179 ++++++++++++++++++++++
 drivers/media/video/omap4iss/iss.h            |  133 +++
 drivers/media/video/omap4iss/iss_csi2.c       | 1324 +++++++++++++++++++++++++
 drivers/media/video/omap4iss/iss_csi2.h       |  166 +++
 drivers/media/video/omap4iss/iss_csiphy.c     |  215 ++++
 drivers/media/video/omap4iss/iss_csiphy.h     |   69 ++
 drivers/media/video/omap4iss/iss_regs.h       |  238 +++++
 drivers/media/video/omap4iss/iss_video.c      | 1192 ++++++++++++++++++++++
 drivers/media/video/omap4iss/iss_video.h      |  205 ++++
 drivers/media/video/ov5640.c                  |  972 ++++++++++++++++++
 drivers/media/video/ov5650.c                  |  524 ++++++++++
 drivers/mfd/twl-core.c                        |    2 +-
 include/linux/mfd/twl6040.h                   |    2 +-
 include/media/ov5640.h                        |   10 +
 include/media/ov5650.h                        |   10 +
 include/media/v4l2-chip-ident.h               |    2 +
 include/media/v4l2-subdev.h                   |   42 +
 29 files changed, 6957 insertions(+), 5 deletions(-)
 create mode 100644 Documentation/video4linux/omap4_camera.txt
 create mode 100644 arch/arm/mach-omap2/board-4430sdp-camera.c
 create mode 100644 arch/arm/mach-omap2/board-omap4panda-camera.c
 create mode 100644 arch/arm/plat-omap/include/plat/omap4-iss.h
 create mode 100644 drivers/media/video/omap4iss/Makefile
 create mode 100644 drivers/media/video/omap4iss/iss.c
 create mode 100644 drivers/media/video/omap4iss/iss.h
 create mode 100644 drivers/media/video/omap4iss/iss_csi2.c
 create mode 100644 drivers/media/video/omap4iss/iss_csi2.h
 create mode 100644 drivers/media/video/omap4iss/iss_csiphy.c
 create mode 100644 drivers/media/video/omap4iss/iss_csiphy.h
 create mode 100644 drivers/media/video/omap4iss/iss_regs.h
 create mode 100644 drivers/media/video/omap4iss/iss_video.c
 create mode 100644 drivers/media/video/omap4iss/iss_video.h
 create mode 100644 drivers/media/video/ov5640.c
 create mode 100644 drivers/media/video/ov5650.c
 create mode 100644 include/media/ov5640.h
 create mode 100644 include/media/ov5650.h

-- 
1.7.7.4


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

* [PATCH v2 00/11] v4l2: OMAP4 ISS driver + Sensor + Board support
@ 2011-12-01  0:14 ` Sergio Aguirre
  0 siblings, 0 replies; 65+ messages in thread
From: Sergio Aguirre @ 2011-12-01  0:14 UTC (permalink / raw)
  To: linux-media; +Cc: linux-omap, laurent.pinchart, sakari.ailus, Sergio Aguirre

Hi everyone,

This is the second version of the OMAP4 ISS driver,
now ported to the Media Controller framework AND supporting
videobuf2 framework.

This patchset should apply cleanly on top of v3.2-rc3 kernel tag.

This driver attempts to provide an fully open source solution to
control the OMAP4 Imaging SubSystem (a.k.a. ISS).

Starts with just CSI2-A interface support, and pretends to be
ready for expansion to add support to the many ISS block modules
as possible.

Please see newly added documentation for more details:

Documentation/video4linux/omap4_camera.txt

Any comments/complaints are welcome. :)

Changes since v1:
- Simplification of auxclk handling in board files. (Pointed out by: Roger Quadros)
- Cleanup of Camera support enablement for 4430sdp & panda. (Pointed out by: Roger Quadros)
- Use of HWMOD declaration for assisted platform_device creation. (Pointed out by: Felipe Balbi)
- Videobuf2 migration (Removal of custom iss_queue buffer handling driver)
- Proper GPO3 handling for CAM_SEL in 4430sdp.

Sergio Aguirre (10):
  TWL6030: Add mapping for auxiliary regs
  mfd: twl6040: Fix wrong TWL6040_GPO3 bitfield value
  OMAP4: hwmod: Include CSI2A and CSIPHY1 memory sections
  OMAP4: Add base addresses for ISS
  v4l: Add support for omap4iss driver
  v4l: Add support for ov5640 sensor
  v4l: Add support for ov5650 sensor
  arm: omap4430sdp: Add support for omap4iss camera
  arm: omap4panda: Add support for omap4iss camera
  arm: Add support for CMA for omap4iss driver

Stanimir Varbanov (1):
  v4l: Introduce sensor operation for getting interface configuration

 Documentation/video4linux/omap4_camera.txt    |   60 ++
 arch/arm/mach-omap2/Kconfig                   |   54 +
 arch/arm/mach-omap2/Makefile                  |    3 +
 arch/arm/mach-omap2/board-4430sdp-camera.c    |  221 ++++
 arch/arm/mach-omap2/board-omap4panda-camera.c |  198 ++++
 arch/arm/mach-omap2/devices.c                 |   40 +
 arch/arm/mach-omap2/omap_hwmod_44xx_data.c    |   16 +-
 arch/arm/plat-omap/include/plat/omap4-iss.h   |   42 +
 arch/arm/plat-omap/include/plat/omap44xx.h    |    9 +
 drivers/media/video/Kconfig                   |   25 +
 drivers/media/video/Makefile                  |    3 +
 drivers/media/video/omap4iss/Makefile         |    6 +
 drivers/media/video/omap4iss/iss.c            | 1179 ++++++++++++++++++++++
 drivers/media/video/omap4iss/iss.h            |  133 +++
 drivers/media/video/omap4iss/iss_csi2.c       | 1324 +++++++++++++++++++++++++
 drivers/media/video/omap4iss/iss_csi2.h       |  166 +++
 drivers/media/video/omap4iss/iss_csiphy.c     |  215 ++++
 drivers/media/video/omap4iss/iss_csiphy.h     |   69 ++
 drivers/media/video/omap4iss/iss_regs.h       |  238 +++++
 drivers/media/video/omap4iss/iss_video.c      | 1192 ++++++++++++++++++++++
 drivers/media/video/omap4iss/iss_video.h      |  205 ++++
 drivers/media/video/ov5640.c                  |  972 ++++++++++++++++++
 drivers/media/video/ov5650.c                  |  524 ++++++++++
 drivers/mfd/twl-core.c                        |    2 +-
 include/linux/mfd/twl6040.h                   |    2 +-
 include/media/ov5640.h                        |   10 +
 include/media/ov5650.h                        |   10 +
 include/media/v4l2-chip-ident.h               |    2 +
 include/media/v4l2-subdev.h                   |   42 +
 29 files changed, 6957 insertions(+), 5 deletions(-)
 create mode 100644 Documentation/video4linux/omap4_camera.txt
 create mode 100644 arch/arm/mach-omap2/board-4430sdp-camera.c
 create mode 100644 arch/arm/mach-omap2/board-omap4panda-camera.c
 create mode 100644 arch/arm/plat-omap/include/plat/omap4-iss.h
 create mode 100644 drivers/media/video/omap4iss/Makefile
 create mode 100644 drivers/media/video/omap4iss/iss.c
 create mode 100644 drivers/media/video/omap4iss/iss.h
 create mode 100644 drivers/media/video/omap4iss/iss_csi2.c
 create mode 100644 drivers/media/video/omap4iss/iss_csi2.h
 create mode 100644 drivers/media/video/omap4iss/iss_csiphy.c
 create mode 100644 drivers/media/video/omap4iss/iss_csiphy.h
 create mode 100644 drivers/media/video/omap4iss/iss_regs.h
 create mode 100644 drivers/media/video/omap4iss/iss_video.c
 create mode 100644 drivers/media/video/omap4iss/iss_video.h
 create mode 100644 drivers/media/video/ov5640.c
 create mode 100644 drivers/media/video/ov5650.c
 create mode 100644 include/media/ov5640.h
 create mode 100644 include/media/ov5650.h

-- 
1.7.7.4


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

* [PATCH v2 01/11] TWL6030: Add mapping for auxiliary regs
  2011-12-01  0:14 ` Sergio Aguirre
@ 2011-12-01  0:14   ` Sergio Aguirre
  -1 siblings, 0 replies; 65+ messages in thread
From: Sergio Aguirre @ 2011-12-01  0:14 UTC (permalink / raw)
  To: linux-media; +Cc: linux-omap, laurent.pinchart, sakari.ailus, Sergio Aguirre

Signed-off-by: Sergio Aguirre <saaguirre@ti.com>
---
 drivers/mfd/twl-core.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c
index bfbd660..e26b564 100644
--- a/drivers/mfd/twl-core.c
+++ b/drivers/mfd/twl-core.c
@@ -323,7 +323,7 @@ static struct twl_mapping twl6030_map[] = {
 	{ SUB_CHIP_ID0, TWL6030_BASEADD_ZERO },
 	{ SUB_CHIP_ID1, TWL6030_BASEADD_ZERO },
 
-	{ SUB_CHIP_ID2, TWL6030_BASEADD_ZERO },
+	{ SUB_CHIP_ID1, TWL6030_BASEADD_AUX },
 	{ SUB_CHIP_ID2, TWL6030_BASEADD_ZERO },
 	{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
 	{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
-- 
1.7.7.4


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

* [PATCH v2 01/11] TWL6030: Add mapping for auxiliary regs
@ 2011-12-01  0:14   ` Sergio Aguirre
  0 siblings, 0 replies; 65+ messages in thread
From: Sergio Aguirre @ 2011-12-01  0:14 UTC (permalink / raw)
  To: linux-media; +Cc: linux-omap, laurent.pinchart, sakari.ailus, Sergio Aguirre

Signed-off-by: Sergio Aguirre <saaguirre@ti.com>
---
 drivers/mfd/twl-core.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c
index bfbd660..e26b564 100644
--- a/drivers/mfd/twl-core.c
+++ b/drivers/mfd/twl-core.c
@@ -323,7 +323,7 @@ static struct twl_mapping twl6030_map[] = {
 	{ SUB_CHIP_ID0, TWL6030_BASEADD_ZERO },
 	{ SUB_CHIP_ID1, TWL6030_BASEADD_ZERO },
 
-	{ SUB_CHIP_ID2, TWL6030_BASEADD_ZERO },
+	{ SUB_CHIP_ID1, TWL6030_BASEADD_AUX },
 	{ SUB_CHIP_ID2, TWL6030_BASEADD_ZERO },
 	{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
 	{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
-- 
1.7.7.4


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

* [PATCH v2 02/11] mfd: twl6040: Fix wrong TWL6040_GPO3 bitfield value
  2011-12-01  0:14 ` Sergio Aguirre
@ 2011-12-01  0:14   ` Sergio Aguirre
  -1 siblings, 0 replies; 65+ messages in thread
From: Sergio Aguirre @ 2011-12-01  0:14 UTC (permalink / raw)
  To: linux-media; +Cc: linux-omap, laurent.pinchart, sakari.ailus, Sergio Aguirre

The define should be the result of 1 << Bit number.

Bit number for GPOCTL.GPO3 field is 2, which results
in 0x4 value.

Signed-off-by: Sergio Aguirre <saaguirre@ti.com>
---
 include/linux/mfd/twl6040.h |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/include/linux/mfd/twl6040.h b/include/linux/mfd/twl6040.h
index 2463c261..2a7ff16 100644
--- a/include/linux/mfd/twl6040.h
+++ b/include/linux/mfd/twl6040.h
@@ -142,7 +142,7 @@
 
 #define TWL6040_GPO1			0x01
 #define TWL6040_GPO2			0x02
-#define TWL6040_GPO3			0x03
+#define TWL6040_GPO3			0x04
 
 /* ACCCTL (0x2D) fields */
 
-- 
1.7.7.4


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

* [PATCH v2 02/11] mfd: twl6040: Fix wrong TWL6040_GPO3 bitfield value
@ 2011-12-01  0:14   ` Sergio Aguirre
  0 siblings, 0 replies; 65+ messages in thread
From: Sergio Aguirre @ 2011-12-01  0:14 UTC (permalink / raw)
  To: linux-media; +Cc: linux-omap, laurent.pinchart, sakari.ailus, Sergio Aguirre

The define should be the result of 1 << Bit number.

Bit number for GPOCTL.GPO3 field is 2, which results
in 0x4 value.

Signed-off-by: Sergio Aguirre <saaguirre@ti.com>
---
 include/linux/mfd/twl6040.h |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/include/linux/mfd/twl6040.h b/include/linux/mfd/twl6040.h
index 2463c261..2a7ff16 100644
--- a/include/linux/mfd/twl6040.h
+++ b/include/linux/mfd/twl6040.h
@@ -142,7 +142,7 @@
 
 #define TWL6040_GPO1			0x01
 #define TWL6040_GPO2			0x02
-#define TWL6040_GPO3			0x03
+#define TWL6040_GPO3			0x04
 
 /* ACCCTL (0x2D) fields */
 
-- 
1.7.7.4

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

* [PATCH v2 03/11] v4l: Introduce sensor operation for getting interface configuration
  2011-12-01  0:14 ` Sergio Aguirre
@ 2011-12-01  0:14   ` Sergio Aguirre
  -1 siblings, 0 replies; 65+ messages in thread
From: Sergio Aguirre @ 2011-12-01  0:14 UTC (permalink / raw)
  To: linux-media; +Cc: linux-omap, laurent.pinchart, sakari.ailus, Stanimir Varbanov

From: Stanimir Varbanov <svarbanov@mm-sol.com>

Introduce g_interface_parms sensor operation for getting sensor
interface parameters. These parameters are needed from the host side
to determine it's own configuration.

Signed-off-by: Stanimir Varbanov <svarbanov@mm-sol.com>
---
 include/media/v4l2-subdev.h |   42 ++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 42 insertions(+), 0 deletions(-)

diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h
index f0f3358..0d322ed 100644
--- a/include/media/v4l2-subdev.h
+++ b/include/media/v4l2-subdev.h
@@ -362,6 +362,42 @@ struct v4l2_subdev_vbi_ops {
 	int (*s_sliced_fmt)(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt);
 };
 
+/* Which interface the sensor use to provide it's image data */
+enum v4l2_subdev_sensor_iface {
+	V4L2_SUBDEV_SENSOR_PARALLEL,
+	V4L2_SUBDEV_SENSOR_SERIAL,
+};
+
+/* Each interface could use the following modes */
+/* Image sensor provides horizontal and vertical sync signals */
+#define V4L2_SUBDEV_SENSOR_MODE_PARALLEL_SYNC	0
+/* BT.656 interface. Embedded sync */
+#define V4L2_SUBDEV_SENSOR_MODE_PARALLEL_ITU	1
+/* MIPI CSI1 */
+#define V4L2_SUBDEV_SENSOR_MODE_SERIAL_CSI1	2
+/* MIPI CSI2 */
+#define V4L2_SUBDEV_SENSOR_MODE_SERIAL_CSI2	3
+
+struct v4l2_subdev_sensor_serial_parms {
+	unsigned char lanes;		/* number of lanes used */
+	unsigned char channel;		/* virtual channel */
+	unsigned int phy_rate;		/* output rate at CSI phy in bps */
+	unsigned int pix_clk;		/* pixel clock in Hz */
+};
+
+struct v4l2_subdev_sensor_parallel_parms {
+	unsigned int pix_clk;		/* pixel clock in Hz */
+};
+
+struct v4l2_subdev_sensor_interface_parms {
+	enum v4l2_subdev_sensor_iface if_type;
+	unsigned int if_mode;
+	union {
+		struct v4l2_subdev_sensor_serial_parms serial;
+		struct v4l2_subdev_sensor_parallel_parms parallel;
+	} parms;
+};
+
 /**
  * struct v4l2_subdev_sensor_ops - v4l2-subdev sensor operations
  * @g_skip_top_lines: number of lines at the top of the image to be skipped.
@@ -371,10 +407,16 @@ struct v4l2_subdev_vbi_ops {
  * @g_skip_frames: number of frames to skip at stream start. This is needed for
  *		   buggy sensors that generate faulty frames when they are
  *		   turned on.
+ * @g_interface_parms: get sensor interface parameters. The sensor subdev should
+ *		       fill this structure with current interface params. These
+ *		       interface parameters are needed on host side to configure
+ *		       it's own hardware receivers.
  */
 struct v4l2_subdev_sensor_ops {
 	int (*g_skip_top_lines)(struct v4l2_subdev *sd, u32 *lines);
 	int (*g_skip_frames)(struct v4l2_subdev *sd, u32 *frames);
+	int (*g_interface_parms)(struct v4l2_subdev *sd,
+			struct v4l2_subdev_sensor_interface_parms *parms);
 };
 
 /*
-- 
1.7.7.4


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

* [PATCH v2 03/11] v4l: Introduce sensor operation for getting interface configuration
@ 2011-12-01  0:14   ` Sergio Aguirre
  0 siblings, 0 replies; 65+ messages in thread
From: Sergio Aguirre @ 2011-12-01  0:14 UTC (permalink / raw)
  To: linux-media; +Cc: linux-omap, laurent.pinchart, sakari.ailus, Stanimir Varbanov

From: Stanimir Varbanov <svarbanov@mm-sol.com>

Introduce g_interface_parms sensor operation for getting sensor
interface parameters. These parameters are needed from the host side
to determine it's own configuration.

Signed-off-by: Stanimir Varbanov <svarbanov@mm-sol.com>
---
 include/media/v4l2-subdev.h |   42 ++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 42 insertions(+), 0 deletions(-)

diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h
index f0f3358..0d322ed 100644
--- a/include/media/v4l2-subdev.h
+++ b/include/media/v4l2-subdev.h
@@ -362,6 +362,42 @@ struct v4l2_subdev_vbi_ops {
 	int (*s_sliced_fmt)(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt);
 };
 
+/* Which interface the sensor use to provide it's image data */
+enum v4l2_subdev_sensor_iface {
+	V4L2_SUBDEV_SENSOR_PARALLEL,
+	V4L2_SUBDEV_SENSOR_SERIAL,
+};
+
+/* Each interface could use the following modes */
+/* Image sensor provides horizontal and vertical sync signals */
+#define V4L2_SUBDEV_SENSOR_MODE_PARALLEL_SYNC	0
+/* BT.656 interface. Embedded sync */
+#define V4L2_SUBDEV_SENSOR_MODE_PARALLEL_ITU	1
+/* MIPI CSI1 */
+#define V4L2_SUBDEV_SENSOR_MODE_SERIAL_CSI1	2
+/* MIPI CSI2 */
+#define V4L2_SUBDEV_SENSOR_MODE_SERIAL_CSI2	3
+
+struct v4l2_subdev_sensor_serial_parms {
+	unsigned char lanes;		/* number of lanes used */
+	unsigned char channel;		/* virtual channel */
+	unsigned int phy_rate;		/* output rate at CSI phy in bps */
+	unsigned int pix_clk;		/* pixel clock in Hz */
+};
+
+struct v4l2_subdev_sensor_parallel_parms {
+	unsigned int pix_clk;		/* pixel clock in Hz */
+};
+
+struct v4l2_subdev_sensor_interface_parms {
+	enum v4l2_subdev_sensor_iface if_type;
+	unsigned int if_mode;
+	union {
+		struct v4l2_subdev_sensor_serial_parms serial;
+		struct v4l2_subdev_sensor_parallel_parms parallel;
+	} parms;
+};
+
 /**
  * struct v4l2_subdev_sensor_ops - v4l2-subdev sensor operations
  * @g_skip_top_lines: number of lines at the top of the image to be skipped.
@@ -371,10 +407,16 @@ struct v4l2_subdev_vbi_ops {
  * @g_skip_frames: number of frames to skip at stream start. This is needed for
  *		   buggy sensors that generate faulty frames when they are
  *		   turned on.
+ * @g_interface_parms: get sensor interface parameters. The sensor subdev should
+ *		       fill this structure with current interface params. These
+ *		       interface parameters are needed on host side to configure
+ *		       it's own hardware receivers.
  */
 struct v4l2_subdev_sensor_ops {
 	int (*g_skip_top_lines)(struct v4l2_subdev *sd, u32 *lines);
 	int (*g_skip_frames)(struct v4l2_subdev *sd, u32 *frames);
+	int (*g_interface_parms)(struct v4l2_subdev *sd,
+			struct v4l2_subdev_sensor_interface_parms *parms);
 };
 
 /*
-- 
1.7.7.4


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

* [PATCH v2 04/11] OMAP4: hwmod: Include CSI2A and CSIPHY1 memory sections
  2011-12-01  0:14 ` Sergio Aguirre
@ 2011-12-01  0:14   ` Sergio Aguirre
  -1 siblings, 0 replies; 65+ messages in thread
From: Sergio Aguirre @ 2011-12-01  0:14 UTC (permalink / raw)
  To: linux-media; +Cc: linux-omap, laurent.pinchart, sakari.ailus, Sergio Aguirre

Signed-off-by: Sergio Aguirre <saaguirre@ti.com>
---
 arch/arm/mach-omap2/omap_hwmod_44xx_data.c |   16 +++++++++++++---
 1 files changed, 13 insertions(+), 3 deletions(-)

diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
index 7695e5d..1b59e2f 100644
--- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
@@ -2623,8 +2623,18 @@ static struct omap_hwmod_ocp_if *omap44xx_iss_masters[] = {
 
 static struct omap_hwmod_addr_space omap44xx_iss_addrs[] = {
 	{
-		.pa_start	= 0x52000000,
-		.pa_end		= 0x520000ff,
+		.pa_start	= OMAP44XX_ISS_TOP_BASE,
+		.pa_end		= OMAP44XX_ISS_TOP_END,
+		.flags		= ADDR_TYPE_RT
+	},
+	{
+		.pa_start	= OMAP44XX_ISS_CSI2_A_REGS1_BASE,
+		.pa_end		= OMAP44XX_ISS_CSI2_A_REGS1_END,
+		.flags		= ADDR_TYPE_RT
+	},
+	{
+		.pa_start	= OMAP44XX_ISS_CAMERARX_CORE1_BASE,
+		.pa_end		= OMAP44XX_ISS_CAMERARX_CORE1_END,
 		.flags		= ADDR_TYPE_RT
 	},
 	{ }
@@ -5350,7 +5360,7 @@ static __initdata struct omap_hwmod *omap44xx_hwmods[] = {
 	&omap44xx_ipu_c1_hwmod,
 
 	/* iss class */
-/*	&omap44xx_iss_hwmod, */
+	&omap44xx_iss_hwmod,
 
 	/* iva class */
 	&omap44xx_iva_hwmod,
-- 
1.7.7.4


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

* [PATCH v2 04/11] OMAP4: hwmod: Include CSI2A and CSIPHY1 memory sections
@ 2011-12-01  0:14   ` Sergio Aguirre
  0 siblings, 0 replies; 65+ messages in thread
From: Sergio Aguirre @ 2011-12-01  0:14 UTC (permalink / raw)
  To: linux-media; +Cc: linux-omap, laurent.pinchart, sakari.ailus, Sergio Aguirre

Signed-off-by: Sergio Aguirre <saaguirre@ti.com>
---
 arch/arm/mach-omap2/omap_hwmod_44xx_data.c |   16 +++++++++++++---
 1 files changed, 13 insertions(+), 3 deletions(-)

diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
index 7695e5d..1b59e2f 100644
--- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
@@ -2623,8 +2623,18 @@ static struct omap_hwmod_ocp_if *omap44xx_iss_masters[] = {
 
 static struct omap_hwmod_addr_space omap44xx_iss_addrs[] = {
 	{
-		.pa_start	= 0x52000000,
-		.pa_end		= 0x520000ff,
+		.pa_start	= OMAP44XX_ISS_TOP_BASE,
+		.pa_end		= OMAP44XX_ISS_TOP_END,
+		.flags		= ADDR_TYPE_RT
+	},
+	{
+		.pa_start	= OMAP44XX_ISS_CSI2_A_REGS1_BASE,
+		.pa_end		= OMAP44XX_ISS_CSI2_A_REGS1_END,
+		.flags		= ADDR_TYPE_RT
+	},
+	{
+		.pa_start	= OMAP44XX_ISS_CAMERARX_CORE1_BASE,
+		.pa_end		= OMAP44XX_ISS_CAMERARX_CORE1_END,
 		.flags		= ADDR_TYPE_RT
 	},
 	{ }
@@ -5350,7 +5360,7 @@ static __initdata struct omap_hwmod *omap44xx_hwmods[] = {
 	&omap44xx_ipu_c1_hwmod,
 
 	/* iss class */
-/*	&omap44xx_iss_hwmod, */
+	&omap44xx_iss_hwmod,
 
 	/* iva class */
 	&omap44xx_iva_hwmod,
-- 
1.7.7.4

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

* [PATCH v2 05/11] OMAP4: Add base addresses for ISS
  2011-12-01  0:14 ` Sergio Aguirre
@ 2011-12-01  0:14   ` Sergio Aguirre
  -1 siblings, 0 replies; 65+ messages in thread
From: Sergio Aguirre @ 2011-12-01  0:14 UTC (permalink / raw)
  To: linux-media; +Cc: linux-omap, laurent.pinchart, sakari.ailus, Sergio Aguirre

NOTE: This isn't the whole list of features that the
ISS supports, but the only ones supported at the moment.

Signed-off-by: Sergio Aguirre <saaguirre@ti.com>
---
 arch/arm/mach-omap2/devices.c              |   32 ++++++++++++++++++++++++++++
 arch/arm/plat-omap/include/plat/omap44xx.h |    9 +++++++
 2 files changed, 41 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-omap2/devices.c b/arch/arm/mach-omap2/devices.c
index c15cfad..b48aeea 100644
--- a/arch/arm/mach-omap2/devices.c
+++ b/arch/arm/mach-omap2/devices.c
@@ -32,6 +32,7 @@
 #include <plat/omap_hwmod.h>
 #include <plat/omap_device.h>
 #include <plat/omap4-keypad.h>
+#include <plat/omap4-iss.h>
 
 #include "mux.h"
 #include "control.h"
@@ -217,6 +218,37 @@ int omap3_init_camera(struct isp_platform_data *pdata)
 	return platform_device_register(&omap3isp_device);
 }
 
+int omap4_init_camera(struct iss_platform_data *pdata, struct omap_board_data *bdata)
+{
+	struct platform_device *pdev;
+	struct omap_hwmod *oh;
+	struct iss_platform_data *omap4iss_pdata;
+	char *oh_name = "iss";
+	char *name = "omap4iss";
+	unsigned int id = -1;
+
+	oh = omap_hwmod_lookup(oh_name);
+	if (!oh) {
+		pr_err("Could not look up %s\n", oh_name);
+		return -ENODEV;
+	}
+
+	omap4iss_pdata = pdata;
+
+	pdev = omap_device_build(name, id, oh, omap4iss_pdata,
+			sizeof(struct iss_platform_data), NULL, 0, 0);
+
+	if (IS_ERR(pdev)) {
+		WARN(1, "Can't build omap_device for %s:%s.\n",
+						name, oh->name);
+		return PTR_ERR(pdev);
+	}
+
+	oh->mux = omap_hwmod_mux_init(bdata->pads, bdata->pads_cnt);
+
+	return 0;
+}
+
 static inline void omap_init_camera(void)
 {
 #if defined(CONFIG_VIDEO_OMAP2) || defined(CONFIG_VIDEO_OMAP2_MODULE)
diff --git a/arch/arm/plat-omap/include/plat/omap44xx.h b/arch/arm/plat-omap/include/plat/omap44xx.h
index ea2b8a6..31432aa 100644
--- a/arch/arm/plat-omap/include/plat/omap44xx.h
+++ b/arch/arm/plat-omap/include/plat/omap44xx.h
@@ -49,6 +49,15 @@
 #define OMAP44XX_MAILBOX_BASE		(L4_44XX_BASE + 0xF4000)
 #define OMAP44XX_HSUSB_OTG_BASE		(L4_44XX_BASE + 0xAB000)
 
+#define OMAP44XX_ISS_BASE			0x52000000
+#define OMAP44XX_ISS_TOP_BASE			(OMAP44XX_ISS_BASE + 0x0)
+#define OMAP44XX_ISS_CSI2_A_REGS1_BASE		(OMAP44XX_ISS_BASE + 0x1000)
+#define OMAP44XX_ISS_CAMERARX_CORE1_BASE	(OMAP44XX_ISS_BASE + 0x1170)
+
+#define OMAP44XX_ISS_TOP_END			(OMAP44XX_ISS_TOP_BASE + 256 - 1)
+#define OMAP44XX_ISS_CSI2_A_REGS1_END		(OMAP44XX_ISS_CSI2_A_REGS1_BASE + 368 - 1)
+#define OMAP44XX_ISS_CAMERARX_CORE1_END		(OMAP44XX_ISS_CAMERARX_CORE1_BASE + 32 - 1)
+
 #define OMAP4_MMU1_BASE			0x55082000
 #define OMAP4_MMU2_BASE			0x4A066000
 
-- 
1.7.7.4


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

* [PATCH v2 05/11] OMAP4: Add base addresses for ISS
@ 2011-12-01  0:14   ` Sergio Aguirre
  0 siblings, 0 replies; 65+ messages in thread
From: Sergio Aguirre @ 2011-12-01  0:14 UTC (permalink / raw)
  To: linux-media; +Cc: linux-omap, laurent.pinchart, sakari.ailus, Sergio Aguirre

NOTE: This isn't the whole list of features that the
ISS supports, but the only ones supported at the moment.

Signed-off-by: Sergio Aguirre <saaguirre@ti.com>
---
 arch/arm/mach-omap2/devices.c              |   32 ++++++++++++++++++++++++++++
 arch/arm/plat-omap/include/plat/omap44xx.h |    9 +++++++
 2 files changed, 41 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-omap2/devices.c b/arch/arm/mach-omap2/devices.c
index c15cfad..b48aeea 100644
--- a/arch/arm/mach-omap2/devices.c
+++ b/arch/arm/mach-omap2/devices.c
@@ -32,6 +32,7 @@
 #include <plat/omap_hwmod.h>
 #include <plat/omap_device.h>
 #include <plat/omap4-keypad.h>
+#include <plat/omap4-iss.h>
 
 #include "mux.h"
 #include "control.h"
@@ -217,6 +218,37 @@ int omap3_init_camera(struct isp_platform_data *pdata)
 	return platform_device_register(&omap3isp_device);
 }
 
+int omap4_init_camera(struct iss_platform_data *pdata, struct omap_board_data *bdata)
+{
+	struct platform_device *pdev;
+	struct omap_hwmod *oh;
+	struct iss_platform_data *omap4iss_pdata;
+	char *oh_name = "iss";
+	char *name = "omap4iss";
+	unsigned int id = -1;
+
+	oh = omap_hwmod_lookup(oh_name);
+	if (!oh) {
+		pr_err("Could not look up %s\n", oh_name);
+		return -ENODEV;
+	}
+
+	omap4iss_pdata = pdata;
+
+	pdev = omap_device_build(name, id, oh, omap4iss_pdata,
+			sizeof(struct iss_platform_data), NULL, 0, 0);
+
+	if (IS_ERR(pdev)) {
+		WARN(1, "Can't build omap_device for %s:%s.\n",
+						name, oh->name);
+		return PTR_ERR(pdev);
+	}
+
+	oh->mux = omap_hwmod_mux_init(bdata->pads, bdata->pads_cnt);
+
+	return 0;
+}
+
 static inline void omap_init_camera(void)
 {
 #if defined(CONFIG_VIDEO_OMAP2) || defined(CONFIG_VIDEO_OMAP2_MODULE)
diff --git a/arch/arm/plat-omap/include/plat/omap44xx.h b/arch/arm/plat-omap/include/plat/omap44xx.h
index ea2b8a6..31432aa 100644
--- a/arch/arm/plat-omap/include/plat/omap44xx.h
+++ b/arch/arm/plat-omap/include/plat/omap44xx.h
@@ -49,6 +49,15 @@
 #define OMAP44XX_MAILBOX_BASE		(L4_44XX_BASE + 0xF4000)
 #define OMAP44XX_HSUSB_OTG_BASE		(L4_44XX_BASE + 0xAB000)
 
+#define OMAP44XX_ISS_BASE			0x52000000
+#define OMAP44XX_ISS_TOP_BASE			(OMAP44XX_ISS_BASE + 0x0)
+#define OMAP44XX_ISS_CSI2_A_REGS1_BASE		(OMAP44XX_ISS_BASE + 0x1000)
+#define OMAP44XX_ISS_CAMERARX_CORE1_BASE	(OMAP44XX_ISS_BASE + 0x1170)
+
+#define OMAP44XX_ISS_TOP_END			(OMAP44XX_ISS_TOP_BASE + 256 - 1)
+#define OMAP44XX_ISS_CSI2_A_REGS1_END		(OMAP44XX_ISS_CSI2_A_REGS1_BASE + 368 - 1)
+#define OMAP44XX_ISS_CAMERARX_CORE1_END		(OMAP44XX_ISS_CAMERARX_CORE1_BASE + 32 - 1)
+
 #define OMAP4_MMU1_BASE			0x55082000
 #define OMAP4_MMU2_BASE			0x4A066000
 
-- 
1.7.7.4


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

* [PATCH v2 06/11] v4l: Add support for omap4iss driver
  2011-12-01  0:14 ` Sergio Aguirre
@ 2011-12-01  0:14   ` Sergio Aguirre
  -1 siblings, 0 replies; 65+ messages in thread
From: Sergio Aguirre @ 2011-12-01  0:14 UTC (permalink / raw)
  To: linux-media; +Cc: linux-omap, laurent.pinchart, sakari.ailus, Sergio Aguirre

This adds a very simplistic driver to utilize the
CSI2A interface inside the ISS subsystem in OMAP4,
and dump the data to memory.

Tested so far on omap4430sdp w/ ES2.1 GP & w/ ES2.2 EMU.

Check newly added Documentation/video4linux/omap4_camera.txt
for details.

Signed-off-by: Sergio Aguirre <saaguirre@ti.com>
---
 Documentation/video4linux/omap4_camera.txt  |   60 ++
 arch/arm/plat-omap/include/plat/omap4-iss.h |   42 +
 drivers/media/video/Kconfig                 |   13 +
 drivers/media/video/Makefile                |    1 +
 drivers/media/video/omap4iss/Makefile       |    6 +
 drivers/media/video/omap4iss/iss.c          | 1179 ++++++++++++++++++++++++
 drivers/media/video/omap4iss/iss.h          |  133 +++
 drivers/media/video/omap4iss/iss_csi2.c     | 1324 +++++++++++++++++++++++++++
 drivers/media/video/omap4iss/iss_csi2.h     |  166 ++++
 drivers/media/video/omap4iss/iss_csiphy.c   |  215 +++++
 drivers/media/video/omap4iss/iss_csiphy.h   |   69 ++
 drivers/media/video/omap4iss/iss_regs.h     |  238 +++++
 drivers/media/video/omap4iss/iss_video.c    | 1192 ++++++++++++++++++++++++
 drivers/media/video/omap4iss/iss_video.h    |  205 +++++
 14 files changed, 4843 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/video4linux/omap4_camera.txt
 create mode 100644 arch/arm/plat-omap/include/plat/omap4-iss.h
 create mode 100644 drivers/media/video/omap4iss/Makefile
 create mode 100644 drivers/media/video/omap4iss/iss.c
 create mode 100644 drivers/media/video/omap4iss/iss.h
 create mode 100644 drivers/media/video/omap4iss/iss_csi2.c
 create mode 100644 drivers/media/video/omap4iss/iss_csi2.h
 create mode 100644 drivers/media/video/omap4iss/iss_csiphy.c
 create mode 100644 drivers/media/video/omap4iss/iss_csiphy.h
 create mode 100644 drivers/media/video/omap4iss/iss_regs.h
 create mode 100644 drivers/media/video/omap4iss/iss_video.c
 create mode 100644 drivers/media/video/omap4iss/iss_video.h

diff --git a/Documentation/video4linux/omap4_camera.txt b/Documentation/video4linux/omap4_camera.txt
new file mode 100644
index 0000000..a60c80f
--- /dev/null
+++ b/Documentation/video4linux/omap4_camera.txt
@@ -0,0 +1,60 @@
+                              OMAP4 ISS Driver
+                              ================
+
+Introduction
+------------
+
+The OMAP44XX family of chips contains the Imaging SubSystem (a.k.a. ISS),
+Which contains several components that can be categorized in 3 big groups:
+
+- Interfaces (2 Interfaces: CSI2-A & CSI2-B/CCP2)
+- ISP (Image Signal Processor)
+- SIMCOP (Still Image Coprocessor)
+
+For more information, please look in [1] for latest version of:
+	"OMAP4430 Multimedia Device Silicon Revision 2.x"
+
+As of Revision L, the ISS is described in detail in section 8.
+
+This driver is supporting _only_ the CSI2-A interface for now.
+
+It makes use of the Media Controller framework [2], and inherited most of the
+code from OMAP3 ISP driver (found under drivers/media/video/omap3isp/*), except
+that it doesn't need an IOMMU now for ISS buffers memory mapping.
+
+Supports usage of MMAP buffers only (for now).
+
+IMPORTANT: It is recommended to have this patchset:
+  Contiguous Memory Allocator (v15) [3].
+
+Tested platforms
+----------------
+
+- OMAP4430SDP, w/ ES2.1 GP & SEVM4430-CAM-V1-0 (Contains IMX060 & OV5640, in
+  which only the last one is supported, outputting YUV422 frames).
+
+- PandaBoard, Rev. A2, w/ OMAP4430 ES2.1 GP & OV adapter board, tested with
+  following sensors:
+  * OV5640
+  * OV5650
+
+- Tested on mainline kernel:
+
+	http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=summary
+
+  Tag: v3.0 (commit 02f8c6aee8df3cdc935e9bdd4f2d020306035dbe)
+
+File list
+---------
+drivers/media/video/omap4iss/
+
+References
+----------
+
+[1] http://focus.ti.com/general/docs/wtbu/wtbudocumentcenter.tsp?navigationId=12037&templateId=6123#62
+[2] http://lwn.net/Articles/420485/
+[3] http://lwn.net/Articles/455801/
+--
+Author: Sergio Aguirre <saaguirre@ti.com>
+Copyright (C) 2011, Texas Instruments
+
diff --git a/arch/arm/plat-omap/include/plat/omap4-iss.h b/arch/arm/plat-omap/include/plat/omap4-iss.h
new file mode 100644
index 0000000..3a1c6b6
--- /dev/null
+++ b/arch/arm/plat-omap/include/plat/omap4-iss.h
@@ -0,0 +1,42 @@
+#ifndef ARCH_ARM_PLAT_OMAP4_ISS_H
+#define ARCH_ARM_PLAT_OMAP4_ISS_H
+
+#include <linux/i2c.h>
+
+struct iss_device;
+
+enum iss_interface_type {
+	ISS_INTERFACE_CSI2A_PHY1,
+};
+
+/**
+ * struct iss_csi2_platform_data - CSI2 interface platform data
+ * @crc: Enable the cyclic redundancy check
+ * @vpclk_div: Video port output clock control
+ */
+struct iss_csi2_platform_data {
+	unsigned crc:1;
+	unsigned vpclk_div:2;
+};
+
+struct iss_subdev_i2c_board_info {
+	struct i2c_board_info *board_info;
+	int i2c_adapter_id;
+};
+
+struct iss_v4l2_subdevs_group {
+	struct iss_subdev_i2c_board_info *subdevs;
+	enum iss_interface_type interface;
+	union {
+		struct iss_csi2_platform_data csi2;
+	} bus; /* gcc < 4.6.0 chokes on anonymous union initializers */
+};
+
+struct iss_platform_data {
+	struct iss_v4l2_subdevs_group *subdevs;
+	void (*set_constraints)(struct iss_device *iss, bool enable);
+};
+
+extern int omap4_init_camera(struct iss_platform_data *pdata,
+			     struct omap_board_data *bdata);
+#endif
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index b303a3f..ae2a99d 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -796,6 +796,19 @@ config VIDEO_OMAP3_DEBUG
 	---help---
 	  Enable debug messages on OMAP 3 camera controller driver.
 
+config VIDEO_OMAP4
+	tristate "OMAP 4 Camera support (EXPERIMENTAL)"
+	depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP4 && EXPERIMENTAL
+	select VIDEOBUF2_DMA_CONTIG
+	---help---
+	  Driver for an OMAP 4 ISS controller.
+
+config VIDEO_OMAP4_DEBUG
+	bool "OMAP 4 Camera debug messages"
+	depends on VIDEO_OMAP4
+	---help---
+	  Enable debug messages on OMAP 4 ISS controller driver.
+
 config SOC_CAMERA
 	tristate "SoC camera support"
 	depends on VIDEO_V4L2 && HAS_DMA && I2C
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index 117f9c4..f02a4c4 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -140,6 +140,7 @@ obj-$(CONFIG_VIDEO_MMP_CAMERA) += marvell-ccic/
 obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o
 
 obj-$(CONFIG_VIDEO_OMAP3)	+= omap3isp/
+obj-$(CONFIG_VIDEO_OMAP4)	+= omap4iss/
 
 obj-$(CONFIG_USB_ZR364XX)       += zr364xx.o
 obj-$(CONFIG_USB_STKWEBCAM)     += stkwebcam.o
diff --git a/drivers/media/video/omap4iss/Makefile b/drivers/media/video/omap4iss/Makefile
new file mode 100644
index 0000000..1d3b0a7
--- /dev/null
+++ b/drivers/media/video/omap4iss/Makefile
@@ -0,0 +1,6 @@
+# Makefile for OMAP4 ISS driver
+
+omap4-iss-objs += \
+	iss.o iss_csi2.o iss_csiphy.o iss_video.o
+
+obj-$(CONFIG_VIDEO_OMAP4) += omap4-iss.o
diff --git a/drivers/media/video/omap4iss/iss.c b/drivers/media/video/omap4iss/iss.c
new file mode 100644
index 0000000..255738b
--- /dev/null
+++ b/drivers/media/video/omap4iss/iss.c
@@ -0,0 +1,1179 @@
+/*
+ * V4L2 Driver for OMAP4 ISS
+ *
+ * Copyright (C) 2011, Texas Instruments
+ *
+ * Author: Sergio Aguirre <saaguirre@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/vmalloc.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+
+#include "iss.h"
+#include "iss_regs.h"
+
+static void iss_save_ctx(struct iss_device *iss);
+
+static void iss_restore_ctx(struct iss_device *iss);
+
+/* Structure for saving/restoring ISS module registers */
+static struct iss_reg iss_reg_list[] = {
+	{OMAP4_ISS_MEM_TOP, ISS_HL_SYSCONFIG, 0},
+	{OMAP4_ISS_MEM_TOP, ISS_CTRL, 0},
+	{OMAP4_ISS_MEM_TOP, ISS_CLKCTRL, 0},
+	{0, ISS_TOK_TERM, 0}
+};
+
+/*
+ * omap4iss_flush - Post pending L3 bus writes by doing a register readback
+ * @iss: OMAP4 ISS device
+ *
+ * In order to force posting of pending writes, we need to write and
+ * readback the same register, in this case the revision register.
+ *
+ * See this link for reference:
+ *   http://www.mail-archive.com/linux-omap@vger.kernel.org/msg08149.html
+ */
+void omap4iss_flush(struct iss_device *iss)
+{
+	writel(0, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_REVISION);
+	readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_REVISION);
+}
+
+/*
+ * iss_enable_interrupts - Enable ISS interrupts.
+ * @iss: OMAP4 ISS device
+ */
+static void iss_enable_interrupts(struct iss_device *iss)
+{
+	static const u32 irq = ISS_HL_IRQ_CSIA;
+
+	/* Enable HL interrupts */
+	writel(irq, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQSTATUS_5);
+	writel(irq, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQENABLE_5_SET);
+}
+
+/*
+ * iss_disable_interrupts - Disable ISS interrupts.
+ * @iss: OMAP4 ISS device
+ */
+static void iss_disable_interrupts(struct iss_device *iss)
+{
+	writel(-1, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQENABLE_5_CLR);
+}
+
+static inline void iss_isr_dbg(struct iss_device *iss, u32 irqstatus)
+{
+	static const char *name[] = {
+		"ISP_IRQ0",
+		"ISP_IRQ1",
+		"ISP_IRQ2",
+		"ISP_IRQ3",
+		"CSIA_IRQ",
+		"CSIB_IRQ",
+		"CCP2_IRQ0",
+		"CCP2_IRQ1",
+		"CCP2_IRQ2",
+		"CCP2_IRQ3",
+		"CBUFF_IRQ",
+		"BTE_IRQ",
+		"SIMCOP_IRQ0",
+		"SIMCOP_IRQ1",
+		"SIMCOP_IRQ2",
+		"SIMCOP_IRQ3",
+		"CCP2_IRQ8",
+		"HS_VS_IRQ",
+		"res18",
+		"res19",
+		"res20",
+		"res21",
+		"res22",
+		"res23",
+		"res24",
+		"res25",
+		"res26",
+		"res27",
+		"res28",
+		"res29",
+		"res30",
+		"res31",
+	};
+	int i;
+
+	dev_dbg(iss->dev, "ISS IRQ: ");
+
+	for (i = 0; i < ARRAY_SIZE(name); i++) {
+		if ((1 << i) & irqstatus)
+			printk(KERN_CONT "%s ", name[i]);
+	}
+	printk(KERN_CONT "\n");
+}
+
+/*
+ * iss_isr - Interrupt Service Routine for ISS module.
+ * @irq: Not used currently.
+ * @_iss: Pointer to the OMAP4 ISS device
+ *
+ * Handles the corresponding callback if plugged in.
+ *
+ * Returns IRQ_HANDLED when IRQ was correctly handled, or IRQ_NONE when the
+ * IRQ wasn't handled.
+ */
+static irqreturn_t iss_isr(int irq, void *_iss)
+{
+	struct iss_device *iss = _iss;
+	u32 irqstatus;
+
+	irqstatus = readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQSTATUS_5);
+	writel(irqstatus, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQSTATUS_5);
+
+	if (irqstatus & ISS_HL_IRQ_CSIA)
+		omap4iss_csi2_isr(&iss->csi2a);
+
+	omap4iss_flush(iss);
+
+#if defined(DEBUG) && defined(ISS_ISR_DEBUG)
+	iss_isr_dbg(iss, irqstatus);
+#endif
+
+	return IRQ_HANDLED;
+}
+
+/* -----------------------------------------------------------------------------
+ * Pipeline power management
+ *
+ * Entities must be powered up when part of a pipeline that contains at least
+ * one open video device node.
+ *
+ * To achieve this use the entity use_count field to track the number of users.
+ * For entities corresponding to video device nodes the use_count field stores
+ * the users count of the node. For entities corresponding to subdevs the
+ * use_count field stores the total number of users of all video device nodes
+ * in the pipeline.
+ *
+ * The omap4iss_pipeline_pm_use() function must be called in the open() and
+ * close() handlers of video device nodes. It increments or decrements the use
+ * count of all subdev entities in the pipeline.
+ *
+ * To react to link management on powered pipelines, the link setup notification
+ * callback updates the use count of all entities in the source and sink sides
+ * of the link.
+ */
+
+/*
+ * iss_pipeline_pm_use_count - Count the number of users of a pipeline
+ * @entity: The entity
+ *
+ * Return the total number of users of all video device nodes in the pipeline.
+ */
+static int iss_pipeline_pm_use_count(struct media_entity *entity)
+{
+	struct media_entity_graph graph;
+	int use = 0;
+
+	media_entity_graph_walk_start(&graph, entity);
+
+	while ((entity = media_entity_graph_walk_next(&graph))) {
+		if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE)
+			use += entity->use_count;
+	}
+
+	return use;
+}
+
+/*
+ * iss_pipeline_pm_power_one - Apply power change to an entity
+ * @entity: The entity
+ * @change: Use count change
+ *
+ * Change the entity use count by @change. If the entity is a subdev update its
+ * power state by calling the core::s_power operation when the use count goes
+ * from 0 to != 0 or from != 0 to 0.
+ *
+ * Return 0 on success or a negative error code on failure.
+ */
+static int iss_pipeline_pm_power_one(struct media_entity *entity, int change)
+{
+	struct v4l2_subdev *subdev;
+
+	subdev = media_entity_type(entity) == MEDIA_ENT_T_V4L2_SUBDEV
+	       ? media_entity_to_v4l2_subdev(entity) : NULL;
+
+	if (entity->use_count == 0 && change > 0 && subdev != NULL) {
+		int ret;
+
+		ret = v4l2_subdev_call(subdev, core, s_power, 1);
+		if (ret < 0 && ret != -ENOIOCTLCMD)
+			return ret;
+	}
+
+	entity->use_count += change;
+	WARN_ON(entity->use_count < 0);
+
+	if (entity->use_count == 0 && change < 0 && subdev != NULL)
+		v4l2_subdev_call(subdev, core, s_power, 0);
+
+	return 0;
+}
+
+/*
+ * iss_pipeline_pm_power - Apply power change to all entities in a pipeline
+ * @entity: The entity
+ * @change: Use count change
+ *
+ * Walk the pipeline to update the use count and the power state of all non-node
+ * entities.
+ *
+ * Return 0 on success or a negative error code on failure.
+ */
+static int iss_pipeline_pm_power(struct media_entity *entity, int change)
+{
+	struct media_entity_graph graph;
+	struct media_entity *first = entity;
+	int ret = 0;
+
+	if (!change)
+		return 0;
+
+	media_entity_graph_walk_start(&graph, entity);
+
+	while (!ret && (entity = media_entity_graph_walk_next(&graph)))
+		if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE)
+			ret = iss_pipeline_pm_power_one(entity, change);
+
+	if (!ret)
+		return 0;
+
+	media_entity_graph_walk_start(&graph, first);
+
+	while ((first = media_entity_graph_walk_next(&graph))
+	       && first != entity)
+		if (media_entity_type(first) != MEDIA_ENT_T_DEVNODE)
+			iss_pipeline_pm_power_one(first, -change);
+
+	return ret;
+}
+
+/*
+ * omap4iss_pipeline_pm_use - Update the use count of an entity
+ * @entity: The entity
+ * @use: Use (1) or stop using (0) the entity
+ *
+ * Update the use count of all entities in the pipeline and power entities on or
+ * off accordingly.
+ *
+ * Return 0 on success or a negative error code on failure. Powering entities
+ * off is assumed to never fail. No failure can occur when the use parameter is
+ * set to 0.
+ */
+int omap4iss_pipeline_pm_use(struct media_entity *entity, int use)
+{
+	int change = use ? 1 : -1;
+	int ret;
+
+	mutex_lock(&entity->parent->graph_mutex);
+
+	/* Apply use count to node. */
+	entity->use_count += change;
+	WARN_ON(entity->use_count < 0);
+
+	/* Apply power change to connected non-nodes. */
+	ret = iss_pipeline_pm_power(entity, change);
+	if (ret < 0)
+		entity->use_count -= change;
+
+	mutex_unlock(&entity->parent->graph_mutex);
+
+	return ret;
+}
+
+/*
+ * iss_pipeline_link_notify - Link management notification callback
+ * @source: Pad at the start of the link
+ * @sink: Pad at the end of the link
+ * @flags: New link flags that will be applied
+ *
+ * React to link management on powered pipelines by updating the use count of
+ * all entities in the source and sink sides of the link. Entities are powered
+ * on or off accordingly.
+ *
+ * Return 0 on success or a negative error code on failure. Powering entities
+ * off is assumed to never fail. This function will not fail for disconnection
+ * events.
+ */
+static int iss_pipeline_link_notify(struct media_pad *source,
+				    struct media_pad *sink, u32 flags)
+{
+	int source_use = iss_pipeline_pm_use_count(source->entity);
+	int sink_use = iss_pipeline_pm_use_count(sink->entity);
+	int ret;
+
+	if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+		/* Powering off entities is assumed to never fail. */
+		iss_pipeline_pm_power(source->entity, -sink_use);
+		iss_pipeline_pm_power(sink->entity, -source_use);
+		return 0;
+	}
+
+	ret = iss_pipeline_pm_power(source->entity, sink_use);
+	if (ret < 0)
+		return ret;
+
+	ret = iss_pipeline_pm_power(sink->entity, source_use);
+	if (ret < 0)
+		iss_pipeline_pm_power(source->entity, -sink_use);
+
+	return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * Pipeline stream management
+ */
+
+/*
+ * iss_pipeline_enable - Enable streaming on a pipeline
+ * @pipe: ISS pipeline
+ * @mode: Stream mode (single shot or continuous)
+ *
+ * Walk the entities chain starting at the pipeline output video node and start
+ * all modules in the chain in the given mode.
+ *
+ * Return 0 if successful, or the return value of the failed video::s_stream
+ * operation otherwise.
+ */
+static int iss_pipeline_enable(struct iss_pipeline *pipe,
+			       enum iss_pipeline_stream_state mode)
+{
+	struct media_entity *entity;
+	struct media_pad *pad;
+	struct v4l2_subdev *subdev;
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&pipe->lock, flags);
+	pipe->state &= ~(ISS_PIPELINE_IDLE_INPUT | ISS_PIPELINE_IDLE_OUTPUT);
+	spin_unlock_irqrestore(&pipe->lock, flags);
+
+	pipe->do_propagation = false;
+
+	entity = &pipe->output->video.entity;
+	while (1) {
+		pad = &entity->pads[0];
+		if (!(pad->flags & MEDIA_PAD_FL_SINK))
+			break;
+
+		pad = media_entity_remote_source(pad);
+		if (pad == NULL ||
+		    media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+			break;
+
+		entity = pad->entity;
+		subdev = media_entity_to_v4l2_subdev(entity);
+
+		ret = v4l2_subdev_call(subdev, video, s_stream, mode);
+		if (ret < 0 && ret != -ENOIOCTLCMD)
+			break;
+	}
+
+	return ret;
+}
+
+/*
+ * iss_pipeline_disable - Disable streaming on a pipeline
+ * @pipe: ISS pipeline
+ *
+ * Walk the entities chain starting at the pipeline output video node and stop
+ * all modules in the chain. Wait synchronously for the modules to be stopped if
+ * necessary.
+ */
+static int iss_pipeline_disable(struct iss_pipeline *pipe)
+{
+	struct media_entity *entity;
+	struct media_pad *pad;
+	struct v4l2_subdev *subdev;
+	int failure = 0;
+
+	entity = &pipe->output->video.entity;
+	while (1) {
+		pad = &entity->pads[0];
+		if (!(pad->flags & MEDIA_PAD_FL_SINK))
+			break;
+
+		pad = media_entity_remote_source(pad);
+		if (pad == NULL ||
+		    media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+			break;
+
+		entity = pad->entity;
+		subdev = media_entity_to_v4l2_subdev(entity);
+
+		v4l2_subdev_call(subdev, video, s_stream, 0);
+	}
+
+	return failure;
+}
+
+/*
+ * omap4iss_pipeline_set_stream - Enable/disable streaming on a pipeline
+ * @pipe: ISS pipeline
+ * @state: Stream state (stopped, single shot or continuous)
+ *
+ * Set the pipeline to the given stream state. Pipelines can be started in
+ * single-shot or continuous mode.
+ *
+ * Return 0 if successful, or the return value of the failed video::s_stream
+ * operation otherwise. The pipeline state is not updated when the operation
+ * fails, except when stopping the pipeline.
+ */
+int omap4iss_pipeline_set_stream(struct iss_pipeline *pipe,
+				 enum iss_pipeline_stream_state state)
+{
+	int ret;
+
+	if (state == ISS_PIPELINE_STREAM_STOPPED)
+		ret = iss_pipeline_disable(pipe);
+	else
+		ret = iss_pipeline_enable(pipe, state);
+
+	if (ret == 0 || state == ISS_PIPELINE_STREAM_STOPPED)
+		pipe->stream_state = state;
+
+	return ret;
+}
+
+/*
+ * iss_pipeline_is_last - Verify if entity has an enabled link to the output
+ *			  video node
+ * @me: ISS module's media entity
+ *
+ * Returns 1 if the entity has an enabled link to the output video node or 0
+ * otherwise. It's true only while pipeline can have no more than one output
+ * node.
+ */
+static int iss_pipeline_is_last(struct media_entity *me)
+{
+	struct iss_pipeline *pipe;
+	struct media_pad *pad;
+
+	if (!me->pipe)
+		return 0;
+	pipe = to_iss_pipeline(me);
+	if (pipe->stream_state == ISS_PIPELINE_STREAM_STOPPED)
+		return 0;
+	pad = media_entity_remote_source(&pipe->output->pad);
+	return pad->entity == me;
+}
+
+static int iss_reset(struct iss_device *iss)
+{
+	unsigned long timeout = 0;
+
+	writel(readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_SYSCONFIG) |
+		ISS_HL_SYSCONFIG_SOFTRESET,
+		iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_SYSCONFIG);
+
+	while (readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_SYSCONFIG) &
+			ISS_HL_SYSCONFIG_SOFTRESET) {
+		if (timeout++ > 10000) {
+			dev_alert(iss->dev, "cannot reset ISS\n");
+			return -ETIMEDOUT;
+		}
+		udelay(1);
+	}
+
+	return 0;
+}
+
+/*
+ * iss_save_context - Saves the values of the ISS module registers.
+ * @iss: OMAP4 ISS device
+ * @reg_list: Structure containing pairs of register address and value to
+ *            modify on OMAP.
+ */
+static void
+iss_save_context(struct iss_device *iss, struct iss_reg *reg_list)
+{
+	struct iss_reg *next = reg_list;
+
+	for (; next->reg != ISS_TOK_TERM; next++)
+		next->val = readl(iss->regs[next->mmio_range] + next->reg);
+}
+
+/*
+ * iss_restore_context - Restores the values of the ISS module registers.
+ * @iss: OMAP4 ISS device
+ * @reg_list: Structure containing pairs of register address and value to
+ *            modify on OMAP.
+ */
+static void
+iss_restore_context(struct iss_device *iss, struct iss_reg *reg_list)
+{
+	struct iss_reg *next = reg_list;
+
+	for (; next->reg != ISS_TOK_TERM; next++)
+		writel(next->val, iss->regs[next->mmio_range] + next->reg);
+}
+
+/*
+ * iss_save_ctx - Saves ISS context.
+ * @iss: OMAP4 ISS device
+ *
+ * Routine for saving the context of each module in the ISS.
+ */
+static void iss_save_ctx(struct iss_device *iss)
+{
+	iss_save_context(iss, iss_reg_list);
+}
+
+/*
+ * iss_restore_ctx - Restores ISS context.
+ * @iss: OMAP4 ISS device
+ *
+ * Routine for restoring the context of each module in the ISS.
+ */
+static void iss_restore_ctx(struct iss_device *iss)
+{
+	iss_restore_context(iss, iss_reg_list);
+}
+
+/*
+ * iss_module_sync_idle - Helper to sync module with its idle state
+ * @me: ISS submodule's media entity
+ * @wait: ISS submodule's wait queue for streamoff/interrupt synchronization
+ * @stopping: flag which tells module wants to stop
+ *
+ * This function checks if ISS submodule needs to wait for next interrupt. If
+ * yes, makes the caller to sleep while waiting for such event.
+ */
+int omap4iss_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait,
+			      atomic_t *stopping)
+{
+	struct iss_pipeline *pipe = to_iss_pipeline(me);
+
+	if (pipe->stream_state == ISS_PIPELINE_STREAM_STOPPED ||
+	    (pipe->stream_state == ISS_PIPELINE_STREAM_SINGLESHOT &&
+	     !iss_pipeline_ready(pipe)))
+		return 0;
+
+	/*
+	 * atomic_set() doesn't include memory barrier on ARM platform for SMP
+	 * scenario. We'll call it here to avoid race conditions.
+	 */
+	atomic_set(stopping, 1);
+	smp_mb();
+
+	/*
+	 * If module is the last one, it's writing to memory. In this case,
+	 * it's necessary to check if the module is already paused due to
+	 * DMA queue underrun or if it has to wait for next interrupt to be
+	 * idle.
+	 * If it isn't the last one, the function won't sleep but *stopping
+	 * will still be set to warn next submodule caller's interrupt the
+	 * module wants to be idle.
+	 */
+	if (iss_pipeline_is_last(me)) {
+		struct iss_video *video = pipe->output;
+		unsigned long flags;
+
+		spin_lock_irqsave(&video->qlock, flags);
+		if (video->dmaqueue_flags & ISS_VIDEO_DMAQUEUE_UNDERRUN) {
+			spin_unlock_irqrestore(&video->qlock, flags);
+			atomic_set(stopping, 0);
+			smp_mb();
+			return 0;
+		}
+		spin_unlock_irqrestore(&video->qlock, flags);
+		if (!wait_event_timeout(*wait, !atomic_read(stopping),
+					msecs_to_jiffies(1000))) {
+			atomic_set(stopping, 0);
+			smp_mb();
+			return -ETIMEDOUT;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * omap4iss_module_sync_is_stopped - Helper to verify if module was stopping
+ * @wait: ISS submodule's wait queue for streamoff/interrupt synchronization
+ * @stopping: flag which tells module wants to stop
+ *
+ * This function checks if ISS submodule was stopping. In case of yes, it
+ * notices the caller by setting stopping to 0 and waking up the wait queue.
+ * Returns 1 if it was stopping or 0 otherwise.
+ */
+int omap4iss_module_sync_is_stopping(wait_queue_head_t *wait,
+				     atomic_t *stopping)
+{
+	if (atomic_cmpxchg(stopping, 1, 0)) {
+		wake_up(wait);
+		return 1;
+	}
+
+	return 0;
+}
+
+/* --------------------------------------------------------------------------
+ * Clock management
+ */
+
+#define ISS_CLKCTRL_MASK	(ISS_CLKCTRL_CSI2_A)
+
+static int __iss_subclk_update(struct iss_device *iss)
+{
+	u32 clk = 0;
+	int ret = 0, timeout = 1000;
+
+	if (iss->subclk_resources & OMAP4_ISS_SUBCLK_CSI2_A)
+		clk |= ISS_CLKCTRL_CSI2_A;
+
+	writel((readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_CLKCTRL) &
+		~ISS_CLKCTRL_MASK) | clk,
+		iss->regs[OMAP4_ISS_MEM_TOP] + ISS_CLKCTRL);
+
+	/* Wait for HW assertion */
+	while (timeout-- > 0) {
+		udelay(1);
+		if ((readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_CLKSTAT) &
+		     ISS_CLKCTRL_MASK) == clk)
+			break;
+	}
+
+	if (!timeout)
+		ret = -EBUSY;
+
+	return ret;
+}
+
+int omap4iss_subclk_enable(struct iss_device *iss,
+			    enum iss_subclk_resource res)
+{
+	iss->subclk_resources |= res;
+
+	return __iss_subclk_update(iss);
+}
+
+int omap4iss_subclk_disable(struct iss_device *iss,
+			     enum iss_subclk_resource res)
+{
+	iss->subclk_resources &= ~res;
+
+	return __iss_subclk_update(iss);
+}
+
+/*
+ * iss_enable_clocks - Enable ISS clocks
+ * @iss: OMAP4 ISS device
+ *
+ * Return 0 if successful, or clk_enable return value if any of tthem fails.
+ */
+static int iss_enable_clocks(struct iss_device *iss)
+{
+	int r;
+
+	r = clk_enable(iss->iss_fck);
+	if (r) {
+		dev_err(iss->dev, "clk_enable iss_fck failed\n");
+		goto out_clk_enable_fck;
+	}
+
+	r = clk_enable(iss->iss_ctrlclk);
+	if (r) {
+		dev_err(iss->dev, "clk_enable iss_ctrlclk failed\n");
+		goto out_clk_enable_ctrlclk;
+	}
+	return 0;
+
+out_clk_enable_ctrlclk:
+	clk_disable(iss->iss_fck);
+out_clk_enable_fck:
+	return r;
+}
+
+/*
+ * iss_disable_clocks - Disable ISS clocks
+ * @iss: OMAP4 ISS device
+ */
+static void iss_disable_clocks(struct iss_device *iss)
+{
+	clk_disable(iss->iss_ctrlclk);
+	clk_disable(iss->iss_fck);
+}
+
+static void iss_put_clocks(struct iss_device *iss)
+{
+	if (iss->iss_fck) {
+		clk_put(iss->iss_fck);
+		iss->iss_fck = NULL;
+	}
+
+	if (iss->iss_ctrlclk) {
+		clk_put(iss->iss_ctrlclk);
+		iss->iss_ctrlclk = NULL;
+	}
+}
+
+static int iss_get_clocks(struct iss_device *iss)
+{
+	iss->iss_fck = clk_get(iss->dev, "iss_fck");
+	if (IS_ERR(iss->iss_fck)) {
+		dev_err(iss->dev, "Unable to get iss_fck clock info\n");
+		iss_put_clocks(iss);
+		return PTR_ERR(iss->iss_fck);
+	}
+
+	iss->iss_ctrlclk = clk_get(iss->dev, "iss_ctrlclk");
+	if (IS_ERR(iss->iss_ctrlclk)) {
+		dev_err(iss->dev, "Unable to get iss_ctrlclk clock info\n");
+		iss_put_clocks(iss);
+		return PTR_ERR(iss->iss_fck);
+	}
+
+	return 0;
+}
+
+/*
+ * omap4iss_get - Acquire the ISS resource.
+ *
+ * Initializes the clocks for the first acquire.
+ *
+ * Increment the reference count on the ISS. If the first reference is taken,
+ * enable clocks and power-up all submodules.
+ *
+ * Return a pointer to the ISS device structure, or NULL if an error occurred.
+ */
+struct iss_device *omap4iss_get(struct iss_device *iss)
+{
+	struct iss_device *__iss = iss;
+
+	if (iss == NULL)
+		return NULL;
+
+	mutex_lock(&iss->iss_mutex);
+	if (iss->ref_count > 0)
+		goto out;
+
+	if (iss_enable_clocks(iss) < 0) {
+		__iss = NULL;
+		goto out;
+	}
+
+	/* We don't want to restore context before saving it! */
+	if (iss->has_context)
+		iss_restore_ctx(iss);
+	else
+		iss->has_context = 1;
+
+	iss_enable_interrupts(iss);
+
+out:
+	if (__iss != NULL)
+		iss->ref_count++;
+	mutex_unlock(&iss->iss_mutex);
+
+	return __iss;
+}
+
+/*
+ * omap4iss_put - Release the ISS
+ *
+ * Decrement the reference count on the ISS. If the last reference is released,
+ * power-down all submodules, disable clocks and free temporary buffers.
+ */
+void omap4iss_put(struct iss_device *iss)
+{
+	if (iss == NULL)
+		return;
+
+	mutex_lock(&iss->iss_mutex);
+	BUG_ON(iss->ref_count == 0);
+	if (--iss->ref_count == 0) {
+		iss_disable_interrupts(iss);
+		iss_save_ctx(iss);
+		iss_disable_clocks(iss);
+	}
+	mutex_unlock(&iss->iss_mutex);
+}
+
+static int iss_map_mem_resource(struct platform_device *pdev,
+				struct iss_device *iss,
+				enum iss_mem_resources res)
+{
+	struct resource *mem;
+
+	/* request the mem region for the camera registers */
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, res);
+	if (!mem) {
+		dev_err(iss->dev, "no mem resource?\n");
+		return -ENODEV;
+	}
+
+	if (!request_mem_region(mem->start, resource_size(mem), pdev->name)) {
+		dev_err(iss->dev,
+			"cannot reserve camera register I/O region\n");
+		return -ENODEV;
+	}
+	iss->res[res] = mem;
+
+	/* map the region */
+	iss->regs[res] = ioremap_nocache(mem->start, resource_size(mem));
+	if (!iss->regs[res]) {
+		dev_err(iss->dev, "cannot map camera register I/O region\n");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void iss_unregister_entities(struct iss_device *iss)
+{
+	omap4iss_csi2_unregister_entities(&iss->csi2a);
+
+	v4l2_device_unregister(&iss->v4l2_dev);
+	media_device_unregister(&iss->media_dev);
+}
+
+/*
+ * iss_register_subdev_group - Register a group of subdevices
+ * @iss: OMAP4 ISS device
+ * @board_info: I2C subdevs board information array
+ *
+ * Register all I2C subdevices in the board_info array. The array must be
+ * terminated by a NULL entry, and the first entry must be the sensor.
+ *
+ * Return a pointer to the sensor media entity if it has been successfully
+ * registered, or NULL otherwise.
+ */
+static struct v4l2_subdev *
+iss_register_subdev_group(struct iss_device *iss,
+		     struct iss_subdev_i2c_board_info *board_info)
+{
+	struct v4l2_subdev *sensor = NULL;
+	unsigned int first;
+
+	if (board_info->board_info == NULL)
+		return NULL;
+
+	for (first = 1; board_info->board_info; ++board_info, first = 0) {
+		struct v4l2_subdev *subdev;
+		struct i2c_adapter *adapter;
+
+		adapter = i2c_get_adapter(board_info->i2c_adapter_id);
+		if (adapter == NULL) {
+			printk(KERN_ERR "%s: Unable to get I2C adapter %d for "
+				"device %s\n", __func__,
+				board_info->i2c_adapter_id,
+				board_info->board_info->type);
+			continue;
+		}
+
+		subdev = v4l2_i2c_new_subdev_board(&iss->v4l2_dev, adapter,
+				board_info->board_info, NULL);
+		if (subdev == NULL) {
+			printk(KERN_ERR "%s: Unable to register subdev %s\n",
+				__func__, board_info->board_info->type);
+			continue;
+		}
+
+		if (first)
+			sensor = subdev;
+	}
+
+	return sensor;
+}
+
+static int iss_register_entities(struct iss_device *iss)
+{
+	struct iss_platform_data *pdata = iss->pdata;
+	struct iss_v4l2_subdevs_group *subdevs;
+	int ret;
+
+	iss->media_dev.dev = iss->dev;
+	strlcpy(iss->media_dev.model, "TI OMAP4 ISS",
+		sizeof(iss->media_dev.model));
+	iss->media_dev.link_notify = iss_pipeline_link_notify;
+	ret = media_device_register(&iss->media_dev);
+	if (ret < 0) {
+		printk(KERN_ERR "%s: Media device registration failed (%d)\n",
+			__func__, ret);
+		return ret;
+	}
+
+	iss->v4l2_dev.mdev = &iss->media_dev;
+	ret = v4l2_device_register(iss->dev, &iss->v4l2_dev);
+	if (ret < 0) {
+		printk(KERN_ERR "%s: V4L2 device registration failed (%d)\n",
+			__func__, ret);
+		goto done;
+	}
+
+	/* Register internal entities */
+	ret = omap4iss_csi2_register_entities(&iss->csi2a, &iss->v4l2_dev);
+	if (ret < 0)
+		goto done;
+
+	/* Register external entities */
+	for (subdevs = pdata->subdevs; subdevs && subdevs->subdevs; ++subdevs) {
+		struct v4l2_subdev *sensor;
+		struct media_entity *input;
+		unsigned int flags;
+		unsigned int pad;
+
+		sensor = iss_register_subdev_group(iss, subdevs->subdevs);
+		if (sensor == NULL)
+			continue;
+
+		sensor->host_priv = subdevs;
+
+		/* Connect the sensor to the correct interface module.
+		 * CSI2a receiver through CSIPHY1.
+		 */
+		switch (subdevs->interface) {
+		case ISS_INTERFACE_CSI2A_PHY1:
+			input = &iss->csi2a.subdev.entity;
+			pad = CSI2_PAD_SINK;
+			flags = MEDIA_LNK_FL_IMMUTABLE
+			      | MEDIA_LNK_FL_ENABLED;
+			break;
+
+		default:
+			printk(KERN_ERR "%s: invalid interface type %u\n",
+			       __func__, subdevs->interface);
+			ret = -EINVAL;
+			goto done;
+		}
+
+		ret = media_entity_create_link(&sensor->entity, 0, input, pad,
+					       flags);
+		if (ret < 0)
+			goto done;
+	}
+
+	ret = v4l2_device_register_subdev_nodes(&iss->v4l2_dev);
+
+done:
+	if (ret < 0)
+		iss_unregister_entities(iss);
+
+	return ret;
+}
+
+static void iss_cleanup_modules(struct iss_device *iss)
+{
+	omap4iss_csi2_cleanup(iss);
+}
+
+static int iss_initialize_modules(struct iss_device *iss)
+{
+	int ret;
+
+	ret = omap4iss_csiphy_init(iss);
+	if (ret < 0) {
+		dev_err(iss->dev, "CSI PHY initialization failed\n");
+		goto error_csiphy;
+	}
+
+	ret = omap4iss_csi2_init(iss);
+	if (ret < 0) {
+		dev_err(iss->dev, "CSI2 initialization failed\n");
+		goto error_csi2;
+	}
+
+	return 0;
+
+error_csi2:
+error_csiphy:
+	return ret;
+}
+
+static int iss_probe(struct platform_device *pdev)
+{
+	struct iss_platform_data *pdata = pdev->dev.platform_data;
+	struct iss_device *iss;
+	int i, ret;
+
+	if (pdata == NULL)
+		return -EINVAL;
+
+	iss = kzalloc(sizeof(*iss), GFP_KERNEL);
+	if (!iss) {
+		dev_err(&pdev->dev, "Could not allocate memory\n");
+		return -ENOMEM;
+	}
+
+	mutex_init(&iss->iss_mutex);
+
+	iss->dev = &pdev->dev;
+	iss->pdata = pdata;
+	iss->ref_count = 0;
+
+	iss->raw_dmamask = DMA_BIT_MASK(32);
+	iss->dev->dma_mask = &iss->raw_dmamask;
+	iss->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+	platform_set_drvdata(pdev, iss);
+
+	/* Clocks */
+	ret = iss_map_mem_resource(pdev, iss, OMAP4_ISS_MEM_TOP);
+	if (ret < 0)
+		goto error;
+
+	ret = iss_get_clocks(iss);
+	if (ret < 0)
+		goto error;
+
+	if (omap4iss_get(iss) == NULL)
+		goto error;
+
+	ret = iss_reset(iss);
+	if (ret < 0)
+		goto error_iss;
+
+	iss->revision = readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_REVISION);
+	dev_info(iss->dev, "Revision %08x found\n", iss->revision);
+
+	for (i = 1; i < OMAP4_ISS_MEM_LAST; i++) {
+		ret = iss_map_mem_resource(pdev, iss, i);
+		if (ret)
+			goto error_iss;
+	}
+
+	/* Interrupt */
+	iss->irq_num = platform_get_irq(pdev, 0);
+	if (iss->irq_num <= 0) {
+		dev_err(iss->dev, "No IRQ resource\n");
+		ret = -ENODEV;
+		goto error_iss;
+	}
+
+	if (request_irq(iss->irq_num, iss_isr, IRQF_SHARED, "OMAP4 ISS", iss)) {
+		dev_err(iss->dev, "Unable to request IRQ\n");
+		ret = -EINVAL;
+		goto error_iss;
+	}
+
+	/* Entities */
+	ret = iss_initialize_modules(iss);
+	if (ret < 0)
+		goto error_irq;
+
+	ret = iss_register_entities(iss);
+	if (ret < 0)
+		goto error_modules;
+
+	omap4iss_put(iss);
+
+	return 0;
+
+error_modules:
+	iss_cleanup_modules(iss);
+error_irq:
+	free_irq(iss->irq_num, iss);
+error_iss:
+	omap4iss_put(iss);
+error:
+	iss_put_clocks(iss);
+
+	for (i = 0; i < OMAP4_ISS_MEM_LAST; i++) {
+		if (iss->regs[i]) {
+			iounmap(iss->regs[i]);
+			iss->regs[i] = NULL;
+		}
+
+		if (iss->res[i]) {
+			release_mem_region(iss->res[i]->start,
+					   resource_size(iss->res[i]));
+			iss->res[i] = NULL;
+		}
+	}
+	platform_set_drvdata(pdev, NULL);
+	kfree(iss);
+
+	return ret;
+}
+
+static int iss_remove(struct platform_device *pdev)
+{
+	struct iss_device *iss = platform_get_drvdata(pdev);
+	int i;
+
+	iss_unregister_entities(iss);
+	iss_cleanup_modules(iss);
+
+	free_irq(iss->irq_num, iss);
+	iss_put_clocks(iss);
+
+	for (i = 0; i < OMAP4_ISS_MEM_LAST; i++) {
+		if (iss->regs[i]) {
+			iounmap(iss->regs[i]);
+			iss->regs[i] = NULL;
+		}
+
+		if (iss->res[i]) {
+			release_mem_region(iss->res[i]->start,
+					   resource_size(iss->res[i]));
+			iss->res[i] = NULL;
+		}
+	}
+
+	kfree(iss);
+
+	return 0;
+}
+
+static struct platform_device_id omap4iss_id_table[] = {
+	{ "omap4iss", 0 },
+	{ },
+};
+MODULE_DEVICE_TABLE(platform, omap4iss_id_table);
+
+static struct platform_driver iss_driver = {
+	.probe		= iss_probe,
+	.remove		= iss_remove,
+	.id_table	= omap4iss_id_table,
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "omap4iss",
+	},
+};
+
+static int __init iss_init(void)
+{
+	return platform_driver_register(&iss_driver);
+}
+
+static void __exit iss_exit(void)
+{
+	platform_driver_unregister(&iss_driver);
+}
+
+/*
+ * FIXME: Had to make it late_initcall. Strangely while being module_init,
+ * The I2C communication was failing in the sensor, because no XCLK was
+ * provided.
+ */
+late_initcall(iss_init);
+module_exit(iss_exit);
+
+MODULE_DESCRIPTION("TI OMAP4 ISS driver");
+MODULE_AUTHOR("Sergio Aguirre <saaguirre@ti.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/omap4iss/iss.h b/drivers/media/video/omap4iss/iss.h
new file mode 100644
index 0000000..8346c80
--- /dev/null
+++ b/drivers/media/video/omap4iss/iss.h
@@ -0,0 +1,133 @@
+/*
+ * iss.h
+ *
+ * Copyright (C) 2011 Texas Instruments.
+ *
+ * Author: Sergio Aguirre <saaguirre@ti.com>
+ */
+
+#ifndef _OMAP4_ISS_H_
+#define _OMAP4_ISS_H_
+
+#include <media/v4l2-device.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/wait.h>
+
+#include <plat/omap4-iss.h>
+
+#include "iss_regs.h"
+#include "iss_csiphy.h"
+#include "iss_csi2.h"
+
+#define ISS_TOK_TERM		0xFFFFFFFF	/*
+						 * terminating token for ISS
+						 * modules reg list
+						 */
+#define to_iss_device(ptr_module)				\
+	container_of(ptr_module, struct iss_device, ptr_module)
+#define to_device(ptr_module)						\
+	(to_iss_device(ptr_module)->dev)
+
+enum iss_mem_resources {
+	OMAP4_ISS_MEM_TOP,
+	OMAP4_ISS_MEM_CSI2_A_REGS1,
+	OMAP4_ISS_MEM_CAMERARX_CORE1,
+	OMAP4_ISS_MEM_LAST,
+};
+
+enum iss_subclk_resource {
+	OMAP4_ISS_SUBCLK_SIMCOP		= (1 << 0),
+	OMAP4_ISS_SUBCLK_ISP		= (1 << 1),
+	OMAP4_ISS_SUBCLK_CSI2_A		= (1 << 2),
+	OMAP4_ISS_SUBCLK_CSI2_B		= (1 << 3),
+	OMAP4_ISS_SUBCLK_CCP2		= (1 << 4),
+};
+
+/*
+ * struct iss_reg - Structure for ISS register values.
+ * @reg: 32-bit Register address.
+ * @val: 32-bit Register value.
+ */
+struct iss_reg {
+	enum iss_mem_resources mmio_range;
+	u32 reg;
+	u32 val;
+};
+
+struct iss_platform_callback {
+	int (*csiphy_config)(struct iss_csiphy *phy,
+			     struct iss_csiphy_dphy_cfg *dphy,
+			     struct iss_csiphy_lanes_cfg *lanes);
+};
+
+struct iss_device {
+	struct v4l2_device v4l2_dev;
+	struct media_device media_dev;
+	struct device *dev;
+	u32 revision;
+
+	/* platform HW resources */
+	struct iss_platform_data *pdata;
+	unsigned int irq_num;
+
+	struct resource *res[OMAP4_ISS_MEM_LAST];
+	void __iomem *regs[OMAP4_ISS_MEM_LAST];
+
+	u64 raw_dmamask;
+
+	struct mutex iss_mutex;	/* For handling ref_count field */
+	int has_context;
+	int ref_count;
+
+	struct clk *iss_fck;
+	struct clk *iss_ctrlclk;
+
+	/* ISS modules */
+	struct iss_csi2_device csi2a;
+	struct iss_csiphy csiphy1;
+
+	unsigned int subclk_resources;
+
+	struct iss_platform_callback platform_cb;
+};
+
+#define v4l2_dev_to_iss_device(dev) \
+	container_of(dev, struct iss_device, v4l2_dev)
+
+int omap4iss_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait,
+			      atomic_t *stopping);
+
+int omap4iss_module_sync_is_stopping(wait_queue_head_t *wait,
+				     atomic_t *stopping);
+
+int omap4iss_pipeline_set_stream(struct iss_pipeline *pipe,
+				 enum iss_pipeline_stream_state state);
+
+struct iss_device *omap4iss_get(struct iss_device *iss);
+void omap4iss_put(struct iss_device *iss);
+int omap4iss_subclk_enable(struct iss_device *iss,
+			   enum iss_subclk_resource res);
+int omap4iss_subclk_disable(struct iss_device *iss,
+			    enum iss_subclk_resource res);
+
+int omap4iss_pipeline_pm_use(struct media_entity *entity, int use);
+
+int omap4iss_register_entities(struct platform_device *pdev,
+			       struct v4l2_device *v4l2_dev);
+void omap4iss_unregister_entities(struct platform_device *pdev);
+
+static inline enum v4l2_buf_type
+iss_pad_buffer_type(const struct v4l2_subdev *subdev, int pad)
+{
+	if (pad >= subdev->entity.num_pads)
+		return 0;
+
+	if (subdev->entity.pads[pad].flags & MEDIA_PAD_FL_SINK)
+		return V4L2_BUF_TYPE_VIDEO_OUTPUT;
+	else
+		return V4L2_BUF_TYPE_VIDEO_CAPTURE;
+}
+
+#endif /* _OMAP4_ISS_H_ */
diff --git a/drivers/media/video/omap4iss/iss_csi2.c b/drivers/media/video/omap4iss/iss_csi2.c
new file mode 100644
index 0000000..916d5ef
--- /dev/null
+++ b/drivers/media/video/omap4iss/iss_csi2.c
@@ -0,0 +1,1324 @@
+/*
+ * iss_csi2.c
+ *
+ * TI OMAP4 ISS - CSI PHY module
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ *
+ * Contacts: Sergio Aguirre <saaguirre@ti.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+#include <linux/delay.h>
+#include <media/v4l2-common.h>
+#include <linux/v4l2-mediabus.h>
+#include <linux/mm.h>
+
+#include "iss.h"
+#include "iss_regs.h"
+#include "iss_csi2.h"
+
+/*
+ * csi2_if_enable - Enable CSI2 Receiver interface.
+ * @enable: enable flag
+ *
+ */
+static void csi2_if_enable(struct iss_csi2_device *csi2, u8 enable)
+{
+	struct iss_csi2_ctrl_cfg *currctrl = &csi2->ctrl;
+
+	writel((readl(csi2->regs1 + CSI2_CTRL) & ~CSI2_CTRL_IF_EN) |
+		(enable ? CSI2_CTRL_IF_EN : 0),
+		csi2->regs1 + CSI2_CTRL);
+
+	currctrl->if_enable = enable;
+}
+
+/*
+ * csi2_recv_config - CSI2 receiver module configuration.
+ * @currctrl: iss_csi2_ctrl_cfg structure
+ *
+ */
+static void csi2_recv_config(struct iss_csi2_device *csi2,
+			     struct iss_csi2_ctrl_cfg *currctrl)
+{
+	u32 reg;
+
+	reg = readl(csi2->regs1 + CSI2_CTRL);
+
+	if (currctrl->frame_mode)
+		reg |= CSI2_CTRL_FRAME;
+	else
+		reg &= ~CSI2_CTRL_FRAME;
+
+	if (currctrl->vp_clk_enable)
+		reg |= CSI2_CTRL_VP_CLK_EN;
+	else
+		reg &= ~CSI2_CTRL_VP_CLK_EN;
+
+	if (currctrl->vp_only_enable)
+		reg |= CSI2_CTRL_VP_ONLY_EN;
+	else
+		reg &= ~CSI2_CTRL_VP_ONLY_EN;
+
+	reg &= ~CSI2_CTRL_VP_OUT_CTRL_MASK;
+	reg |= currctrl->vp_out_ctrl << CSI2_CTRL_VP_OUT_CTRL_SHIFT;
+
+	if (currctrl->ecc_enable)
+		reg |= CSI2_CTRL_ECC_EN;
+	else
+		reg &= ~CSI2_CTRL_ECC_EN;
+
+	/*
+	 * Set MFlag assertion boundaries to:
+	 * Low: 4/8 of FIFO size
+	 * High: 6/8 of FIFO size
+	 */
+	reg &= ~(CSI2_CTRL_MFLAG_LEVH_MASK | CSI2_CTRL_MFLAG_LEVL_MASK);
+	reg |= (2 << CSI2_CTRL_MFLAG_LEVH_SHIFT) |
+	       (4 << CSI2_CTRL_MFLAG_LEVL_SHIFT);
+
+	/* Generation of 16x64-bit bursts (Recommended) */
+	reg |= CSI2_CTRL_BURST_SIZE_EXPAND;
+
+	/* Do Non-Posted writes (Recommended) */
+	reg |= CSI2_CTRL_NON_POSTED_WRITE;
+
+	/*
+	 * Enforce Little endian for all formats, including:
+	 * YUV4:2:2 8-bit and YUV4:2:0 Legacy
+	 */
+	reg |= CSI2_CTRL_ENDIANNESS;
+
+	writel(reg, csi2->regs1 + CSI2_CTRL);
+}
+
+static const unsigned int csi2_input_fmts[] = {
+	V4L2_MBUS_FMT_SGRBG10_1X10,
+	V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8,
+	V4L2_MBUS_FMT_SRGGB10_1X10,
+	V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8,
+	V4L2_MBUS_FMT_SBGGR10_1X10,
+	V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8,
+	V4L2_MBUS_FMT_SGBRG10_1X10,
+	V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8,
+	V4L2_MBUS_FMT_UYVY8_1X16,
+	V4L2_MBUS_FMT_YUYV8_1X16,
+};
+
+/* To set the format on the CSI2 requires a mapping function that takes
+ * the following inputs:
+ * - 3 different formats (at this time)
+ * - 2 destinations (mem, vp+mem) (vp only handled separately)
+ * - 2 decompression options (on, off)
+ * Output should be CSI2 frame format code
+ * Array indices as follows: [format][dest][decompr]
+ * Not all combinations are valid. 0 means invalid.
+ */
+static const u16 __csi2_fmt_map[][2][2] = {
+	/* RAW10 formats */
+	{
+		/* Output to memory */
+		{
+			/* No DPCM decompression */
+			CSI2_PIX_FMT_RAW10_EXP16,
+			/* DPCM decompression */
+			0,
+		},
+		/* Output to both */
+		{
+			/* No DPCM decompression */
+			CSI2_PIX_FMT_RAW10_EXP16_VP,
+			/* DPCM decompression */
+			0,
+		},
+	},
+	/* RAW10 DPCM8 formats */
+	{
+		/* Output to memory */
+		{
+			/* No DPCM decompression */
+			CSI2_USERDEF_8BIT_DATA1,
+			/* DPCM decompression */
+			CSI2_USERDEF_8BIT_DATA1_DPCM10,
+		},
+		/* Output to both */
+		{
+			/* No DPCM decompression */
+			CSI2_PIX_FMT_RAW8_VP,
+			/* DPCM decompression */
+			CSI2_USERDEF_8BIT_DATA1_DPCM10_VP,
+		},
+	},
+	/* YUV422 formats */
+	{
+		/* Output to memory */
+		{
+			/* No DPCM decompression */
+			CSI2_PIX_FMT_YUV422_8BIT,
+			/* DPCM decompression */
+			0,
+		},
+		/* Output to both */
+		{
+			/* No DPCM decompression */
+			CSI2_PIX_FMT_YUV422_8BIT_VP,
+			/* DPCM decompression */
+			0,
+		},
+	},
+};
+
+/*
+ * csi2_ctx_map_format - Map CSI2 sink media bus format to CSI2 format ID
+ * @csi2: ISS CSI2 device
+ *
+ * Returns CSI2 physical format id
+ */
+static u16 csi2_ctx_map_format(struct iss_csi2_device *csi2)
+{
+	const struct v4l2_mbus_framefmt *fmt = &csi2->formats[CSI2_PAD_SINK];
+	int fmtidx, destidx;
+
+	switch (fmt->code) {
+	case V4L2_MBUS_FMT_SGRBG10_1X10:
+	case V4L2_MBUS_FMT_SRGGB10_1X10:
+	case V4L2_MBUS_FMT_SBGGR10_1X10:
+	case V4L2_MBUS_FMT_SGBRG10_1X10:
+		fmtidx = 0;
+		break;
+	case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8:
+	case V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8:
+	case V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8:
+	case V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8:
+		fmtidx = 1;
+		break;
+	case V4L2_MBUS_FMT_UYVY8_1X16:
+	case V4L2_MBUS_FMT_YUYV8_1X16:
+		fmtidx = 2;
+		break;
+	default:
+		WARN(1, KERN_ERR "CSI2: pixel format %08x unsupported!\n",
+		     fmt->code);
+		return 0;
+	}
+
+	if (!(csi2->output & CSI2_OUTPUT_IPIPEIF) &&
+	    !(csi2->output & CSI2_OUTPUT_MEMORY)) {
+		/* Neither output enabled is a valid combination */
+		return CSI2_PIX_FMT_OTHERS;
+	}
+
+	/* If we need to skip frames at the beginning of the stream disable the
+	 * video port to avoid sending the skipped frames to the IPIPEIF.
+	 */
+	destidx = csi2->frame_skip ? 0 : !!(csi2->output & CSI2_OUTPUT_IPIPEIF);
+
+	return __csi2_fmt_map[fmtidx][destidx][csi2->dpcm_decompress];
+}
+
+/*
+ * csi2_set_outaddr - Set memory address to save output image
+ * @csi2: Pointer to ISS CSI2a device.
+ * @addr: 32-bit memory address aligned on 32 byte boundary.
+ *
+ * Sets the memory address where the output will be saved.
+ *
+ * Returns 0 if successful, or -EINVAL if the address is not in the 32 byte
+ * boundary.
+ */
+static void csi2_set_outaddr(struct iss_csi2_device *csi2, u32 addr)
+{
+	struct iss_csi2_ctx_cfg *ctx = &csi2->contexts[0];
+
+	ctx->ping_addr = addr;
+	ctx->pong_addr = addr;
+	writel(ctx->ping_addr,
+	       csi2->regs1 + CSI2_CTX_PING_ADDR(ctx->ctxnum));
+	writel(ctx->pong_addr,
+	       csi2->regs1 + CSI2_CTX_PONG_ADDR(ctx->ctxnum));
+}
+
+/*
+ * is_usr_def_mapping - Checks whether USER_DEF_MAPPING should
+ *			be enabled by CSI2.
+ * @format_id: mapped format id
+ *
+ */
+static inline int is_usr_def_mapping(u32 format_id)
+{
+	return (format_id & 0x40) ? 1 : 0;
+}
+
+/*
+ * csi2_ctx_enable - Enable specified CSI2 context
+ * @ctxnum: Context number, valid between 0 and 7 values.
+ * @enable: enable
+ *
+ */
+static void csi2_ctx_enable(struct iss_csi2_device *csi2, u8 ctxnum, u8 enable)
+{
+	struct iss_csi2_ctx_cfg *ctx = &csi2->contexts[ctxnum];
+	u32 reg;
+
+	reg = readl(csi2->regs1 + CSI2_CTX_CTRL1(ctxnum));
+
+	if (enable) {
+		unsigned int skip = 0;
+
+		if (csi2->frame_skip)
+			skip = csi2->frame_skip;
+		else if (csi2->output & CSI2_OUTPUT_MEMORY)
+			skip = 1;
+
+		reg &= ~CSI2_CTX_CTRL1_COUNT_MASK;
+		reg |= CSI2_CTX_CTRL1_COUNT_UNLOCK
+		    |  (skip << CSI2_CTX_CTRL1_COUNT_SHIFT)
+		    |  CSI2_CTX_CTRL1_CTX_EN;
+	} else {
+		reg &= ~CSI2_CTX_CTRL1_CTX_EN;
+	}
+
+	writel(reg, csi2->regs1 + CSI2_CTX_CTRL1(ctxnum));
+	ctx->enabled = enable;
+}
+
+/*
+ * csi2_ctx_config - CSI2 context configuration.
+ * @ctx: context configuration
+ *
+ */
+static void csi2_ctx_config(struct iss_csi2_device *csi2,
+			    struct iss_csi2_ctx_cfg *ctx)
+{
+	u32 reg;
+
+	/* Set up CSI2_CTx_CTRL1 */
+	reg = readl(csi2->regs1 + CSI2_CTX_CTRL1(ctx->ctxnum));
+
+	if (ctx->eof_enabled)
+		reg |= CSI2_CTX_CTRL1_EOF_EN;
+	else
+		reg &= ~CSI2_CTX_CTRL1_EOF_EN;
+
+	if (ctx->eol_enabled)
+		reg |= CSI2_CTX_CTRL1_EOL_EN;
+	else
+		reg &= ~CSI2_CTX_CTRL1_EOL_EN;
+
+	if (ctx->checksum_enabled)
+		reg |= CSI2_CTX_CTRL1_CS_EN;
+	else
+		reg &= ~CSI2_CTX_CTRL1_CS_EN;
+
+	writel(reg, csi2->regs1 + CSI2_CTX_CTRL1(ctx->ctxnum));
+
+	/* Set up CSI2_CTx_CTRL2 */
+	reg = readl(csi2->regs1 + CSI2_CTX_CTRL2(ctx->ctxnum));
+
+	reg &= ~(CSI2_CTX_CTRL2_VIRTUAL_ID_MASK);
+	reg |= ctx->virtual_id << CSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT;
+
+	reg &= ~(CSI2_CTX_CTRL2_FORMAT_MASK);
+	reg |= ctx->format_id << CSI2_CTX_CTRL2_FORMAT_SHIFT;
+
+	if (ctx->dpcm_decompress) {
+		if (ctx->dpcm_predictor)
+			reg |= CSI2_CTX_CTRL2_DPCM_PRED;
+		else
+			reg &= ~CSI2_CTX_CTRL2_DPCM_PRED;
+	}
+
+	if (is_usr_def_mapping(ctx->format_id)) {
+		reg &= ~CSI2_CTX_CTRL2_USER_DEF_MAP_MASK;
+		reg |= 2 << CSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT;
+	}
+
+	writel(reg, csi2->regs1 + CSI2_CTX_CTRL2(ctx->ctxnum));
+
+	/* Set up CSI2_CTx_CTRL3 */
+	reg = readl(csi2->regs1 + CSI2_CTX_CTRL3(ctx->ctxnum));
+	reg &= ~(CSI2_CTX_CTRL3_ALPHA_MASK);
+	reg |= (ctx->alpha << CSI2_CTX_CTRL3_ALPHA_SHIFT);
+
+	writel(reg, csi2->regs1 + CSI2_CTX_CTRL3(ctx->ctxnum));
+
+	/* Set up CSI2_CTx_DAT_OFST */
+	reg = readl(csi2->regs1 + CSI2_CTX_DAT_OFST(ctx->ctxnum));
+	reg &= ~CSI2_CTX_DAT_OFST_MASK;
+	reg |= ctx->data_offset;
+	writel(reg, csi2->regs1 + CSI2_CTX_DAT_OFST(ctx->ctxnum));
+
+	writel(ctx->ping_addr,
+		       csi2->regs1 + CSI2_CTX_PING_ADDR(ctx->ctxnum));
+
+	writel(ctx->pong_addr,
+		       csi2->regs1 + CSI2_CTX_PONG_ADDR(ctx->ctxnum));
+}
+
+/*
+ * csi2_timing_config - CSI2 timing configuration.
+ * @timing: csi2_timing_cfg structure
+ */
+static void csi2_timing_config(struct iss_csi2_device *csi2,
+			       struct iss_csi2_timing_cfg *timing)
+{
+	u32 reg;
+
+	reg = readl(csi2->regs1 + CSI2_TIMING);
+
+	if (timing->force_rx_mode)
+		reg |= CSI2_TIMING_FORCE_RX_MODE_IO1;
+	else
+		reg &= ~CSI2_TIMING_FORCE_RX_MODE_IO1;
+
+	if (timing->stop_state_16x)
+		reg |= CSI2_TIMING_STOP_STATE_X16_IO1;
+	else
+		reg &= ~CSI2_TIMING_STOP_STATE_X16_IO1;
+
+	if (timing->stop_state_4x)
+		reg |= CSI2_TIMING_STOP_STATE_X4_IO1;
+	else
+		reg &= ~CSI2_TIMING_STOP_STATE_X4_IO1;
+
+	reg &= ~CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK;
+	reg |= timing->stop_state_counter <<
+	       CSI2_TIMING_STOP_STATE_COUNTER_IO1_SHIFT;
+
+	writel(reg, csi2->regs1 + CSI2_TIMING);
+}
+
+/*
+ * csi2_irq_ctx_set - Enables CSI2 Context IRQs.
+ * @enable: Enable/disable CSI2 Context interrupts
+ */
+static void csi2_irq_ctx_set(struct iss_csi2_device *csi2, int enable)
+{
+	u32 reg = CSI2_CTX_IRQ_FE;
+	int i;
+
+	if (csi2->use_fs_irq)
+		reg |= CSI2_CTX_IRQ_FS;
+
+	for (i = 0; i < 8; i++) {
+		writel(reg, csi2->regs1 + CSI2_CTX_IRQSTATUS(i));
+		if (enable)
+			writel(readl(csi2->regs1 + CSI2_CTX_IRQENABLE(i)) | reg,
+				csi2->regs1 + CSI2_CTX_IRQENABLE(i));
+		else
+			writel(readl(csi2->regs1 + CSI2_CTX_IRQENABLE(i)) &
+				~reg,
+				csi2->regs1 + CSI2_CTX_IRQENABLE(i));
+	}
+}
+
+/*
+ * csi2_irq_complexio1_set - Enables CSI2 ComplexIO IRQs.
+ * @enable: Enable/disable CSI2 ComplexIO #1 interrupts
+ */
+static void csi2_irq_complexio1_set(struct iss_csi2_device *csi2, int enable)
+{
+	u32 reg;
+	reg = CSI2_COMPLEXIO_IRQ_STATEALLULPMEXIT |
+		CSI2_COMPLEXIO_IRQ_STATEALLULPMENTER |
+		CSI2_COMPLEXIO_IRQ_STATEULPM5 |
+		CSI2_COMPLEXIO_IRQ_ERRCONTROL5 |
+		CSI2_COMPLEXIO_IRQ_ERRESC5 |
+		CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS5 |
+		CSI2_COMPLEXIO_IRQ_ERRSOTHS5 |
+		CSI2_COMPLEXIO_IRQ_STATEULPM4 |
+		CSI2_COMPLEXIO_IRQ_ERRCONTROL4 |
+		CSI2_COMPLEXIO_IRQ_ERRESC4 |
+		CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS4 |
+		CSI2_COMPLEXIO_IRQ_ERRSOTHS4 |
+		CSI2_COMPLEXIO_IRQ_STATEULPM3 |
+		CSI2_COMPLEXIO_IRQ_ERRCONTROL3 |
+		CSI2_COMPLEXIO_IRQ_ERRESC3 |
+		CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS3 |
+		CSI2_COMPLEXIO_IRQ_ERRSOTHS3 |
+		CSI2_COMPLEXIO_IRQ_STATEULPM2 |
+		CSI2_COMPLEXIO_IRQ_ERRCONTROL2 |
+		CSI2_COMPLEXIO_IRQ_ERRESC2 |
+		CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS2 |
+		CSI2_COMPLEXIO_IRQ_ERRSOTHS2 |
+		CSI2_COMPLEXIO_IRQ_STATEULPM1 |
+		CSI2_COMPLEXIO_IRQ_ERRCONTROL1 |
+		CSI2_COMPLEXIO_IRQ_ERRESC1 |
+		CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS1 |
+		CSI2_COMPLEXIO_IRQ_ERRSOTHS1;
+	writel(reg, csi2->regs1 + CSI2_COMPLEXIO_IRQSTATUS);
+	if (enable)
+		reg |= readl(csi2->regs1 + CSI2_COMPLEXIO_IRQENABLE);
+	else
+		reg = 0;
+	writel(reg, csi2->regs1 + CSI2_COMPLEXIO_IRQENABLE);
+}
+
+/*
+ * csi2_irq_status_set - Enables CSI2 Status IRQs.
+ * @enable: Enable/disable CSI2 Status interrupts
+ */
+static void csi2_irq_status_set(struct iss_csi2_device *csi2, int enable)
+{
+	u32 reg;
+	reg = CSI2_IRQ_OCP_ERR |
+		CSI2_IRQ_SHORT_PACKET |
+		CSI2_IRQ_ECC_CORRECTION |
+		CSI2_IRQ_ECC_NO_CORRECTION |
+		CSI2_IRQ_COMPLEXIO_ERR |
+		CSI2_IRQ_FIFO_OVF |
+		CSI2_IRQ_CONTEXT0;
+	writel(reg, csi2->regs1 + CSI2_IRQSTATUS);
+	if (enable)
+		reg |= readl(csi2->regs1 + CSI2_IRQENABLE);
+	else
+		reg = 0;
+
+	writel(reg, csi2->regs1 + CSI2_IRQENABLE);
+}
+
+/*
+ * omap4iss_csi2_reset - Resets the CSI2 module.
+ *
+ * Must be called with the phy lock held.
+ *
+ * Returns 0 if successful, or -EBUSY if power command didn't respond.
+ */
+int omap4iss_csi2_reset(struct iss_csi2_device *csi2)
+{
+	u8 soft_reset_retries = 0;
+	u32 reg;
+	int i;
+
+	if (!csi2->available)
+		return -ENODEV;
+
+	if (csi2->phy->phy_in_use)
+		return -EBUSY;
+
+	writel(readl(csi2->regs1 + CSI2_SYSCONFIG) |
+		CSI2_SYSCONFIG_SOFT_RESET,
+		csi2->regs1 + CSI2_SYSCONFIG);
+
+	do {
+		reg = readl(csi2->regs1 + CSI2_SYSSTATUS) &
+				    CSI2_SYSSTATUS_RESET_DONE;
+		if (reg == CSI2_SYSSTATUS_RESET_DONE)
+			break;
+		soft_reset_retries++;
+		if (soft_reset_retries < 5)
+			udelay(100);
+	} while (soft_reset_retries < 5);
+
+	if (soft_reset_retries == 5) {
+		printk(KERN_ERR "CSI2: Soft reset try count exceeded!\n");
+		return -EBUSY;
+	}
+
+	writel(readl(csi2->regs1 + CSI2_COMPLEXIO_CFG) |
+		CSI2_COMPLEXIO_CFG_RESET_CTRL,
+		csi2->regs1 + CSI2_COMPLEXIO_CFG);
+
+	i = 100;
+	do {
+		reg = readl(csi2->phy->phy_regs + REGISTER1)
+		    & REGISTER1_RESET_DONE_CTRLCLK;
+		if (reg == REGISTER1_RESET_DONE_CTRLCLK)
+			break;
+		udelay(100);
+	} while (--i > 0);
+
+	if (i == 0) {
+		printk(KERN_ERR
+		       "CSI2: Reset for CSI2_96M_FCLK domain Failed!\n");
+		return -EBUSY;
+	}
+
+	writel((readl(csi2->regs1 + CSI2_SYSCONFIG) &
+		~(CSI2_SYSCONFIG_MSTANDBY_MODE_MASK |
+		  CSI2_SYSCONFIG_AUTO_IDLE)) |
+		CSI2_SYSCONFIG_MSTANDBY_MODE_NO,
+		csi2->regs1 + CSI2_SYSCONFIG);
+
+	return 0;
+}
+
+static int csi2_configure(struct iss_csi2_device *csi2)
+{
+	const struct iss_v4l2_subdevs_group *pdata;
+	struct iss_csi2_timing_cfg *timing = &csi2->timing[0];
+	struct v4l2_subdev *sensor;
+	struct media_pad *pad;
+
+	/*
+	 * CSI2 fields that can be updated while the context has
+	 * been enabled or the interface has been enabled are not
+	 * updated dynamically currently. So we do not allow to
+	 * reconfigure if either has been enabled
+	 */
+	if (csi2->contexts[0].enabled || csi2->ctrl.if_enable)
+		return -EBUSY;
+
+	pad = media_entity_remote_source(&csi2->pads[CSI2_PAD_SINK]);
+	sensor = media_entity_to_v4l2_subdev(pad->entity);
+	pdata = sensor->host_priv;
+
+	csi2->frame_skip = 0;
+	v4l2_subdev_call(sensor, sensor, g_skip_frames, &csi2->frame_skip);
+
+	csi2->ctrl.vp_out_ctrl = pdata->bus.csi2.vpclk_div;
+	csi2->ctrl.frame_mode = ISS_CSI2_FRAME_IMMEDIATE;
+	csi2->ctrl.ecc_enable = pdata->bus.csi2.crc;
+
+	timing->force_rx_mode = 1;
+	timing->stop_state_16x = 1;
+	timing->stop_state_4x = 1;
+	timing->stop_state_counter = 0x1FF;
+
+	/*
+	 * The CSI2 receiver can't do any format conversion except DPCM
+	 * decompression, so every set_format call configures both pads
+	 * and enables DPCM decompression as a special case:
+	 */
+	if (csi2->formats[CSI2_PAD_SINK].code !=
+	    csi2->formats[CSI2_PAD_SOURCE].code)
+		csi2->dpcm_decompress = true;
+	else
+		csi2->dpcm_decompress = false;
+
+	csi2->contexts[0].format_id = csi2_ctx_map_format(csi2);
+
+	if (csi2->video_out.bpl_padding == 0)
+		csi2->contexts[0].data_offset = 0;
+	else
+		csi2->contexts[0].data_offset = csi2->video_out.bpl_value;
+
+	/*
+	 * Enable end of frame and end of line signals generation for
+	 * context 0. These signals are generated from CSI2 receiver to
+	 * qualify the last pixel of a frame and the last pixel of a line.
+	 * Without enabling the signals CSI2 receiver writes data to memory
+	 * beyond buffer size and/or data line offset is not handled correctly.
+	 */
+	csi2->contexts[0].eof_enabled = 1;
+	csi2->contexts[0].eol_enabled = 1;
+
+	csi2_irq_complexio1_set(csi2, 1);
+	csi2_irq_ctx_set(csi2, 1);
+	csi2_irq_status_set(csi2, 1);
+
+	/* Set configuration (timings, format and links) */
+	csi2_timing_config(csi2, timing);
+	csi2_recv_config(csi2, &csi2->ctrl);
+	csi2_ctx_config(csi2, &csi2->contexts[0]);
+
+	return 0;
+}
+
+/*
+ * csi2_print_status - Prints CSI2 debug information.
+ */
+#define CSI2_PRINT_REGISTER(iss, regs, name)\
+	dev_dbg(iss->dev, "###CSI2 " #name "=0x%08x\n", \
+		readl(regs + CSI2_##name))
+
+static void csi2_print_status(struct iss_csi2_device *csi2)
+{
+	struct iss_device *iss = csi2->iss;
+
+	if (!csi2->available)
+		return;
+
+	dev_dbg(iss->dev, "-------------CSI2 Register dump-------------\n");
+
+	CSI2_PRINT_REGISTER(iss, csi2->regs1, SYSCONFIG);
+	CSI2_PRINT_REGISTER(iss, csi2->regs1, SYSSTATUS);
+	CSI2_PRINT_REGISTER(iss, csi2->regs1, IRQENABLE);
+	CSI2_PRINT_REGISTER(iss, csi2->regs1, IRQSTATUS);
+	CSI2_PRINT_REGISTER(iss, csi2->regs1, CTRL);
+	CSI2_PRINT_REGISTER(iss, csi2->regs1, DBG_H);
+	CSI2_PRINT_REGISTER(iss, csi2->regs1, COMPLEXIO_CFG);
+	CSI2_PRINT_REGISTER(iss, csi2->regs1, COMPLEXIO_IRQSTATUS);
+	CSI2_PRINT_REGISTER(iss, csi2->regs1, SHORT_PACKET);
+	CSI2_PRINT_REGISTER(iss, csi2->regs1, COMPLEXIO_IRQENABLE);
+	CSI2_PRINT_REGISTER(iss, csi2->regs1, DBG_P);
+	CSI2_PRINT_REGISTER(iss, csi2->regs1, TIMING);
+	CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_CTRL1(0));
+	CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_CTRL2(0));
+	CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_DAT_OFST(0));
+	CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_PING_ADDR(0));
+	CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_PONG_ADDR(0));
+	CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_IRQENABLE(0));
+	CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_IRQSTATUS(0));
+	CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_CTRL3(0));
+
+	dev_dbg(iss->dev, "--------------------------------------------\n");
+}
+
+/* -----------------------------------------------------------------------------
+ * Interrupt handling
+ */
+
+/*
+ * csi2_isr_buffer - Does buffer handling at end-of-frame
+ * when writing to memory.
+ */
+static void csi2_isr_buffer(struct iss_csi2_device *csi2)
+{
+	struct iss_buffer *buffer;
+
+	csi2_ctx_enable(csi2, 0, 0);
+
+	buffer = omap4iss_video_buffer_next(&csi2->video_out, 0);
+
+	/*
+	 * Let video queue operation restart engine if there is an underrun
+	 * condition.
+	 */
+	if (buffer == NULL)
+		return;
+
+	csi2_set_outaddr(csi2, buffer->iss_addr);
+	csi2_ctx_enable(csi2, 0, 1);
+}
+
+static void csi2_isr_ctx(struct iss_csi2_device *csi2,
+			 struct iss_csi2_ctx_cfg *ctx)
+{
+	unsigned int n = ctx->ctxnum;
+	u32 status;
+
+	status = readl(csi2->regs1 + CSI2_CTX_IRQSTATUS(n));
+	writel(status, csi2->regs1 + CSI2_CTX_IRQSTATUS(n));
+
+	/* Propagate frame number */
+	if (status & CSI2_CTX_IRQ_FS) {
+		struct iss_pipeline *pipe =
+				     to_iss_pipeline(&csi2->subdev.entity);
+		if (pipe->do_propagation)
+			atomic_inc(&pipe->frame_number);
+	}
+
+	if (!(status & CSI2_CTX_IRQ_FE))
+		return;
+
+	/* Skip interrupts until we reach the frame skip count. The CSI2 will be
+	 * automatically disabled, as the frame skip count has been programmed
+	 * in the CSI2_CTx_CTRL1::COUNT field, so reenable it.
+	 *
+	 * It would have been nice to rely on the FRAME_NUMBER interrupt instead
+	 * but it turned out that the interrupt is only generated when the CSI2
+	 * writes to memory (the CSI2_CTx_CTRL1::COUNT field is decreased
+	 * correctly and reaches 0 when data is forwarded to the video port only
+	 * but no interrupt arrives). Maybe a CSI2 hardware bug.
+	 */
+	if (csi2->frame_skip) {
+		csi2->frame_skip--;
+		if (csi2->frame_skip == 0) {
+			ctx->format_id = csi2_ctx_map_format(csi2);
+			csi2_ctx_config(csi2, ctx);
+			csi2_ctx_enable(csi2, n, 1);
+		}
+		return;
+	}
+
+	if (csi2->output & CSI2_OUTPUT_MEMORY)
+		csi2_isr_buffer(csi2);
+}
+
+/*
+ * omap4iss_csi2_isr - CSI2 interrupt handling.
+ *
+ * Return -EIO on Transmission error
+ */
+int omap4iss_csi2_isr(struct iss_csi2_device *csi2)
+{
+	u32 csi2_irqstatus, cpxio1_irqstatus;
+	struct iss_device *iss = csi2->iss;
+	int retval = 0;
+
+	if (!csi2->available)
+		return -ENODEV;
+
+	csi2_irqstatus = readl(csi2->regs1 + CSI2_IRQSTATUS);
+	writel(csi2_irqstatus, csi2->regs1 + CSI2_IRQSTATUS);
+
+	/* Failure Cases */
+	if (csi2_irqstatus & CSI2_IRQ_COMPLEXIO_ERR) {
+		cpxio1_irqstatus = readl(csi2->regs1 +
+					 CSI2_COMPLEXIO_IRQSTATUS);
+		writel(cpxio1_irqstatus,
+			csi2->regs1 + CSI2_COMPLEXIO_IRQSTATUS);
+		dev_dbg(iss->dev, "CSI2: ComplexIO Error IRQ "
+			"%x\n", cpxio1_irqstatus);
+		retval = -EIO;
+	}
+
+	if (csi2_irqstatus & (CSI2_IRQ_OCP_ERR |
+			      CSI2_IRQ_SHORT_PACKET |
+			      CSI2_IRQ_ECC_NO_CORRECTION |
+			      CSI2_IRQ_COMPLEXIO_ERR |
+			      CSI2_IRQ_FIFO_OVF)) {
+		dev_dbg(iss->dev, "CSI2 Err:"
+			" OCP:%d,"
+			" Short_pack:%d,"
+			" ECC:%d,"
+			" CPXIO:%d,"
+			" FIFO_OVF:%d,"
+			"\n",
+			(csi2_irqstatus &
+			 CSI2_IRQ_OCP_ERR) ? 1 : 0,
+			(csi2_irqstatus &
+			 CSI2_IRQ_SHORT_PACKET) ? 1 : 0,
+			(csi2_irqstatus &
+			 CSI2_IRQ_ECC_NO_CORRECTION) ? 1 : 0,
+			(csi2_irqstatus &
+			 CSI2_IRQ_COMPLEXIO_ERR) ? 1 : 0,
+			(csi2_irqstatus &
+			 CSI2_IRQ_FIFO_OVF) ? 1 : 0);
+		retval = -EIO;
+	}
+
+	if (omap4iss_module_sync_is_stopping(&csi2->wait, &csi2->stopping))
+		return 0;
+
+	/* Successful cases */
+	if (csi2_irqstatus & CSI2_IRQ_CONTEXT0)
+		csi2_isr_ctx(csi2, &csi2->contexts[0]);
+
+	if (csi2_irqstatus & CSI2_IRQ_ECC_CORRECTION)
+		dev_dbg(iss->dev, "CSI2: ECC correction done\n");
+
+	return retval;
+}
+
+/* -----------------------------------------------------------------------------
+ * ISS video operations
+ */
+
+/*
+ * csi2_queue - Queues the first buffer when using memory output
+ * @video: The video node
+ * @buffer: buffer to queue
+ */
+static int csi2_queue(struct iss_video *video, struct iss_buffer *buffer)
+{
+	struct iss_device *iss = video->iss;
+	struct iss_csi2_device *csi2 = &iss->csi2a;
+
+	csi2_set_outaddr(csi2, buffer->iss_addr);
+
+	/*
+	 * If streaming was enabled before there was a buffer queued
+	 * or underrun happened in the ISR, the hardware was not enabled
+	 * and DMA queue flag ISS_VIDEO_DMAQUEUE_UNDERRUN is still set.
+	 * Enable it now.
+	 */
+	if (csi2->video_out.dmaqueue_flags & ISS_VIDEO_DMAQUEUE_UNDERRUN) {
+		/* Enable / disable context 0 and IRQs */
+		csi2_if_enable(csi2, 1);
+		csi2_ctx_enable(csi2, 0, 1);
+		iss_video_dmaqueue_flags_clr(&csi2->video_out);
+	}
+
+	return 0;
+}
+
+static const struct iss_video_operations csi2_issvideo_ops = {
+	.queue = csi2_queue,
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev operations
+ */
+
+static struct v4l2_mbus_framefmt *
+__csi2_get_format(struct iss_csi2_device *csi2, struct v4l2_subdev_fh *fh,
+		  unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+	if (which == V4L2_SUBDEV_FORMAT_TRY)
+		return v4l2_subdev_get_try_format(fh, pad);
+	else
+		return &csi2->formats[pad];
+}
+
+static void
+csi2_try_format(struct iss_csi2_device *csi2, struct v4l2_subdev_fh *fh,
+		unsigned int pad, struct v4l2_mbus_framefmt *fmt,
+		enum v4l2_subdev_format_whence which)
+{
+	enum v4l2_mbus_pixelcode pixelcode;
+	struct v4l2_mbus_framefmt *format;
+	const struct iss_format_info *info;
+	unsigned int i;
+
+	switch (pad) {
+	case CSI2_PAD_SINK:
+		/* Clamp the width and height to valid range (1-8191). */
+		for (i = 0; i < ARRAY_SIZE(csi2_input_fmts); i++) {
+			if (fmt->code == csi2_input_fmts[i])
+				break;
+		}
+
+		/* If not found, use SGRBG10 as default */
+		if (i >= ARRAY_SIZE(csi2_input_fmts))
+			fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10;
+
+		fmt->width = clamp_t(u32, fmt->width, 1, 8191);
+		fmt->height = clamp_t(u32, fmt->height, 1, 8191);
+		break;
+
+	case CSI2_PAD_SOURCE:
+		/* Source format same as sink format, except for DPCM
+		 * compression.
+		 */
+		pixelcode = fmt->code;
+		format = __csi2_get_format(csi2, fh, CSI2_PAD_SINK, which);
+		memcpy(fmt, format, sizeof(*fmt));
+
+		/*
+		 * Only Allow DPCM decompression, and check that the
+		 * pattern is preserved
+		 */
+		info = omap4iss_video_format_info(fmt->code);
+		if (info->uncompressed == pixelcode)
+			fmt->code = pixelcode;
+		break;
+	}
+
+	/* RGB, non-interlaced */
+	fmt->colorspace = V4L2_COLORSPACE_SRGB;
+	fmt->field = V4L2_FIELD_NONE;
+}
+
+/*
+ * csi2_enum_mbus_code - Handle pixel format enumeration
+ * @sd     : pointer to v4l2 subdev structure
+ * @fh     : V4L2 subdev file handle
+ * @code   : pointer to v4l2_subdev_mbus_code_enum structure
+ * return -EINVAL or zero on success
+ */
+static int csi2_enum_mbus_code(struct v4l2_subdev *sd,
+			       struct v4l2_subdev_fh *fh,
+			       struct v4l2_subdev_mbus_code_enum *code)
+{
+	struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
+	struct v4l2_mbus_framefmt *format;
+	const struct iss_format_info *info;
+
+	if (code->pad == CSI2_PAD_SINK) {
+		if (code->index >= ARRAY_SIZE(csi2_input_fmts))
+			return -EINVAL;
+
+		code->code = csi2_input_fmts[code->index];
+	} else {
+		format = __csi2_get_format(csi2, fh, CSI2_PAD_SINK,
+					   V4L2_SUBDEV_FORMAT_TRY);
+		switch (code->index) {
+		case 0:
+			/* Passthrough sink pad code */
+			code->code = format->code;
+			break;
+		case 1:
+			/* Uncompressed code */
+			info = omap4iss_video_format_info(format->code);
+			if (info->uncompressed == format->code)
+				return -EINVAL;
+
+			code->code = info->uncompressed;
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int csi2_enum_frame_size(struct v4l2_subdev *sd,
+				struct v4l2_subdev_fh *fh,
+				struct v4l2_subdev_frame_size_enum *fse)
+{
+	struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
+	struct v4l2_mbus_framefmt format;
+
+	if (fse->index != 0)
+		return -EINVAL;
+
+	format.code = fse->code;
+	format.width = 1;
+	format.height = 1;
+	csi2_try_format(csi2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
+	fse->min_width = format.width;
+	fse->min_height = format.height;
+
+	if (format.code != fse->code)
+		return -EINVAL;
+
+	format.code = fse->code;
+	format.width = -1;
+	format.height = -1;
+	csi2_try_format(csi2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
+	fse->max_width = format.width;
+	fse->max_height = format.height;
+
+	return 0;
+}
+
+/*
+ * csi2_get_format - Handle get format by pads subdev method
+ * @sd : pointer to v4l2 subdev structure
+ * @fh : V4L2 subdev file handle
+ * @fmt: pointer to v4l2 subdev format structure
+ * return -EINVAL or zero on success
+ */
+static int csi2_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+			   struct v4l2_subdev_format *fmt)
+{
+	struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
+	struct v4l2_mbus_framefmt *format;
+
+	format = __csi2_get_format(csi2, fh, fmt->pad, fmt->which);
+	if (format == NULL)
+		return -EINVAL;
+
+	fmt->format = *format;
+	return 0;
+}
+
+/*
+ * csi2_set_format - Handle set format by pads subdev method
+ * @sd : pointer to v4l2 subdev structure
+ * @fh : V4L2 subdev file handle
+ * @fmt: pointer to v4l2 subdev format structure
+ * return -EINVAL or zero on success
+ */
+static int csi2_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+			   struct v4l2_subdev_format *fmt)
+{
+	struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
+	struct v4l2_mbus_framefmt *format;
+
+	format = __csi2_get_format(csi2, fh, fmt->pad, fmt->which);
+	if (format == NULL)
+		return -EINVAL;
+
+	csi2_try_format(csi2, fh, fmt->pad, &fmt->format, fmt->which);
+	*format = fmt->format;
+
+	/* Propagate the format from sink to source */
+	if (fmt->pad == CSI2_PAD_SINK) {
+		format = __csi2_get_format(csi2, fh, CSI2_PAD_SOURCE,
+					   fmt->which);
+		*format = fmt->format;
+		csi2_try_format(csi2, fh, CSI2_PAD_SOURCE, format, fmt->which);
+	}
+
+	return 0;
+}
+
+/*
+ * csi2_init_formats - Initialize formats on all pads
+ * @sd: ISS CSI2 V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ *
+ * Initialize all pad formats with default values. If fh is not NULL, try
+ * formats are initialized on the file handle. Otherwise active formats are
+ * initialized on the device.
+ */
+static int csi2_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	struct v4l2_subdev_format format;
+
+	memset(&format, 0, sizeof(format));
+	format.pad = CSI2_PAD_SINK;
+	format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
+	format.format.code = V4L2_MBUS_FMT_SGRBG10_1X10;
+	format.format.width = 4096;
+	format.format.height = 4096;
+	csi2_set_format(sd, fh, &format);
+
+	return 0;
+}
+
+/*
+ * csi2_set_stream - Enable/Disable streaming on the CSI2 module
+ * @sd: ISS CSI2 V4L2 subdevice
+ * @enable: ISS pipeline stream state
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+static int csi2_set_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
+	struct iss_device *iss = csi2->iss;
+	struct iss_pipeline *pipe = to_iss_pipeline(&csi2->subdev.entity);
+	struct iss_video *video_out = &csi2->video_out;
+
+	if (csi2->state == ISS_PIPELINE_STREAM_STOPPED) {
+		if (enable == ISS_PIPELINE_STREAM_STOPPED)
+			return 0;
+
+		omap4iss_subclk_enable(iss, OMAP4_ISS_SUBCLK_CSI2_A);
+	}
+
+	switch (enable) {
+	case ISS_PIPELINE_STREAM_CONTINUOUS:
+		if (omap4iss_csiphy_acquire(csi2->phy) < 0)
+			return -ENODEV;
+		csi2->use_fs_irq = pipe->do_propagation;
+		csi2_configure(csi2);
+		csi2_print_status(csi2);
+
+		/*
+		 * When outputting to memory with no buffer available, let the
+		 * buffer queue handler start the hardware. A DMA queue flag
+		 * ISS_VIDEO_DMAQUEUE_QUEUED will be set as soon as there is
+		 * a buffer available.
+		 */
+		if (csi2->output & CSI2_OUTPUT_MEMORY &&
+		    !(video_out->dmaqueue_flags & ISS_VIDEO_DMAQUEUE_QUEUED))
+			break;
+		/* Enable context 0 and IRQs */
+		atomic_set(&csi2->stopping, 0);
+		csi2_ctx_enable(csi2, 0, 1);
+		csi2_if_enable(csi2, 1);
+		iss_video_dmaqueue_flags_clr(video_out);
+		break;
+
+	case ISS_PIPELINE_STREAM_STOPPED:
+		if (csi2->state == ISS_PIPELINE_STREAM_STOPPED)
+			return 0;
+		if (omap4iss_module_sync_idle(&sd->entity, &csi2->wait,
+					      &csi2->stopping))
+			dev_dbg(iss->dev, "%s: module stop timeout.\n",
+				sd->name);
+		csi2_ctx_enable(csi2, 0, 0);
+		csi2_if_enable(csi2, 0);
+		csi2_irq_ctx_set(csi2, 0);
+		omap4iss_csiphy_release(csi2->phy);
+		omap4iss_subclk_disable(iss, OMAP4_ISS_SUBCLK_CSI2_A);
+		iss_video_dmaqueue_flags_clr(video_out);
+		break;
+	}
+
+	csi2->state = enable;
+	return 0;
+}
+
+/* subdev video operations */
+static const struct v4l2_subdev_video_ops csi2_video_ops = {
+	.s_stream = csi2_set_stream,
+};
+
+/* subdev pad operations */
+static const struct v4l2_subdev_pad_ops csi2_pad_ops = {
+	.enum_mbus_code = csi2_enum_mbus_code,
+	.enum_frame_size = csi2_enum_frame_size,
+	.get_fmt = csi2_get_format,
+	.set_fmt = csi2_set_format,
+};
+
+/* subdev operations */
+static const struct v4l2_subdev_ops csi2_ops = {
+	.video = &csi2_video_ops,
+	.pad = &csi2_pad_ops,
+};
+
+/* subdev internal operations */
+static const struct v4l2_subdev_internal_ops csi2_internal_ops = {
+	.open = csi2_init_formats,
+};
+
+/* -----------------------------------------------------------------------------
+ * Media entity operations
+ */
+
+/*
+ * csi2_link_setup - Setup CSI2 connections.
+ * @entity : Pointer to media entity structure
+ * @local  : Pointer to local pad array
+ * @remote : Pointer to remote pad array
+ * @flags  : Link flags
+ * return -EINVAL or zero on success
+ */
+static int csi2_link_setup(struct media_entity *entity,
+			   const struct media_pad *local,
+			   const struct media_pad *remote, u32 flags)
+{
+	struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+	struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
+	struct iss_csi2_ctrl_cfg *ctrl = &csi2->ctrl;
+
+	/*
+	 * The ISS core doesn't support pipelines with multiple video outputs.
+	 * Revisit this when it will be implemented, and return -EBUSY for now.
+	 */
+
+	switch (local->index | media_entity_type(remote->entity)) {
+	case CSI2_PAD_SOURCE | MEDIA_ENT_T_DEVNODE:
+		if (flags & MEDIA_LNK_FL_ENABLED) {
+			if (csi2->output & ~CSI2_OUTPUT_MEMORY)
+				return -EBUSY;
+			csi2->output |= CSI2_OUTPUT_MEMORY;
+		} else {
+			csi2->output &= ~CSI2_OUTPUT_MEMORY;
+		}
+		break;
+
+	case CSI2_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV:
+		if (flags & MEDIA_LNK_FL_ENABLED) {
+			if (csi2->output & ~CSI2_OUTPUT_IPIPEIF)
+				return -EBUSY;
+			csi2->output |= CSI2_OUTPUT_IPIPEIF;
+		} else {
+			csi2->output &= ~CSI2_OUTPUT_IPIPEIF;
+		}
+		break;
+
+	default:
+		/* Link from camera to CSI2 is fixed... */
+		return -EINVAL;
+	}
+
+	ctrl->vp_only_enable =
+		(csi2->output & CSI2_OUTPUT_MEMORY) ? false : true;
+	ctrl->vp_clk_enable = !!(csi2->output & CSI2_OUTPUT_IPIPEIF);
+
+	return 0;
+}
+
+/* media operations */
+static const struct media_entity_operations csi2_media_ops = {
+	.link_setup = csi2_link_setup,
+};
+
+/*
+ * csi2_init_entities - Initialize subdev and media entity.
+ * @csi2: Pointer to csi2 structure.
+ * return -ENOMEM or zero on success
+ */
+static int csi2_init_entities(struct iss_csi2_device *csi2)
+{
+	struct v4l2_subdev *sd = &csi2->subdev;
+	struct media_pad *pads = csi2->pads;
+	struct media_entity *me = &sd->entity;
+	int ret;
+
+	v4l2_subdev_init(sd, &csi2_ops);
+	sd->internal_ops = &csi2_internal_ops;
+	strlcpy(sd->name, "OMAP4 ISS CSI2a", sizeof(sd->name));
+
+	sd->grp_id = 1 << 16;	/* group ID for iss subdevs */
+	v4l2_set_subdevdata(sd, csi2);
+	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+	pads[CSI2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+	pads[CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+
+	me->ops = &csi2_media_ops;
+	ret = media_entity_init(me, CSI2_PADS_NUM, pads, 0);
+	if (ret < 0)
+		return ret;
+
+	csi2_init_formats(sd, NULL);
+
+	/* Video device node */
+	csi2->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	csi2->video_out.ops = &csi2_issvideo_ops;
+	csi2->video_out.bpl_alignment = 32;
+	csi2->video_out.bpl_zero_padding = 1;
+	csi2->video_out.bpl_max = 0x1ffe0;
+	csi2->video_out.iss = csi2->iss;
+	csi2->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 3;
+
+	ret = omap4iss_video_init(&csi2->video_out, "CSI2a");
+	if (ret < 0)
+		return ret;
+
+	/* Connect the CSI2 subdev to the video node. */
+	ret = media_entity_create_link(&csi2->subdev.entity, CSI2_PAD_SOURCE,
+				       &csi2->video_out.video.entity, 0, 0);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+void omap4iss_csi2_unregister_entities(struct iss_csi2_device *csi2)
+{
+	media_entity_cleanup(&csi2->subdev.entity);
+
+	v4l2_device_unregister_subdev(&csi2->subdev);
+	omap4iss_video_unregister(&csi2->video_out);
+}
+
+int omap4iss_csi2_register_entities(struct iss_csi2_device *csi2,
+				    struct v4l2_device *vdev)
+{
+	int ret;
+
+	/* Register the subdev and video nodes. */
+	ret = v4l2_device_register_subdev(vdev, &csi2->subdev);
+	if (ret < 0)
+		goto error;
+
+	ret = omap4iss_video_register(&csi2->video_out, vdev);
+	if (ret < 0)
+		goto error;
+
+	return 0;
+
+error:
+	omap4iss_csi2_unregister_entities(csi2);
+	return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * ISS CSI2 initialisation and cleanup
+ */
+
+/*
+ * omap4iss_csi2_cleanup - Routine for module driver cleanup
+ */
+void omap4iss_csi2_cleanup(struct iss_device *iss)
+{
+}
+
+/*
+ * omap4iss_csi2_init - Routine for module driver init
+ */
+int omap4iss_csi2_init(struct iss_device *iss)
+{
+	struct iss_csi2_device *csi2a = &iss->csi2a;
+	int ret;
+
+	csi2a->iss = iss;
+	csi2a->available = 1;
+	csi2a->regs1 = iss->regs[OMAP4_ISS_MEM_CSI2_A_REGS1];
+	csi2a->phy = &iss->csiphy1;
+	csi2a->state = ISS_PIPELINE_STREAM_STOPPED;
+	init_waitqueue_head(&csi2a->wait);
+
+	ret = csi2_init_entities(csi2a);
+	if (ret < 0)
+		goto fail;
+
+	return 0;
+fail:
+	omap4iss_csi2_cleanup(iss);
+	return ret;
+}
diff --git a/drivers/media/video/omap4iss/iss_csi2.h b/drivers/media/video/omap4iss/iss_csi2.h
new file mode 100644
index 0000000..4fa94cf
--- /dev/null
+++ b/drivers/media/video/omap4iss/iss_csi2.h
@@ -0,0 +1,166 @@
+/*
+ * iss_csi2.h
+ *
+ * TI OMAP4 ISS - CSI2 module
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ *
+ * Contacts: Sergio Aguirre <saaguirre@ti.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef OMAP4_ISS_CSI2_H
+#define OMAP4_ISS_CSI2_H
+
+#include <linux/types.h>
+#include <linux/videodev2.h>
+
+#include "iss_video.h"
+
+struct iss_csiphy;
+
+/* This is not an exhaustive list */
+enum iss_csi2_pix_formats {
+	CSI2_PIX_FMT_OTHERS = 0,
+	CSI2_PIX_FMT_YUV422_8BIT = 0x1e,
+	CSI2_PIX_FMT_YUV422_8BIT_VP = 0x9e,
+	CSI2_PIX_FMT_RAW10_EXP16 = 0xab,
+	CSI2_PIX_FMT_RAW10_EXP16_VP = 0x12f,
+	CSI2_PIX_FMT_RAW8 = 0x2a,
+	CSI2_PIX_FMT_RAW8_DPCM10_EXP16 = 0x2aa,
+	CSI2_PIX_FMT_RAW8_DPCM10_VP = 0x32a,
+	CSI2_PIX_FMT_RAW8_VP = 0x12a,
+	CSI2_USERDEF_8BIT_DATA1_DPCM10_VP = 0x340,
+	CSI2_USERDEF_8BIT_DATA1_DPCM10 = 0x2c0,
+	CSI2_USERDEF_8BIT_DATA1 = 0x40,
+};
+
+enum iss_csi2_irqevents {
+	OCP_ERR_IRQ = 0x4000,
+	SHORT_PACKET_IRQ = 0x2000,
+	ECC_CORRECTION_IRQ = 0x1000,
+	ECC_NO_CORRECTION_IRQ = 0x800,
+	COMPLEXIO2_ERR_IRQ = 0x400,
+	COMPLEXIO1_ERR_IRQ = 0x200,
+	FIFO_OVF_IRQ = 0x100,
+	CONTEXT7 = 0x80,
+	CONTEXT6 = 0x40,
+	CONTEXT5 = 0x20,
+	CONTEXT4 = 0x10,
+	CONTEXT3 = 0x8,
+	CONTEXT2 = 0x4,
+	CONTEXT1 = 0x2,
+	CONTEXT0 = 0x1,
+};
+
+enum iss_csi2_ctx_irqevents {
+	CTX_ECC_CORRECTION = 0x100,
+	CTX_LINE_NUMBER = 0x80,
+	CTX_FRAME_NUMBER = 0x40,
+	CTX_CS = 0x20,
+	CTX_LE = 0x8,
+	CTX_LS = 0x4,
+	CTX_FE = 0x2,
+	CTX_FS = 0x1,
+};
+
+enum iss_csi2_frame_mode {
+	ISS_CSI2_FRAME_IMMEDIATE,
+	ISS_CSI2_FRAME_AFTERFEC,
+};
+
+#define ISS_CSI2_MAX_CTX_NUM	7
+
+struct iss_csi2_ctx_cfg {
+	u8 ctxnum;		/* context number 0 - 7 */
+	u8 dpcm_decompress;
+
+	/* Fields in CSI2_CTx_CTRL2 - locked by CSI2_CTx_CTRL1.CTX_EN */
+	u8 virtual_id;
+	u16 format_id;		/* as in CSI2_CTx_CTRL2[9:0] */
+	u8 dpcm_predictor;	/* 1: simple, 0: advanced */
+
+	/* Fields in CSI2_CTx_CTRL1/3 - Shadowed */
+	u16 alpha;
+	u16 data_offset;
+	u32 ping_addr;
+	u32 pong_addr;
+	u8 eof_enabled;
+	u8 eol_enabled;
+	u8 checksum_enabled;
+	u8 enabled;
+};
+
+struct iss_csi2_timing_cfg {
+	u8 ionum;			/* IO1 or IO2 as in CSI2_TIMING */
+	unsigned force_rx_mode:1;
+	unsigned stop_state_16x:1;
+	unsigned stop_state_4x:1;
+	u16 stop_state_counter;
+};
+
+struct iss_csi2_ctrl_cfg {
+	bool vp_clk_enable;
+	bool vp_only_enable;
+	u8 vp_out_ctrl;
+	enum iss_csi2_frame_mode frame_mode;
+	bool ecc_enable;
+	bool if_enable;
+};
+
+#define CSI2_PAD_SINK		0
+#define CSI2_PAD_SOURCE		1
+#define CSI2_PADS_NUM		2
+
+#define CSI2_OUTPUT_IPIPEIF	(1 << 0)
+#define CSI2_OUTPUT_MEMORY	(1 << 1)
+
+struct iss_csi2_device {
+	struct v4l2_subdev subdev;
+	struct media_pad pads[CSI2_PADS_NUM];
+	struct v4l2_mbus_framefmt formats[CSI2_PADS_NUM];
+
+	struct iss_video video_out;
+	struct iss_device *iss;
+
+	u8 available;		/* Is the IP present on the silicon? */
+
+	/* Pointer to register remaps into kernel space */
+	void __iomem *regs1;
+	void __iomem *regs2;
+
+	u32 output; /* output to IPIPEIF, memory or both? */
+	bool dpcm_decompress;
+	unsigned int frame_skip;
+	bool use_fs_irq;
+
+	struct iss_csiphy *phy;
+	struct iss_csi2_ctx_cfg contexts[ISS_CSI2_MAX_CTX_NUM + 1];
+	struct iss_csi2_timing_cfg timing[2];
+	struct iss_csi2_ctrl_cfg ctrl;
+	enum iss_pipeline_stream_state state;
+	wait_queue_head_t wait;
+	atomic_t stopping;
+};
+
+int omap4iss_csi2_isr(struct iss_csi2_device *csi2);
+int omap4iss_csi2_reset(struct iss_csi2_device *csi2);
+int omap4iss_csi2_init(struct iss_device *iss);
+void omap4iss_csi2_cleanup(struct iss_device *iss);
+void omap4iss_csi2_unregister_entities(struct iss_csi2_device *csi2);
+int omap4iss_csi2_register_entities(struct iss_csi2_device *csi2,
+				    struct v4l2_device *vdev);
+#endif	/* OMAP4_ISS_CSI2_H */
diff --git a/drivers/media/video/omap4iss/iss_csiphy.c b/drivers/media/video/omap4iss/iss_csiphy.c
new file mode 100644
index 0000000..9545622
--- /dev/null
+++ b/drivers/media/video/omap4iss/iss_csiphy.c
@@ -0,0 +1,215 @@
+/*
+ * iss_csiphy.c
+ *
+ * TI OMAP4 ISS - CSI PHY module
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ *
+ * Contacts: Sergio Aguirre <saaguirre@ti.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+
+#include "iss.h"
+#include "iss_regs.h"
+#include "iss_csiphy.h"
+
+/*
+ * csiphy_lanes_config - Configuration of CSIPHY lanes.
+ *
+ * Updates HW configuration.
+ * Called with phy->mutex taken.
+ */
+static void csiphy_lanes_config(struct iss_csiphy *phy)
+{
+	unsigned int i;
+	u32 reg;
+
+	reg = readl(phy->cfg_regs + CSI2_COMPLEXIO_CFG);
+
+	for (i = 0; i < phy->num_data_lanes; i++) {
+		reg &= ~(CSI2_COMPLEXIO_CFG_DATA_POL(i + 1) |
+			 CSI2_COMPLEXIO_CFG_DATA_POSITION_MASK(i + 1));
+		reg |= (phy->lanes.data[i].pol ?
+			CSI2_COMPLEXIO_CFG_DATA_POL(i + 1) : 0);
+		reg |= (phy->lanes.data[i].pos <<
+			CSI2_COMPLEXIO_CFG_DATA_POSITION_SHIFT(i + 1));
+	}
+
+	reg &= ~(CSI2_COMPLEXIO_CFG_CLOCK_POL |
+		 CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK);
+	reg |= phy->lanes.clk.pol ? CSI2_COMPLEXIO_CFG_CLOCK_POL : 0;
+	reg |= phy->lanes.clk.pos << CSI2_COMPLEXIO_CFG_CLOCK_POSITION_SHIFT;
+
+	writel(reg, phy->cfg_regs + CSI2_COMPLEXIO_CFG);
+}
+
+/*
+ * csiphy_set_power
+ * @power: Power state to be set.
+ *
+ * Returns 0 if successful, or -EBUSY if the retry count is exceeded.
+ */
+static int csiphy_set_power(struct iss_csiphy *phy, u32 power)
+{
+	u32 reg;
+	u8 retry_count;
+
+	writel((readl(phy->cfg_regs + CSI2_COMPLEXIO_CFG) &
+		~CSI2_COMPLEXIO_CFG_PWD_CMD_MASK) |
+		power,
+		phy->cfg_regs + CSI2_COMPLEXIO_CFG);
+
+	retry_count = 0;
+	do {
+		udelay(50);
+		reg = readl(phy->cfg_regs + CSI2_COMPLEXIO_CFG) &
+				CSI2_COMPLEXIO_CFG_PWD_STATUS_MASK;
+
+		if (reg != power >> 2)
+			retry_count++;
+
+	} while ((reg != power >> 2) && (retry_count < 100));
+
+	if (retry_count == 100) {
+		printk(KERN_ERR "CSI2 CIO set power failed!\n");
+		return -EBUSY;
+	}
+
+	return 0;
+}
+
+/*
+ * csiphy_dphy_config - Configure CSI2 D-PHY parameters.
+ *
+ * Called with phy->mutex taken.
+ */
+static void csiphy_dphy_config(struct iss_csiphy *phy)
+{
+	u32 reg;
+
+	/* Set up REGISTER0 */
+	reg = readl(phy->phy_regs + REGISTER0);
+
+	reg &= ~(REGISTER0_THS_TERM_MASK |
+		 REGISTER0_THS_SETTLE_MASK);
+	reg |= phy->dphy.ths_term << REGISTER0_THS_TERM_SHIFT;
+	reg |= phy->dphy.ths_settle << REGISTER0_THS_SETTLE_SHIFT;
+
+	writel(reg, phy->phy_regs + REGISTER0);
+
+	/* Set up REGISTER1 */
+	reg = readl(phy->phy_regs + REGISTER1);
+
+	reg &= ~(REGISTER1_TCLK_TERM_MASK |
+		 REGISTER1_CTRLCLK_DIV_FACTOR_MASK |
+		 REGISTER1_TCLK_SETTLE_MASK);
+	reg |= phy->dphy.tclk_term << REGISTER1_TCLK_TERM_SHIFT;
+	reg |= phy->dphy.tclk_miss << REGISTER1_CTRLCLK_DIV_FACTOR_SHIFT;
+	reg |= phy->dphy.tclk_settle << REGISTER1_TCLK_SETTLE_SHIFT;
+
+	writel(reg, phy->phy_regs + REGISTER1);
+}
+
+static int csiphy_config(struct iss_csiphy *phy,
+			 struct iss_csiphy_dphy_cfg *dphy,
+			 struct iss_csiphy_lanes_cfg *lanes)
+{
+	unsigned int used_lanes = 0;
+	unsigned int i;
+
+	/* Clock and data lanes verification */
+	for (i = 0; i < phy->num_data_lanes; i++) {
+		if (lanes->data[i].pos == 0)
+			continue;
+
+		if (lanes->data[i].pol > 1 || lanes->data[i].pos > 5)
+			return -EINVAL;
+
+		if (used_lanes & (1 << lanes->data[i].pos))
+			return -EINVAL;
+
+		used_lanes |= 1 << lanes->data[i].pos;
+	}
+
+	if (lanes->clk.pol > 1 || lanes->clk.pos > 4)
+		return -EINVAL;
+
+	if (lanes->clk.pos == 0 || used_lanes & (1 << lanes->clk.pos))
+		return -EINVAL;
+
+	mutex_lock(&phy->mutex);
+	phy->dphy = *dphy;
+	phy->lanes = *lanes;
+	mutex_unlock(&phy->mutex);
+
+	return 0;
+}
+
+int omap4iss_csiphy_acquire(struct iss_csiphy *phy)
+{
+	int rval;
+
+	mutex_lock(&phy->mutex);
+
+	rval = omap4iss_csi2_reset(phy->csi2);
+	if (rval)
+		goto done;
+
+	csiphy_dphy_config(phy);
+	csiphy_lanes_config(phy);
+
+	rval = csiphy_set_power(phy, CSI2_COMPLEXIO_CFG_PWD_CMD_ON);
+	if (rval)
+		goto done;
+
+	phy->phy_in_use = 1;
+
+done:
+	mutex_unlock(&phy->mutex);
+	return rval;
+}
+
+void omap4iss_csiphy_release(struct iss_csiphy *phy)
+{
+	mutex_lock(&phy->mutex);
+	if (phy->phy_in_use) {
+		csiphy_set_power(phy, CSI2_COMPLEXIO_CFG_PWD_CMD_OFF);
+		phy->phy_in_use = 0;
+	}
+	mutex_unlock(&phy->mutex);
+}
+
+/*
+ * omap4iss_csiphy_init - Initialize the CSI PHY frontends
+ */
+int omap4iss_csiphy_init(struct iss_device *iss)
+{
+	struct iss_csiphy *phy1 = &iss->csiphy1;
+
+	iss->platform_cb.csiphy_config = csiphy_config;
+
+	phy1->iss = iss;
+	phy1->csi2 = &iss->csi2a;
+	phy1->num_data_lanes = ISS_CSIPHY1_NUM_DATA_LANES;
+	phy1->cfg_regs = iss->regs[OMAP4_ISS_MEM_CSI2_A_REGS1];
+	phy1->phy_regs = iss->regs[OMAP4_ISS_MEM_CAMERARX_CORE1];
+	mutex_init(&phy1->mutex);
+
+	return 0;
+}
diff --git a/drivers/media/video/omap4iss/iss_csiphy.h b/drivers/media/video/omap4iss/iss_csiphy.h
new file mode 100644
index 0000000..c513ba8
--- /dev/null
+++ b/drivers/media/video/omap4iss/iss_csiphy.h
@@ -0,0 +1,69 @@
+/*
+ * iss_csiphy.h
+ *
+ * TI OMAP4 ISS - CSI PHY module
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ *
+ * Contacts: Sergio Aguirre <saaguirre@ti.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef OMAP4_ISS_CSI_PHY_H
+#define OMAP4_ISS_CSI_PHY_H
+
+struct iss_csi2_device;
+
+struct csiphy_lane {
+	u8 pos;
+	u8 pol;
+};
+
+#define ISS_CSIPHY1_NUM_DATA_LANES	4
+
+struct iss_csiphy_lanes_cfg {
+	struct csiphy_lane data[ISS_CSIPHY1_NUM_DATA_LANES];
+	struct csiphy_lane clk;
+};
+
+struct iss_csiphy_dphy_cfg {
+	u8 ths_term;
+	u8 ths_settle;
+	u8 tclk_term;
+	unsigned tclk_miss:1;
+	u8 tclk_settle;
+};
+
+struct iss_csiphy {
+	struct iss_device *iss;
+	struct mutex mutex;	/* serialize csiphy configuration */
+	u8 phy_in_use;
+	struct iss_csi2_device *csi2;
+
+	/* Pointer to register remaps into kernel space */
+	void __iomem *cfg_regs;
+	void __iomem *phy_regs;
+
+	u8 num_data_lanes;	/* number of CSI2 Data Lanes supported */
+	struct iss_csiphy_lanes_cfg lanes;
+	struct iss_csiphy_dphy_cfg dphy;
+};
+
+int omap4iss_csiphy_acquire(struct iss_csiphy *phy);
+void omap4iss_csiphy_release(struct iss_csiphy *phy);
+int omap4iss_csiphy_init(struct iss_device *iss);
+
+#endif	/* OMAP4_ISS_CSI_PHY_H */
diff --git a/drivers/media/video/omap4iss/iss_regs.h b/drivers/media/video/omap4iss/iss_regs.h
new file mode 100644
index 0000000..0bd70ac
--- /dev/null
+++ b/drivers/media/video/omap4iss/iss_regs.h
@@ -0,0 +1,238 @@
+/*
+ * iss_regs.h
+ *
+ * Copyright (C) 2011 Texas Instruments.
+ *
+ * Author: Sergio Aguirre <saaguirre@ti.com>
+ */
+
+#ifndef _OMAP4_ISS_REGS_H_
+#define _OMAP4_ISS_REGS_H_
+
+/* ISS */
+#define ISS_HL_REVISION					(0x0)
+
+#define ISS_HL_SYSCONFIG				(0x10)
+#define ISS_HL_SYSCONFIG_IDLEMODE_SHIFT			2
+#define ISS_HL_SYSCONFIG_IDLEMODE_FORCEIDLE		0x0
+#define ISS_HL_SYSCONFIG_IDLEMODE_NOIDLE		0x1
+#define ISS_HL_SYSCONFIG_IDLEMODE_SMARTIDLE		0x2
+#define ISS_HL_SYSCONFIG_SOFTRESET			(1 << 0)
+
+#define ISS_HL_IRQSTATUS_5				(0x24 + (0x10 * 5))
+#define ISS_HL_IRQENABLE_5_SET				(0x28 + (0x10 * 5))
+#define ISS_HL_IRQENABLE_5_CLR				(0x2C + (0x10 * 5))
+
+#define ISS_HL_IRQ_BTE					(1 << 11)
+#define ISS_HL_IRQ_CBUFF				(1 << 10)
+#define ISS_HL_IRQ_CSIA					(1 << 4)
+
+#define ISS_CTRL					(0x80)
+
+#define ISS_CLKCTRL					(0x84)
+#define ISS_CLKCTRL_VPORT2_CLK				(1 << 30)
+#define ISS_CLKCTRL_VPORT1_CLK				(1 << 29)
+#define ISS_CLKCTRL_VPORT0_CLK				(1 << 28)
+#define ISS_CLKCTRL_CCP2				(1 << 4)
+#define ISS_CLKCTRL_CSI2_B				(1 << 3)
+#define ISS_CLKCTRL_CSI2_A				(1 << 2)
+#define ISS_CLKCTRL_ISP					(1 << 1)
+#define ISS_CLKCTRL_SIMCOP				(1 << 0)
+
+#define ISS_CLKSTAT					(0x88)
+#define ISS_CLKSTAT_VPORT2_CLK				(1 << 30)
+#define ISS_CLKSTAT_VPORT1_CLK				(1 << 29)
+#define ISS_CLKSTAT_VPORT0_CLK				(1 << 28)
+#define ISS_CLKSTAT_CCP2				(1 << 4)
+#define ISS_CLKSTAT_CSI2_B				(1 << 3)
+#define ISS_CLKSTAT_CSI2_A				(1 << 2)
+#define ISS_CLKSTAT_ISP					(1 << 1)
+#define ISS_CLKSTAT_SIMCOP				(1 << 0)
+
+#define ISS_PM_STATUS					(0x8C)
+#define ISS_PM_STATUS_CBUFF_PM_MASK			(3 << 12)
+#define ISS_PM_STATUS_BTE_PM_MASK			(3 << 10)
+#define ISS_PM_STATUS_SIMCOP_PM_MASK			(3 << 8)
+#define ISS_PM_STATUS_ISP_PM_MASK			(3 << 6)
+#define ISS_PM_STATUS_CCP2_PM_MASK			(3 << 4)
+#define ISS_PM_STATUS_CSI2_B_PM_MASK			(3 << 2)
+#define ISS_PM_STATUS_CSI2_A_PM_MASK			(3 << 0)
+
+#define REGISTER0					(0x0)
+#define REGISTER0_HSCLOCKCONFIG				(1 << 24)
+#define REGISTER0_THS_TERM_MASK				(0xFF << 8)
+#define REGISTER0_THS_TERM_SHIFT			8
+#define REGISTER0_THS_SETTLE_MASK			(0xFF << 0)
+#define REGISTER0_THS_SETTLE_SHIFT			0
+
+#define REGISTER1					(0x4)
+#define REGISTER1_RESET_DONE_CTRLCLK			(1 << 29)
+#define REGISTER1_CLOCK_MISS_DETECTOR_STATUS		(1 << 25)
+#define REGISTER1_TCLK_TERM_MASK			(0x3F << 18)
+#define REGISTER1_TCLK_TERM_SHIFT			18
+#define REGISTER1_DPHY_HS_SYNC_PATTERN_MASK		(0xFF << 10)
+#define REGISTER1_CTRLCLK_DIV_FACTOR_MASK		(0x3 << 8)
+#define REGISTER1_CTRLCLK_DIV_FACTOR_SHIFT		8
+#define REGISTER1_TCLK_SETTLE_MASK			(0xFF << 0)
+#define REGISTER1_TCLK_SETTLE_SHIFT			0
+
+#define REGISTER2					(0x8)
+
+#define CSI2_SYSCONFIG					(0x10)
+#define CSI2_SYSCONFIG_MSTANDBY_MODE_MASK		(3 << 12)
+#define CSI2_SYSCONFIG_MSTANDBY_MODE_FORCE		(0 << 12)
+#define CSI2_SYSCONFIG_MSTANDBY_MODE_NO			(1 << 12)
+#define CSI2_SYSCONFIG_MSTANDBY_MODE_SMART		(2 << 12)
+#define CSI2_SYSCONFIG_SOFT_RESET			(1 << 1)
+#define CSI2_SYSCONFIG_AUTO_IDLE			(1 << 0)
+
+#define CSI2_SYSSTATUS					(0x14)
+#define CSI2_SYSSTATUS_RESET_DONE			(1 << 0)
+
+#define CSI2_IRQSTATUS					(0x18)
+#define CSI2_IRQENABLE					(0x1C)
+
+/* Shared bits across CSI2_IRQENABLE and IRQSTATUS */
+
+#define CSI2_IRQ_OCP_ERR				(1 << 14)
+#define CSI2_IRQ_SHORT_PACKET				(1 << 13)
+#define CSI2_IRQ_ECC_CORRECTION				(1 << 12)
+#define CSI2_IRQ_ECC_NO_CORRECTION			(1 << 11)
+#define CSI2_IRQ_COMPLEXIO_ERR				(1 << 9)
+#define CSI2_IRQ_FIFO_OVF				(1 << 8)
+#define CSI2_IRQ_CONTEXT0				(1 << 0)
+
+#define CSI2_CTRL					(0x40)
+#define CSI2_CTRL_MFLAG_LEVH_MASK			(7 << 20)
+#define CSI2_CTRL_MFLAG_LEVH_SHIFT			20
+#define CSI2_CTRL_MFLAG_LEVL_MASK			(7 << 17)
+#define CSI2_CTRL_MFLAG_LEVL_SHIFT			17
+#define CSI2_CTRL_BURST_SIZE_EXPAND			(1 << 16)
+#define CSI2_CTRL_VP_CLK_EN				(1 << 15)
+#define CSI2_CTRL_NON_POSTED_WRITE			(1 << 13)
+#define CSI2_CTRL_VP_ONLY_EN				(1 << 11)
+#define CSI2_CTRL_VP_OUT_CTRL_MASK			(3 << 8)
+#define CSI2_CTRL_VP_OUT_CTRL_SHIFT			8
+#define CSI2_CTRL_DBG_EN				(1 << 7)
+#define CSI2_CTRL_BURST_SIZE_MASK			(3 << 5)
+#define CSI2_CTRL_ENDIANNESS				(1 << 4)
+#define CSI2_CTRL_FRAME					(1 << 3)
+#define CSI2_CTRL_ECC_EN				(1 << 2)
+#define CSI2_CTRL_IF_EN					(1 << 0)
+
+#define CSI2_DBG_H					(0x44)
+
+#define CSI2_COMPLEXIO_CFG				(0x50)
+#define CSI2_COMPLEXIO_CFG_RESET_CTRL			(1 << 30)
+#define CSI2_COMPLEXIO_CFG_RESET_DONE			(1 << 29)
+#define CSI2_COMPLEXIO_CFG_PWD_CMD_MASK			(3 << 27)
+#define CSI2_COMPLEXIO_CFG_PWD_CMD_OFF			(0 << 27)
+#define CSI2_COMPLEXIO_CFG_PWD_CMD_ON			(1 << 27)
+#define CSI2_COMPLEXIO_CFG_PWD_CMD_ULP			(2 << 27)
+#define CSI2_COMPLEXIO_CFG_PWD_STATUS_MASK		(3 << 25)
+#define CSI2_COMPLEXIO_CFG_PWD_STATUS_OFF		(0 << 25)
+#define CSI2_COMPLEXIO_CFG_PWD_STATUS_ON		(1 << 25)
+#define CSI2_COMPLEXIO_CFG_PWD_STATUS_ULP		(2 << 25)
+#define CSI2_COMPLEXIO_CFG_PWR_AUTO			(1 << 24)
+#define CSI2_COMPLEXIO_CFG_DATA_POL(i)			(1 << (((i) * 4) + 3))
+#define CSI2_COMPLEXIO_CFG_DATA_POSITION_MASK(i)	(7 << ((i) * 4))
+#define CSI2_COMPLEXIO_CFG_DATA_POSITION_SHIFT(i)	((i) * 4)
+#define CSI2_COMPLEXIO_CFG_CLOCK_POL			(1 << 3)
+#define CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK		(7 << 0)
+#define CSI2_COMPLEXIO_CFG_CLOCK_POSITION_SHIFT		0
+
+#define CSI2_COMPLEXIO_IRQSTATUS			(0x54)
+
+#define CSI2_SHORT_PACKET				(0x5C)
+
+#define CSI2_COMPLEXIO_IRQENABLE			(0x60)
+
+/* Shared bits across CSI2_COMPLEXIO_IRQENABLE and IRQSTATUS */
+#define CSI2_COMPLEXIO_IRQ_STATEALLULPMEXIT		(1 << 26)
+#define CSI2_COMPLEXIO_IRQ_STATEALLULPMENTER		(1 << 25)
+#define CSI2_COMPLEXIO_IRQ_STATEULPM5			(1 << 24)
+#define CSI2_COMPLEXIO_IRQ_STATEULPM4			(1 << 23)
+#define CSI2_COMPLEXIO_IRQ_STATEULPM3			(1 << 22)
+#define CSI2_COMPLEXIO_IRQ_STATEULPM2			(1 << 21)
+#define CSI2_COMPLEXIO_IRQ_STATEULPM1			(1 << 20)
+#define CSI2_COMPLEXIO_IRQ_ERRCONTROL5			(1 << 19)
+#define CSI2_COMPLEXIO_IRQ_ERRCONTROL4			(1 << 18)
+#define CSI2_COMPLEXIO_IRQ_ERRCONTROL3			(1 << 17)
+#define CSI2_COMPLEXIO_IRQ_ERRCONTROL2			(1 << 16)
+#define CSI2_COMPLEXIO_IRQ_ERRCONTROL1			(1 << 15)
+#define CSI2_COMPLEXIO_IRQ_ERRESC5			(1 << 14)
+#define CSI2_COMPLEXIO_IRQ_ERRESC4			(1 << 13)
+#define CSI2_COMPLEXIO_IRQ_ERRESC3			(1 << 12)
+#define CSI2_COMPLEXIO_IRQ_ERRESC2			(1 << 11)
+#define CSI2_COMPLEXIO_IRQ_ERRESC1			(1 << 10)
+#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS5		(1 << 9)
+#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS4		(1 << 8)
+#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS3		(1 << 7)
+#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS2		(1 << 6)
+#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS1		(1 << 5)
+#define CSI2_COMPLEXIO_IRQ_ERRSOTHS5			(1 << 4)
+#define CSI2_COMPLEXIO_IRQ_ERRSOTHS4			(1 << 3)
+#define CSI2_COMPLEXIO_IRQ_ERRSOTHS3			(1 << 2)
+#define CSI2_COMPLEXIO_IRQ_ERRSOTHS2			(1 << 1)
+#define CSI2_COMPLEXIO_IRQ_ERRSOTHS1			(1 << 0)
+
+#define CSI2_DBG_P					(0x68)
+
+#define CSI2_TIMING					(0x6C)
+#define CSI2_TIMING_FORCE_RX_MODE_IO1			(1 << 15)
+#define CSI2_TIMING_STOP_STATE_X16_IO1			(1 << 14)
+#define CSI2_TIMING_STOP_STATE_X4_IO1			(1 << 13)
+#define CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK		(0x1FFF << 0)
+#define CSI2_TIMING_STOP_STATE_COUNTER_IO1_SHIFT	0
+
+#define CSI2_CTX_CTRL1(i)				(0x70 + (0x20 * i))
+#define CSI2_CTX_CTRL1_GENERIC				(1 << 30)
+#define CSI2_CTX_CTRL1_TRANSCODE			(0xF << 24)
+#define CSI2_CTX_CTRL1_FEC_NUMBER_MASK			(0xFF << 16)
+#define CSI2_CTX_CTRL1_COUNT_MASK			(0xFF << 8)
+#define CSI2_CTX_CTRL1_COUNT_SHIFT			8
+#define CSI2_CTX_CTRL1_EOF_EN				(1 << 7)
+#define CSI2_CTX_CTRL1_EOL_EN				(1 << 6)
+#define CSI2_CTX_CTRL1_CS_EN				(1 << 5)
+#define CSI2_CTX_CTRL1_COUNT_UNLOCK			(1 << 4)
+#define CSI2_CTX_CTRL1_PING_PONG			(1 << 3)
+#define CSI2_CTX_CTRL1_CTX_EN				(1 << 0)
+
+#define CSI2_CTX_CTRL2(i)				(0x74 + (0x20 * i))
+#define CSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT		13
+#define CSI2_CTX_CTRL2_USER_DEF_MAP_MASK		\
+		(0x3 << CSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT)
+#define CSI2_CTX_CTRL2_VIRTUAL_ID_MASK			(3 << 11)
+#define CSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT			11
+#define CSI2_CTX_CTRL2_DPCM_PRED			(1 << 10)
+#define CSI2_CTX_CTRL2_FORMAT_MASK			(0x3FF << 0)
+#define CSI2_CTX_CTRL2_FORMAT_SHIFT			0
+
+#define CSI2_CTX_DAT_OFST(i)				(0x78 + (0x20 * i))
+#define CSI2_CTX_DAT_OFST_MASK				(0xFFF << 5)
+
+#define CSI2_CTX_PING_ADDR(i)				(0x7C + (0x20 * i))
+#define CSI2_CTX_PING_ADDR_MASK				0xFFFFFFE0
+
+#define CSI2_CTX_PONG_ADDR(i)				(0x80 + (0x20 * i))
+#define CSI2_CTX_PONG_ADDR_MASK				CSI2_CTX_PING_ADDR_MASK
+
+#define CSI2_CTX_IRQENABLE(i)				(0x84 + (0x20 * i))
+#define CSI2_CTX_IRQSTATUS(i)				(0x88 + (0x20 * i))
+
+#define CSI2_CTX_CTRL3(i)				(0x8C + (0x20 * i))
+#define CSI2_CTX_CTRL3_ALPHA_SHIFT			5
+#define CSI2_CTX_CTRL3_ALPHA_MASK			\
+		(0x3fff << CSI2_CTX_CTRL3_ALPHA_SHIFT)
+
+/* Shared bits across CSI2_CTX_IRQENABLE and IRQSTATUS */
+#define CSI2_CTX_IRQ_ECC_CORRECTION			(1 << 8)
+#define CSI2_CTX_IRQ_LINE_NUMBER			(1 << 7)
+#define CSI2_CTX_IRQ_FRAME_NUMBER			(1 << 6)
+#define CSI2_CTX_IRQ_CS					(1 << 5)
+#define CSI2_CTX_IRQ_LE					(1 << 3)
+#define CSI2_CTX_IRQ_LS					(1 << 2)
+#define CSI2_CTX_IRQ_FE					(1 << 1)
+#define CSI2_CTX_IRQ_FS					(1 << 0)
+
+#endif /* _OMAP4_CAMERA_REGS_H_ */
diff --git a/drivers/media/video/omap4iss/iss_video.c b/drivers/media/video/omap4iss/iss_video.c
new file mode 100644
index 0000000..3248711
--- /dev/null
+++ b/drivers/media/video/omap4iss/iss_video.c
@@ -0,0 +1,1192 @@
+/*
+ * iss_video.c
+ *
+ * TI OMAP4 ISS - Generic video node
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ *
+ * Contacts: Sergio Aguirre <saaguirre@ti.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <asm/cacheflush.h>
+#include <linux/clk.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/module.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+#include <plat/omap-pm.h>
+
+#include "iss_video.h"
+#include "iss.h"
+
+
+/* -----------------------------------------------------------------------------
+ * Helper functions
+ */
+
+static struct iss_format_info formats[] = {
+	{ V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8,
+	  V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8,
+	  V4L2_PIX_FMT_GREY, 8, },
+	{ V4L2_MBUS_FMT_Y10_1X10, V4L2_MBUS_FMT_Y10_1X10,
+	  V4L2_MBUS_FMT_Y10_1X10, V4L2_MBUS_FMT_Y8_1X8,
+	  V4L2_PIX_FMT_Y10, 10, },
+	{ V4L2_MBUS_FMT_Y12_1X12, V4L2_MBUS_FMT_Y10_1X10,
+	  V4L2_MBUS_FMT_Y12_1X12, V4L2_MBUS_FMT_Y8_1X8,
+	  V4L2_PIX_FMT_Y12, 12, },
+	{ V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_MBUS_FMT_SBGGR8_1X8,
+	  V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_MBUS_FMT_SBGGR8_1X8,
+	  V4L2_PIX_FMT_SBGGR8, 8, },
+	{ V4L2_MBUS_FMT_SGBRG8_1X8, V4L2_MBUS_FMT_SGBRG8_1X8,
+	  V4L2_MBUS_FMT_SGBRG8_1X8, V4L2_MBUS_FMT_SGBRG8_1X8,
+	  V4L2_PIX_FMT_SGBRG8, 8, },
+	{ V4L2_MBUS_FMT_SGRBG8_1X8, V4L2_MBUS_FMT_SGRBG8_1X8,
+	  V4L2_MBUS_FMT_SGRBG8_1X8, V4L2_MBUS_FMT_SGRBG8_1X8,
+	  V4L2_PIX_FMT_SGRBG8, 8, },
+	{ V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8,
+	  V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8,
+	  V4L2_PIX_FMT_SRGGB8, 8, },
+	{ V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8,
+	  V4L2_MBUS_FMT_SGRBG10_1X10, 0,
+	  V4L2_PIX_FMT_SGRBG10DPCM8, 8, },
+	{ V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR10_1X10,
+	  V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR8_1X8,
+	  V4L2_PIX_FMT_SBGGR10, 10, },
+	{ V4L2_MBUS_FMT_SGBRG10_1X10, V4L2_MBUS_FMT_SGBRG10_1X10,
+	  V4L2_MBUS_FMT_SGBRG10_1X10, V4L2_MBUS_FMT_SGBRG8_1X8,
+	  V4L2_PIX_FMT_SGBRG10, 10, },
+	{ V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_MBUS_FMT_SGRBG10_1X10,
+	  V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_MBUS_FMT_SGRBG8_1X8,
+	  V4L2_PIX_FMT_SGRBG10, 10, },
+	{ V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_MBUS_FMT_SRGGB10_1X10,
+	  V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_MBUS_FMT_SRGGB8_1X8,
+	  V4L2_PIX_FMT_SRGGB10, 10, },
+	{ V4L2_MBUS_FMT_SBGGR12_1X12, V4L2_MBUS_FMT_SBGGR10_1X10,
+	  V4L2_MBUS_FMT_SBGGR12_1X12, V4L2_MBUS_FMT_SBGGR8_1X8,
+	  V4L2_PIX_FMT_SBGGR12, 12, },
+	{ V4L2_MBUS_FMT_SGBRG12_1X12, V4L2_MBUS_FMT_SGBRG10_1X10,
+	  V4L2_MBUS_FMT_SGBRG12_1X12, V4L2_MBUS_FMT_SGBRG8_1X8,
+	  V4L2_PIX_FMT_SGBRG12, 12, },
+	{ V4L2_MBUS_FMT_SGRBG12_1X12, V4L2_MBUS_FMT_SGRBG10_1X10,
+	  V4L2_MBUS_FMT_SGRBG12_1X12, V4L2_MBUS_FMT_SGRBG8_1X8,
+	  V4L2_PIX_FMT_SGRBG12, 12, },
+	{ V4L2_MBUS_FMT_SRGGB12_1X12, V4L2_MBUS_FMT_SRGGB10_1X10,
+	  V4L2_MBUS_FMT_SRGGB12_1X12, V4L2_MBUS_FMT_SRGGB8_1X8,
+	  V4L2_PIX_FMT_SRGGB12, 12, },
+	{ V4L2_MBUS_FMT_UYVY8_1X16, V4L2_MBUS_FMT_UYVY8_1X16,
+	  V4L2_MBUS_FMT_UYVY8_1X16, 0,
+	  V4L2_PIX_FMT_UYVY, 16, },
+	{ V4L2_MBUS_FMT_YUYV8_1X16, V4L2_MBUS_FMT_YUYV8_1X16,
+	  V4L2_MBUS_FMT_YUYV8_1X16, 0,
+	  V4L2_PIX_FMT_YUYV, 16, },
+};
+
+const struct iss_format_info *
+omap4iss_video_format_info(enum v4l2_mbus_pixelcode code)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(formats); ++i) {
+		if (formats[i].code == code)
+			return &formats[i];
+	}
+
+	return NULL;
+}
+
+/*
+ * iss_video_mbus_to_pix - Convert v4l2_mbus_framefmt to v4l2_pix_format
+ * @video: ISS video instance
+ * @mbus: v4l2_mbus_framefmt format (input)
+ * @pix: v4l2_pix_format format (output)
+ *
+ * Fill the output pix structure with information from the input mbus format.
+ * The bytesperline and sizeimage fields are computed from the requested bytes
+ * per line value in the pix format and information from the video instance.
+ *
+ * Return the number of padding bytes at end of line.
+ */
+static unsigned int iss_video_mbus_to_pix(const struct iss_video *video,
+					  const struct v4l2_mbus_framefmt *mbus,
+					  struct v4l2_pix_format *pix)
+{
+	unsigned int bpl = pix->bytesperline;
+	unsigned int min_bpl;
+	unsigned int i;
+
+	memset(pix, 0, sizeof(*pix));
+	pix->width = mbus->width;
+	pix->height = mbus->height;
+
+	for (i = 0; i < ARRAY_SIZE(formats); ++i) {
+		if (formats[i].code == mbus->code)
+			break;
+	}
+
+	if (WARN_ON(i == ARRAY_SIZE(formats)))
+		return 0;
+
+	min_bpl = pix->width * ALIGN(formats[i].bpp, 8) / 8;
+
+	/* Clamp the requested bytes per line value. If the maximum bytes per
+	 * line value is zero, the module doesn't support user configurable line
+	 * sizes. Override the requested value with the minimum in that case.
+	 */
+	if (video->bpl_max)
+		bpl = clamp(bpl, min_bpl, video->bpl_max);
+	else
+		bpl = min_bpl;
+
+	if (!video->bpl_zero_padding || bpl != min_bpl)
+		bpl = ALIGN(bpl, video->bpl_alignment);
+
+	pix->pixelformat = formats[i].pixelformat;
+	pix->bytesperline = bpl;
+	pix->sizeimage = pix->bytesperline * pix->height;
+	pix->colorspace = mbus->colorspace;
+	pix->field = mbus->field;
+
+	return bpl - min_bpl;
+}
+
+static void iss_video_pix_to_mbus(const struct v4l2_pix_format *pix,
+				  struct v4l2_mbus_framefmt *mbus)
+{
+	unsigned int i;
+
+	memset(mbus, 0, sizeof(*mbus));
+	mbus->width = pix->width;
+	mbus->height = pix->height;
+
+	for (i = 0; i < ARRAY_SIZE(formats); ++i) {
+		if (formats[i].pixelformat == pix->pixelformat)
+			break;
+	}
+
+	if (WARN_ON(i == ARRAY_SIZE(formats)))
+		return;
+
+	mbus->code = formats[i].code;
+	mbus->colorspace = pix->colorspace;
+	mbus->field = pix->field;
+}
+
+static struct v4l2_subdev *
+iss_video_remote_subdev(struct iss_video *video, u32 *pad)
+{
+	struct media_pad *remote;
+
+	remote = media_entity_remote_source(&video->pad);
+
+	if (remote == NULL ||
+	    media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+		return NULL;
+
+	if (pad)
+		*pad = remote->index;
+
+	return media_entity_to_v4l2_subdev(remote->entity);
+}
+
+/* Return a pointer to the ISS video instance at the far end of the pipeline. */
+static struct iss_video *
+iss_video_far_end(struct iss_video *video)
+{
+	struct media_entity_graph graph;
+	struct media_entity *entity = &video->video.entity;
+	struct media_device *mdev = entity->parent;
+	struct iss_video *far_end = NULL;
+
+	mutex_lock(&mdev->graph_mutex);
+	media_entity_graph_walk_start(&graph, entity);
+
+	while ((entity = media_entity_graph_walk_next(&graph))) {
+		if (entity == &video->video.entity)
+			continue;
+
+		if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE)
+			continue;
+
+		far_end = to_iss_video(media_entity_to_video_device(entity));
+		if (far_end->type != video->type)
+			break;
+
+		far_end = NULL;
+	}
+
+	mutex_unlock(&mdev->graph_mutex);
+	return far_end;
+}
+
+/*
+ * Validate a pipeline by checking both ends of all links for format
+ * discrepancies.
+ *
+ * Compute the minimum time per frame value as the maximum of time per frame
+ * limits reported by every block in the pipeline.
+ *
+ * Return 0 if all formats match, or -EPIPE if at least one link is found with
+ * different formats on its two ends.
+ */
+static int iss_video_validate_pipeline(struct iss_pipeline *pipe)
+{
+	struct v4l2_subdev_format fmt_source;
+	struct v4l2_subdev_format fmt_sink;
+	struct media_pad *pad;
+	struct v4l2_subdev *subdev;
+	int ret;
+
+	subdev = iss_video_remote_subdev(pipe->output, NULL);
+	if (subdev == NULL)
+		return -EPIPE;
+
+	while (1) {
+		/* Retrieve the sink format */
+		pad = &subdev->entity.pads[0];
+		if (!(pad->flags & MEDIA_PAD_FL_SINK))
+			break;
+
+		fmt_sink.pad = pad->index;
+		fmt_sink.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+		ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt_sink);
+		if (ret < 0 && ret != -ENOIOCTLCMD)
+			return -EPIPE;
+
+		/* Retrieve the source format */
+		pad = media_entity_remote_source(pad);
+		if (pad == NULL ||
+		    media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+			break;
+
+		subdev = media_entity_to_v4l2_subdev(pad->entity);
+
+		fmt_source.pad = pad->index;
+		fmt_source.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+		ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt_source);
+		if (ret < 0 && ret != -ENOIOCTLCMD)
+			return -EPIPE;
+
+		/* Check if the two ends match */
+		if (fmt_source.format.width != fmt_sink.format.width ||
+		    fmt_source.format.height != fmt_sink.format.height)
+			return -EPIPE;
+
+		if (fmt_source.format.code != fmt_sink.format.code)
+			return -EPIPE;
+	}
+
+	return 0;
+}
+
+static int
+__iss_video_get_format(struct iss_video *video, struct v4l2_format *format)
+{
+	struct v4l2_subdev_format fmt;
+	struct v4l2_subdev *subdev;
+	u32 pad;
+	int ret;
+
+	subdev = iss_video_remote_subdev(video, &pad);
+	if (subdev == NULL)
+		return -EINVAL;
+
+	mutex_lock(&video->mutex);
+
+	fmt.pad = pad;
+	fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+	ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt);
+	if (ret == -ENOIOCTLCMD)
+		ret = -EINVAL;
+
+	mutex_unlock(&video->mutex);
+
+	if (ret)
+		return ret;
+
+	format->type = video->type;
+	return iss_video_mbus_to_pix(video, &fmt.format, &format->fmt.pix);
+}
+
+static int
+iss_video_check_format(struct iss_video *video, struct iss_video_fh *vfh)
+{
+	struct v4l2_format format;
+	int ret;
+
+	memcpy(&format, &vfh->format, sizeof(format));
+	ret = __iss_video_get_format(video, &format);
+	if (ret < 0)
+		return ret;
+
+	if (vfh->format.fmt.pix.pixelformat != format.fmt.pix.pixelformat ||
+	    vfh->format.fmt.pix.height != format.fmt.pix.height ||
+	    vfh->format.fmt.pix.width != format.fmt.pix.width ||
+	    vfh->format.fmt.pix.bytesperline != format.fmt.pix.bytesperline ||
+	    vfh->format.fmt.pix.sizeimage != format.fmt.pix.sizeimage)
+		return -EINVAL;
+
+	return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * Video queue operations
+ */
+
+static int iss_video_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+				 unsigned int *count, unsigned int *num_planes,
+				 unsigned int sizes[], void *alloc_ctxs[])
+{
+	struct iss_video_fh *vfh = vb2_get_drv_priv(vq);
+	struct iss_video *video = vfh->video;
+
+	/* Revisit multi-planar support for NV12 */
+	*num_planes = 1;
+
+	sizes[0] = vfh->format.fmt.pix.sizeimage;
+	if (sizes[0] == 0)
+		return -EINVAL;
+
+	alloc_ctxs[0] = video->alloc_ctx;
+
+	*count = min(*count, (unsigned int)(video->capture_mem / PAGE_ALIGN(sizes[0])));
+
+	return 0;
+}
+
+static void iss_video_buf_cleanup(struct vb2_buffer *vb)
+{
+	struct iss_buffer *buffer = container_of(vb, struct iss_buffer, vb);
+
+	if (buffer->iss_addr)
+		buffer->iss_addr = 0;
+}
+
+static int iss_video_buf_prepare(struct vb2_buffer *vb)
+{
+	struct iss_video_fh *vfh = vb2_get_drv_priv(vb->vb2_queue);
+	struct iss_buffer *buffer = container_of(vb, struct iss_buffer, vb);
+	struct iss_video *video = vfh->video;
+	unsigned long size = vfh->format.fmt.pix.sizeimage;
+	dma_addr_t addr;
+
+	if (vb2_plane_size(vb, 0) < size)
+		return -ENOBUFS;
+
+	addr = vb2_dma_contig_plane_dma_addr(vb, 0);
+	if (!IS_ALIGNED(addr, 32)) {
+		dev_dbg(video->iss->dev, "Buffer address must be "
+			"aligned to 32 bytes boundary.\n");
+		return -EINVAL;
+	}
+
+	vb2_set_plane_payload(vb, 0, size);
+	buffer->iss_addr = addr;
+	return 0;
+}
+
+static void iss_video_buf_queue(struct vb2_buffer *vb)
+{
+	struct iss_video_fh *vfh = vb2_get_drv_priv(vb->vb2_queue);
+	struct iss_video *video = vfh->video;
+	struct iss_buffer *buffer = container_of(vb, struct iss_buffer, vb);
+	struct iss_pipeline *pipe = to_iss_pipeline(&video->video.entity);
+	unsigned int empty;
+	unsigned long flags;
+
+	spin_lock_irqsave(&video->qlock, flags);
+	empty = list_empty(&video->dmaqueue);
+	list_add_tail(&buffer->list, &video->dmaqueue);
+	spin_unlock_irqrestore(&video->qlock, flags);
+
+	if (empty) {
+		enum iss_pipeline_state state;
+		unsigned int start;
+
+		if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			state = ISS_PIPELINE_QUEUE_OUTPUT;
+		else
+			state = ISS_PIPELINE_QUEUE_INPUT;
+
+		spin_lock_irqsave(&pipe->lock, flags);
+		pipe->state |= state;
+		video->ops->queue(video, buffer);
+		video->dmaqueue_flags |= ISS_VIDEO_DMAQUEUE_QUEUED;
+
+		start = iss_pipeline_ready(pipe);
+		if (start)
+			pipe->state |= ISS_PIPELINE_STREAM;
+		spin_unlock_irqrestore(&pipe->lock, flags);
+
+		if (start)
+			omap4iss_pipeline_set_stream(pipe,
+						ISS_PIPELINE_STREAM_SINGLESHOT);
+	}
+}
+
+static struct vb2_ops iss_video_vb2ops = {
+	.queue_setup	= iss_video_queue_setup,
+	.buf_prepare	= iss_video_buf_prepare,
+	.buf_queue	= iss_video_buf_queue,
+	.buf_cleanup	= iss_video_buf_cleanup,
+};
+
+/*
+ * omap4iss_video_buffer_next - Complete the current buffer and return the next
+ * @video: ISS video object
+ * @error: Whether an error occurred during capture
+ *
+ * Remove the current video buffer from the DMA queue and fill its timestamp,
+ * field count and state fields before waking up its completion handler.
+ *
+ * The buffer state is set to VIDEOBUF_DONE if no error occurred (@error is 0)
+ * or VIDEOBUF_ERROR otherwise (@error is non-zero).
+ *
+ * The DMA queue is expected to contain at least one buffer.
+ *
+ * Return a pointer to the next buffer in the DMA queue, or NULL if the queue is
+ * empty.
+ */
+struct iss_buffer *omap4iss_video_buffer_next(struct iss_video *video,
+					      unsigned int error)
+{
+	struct iss_pipeline *pipe = to_iss_pipeline(&video->video.entity);
+	enum iss_pipeline_state state;
+	struct iss_buffer *buf;
+	unsigned long flags;
+	struct timespec ts;
+
+	spin_lock_irqsave(&video->qlock, flags);
+	if (WARN_ON(list_empty(&video->dmaqueue))) {
+		spin_unlock_irqrestore(&video->qlock, flags);
+		return NULL;
+	}
+
+	buf = list_first_entry(&video->dmaqueue, struct iss_buffer,
+			       list);
+	list_del(&buf->list);
+	spin_unlock_irqrestore(&video->qlock, flags);
+
+	ktime_get_ts(&ts);
+	buf->vb.v4l2_buf.timestamp.tv_sec = ts.tv_sec;
+	buf->vb.v4l2_buf.timestamp.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
+
+	/* Do frame number propagation only if this is the output video node.
+	 * Frame number either comes from the CSI receivers or it gets
+	 * incremented here if H3A is not active.
+	 * Note: There is no guarantee that the output buffer will finish
+	 * first, so the input number might lag behind by 1 in some cases.
+	 */
+	if (video == pipe->output && !pipe->do_propagation)
+		buf->vb.v4l2_buf.sequence = atomic_inc_return(&pipe->frame_number);
+	else
+		buf->vb.v4l2_buf.sequence = atomic_read(&pipe->frame_number);
+
+	vb2_buffer_done(&buf->vb, error < 0 ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
+
+	spin_lock_irqsave(&video->qlock, flags);
+	if (list_empty(&video->dmaqueue)) {
+		spin_unlock_irqrestore(&video->qlock, flags);
+		if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			state = ISS_PIPELINE_QUEUE_OUTPUT
+			      | ISS_PIPELINE_STREAM;
+		else
+			state = ISS_PIPELINE_QUEUE_INPUT
+			      | ISS_PIPELINE_STREAM;
+
+		spin_lock_irqsave(&pipe->lock, flags);
+		pipe->state &= ~state;
+		if (video->pipe.stream_state == ISS_PIPELINE_STREAM_CONTINUOUS)
+			video->dmaqueue_flags |= ISS_VIDEO_DMAQUEUE_UNDERRUN;
+		spin_unlock_irqrestore(&pipe->lock, flags);
+		return NULL;
+	}
+
+	if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->input != NULL) {
+		spin_lock_irqsave(&pipe->lock, flags);
+		pipe->state &= ~ISS_PIPELINE_STREAM;
+		spin_unlock_irqrestore(&pipe->lock, flags);
+	}
+
+	buf = list_first_entry(&video->dmaqueue, struct iss_buffer,
+			       list);
+	spin_unlock_irqrestore(&video->qlock, flags);
+	buf->vb.state = VB2_BUF_STATE_ACTIVE;
+	return buf;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 ioctls
+ */
+
+static int
+iss_video_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
+{
+	struct iss_video *video = video_drvdata(file);
+
+	strlcpy(cap->driver, ISS_VIDEO_DRIVER_NAME, sizeof(cap->driver));
+	strlcpy(cap->card, video->video.name, sizeof(cap->card));
+	strlcpy(cap->bus_info, "media", sizeof(cap->bus_info));
+	cap->version = ISS_VIDEO_DRIVER_VERSION;
+
+	if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+	else
+		cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
+
+	return 0;
+}
+
+static int
+iss_video_get_format(struct file *file, void *fh, struct v4l2_format *format)
+{
+	struct iss_video_fh *vfh = to_iss_video_fh(fh);
+	struct iss_video *video = video_drvdata(file);
+
+	if (format->type != video->type)
+		return -EINVAL;
+
+	mutex_lock(&video->mutex);
+	*format = vfh->format;
+	mutex_unlock(&video->mutex);
+
+	return 0;
+}
+
+static int
+iss_video_set_format(struct file *file, void *fh, struct v4l2_format *format)
+{
+	struct iss_video_fh *vfh = to_iss_video_fh(fh);
+	struct iss_video *video = video_drvdata(file);
+	struct v4l2_mbus_framefmt fmt;
+
+	if (format->type != video->type)
+		return -EINVAL;
+
+	mutex_lock(&video->mutex);
+
+	/* Fill the bytesperline and sizeimage fields by converting to media bus
+	 * format and back to pixel format.
+	 */
+	iss_video_pix_to_mbus(&format->fmt.pix, &fmt);
+	iss_video_mbus_to_pix(video, &fmt, &format->fmt.pix);
+
+	vfh->format = *format;
+
+	mutex_unlock(&video->mutex);
+	return 0;
+}
+
+static int
+iss_video_try_format(struct file *file, void *fh, struct v4l2_format *format)
+{
+	struct iss_video *video = video_drvdata(file);
+	struct v4l2_subdev_format fmt;
+	struct v4l2_subdev *subdev;
+	u32 pad;
+	int ret;
+
+	if (format->type != video->type)
+		return -EINVAL;
+
+	subdev = iss_video_remote_subdev(video, &pad);
+	if (subdev == NULL)
+		return -EINVAL;
+
+	iss_video_pix_to_mbus(&format->fmt.pix, &fmt.format);
+
+	fmt.pad = pad;
+	fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+	ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt);
+	if (ret)
+		return ret == -ENOIOCTLCMD ? -EINVAL : ret;
+
+	iss_video_mbus_to_pix(video, &fmt.format, &format->fmt.pix);
+	return 0;
+}
+
+static int
+iss_video_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cropcap)
+{
+	struct iss_video *video = video_drvdata(file);
+	struct v4l2_subdev *subdev;
+	int ret;
+
+	subdev = iss_video_remote_subdev(video, NULL);
+	if (subdev == NULL)
+		return -EINVAL;
+
+	mutex_lock(&video->mutex);
+	ret = v4l2_subdev_call(subdev, video, cropcap, cropcap);
+	mutex_unlock(&video->mutex);
+
+	return ret == -ENOIOCTLCMD ? -EINVAL : ret;
+}
+
+static int
+iss_video_get_crop(struct file *file, void *fh, struct v4l2_crop *crop)
+{
+	struct iss_video *video = video_drvdata(file);
+	struct v4l2_subdev_format format;
+	struct v4l2_subdev *subdev;
+	u32 pad;
+	int ret;
+
+	subdev = iss_video_remote_subdev(video, &pad);
+	if (subdev == NULL)
+		return -EINVAL;
+
+	/* Try the get crop operation first and fallback to get format if not
+	 * implemented.
+	 */
+	ret = v4l2_subdev_call(subdev, video, g_crop, crop);
+	if (ret != -ENOIOCTLCMD)
+		return ret;
+
+	format.pad = pad;
+	format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+	ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &format);
+	if (ret < 0)
+		return ret == -ENOIOCTLCMD ? -EINVAL : ret;
+
+	crop->c.left = 0;
+	crop->c.top = 0;
+	crop->c.width = format.format.width;
+	crop->c.height = format.format.height;
+
+	return 0;
+}
+
+static int
+iss_video_set_crop(struct file *file, void *fh, struct v4l2_crop *crop)
+{
+	struct iss_video *video = video_drvdata(file);
+	struct v4l2_subdev *subdev;
+	int ret;
+
+	subdev = iss_video_remote_subdev(video, NULL);
+	if (subdev == NULL)
+		return -EINVAL;
+
+	mutex_lock(&video->mutex);
+	ret = v4l2_subdev_call(subdev, video, s_crop, crop);
+	mutex_unlock(&video->mutex);
+
+	return ret == -ENOIOCTLCMD ? -EINVAL : ret;
+}
+
+static int
+iss_video_get_param(struct file *file, void *fh, struct v4l2_streamparm *a)
+{
+	struct iss_video_fh *vfh = to_iss_video_fh(fh);
+	struct iss_video *video = video_drvdata(file);
+
+	if (video->type != V4L2_BUF_TYPE_VIDEO_OUTPUT ||
+	    video->type != a->type)
+		return -EINVAL;
+
+	memset(a, 0, sizeof(*a));
+	a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+	a->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
+	a->parm.output.timeperframe = vfh->timeperframe;
+
+	return 0;
+}
+
+static int
+iss_video_set_param(struct file *file, void *fh, struct v4l2_streamparm *a)
+{
+	struct iss_video_fh *vfh = to_iss_video_fh(fh);
+	struct iss_video *video = video_drvdata(file);
+
+	if (video->type != V4L2_BUF_TYPE_VIDEO_OUTPUT ||
+	    video->type != a->type)
+		return -EINVAL;
+
+	if (a->parm.output.timeperframe.denominator == 0)
+		a->parm.output.timeperframe.denominator = 1;
+
+	vfh->timeperframe = a->parm.output.timeperframe;
+
+	return 0;
+}
+
+static int
+iss_video_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *rb)
+{
+	struct iss_video_fh *vfh = to_iss_video_fh(fh);
+
+	return vb2_reqbufs(&vfh->queue, rb);
+}
+
+static int
+iss_video_querybuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+	struct iss_video_fh *vfh = to_iss_video_fh(fh);
+
+	return vb2_querybuf(&vfh->queue, b);
+}
+
+static int
+iss_video_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+	struct iss_video_fh *vfh = to_iss_video_fh(fh);
+
+	return vb2_qbuf(&vfh->queue, b);
+}
+
+static int
+iss_video_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+	struct iss_video_fh *vfh = to_iss_video_fh(fh);
+
+	return vb2_dqbuf(&vfh->queue, b, file->f_flags & O_NONBLOCK);
+}
+
+/*
+ * Stream management
+ *
+ * Every ISS pipeline has a single input and a single output. The input can be
+ * either a sensor or a video node. The output is always a video node.
+ *
+ * As every pipeline has an output video node, the ISS video objects at the
+ * pipeline output stores the pipeline state. It tracks the streaming state of
+ * both the input and output, as well as the availability of buffers.
+ *
+ * In sensor-to-memory mode, frames are always available at the pipeline input.
+ * Starting the sensor usually requires I2C transfers and must be done in
+ * interruptible context. The pipeline is started and stopped synchronously
+ * to the stream on/off commands. All modules in the pipeline will get their
+ * subdev set stream handler called. The module at the end of the pipeline must
+ * delay starting the hardware until buffers are available at its output.
+ *
+ * In memory-to-memory mode, starting/stopping the stream requires
+ * synchronization between the input and output. ISS modules can't be stopped
+ * in the middle of a frame, and at least some of the modules seem to become
+ * busy as soon as they're started, even if they don't receive a frame start
+ * event. For that reason frames need to be processed in single-shot mode. The
+ * driver needs to wait until a frame is completely processed and written to
+ * memory before restarting the pipeline for the next frame. Pipelined
+ * processing might be possible but requires more testing.
+ *
+ * Stream start must be delayed until buffers are available at both the input
+ * and output. The pipeline must be started in the videobuf queue callback with
+ * the buffers queue spinlock held. The modules subdev set stream operation must
+ * not sleep.
+ */
+static int
+iss_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
+{
+	struct iss_video_fh *vfh = to_iss_video_fh(fh);
+	struct iss_video *video = video_drvdata(file);
+	enum iss_pipeline_state state;
+	struct iss_pipeline *pipe;
+	struct iss_video *far_end;
+	unsigned long flags;
+	int ret;
+
+	if (type != video->type)
+		return -EINVAL;
+
+	mutex_lock(&video->stream_lock);
+
+	if (video->streaming) {
+		mutex_unlock(&video->stream_lock);
+		return -EBUSY;
+	}
+
+	/* Start streaming on the pipeline. No link touching an entity in the
+	 * pipeline can be activated or deactivated once streaming is started.
+	 */
+	pipe = video->video.entity.pipe
+	     ? to_iss_pipeline(&video->video.entity) : &video->pipe;
+	media_entity_pipeline_start(&video->video.entity, &pipe->pipe);
+
+	/* Verify that the currently configured format matches the output of
+	 * the connected subdev.
+	 */
+	ret = iss_video_check_format(video, vfh);
+	if (ret < 0)
+		goto error;
+
+	video->bpl_padding = ret;
+	video->bpl_value = vfh->format.fmt.pix.bytesperline;
+
+	/* Find the ISS video node connected at the far end of the pipeline and
+	 * update the pipeline.
+	 */
+	far_end = iss_video_far_end(video);
+
+	if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+		state = ISS_PIPELINE_STREAM_OUTPUT | ISS_PIPELINE_IDLE_OUTPUT;
+		pipe->input = far_end;
+		pipe->output = video;
+	} else {
+		if (far_end == NULL) {
+			ret = -EPIPE;
+			goto error;
+		}
+
+		state = ISS_PIPELINE_STREAM_INPUT | ISS_PIPELINE_IDLE_INPUT;
+		pipe->input = video;
+		pipe->output = far_end;
+	}
+
+	if (video->iss->pdata->set_constraints)
+		video->iss->pdata->set_constraints(video->iss, true);
+
+	/* Validate the pipeline and update its state. */
+	ret = iss_video_validate_pipeline(pipe);
+	if (ret < 0)
+		goto error;
+
+	spin_lock_irqsave(&pipe->lock, flags);
+	pipe->state &= ~ISS_PIPELINE_STREAM;
+	pipe->state |= state;
+	spin_unlock_irqrestore(&pipe->lock, flags);
+
+	/* Set the maximum time per frame as the value requested by userspace.
+	 * This is a soft limit that can be overridden if the hardware doesn't
+	 * support the request limit.
+	 */
+	if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+		pipe->max_timeperframe = vfh->timeperframe;
+
+	video->queue = &vfh->queue;
+	INIT_LIST_HEAD(&video->dmaqueue);
+	spin_lock_init(&video->qlock);
+	atomic_set(&pipe->frame_number, -1);
+
+	ret = vb2_streamon(&vfh->queue, type);
+	if (ret < 0)
+		goto error;
+
+	/* In sensor-to-memory mode, the stream can be started synchronously
+	 * to the stream on command. In memory-to-memory mode, it will be
+	 * started when buffers are queued on both the input and output.
+	 */
+	if (pipe->input == NULL) {
+		unsigned long flags;
+		ret = omap4iss_pipeline_set_stream(pipe,
+					      ISS_PIPELINE_STREAM_CONTINUOUS);
+		if (ret < 0)
+			goto error;
+		spin_lock_irqsave(&video->qlock, flags);
+		if (list_empty(&video->dmaqueue))
+			video->dmaqueue_flags |= ISS_VIDEO_DMAQUEUE_UNDERRUN;
+		spin_unlock_irqrestore(&video->qlock, flags);
+	}
+
+error:
+	if (ret < 0) {
+		vb2_streamoff(&vfh->queue, type);
+		if (video->iss->pdata->set_constraints)
+			video->iss->pdata->set_constraints(video->iss, false);
+		media_entity_pipeline_stop(&video->video.entity);
+		video->queue = NULL;
+	}
+
+	if (!ret)
+		video->streaming = 1;
+
+	mutex_unlock(&video->stream_lock);
+	return ret;
+}
+
+static int
+iss_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type)
+{
+	struct iss_video_fh *vfh = to_iss_video_fh(fh);
+	struct iss_video *video = video_drvdata(file);
+	struct iss_pipeline *pipe = to_iss_pipeline(&video->video.entity);
+	enum iss_pipeline_state state;
+	unsigned long flags;
+
+	if (type != video->type)
+		return -EINVAL;
+
+	mutex_lock(&video->stream_lock);
+
+	if (!vb2_is_streaming(&vfh->queue))
+		goto done;
+
+	/* Update the pipeline state. */
+	if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		state = ISS_PIPELINE_STREAM_OUTPUT
+		      | ISS_PIPELINE_QUEUE_OUTPUT;
+	else
+		state = ISS_PIPELINE_STREAM_INPUT
+		      | ISS_PIPELINE_QUEUE_INPUT;
+
+	spin_lock_irqsave(&pipe->lock, flags);
+	pipe->state &= ~state;
+	spin_unlock_irqrestore(&pipe->lock, flags);
+
+	/* Stop the stream. */
+	omap4iss_pipeline_set_stream(pipe, ISS_PIPELINE_STREAM_STOPPED);
+	vb2_streamoff(&vfh->queue, type);
+	video->queue = NULL;
+	video->streaming = 0;
+
+	if (video->iss->pdata->set_constraints)
+		video->iss->pdata->set_constraints(video->iss, false);
+	media_entity_pipeline_stop(&video->video.entity);
+
+done:
+	mutex_unlock(&video->stream_lock);
+	return 0;
+}
+
+static int
+iss_video_enum_input(struct file *file, void *fh, struct v4l2_input *input)
+{
+	if (input->index > 0)
+		return -EINVAL;
+
+	strlcpy(input->name, "camera", sizeof(input->name));
+	input->type = V4L2_INPUT_TYPE_CAMERA;
+
+	return 0;
+}
+
+static int
+iss_video_g_input(struct file *file, void *fh, unsigned int *input)
+{
+	*input = 0;
+
+	return 0;
+}
+
+static int
+iss_video_s_input(struct file *file, void *fh, unsigned int input)
+{
+	return input == 0 ? 0 : -EINVAL;
+}
+
+static const struct v4l2_ioctl_ops iss_video_ioctl_ops = {
+	.vidioc_querycap		= iss_video_querycap,
+	.vidioc_g_fmt_vid_cap		= iss_video_get_format,
+	.vidioc_s_fmt_vid_cap		= iss_video_set_format,
+	.vidioc_try_fmt_vid_cap		= iss_video_try_format,
+	.vidioc_g_fmt_vid_out		= iss_video_get_format,
+	.vidioc_s_fmt_vid_out		= iss_video_set_format,
+	.vidioc_try_fmt_vid_out		= iss_video_try_format,
+	.vidioc_cropcap			= iss_video_cropcap,
+	.vidioc_g_crop			= iss_video_get_crop,
+	.vidioc_s_crop			= iss_video_set_crop,
+	.vidioc_g_parm			= iss_video_get_param,
+	.vidioc_s_parm			= iss_video_set_param,
+	.vidioc_reqbufs			= iss_video_reqbufs,
+	.vidioc_querybuf		= iss_video_querybuf,
+	.vidioc_qbuf			= iss_video_qbuf,
+	.vidioc_dqbuf			= iss_video_dqbuf,
+	.vidioc_streamon		= iss_video_streamon,
+	.vidioc_streamoff		= iss_video_streamoff,
+	.vidioc_enum_input		= iss_video_enum_input,
+	.vidioc_g_input			= iss_video_g_input,
+	.vidioc_s_input			= iss_video_s_input,
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 file operations
+ */
+
+static int iss_video_open(struct file *file)
+{
+	struct iss_video *video = video_drvdata(file);
+	struct iss_video_fh *handle;
+	struct vb2_queue *q;
+	int ret = 0;
+
+	handle = kzalloc(sizeof(*handle), GFP_KERNEL);
+	if (handle == NULL)
+		return -ENOMEM;
+
+	v4l2_fh_init(&handle->vfh, &video->video);
+	v4l2_fh_add(&handle->vfh);
+
+	/* If this is the first user, initialise the pipeline. */
+	if (omap4iss_get(video->iss) == NULL) {
+		ret = -EBUSY;
+		goto done;
+	}
+
+	ret = omap4iss_pipeline_pm_use(&video->video.entity, 1);
+	if (ret < 0) {
+		omap4iss_put(video->iss);
+		goto done;
+	}
+
+	video->alloc_ctx = vb2_dma_contig_init_ctx(video->iss->dev);
+	if (IS_ERR(video->alloc_ctx)) {
+		ret = PTR_ERR(video->alloc_ctx);
+		omap4iss_put(video->iss);
+		goto done;
+	}
+
+	q = &handle->queue;
+
+	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	q->io_modes = VB2_MMAP;
+	q->drv_priv = handle;
+	q->ops = &iss_video_vb2ops;
+	q->mem_ops = &vb2_dma_contig_memops;
+	q->buf_struct_size = sizeof(struct iss_buffer);
+
+	ret = vb2_queue_init(q);
+	if (ret) {
+		omap4iss_put(video->iss);
+		goto done;
+	}
+
+	memset(&handle->format, 0, sizeof(handle->format));
+	handle->format.type = video->type;
+	handle->timeperframe.denominator = 1;
+
+	handle->video = video;
+	file->private_data = &handle->vfh;
+
+done:
+	if (ret < 0) {
+		v4l2_fh_del(&handle->vfh);
+		kfree(handle);
+	}
+
+	return ret;
+}
+
+static int iss_video_release(struct file *file)
+{
+	struct iss_video *video = video_drvdata(file);
+	struct v4l2_fh *vfh = file->private_data;
+	struct iss_video_fh *handle = to_iss_video_fh(vfh);
+
+	/* Disable streaming and free the buffers queue resources. */
+	iss_video_streamoff(file, vfh, video->type);
+
+	omap4iss_pipeline_pm_use(&video->video.entity, 0);
+
+	/* Release the file handle. */
+	v4l2_fh_del(vfh);
+	kfree(handle);
+	file->private_data = NULL;
+
+	omap4iss_put(video->iss);
+
+	return 0;
+}
+
+static unsigned int iss_video_poll(struct file *file, poll_table *wait)
+{
+	struct iss_video_fh *vfh = to_iss_video_fh(file->private_data);
+
+	return vb2_poll(&vfh->queue, file, wait);
+}
+
+static int iss_video_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct iss_video_fh *vfh = to_iss_video_fh(file->private_data);
+
+	return vb2_mmap(&vfh->queue, vma);;
+}
+
+static struct v4l2_file_operations iss_video_fops = {
+	.owner = THIS_MODULE,
+	.unlocked_ioctl = video_ioctl2,
+	.open = iss_video_open,
+	.release = iss_video_release,
+	.poll = iss_video_poll,
+	.mmap = iss_video_mmap,
+};
+
+/* -----------------------------------------------------------------------------
+ * ISS video core
+ */
+
+static const struct iss_video_operations iss_video_dummy_ops = {
+};
+
+int omap4iss_video_init(struct iss_video *video, const char *name)
+{
+	const char *direction;
+	int ret;
+
+	switch (video->type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+		direction = "output";
+		video->pad.flags = MEDIA_PAD_FL_SINK;
+		break;
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+		direction = "input";
+		video->pad.flags = MEDIA_PAD_FL_SOURCE;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	ret = media_entity_init(&video->video.entity, 1, &video->pad, 0);
+	if (ret < 0)
+		return ret;
+
+	mutex_init(&video->mutex);
+	atomic_set(&video->active, 0);
+
+	spin_lock_init(&video->pipe.lock);
+	mutex_init(&video->stream_lock);
+
+	/* Initialize the video device. */
+	if (video->ops == NULL)
+		video->ops = &iss_video_dummy_ops;
+
+	video->video.fops = &iss_video_fops;
+	snprintf(video->video.name, sizeof(video->video.name),
+		 "OMAP4 ISS %s %s", name, direction);
+	video->video.vfl_type = VFL_TYPE_GRABBER;
+	video->video.release = video_device_release_empty;
+	video->video.ioctl_ops = &iss_video_ioctl_ops;
+	video->pipe.stream_state = ISS_PIPELINE_STREAM_STOPPED;
+
+	video_set_drvdata(&video->video, video);
+
+	return 0;
+}
+
+int omap4iss_video_register(struct iss_video *video, struct v4l2_device *vdev)
+{
+	int ret;
+
+	video->video.v4l2_dev = vdev;
+
+	ret = video_register_device(&video->video, VFL_TYPE_GRABBER, -1);
+	if (ret < 0)
+		printk(KERN_ERR "%s: could not register video device (%d)\n",
+			__func__, ret);
+
+	return ret;
+}
+
+void omap4iss_video_unregister(struct iss_video *video)
+{
+	if (video_is_registered(&video->video)) {
+		media_entity_cleanup(&video->video.entity);
+		video_unregister_device(&video->video);
+	}
+}
diff --git a/drivers/media/video/omap4iss/iss_video.h b/drivers/media/video/omap4iss/iss_video.h
new file mode 100644
index 0000000..fc123b0
--- /dev/null
+++ b/drivers/media/video/omap4iss/iss_video.h
@@ -0,0 +1,205 @@
+/*
+ * iss_video.h
+ *
+ * TI OMAP4 ISS - Generic video node
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ *
+ * Contacts: Sergio Aguirre <saaguirre@ti.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef OMAP4_ISS_VIDEO_H
+#define OMAP4_ISS_VIDEO_H
+
+#include <linux/v4l2-mediabus.h>
+#include <linux/version.h>
+#include <media/media-entity.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-fh.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+
+#define ISS_VIDEO_DRIVER_NAME		"issvideo"
+#define ISS_VIDEO_DRIVER_VERSION	KERNEL_VERSION(0, 0, 1)
+
+struct iss_device;
+struct iss_video;
+struct v4l2_mbus_framefmt;
+struct v4l2_pix_format;
+
+/*
+ * struct iss_format_info - ISS media bus format information
+ * @code: V4L2 media bus format code
+ * @truncated: V4L2 media bus format code for the same format truncated to 10
+ *	bits. Identical to @code if the format is 10 bits wide or less.
+ * @uncompressed: V4L2 media bus format code for the corresponding uncompressed
+ *	format. Identical to @code if the format is not DPCM compressed.
+ * @flavor: V4L2 media bus format code for the same pixel layout but
+ *	shifted to be 8 bits per pixel. =0 if format is not shiftable.
+ * @pixelformat: V4L2 pixel format FCC identifier
+ * @bpp: Bits per pixel
+ */
+struct iss_format_info {
+	enum v4l2_mbus_pixelcode code;
+	enum v4l2_mbus_pixelcode truncated;
+	enum v4l2_mbus_pixelcode uncompressed;
+	enum v4l2_mbus_pixelcode flavor;
+	u32 pixelformat;
+	unsigned int bpp;
+};
+
+enum iss_pipeline_stream_state {
+	ISS_PIPELINE_STREAM_STOPPED = 0,
+	ISS_PIPELINE_STREAM_CONTINUOUS = 1,
+	ISS_PIPELINE_STREAM_SINGLESHOT = 2,
+};
+
+enum iss_pipeline_state {
+	/* The stream has been started on the input video node. */
+	ISS_PIPELINE_STREAM_INPUT = 1,
+	/* The stream has been started on the output video node. */
+	ISS_PIPELINE_STREAM_OUTPUT = 2,
+	/* At least one buffer is queued on the input video node. */
+	ISS_PIPELINE_QUEUE_INPUT = 4,
+	/* At least one buffer is queued on the output video node. */
+	ISS_PIPELINE_QUEUE_OUTPUT = 8,
+	/* The input entity is idle, ready to be started. */
+	ISS_PIPELINE_IDLE_INPUT = 16,
+	/* The output entity is idle, ready to be started. */
+	ISS_PIPELINE_IDLE_OUTPUT = 32,
+	/* The pipeline is currently streaming. */
+	ISS_PIPELINE_STREAM = 64,
+};
+
+struct iss_pipeline {
+	struct media_pipeline pipe;
+	spinlock_t lock;		/* Pipeline state and queue flags */
+	unsigned int state;
+	enum iss_pipeline_stream_state stream_state;
+	struct iss_video *input;
+	struct iss_video *output;
+	atomic_t frame_number;
+	bool do_propagation; /* of frame number */
+	struct v4l2_fract max_timeperframe;
+};
+
+#define to_iss_pipeline(__e) \
+	container_of((__e)->pipe, struct iss_pipeline, pipe)
+
+static inline int iss_pipeline_ready(struct iss_pipeline *pipe)
+{
+	return pipe->state == (ISS_PIPELINE_STREAM_INPUT |
+			       ISS_PIPELINE_STREAM_OUTPUT |
+			       ISS_PIPELINE_QUEUE_INPUT |
+			       ISS_PIPELINE_QUEUE_OUTPUT |
+			       ISS_PIPELINE_IDLE_INPUT |
+			       ISS_PIPELINE_IDLE_OUTPUT);
+}
+
+/*
+ * struct iss_buffer - ISS buffer
+ * @buffer: ISS video buffer
+ * @iss_addr: Physical address of the buffer.
+ */
+struct iss_buffer {
+	/* common v4l buffer stuff -- must be first */
+	struct vb2_buffer	vb;
+	struct list_head	list;
+	dma_addr_t iss_addr;
+};
+
+#define to_iss_buffer(buf)	container_of(buf, struct iss_buffer, buffer)
+
+enum iss_video_dmaqueue_flags {
+	/* Set if DMA queue becomes empty when ISS_PIPELINE_STREAM_CONTINUOUS */
+	ISS_VIDEO_DMAQUEUE_UNDERRUN = (1 << 0),
+	/* Set when queuing buffer to an empty DMA queue */
+	ISS_VIDEO_DMAQUEUE_QUEUED = (1 << 1),
+};
+
+#define iss_video_dmaqueue_flags_clr(video)	\
+			({ (video)->dmaqueue_flags = 0; })
+
+/*
+ * struct iss_video_operations - ISS video operations
+ * @queue:	Resume streaming when a buffer is queued. Called on VIDIOC_QBUF
+ *		if there was no buffer previously queued.
+ */
+struct iss_video_operations {
+	int(*queue)(struct iss_video *video, struct iss_buffer *buffer);
+};
+
+struct iss_video {
+	struct video_device video;
+	enum v4l2_buf_type type;
+	struct media_pad pad;
+
+	struct mutex mutex;		/* format and crop settings */
+	atomic_t active;
+
+	struct iss_device *iss;
+
+	unsigned int capture_mem;
+	unsigned int bpl_alignment;	/* alignment value */
+	unsigned int bpl_zero_padding;	/* whether the alignment is optional */
+	unsigned int bpl_max;		/* maximum bytes per line value */
+	unsigned int bpl_value;		/* bytes per line value */
+	unsigned int bpl_padding;	/* padding at end of line */
+
+	/* Entity video node streaming */
+	unsigned int streaming:1;
+
+	/* Pipeline state */
+	struct iss_pipeline pipe;
+	struct mutex stream_lock;	/* pipeline and stream states */
+
+	/* Video buffers queue */
+	struct vb2_queue *queue;
+	spinlock_t qlock;	/* Spinlock for dmaqueue */
+	struct list_head dmaqueue;
+	enum iss_video_dmaqueue_flags dmaqueue_flags;
+	struct vb2_alloc_ctx *alloc_ctx;
+
+	const struct iss_video_operations *ops;
+};
+
+#define to_iss_video(vdev)	container_of(vdev, struct iss_video, video)
+
+struct iss_video_fh {
+	struct v4l2_fh vfh;
+	struct iss_video *video;
+	struct vb2_queue queue;
+	struct v4l2_format format;
+	struct v4l2_fract timeperframe;
+};
+
+#define to_iss_video_fh(fh)	container_of(fh, struct iss_video_fh, vfh)
+#define iss_video_queue_to_iss_video_fh(q) \
+				container_of(q, struct iss_video_fh, queue)
+
+int omap4iss_video_init(struct iss_video *video, const char *name);
+int omap4iss_video_register(struct iss_video *video,
+			    struct v4l2_device *vdev);
+void omap4iss_video_unregister(struct iss_video *video);
+struct iss_buffer *omap4iss_video_buffer_next(struct iss_video *video,
+					      unsigned int error);
+struct media_pad *omap4iss_video_remote_pad(struct iss_video *video);
+
+const struct iss_format_info *
+omap4iss_video_format_info(enum v4l2_mbus_pixelcode code);
+
+#endif /* OMAP4_ISS_VIDEO_H */
-- 
1.7.7.4


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

* [PATCH v2 06/11] v4l: Add support for omap4iss driver
@ 2011-12-01  0:14   ` Sergio Aguirre
  0 siblings, 0 replies; 65+ messages in thread
From: Sergio Aguirre @ 2011-12-01  0:14 UTC (permalink / raw)
  To: linux-media; +Cc: linux-omap, laurent.pinchart, sakari.ailus, Sergio Aguirre

This adds a very simplistic driver to utilize the
CSI2A interface inside the ISS subsystem in OMAP4,
and dump the data to memory.

Tested so far on omap4430sdp w/ ES2.1 GP & w/ ES2.2 EMU.

Check newly added Documentation/video4linux/omap4_camera.txt
for details.

Signed-off-by: Sergio Aguirre <saaguirre@ti.com>
---
 Documentation/video4linux/omap4_camera.txt  |   60 ++
 arch/arm/plat-omap/include/plat/omap4-iss.h |   42 +
 drivers/media/video/Kconfig                 |   13 +
 drivers/media/video/Makefile                |    1 +
 drivers/media/video/omap4iss/Makefile       |    6 +
 drivers/media/video/omap4iss/iss.c          | 1179 ++++++++++++++++++++++++
 drivers/media/video/omap4iss/iss.h          |  133 +++
 drivers/media/video/omap4iss/iss_csi2.c     | 1324 +++++++++++++++++++++++++++
 drivers/media/video/omap4iss/iss_csi2.h     |  166 ++++
 drivers/media/video/omap4iss/iss_csiphy.c   |  215 +++++
 drivers/media/video/omap4iss/iss_csiphy.h   |   69 ++
 drivers/media/video/omap4iss/iss_regs.h     |  238 +++++
 drivers/media/video/omap4iss/iss_video.c    | 1192 ++++++++++++++++++++++++
 drivers/media/video/omap4iss/iss_video.h    |  205 +++++
 14 files changed, 4843 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/video4linux/omap4_camera.txt
 create mode 100644 arch/arm/plat-omap/include/plat/omap4-iss.h
 create mode 100644 drivers/media/video/omap4iss/Makefile
 create mode 100644 drivers/media/video/omap4iss/iss.c
 create mode 100644 drivers/media/video/omap4iss/iss.h
 create mode 100644 drivers/media/video/omap4iss/iss_csi2.c
 create mode 100644 drivers/media/video/omap4iss/iss_csi2.h
 create mode 100644 drivers/media/video/omap4iss/iss_csiphy.c
 create mode 100644 drivers/media/video/omap4iss/iss_csiphy.h
 create mode 100644 drivers/media/video/omap4iss/iss_regs.h
 create mode 100644 drivers/media/video/omap4iss/iss_video.c
 create mode 100644 drivers/media/video/omap4iss/iss_video.h

diff --git a/Documentation/video4linux/omap4_camera.txt b/Documentation/video4linux/omap4_camera.txt
new file mode 100644
index 0000000..a60c80f
--- /dev/null
+++ b/Documentation/video4linux/omap4_camera.txt
@@ -0,0 +1,60 @@
+                              OMAP4 ISS Driver
+                              ================
+
+Introduction
+------------
+
+The OMAP44XX family of chips contains the Imaging SubSystem (a.k.a. ISS),
+Which contains several components that can be categorized in 3 big groups:
+
+- Interfaces (2 Interfaces: CSI2-A & CSI2-B/CCP2)
+- ISP (Image Signal Processor)
+- SIMCOP (Still Image Coprocessor)
+
+For more information, please look in [1] for latest version of:
+	"OMAP4430 Multimedia Device Silicon Revision 2.x"
+
+As of Revision L, the ISS is described in detail in section 8.
+
+This driver is supporting _only_ the CSI2-A interface for now.
+
+It makes use of the Media Controller framework [2], and inherited most of the
+code from OMAP3 ISP driver (found under drivers/media/video/omap3isp/*), except
+that it doesn't need an IOMMU now for ISS buffers memory mapping.
+
+Supports usage of MMAP buffers only (for now).
+
+IMPORTANT: It is recommended to have this patchset:
+  Contiguous Memory Allocator (v15) [3].
+
+Tested platforms
+----------------
+
+- OMAP4430SDP, w/ ES2.1 GP & SEVM4430-CAM-V1-0 (Contains IMX060 & OV5640, in
+  which only the last one is supported, outputting YUV422 frames).
+
+- PandaBoard, Rev. A2, w/ OMAP4430 ES2.1 GP & OV adapter board, tested with
+  following sensors:
+  * OV5640
+  * OV5650
+
+- Tested on mainline kernel:
+
+	http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=summary
+
+  Tag: v3.0 (commit 02f8c6aee8df3cdc935e9bdd4f2d020306035dbe)
+
+File list
+---------
+drivers/media/video/omap4iss/
+
+References
+----------
+
+[1] http://focus.ti.com/general/docs/wtbu/wtbudocumentcenter.tsp?navigationId=12037&templateId=6123#62
+[2] http://lwn.net/Articles/420485/
+[3] http://lwn.net/Articles/455801/
+--
+Author: Sergio Aguirre <saaguirre@ti.com>
+Copyright (C) 2011, Texas Instruments
+
diff --git a/arch/arm/plat-omap/include/plat/omap4-iss.h b/arch/arm/plat-omap/include/plat/omap4-iss.h
new file mode 100644
index 0000000..3a1c6b6
--- /dev/null
+++ b/arch/arm/plat-omap/include/plat/omap4-iss.h
@@ -0,0 +1,42 @@
+#ifndef ARCH_ARM_PLAT_OMAP4_ISS_H
+#define ARCH_ARM_PLAT_OMAP4_ISS_H
+
+#include <linux/i2c.h>
+
+struct iss_device;
+
+enum iss_interface_type {
+	ISS_INTERFACE_CSI2A_PHY1,
+};
+
+/**
+ * struct iss_csi2_platform_data - CSI2 interface platform data
+ * @crc: Enable the cyclic redundancy check
+ * @vpclk_div: Video port output clock control
+ */
+struct iss_csi2_platform_data {
+	unsigned crc:1;
+	unsigned vpclk_div:2;
+};
+
+struct iss_subdev_i2c_board_info {
+	struct i2c_board_info *board_info;
+	int i2c_adapter_id;
+};
+
+struct iss_v4l2_subdevs_group {
+	struct iss_subdev_i2c_board_info *subdevs;
+	enum iss_interface_type interface;
+	union {
+		struct iss_csi2_platform_data csi2;
+	} bus; /* gcc < 4.6.0 chokes on anonymous union initializers */
+};
+
+struct iss_platform_data {
+	struct iss_v4l2_subdevs_group *subdevs;
+	void (*set_constraints)(struct iss_device *iss, bool enable);
+};
+
+extern int omap4_init_camera(struct iss_platform_data *pdata,
+			     struct omap_board_data *bdata);
+#endif
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index b303a3f..ae2a99d 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -796,6 +796,19 @@ config VIDEO_OMAP3_DEBUG
 	---help---
 	  Enable debug messages on OMAP 3 camera controller driver.
 
+config VIDEO_OMAP4
+	tristate "OMAP 4 Camera support (EXPERIMENTAL)"
+	depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP4 && EXPERIMENTAL
+	select VIDEOBUF2_DMA_CONTIG
+	---help---
+	  Driver for an OMAP 4 ISS controller.
+
+config VIDEO_OMAP4_DEBUG
+	bool "OMAP 4 Camera debug messages"
+	depends on VIDEO_OMAP4
+	---help---
+	  Enable debug messages on OMAP 4 ISS controller driver.
+
 config SOC_CAMERA
 	tristate "SoC camera support"
 	depends on VIDEO_V4L2 && HAS_DMA && I2C
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index 117f9c4..f02a4c4 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -140,6 +140,7 @@ obj-$(CONFIG_VIDEO_MMP_CAMERA) += marvell-ccic/
 obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o
 
 obj-$(CONFIG_VIDEO_OMAP3)	+= omap3isp/
+obj-$(CONFIG_VIDEO_OMAP4)	+= omap4iss/
 
 obj-$(CONFIG_USB_ZR364XX)       += zr364xx.o
 obj-$(CONFIG_USB_STKWEBCAM)     += stkwebcam.o
diff --git a/drivers/media/video/omap4iss/Makefile b/drivers/media/video/omap4iss/Makefile
new file mode 100644
index 0000000..1d3b0a7
--- /dev/null
+++ b/drivers/media/video/omap4iss/Makefile
@@ -0,0 +1,6 @@
+# Makefile for OMAP4 ISS driver
+
+omap4-iss-objs += \
+	iss.o iss_csi2.o iss_csiphy.o iss_video.o
+
+obj-$(CONFIG_VIDEO_OMAP4) += omap4-iss.o
diff --git a/drivers/media/video/omap4iss/iss.c b/drivers/media/video/omap4iss/iss.c
new file mode 100644
index 0000000..255738b
--- /dev/null
+++ b/drivers/media/video/omap4iss/iss.c
@@ -0,0 +1,1179 @@
+/*
+ * V4L2 Driver for OMAP4 ISS
+ *
+ * Copyright (C) 2011, Texas Instruments
+ *
+ * Author: Sergio Aguirre <saaguirre@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/vmalloc.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+
+#include "iss.h"
+#include "iss_regs.h"
+
+static void iss_save_ctx(struct iss_device *iss);
+
+static void iss_restore_ctx(struct iss_device *iss);
+
+/* Structure for saving/restoring ISS module registers */
+static struct iss_reg iss_reg_list[] = {
+	{OMAP4_ISS_MEM_TOP, ISS_HL_SYSCONFIG, 0},
+	{OMAP4_ISS_MEM_TOP, ISS_CTRL, 0},
+	{OMAP4_ISS_MEM_TOP, ISS_CLKCTRL, 0},
+	{0, ISS_TOK_TERM, 0}
+};
+
+/*
+ * omap4iss_flush - Post pending L3 bus writes by doing a register readback
+ * @iss: OMAP4 ISS device
+ *
+ * In order to force posting of pending writes, we need to write and
+ * readback the same register, in this case the revision register.
+ *
+ * See this link for reference:
+ *   http://www.mail-archive.com/linux-omap@vger.kernel.org/msg08149.html
+ */
+void omap4iss_flush(struct iss_device *iss)
+{
+	writel(0, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_REVISION);
+	readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_REVISION);
+}
+
+/*
+ * iss_enable_interrupts - Enable ISS interrupts.
+ * @iss: OMAP4 ISS device
+ */
+static void iss_enable_interrupts(struct iss_device *iss)
+{
+	static const u32 irq = ISS_HL_IRQ_CSIA;
+
+	/* Enable HL interrupts */
+	writel(irq, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQSTATUS_5);
+	writel(irq, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQENABLE_5_SET);
+}
+
+/*
+ * iss_disable_interrupts - Disable ISS interrupts.
+ * @iss: OMAP4 ISS device
+ */
+static void iss_disable_interrupts(struct iss_device *iss)
+{
+	writel(-1, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQENABLE_5_CLR);
+}
+
+static inline void iss_isr_dbg(struct iss_device *iss, u32 irqstatus)
+{
+	static const char *name[] = {
+		"ISP_IRQ0",
+		"ISP_IRQ1",
+		"ISP_IRQ2",
+		"ISP_IRQ3",
+		"CSIA_IRQ",
+		"CSIB_IRQ",
+		"CCP2_IRQ0",
+		"CCP2_IRQ1",
+		"CCP2_IRQ2",
+		"CCP2_IRQ3",
+		"CBUFF_IRQ",
+		"BTE_IRQ",
+		"SIMCOP_IRQ0",
+		"SIMCOP_IRQ1",
+		"SIMCOP_IRQ2",
+		"SIMCOP_IRQ3",
+		"CCP2_IRQ8",
+		"HS_VS_IRQ",
+		"res18",
+		"res19",
+		"res20",
+		"res21",
+		"res22",
+		"res23",
+		"res24",
+		"res25",
+		"res26",
+		"res27",
+		"res28",
+		"res29",
+		"res30",
+		"res31",
+	};
+	int i;
+
+	dev_dbg(iss->dev, "ISS IRQ: ");
+
+	for (i = 0; i < ARRAY_SIZE(name); i++) {
+		if ((1 << i) & irqstatus)
+			printk(KERN_CONT "%s ", name[i]);
+	}
+	printk(KERN_CONT "\n");
+}
+
+/*
+ * iss_isr - Interrupt Service Routine for ISS module.
+ * @irq: Not used currently.
+ * @_iss: Pointer to the OMAP4 ISS device
+ *
+ * Handles the corresponding callback if plugged in.
+ *
+ * Returns IRQ_HANDLED when IRQ was correctly handled, or IRQ_NONE when the
+ * IRQ wasn't handled.
+ */
+static irqreturn_t iss_isr(int irq, void *_iss)
+{
+	struct iss_device *iss = _iss;
+	u32 irqstatus;
+
+	irqstatus = readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQSTATUS_5);
+	writel(irqstatus, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQSTATUS_5);
+
+	if (irqstatus & ISS_HL_IRQ_CSIA)
+		omap4iss_csi2_isr(&iss->csi2a);
+
+	omap4iss_flush(iss);
+
+#if defined(DEBUG) && defined(ISS_ISR_DEBUG)
+	iss_isr_dbg(iss, irqstatus);
+#endif
+
+	return IRQ_HANDLED;
+}
+
+/* -----------------------------------------------------------------------------
+ * Pipeline power management
+ *
+ * Entities must be powered up when part of a pipeline that contains at least
+ * one open video device node.
+ *
+ * To achieve this use the entity use_count field to track the number of users.
+ * For entities corresponding to video device nodes the use_count field stores
+ * the users count of the node. For entities corresponding to subdevs the
+ * use_count field stores the total number of users of all video device nodes
+ * in the pipeline.
+ *
+ * The omap4iss_pipeline_pm_use() function must be called in the open() and
+ * close() handlers of video device nodes. It increments or decrements the use
+ * count of all subdev entities in the pipeline.
+ *
+ * To react to link management on powered pipelines, the link setup notification
+ * callback updates the use count of all entities in the source and sink sides
+ * of the link.
+ */
+
+/*
+ * iss_pipeline_pm_use_count - Count the number of users of a pipeline
+ * @entity: The entity
+ *
+ * Return the total number of users of all video device nodes in the pipeline.
+ */
+static int iss_pipeline_pm_use_count(struct media_entity *entity)
+{
+	struct media_entity_graph graph;
+	int use = 0;
+
+	media_entity_graph_walk_start(&graph, entity);
+
+	while ((entity = media_entity_graph_walk_next(&graph))) {
+		if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE)
+			use += entity->use_count;
+	}
+
+	return use;
+}
+
+/*
+ * iss_pipeline_pm_power_one - Apply power change to an entity
+ * @entity: The entity
+ * @change: Use count change
+ *
+ * Change the entity use count by @change. If the entity is a subdev update its
+ * power state by calling the core::s_power operation when the use count goes
+ * from 0 to != 0 or from != 0 to 0.
+ *
+ * Return 0 on success or a negative error code on failure.
+ */
+static int iss_pipeline_pm_power_one(struct media_entity *entity, int change)
+{
+	struct v4l2_subdev *subdev;
+
+	subdev = media_entity_type(entity) == MEDIA_ENT_T_V4L2_SUBDEV
+	       ? media_entity_to_v4l2_subdev(entity) : NULL;
+
+	if (entity->use_count == 0 && change > 0 && subdev != NULL) {
+		int ret;
+
+		ret = v4l2_subdev_call(subdev, core, s_power, 1);
+		if (ret < 0 && ret != -ENOIOCTLCMD)
+			return ret;
+	}
+
+	entity->use_count += change;
+	WARN_ON(entity->use_count < 0);
+
+	if (entity->use_count == 0 && change < 0 && subdev != NULL)
+		v4l2_subdev_call(subdev, core, s_power, 0);
+
+	return 0;
+}
+
+/*
+ * iss_pipeline_pm_power - Apply power change to all entities in a pipeline
+ * @entity: The entity
+ * @change: Use count change
+ *
+ * Walk the pipeline to update the use count and the power state of all non-node
+ * entities.
+ *
+ * Return 0 on success or a negative error code on failure.
+ */
+static int iss_pipeline_pm_power(struct media_entity *entity, int change)
+{
+	struct media_entity_graph graph;
+	struct media_entity *first = entity;
+	int ret = 0;
+
+	if (!change)
+		return 0;
+
+	media_entity_graph_walk_start(&graph, entity);
+
+	while (!ret && (entity = media_entity_graph_walk_next(&graph)))
+		if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE)
+			ret = iss_pipeline_pm_power_one(entity, change);
+
+	if (!ret)
+		return 0;
+
+	media_entity_graph_walk_start(&graph, first);
+
+	while ((first = media_entity_graph_walk_next(&graph))
+	       && first != entity)
+		if (media_entity_type(first) != MEDIA_ENT_T_DEVNODE)
+			iss_pipeline_pm_power_one(first, -change);
+
+	return ret;
+}
+
+/*
+ * omap4iss_pipeline_pm_use - Update the use count of an entity
+ * @entity: The entity
+ * @use: Use (1) or stop using (0) the entity
+ *
+ * Update the use count of all entities in the pipeline and power entities on or
+ * off accordingly.
+ *
+ * Return 0 on success or a negative error code on failure. Powering entities
+ * off is assumed to never fail. No failure can occur when the use parameter is
+ * set to 0.
+ */
+int omap4iss_pipeline_pm_use(struct media_entity *entity, int use)
+{
+	int change = use ? 1 : -1;
+	int ret;
+
+	mutex_lock(&entity->parent->graph_mutex);
+
+	/* Apply use count to node. */
+	entity->use_count += change;
+	WARN_ON(entity->use_count < 0);
+
+	/* Apply power change to connected non-nodes. */
+	ret = iss_pipeline_pm_power(entity, change);
+	if (ret < 0)
+		entity->use_count -= change;
+
+	mutex_unlock(&entity->parent->graph_mutex);
+
+	return ret;
+}
+
+/*
+ * iss_pipeline_link_notify - Link management notification callback
+ * @source: Pad at the start of the link
+ * @sink: Pad at the end of the link
+ * @flags: New link flags that will be applied
+ *
+ * React to link management on powered pipelines by updating the use count of
+ * all entities in the source and sink sides of the link. Entities are powered
+ * on or off accordingly.
+ *
+ * Return 0 on success or a negative error code on failure. Powering entities
+ * off is assumed to never fail. This function will not fail for disconnection
+ * events.
+ */
+static int iss_pipeline_link_notify(struct media_pad *source,
+				    struct media_pad *sink, u32 flags)
+{
+	int source_use = iss_pipeline_pm_use_count(source->entity);
+	int sink_use = iss_pipeline_pm_use_count(sink->entity);
+	int ret;
+
+	if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+		/* Powering off entities is assumed to never fail. */
+		iss_pipeline_pm_power(source->entity, -sink_use);
+		iss_pipeline_pm_power(sink->entity, -source_use);
+		return 0;
+	}
+
+	ret = iss_pipeline_pm_power(source->entity, sink_use);
+	if (ret < 0)
+		return ret;
+
+	ret = iss_pipeline_pm_power(sink->entity, source_use);
+	if (ret < 0)
+		iss_pipeline_pm_power(source->entity, -sink_use);
+
+	return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * Pipeline stream management
+ */
+
+/*
+ * iss_pipeline_enable - Enable streaming on a pipeline
+ * @pipe: ISS pipeline
+ * @mode: Stream mode (single shot or continuous)
+ *
+ * Walk the entities chain starting at the pipeline output video node and start
+ * all modules in the chain in the given mode.
+ *
+ * Return 0 if successful, or the return value of the failed video::s_stream
+ * operation otherwise.
+ */
+static int iss_pipeline_enable(struct iss_pipeline *pipe,
+			       enum iss_pipeline_stream_state mode)
+{
+	struct media_entity *entity;
+	struct media_pad *pad;
+	struct v4l2_subdev *subdev;
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&pipe->lock, flags);
+	pipe->state &= ~(ISS_PIPELINE_IDLE_INPUT | ISS_PIPELINE_IDLE_OUTPUT);
+	spin_unlock_irqrestore(&pipe->lock, flags);
+
+	pipe->do_propagation = false;
+
+	entity = &pipe->output->video.entity;
+	while (1) {
+		pad = &entity->pads[0];
+		if (!(pad->flags & MEDIA_PAD_FL_SINK))
+			break;
+
+		pad = media_entity_remote_source(pad);
+		if (pad == NULL ||
+		    media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+			break;
+
+		entity = pad->entity;
+		subdev = media_entity_to_v4l2_subdev(entity);
+
+		ret = v4l2_subdev_call(subdev, video, s_stream, mode);
+		if (ret < 0 && ret != -ENOIOCTLCMD)
+			break;
+	}
+
+	return ret;
+}
+
+/*
+ * iss_pipeline_disable - Disable streaming on a pipeline
+ * @pipe: ISS pipeline
+ *
+ * Walk the entities chain starting at the pipeline output video node and stop
+ * all modules in the chain. Wait synchronously for the modules to be stopped if
+ * necessary.
+ */
+static int iss_pipeline_disable(struct iss_pipeline *pipe)
+{
+	struct media_entity *entity;
+	struct media_pad *pad;
+	struct v4l2_subdev *subdev;
+	int failure = 0;
+
+	entity = &pipe->output->video.entity;
+	while (1) {
+		pad = &entity->pads[0];
+		if (!(pad->flags & MEDIA_PAD_FL_SINK))
+			break;
+
+		pad = media_entity_remote_source(pad);
+		if (pad == NULL ||
+		    media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+			break;
+
+		entity = pad->entity;
+		subdev = media_entity_to_v4l2_subdev(entity);
+
+		v4l2_subdev_call(subdev, video, s_stream, 0);
+	}
+
+	return failure;
+}
+
+/*
+ * omap4iss_pipeline_set_stream - Enable/disable streaming on a pipeline
+ * @pipe: ISS pipeline
+ * @state: Stream state (stopped, single shot or continuous)
+ *
+ * Set the pipeline to the given stream state. Pipelines can be started in
+ * single-shot or continuous mode.
+ *
+ * Return 0 if successful, or the return value of the failed video::s_stream
+ * operation otherwise. The pipeline state is not updated when the operation
+ * fails, except when stopping the pipeline.
+ */
+int omap4iss_pipeline_set_stream(struct iss_pipeline *pipe,
+				 enum iss_pipeline_stream_state state)
+{
+	int ret;
+
+	if (state == ISS_PIPELINE_STREAM_STOPPED)
+		ret = iss_pipeline_disable(pipe);
+	else
+		ret = iss_pipeline_enable(pipe, state);
+
+	if (ret == 0 || state == ISS_PIPELINE_STREAM_STOPPED)
+		pipe->stream_state = state;
+
+	return ret;
+}
+
+/*
+ * iss_pipeline_is_last - Verify if entity has an enabled link to the output
+ *			  video node
+ * @me: ISS module's media entity
+ *
+ * Returns 1 if the entity has an enabled link to the output video node or 0
+ * otherwise. It's true only while pipeline can have no more than one output
+ * node.
+ */
+static int iss_pipeline_is_last(struct media_entity *me)
+{
+	struct iss_pipeline *pipe;
+	struct media_pad *pad;
+
+	if (!me->pipe)
+		return 0;
+	pipe = to_iss_pipeline(me);
+	if (pipe->stream_state == ISS_PIPELINE_STREAM_STOPPED)
+		return 0;
+	pad = media_entity_remote_source(&pipe->output->pad);
+	return pad->entity == me;
+}
+
+static int iss_reset(struct iss_device *iss)
+{
+	unsigned long timeout = 0;
+
+	writel(readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_SYSCONFIG) |
+		ISS_HL_SYSCONFIG_SOFTRESET,
+		iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_SYSCONFIG);
+
+	while (readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_SYSCONFIG) &
+			ISS_HL_SYSCONFIG_SOFTRESET) {
+		if (timeout++ > 10000) {
+			dev_alert(iss->dev, "cannot reset ISS\n");
+			return -ETIMEDOUT;
+		}
+		udelay(1);
+	}
+
+	return 0;
+}
+
+/*
+ * iss_save_context - Saves the values of the ISS module registers.
+ * @iss: OMAP4 ISS device
+ * @reg_list: Structure containing pairs of register address and value to
+ *            modify on OMAP.
+ */
+static void
+iss_save_context(struct iss_device *iss, struct iss_reg *reg_list)
+{
+	struct iss_reg *next = reg_list;
+
+	for (; next->reg != ISS_TOK_TERM; next++)
+		next->val = readl(iss->regs[next->mmio_range] + next->reg);
+}
+
+/*
+ * iss_restore_context - Restores the values of the ISS module registers.
+ * @iss: OMAP4 ISS device
+ * @reg_list: Structure containing pairs of register address and value to
+ *            modify on OMAP.
+ */
+static void
+iss_restore_context(struct iss_device *iss, struct iss_reg *reg_list)
+{
+	struct iss_reg *next = reg_list;
+
+	for (; next->reg != ISS_TOK_TERM; next++)
+		writel(next->val, iss->regs[next->mmio_range] + next->reg);
+}
+
+/*
+ * iss_save_ctx - Saves ISS context.
+ * @iss: OMAP4 ISS device
+ *
+ * Routine for saving the context of each module in the ISS.
+ */
+static void iss_save_ctx(struct iss_device *iss)
+{
+	iss_save_context(iss, iss_reg_list);
+}
+
+/*
+ * iss_restore_ctx - Restores ISS context.
+ * @iss: OMAP4 ISS device
+ *
+ * Routine for restoring the context of each module in the ISS.
+ */
+static void iss_restore_ctx(struct iss_device *iss)
+{
+	iss_restore_context(iss, iss_reg_list);
+}
+
+/*
+ * iss_module_sync_idle - Helper to sync module with its idle state
+ * @me: ISS submodule's media entity
+ * @wait: ISS submodule's wait queue for streamoff/interrupt synchronization
+ * @stopping: flag which tells module wants to stop
+ *
+ * This function checks if ISS submodule needs to wait for next interrupt. If
+ * yes, makes the caller to sleep while waiting for such event.
+ */
+int omap4iss_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait,
+			      atomic_t *stopping)
+{
+	struct iss_pipeline *pipe = to_iss_pipeline(me);
+
+	if (pipe->stream_state == ISS_PIPELINE_STREAM_STOPPED ||
+	    (pipe->stream_state == ISS_PIPELINE_STREAM_SINGLESHOT &&
+	     !iss_pipeline_ready(pipe)))
+		return 0;
+
+	/*
+	 * atomic_set() doesn't include memory barrier on ARM platform for SMP
+	 * scenario. We'll call it here to avoid race conditions.
+	 */
+	atomic_set(stopping, 1);
+	smp_mb();
+
+	/*
+	 * If module is the last one, it's writing to memory. In this case,
+	 * it's necessary to check if the module is already paused due to
+	 * DMA queue underrun or if it has to wait for next interrupt to be
+	 * idle.
+	 * If it isn't the last one, the function won't sleep but *stopping
+	 * will still be set to warn next submodule caller's interrupt the
+	 * module wants to be idle.
+	 */
+	if (iss_pipeline_is_last(me)) {
+		struct iss_video *video = pipe->output;
+		unsigned long flags;
+
+		spin_lock_irqsave(&video->qlock, flags);
+		if (video->dmaqueue_flags & ISS_VIDEO_DMAQUEUE_UNDERRUN) {
+			spin_unlock_irqrestore(&video->qlock, flags);
+			atomic_set(stopping, 0);
+			smp_mb();
+			return 0;
+		}
+		spin_unlock_irqrestore(&video->qlock, flags);
+		if (!wait_event_timeout(*wait, !atomic_read(stopping),
+					msecs_to_jiffies(1000))) {
+			atomic_set(stopping, 0);
+			smp_mb();
+			return -ETIMEDOUT;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * omap4iss_module_sync_is_stopped - Helper to verify if module was stopping
+ * @wait: ISS submodule's wait queue for streamoff/interrupt synchronization
+ * @stopping: flag which tells module wants to stop
+ *
+ * This function checks if ISS submodule was stopping. In case of yes, it
+ * notices the caller by setting stopping to 0 and waking up the wait queue.
+ * Returns 1 if it was stopping or 0 otherwise.
+ */
+int omap4iss_module_sync_is_stopping(wait_queue_head_t *wait,
+				     atomic_t *stopping)
+{
+	if (atomic_cmpxchg(stopping, 1, 0)) {
+		wake_up(wait);
+		return 1;
+	}
+
+	return 0;
+}
+
+/* --------------------------------------------------------------------------
+ * Clock management
+ */
+
+#define ISS_CLKCTRL_MASK	(ISS_CLKCTRL_CSI2_A)
+
+static int __iss_subclk_update(struct iss_device *iss)
+{
+	u32 clk = 0;
+	int ret = 0, timeout = 1000;
+
+	if (iss->subclk_resources & OMAP4_ISS_SUBCLK_CSI2_A)
+		clk |= ISS_CLKCTRL_CSI2_A;
+
+	writel((readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_CLKCTRL) &
+		~ISS_CLKCTRL_MASK) | clk,
+		iss->regs[OMAP4_ISS_MEM_TOP] + ISS_CLKCTRL);
+
+	/* Wait for HW assertion */
+	while (timeout-- > 0) {
+		udelay(1);
+		if ((readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_CLKSTAT) &
+		     ISS_CLKCTRL_MASK) == clk)
+			break;
+	}
+
+	if (!timeout)
+		ret = -EBUSY;
+
+	return ret;
+}
+
+int omap4iss_subclk_enable(struct iss_device *iss,
+			    enum iss_subclk_resource res)
+{
+	iss->subclk_resources |= res;
+
+	return __iss_subclk_update(iss);
+}
+
+int omap4iss_subclk_disable(struct iss_device *iss,
+			     enum iss_subclk_resource res)
+{
+	iss->subclk_resources &= ~res;
+
+	return __iss_subclk_update(iss);
+}
+
+/*
+ * iss_enable_clocks - Enable ISS clocks
+ * @iss: OMAP4 ISS device
+ *
+ * Return 0 if successful, or clk_enable return value if any of tthem fails.
+ */
+static int iss_enable_clocks(struct iss_device *iss)
+{
+	int r;
+
+	r = clk_enable(iss->iss_fck);
+	if (r) {
+		dev_err(iss->dev, "clk_enable iss_fck failed\n");
+		goto out_clk_enable_fck;
+	}
+
+	r = clk_enable(iss->iss_ctrlclk);
+	if (r) {
+		dev_err(iss->dev, "clk_enable iss_ctrlclk failed\n");
+		goto out_clk_enable_ctrlclk;
+	}
+	return 0;
+
+out_clk_enable_ctrlclk:
+	clk_disable(iss->iss_fck);
+out_clk_enable_fck:
+	return r;
+}
+
+/*
+ * iss_disable_clocks - Disable ISS clocks
+ * @iss: OMAP4 ISS device
+ */
+static void iss_disable_clocks(struct iss_device *iss)
+{
+	clk_disable(iss->iss_ctrlclk);
+	clk_disable(iss->iss_fck);
+}
+
+static void iss_put_clocks(struct iss_device *iss)
+{
+	if (iss->iss_fck) {
+		clk_put(iss->iss_fck);
+		iss->iss_fck = NULL;
+	}
+
+	if (iss->iss_ctrlclk) {
+		clk_put(iss->iss_ctrlclk);
+		iss->iss_ctrlclk = NULL;
+	}
+}
+
+static int iss_get_clocks(struct iss_device *iss)
+{
+	iss->iss_fck = clk_get(iss->dev, "iss_fck");
+	if (IS_ERR(iss->iss_fck)) {
+		dev_err(iss->dev, "Unable to get iss_fck clock info\n");
+		iss_put_clocks(iss);
+		return PTR_ERR(iss->iss_fck);
+	}
+
+	iss->iss_ctrlclk = clk_get(iss->dev, "iss_ctrlclk");
+	if (IS_ERR(iss->iss_ctrlclk)) {
+		dev_err(iss->dev, "Unable to get iss_ctrlclk clock info\n");
+		iss_put_clocks(iss);
+		return PTR_ERR(iss->iss_fck);
+	}
+
+	return 0;
+}
+
+/*
+ * omap4iss_get - Acquire the ISS resource.
+ *
+ * Initializes the clocks for the first acquire.
+ *
+ * Increment the reference count on the ISS. If the first reference is taken,
+ * enable clocks and power-up all submodules.
+ *
+ * Return a pointer to the ISS device structure, or NULL if an error occurred.
+ */
+struct iss_device *omap4iss_get(struct iss_device *iss)
+{
+	struct iss_device *__iss = iss;
+
+	if (iss == NULL)
+		return NULL;
+
+	mutex_lock(&iss->iss_mutex);
+	if (iss->ref_count > 0)
+		goto out;
+
+	if (iss_enable_clocks(iss) < 0) {
+		__iss = NULL;
+		goto out;
+	}
+
+	/* We don't want to restore context before saving it! */
+	if (iss->has_context)
+		iss_restore_ctx(iss);
+	else
+		iss->has_context = 1;
+
+	iss_enable_interrupts(iss);
+
+out:
+	if (__iss != NULL)
+		iss->ref_count++;
+	mutex_unlock(&iss->iss_mutex);
+
+	return __iss;
+}
+
+/*
+ * omap4iss_put - Release the ISS
+ *
+ * Decrement the reference count on the ISS. If the last reference is released,
+ * power-down all submodules, disable clocks and free temporary buffers.
+ */
+void omap4iss_put(struct iss_device *iss)
+{
+	if (iss == NULL)
+		return;
+
+	mutex_lock(&iss->iss_mutex);
+	BUG_ON(iss->ref_count == 0);
+	if (--iss->ref_count == 0) {
+		iss_disable_interrupts(iss);
+		iss_save_ctx(iss);
+		iss_disable_clocks(iss);
+	}
+	mutex_unlock(&iss->iss_mutex);
+}
+
+static int iss_map_mem_resource(struct platform_device *pdev,
+				struct iss_device *iss,
+				enum iss_mem_resources res)
+{
+	struct resource *mem;
+
+	/* request the mem region for the camera registers */
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, res);
+	if (!mem) {
+		dev_err(iss->dev, "no mem resource?\n");
+		return -ENODEV;
+	}
+
+	if (!request_mem_region(mem->start, resource_size(mem), pdev->name)) {
+		dev_err(iss->dev,
+			"cannot reserve camera register I/O region\n");
+		return -ENODEV;
+	}
+	iss->res[res] = mem;
+
+	/* map the region */
+	iss->regs[res] = ioremap_nocache(mem->start, resource_size(mem));
+	if (!iss->regs[res]) {
+		dev_err(iss->dev, "cannot map camera register I/O region\n");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void iss_unregister_entities(struct iss_device *iss)
+{
+	omap4iss_csi2_unregister_entities(&iss->csi2a);
+
+	v4l2_device_unregister(&iss->v4l2_dev);
+	media_device_unregister(&iss->media_dev);
+}
+
+/*
+ * iss_register_subdev_group - Register a group of subdevices
+ * @iss: OMAP4 ISS device
+ * @board_info: I2C subdevs board information array
+ *
+ * Register all I2C subdevices in the board_info array. The array must be
+ * terminated by a NULL entry, and the first entry must be the sensor.
+ *
+ * Return a pointer to the sensor media entity if it has been successfully
+ * registered, or NULL otherwise.
+ */
+static struct v4l2_subdev *
+iss_register_subdev_group(struct iss_device *iss,
+		     struct iss_subdev_i2c_board_info *board_info)
+{
+	struct v4l2_subdev *sensor = NULL;
+	unsigned int first;
+
+	if (board_info->board_info == NULL)
+		return NULL;
+
+	for (first = 1; board_info->board_info; ++board_info, first = 0) {
+		struct v4l2_subdev *subdev;
+		struct i2c_adapter *adapter;
+
+		adapter = i2c_get_adapter(board_info->i2c_adapter_id);
+		if (adapter == NULL) {
+			printk(KERN_ERR "%s: Unable to get I2C adapter %d for "
+				"device %s\n", __func__,
+				board_info->i2c_adapter_id,
+				board_info->board_info->type);
+			continue;
+		}
+
+		subdev = v4l2_i2c_new_subdev_board(&iss->v4l2_dev, adapter,
+				board_info->board_info, NULL);
+		if (subdev == NULL) {
+			printk(KERN_ERR "%s: Unable to register subdev %s\n",
+				__func__, board_info->board_info->type);
+			continue;
+		}
+
+		if (first)
+			sensor = subdev;
+	}
+
+	return sensor;
+}
+
+static int iss_register_entities(struct iss_device *iss)
+{
+	struct iss_platform_data *pdata = iss->pdata;
+	struct iss_v4l2_subdevs_group *subdevs;
+	int ret;
+
+	iss->media_dev.dev = iss->dev;
+	strlcpy(iss->media_dev.model, "TI OMAP4 ISS",
+		sizeof(iss->media_dev.model));
+	iss->media_dev.link_notify = iss_pipeline_link_notify;
+	ret = media_device_register(&iss->media_dev);
+	if (ret < 0) {
+		printk(KERN_ERR "%s: Media device registration failed (%d)\n",
+			__func__, ret);
+		return ret;
+	}
+
+	iss->v4l2_dev.mdev = &iss->media_dev;
+	ret = v4l2_device_register(iss->dev, &iss->v4l2_dev);
+	if (ret < 0) {
+		printk(KERN_ERR "%s: V4L2 device registration failed (%d)\n",
+			__func__, ret);
+		goto done;
+	}
+
+	/* Register internal entities */
+	ret = omap4iss_csi2_register_entities(&iss->csi2a, &iss->v4l2_dev);
+	if (ret < 0)
+		goto done;
+
+	/* Register external entities */
+	for (subdevs = pdata->subdevs; subdevs && subdevs->subdevs; ++subdevs) {
+		struct v4l2_subdev *sensor;
+		struct media_entity *input;
+		unsigned int flags;
+		unsigned int pad;
+
+		sensor = iss_register_subdev_group(iss, subdevs->subdevs);
+		if (sensor == NULL)
+			continue;
+
+		sensor->host_priv = subdevs;
+
+		/* Connect the sensor to the correct interface module.
+		 * CSI2a receiver through CSIPHY1.
+		 */
+		switch (subdevs->interface) {
+		case ISS_INTERFACE_CSI2A_PHY1:
+			input = &iss->csi2a.subdev.entity;
+			pad = CSI2_PAD_SINK;
+			flags = MEDIA_LNK_FL_IMMUTABLE
+			      | MEDIA_LNK_FL_ENABLED;
+			break;
+
+		default:
+			printk(KERN_ERR "%s: invalid interface type %u\n",
+			       __func__, subdevs->interface);
+			ret = -EINVAL;
+			goto done;
+		}
+
+		ret = media_entity_create_link(&sensor->entity, 0, input, pad,
+					       flags);
+		if (ret < 0)
+			goto done;
+	}
+
+	ret = v4l2_device_register_subdev_nodes(&iss->v4l2_dev);
+
+done:
+	if (ret < 0)
+		iss_unregister_entities(iss);
+
+	return ret;
+}
+
+static void iss_cleanup_modules(struct iss_device *iss)
+{
+	omap4iss_csi2_cleanup(iss);
+}
+
+static int iss_initialize_modules(struct iss_device *iss)
+{
+	int ret;
+
+	ret = omap4iss_csiphy_init(iss);
+	if (ret < 0) {
+		dev_err(iss->dev, "CSI PHY initialization failed\n");
+		goto error_csiphy;
+	}
+
+	ret = omap4iss_csi2_init(iss);
+	if (ret < 0) {
+		dev_err(iss->dev, "CSI2 initialization failed\n");
+		goto error_csi2;
+	}
+
+	return 0;
+
+error_csi2:
+error_csiphy:
+	return ret;
+}
+
+static int iss_probe(struct platform_device *pdev)
+{
+	struct iss_platform_data *pdata = pdev->dev.platform_data;
+	struct iss_device *iss;
+	int i, ret;
+
+	if (pdata == NULL)
+		return -EINVAL;
+
+	iss = kzalloc(sizeof(*iss), GFP_KERNEL);
+	if (!iss) {
+		dev_err(&pdev->dev, "Could not allocate memory\n");
+		return -ENOMEM;
+	}
+
+	mutex_init(&iss->iss_mutex);
+
+	iss->dev = &pdev->dev;
+	iss->pdata = pdata;
+	iss->ref_count = 0;
+
+	iss->raw_dmamask = DMA_BIT_MASK(32);
+	iss->dev->dma_mask = &iss->raw_dmamask;
+	iss->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+	platform_set_drvdata(pdev, iss);
+
+	/* Clocks */
+	ret = iss_map_mem_resource(pdev, iss, OMAP4_ISS_MEM_TOP);
+	if (ret < 0)
+		goto error;
+
+	ret = iss_get_clocks(iss);
+	if (ret < 0)
+		goto error;
+
+	if (omap4iss_get(iss) == NULL)
+		goto error;
+
+	ret = iss_reset(iss);
+	if (ret < 0)
+		goto error_iss;
+
+	iss->revision = readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_REVISION);
+	dev_info(iss->dev, "Revision %08x found\n", iss->revision);
+
+	for (i = 1; i < OMAP4_ISS_MEM_LAST; i++) {
+		ret = iss_map_mem_resource(pdev, iss, i);
+		if (ret)
+			goto error_iss;
+	}
+
+	/* Interrupt */
+	iss->irq_num = platform_get_irq(pdev, 0);
+	if (iss->irq_num <= 0) {
+		dev_err(iss->dev, "No IRQ resource\n");
+		ret = -ENODEV;
+		goto error_iss;
+	}
+
+	if (request_irq(iss->irq_num, iss_isr, IRQF_SHARED, "OMAP4 ISS", iss)) {
+		dev_err(iss->dev, "Unable to request IRQ\n");
+		ret = -EINVAL;
+		goto error_iss;
+	}
+
+	/* Entities */
+	ret = iss_initialize_modules(iss);
+	if (ret < 0)
+		goto error_irq;
+
+	ret = iss_register_entities(iss);
+	if (ret < 0)
+		goto error_modules;
+
+	omap4iss_put(iss);
+
+	return 0;
+
+error_modules:
+	iss_cleanup_modules(iss);
+error_irq:
+	free_irq(iss->irq_num, iss);
+error_iss:
+	omap4iss_put(iss);
+error:
+	iss_put_clocks(iss);
+
+	for (i = 0; i < OMAP4_ISS_MEM_LAST; i++) {
+		if (iss->regs[i]) {
+			iounmap(iss->regs[i]);
+			iss->regs[i] = NULL;
+		}
+
+		if (iss->res[i]) {
+			release_mem_region(iss->res[i]->start,
+					   resource_size(iss->res[i]));
+			iss->res[i] = NULL;
+		}
+	}
+	platform_set_drvdata(pdev, NULL);
+	kfree(iss);
+
+	return ret;
+}
+
+static int iss_remove(struct platform_device *pdev)
+{
+	struct iss_device *iss = platform_get_drvdata(pdev);
+	int i;
+
+	iss_unregister_entities(iss);
+	iss_cleanup_modules(iss);
+
+	free_irq(iss->irq_num, iss);
+	iss_put_clocks(iss);
+
+	for (i = 0; i < OMAP4_ISS_MEM_LAST; i++) {
+		if (iss->regs[i]) {
+			iounmap(iss->regs[i]);
+			iss->regs[i] = NULL;
+		}
+
+		if (iss->res[i]) {
+			release_mem_region(iss->res[i]->start,
+					   resource_size(iss->res[i]));
+			iss->res[i] = NULL;
+		}
+	}
+
+	kfree(iss);
+
+	return 0;
+}
+
+static struct platform_device_id omap4iss_id_table[] = {
+	{ "omap4iss", 0 },
+	{ },
+};
+MODULE_DEVICE_TABLE(platform, omap4iss_id_table);
+
+static struct platform_driver iss_driver = {
+	.probe		= iss_probe,
+	.remove		= iss_remove,
+	.id_table	= omap4iss_id_table,
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "omap4iss",
+	},
+};
+
+static int __init iss_init(void)
+{
+	return platform_driver_register(&iss_driver);
+}
+
+static void __exit iss_exit(void)
+{
+	platform_driver_unregister(&iss_driver);
+}
+
+/*
+ * FIXME: Had to make it late_initcall. Strangely while being module_init,
+ * The I2C communication was failing in the sensor, because no XCLK was
+ * provided.
+ */
+late_initcall(iss_init);
+module_exit(iss_exit);
+
+MODULE_DESCRIPTION("TI OMAP4 ISS driver");
+MODULE_AUTHOR("Sergio Aguirre <saaguirre@ti.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/omap4iss/iss.h b/drivers/media/video/omap4iss/iss.h
new file mode 100644
index 0000000..8346c80
--- /dev/null
+++ b/drivers/media/video/omap4iss/iss.h
@@ -0,0 +1,133 @@
+/*
+ * iss.h
+ *
+ * Copyright (C) 2011 Texas Instruments.
+ *
+ * Author: Sergio Aguirre <saaguirre@ti.com>
+ */
+
+#ifndef _OMAP4_ISS_H_
+#define _OMAP4_ISS_H_
+
+#include <media/v4l2-device.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/wait.h>
+
+#include <plat/omap4-iss.h>
+
+#include "iss_regs.h"
+#include "iss_csiphy.h"
+#include "iss_csi2.h"
+
+#define ISS_TOK_TERM		0xFFFFFFFF	/*
+						 * terminating token for ISS
+						 * modules reg list
+						 */
+#define to_iss_device(ptr_module)				\
+	container_of(ptr_module, struct iss_device, ptr_module)
+#define to_device(ptr_module)						\
+	(to_iss_device(ptr_module)->dev)
+
+enum iss_mem_resources {
+	OMAP4_ISS_MEM_TOP,
+	OMAP4_ISS_MEM_CSI2_A_REGS1,
+	OMAP4_ISS_MEM_CAMERARX_CORE1,
+	OMAP4_ISS_MEM_LAST,
+};
+
+enum iss_subclk_resource {
+	OMAP4_ISS_SUBCLK_SIMCOP		= (1 << 0),
+	OMAP4_ISS_SUBCLK_ISP		= (1 << 1),
+	OMAP4_ISS_SUBCLK_CSI2_A		= (1 << 2),
+	OMAP4_ISS_SUBCLK_CSI2_B		= (1 << 3),
+	OMAP4_ISS_SUBCLK_CCP2		= (1 << 4),
+};
+
+/*
+ * struct iss_reg - Structure for ISS register values.
+ * @reg: 32-bit Register address.
+ * @val: 32-bit Register value.
+ */
+struct iss_reg {
+	enum iss_mem_resources mmio_range;
+	u32 reg;
+	u32 val;
+};
+
+struct iss_platform_callback {
+	int (*csiphy_config)(struct iss_csiphy *phy,
+			     struct iss_csiphy_dphy_cfg *dphy,
+			     struct iss_csiphy_lanes_cfg *lanes);
+};
+
+struct iss_device {
+	struct v4l2_device v4l2_dev;
+	struct media_device media_dev;
+	struct device *dev;
+	u32 revision;
+
+	/* platform HW resources */
+	struct iss_platform_data *pdata;
+	unsigned int irq_num;
+
+	struct resource *res[OMAP4_ISS_MEM_LAST];
+	void __iomem *regs[OMAP4_ISS_MEM_LAST];
+
+	u64 raw_dmamask;
+
+	struct mutex iss_mutex;	/* For handling ref_count field */
+	int has_context;
+	int ref_count;
+
+	struct clk *iss_fck;
+	struct clk *iss_ctrlclk;
+
+	/* ISS modules */
+	struct iss_csi2_device csi2a;
+	struct iss_csiphy csiphy1;
+
+	unsigned int subclk_resources;
+
+	struct iss_platform_callback platform_cb;
+};
+
+#define v4l2_dev_to_iss_device(dev) \
+	container_of(dev, struct iss_device, v4l2_dev)
+
+int omap4iss_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait,
+			      atomic_t *stopping);
+
+int omap4iss_module_sync_is_stopping(wait_queue_head_t *wait,
+				     atomic_t *stopping);
+
+int omap4iss_pipeline_set_stream(struct iss_pipeline *pipe,
+				 enum iss_pipeline_stream_state state);
+
+struct iss_device *omap4iss_get(struct iss_device *iss);
+void omap4iss_put(struct iss_device *iss);
+int omap4iss_subclk_enable(struct iss_device *iss,
+			   enum iss_subclk_resource res);
+int omap4iss_subclk_disable(struct iss_device *iss,
+			    enum iss_subclk_resource res);
+
+int omap4iss_pipeline_pm_use(struct media_entity *entity, int use);
+
+int omap4iss_register_entities(struct platform_device *pdev,
+			       struct v4l2_device *v4l2_dev);
+void omap4iss_unregister_entities(struct platform_device *pdev);
+
+static inline enum v4l2_buf_type
+iss_pad_buffer_type(const struct v4l2_subdev *subdev, int pad)
+{
+	if (pad >= subdev->entity.num_pads)
+		return 0;
+
+	if (subdev->entity.pads[pad].flags & MEDIA_PAD_FL_SINK)
+		return V4L2_BUF_TYPE_VIDEO_OUTPUT;
+	else
+		return V4L2_BUF_TYPE_VIDEO_CAPTURE;
+}
+
+#endif /* _OMAP4_ISS_H_ */
diff --git a/drivers/media/video/omap4iss/iss_csi2.c b/drivers/media/video/omap4iss/iss_csi2.c
new file mode 100644
index 0000000..916d5ef
--- /dev/null
+++ b/drivers/media/video/omap4iss/iss_csi2.c
@@ -0,0 +1,1324 @@
+/*
+ * iss_csi2.c
+ *
+ * TI OMAP4 ISS - CSI PHY module
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ *
+ * Contacts: Sergio Aguirre <saaguirre@ti.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+#include <linux/delay.h>
+#include <media/v4l2-common.h>
+#include <linux/v4l2-mediabus.h>
+#include <linux/mm.h>
+
+#include "iss.h"
+#include "iss_regs.h"
+#include "iss_csi2.h"
+
+/*
+ * csi2_if_enable - Enable CSI2 Receiver interface.
+ * @enable: enable flag
+ *
+ */
+static void csi2_if_enable(struct iss_csi2_device *csi2, u8 enable)
+{
+	struct iss_csi2_ctrl_cfg *currctrl = &csi2->ctrl;
+
+	writel((readl(csi2->regs1 + CSI2_CTRL) & ~CSI2_CTRL_IF_EN) |
+		(enable ? CSI2_CTRL_IF_EN : 0),
+		csi2->regs1 + CSI2_CTRL);
+
+	currctrl->if_enable = enable;
+}
+
+/*
+ * csi2_recv_config - CSI2 receiver module configuration.
+ * @currctrl: iss_csi2_ctrl_cfg structure
+ *
+ */
+static void csi2_recv_config(struct iss_csi2_device *csi2,
+			     struct iss_csi2_ctrl_cfg *currctrl)
+{
+	u32 reg;
+
+	reg = readl(csi2->regs1 + CSI2_CTRL);
+
+	if (currctrl->frame_mode)
+		reg |= CSI2_CTRL_FRAME;
+	else
+		reg &= ~CSI2_CTRL_FRAME;
+
+	if (currctrl->vp_clk_enable)
+		reg |= CSI2_CTRL_VP_CLK_EN;
+	else
+		reg &= ~CSI2_CTRL_VP_CLK_EN;
+
+	if (currctrl->vp_only_enable)
+		reg |= CSI2_CTRL_VP_ONLY_EN;
+	else
+		reg &= ~CSI2_CTRL_VP_ONLY_EN;
+
+	reg &= ~CSI2_CTRL_VP_OUT_CTRL_MASK;
+	reg |= currctrl->vp_out_ctrl << CSI2_CTRL_VP_OUT_CTRL_SHIFT;
+
+	if (currctrl->ecc_enable)
+		reg |= CSI2_CTRL_ECC_EN;
+	else
+		reg &= ~CSI2_CTRL_ECC_EN;
+
+	/*
+	 * Set MFlag assertion boundaries to:
+	 * Low: 4/8 of FIFO size
+	 * High: 6/8 of FIFO size
+	 */
+	reg &= ~(CSI2_CTRL_MFLAG_LEVH_MASK | CSI2_CTRL_MFLAG_LEVL_MASK);
+	reg |= (2 << CSI2_CTRL_MFLAG_LEVH_SHIFT) |
+	       (4 << CSI2_CTRL_MFLAG_LEVL_SHIFT);
+
+	/* Generation of 16x64-bit bursts (Recommended) */
+	reg |= CSI2_CTRL_BURST_SIZE_EXPAND;
+
+	/* Do Non-Posted writes (Recommended) */
+	reg |= CSI2_CTRL_NON_POSTED_WRITE;
+
+	/*
+	 * Enforce Little endian for all formats, including:
+	 * YUV4:2:2 8-bit and YUV4:2:0 Legacy
+	 */
+	reg |= CSI2_CTRL_ENDIANNESS;
+
+	writel(reg, csi2->regs1 + CSI2_CTRL);
+}
+
+static const unsigned int csi2_input_fmts[] = {
+	V4L2_MBUS_FMT_SGRBG10_1X10,
+	V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8,
+	V4L2_MBUS_FMT_SRGGB10_1X10,
+	V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8,
+	V4L2_MBUS_FMT_SBGGR10_1X10,
+	V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8,
+	V4L2_MBUS_FMT_SGBRG10_1X10,
+	V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8,
+	V4L2_MBUS_FMT_UYVY8_1X16,
+	V4L2_MBUS_FMT_YUYV8_1X16,
+};
+
+/* To set the format on the CSI2 requires a mapping function that takes
+ * the following inputs:
+ * - 3 different formats (at this time)
+ * - 2 destinations (mem, vp+mem) (vp only handled separately)
+ * - 2 decompression options (on, off)
+ * Output should be CSI2 frame format code
+ * Array indices as follows: [format][dest][decompr]
+ * Not all combinations are valid. 0 means invalid.
+ */
+static const u16 __csi2_fmt_map[][2][2] = {
+	/* RAW10 formats */
+	{
+		/* Output to memory */
+		{
+			/* No DPCM decompression */
+			CSI2_PIX_FMT_RAW10_EXP16,
+			/* DPCM decompression */
+			0,
+		},
+		/* Output to both */
+		{
+			/* No DPCM decompression */
+			CSI2_PIX_FMT_RAW10_EXP16_VP,
+			/* DPCM decompression */
+			0,
+		},
+	},
+	/* RAW10 DPCM8 formats */
+	{
+		/* Output to memory */
+		{
+			/* No DPCM decompression */
+			CSI2_USERDEF_8BIT_DATA1,
+			/* DPCM decompression */
+			CSI2_USERDEF_8BIT_DATA1_DPCM10,
+		},
+		/* Output to both */
+		{
+			/* No DPCM decompression */
+			CSI2_PIX_FMT_RAW8_VP,
+			/* DPCM decompression */
+			CSI2_USERDEF_8BIT_DATA1_DPCM10_VP,
+		},
+	},
+	/* YUV422 formats */
+	{
+		/* Output to memory */
+		{
+			/* No DPCM decompression */
+			CSI2_PIX_FMT_YUV422_8BIT,
+			/* DPCM decompression */
+			0,
+		},
+		/* Output to both */
+		{
+			/* No DPCM decompression */
+			CSI2_PIX_FMT_YUV422_8BIT_VP,
+			/* DPCM decompression */
+			0,
+		},
+	},
+};
+
+/*
+ * csi2_ctx_map_format - Map CSI2 sink media bus format to CSI2 format ID
+ * @csi2: ISS CSI2 device
+ *
+ * Returns CSI2 physical format id
+ */
+static u16 csi2_ctx_map_format(struct iss_csi2_device *csi2)
+{
+	const struct v4l2_mbus_framefmt *fmt = &csi2->formats[CSI2_PAD_SINK];
+	int fmtidx, destidx;
+
+	switch (fmt->code) {
+	case V4L2_MBUS_FMT_SGRBG10_1X10:
+	case V4L2_MBUS_FMT_SRGGB10_1X10:
+	case V4L2_MBUS_FMT_SBGGR10_1X10:
+	case V4L2_MBUS_FMT_SGBRG10_1X10:
+		fmtidx = 0;
+		break;
+	case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8:
+	case V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8:
+	case V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8:
+	case V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8:
+		fmtidx = 1;
+		break;
+	case V4L2_MBUS_FMT_UYVY8_1X16:
+	case V4L2_MBUS_FMT_YUYV8_1X16:
+		fmtidx = 2;
+		break;
+	default:
+		WARN(1, KERN_ERR "CSI2: pixel format %08x unsupported!\n",
+		     fmt->code);
+		return 0;
+	}
+
+	if (!(csi2->output & CSI2_OUTPUT_IPIPEIF) &&
+	    !(csi2->output & CSI2_OUTPUT_MEMORY)) {
+		/* Neither output enabled is a valid combination */
+		return CSI2_PIX_FMT_OTHERS;
+	}
+
+	/* If we need to skip frames at the beginning of the stream disable the
+	 * video port to avoid sending the skipped frames to the IPIPEIF.
+	 */
+	destidx = csi2->frame_skip ? 0 : !!(csi2->output & CSI2_OUTPUT_IPIPEIF);
+
+	return __csi2_fmt_map[fmtidx][destidx][csi2->dpcm_decompress];
+}
+
+/*
+ * csi2_set_outaddr - Set memory address to save output image
+ * @csi2: Pointer to ISS CSI2a device.
+ * @addr: 32-bit memory address aligned on 32 byte boundary.
+ *
+ * Sets the memory address where the output will be saved.
+ *
+ * Returns 0 if successful, or -EINVAL if the address is not in the 32 byte
+ * boundary.
+ */
+static void csi2_set_outaddr(struct iss_csi2_device *csi2, u32 addr)
+{
+	struct iss_csi2_ctx_cfg *ctx = &csi2->contexts[0];
+
+	ctx->ping_addr = addr;
+	ctx->pong_addr = addr;
+	writel(ctx->ping_addr,
+	       csi2->regs1 + CSI2_CTX_PING_ADDR(ctx->ctxnum));
+	writel(ctx->pong_addr,
+	       csi2->regs1 + CSI2_CTX_PONG_ADDR(ctx->ctxnum));
+}
+
+/*
+ * is_usr_def_mapping - Checks whether USER_DEF_MAPPING should
+ *			be enabled by CSI2.
+ * @format_id: mapped format id
+ *
+ */
+static inline int is_usr_def_mapping(u32 format_id)
+{
+	return (format_id & 0x40) ? 1 : 0;
+}
+
+/*
+ * csi2_ctx_enable - Enable specified CSI2 context
+ * @ctxnum: Context number, valid between 0 and 7 values.
+ * @enable: enable
+ *
+ */
+static void csi2_ctx_enable(struct iss_csi2_device *csi2, u8 ctxnum, u8 enable)
+{
+	struct iss_csi2_ctx_cfg *ctx = &csi2->contexts[ctxnum];
+	u32 reg;
+
+	reg = readl(csi2->regs1 + CSI2_CTX_CTRL1(ctxnum));
+
+	if (enable) {
+		unsigned int skip = 0;
+
+		if (csi2->frame_skip)
+			skip = csi2->frame_skip;
+		else if (csi2->output & CSI2_OUTPUT_MEMORY)
+			skip = 1;
+
+		reg &= ~CSI2_CTX_CTRL1_COUNT_MASK;
+		reg |= CSI2_CTX_CTRL1_COUNT_UNLOCK
+		    |  (skip << CSI2_CTX_CTRL1_COUNT_SHIFT)
+		    |  CSI2_CTX_CTRL1_CTX_EN;
+	} else {
+		reg &= ~CSI2_CTX_CTRL1_CTX_EN;
+	}
+
+	writel(reg, csi2->regs1 + CSI2_CTX_CTRL1(ctxnum));
+	ctx->enabled = enable;
+}
+
+/*
+ * csi2_ctx_config - CSI2 context configuration.
+ * @ctx: context configuration
+ *
+ */
+static void csi2_ctx_config(struct iss_csi2_device *csi2,
+			    struct iss_csi2_ctx_cfg *ctx)
+{
+	u32 reg;
+
+	/* Set up CSI2_CTx_CTRL1 */
+	reg = readl(csi2->regs1 + CSI2_CTX_CTRL1(ctx->ctxnum));
+
+	if (ctx->eof_enabled)
+		reg |= CSI2_CTX_CTRL1_EOF_EN;
+	else
+		reg &= ~CSI2_CTX_CTRL1_EOF_EN;
+
+	if (ctx->eol_enabled)
+		reg |= CSI2_CTX_CTRL1_EOL_EN;
+	else
+		reg &= ~CSI2_CTX_CTRL1_EOL_EN;
+
+	if (ctx->checksum_enabled)
+		reg |= CSI2_CTX_CTRL1_CS_EN;
+	else
+		reg &= ~CSI2_CTX_CTRL1_CS_EN;
+
+	writel(reg, csi2->regs1 + CSI2_CTX_CTRL1(ctx->ctxnum));
+
+	/* Set up CSI2_CTx_CTRL2 */
+	reg = readl(csi2->regs1 + CSI2_CTX_CTRL2(ctx->ctxnum));
+
+	reg &= ~(CSI2_CTX_CTRL2_VIRTUAL_ID_MASK);
+	reg |= ctx->virtual_id << CSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT;
+
+	reg &= ~(CSI2_CTX_CTRL2_FORMAT_MASK);
+	reg |= ctx->format_id << CSI2_CTX_CTRL2_FORMAT_SHIFT;
+
+	if (ctx->dpcm_decompress) {
+		if (ctx->dpcm_predictor)
+			reg |= CSI2_CTX_CTRL2_DPCM_PRED;
+		else
+			reg &= ~CSI2_CTX_CTRL2_DPCM_PRED;
+	}
+
+	if (is_usr_def_mapping(ctx->format_id)) {
+		reg &= ~CSI2_CTX_CTRL2_USER_DEF_MAP_MASK;
+		reg |= 2 << CSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT;
+	}
+
+	writel(reg, csi2->regs1 + CSI2_CTX_CTRL2(ctx->ctxnum));
+
+	/* Set up CSI2_CTx_CTRL3 */
+	reg = readl(csi2->regs1 + CSI2_CTX_CTRL3(ctx->ctxnum));
+	reg &= ~(CSI2_CTX_CTRL3_ALPHA_MASK);
+	reg |= (ctx->alpha << CSI2_CTX_CTRL3_ALPHA_SHIFT);
+
+	writel(reg, csi2->regs1 + CSI2_CTX_CTRL3(ctx->ctxnum));
+
+	/* Set up CSI2_CTx_DAT_OFST */
+	reg = readl(csi2->regs1 + CSI2_CTX_DAT_OFST(ctx->ctxnum));
+	reg &= ~CSI2_CTX_DAT_OFST_MASK;
+	reg |= ctx->data_offset;
+	writel(reg, csi2->regs1 + CSI2_CTX_DAT_OFST(ctx->ctxnum));
+
+	writel(ctx->ping_addr,
+		       csi2->regs1 + CSI2_CTX_PING_ADDR(ctx->ctxnum));
+
+	writel(ctx->pong_addr,
+		       csi2->regs1 + CSI2_CTX_PONG_ADDR(ctx->ctxnum));
+}
+
+/*
+ * csi2_timing_config - CSI2 timing configuration.
+ * @timing: csi2_timing_cfg structure
+ */
+static void csi2_timing_config(struct iss_csi2_device *csi2,
+			       struct iss_csi2_timing_cfg *timing)
+{
+	u32 reg;
+
+	reg = readl(csi2->regs1 + CSI2_TIMING);
+
+	if (timing->force_rx_mode)
+		reg |= CSI2_TIMING_FORCE_RX_MODE_IO1;
+	else
+		reg &= ~CSI2_TIMING_FORCE_RX_MODE_IO1;
+
+	if (timing->stop_state_16x)
+		reg |= CSI2_TIMING_STOP_STATE_X16_IO1;
+	else
+		reg &= ~CSI2_TIMING_STOP_STATE_X16_IO1;
+
+	if (timing->stop_state_4x)
+		reg |= CSI2_TIMING_STOP_STATE_X4_IO1;
+	else
+		reg &= ~CSI2_TIMING_STOP_STATE_X4_IO1;
+
+	reg &= ~CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK;
+	reg |= timing->stop_state_counter <<
+	       CSI2_TIMING_STOP_STATE_COUNTER_IO1_SHIFT;
+
+	writel(reg, csi2->regs1 + CSI2_TIMING);
+}
+
+/*
+ * csi2_irq_ctx_set - Enables CSI2 Context IRQs.
+ * @enable: Enable/disable CSI2 Context interrupts
+ */
+static void csi2_irq_ctx_set(struct iss_csi2_device *csi2, int enable)
+{
+	u32 reg = CSI2_CTX_IRQ_FE;
+	int i;
+
+	if (csi2->use_fs_irq)
+		reg |= CSI2_CTX_IRQ_FS;
+
+	for (i = 0; i < 8; i++) {
+		writel(reg, csi2->regs1 + CSI2_CTX_IRQSTATUS(i));
+		if (enable)
+			writel(readl(csi2->regs1 + CSI2_CTX_IRQENABLE(i)) | reg,
+				csi2->regs1 + CSI2_CTX_IRQENABLE(i));
+		else
+			writel(readl(csi2->regs1 + CSI2_CTX_IRQENABLE(i)) &
+				~reg,
+				csi2->regs1 + CSI2_CTX_IRQENABLE(i));
+	}
+}
+
+/*
+ * csi2_irq_complexio1_set - Enables CSI2 ComplexIO IRQs.
+ * @enable: Enable/disable CSI2 ComplexIO #1 interrupts
+ */
+static void csi2_irq_complexio1_set(struct iss_csi2_device *csi2, int enable)
+{
+	u32 reg;
+	reg = CSI2_COMPLEXIO_IRQ_STATEALLULPMEXIT |
+		CSI2_COMPLEXIO_IRQ_STATEALLULPMENTER |
+		CSI2_COMPLEXIO_IRQ_STATEULPM5 |
+		CSI2_COMPLEXIO_IRQ_ERRCONTROL5 |
+		CSI2_COMPLEXIO_IRQ_ERRESC5 |
+		CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS5 |
+		CSI2_COMPLEXIO_IRQ_ERRSOTHS5 |
+		CSI2_COMPLEXIO_IRQ_STATEULPM4 |
+		CSI2_COMPLEXIO_IRQ_ERRCONTROL4 |
+		CSI2_COMPLEXIO_IRQ_ERRESC4 |
+		CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS4 |
+		CSI2_COMPLEXIO_IRQ_ERRSOTHS4 |
+		CSI2_COMPLEXIO_IRQ_STATEULPM3 |
+		CSI2_COMPLEXIO_IRQ_ERRCONTROL3 |
+		CSI2_COMPLEXIO_IRQ_ERRESC3 |
+		CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS3 |
+		CSI2_COMPLEXIO_IRQ_ERRSOTHS3 |
+		CSI2_COMPLEXIO_IRQ_STATEULPM2 |
+		CSI2_COMPLEXIO_IRQ_ERRCONTROL2 |
+		CSI2_COMPLEXIO_IRQ_ERRESC2 |
+		CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS2 |
+		CSI2_COMPLEXIO_IRQ_ERRSOTHS2 |
+		CSI2_COMPLEXIO_IRQ_STATEULPM1 |
+		CSI2_COMPLEXIO_IRQ_ERRCONTROL1 |
+		CSI2_COMPLEXIO_IRQ_ERRESC1 |
+		CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS1 |
+		CSI2_COMPLEXIO_IRQ_ERRSOTHS1;
+	writel(reg, csi2->regs1 + CSI2_COMPLEXIO_IRQSTATUS);
+	if (enable)
+		reg |= readl(csi2->regs1 + CSI2_COMPLEXIO_IRQENABLE);
+	else
+		reg = 0;
+	writel(reg, csi2->regs1 + CSI2_COMPLEXIO_IRQENABLE);
+}
+
+/*
+ * csi2_irq_status_set - Enables CSI2 Status IRQs.
+ * @enable: Enable/disable CSI2 Status interrupts
+ */
+static void csi2_irq_status_set(struct iss_csi2_device *csi2, int enable)
+{
+	u32 reg;
+	reg = CSI2_IRQ_OCP_ERR |
+		CSI2_IRQ_SHORT_PACKET |
+		CSI2_IRQ_ECC_CORRECTION |
+		CSI2_IRQ_ECC_NO_CORRECTION |
+		CSI2_IRQ_COMPLEXIO_ERR |
+		CSI2_IRQ_FIFO_OVF |
+		CSI2_IRQ_CONTEXT0;
+	writel(reg, csi2->regs1 + CSI2_IRQSTATUS);
+	if (enable)
+		reg |= readl(csi2->regs1 + CSI2_IRQENABLE);
+	else
+		reg = 0;
+
+	writel(reg, csi2->regs1 + CSI2_IRQENABLE);
+}
+
+/*
+ * omap4iss_csi2_reset - Resets the CSI2 module.
+ *
+ * Must be called with the phy lock held.
+ *
+ * Returns 0 if successful, or -EBUSY if power command didn't respond.
+ */
+int omap4iss_csi2_reset(struct iss_csi2_device *csi2)
+{
+	u8 soft_reset_retries = 0;
+	u32 reg;
+	int i;
+
+	if (!csi2->available)
+		return -ENODEV;
+
+	if (csi2->phy->phy_in_use)
+		return -EBUSY;
+
+	writel(readl(csi2->regs1 + CSI2_SYSCONFIG) |
+		CSI2_SYSCONFIG_SOFT_RESET,
+		csi2->regs1 + CSI2_SYSCONFIG);
+
+	do {
+		reg = readl(csi2->regs1 + CSI2_SYSSTATUS) &
+				    CSI2_SYSSTATUS_RESET_DONE;
+		if (reg == CSI2_SYSSTATUS_RESET_DONE)
+			break;
+		soft_reset_retries++;
+		if (soft_reset_retries < 5)
+			udelay(100);
+	} while (soft_reset_retries < 5);
+
+	if (soft_reset_retries == 5) {
+		printk(KERN_ERR "CSI2: Soft reset try count exceeded!\n");
+		return -EBUSY;
+	}
+
+	writel(readl(csi2->regs1 + CSI2_COMPLEXIO_CFG) |
+		CSI2_COMPLEXIO_CFG_RESET_CTRL,
+		csi2->regs1 + CSI2_COMPLEXIO_CFG);
+
+	i = 100;
+	do {
+		reg = readl(csi2->phy->phy_regs + REGISTER1)
+		    & REGISTER1_RESET_DONE_CTRLCLK;
+		if (reg == REGISTER1_RESET_DONE_CTRLCLK)
+			break;
+		udelay(100);
+	} while (--i > 0);
+
+	if (i == 0) {
+		printk(KERN_ERR
+		       "CSI2: Reset for CSI2_96M_FCLK domain Failed!\n");
+		return -EBUSY;
+	}
+
+	writel((readl(csi2->regs1 + CSI2_SYSCONFIG) &
+		~(CSI2_SYSCONFIG_MSTANDBY_MODE_MASK |
+		  CSI2_SYSCONFIG_AUTO_IDLE)) |
+		CSI2_SYSCONFIG_MSTANDBY_MODE_NO,
+		csi2->regs1 + CSI2_SYSCONFIG);
+
+	return 0;
+}
+
+static int csi2_configure(struct iss_csi2_device *csi2)
+{
+	const struct iss_v4l2_subdevs_group *pdata;
+	struct iss_csi2_timing_cfg *timing = &csi2->timing[0];
+	struct v4l2_subdev *sensor;
+	struct media_pad *pad;
+
+	/*
+	 * CSI2 fields that can be updated while the context has
+	 * been enabled or the interface has been enabled are not
+	 * updated dynamically currently. So we do not allow to
+	 * reconfigure if either has been enabled
+	 */
+	if (csi2->contexts[0].enabled || csi2->ctrl.if_enable)
+		return -EBUSY;
+
+	pad = media_entity_remote_source(&csi2->pads[CSI2_PAD_SINK]);
+	sensor = media_entity_to_v4l2_subdev(pad->entity);
+	pdata = sensor->host_priv;
+
+	csi2->frame_skip = 0;
+	v4l2_subdev_call(sensor, sensor, g_skip_frames, &csi2->frame_skip);
+
+	csi2->ctrl.vp_out_ctrl = pdata->bus.csi2.vpclk_div;
+	csi2->ctrl.frame_mode = ISS_CSI2_FRAME_IMMEDIATE;
+	csi2->ctrl.ecc_enable = pdata->bus.csi2.crc;
+
+	timing->force_rx_mode = 1;
+	timing->stop_state_16x = 1;
+	timing->stop_state_4x = 1;
+	timing->stop_state_counter = 0x1FF;
+
+	/*
+	 * The CSI2 receiver can't do any format conversion except DPCM
+	 * decompression, so every set_format call configures both pads
+	 * and enables DPCM decompression as a special case:
+	 */
+	if (csi2->formats[CSI2_PAD_SINK].code !=
+	    csi2->formats[CSI2_PAD_SOURCE].code)
+		csi2->dpcm_decompress = true;
+	else
+		csi2->dpcm_decompress = false;
+
+	csi2->contexts[0].format_id = csi2_ctx_map_format(csi2);
+
+	if (csi2->video_out.bpl_padding == 0)
+		csi2->contexts[0].data_offset = 0;
+	else
+		csi2->contexts[0].data_offset = csi2->video_out.bpl_value;
+
+	/*
+	 * Enable end of frame and end of line signals generation for
+	 * context 0. These signals are generated from CSI2 receiver to
+	 * qualify the last pixel of a frame and the last pixel of a line.
+	 * Without enabling the signals CSI2 receiver writes data to memory
+	 * beyond buffer size and/or data line offset is not handled correctly.
+	 */
+	csi2->contexts[0].eof_enabled = 1;
+	csi2->contexts[0].eol_enabled = 1;
+
+	csi2_irq_complexio1_set(csi2, 1);
+	csi2_irq_ctx_set(csi2, 1);
+	csi2_irq_status_set(csi2, 1);
+
+	/* Set configuration (timings, format and links) */
+	csi2_timing_config(csi2, timing);
+	csi2_recv_config(csi2, &csi2->ctrl);
+	csi2_ctx_config(csi2, &csi2->contexts[0]);
+
+	return 0;
+}
+
+/*
+ * csi2_print_status - Prints CSI2 debug information.
+ */
+#define CSI2_PRINT_REGISTER(iss, regs, name)\
+	dev_dbg(iss->dev, "###CSI2 " #name "=0x%08x\n", \
+		readl(regs + CSI2_##name))
+
+static void csi2_print_status(struct iss_csi2_device *csi2)
+{
+	struct iss_device *iss = csi2->iss;
+
+	if (!csi2->available)
+		return;
+
+	dev_dbg(iss->dev, "-------------CSI2 Register dump-------------\n");
+
+	CSI2_PRINT_REGISTER(iss, csi2->regs1, SYSCONFIG);
+	CSI2_PRINT_REGISTER(iss, csi2->regs1, SYSSTATUS);
+	CSI2_PRINT_REGISTER(iss, csi2->regs1, IRQENABLE);
+	CSI2_PRINT_REGISTER(iss, csi2->regs1, IRQSTATUS);
+	CSI2_PRINT_REGISTER(iss, csi2->regs1, CTRL);
+	CSI2_PRINT_REGISTER(iss, csi2->regs1, DBG_H);
+	CSI2_PRINT_REGISTER(iss, csi2->regs1, COMPLEXIO_CFG);
+	CSI2_PRINT_REGISTER(iss, csi2->regs1, COMPLEXIO_IRQSTATUS);
+	CSI2_PRINT_REGISTER(iss, csi2->regs1, SHORT_PACKET);
+	CSI2_PRINT_REGISTER(iss, csi2->regs1, COMPLEXIO_IRQENABLE);
+	CSI2_PRINT_REGISTER(iss, csi2->regs1, DBG_P);
+	CSI2_PRINT_REGISTER(iss, csi2->regs1, TIMING);
+	CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_CTRL1(0));
+	CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_CTRL2(0));
+	CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_DAT_OFST(0));
+	CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_PING_ADDR(0));
+	CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_PONG_ADDR(0));
+	CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_IRQENABLE(0));
+	CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_IRQSTATUS(0));
+	CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_CTRL3(0));
+
+	dev_dbg(iss->dev, "--------------------------------------------\n");
+}
+
+/* -----------------------------------------------------------------------------
+ * Interrupt handling
+ */
+
+/*
+ * csi2_isr_buffer - Does buffer handling at end-of-frame
+ * when writing to memory.
+ */
+static void csi2_isr_buffer(struct iss_csi2_device *csi2)
+{
+	struct iss_buffer *buffer;
+
+	csi2_ctx_enable(csi2, 0, 0);
+
+	buffer = omap4iss_video_buffer_next(&csi2->video_out, 0);
+
+	/*
+	 * Let video queue operation restart engine if there is an underrun
+	 * condition.
+	 */
+	if (buffer == NULL)
+		return;
+
+	csi2_set_outaddr(csi2, buffer->iss_addr);
+	csi2_ctx_enable(csi2, 0, 1);
+}
+
+static void csi2_isr_ctx(struct iss_csi2_device *csi2,
+			 struct iss_csi2_ctx_cfg *ctx)
+{
+	unsigned int n = ctx->ctxnum;
+	u32 status;
+
+	status = readl(csi2->regs1 + CSI2_CTX_IRQSTATUS(n));
+	writel(status, csi2->regs1 + CSI2_CTX_IRQSTATUS(n));
+
+	/* Propagate frame number */
+	if (status & CSI2_CTX_IRQ_FS) {
+		struct iss_pipeline *pipe =
+				     to_iss_pipeline(&csi2->subdev.entity);
+		if (pipe->do_propagation)
+			atomic_inc(&pipe->frame_number);
+	}
+
+	if (!(status & CSI2_CTX_IRQ_FE))
+		return;
+
+	/* Skip interrupts until we reach the frame skip count. The CSI2 will be
+	 * automatically disabled, as the frame skip count has been programmed
+	 * in the CSI2_CTx_CTRL1::COUNT field, so reenable it.
+	 *
+	 * It would have been nice to rely on the FRAME_NUMBER interrupt instead
+	 * but it turned out that the interrupt is only generated when the CSI2
+	 * writes to memory (the CSI2_CTx_CTRL1::COUNT field is decreased
+	 * correctly and reaches 0 when data is forwarded to the video port only
+	 * but no interrupt arrives). Maybe a CSI2 hardware bug.
+	 */
+	if (csi2->frame_skip) {
+		csi2->frame_skip--;
+		if (csi2->frame_skip == 0) {
+			ctx->format_id = csi2_ctx_map_format(csi2);
+			csi2_ctx_config(csi2, ctx);
+			csi2_ctx_enable(csi2, n, 1);
+		}
+		return;
+	}
+
+	if (csi2->output & CSI2_OUTPUT_MEMORY)
+		csi2_isr_buffer(csi2);
+}
+
+/*
+ * omap4iss_csi2_isr - CSI2 interrupt handling.
+ *
+ * Return -EIO on Transmission error
+ */
+int omap4iss_csi2_isr(struct iss_csi2_device *csi2)
+{
+	u32 csi2_irqstatus, cpxio1_irqstatus;
+	struct iss_device *iss = csi2->iss;
+	int retval = 0;
+
+	if (!csi2->available)
+		return -ENODEV;
+
+	csi2_irqstatus = readl(csi2->regs1 + CSI2_IRQSTATUS);
+	writel(csi2_irqstatus, csi2->regs1 + CSI2_IRQSTATUS);
+
+	/* Failure Cases */
+	if (csi2_irqstatus & CSI2_IRQ_COMPLEXIO_ERR) {
+		cpxio1_irqstatus = readl(csi2->regs1 +
+					 CSI2_COMPLEXIO_IRQSTATUS);
+		writel(cpxio1_irqstatus,
+			csi2->regs1 + CSI2_COMPLEXIO_IRQSTATUS);
+		dev_dbg(iss->dev, "CSI2: ComplexIO Error IRQ "
+			"%x\n", cpxio1_irqstatus);
+		retval = -EIO;
+	}
+
+	if (csi2_irqstatus & (CSI2_IRQ_OCP_ERR |
+			      CSI2_IRQ_SHORT_PACKET |
+			      CSI2_IRQ_ECC_NO_CORRECTION |
+			      CSI2_IRQ_COMPLEXIO_ERR |
+			      CSI2_IRQ_FIFO_OVF)) {
+		dev_dbg(iss->dev, "CSI2 Err:"
+			" OCP:%d,"
+			" Short_pack:%d,"
+			" ECC:%d,"
+			" CPXIO:%d,"
+			" FIFO_OVF:%d,"
+			"\n",
+			(csi2_irqstatus &
+			 CSI2_IRQ_OCP_ERR) ? 1 : 0,
+			(csi2_irqstatus &
+			 CSI2_IRQ_SHORT_PACKET) ? 1 : 0,
+			(csi2_irqstatus &
+			 CSI2_IRQ_ECC_NO_CORRECTION) ? 1 : 0,
+			(csi2_irqstatus &
+			 CSI2_IRQ_COMPLEXIO_ERR) ? 1 : 0,
+			(csi2_irqstatus &
+			 CSI2_IRQ_FIFO_OVF) ? 1 : 0);
+		retval = -EIO;
+	}
+
+	if (omap4iss_module_sync_is_stopping(&csi2->wait, &csi2->stopping))
+		return 0;
+
+	/* Successful cases */
+	if (csi2_irqstatus & CSI2_IRQ_CONTEXT0)
+		csi2_isr_ctx(csi2, &csi2->contexts[0]);
+
+	if (csi2_irqstatus & CSI2_IRQ_ECC_CORRECTION)
+		dev_dbg(iss->dev, "CSI2: ECC correction done\n");
+
+	return retval;
+}
+
+/* -----------------------------------------------------------------------------
+ * ISS video operations
+ */
+
+/*
+ * csi2_queue - Queues the first buffer when using memory output
+ * @video: The video node
+ * @buffer: buffer to queue
+ */
+static int csi2_queue(struct iss_video *video, struct iss_buffer *buffer)
+{
+	struct iss_device *iss = video->iss;
+	struct iss_csi2_device *csi2 = &iss->csi2a;
+
+	csi2_set_outaddr(csi2, buffer->iss_addr);
+
+	/*
+	 * If streaming was enabled before there was a buffer queued
+	 * or underrun happened in the ISR, the hardware was not enabled
+	 * and DMA queue flag ISS_VIDEO_DMAQUEUE_UNDERRUN is still set.
+	 * Enable it now.
+	 */
+	if (csi2->video_out.dmaqueue_flags & ISS_VIDEO_DMAQUEUE_UNDERRUN) {
+		/* Enable / disable context 0 and IRQs */
+		csi2_if_enable(csi2, 1);
+		csi2_ctx_enable(csi2, 0, 1);
+		iss_video_dmaqueue_flags_clr(&csi2->video_out);
+	}
+
+	return 0;
+}
+
+static const struct iss_video_operations csi2_issvideo_ops = {
+	.queue = csi2_queue,
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev operations
+ */
+
+static struct v4l2_mbus_framefmt *
+__csi2_get_format(struct iss_csi2_device *csi2, struct v4l2_subdev_fh *fh,
+		  unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+	if (which == V4L2_SUBDEV_FORMAT_TRY)
+		return v4l2_subdev_get_try_format(fh, pad);
+	else
+		return &csi2->formats[pad];
+}
+
+static void
+csi2_try_format(struct iss_csi2_device *csi2, struct v4l2_subdev_fh *fh,
+		unsigned int pad, struct v4l2_mbus_framefmt *fmt,
+		enum v4l2_subdev_format_whence which)
+{
+	enum v4l2_mbus_pixelcode pixelcode;
+	struct v4l2_mbus_framefmt *format;
+	const struct iss_format_info *info;
+	unsigned int i;
+
+	switch (pad) {
+	case CSI2_PAD_SINK:
+		/* Clamp the width and height to valid range (1-8191). */
+		for (i = 0; i < ARRAY_SIZE(csi2_input_fmts); i++) {
+			if (fmt->code == csi2_input_fmts[i])
+				break;
+		}
+
+		/* If not found, use SGRBG10 as default */
+		if (i >= ARRAY_SIZE(csi2_input_fmts))
+			fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10;
+
+		fmt->width = clamp_t(u32, fmt->width, 1, 8191);
+		fmt->height = clamp_t(u32, fmt->height, 1, 8191);
+		break;
+
+	case CSI2_PAD_SOURCE:
+		/* Source format same as sink format, except for DPCM
+		 * compression.
+		 */
+		pixelcode = fmt->code;
+		format = __csi2_get_format(csi2, fh, CSI2_PAD_SINK, which);
+		memcpy(fmt, format, sizeof(*fmt));
+
+		/*
+		 * Only Allow DPCM decompression, and check that the
+		 * pattern is preserved
+		 */
+		info = omap4iss_video_format_info(fmt->code);
+		if (info->uncompressed == pixelcode)
+			fmt->code = pixelcode;
+		break;
+	}
+
+	/* RGB, non-interlaced */
+	fmt->colorspace = V4L2_COLORSPACE_SRGB;
+	fmt->field = V4L2_FIELD_NONE;
+}
+
+/*
+ * csi2_enum_mbus_code - Handle pixel format enumeration
+ * @sd     : pointer to v4l2 subdev structure
+ * @fh     : V4L2 subdev file handle
+ * @code   : pointer to v4l2_subdev_mbus_code_enum structure
+ * return -EINVAL or zero on success
+ */
+static int csi2_enum_mbus_code(struct v4l2_subdev *sd,
+			       struct v4l2_subdev_fh *fh,
+			       struct v4l2_subdev_mbus_code_enum *code)
+{
+	struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
+	struct v4l2_mbus_framefmt *format;
+	const struct iss_format_info *info;
+
+	if (code->pad == CSI2_PAD_SINK) {
+		if (code->index >= ARRAY_SIZE(csi2_input_fmts))
+			return -EINVAL;
+
+		code->code = csi2_input_fmts[code->index];
+	} else {
+		format = __csi2_get_format(csi2, fh, CSI2_PAD_SINK,
+					   V4L2_SUBDEV_FORMAT_TRY);
+		switch (code->index) {
+		case 0:
+			/* Passthrough sink pad code */
+			code->code = format->code;
+			break;
+		case 1:
+			/* Uncompressed code */
+			info = omap4iss_video_format_info(format->code);
+			if (info->uncompressed == format->code)
+				return -EINVAL;
+
+			code->code = info->uncompressed;
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int csi2_enum_frame_size(struct v4l2_subdev *sd,
+				struct v4l2_subdev_fh *fh,
+				struct v4l2_subdev_frame_size_enum *fse)
+{
+	struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
+	struct v4l2_mbus_framefmt format;
+
+	if (fse->index != 0)
+		return -EINVAL;
+
+	format.code = fse->code;
+	format.width = 1;
+	format.height = 1;
+	csi2_try_format(csi2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
+	fse->min_width = format.width;
+	fse->min_height = format.height;
+
+	if (format.code != fse->code)
+		return -EINVAL;
+
+	format.code = fse->code;
+	format.width = -1;
+	format.height = -1;
+	csi2_try_format(csi2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
+	fse->max_width = format.width;
+	fse->max_height = format.height;
+
+	return 0;
+}
+
+/*
+ * csi2_get_format - Handle get format by pads subdev method
+ * @sd : pointer to v4l2 subdev structure
+ * @fh : V4L2 subdev file handle
+ * @fmt: pointer to v4l2 subdev format structure
+ * return -EINVAL or zero on success
+ */
+static int csi2_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+			   struct v4l2_subdev_format *fmt)
+{
+	struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
+	struct v4l2_mbus_framefmt *format;
+
+	format = __csi2_get_format(csi2, fh, fmt->pad, fmt->which);
+	if (format == NULL)
+		return -EINVAL;
+
+	fmt->format = *format;
+	return 0;
+}
+
+/*
+ * csi2_set_format - Handle set format by pads subdev method
+ * @sd : pointer to v4l2 subdev structure
+ * @fh : V4L2 subdev file handle
+ * @fmt: pointer to v4l2 subdev format structure
+ * return -EINVAL or zero on success
+ */
+static int csi2_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+			   struct v4l2_subdev_format *fmt)
+{
+	struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
+	struct v4l2_mbus_framefmt *format;
+
+	format = __csi2_get_format(csi2, fh, fmt->pad, fmt->which);
+	if (format == NULL)
+		return -EINVAL;
+
+	csi2_try_format(csi2, fh, fmt->pad, &fmt->format, fmt->which);
+	*format = fmt->format;
+
+	/* Propagate the format from sink to source */
+	if (fmt->pad == CSI2_PAD_SINK) {
+		format = __csi2_get_format(csi2, fh, CSI2_PAD_SOURCE,
+					   fmt->which);
+		*format = fmt->format;
+		csi2_try_format(csi2, fh, CSI2_PAD_SOURCE, format, fmt->which);
+	}
+
+	return 0;
+}
+
+/*
+ * csi2_init_formats - Initialize formats on all pads
+ * @sd: ISS CSI2 V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ *
+ * Initialize all pad formats with default values. If fh is not NULL, try
+ * formats are initialized on the file handle. Otherwise active formats are
+ * initialized on the device.
+ */
+static int csi2_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	struct v4l2_subdev_format format;
+
+	memset(&format, 0, sizeof(format));
+	format.pad = CSI2_PAD_SINK;
+	format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
+	format.format.code = V4L2_MBUS_FMT_SGRBG10_1X10;
+	format.format.width = 4096;
+	format.format.height = 4096;
+	csi2_set_format(sd, fh, &format);
+
+	return 0;
+}
+
+/*
+ * csi2_set_stream - Enable/Disable streaming on the CSI2 module
+ * @sd: ISS CSI2 V4L2 subdevice
+ * @enable: ISS pipeline stream state
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+static int csi2_set_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
+	struct iss_device *iss = csi2->iss;
+	struct iss_pipeline *pipe = to_iss_pipeline(&csi2->subdev.entity);
+	struct iss_video *video_out = &csi2->video_out;
+
+	if (csi2->state == ISS_PIPELINE_STREAM_STOPPED) {
+		if (enable == ISS_PIPELINE_STREAM_STOPPED)
+			return 0;
+
+		omap4iss_subclk_enable(iss, OMAP4_ISS_SUBCLK_CSI2_A);
+	}
+
+	switch (enable) {
+	case ISS_PIPELINE_STREAM_CONTINUOUS:
+		if (omap4iss_csiphy_acquire(csi2->phy) < 0)
+			return -ENODEV;
+		csi2->use_fs_irq = pipe->do_propagation;
+		csi2_configure(csi2);
+		csi2_print_status(csi2);
+
+		/*
+		 * When outputting to memory with no buffer available, let the
+		 * buffer queue handler start the hardware. A DMA queue flag
+		 * ISS_VIDEO_DMAQUEUE_QUEUED will be set as soon as there is
+		 * a buffer available.
+		 */
+		if (csi2->output & CSI2_OUTPUT_MEMORY &&
+		    !(video_out->dmaqueue_flags & ISS_VIDEO_DMAQUEUE_QUEUED))
+			break;
+		/* Enable context 0 and IRQs */
+		atomic_set(&csi2->stopping, 0);
+		csi2_ctx_enable(csi2, 0, 1);
+		csi2_if_enable(csi2, 1);
+		iss_video_dmaqueue_flags_clr(video_out);
+		break;
+
+	case ISS_PIPELINE_STREAM_STOPPED:
+		if (csi2->state == ISS_PIPELINE_STREAM_STOPPED)
+			return 0;
+		if (omap4iss_module_sync_idle(&sd->entity, &csi2->wait,
+					      &csi2->stopping))
+			dev_dbg(iss->dev, "%s: module stop timeout.\n",
+				sd->name);
+		csi2_ctx_enable(csi2, 0, 0);
+		csi2_if_enable(csi2, 0);
+		csi2_irq_ctx_set(csi2, 0);
+		omap4iss_csiphy_release(csi2->phy);
+		omap4iss_subclk_disable(iss, OMAP4_ISS_SUBCLK_CSI2_A);
+		iss_video_dmaqueue_flags_clr(video_out);
+		break;
+	}
+
+	csi2->state = enable;
+	return 0;
+}
+
+/* subdev video operations */
+static const struct v4l2_subdev_video_ops csi2_video_ops = {
+	.s_stream = csi2_set_stream,
+};
+
+/* subdev pad operations */
+static const struct v4l2_subdev_pad_ops csi2_pad_ops = {
+	.enum_mbus_code = csi2_enum_mbus_code,
+	.enum_frame_size = csi2_enum_frame_size,
+	.get_fmt = csi2_get_format,
+	.set_fmt = csi2_set_format,
+};
+
+/* subdev operations */
+static const struct v4l2_subdev_ops csi2_ops = {
+	.video = &csi2_video_ops,
+	.pad = &csi2_pad_ops,
+};
+
+/* subdev internal operations */
+static const struct v4l2_subdev_internal_ops csi2_internal_ops = {
+	.open = csi2_init_formats,
+};
+
+/* -----------------------------------------------------------------------------
+ * Media entity operations
+ */
+
+/*
+ * csi2_link_setup - Setup CSI2 connections.
+ * @entity : Pointer to media entity structure
+ * @local  : Pointer to local pad array
+ * @remote : Pointer to remote pad array
+ * @flags  : Link flags
+ * return -EINVAL or zero on success
+ */
+static int csi2_link_setup(struct media_entity *entity,
+			   const struct media_pad *local,
+			   const struct media_pad *remote, u32 flags)
+{
+	struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+	struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
+	struct iss_csi2_ctrl_cfg *ctrl = &csi2->ctrl;
+
+	/*
+	 * The ISS core doesn't support pipelines with multiple video outputs.
+	 * Revisit this when it will be implemented, and return -EBUSY for now.
+	 */
+
+	switch (local->index | media_entity_type(remote->entity)) {
+	case CSI2_PAD_SOURCE | MEDIA_ENT_T_DEVNODE:
+		if (flags & MEDIA_LNK_FL_ENABLED) {
+			if (csi2->output & ~CSI2_OUTPUT_MEMORY)
+				return -EBUSY;
+			csi2->output |= CSI2_OUTPUT_MEMORY;
+		} else {
+			csi2->output &= ~CSI2_OUTPUT_MEMORY;
+		}
+		break;
+
+	case CSI2_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV:
+		if (flags & MEDIA_LNK_FL_ENABLED) {
+			if (csi2->output & ~CSI2_OUTPUT_IPIPEIF)
+				return -EBUSY;
+			csi2->output |= CSI2_OUTPUT_IPIPEIF;
+		} else {
+			csi2->output &= ~CSI2_OUTPUT_IPIPEIF;
+		}
+		break;
+
+	default:
+		/* Link from camera to CSI2 is fixed... */
+		return -EINVAL;
+	}
+
+	ctrl->vp_only_enable =
+		(csi2->output & CSI2_OUTPUT_MEMORY) ? false : true;
+	ctrl->vp_clk_enable = !!(csi2->output & CSI2_OUTPUT_IPIPEIF);
+
+	return 0;
+}
+
+/* media operations */
+static const struct media_entity_operations csi2_media_ops = {
+	.link_setup = csi2_link_setup,
+};
+
+/*
+ * csi2_init_entities - Initialize subdev and media entity.
+ * @csi2: Pointer to csi2 structure.
+ * return -ENOMEM or zero on success
+ */
+static int csi2_init_entities(struct iss_csi2_device *csi2)
+{
+	struct v4l2_subdev *sd = &csi2->subdev;
+	struct media_pad *pads = csi2->pads;
+	struct media_entity *me = &sd->entity;
+	int ret;
+
+	v4l2_subdev_init(sd, &csi2_ops);
+	sd->internal_ops = &csi2_internal_ops;
+	strlcpy(sd->name, "OMAP4 ISS CSI2a", sizeof(sd->name));
+
+	sd->grp_id = 1 << 16;	/* group ID for iss subdevs */
+	v4l2_set_subdevdata(sd, csi2);
+	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+	pads[CSI2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+	pads[CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+
+	me->ops = &csi2_media_ops;
+	ret = media_entity_init(me, CSI2_PADS_NUM, pads, 0);
+	if (ret < 0)
+		return ret;
+
+	csi2_init_formats(sd, NULL);
+
+	/* Video device node */
+	csi2->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	csi2->video_out.ops = &csi2_issvideo_ops;
+	csi2->video_out.bpl_alignment = 32;
+	csi2->video_out.bpl_zero_padding = 1;
+	csi2->video_out.bpl_max = 0x1ffe0;
+	csi2->video_out.iss = csi2->iss;
+	csi2->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 3;
+
+	ret = omap4iss_video_init(&csi2->video_out, "CSI2a");
+	if (ret < 0)
+		return ret;
+
+	/* Connect the CSI2 subdev to the video node. */
+	ret = media_entity_create_link(&csi2->subdev.entity, CSI2_PAD_SOURCE,
+				       &csi2->video_out.video.entity, 0, 0);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+void omap4iss_csi2_unregister_entities(struct iss_csi2_device *csi2)
+{
+	media_entity_cleanup(&csi2->subdev.entity);
+
+	v4l2_device_unregister_subdev(&csi2->subdev);
+	omap4iss_video_unregister(&csi2->video_out);
+}
+
+int omap4iss_csi2_register_entities(struct iss_csi2_device *csi2,
+				    struct v4l2_device *vdev)
+{
+	int ret;
+
+	/* Register the subdev and video nodes. */
+	ret = v4l2_device_register_subdev(vdev, &csi2->subdev);
+	if (ret < 0)
+		goto error;
+
+	ret = omap4iss_video_register(&csi2->video_out, vdev);
+	if (ret < 0)
+		goto error;
+
+	return 0;
+
+error:
+	omap4iss_csi2_unregister_entities(csi2);
+	return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * ISS CSI2 initialisation and cleanup
+ */
+
+/*
+ * omap4iss_csi2_cleanup - Routine for module driver cleanup
+ */
+void omap4iss_csi2_cleanup(struct iss_device *iss)
+{
+}
+
+/*
+ * omap4iss_csi2_init - Routine for module driver init
+ */
+int omap4iss_csi2_init(struct iss_device *iss)
+{
+	struct iss_csi2_device *csi2a = &iss->csi2a;
+	int ret;
+
+	csi2a->iss = iss;
+	csi2a->available = 1;
+	csi2a->regs1 = iss->regs[OMAP4_ISS_MEM_CSI2_A_REGS1];
+	csi2a->phy = &iss->csiphy1;
+	csi2a->state = ISS_PIPELINE_STREAM_STOPPED;
+	init_waitqueue_head(&csi2a->wait);
+
+	ret = csi2_init_entities(csi2a);
+	if (ret < 0)
+		goto fail;
+
+	return 0;
+fail:
+	omap4iss_csi2_cleanup(iss);
+	return ret;
+}
diff --git a/drivers/media/video/omap4iss/iss_csi2.h b/drivers/media/video/omap4iss/iss_csi2.h
new file mode 100644
index 0000000..4fa94cf
--- /dev/null
+++ b/drivers/media/video/omap4iss/iss_csi2.h
@@ -0,0 +1,166 @@
+/*
+ * iss_csi2.h
+ *
+ * TI OMAP4 ISS - CSI2 module
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ *
+ * Contacts: Sergio Aguirre <saaguirre@ti.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef OMAP4_ISS_CSI2_H
+#define OMAP4_ISS_CSI2_H
+
+#include <linux/types.h>
+#include <linux/videodev2.h>
+
+#include "iss_video.h"
+
+struct iss_csiphy;
+
+/* This is not an exhaustive list */
+enum iss_csi2_pix_formats {
+	CSI2_PIX_FMT_OTHERS = 0,
+	CSI2_PIX_FMT_YUV422_8BIT = 0x1e,
+	CSI2_PIX_FMT_YUV422_8BIT_VP = 0x9e,
+	CSI2_PIX_FMT_RAW10_EXP16 = 0xab,
+	CSI2_PIX_FMT_RAW10_EXP16_VP = 0x12f,
+	CSI2_PIX_FMT_RAW8 = 0x2a,
+	CSI2_PIX_FMT_RAW8_DPCM10_EXP16 = 0x2aa,
+	CSI2_PIX_FMT_RAW8_DPCM10_VP = 0x32a,
+	CSI2_PIX_FMT_RAW8_VP = 0x12a,
+	CSI2_USERDEF_8BIT_DATA1_DPCM10_VP = 0x340,
+	CSI2_USERDEF_8BIT_DATA1_DPCM10 = 0x2c0,
+	CSI2_USERDEF_8BIT_DATA1 = 0x40,
+};
+
+enum iss_csi2_irqevents {
+	OCP_ERR_IRQ = 0x4000,
+	SHORT_PACKET_IRQ = 0x2000,
+	ECC_CORRECTION_IRQ = 0x1000,
+	ECC_NO_CORRECTION_IRQ = 0x800,
+	COMPLEXIO2_ERR_IRQ = 0x400,
+	COMPLEXIO1_ERR_IRQ = 0x200,
+	FIFO_OVF_IRQ = 0x100,
+	CONTEXT7 = 0x80,
+	CONTEXT6 = 0x40,
+	CONTEXT5 = 0x20,
+	CONTEXT4 = 0x10,
+	CONTEXT3 = 0x8,
+	CONTEXT2 = 0x4,
+	CONTEXT1 = 0x2,
+	CONTEXT0 = 0x1,
+};
+
+enum iss_csi2_ctx_irqevents {
+	CTX_ECC_CORRECTION = 0x100,
+	CTX_LINE_NUMBER = 0x80,
+	CTX_FRAME_NUMBER = 0x40,
+	CTX_CS = 0x20,
+	CTX_LE = 0x8,
+	CTX_LS = 0x4,
+	CTX_FE = 0x2,
+	CTX_FS = 0x1,
+};
+
+enum iss_csi2_frame_mode {
+	ISS_CSI2_FRAME_IMMEDIATE,
+	ISS_CSI2_FRAME_AFTERFEC,
+};
+
+#define ISS_CSI2_MAX_CTX_NUM	7
+
+struct iss_csi2_ctx_cfg {
+	u8 ctxnum;		/* context number 0 - 7 */
+	u8 dpcm_decompress;
+
+	/* Fields in CSI2_CTx_CTRL2 - locked by CSI2_CTx_CTRL1.CTX_EN */
+	u8 virtual_id;
+	u16 format_id;		/* as in CSI2_CTx_CTRL2[9:0] */
+	u8 dpcm_predictor;	/* 1: simple, 0: advanced */
+
+	/* Fields in CSI2_CTx_CTRL1/3 - Shadowed */
+	u16 alpha;
+	u16 data_offset;
+	u32 ping_addr;
+	u32 pong_addr;
+	u8 eof_enabled;
+	u8 eol_enabled;
+	u8 checksum_enabled;
+	u8 enabled;
+};
+
+struct iss_csi2_timing_cfg {
+	u8 ionum;			/* IO1 or IO2 as in CSI2_TIMING */
+	unsigned force_rx_mode:1;
+	unsigned stop_state_16x:1;
+	unsigned stop_state_4x:1;
+	u16 stop_state_counter;
+};
+
+struct iss_csi2_ctrl_cfg {
+	bool vp_clk_enable;
+	bool vp_only_enable;
+	u8 vp_out_ctrl;
+	enum iss_csi2_frame_mode frame_mode;
+	bool ecc_enable;
+	bool if_enable;
+};
+
+#define CSI2_PAD_SINK		0
+#define CSI2_PAD_SOURCE		1
+#define CSI2_PADS_NUM		2
+
+#define CSI2_OUTPUT_IPIPEIF	(1 << 0)
+#define CSI2_OUTPUT_MEMORY	(1 << 1)
+
+struct iss_csi2_device {
+	struct v4l2_subdev subdev;
+	struct media_pad pads[CSI2_PADS_NUM];
+	struct v4l2_mbus_framefmt formats[CSI2_PADS_NUM];
+
+	struct iss_video video_out;
+	struct iss_device *iss;
+
+	u8 available;		/* Is the IP present on the silicon? */
+
+	/* Pointer to register remaps into kernel space */
+	void __iomem *regs1;
+	void __iomem *regs2;
+
+	u32 output; /* output to IPIPEIF, memory or both? */
+	bool dpcm_decompress;
+	unsigned int frame_skip;
+	bool use_fs_irq;
+
+	struct iss_csiphy *phy;
+	struct iss_csi2_ctx_cfg contexts[ISS_CSI2_MAX_CTX_NUM + 1];
+	struct iss_csi2_timing_cfg timing[2];
+	struct iss_csi2_ctrl_cfg ctrl;
+	enum iss_pipeline_stream_state state;
+	wait_queue_head_t wait;
+	atomic_t stopping;
+};
+
+int omap4iss_csi2_isr(struct iss_csi2_device *csi2);
+int omap4iss_csi2_reset(struct iss_csi2_device *csi2);
+int omap4iss_csi2_init(struct iss_device *iss);
+void omap4iss_csi2_cleanup(struct iss_device *iss);
+void omap4iss_csi2_unregister_entities(struct iss_csi2_device *csi2);
+int omap4iss_csi2_register_entities(struct iss_csi2_device *csi2,
+				    struct v4l2_device *vdev);
+#endif	/* OMAP4_ISS_CSI2_H */
diff --git a/drivers/media/video/omap4iss/iss_csiphy.c b/drivers/media/video/omap4iss/iss_csiphy.c
new file mode 100644
index 0000000..9545622
--- /dev/null
+++ b/drivers/media/video/omap4iss/iss_csiphy.c
@@ -0,0 +1,215 @@
+/*
+ * iss_csiphy.c
+ *
+ * TI OMAP4 ISS - CSI PHY module
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ *
+ * Contacts: Sergio Aguirre <saaguirre@ti.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+
+#include "iss.h"
+#include "iss_regs.h"
+#include "iss_csiphy.h"
+
+/*
+ * csiphy_lanes_config - Configuration of CSIPHY lanes.
+ *
+ * Updates HW configuration.
+ * Called with phy->mutex taken.
+ */
+static void csiphy_lanes_config(struct iss_csiphy *phy)
+{
+	unsigned int i;
+	u32 reg;
+
+	reg = readl(phy->cfg_regs + CSI2_COMPLEXIO_CFG);
+
+	for (i = 0; i < phy->num_data_lanes; i++) {
+		reg &= ~(CSI2_COMPLEXIO_CFG_DATA_POL(i + 1) |
+			 CSI2_COMPLEXIO_CFG_DATA_POSITION_MASK(i + 1));
+		reg |= (phy->lanes.data[i].pol ?
+			CSI2_COMPLEXIO_CFG_DATA_POL(i + 1) : 0);
+		reg |= (phy->lanes.data[i].pos <<
+			CSI2_COMPLEXIO_CFG_DATA_POSITION_SHIFT(i + 1));
+	}
+
+	reg &= ~(CSI2_COMPLEXIO_CFG_CLOCK_POL |
+		 CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK);
+	reg |= phy->lanes.clk.pol ? CSI2_COMPLEXIO_CFG_CLOCK_POL : 0;
+	reg |= phy->lanes.clk.pos << CSI2_COMPLEXIO_CFG_CLOCK_POSITION_SHIFT;
+
+	writel(reg, phy->cfg_regs + CSI2_COMPLEXIO_CFG);
+}
+
+/*
+ * csiphy_set_power
+ * @power: Power state to be set.
+ *
+ * Returns 0 if successful, or -EBUSY if the retry count is exceeded.
+ */
+static int csiphy_set_power(struct iss_csiphy *phy, u32 power)
+{
+	u32 reg;
+	u8 retry_count;
+
+	writel((readl(phy->cfg_regs + CSI2_COMPLEXIO_CFG) &
+		~CSI2_COMPLEXIO_CFG_PWD_CMD_MASK) |
+		power,
+		phy->cfg_regs + CSI2_COMPLEXIO_CFG);
+
+	retry_count = 0;
+	do {
+		udelay(50);
+		reg = readl(phy->cfg_regs + CSI2_COMPLEXIO_CFG) &
+				CSI2_COMPLEXIO_CFG_PWD_STATUS_MASK;
+
+		if (reg != power >> 2)
+			retry_count++;
+
+	} while ((reg != power >> 2) && (retry_count < 100));
+
+	if (retry_count == 100) {
+		printk(KERN_ERR "CSI2 CIO set power failed!\n");
+		return -EBUSY;
+	}
+
+	return 0;
+}
+
+/*
+ * csiphy_dphy_config - Configure CSI2 D-PHY parameters.
+ *
+ * Called with phy->mutex taken.
+ */
+static void csiphy_dphy_config(struct iss_csiphy *phy)
+{
+	u32 reg;
+
+	/* Set up REGISTER0 */
+	reg = readl(phy->phy_regs + REGISTER0);
+
+	reg &= ~(REGISTER0_THS_TERM_MASK |
+		 REGISTER0_THS_SETTLE_MASK);
+	reg |= phy->dphy.ths_term << REGISTER0_THS_TERM_SHIFT;
+	reg |= phy->dphy.ths_settle << REGISTER0_THS_SETTLE_SHIFT;
+
+	writel(reg, phy->phy_regs + REGISTER0);
+
+	/* Set up REGISTER1 */
+	reg = readl(phy->phy_regs + REGISTER1);
+
+	reg &= ~(REGISTER1_TCLK_TERM_MASK |
+		 REGISTER1_CTRLCLK_DIV_FACTOR_MASK |
+		 REGISTER1_TCLK_SETTLE_MASK);
+	reg |= phy->dphy.tclk_term << REGISTER1_TCLK_TERM_SHIFT;
+	reg |= phy->dphy.tclk_miss << REGISTER1_CTRLCLK_DIV_FACTOR_SHIFT;
+	reg |= phy->dphy.tclk_settle << REGISTER1_TCLK_SETTLE_SHIFT;
+
+	writel(reg, phy->phy_regs + REGISTER1);
+}
+
+static int csiphy_config(struct iss_csiphy *phy,
+			 struct iss_csiphy_dphy_cfg *dphy,
+			 struct iss_csiphy_lanes_cfg *lanes)
+{
+	unsigned int used_lanes = 0;
+	unsigned int i;
+
+	/* Clock and data lanes verification */
+	for (i = 0; i < phy->num_data_lanes; i++) {
+		if (lanes->data[i].pos == 0)
+			continue;
+
+		if (lanes->data[i].pol > 1 || lanes->data[i].pos > 5)
+			return -EINVAL;
+
+		if (used_lanes & (1 << lanes->data[i].pos))
+			return -EINVAL;
+
+		used_lanes |= 1 << lanes->data[i].pos;
+	}
+
+	if (lanes->clk.pol > 1 || lanes->clk.pos > 4)
+		return -EINVAL;
+
+	if (lanes->clk.pos == 0 || used_lanes & (1 << lanes->clk.pos))
+		return -EINVAL;
+
+	mutex_lock(&phy->mutex);
+	phy->dphy = *dphy;
+	phy->lanes = *lanes;
+	mutex_unlock(&phy->mutex);
+
+	return 0;
+}
+
+int omap4iss_csiphy_acquire(struct iss_csiphy *phy)
+{
+	int rval;
+
+	mutex_lock(&phy->mutex);
+
+	rval = omap4iss_csi2_reset(phy->csi2);
+	if (rval)
+		goto done;
+
+	csiphy_dphy_config(phy);
+	csiphy_lanes_config(phy);
+
+	rval = csiphy_set_power(phy, CSI2_COMPLEXIO_CFG_PWD_CMD_ON);
+	if (rval)
+		goto done;
+
+	phy->phy_in_use = 1;
+
+done:
+	mutex_unlock(&phy->mutex);
+	return rval;
+}
+
+void omap4iss_csiphy_release(struct iss_csiphy *phy)
+{
+	mutex_lock(&phy->mutex);
+	if (phy->phy_in_use) {
+		csiphy_set_power(phy, CSI2_COMPLEXIO_CFG_PWD_CMD_OFF);
+		phy->phy_in_use = 0;
+	}
+	mutex_unlock(&phy->mutex);
+}
+
+/*
+ * omap4iss_csiphy_init - Initialize the CSI PHY frontends
+ */
+int omap4iss_csiphy_init(struct iss_device *iss)
+{
+	struct iss_csiphy *phy1 = &iss->csiphy1;
+
+	iss->platform_cb.csiphy_config = csiphy_config;
+
+	phy1->iss = iss;
+	phy1->csi2 = &iss->csi2a;
+	phy1->num_data_lanes = ISS_CSIPHY1_NUM_DATA_LANES;
+	phy1->cfg_regs = iss->regs[OMAP4_ISS_MEM_CSI2_A_REGS1];
+	phy1->phy_regs = iss->regs[OMAP4_ISS_MEM_CAMERARX_CORE1];
+	mutex_init(&phy1->mutex);
+
+	return 0;
+}
diff --git a/drivers/media/video/omap4iss/iss_csiphy.h b/drivers/media/video/omap4iss/iss_csiphy.h
new file mode 100644
index 0000000..c513ba8
--- /dev/null
+++ b/drivers/media/video/omap4iss/iss_csiphy.h
@@ -0,0 +1,69 @@
+/*
+ * iss_csiphy.h
+ *
+ * TI OMAP4 ISS - CSI PHY module
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ *
+ * Contacts: Sergio Aguirre <saaguirre@ti.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef OMAP4_ISS_CSI_PHY_H
+#define OMAP4_ISS_CSI_PHY_H
+
+struct iss_csi2_device;
+
+struct csiphy_lane {
+	u8 pos;
+	u8 pol;
+};
+
+#define ISS_CSIPHY1_NUM_DATA_LANES	4
+
+struct iss_csiphy_lanes_cfg {
+	struct csiphy_lane data[ISS_CSIPHY1_NUM_DATA_LANES];
+	struct csiphy_lane clk;
+};
+
+struct iss_csiphy_dphy_cfg {
+	u8 ths_term;
+	u8 ths_settle;
+	u8 tclk_term;
+	unsigned tclk_miss:1;
+	u8 tclk_settle;
+};
+
+struct iss_csiphy {
+	struct iss_device *iss;
+	struct mutex mutex;	/* serialize csiphy configuration */
+	u8 phy_in_use;
+	struct iss_csi2_device *csi2;
+
+	/* Pointer to register remaps into kernel space */
+	void __iomem *cfg_regs;
+	void __iomem *phy_regs;
+
+	u8 num_data_lanes;	/* number of CSI2 Data Lanes supported */
+	struct iss_csiphy_lanes_cfg lanes;
+	struct iss_csiphy_dphy_cfg dphy;
+};
+
+int omap4iss_csiphy_acquire(struct iss_csiphy *phy);
+void omap4iss_csiphy_release(struct iss_csiphy *phy);
+int omap4iss_csiphy_init(struct iss_device *iss);
+
+#endif	/* OMAP4_ISS_CSI_PHY_H */
diff --git a/drivers/media/video/omap4iss/iss_regs.h b/drivers/media/video/omap4iss/iss_regs.h
new file mode 100644
index 0000000..0bd70ac
--- /dev/null
+++ b/drivers/media/video/omap4iss/iss_regs.h
@@ -0,0 +1,238 @@
+/*
+ * iss_regs.h
+ *
+ * Copyright (C) 2011 Texas Instruments.
+ *
+ * Author: Sergio Aguirre <saaguirre@ti.com>
+ */
+
+#ifndef _OMAP4_ISS_REGS_H_
+#define _OMAP4_ISS_REGS_H_
+
+/* ISS */
+#define ISS_HL_REVISION					(0x0)
+
+#define ISS_HL_SYSCONFIG				(0x10)
+#define ISS_HL_SYSCONFIG_IDLEMODE_SHIFT			2
+#define ISS_HL_SYSCONFIG_IDLEMODE_FORCEIDLE		0x0
+#define ISS_HL_SYSCONFIG_IDLEMODE_NOIDLE		0x1
+#define ISS_HL_SYSCONFIG_IDLEMODE_SMARTIDLE		0x2
+#define ISS_HL_SYSCONFIG_SOFTRESET			(1 << 0)
+
+#define ISS_HL_IRQSTATUS_5				(0x24 + (0x10 * 5))
+#define ISS_HL_IRQENABLE_5_SET				(0x28 + (0x10 * 5))
+#define ISS_HL_IRQENABLE_5_CLR				(0x2C + (0x10 * 5))
+
+#define ISS_HL_IRQ_BTE					(1 << 11)
+#define ISS_HL_IRQ_CBUFF				(1 << 10)
+#define ISS_HL_IRQ_CSIA					(1 << 4)
+
+#define ISS_CTRL					(0x80)
+
+#define ISS_CLKCTRL					(0x84)
+#define ISS_CLKCTRL_VPORT2_CLK				(1 << 30)
+#define ISS_CLKCTRL_VPORT1_CLK				(1 << 29)
+#define ISS_CLKCTRL_VPORT0_CLK				(1 << 28)
+#define ISS_CLKCTRL_CCP2				(1 << 4)
+#define ISS_CLKCTRL_CSI2_B				(1 << 3)
+#define ISS_CLKCTRL_CSI2_A				(1 << 2)
+#define ISS_CLKCTRL_ISP					(1 << 1)
+#define ISS_CLKCTRL_SIMCOP				(1 << 0)
+
+#define ISS_CLKSTAT					(0x88)
+#define ISS_CLKSTAT_VPORT2_CLK				(1 << 30)
+#define ISS_CLKSTAT_VPORT1_CLK				(1 << 29)
+#define ISS_CLKSTAT_VPORT0_CLK				(1 << 28)
+#define ISS_CLKSTAT_CCP2				(1 << 4)
+#define ISS_CLKSTAT_CSI2_B				(1 << 3)
+#define ISS_CLKSTAT_CSI2_A				(1 << 2)
+#define ISS_CLKSTAT_ISP					(1 << 1)
+#define ISS_CLKSTAT_SIMCOP				(1 << 0)
+
+#define ISS_PM_STATUS					(0x8C)
+#define ISS_PM_STATUS_CBUFF_PM_MASK			(3 << 12)
+#define ISS_PM_STATUS_BTE_PM_MASK			(3 << 10)
+#define ISS_PM_STATUS_SIMCOP_PM_MASK			(3 << 8)
+#define ISS_PM_STATUS_ISP_PM_MASK			(3 << 6)
+#define ISS_PM_STATUS_CCP2_PM_MASK			(3 << 4)
+#define ISS_PM_STATUS_CSI2_B_PM_MASK			(3 << 2)
+#define ISS_PM_STATUS_CSI2_A_PM_MASK			(3 << 0)
+
+#define REGISTER0					(0x0)
+#define REGISTER0_HSCLOCKCONFIG				(1 << 24)
+#define REGISTER0_THS_TERM_MASK				(0xFF << 8)
+#define REGISTER0_THS_TERM_SHIFT			8
+#define REGISTER0_THS_SETTLE_MASK			(0xFF << 0)
+#define REGISTER0_THS_SETTLE_SHIFT			0
+
+#define REGISTER1					(0x4)
+#define REGISTER1_RESET_DONE_CTRLCLK			(1 << 29)
+#define REGISTER1_CLOCK_MISS_DETECTOR_STATUS		(1 << 25)
+#define REGISTER1_TCLK_TERM_MASK			(0x3F << 18)
+#define REGISTER1_TCLK_TERM_SHIFT			18
+#define REGISTER1_DPHY_HS_SYNC_PATTERN_MASK		(0xFF << 10)
+#define REGISTER1_CTRLCLK_DIV_FACTOR_MASK		(0x3 << 8)
+#define REGISTER1_CTRLCLK_DIV_FACTOR_SHIFT		8
+#define REGISTER1_TCLK_SETTLE_MASK			(0xFF << 0)
+#define REGISTER1_TCLK_SETTLE_SHIFT			0
+
+#define REGISTER2					(0x8)
+
+#define CSI2_SYSCONFIG					(0x10)
+#define CSI2_SYSCONFIG_MSTANDBY_MODE_MASK		(3 << 12)
+#define CSI2_SYSCONFIG_MSTANDBY_MODE_FORCE		(0 << 12)
+#define CSI2_SYSCONFIG_MSTANDBY_MODE_NO			(1 << 12)
+#define CSI2_SYSCONFIG_MSTANDBY_MODE_SMART		(2 << 12)
+#define CSI2_SYSCONFIG_SOFT_RESET			(1 << 1)
+#define CSI2_SYSCONFIG_AUTO_IDLE			(1 << 0)
+
+#define CSI2_SYSSTATUS					(0x14)
+#define CSI2_SYSSTATUS_RESET_DONE			(1 << 0)
+
+#define CSI2_IRQSTATUS					(0x18)
+#define CSI2_IRQENABLE					(0x1C)
+
+/* Shared bits across CSI2_IRQENABLE and IRQSTATUS */
+
+#define CSI2_IRQ_OCP_ERR				(1 << 14)
+#define CSI2_IRQ_SHORT_PACKET				(1 << 13)
+#define CSI2_IRQ_ECC_CORRECTION				(1 << 12)
+#define CSI2_IRQ_ECC_NO_CORRECTION			(1 << 11)
+#define CSI2_IRQ_COMPLEXIO_ERR				(1 << 9)
+#define CSI2_IRQ_FIFO_OVF				(1 << 8)
+#define CSI2_IRQ_CONTEXT0				(1 << 0)
+
+#define CSI2_CTRL					(0x40)
+#define CSI2_CTRL_MFLAG_LEVH_MASK			(7 << 20)
+#define CSI2_CTRL_MFLAG_LEVH_SHIFT			20
+#define CSI2_CTRL_MFLAG_LEVL_MASK			(7 << 17)
+#define CSI2_CTRL_MFLAG_LEVL_SHIFT			17
+#define CSI2_CTRL_BURST_SIZE_EXPAND			(1 << 16)
+#define CSI2_CTRL_VP_CLK_EN				(1 << 15)
+#define CSI2_CTRL_NON_POSTED_WRITE			(1 << 13)
+#define CSI2_CTRL_VP_ONLY_EN				(1 << 11)
+#define CSI2_CTRL_VP_OUT_CTRL_MASK			(3 << 8)
+#define CSI2_CTRL_VP_OUT_CTRL_SHIFT			8
+#define CSI2_CTRL_DBG_EN				(1 << 7)
+#define CSI2_CTRL_BURST_SIZE_MASK			(3 << 5)
+#define CSI2_CTRL_ENDIANNESS				(1 << 4)
+#define CSI2_CTRL_FRAME					(1 << 3)
+#define CSI2_CTRL_ECC_EN				(1 << 2)
+#define CSI2_CTRL_IF_EN					(1 << 0)
+
+#define CSI2_DBG_H					(0x44)
+
+#define CSI2_COMPLEXIO_CFG				(0x50)
+#define CSI2_COMPLEXIO_CFG_RESET_CTRL			(1 << 30)
+#define CSI2_COMPLEXIO_CFG_RESET_DONE			(1 << 29)
+#define CSI2_COMPLEXIO_CFG_PWD_CMD_MASK			(3 << 27)
+#define CSI2_COMPLEXIO_CFG_PWD_CMD_OFF			(0 << 27)
+#define CSI2_COMPLEXIO_CFG_PWD_CMD_ON			(1 << 27)
+#define CSI2_COMPLEXIO_CFG_PWD_CMD_ULP			(2 << 27)
+#define CSI2_COMPLEXIO_CFG_PWD_STATUS_MASK		(3 << 25)
+#define CSI2_COMPLEXIO_CFG_PWD_STATUS_OFF		(0 << 25)
+#define CSI2_COMPLEXIO_CFG_PWD_STATUS_ON		(1 << 25)
+#define CSI2_COMPLEXIO_CFG_PWD_STATUS_ULP		(2 << 25)
+#define CSI2_COMPLEXIO_CFG_PWR_AUTO			(1 << 24)
+#define CSI2_COMPLEXIO_CFG_DATA_POL(i)			(1 << (((i) * 4) + 3))
+#define CSI2_COMPLEXIO_CFG_DATA_POSITION_MASK(i)	(7 << ((i) * 4))
+#define CSI2_COMPLEXIO_CFG_DATA_POSITION_SHIFT(i)	((i) * 4)
+#define CSI2_COMPLEXIO_CFG_CLOCK_POL			(1 << 3)
+#define CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK		(7 << 0)
+#define CSI2_COMPLEXIO_CFG_CLOCK_POSITION_SHIFT		0
+
+#define CSI2_COMPLEXIO_IRQSTATUS			(0x54)
+
+#define CSI2_SHORT_PACKET				(0x5C)
+
+#define CSI2_COMPLEXIO_IRQENABLE			(0x60)
+
+/* Shared bits across CSI2_COMPLEXIO_IRQENABLE and IRQSTATUS */
+#define CSI2_COMPLEXIO_IRQ_STATEALLULPMEXIT		(1 << 26)
+#define CSI2_COMPLEXIO_IRQ_STATEALLULPMENTER		(1 << 25)
+#define CSI2_COMPLEXIO_IRQ_STATEULPM5			(1 << 24)
+#define CSI2_COMPLEXIO_IRQ_STATEULPM4			(1 << 23)
+#define CSI2_COMPLEXIO_IRQ_STATEULPM3			(1 << 22)
+#define CSI2_COMPLEXIO_IRQ_STATEULPM2			(1 << 21)
+#define CSI2_COMPLEXIO_IRQ_STATEULPM1			(1 << 20)
+#define CSI2_COMPLEXIO_IRQ_ERRCONTROL5			(1 << 19)
+#define CSI2_COMPLEXIO_IRQ_ERRCONTROL4			(1 << 18)
+#define CSI2_COMPLEXIO_IRQ_ERRCONTROL3			(1 << 17)
+#define CSI2_COMPLEXIO_IRQ_ERRCONTROL2			(1 << 16)
+#define CSI2_COMPLEXIO_IRQ_ERRCONTROL1			(1 << 15)
+#define CSI2_COMPLEXIO_IRQ_ERRESC5			(1 << 14)
+#define CSI2_COMPLEXIO_IRQ_ERRESC4			(1 << 13)
+#define CSI2_COMPLEXIO_IRQ_ERRESC3			(1 << 12)
+#define CSI2_COMPLEXIO_IRQ_ERRESC2			(1 << 11)
+#define CSI2_COMPLEXIO_IRQ_ERRESC1			(1 << 10)
+#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS5		(1 << 9)
+#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS4		(1 << 8)
+#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS3		(1 << 7)
+#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS2		(1 << 6)
+#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS1		(1 << 5)
+#define CSI2_COMPLEXIO_IRQ_ERRSOTHS5			(1 << 4)
+#define CSI2_COMPLEXIO_IRQ_ERRSOTHS4			(1 << 3)
+#define CSI2_COMPLEXIO_IRQ_ERRSOTHS3			(1 << 2)
+#define CSI2_COMPLEXIO_IRQ_ERRSOTHS2			(1 << 1)
+#define CSI2_COMPLEXIO_IRQ_ERRSOTHS1			(1 << 0)
+
+#define CSI2_DBG_P					(0x68)
+
+#define CSI2_TIMING					(0x6C)
+#define CSI2_TIMING_FORCE_RX_MODE_IO1			(1 << 15)
+#define CSI2_TIMING_STOP_STATE_X16_IO1			(1 << 14)
+#define CSI2_TIMING_STOP_STATE_X4_IO1			(1 << 13)
+#define CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK		(0x1FFF << 0)
+#define CSI2_TIMING_STOP_STATE_COUNTER_IO1_SHIFT	0
+
+#define CSI2_CTX_CTRL1(i)				(0x70 + (0x20 * i))
+#define CSI2_CTX_CTRL1_GENERIC				(1 << 30)
+#define CSI2_CTX_CTRL1_TRANSCODE			(0xF << 24)
+#define CSI2_CTX_CTRL1_FEC_NUMBER_MASK			(0xFF << 16)
+#define CSI2_CTX_CTRL1_COUNT_MASK			(0xFF << 8)
+#define CSI2_CTX_CTRL1_COUNT_SHIFT			8
+#define CSI2_CTX_CTRL1_EOF_EN				(1 << 7)
+#define CSI2_CTX_CTRL1_EOL_EN				(1 << 6)
+#define CSI2_CTX_CTRL1_CS_EN				(1 << 5)
+#define CSI2_CTX_CTRL1_COUNT_UNLOCK			(1 << 4)
+#define CSI2_CTX_CTRL1_PING_PONG			(1 << 3)
+#define CSI2_CTX_CTRL1_CTX_EN				(1 << 0)
+
+#define CSI2_CTX_CTRL2(i)				(0x74 + (0x20 * i))
+#define CSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT		13
+#define CSI2_CTX_CTRL2_USER_DEF_MAP_MASK		\
+		(0x3 << CSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT)
+#define CSI2_CTX_CTRL2_VIRTUAL_ID_MASK			(3 << 11)
+#define CSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT			11
+#define CSI2_CTX_CTRL2_DPCM_PRED			(1 << 10)
+#define CSI2_CTX_CTRL2_FORMAT_MASK			(0x3FF << 0)
+#define CSI2_CTX_CTRL2_FORMAT_SHIFT			0
+
+#define CSI2_CTX_DAT_OFST(i)				(0x78 + (0x20 * i))
+#define CSI2_CTX_DAT_OFST_MASK				(0xFFF << 5)
+
+#define CSI2_CTX_PING_ADDR(i)				(0x7C + (0x20 * i))
+#define CSI2_CTX_PING_ADDR_MASK				0xFFFFFFE0
+
+#define CSI2_CTX_PONG_ADDR(i)				(0x80 + (0x20 * i))
+#define CSI2_CTX_PONG_ADDR_MASK				CSI2_CTX_PING_ADDR_MASK
+
+#define CSI2_CTX_IRQENABLE(i)				(0x84 + (0x20 * i))
+#define CSI2_CTX_IRQSTATUS(i)				(0x88 + (0x20 * i))
+
+#define CSI2_CTX_CTRL3(i)				(0x8C + (0x20 * i))
+#define CSI2_CTX_CTRL3_ALPHA_SHIFT			5
+#define CSI2_CTX_CTRL3_ALPHA_MASK			\
+		(0x3fff << CSI2_CTX_CTRL3_ALPHA_SHIFT)
+
+/* Shared bits across CSI2_CTX_IRQENABLE and IRQSTATUS */
+#define CSI2_CTX_IRQ_ECC_CORRECTION			(1 << 8)
+#define CSI2_CTX_IRQ_LINE_NUMBER			(1 << 7)
+#define CSI2_CTX_IRQ_FRAME_NUMBER			(1 << 6)
+#define CSI2_CTX_IRQ_CS					(1 << 5)
+#define CSI2_CTX_IRQ_LE					(1 << 3)
+#define CSI2_CTX_IRQ_LS					(1 << 2)
+#define CSI2_CTX_IRQ_FE					(1 << 1)
+#define CSI2_CTX_IRQ_FS					(1 << 0)
+
+#endif /* _OMAP4_CAMERA_REGS_H_ */
diff --git a/drivers/media/video/omap4iss/iss_video.c b/drivers/media/video/omap4iss/iss_video.c
new file mode 100644
index 0000000..3248711
--- /dev/null
+++ b/drivers/media/video/omap4iss/iss_video.c
@@ -0,0 +1,1192 @@
+/*
+ * iss_video.c
+ *
+ * TI OMAP4 ISS - Generic video node
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ *
+ * Contacts: Sergio Aguirre <saaguirre@ti.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <asm/cacheflush.h>
+#include <linux/clk.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/module.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+#include <plat/omap-pm.h>
+
+#include "iss_video.h"
+#include "iss.h"
+
+
+/* -----------------------------------------------------------------------------
+ * Helper functions
+ */
+
+static struct iss_format_info formats[] = {
+	{ V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8,
+	  V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8,
+	  V4L2_PIX_FMT_GREY, 8, },
+	{ V4L2_MBUS_FMT_Y10_1X10, V4L2_MBUS_FMT_Y10_1X10,
+	  V4L2_MBUS_FMT_Y10_1X10, V4L2_MBUS_FMT_Y8_1X8,
+	  V4L2_PIX_FMT_Y10, 10, },
+	{ V4L2_MBUS_FMT_Y12_1X12, V4L2_MBUS_FMT_Y10_1X10,
+	  V4L2_MBUS_FMT_Y12_1X12, V4L2_MBUS_FMT_Y8_1X8,
+	  V4L2_PIX_FMT_Y12, 12, },
+	{ V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_MBUS_FMT_SBGGR8_1X8,
+	  V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_MBUS_FMT_SBGGR8_1X8,
+	  V4L2_PIX_FMT_SBGGR8, 8, },
+	{ V4L2_MBUS_FMT_SGBRG8_1X8, V4L2_MBUS_FMT_SGBRG8_1X8,
+	  V4L2_MBUS_FMT_SGBRG8_1X8, V4L2_MBUS_FMT_SGBRG8_1X8,
+	  V4L2_PIX_FMT_SGBRG8, 8, },
+	{ V4L2_MBUS_FMT_SGRBG8_1X8, V4L2_MBUS_FMT_SGRBG8_1X8,
+	  V4L2_MBUS_FMT_SGRBG8_1X8, V4L2_MBUS_FMT_SGRBG8_1X8,
+	  V4L2_PIX_FMT_SGRBG8, 8, },
+	{ V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8,
+	  V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8,
+	  V4L2_PIX_FMT_SRGGB8, 8, },
+	{ V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8,
+	  V4L2_MBUS_FMT_SGRBG10_1X10, 0,
+	  V4L2_PIX_FMT_SGRBG10DPCM8, 8, },
+	{ V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR10_1X10,
+	  V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR8_1X8,
+	  V4L2_PIX_FMT_SBGGR10, 10, },
+	{ V4L2_MBUS_FMT_SGBRG10_1X10, V4L2_MBUS_FMT_SGBRG10_1X10,
+	  V4L2_MBUS_FMT_SGBRG10_1X10, V4L2_MBUS_FMT_SGBRG8_1X8,
+	  V4L2_PIX_FMT_SGBRG10, 10, },
+	{ V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_MBUS_FMT_SGRBG10_1X10,
+	  V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_MBUS_FMT_SGRBG8_1X8,
+	  V4L2_PIX_FMT_SGRBG10, 10, },
+	{ V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_MBUS_FMT_SRGGB10_1X10,
+	  V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_MBUS_FMT_SRGGB8_1X8,
+	  V4L2_PIX_FMT_SRGGB10, 10, },
+	{ V4L2_MBUS_FMT_SBGGR12_1X12, V4L2_MBUS_FMT_SBGGR10_1X10,
+	  V4L2_MBUS_FMT_SBGGR12_1X12, V4L2_MBUS_FMT_SBGGR8_1X8,
+	  V4L2_PIX_FMT_SBGGR12, 12, },
+	{ V4L2_MBUS_FMT_SGBRG12_1X12, V4L2_MBUS_FMT_SGBRG10_1X10,
+	  V4L2_MBUS_FMT_SGBRG12_1X12, V4L2_MBUS_FMT_SGBRG8_1X8,
+	  V4L2_PIX_FMT_SGBRG12, 12, },
+	{ V4L2_MBUS_FMT_SGRBG12_1X12, V4L2_MBUS_FMT_SGRBG10_1X10,
+	  V4L2_MBUS_FMT_SGRBG12_1X12, V4L2_MBUS_FMT_SGRBG8_1X8,
+	  V4L2_PIX_FMT_SGRBG12, 12, },
+	{ V4L2_MBUS_FMT_SRGGB12_1X12, V4L2_MBUS_FMT_SRGGB10_1X10,
+	  V4L2_MBUS_FMT_SRGGB12_1X12, V4L2_MBUS_FMT_SRGGB8_1X8,
+	  V4L2_PIX_FMT_SRGGB12, 12, },
+	{ V4L2_MBUS_FMT_UYVY8_1X16, V4L2_MBUS_FMT_UYVY8_1X16,
+	  V4L2_MBUS_FMT_UYVY8_1X16, 0,
+	  V4L2_PIX_FMT_UYVY, 16, },
+	{ V4L2_MBUS_FMT_YUYV8_1X16, V4L2_MBUS_FMT_YUYV8_1X16,
+	  V4L2_MBUS_FMT_YUYV8_1X16, 0,
+	  V4L2_PIX_FMT_YUYV, 16, },
+};
+
+const struct iss_format_info *
+omap4iss_video_format_info(enum v4l2_mbus_pixelcode code)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(formats); ++i) {
+		if (formats[i].code == code)
+			return &formats[i];
+	}
+
+	return NULL;
+}
+
+/*
+ * iss_video_mbus_to_pix - Convert v4l2_mbus_framefmt to v4l2_pix_format
+ * @video: ISS video instance
+ * @mbus: v4l2_mbus_framefmt format (input)
+ * @pix: v4l2_pix_format format (output)
+ *
+ * Fill the output pix structure with information from the input mbus format.
+ * The bytesperline and sizeimage fields are computed from the requested bytes
+ * per line value in the pix format and information from the video instance.
+ *
+ * Return the number of padding bytes at end of line.
+ */
+static unsigned int iss_video_mbus_to_pix(const struct iss_video *video,
+					  const struct v4l2_mbus_framefmt *mbus,
+					  struct v4l2_pix_format *pix)
+{
+	unsigned int bpl = pix->bytesperline;
+	unsigned int min_bpl;
+	unsigned int i;
+
+	memset(pix, 0, sizeof(*pix));
+	pix->width = mbus->width;
+	pix->height = mbus->height;
+
+	for (i = 0; i < ARRAY_SIZE(formats); ++i) {
+		if (formats[i].code == mbus->code)
+			break;
+	}
+
+	if (WARN_ON(i == ARRAY_SIZE(formats)))
+		return 0;
+
+	min_bpl = pix->width * ALIGN(formats[i].bpp, 8) / 8;
+
+	/* Clamp the requested bytes per line value. If the maximum bytes per
+	 * line value is zero, the module doesn't support user configurable line
+	 * sizes. Override the requested value with the minimum in that case.
+	 */
+	if (video->bpl_max)
+		bpl = clamp(bpl, min_bpl, video->bpl_max);
+	else
+		bpl = min_bpl;
+
+	if (!video->bpl_zero_padding || bpl != min_bpl)
+		bpl = ALIGN(bpl, video->bpl_alignment);
+
+	pix->pixelformat = formats[i].pixelformat;
+	pix->bytesperline = bpl;
+	pix->sizeimage = pix->bytesperline * pix->height;
+	pix->colorspace = mbus->colorspace;
+	pix->field = mbus->field;
+
+	return bpl - min_bpl;
+}
+
+static void iss_video_pix_to_mbus(const struct v4l2_pix_format *pix,
+				  struct v4l2_mbus_framefmt *mbus)
+{
+	unsigned int i;
+
+	memset(mbus, 0, sizeof(*mbus));
+	mbus->width = pix->width;
+	mbus->height = pix->height;
+
+	for (i = 0; i < ARRAY_SIZE(formats); ++i) {
+		if (formats[i].pixelformat == pix->pixelformat)
+			break;
+	}
+
+	if (WARN_ON(i == ARRAY_SIZE(formats)))
+		return;
+
+	mbus->code = formats[i].code;
+	mbus->colorspace = pix->colorspace;
+	mbus->field = pix->field;
+}
+
+static struct v4l2_subdev *
+iss_video_remote_subdev(struct iss_video *video, u32 *pad)
+{
+	struct media_pad *remote;
+
+	remote = media_entity_remote_source(&video->pad);
+
+	if (remote == NULL ||
+	    media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+		return NULL;
+
+	if (pad)
+		*pad = remote->index;
+
+	return media_entity_to_v4l2_subdev(remote->entity);
+}
+
+/* Return a pointer to the ISS video instance at the far end of the pipeline. */
+static struct iss_video *
+iss_video_far_end(struct iss_video *video)
+{
+	struct media_entity_graph graph;
+	struct media_entity *entity = &video->video.entity;
+	struct media_device *mdev = entity->parent;
+	struct iss_video *far_end = NULL;
+
+	mutex_lock(&mdev->graph_mutex);
+	media_entity_graph_walk_start(&graph, entity);
+
+	while ((entity = media_entity_graph_walk_next(&graph))) {
+		if (entity == &video->video.entity)
+			continue;
+
+		if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE)
+			continue;
+
+		far_end = to_iss_video(media_entity_to_video_device(entity));
+		if (far_end->type != video->type)
+			break;
+
+		far_end = NULL;
+	}
+
+	mutex_unlock(&mdev->graph_mutex);
+	return far_end;
+}
+
+/*
+ * Validate a pipeline by checking both ends of all links for format
+ * discrepancies.
+ *
+ * Compute the minimum time per frame value as the maximum of time per frame
+ * limits reported by every block in the pipeline.
+ *
+ * Return 0 if all formats match, or -EPIPE if at least one link is found with
+ * different formats on its two ends.
+ */
+static int iss_video_validate_pipeline(struct iss_pipeline *pipe)
+{
+	struct v4l2_subdev_format fmt_source;
+	struct v4l2_subdev_format fmt_sink;
+	struct media_pad *pad;
+	struct v4l2_subdev *subdev;
+	int ret;
+
+	subdev = iss_video_remote_subdev(pipe->output, NULL);
+	if (subdev == NULL)
+		return -EPIPE;
+
+	while (1) {
+		/* Retrieve the sink format */
+		pad = &subdev->entity.pads[0];
+		if (!(pad->flags & MEDIA_PAD_FL_SINK))
+			break;
+
+		fmt_sink.pad = pad->index;
+		fmt_sink.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+		ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt_sink);
+		if (ret < 0 && ret != -ENOIOCTLCMD)
+			return -EPIPE;
+
+		/* Retrieve the source format */
+		pad = media_entity_remote_source(pad);
+		if (pad == NULL ||
+		    media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+			break;
+
+		subdev = media_entity_to_v4l2_subdev(pad->entity);
+
+		fmt_source.pad = pad->index;
+		fmt_source.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+		ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt_source);
+		if (ret < 0 && ret != -ENOIOCTLCMD)
+			return -EPIPE;
+
+		/* Check if the two ends match */
+		if (fmt_source.format.width != fmt_sink.format.width ||
+		    fmt_source.format.height != fmt_sink.format.height)
+			return -EPIPE;
+
+		if (fmt_source.format.code != fmt_sink.format.code)
+			return -EPIPE;
+	}
+
+	return 0;
+}
+
+static int
+__iss_video_get_format(struct iss_video *video, struct v4l2_format *format)
+{
+	struct v4l2_subdev_format fmt;
+	struct v4l2_subdev *subdev;
+	u32 pad;
+	int ret;
+
+	subdev = iss_video_remote_subdev(video, &pad);
+	if (subdev == NULL)
+		return -EINVAL;
+
+	mutex_lock(&video->mutex);
+
+	fmt.pad = pad;
+	fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+	ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt);
+	if (ret == -ENOIOCTLCMD)
+		ret = -EINVAL;
+
+	mutex_unlock(&video->mutex);
+
+	if (ret)
+		return ret;
+
+	format->type = video->type;
+	return iss_video_mbus_to_pix(video, &fmt.format, &format->fmt.pix);
+}
+
+static int
+iss_video_check_format(struct iss_video *video, struct iss_video_fh *vfh)
+{
+	struct v4l2_format format;
+	int ret;
+
+	memcpy(&format, &vfh->format, sizeof(format));
+	ret = __iss_video_get_format(video, &format);
+	if (ret < 0)
+		return ret;
+
+	if (vfh->format.fmt.pix.pixelformat != format.fmt.pix.pixelformat ||
+	    vfh->format.fmt.pix.height != format.fmt.pix.height ||
+	    vfh->format.fmt.pix.width != format.fmt.pix.width ||
+	    vfh->format.fmt.pix.bytesperline != format.fmt.pix.bytesperline ||
+	    vfh->format.fmt.pix.sizeimage != format.fmt.pix.sizeimage)
+		return -EINVAL;
+
+	return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * Video queue operations
+ */
+
+static int iss_video_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+				 unsigned int *count, unsigned int *num_planes,
+				 unsigned int sizes[], void *alloc_ctxs[])
+{
+	struct iss_video_fh *vfh = vb2_get_drv_priv(vq);
+	struct iss_video *video = vfh->video;
+
+	/* Revisit multi-planar support for NV12 */
+	*num_planes = 1;
+
+	sizes[0] = vfh->format.fmt.pix.sizeimage;
+	if (sizes[0] == 0)
+		return -EINVAL;
+
+	alloc_ctxs[0] = video->alloc_ctx;
+
+	*count = min(*count, (unsigned int)(video->capture_mem / PAGE_ALIGN(sizes[0])));
+
+	return 0;
+}
+
+static void iss_video_buf_cleanup(struct vb2_buffer *vb)
+{
+	struct iss_buffer *buffer = container_of(vb, struct iss_buffer, vb);
+
+	if (buffer->iss_addr)
+		buffer->iss_addr = 0;
+}
+
+static int iss_video_buf_prepare(struct vb2_buffer *vb)
+{
+	struct iss_video_fh *vfh = vb2_get_drv_priv(vb->vb2_queue);
+	struct iss_buffer *buffer = container_of(vb, struct iss_buffer, vb);
+	struct iss_video *video = vfh->video;
+	unsigned long size = vfh->format.fmt.pix.sizeimage;
+	dma_addr_t addr;
+
+	if (vb2_plane_size(vb, 0) < size)
+		return -ENOBUFS;
+
+	addr = vb2_dma_contig_plane_dma_addr(vb, 0);
+	if (!IS_ALIGNED(addr, 32)) {
+		dev_dbg(video->iss->dev, "Buffer address must be "
+			"aligned to 32 bytes boundary.\n");
+		return -EINVAL;
+	}
+
+	vb2_set_plane_payload(vb, 0, size);
+	buffer->iss_addr = addr;
+	return 0;
+}
+
+static void iss_video_buf_queue(struct vb2_buffer *vb)
+{
+	struct iss_video_fh *vfh = vb2_get_drv_priv(vb->vb2_queue);
+	struct iss_video *video = vfh->video;
+	struct iss_buffer *buffer = container_of(vb, struct iss_buffer, vb);
+	struct iss_pipeline *pipe = to_iss_pipeline(&video->video.entity);
+	unsigned int empty;
+	unsigned long flags;
+
+	spin_lock_irqsave(&video->qlock, flags);
+	empty = list_empty(&video->dmaqueue);
+	list_add_tail(&buffer->list, &video->dmaqueue);
+	spin_unlock_irqrestore(&video->qlock, flags);
+
+	if (empty) {
+		enum iss_pipeline_state state;
+		unsigned int start;
+
+		if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			state = ISS_PIPELINE_QUEUE_OUTPUT;
+		else
+			state = ISS_PIPELINE_QUEUE_INPUT;
+
+		spin_lock_irqsave(&pipe->lock, flags);
+		pipe->state |= state;
+		video->ops->queue(video, buffer);
+		video->dmaqueue_flags |= ISS_VIDEO_DMAQUEUE_QUEUED;
+
+		start = iss_pipeline_ready(pipe);
+		if (start)
+			pipe->state |= ISS_PIPELINE_STREAM;
+		spin_unlock_irqrestore(&pipe->lock, flags);
+
+		if (start)
+			omap4iss_pipeline_set_stream(pipe,
+						ISS_PIPELINE_STREAM_SINGLESHOT);
+	}
+}
+
+static struct vb2_ops iss_video_vb2ops = {
+	.queue_setup	= iss_video_queue_setup,
+	.buf_prepare	= iss_video_buf_prepare,
+	.buf_queue	= iss_video_buf_queue,
+	.buf_cleanup	= iss_video_buf_cleanup,
+};
+
+/*
+ * omap4iss_video_buffer_next - Complete the current buffer and return the next
+ * @video: ISS video object
+ * @error: Whether an error occurred during capture
+ *
+ * Remove the current video buffer from the DMA queue and fill its timestamp,
+ * field count and state fields before waking up its completion handler.
+ *
+ * The buffer state is set to VIDEOBUF_DONE if no error occurred (@error is 0)
+ * or VIDEOBUF_ERROR otherwise (@error is non-zero).
+ *
+ * The DMA queue is expected to contain at least one buffer.
+ *
+ * Return a pointer to the next buffer in the DMA queue, or NULL if the queue is
+ * empty.
+ */
+struct iss_buffer *omap4iss_video_buffer_next(struct iss_video *video,
+					      unsigned int error)
+{
+	struct iss_pipeline *pipe = to_iss_pipeline(&video->video.entity);
+	enum iss_pipeline_state state;
+	struct iss_buffer *buf;
+	unsigned long flags;
+	struct timespec ts;
+
+	spin_lock_irqsave(&video->qlock, flags);
+	if (WARN_ON(list_empty(&video->dmaqueue))) {
+		spin_unlock_irqrestore(&video->qlock, flags);
+		return NULL;
+	}
+
+	buf = list_first_entry(&video->dmaqueue, struct iss_buffer,
+			       list);
+	list_del(&buf->list);
+	spin_unlock_irqrestore(&video->qlock, flags);
+
+	ktime_get_ts(&ts);
+	buf->vb.v4l2_buf.timestamp.tv_sec = ts.tv_sec;
+	buf->vb.v4l2_buf.timestamp.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
+
+	/* Do frame number propagation only if this is the output video node.
+	 * Frame number either comes from the CSI receivers or it gets
+	 * incremented here if H3A is not active.
+	 * Note: There is no guarantee that the output buffer will finish
+	 * first, so the input number might lag behind by 1 in some cases.
+	 */
+	if (video == pipe->output && !pipe->do_propagation)
+		buf->vb.v4l2_buf.sequence = atomic_inc_return(&pipe->frame_number);
+	else
+		buf->vb.v4l2_buf.sequence = atomic_read(&pipe->frame_number);
+
+	vb2_buffer_done(&buf->vb, error < 0 ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
+
+	spin_lock_irqsave(&video->qlock, flags);
+	if (list_empty(&video->dmaqueue)) {
+		spin_unlock_irqrestore(&video->qlock, flags);
+		if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			state = ISS_PIPELINE_QUEUE_OUTPUT
+			      | ISS_PIPELINE_STREAM;
+		else
+			state = ISS_PIPELINE_QUEUE_INPUT
+			      | ISS_PIPELINE_STREAM;
+
+		spin_lock_irqsave(&pipe->lock, flags);
+		pipe->state &= ~state;
+		if (video->pipe.stream_state == ISS_PIPELINE_STREAM_CONTINUOUS)
+			video->dmaqueue_flags |= ISS_VIDEO_DMAQUEUE_UNDERRUN;
+		spin_unlock_irqrestore(&pipe->lock, flags);
+		return NULL;
+	}
+
+	if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->input != NULL) {
+		spin_lock_irqsave(&pipe->lock, flags);
+		pipe->state &= ~ISS_PIPELINE_STREAM;
+		spin_unlock_irqrestore(&pipe->lock, flags);
+	}
+
+	buf = list_first_entry(&video->dmaqueue, struct iss_buffer,
+			       list);
+	spin_unlock_irqrestore(&video->qlock, flags);
+	buf->vb.state = VB2_BUF_STATE_ACTIVE;
+	return buf;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 ioctls
+ */
+
+static int
+iss_video_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
+{
+	struct iss_video *video = video_drvdata(file);
+
+	strlcpy(cap->driver, ISS_VIDEO_DRIVER_NAME, sizeof(cap->driver));
+	strlcpy(cap->card, video->video.name, sizeof(cap->card));
+	strlcpy(cap->bus_info, "media", sizeof(cap->bus_info));
+	cap->version = ISS_VIDEO_DRIVER_VERSION;
+
+	if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+	else
+		cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
+
+	return 0;
+}
+
+static int
+iss_video_get_format(struct file *file, void *fh, struct v4l2_format *format)
+{
+	struct iss_video_fh *vfh = to_iss_video_fh(fh);
+	struct iss_video *video = video_drvdata(file);
+
+	if (format->type != video->type)
+		return -EINVAL;
+
+	mutex_lock(&video->mutex);
+	*format = vfh->format;
+	mutex_unlock(&video->mutex);
+
+	return 0;
+}
+
+static int
+iss_video_set_format(struct file *file, void *fh, struct v4l2_format *format)
+{
+	struct iss_video_fh *vfh = to_iss_video_fh(fh);
+	struct iss_video *video = video_drvdata(file);
+	struct v4l2_mbus_framefmt fmt;
+
+	if (format->type != video->type)
+		return -EINVAL;
+
+	mutex_lock(&video->mutex);
+
+	/* Fill the bytesperline and sizeimage fields by converting to media bus
+	 * format and back to pixel format.
+	 */
+	iss_video_pix_to_mbus(&format->fmt.pix, &fmt);
+	iss_video_mbus_to_pix(video, &fmt, &format->fmt.pix);
+
+	vfh->format = *format;
+
+	mutex_unlock(&video->mutex);
+	return 0;
+}
+
+static int
+iss_video_try_format(struct file *file, void *fh, struct v4l2_format *format)
+{
+	struct iss_video *video = video_drvdata(file);
+	struct v4l2_subdev_format fmt;
+	struct v4l2_subdev *subdev;
+	u32 pad;
+	int ret;
+
+	if (format->type != video->type)
+		return -EINVAL;
+
+	subdev = iss_video_remote_subdev(video, &pad);
+	if (subdev == NULL)
+		return -EINVAL;
+
+	iss_video_pix_to_mbus(&format->fmt.pix, &fmt.format);
+
+	fmt.pad = pad;
+	fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+	ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt);
+	if (ret)
+		return ret == -ENOIOCTLCMD ? -EINVAL : ret;
+
+	iss_video_mbus_to_pix(video, &fmt.format, &format->fmt.pix);
+	return 0;
+}
+
+static int
+iss_video_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cropcap)
+{
+	struct iss_video *video = video_drvdata(file);
+	struct v4l2_subdev *subdev;
+	int ret;
+
+	subdev = iss_video_remote_subdev(video, NULL);
+	if (subdev == NULL)
+		return -EINVAL;
+
+	mutex_lock(&video->mutex);
+	ret = v4l2_subdev_call(subdev, video, cropcap, cropcap);
+	mutex_unlock(&video->mutex);
+
+	return ret == -ENOIOCTLCMD ? -EINVAL : ret;
+}
+
+static int
+iss_video_get_crop(struct file *file, void *fh, struct v4l2_crop *crop)
+{
+	struct iss_video *video = video_drvdata(file);
+	struct v4l2_subdev_format format;
+	struct v4l2_subdev *subdev;
+	u32 pad;
+	int ret;
+
+	subdev = iss_video_remote_subdev(video, &pad);
+	if (subdev == NULL)
+		return -EINVAL;
+
+	/* Try the get crop operation first and fallback to get format if not
+	 * implemented.
+	 */
+	ret = v4l2_subdev_call(subdev, video, g_crop, crop);
+	if (ret != -ENOIOCTLCMD)
+		return ret;
+
+	format.pad = pad;
+	format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+	ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &format);
+	if (ret < 0)
+		return ret == -ENOIOCTLCMD ? -EINVAL : ret;
+
+	crop->c.left = 0;
+	crop->c.top = 0;
+	crop->c.width = format.format.width;
+	crop->c.height = format.format.height;
+
+	return 0;
+}
+
+static int
+iss_video_set_crop(struct file *file, void *fh, struct v4l2_crop *crop)
+{
+	struct iss_video *video = video_drvdata(file);
+	struct v4l2_subdev *subdev;
+	int ret;
+
+	subdev = iss_video_remote_subdev(video, NULL);
+	if (subdev == NULL)
+		return -EINVAL;
+
+	mutex_lock(&video->mutex);
+	ret = v4l2_subdev_call(subdev, video, s_crop, crop);
+	mutex_unlock(&video->mutex);
+
+	return ret == -ENOIOCTLCMD ? -EINVAL : ret;
+}
+
+static int
+iss_video_get_param(struct file *file, void *fh, struct v4l2_streamparm *a)
+{
+	struct iss_video_fh *vfh = to_iss_video_fh(fh);
+	struct iss_video *video = video_drvdata(file);
+
+	if (video->type != V4L2_BUF_TYPE_VIDEO_OUTPUT ||
+	    video->type != a->type)
+		return -EINVAL;
+
+	memset(a, 0, sizeof(*a));
+	a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+	a->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
+	a->parm.output.timeperframe = vfh->timeperframe;
+
+	return 0;
+}
+
+static int
+iss_video_set_param(struct file *file, void *fh, struct v4l2_streamparm *a)
+{
+	struct iss_video_fh *vfh = to_iss_video_fh(fh);
+	struct iss_video *video = video_drvdata(file);
+
+	if (video->type != V4L2_BUF_TYPE_VIDEO_OUTPUT ||
+	    video->type != a->type)
+		return -EINVAL;
+
+	if (a->parm.output.timeperframe.denominator == 0)
+		a->parm.output.timeperframe.denominator = 1;
+
+	vfh->timeperframe = a->parm.output.timeperframe;
+
+	return 0;
+}
+
+static int
+iss_video_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *rb)
+{
+	struct iss_video_fh *vfh = to_iss_video_fh(fh);
+
+	return vb2_reqbufs(&vfh->queue, rb);
+}
+
+static int
+iss_video_querybuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+	struct iss_video_fh *vfh = to_iss_video_fh(fh);
+
+	return vb2_querybuf(&vfh->queue, b);
+}
+
+static int
+iss_video_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+	struct iss_video_fh *vfh = to_iss_video_fh(fh);
+
+	return vb2_qbuf(&vfh->queue, b);
+}
+
+static int
+iss_video_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+	struct iss_video_fh *vfh = to_iss_video_fh(fh);
+
+	return vb2_dqbuf(&vfh->queue, b, file->f_flags & O_NONBLOCK);
+}
+
+/*
+ * Stream management
+ *
+ * Every ISS pipeline has a single input and a single output. The input can be
+ * either a sensor or a video node. The output is always a video node.
+ *
+ * As every pipeline has an output video node, the ISS video objects at the
+ * pipeline output stores the pipeline state. It tracks the streaming state of
+ * both the input and output, as well as the availability of buffers.
+ *
+ * In sensor-to-memory mode, frames are always available at the pipeline input.
+ * Starting the sensor usually requires I2C transfers and must be done in
+ * interruptible context. The pipeline is started and stopped synchronously
+ * to the stream on/off commands. All modules in the pipeline will get their
+ * subdev set stream handler called. The module at the end of the pipeline must
+ * delay starting the hardware until buffers are available at its output.
+ *
+ * In memory-to-memory mode, starting/stopping the stream requires
+ * synchronization between the input and output. ISS modules can't be stopped
+ * in the middle of a frame, and at least some of the modules seem to become
+ * busy as soon as they're started, even if they don't receive a frame start
+ * event. For that reason frames need to be processed in single-shot mode. The
+ * driver needs to wait until a frame is completely processed and written to
+ * memory before restarting the pipeline for the next frame. Pipelined
+ * processing might be possible but requires more testing.
+ *
+ * Stream start must be delayed until buffers are available at both the input
+ * and output. The pipeline must be started in the videobuf queue callback with
+ * the buffers queue spinlock held. The modules subdev set stream operation must
+ * not sleep.
+ */
+static int
+iss_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
+{
+	struct iss_video_fh *vfh = to_iss_video_fh(fh);
+	struct iss_video *video = video_drvdata(file);
+	enum iss_pipeline_state state;
+	struct iss_pipeline *pipe;
+	struct iss_video *far_end;
+	unsigned long flags;
+	int ret;
+
+	if (type != video->type)
+		return -EINVAL;
+
+	mutex_lock(&video->stream_lock);
+
+	if (video->streaming) {
+		mutex_unlock(&video->stream_lock);
+		return -EBUSY;
+	}
+
+	/* Start streaming on the pipeline. No link touching an entity in the
+	 * pipeline can be activated or deactivated once streaming is started.
+	 */
+	pipe = video->video.entity.pipe
+	     ? to_iss_pipeline(&video->video.entity) : &video->pipe;
+	media_entity_pipeline_start(&video->video.entity, &pipe->pipe);
+
+	/* Verify that the currently configured format matches the output of
+	 * the connected subdev.
+	 */
+	ret = iss_video_check_format(video, vfh);
+	if (ret < 0)
+		goto error;
+
+	video->bpl_padding = ret;
+	video->bpl_value = vfh->format.fmt.pix.bytesperline;
+
+	/* Find the ISS video node connected at the far end of the pipeline and
+	 * update the pipeline.
+	 */
+	far_end = iss_video_far_end(video);
+
+	if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+		state = ISS_PIPELINE_STREAM_OUTPUT | ISS_PIPELINE_IDLE_OUTPUT;
+		pipe->input = far_end;
+		pipe->output = video;
+	} else {
+		if (far_end == NULL) {
+			ret = -EPIPE;
+			goto error;
+		}
+
+		state = ISS_PIPELINE_STREAM_INPUT | ISS_PIPELINE_IDLE_INPUT;
+		pipe->input = video;
+		pipe->output = far_end;
+	}
+
+	if (video->iss->pdata->set_constraints)
+		video->iss->pdata->set_constraints(video->iss, true);
+
+	/* Validate the pipeline and update its state. */
+	ret = iss_video_validate_pipeline(pipe);
+	if (ret < 0)
+		goto error;
+
+	spin_lock_irqsave(&pipe->lock, flags);
+	pipe->state &= ~ISS_PIPELINE_STREAM;
+	pipe->state |= state;
+	spin_unlock_irqrestore(&pipe->lock, flags);
+
+	/* Set the maximum time per frame as the value requested by userspace.
+	 * This is a soft limit that can be overridden if the hardware doesn't
+	 * support the request limit.
+	 */
+	if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+		pipe->max_timeperframe = vfh->timeperframe;
+
+	video->queue = &vfh->queue;
+	INIT_LIST_HEAD(&video->dmaqueue);
+	spin_lock_init(&video->qlock);
+	atomic_set(&pipe->frame_number, -1);
+
+	ret = vb2_streamon(&vfh->queue, type);
+	if (ret < 0)
+		goto error;
+
+	/* In sensor-to-memory mode, the stream can be started synchronously
+	 * to the stream on command. In memory-to-memory mode, it will be
+	 * started when buffers are queued on both the input and output.
+	 */
+	if (pipe->input == NULL) {
+		unsigned long flags;
+		ret = omap4iss_pipeline_set_stream(pipe,
+					      ISS_PIPELINE_STREAM_CONTINUOUS);
+		if (ret < 0)
+			goto error;
+		spin_lock_irqsave(&video->qlock, flags);
+		if (list_empty(&video->dmaqueue))
+			video->dmaqueue_flags |= ISS_VIDEO_DMAQUEUE_UNDERRUN;
+		spin_unlock_irqrestore(&video->qlock, flags);
+	}
+
+error:
+	if (ret < 0) {
+		vb2_streamoff(&vfh->queue, type);
+		if (video->iss->pdata->set_constraints)
+			video->iss->pdata->set_constraints(video->iss, false);
+		media_entity_pipeline_stop(&video->video.entity);
+		video->queue = NULL;
+	}
+
+	if (!ret)
+		video->streaming = 1;
+
+	mutex_unlock(&video->stream_lock);
+	return ret;
+}
+
+static int
+iss_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type)
+{
+	struct iss_video_fh *vfh = to_iss_video_fh(fh);
+	struct iss_video *video = video_drvdata(file);
+	struct iss_pipeline *pipe = to_iss_pipeline(&video->video.entity);
+	enum iss_pipeline_state state;
+	unsigned long flags;
+
+	if (type != video->type)
+		return -EINVAL;
+
+	mutex_lock(&video->stream_lock);
+
+	if (!vb2_is_streaming(&vfh->queue))
+		goto done;
+
+	/* Update the pipeline state. */
+	if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		state = ISS_PIPELINE_STREAM_OUTPUT
+		      | ISS_PIPELINE_QUEUE_OUTPUT;
+	else
+		state = ISS_PIPELINE_STREAM_INPUT
+		      | ISS_PIPELINE_QUEUE_INPUT;
+
+	spin_lock_irqsave(&pipe->lock, flags);
+	pipe->state &= ~state;
+	spin_unlock_irqrestore(&pipe->lock, flags);
+
+	/* Stop the stream. */
+	omap4iss_pipeline_set_stream(pipe, ISS_PIPELINE_STREAM_STOPPED);
+	vb2_streamoff(&vfh->queue, type);
+	video->queue = NULL;
+	video->streaming = 0;
+
+	if (video->iss->pdata->set_constraints)
+		video->iss->pdata->set_constraints(video->iss, false);
+	media_entity_pipeline_stop(&video->video.entity);
+
+done:
+	mutex_unlock(&video->stream_lock);
+	return 0;
+}
+
+static int
+iss_video_enum_input(struct file *file, void *fh, struct v4l2_input *input)
+{
+	if (input->index > 0)
+		return -EINVAL;
+
+	strlcpy(input->name, "camera", sizeof(input->name));
+	input->type = V4L2_INPUT_TYPE_CAMERA;
+
+	return 0;
+}
+
+static int
+iss_video_g_input(struct file *file, void *fh, unsigned int *input)
+{
+	*input = 0;
+
+	return 0;
+}
+
+static int
+iss_video_s_input(struct file *file, void *fh, unsigned int input)
+{
+	return input == 0 ? 0 : -EINVAL;
+}
+
+static const struct v4l2_ioctl_ops iss_video_ioctl_ops = {
+	.vidioc_querycap		= iss_video_querycap,
+	.vidioc_g_fmt_vid_cap		= iss_video_get_format,
+	.vidioc_s_fmt_vid_cap		= iss_video_set_format,
+	.vidioc_try_fmt_vid_cap		= iss_video_try_format,
+	.vidioc_g_fmt_vid_out		= iss_video_get_format,
+	.vidioc_s_fmt_vid_out		= iss_video_set_format,
+	.vidioc_try_fmt_vid_out		= iss_video_try_format,
+	.vidioc_cropcap			= iss_video_cropcap,
+	.vidioc_g_crop			= iss_video_get_crop,
+	.vidioc_s_crop			= iss_video_set_crop,
+	.vidioc_g_parm			= iss_video_get_param,
+	.vidioc_s_parm			= iss_video_set_param,
+	.vidioc_reqbufs			= iss_video_reqbufs,
+	.vidioc_querybuf		= iss_video_querybuf,
+	.vidioc_qbuf			= iss_video_qbuf,
+	.vidioc_dqbuf			= iss_video_dqbuf,
+	.vidioc_streamon		= iss_video_streamon,
+	.vidioc_streamoff		= iss_video_streamoff,
+	.vidioc_enum_input		= iss_video_enum_input,
+	.vidioc_g_input			= iss_video_g_input,
+	.vidioc_s_input			= iss_video_s_input,
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 file operations
+ */
+
+static int iss_video_open(struct file *file)
+{
+	struct iss_video *video = video_drvdata(file);
+	struct iss_video_fh *handle;
+	struct vb2_queue *q;
+	int ret = 0;
+
+	handle = kzalloc(sizeof(*handle), GFP_KERNEL);
+	if (handle == NULL)
+		return -ENOMEM;
+
+	v4l2_fh_init(&handle->vfh, &video->video);
+	v4l2_fh_add(&handle->vfh);
+
+	/* If this is the first user, initialise the pipeline. */
+	if (omap4iss_get(video->iss) == NULL) {
+		ret = -EBUSY;
+		goto done;
+	}
+
+	ret = omap4iss_pipeline_pm_use(&video->video.entity, 1);
+	if (ret < 0) {
+		omap4iss_put(video->iss);
+		goto done;
+	}
+
+	video->alloc_ctx = vb2_dma_contig_init_ctx(video->iss->dev);
+	if (IS_ERR(video->alloc_ctx)) {
+		ret = PTR_ERR(video->alloc_ctx);
+		omap4iss_put(video->iss);
+		goto done;
+	}
+
+	q = &handle->queue;
+
+	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	q->io_modes = VB2_MMAP;
+	q->drv_priv = handle;
+	q->ops = &iss_video_vb2ops;
+	q->mem_ops = &vb2_dma_contig_memops;
+	q->buf_struct_size = sizeof(struct iss_buffer);
+
+	ret = vb2_queue_init(q);
+	if (ret) {
+		omap4iss_put(video->iss);
+		goto done;
+	}
+
+	memset(&handle->format, 0, sizeof(handle->format));
+	handle->format.type = video->type;
+	handle->timeperframe.denominator = 1;
+
+	handle->video = video;
+	file->private_data = &handle->vfh;
+
+done:
+	if (ret < 0) {
+		v4l2_fh_del(&handle->vfh);
+		kfree(handle);
+	}
+
+	return ret;
+}
+
+static int iss_video_release(struct file *file)
+{
+	struct iss_video *video = video_drvdata(file);
+	struct v4l2_fh *vfh = file->private_data;
+	struct iss_video_fh *handle = to_iss_video_fh(vfh);
+
+	/* Disable streaming and free the buffers queue resources. */
+	iss_video_streamoff(file, vfh, video->type);
+
+	omap4iss_pipeline_pm_use(&video->video.entity, 0);
+
+	/* Release the file handle. */
+	v4l2_fh_del(vfh);
+	kfree(handle);
+	file->private_data = NULL;
+
+	omap4iss_put(video->iss);
+
+	return 0;
+}
+
+static unsigned int iss_video_poll(struct file *file, poll_table *wait)
+{
+	struct iss_video_fh *vfh = to_iss_video_fh(file->private_data);
+
+	return vb2_poll(&vfh->queue, file, wait);
+}
+
+static int iss_video_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct iss_video_fh *vfh = to_iss_video_fh(file->private_data);
+
+	return vb2_mmap(&vfh->queue, vma);;
+}
+
+static struct v4l2_file_operations iss_video_fops = {
+	.owner = THIS_MODULE,
+	.unlocked_ioctl = video_ioctl2,
+	.open = iss_video_open,
+	.release = iss_video_release,
+	.poll = iss_video_poll,
+	.mmap = iss_video_mmap,
+};
+
+/* -----------------------------------------------------------------------------
+ * ISS video core
+ */
+
+static const struct iss_video_operations iss_video_dummy_ops = {
+};
+
+int omap4iss_video_init(struct iss_video *video, const char *name)
+{
+	const char *direction;
+	int ret;
+
+	switch (video->type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+		direction = "output";
+		video->pad.flags = MEDIA_PAD_FL_SINK;
+		break;
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+		direction = "input";
+		video->pad.flags = MEDIA_PAD_FL_SOURCE;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	ret = media_entity_init(&video->video.entity, 1, &video->pad, 0);
+	if (ret < 0)
+		return ret;
+
+	mutex_init(&video->mutex);
+	atomic_set(&video->active, 0);
+
+	spin_lock_init(&video->pipe.lock);
+	mutex_init(&video->stream_lock);
+
+	/* Initialize the video device. */
+	if (video->ops == NULL)
+		video->ops = &iss_video_dummy_ops;
+
+	video->video.fops = &iss_video_fops;
+	snprintf(video->video.name, sizeof(video->video.name),
+		 "OMAP4 ISS %s %s", name, direction);
+	video->video.vfl_type = VFL_TYPE_GRABBER;
+	video->video.release = video_device_release_empty;
+	video->video.ioctl_ops = &iss_video_ioctl_ops;
+	video->pipe.stream_state = ISS_PIPELINE_STREAM_STOPPED;
+
+	video_set_drvdata(&video->video, video);
+
+	return 0;
+}
+
+int omap4iss_video_register(struct iss_video *video, struct v4l2_device *vdev)
+{
+	int ret;
+
+	video->video.v4l2_dev = vdev;
+
+	ret = video_register_device(&video->video, VFL_TYPE_GRABBER, -1);
+	if (ret < 0)
+		printk(KERN_ERR "%s: could not register video device (%d)\n",
+			__func__, ret);
+
+	return ret;
+}
+
+void omap4iss_video_unregister(struct iss_video *video)
+{
+	if (video_is_registered(&video->video)) {
+		media_entity_cleanup(&video->video.entity);
+		video_unregister_device(&video->video);
+	}
+}
diff --git a/drivers/media/video/omap4iss/iss_video.h b/drivers/media/video/omap4iss/iss_video.h
new file mode 100644
index 0000000..fc123b0
--- /dev/null
+++ b/drivers/media/video/omap4iss/iss_video.h
@@ -0,0 +1,205 @@
+/*
+ * iss_video.h
+ *
+ * TI OMAP4 ISS - Generic video node
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ *
+ * Contacts: Sergio Aguirre <saaguirre@ti.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef OMAP4_ISS_VIDEO_H
+#define OMAP4_ISS_VIDEO_H
+
+#include <linux/v4l2-mediabus.h>
+#include <linux/version.h>
+#include <media/media-entity.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-fh.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+
+#define ISS_VIDEO_DRIVER_NAME		"issvideo"
+#define ISS_VIDEO_DRIVER_VERSION	KERNEL_VERSION(0, 0, 1)
+
+struct iss_device;
+struct iss_video;
+struct v4l2_mbus_framefmt;
+struct v4l2_pix_format;
+
+/*
+ * struct iss_format_info - ISS media bus format information
+ * @code: V4L2 media bus format code
+ * @truncated: V4L2 media bus format code for the same format truncated to 10
+ *	bits. Identical to @code if the format is 10 bits wide or less.
+ * @uncompressed: V4L2 media bus format code for the corresponding uncompressed
+ *	format. Identical to @code if the format is not DPCM compressed.
+ * @flavor: V4L2 media bus format code for the same pixel layout but
+ *	shifted to be 8 bits per pixel. =0 if format is not shiftable.
+ * @pixelformat: V4L2 pixel format FCC identifier
+ * @bpp: Bits per pixel
+ */
+struct iss_format_info {
+	enum v4l2_mbus_pixelcode code;
+	enum v4l2_mbus_pixelcode truncated;
+	enum v4l2_mbus_pixelcode uncompressed;
+	enum v4l2_mbus_pixelcode flavor;
+	u32 pixelformat;
+	unsigned int bpp;
+};
+
+enum iss_pipeline_stream_state {
+	ISS_PIPELINE_STREAM_STOPPED = 0,
+	ISS_PIPELINE_STREAM_CONTINUOUS = 1,
+	ISS_PIPELINE_STREAM_SINGLESHOT = 2,
+};
+
+enum iss_pipeline_state {
+	/* The stream has been started on the input video node. */
+	ISS_PIPELINE_STREAM_INPUT = 1,
+	/* The stream has been started on the output video node. */
+	ISS_PIPELINE_STREAM_OUTPUT = 2,
+	/* At least one buffer is queued on the input video node. */
+	ISS_PIPELINE_QUEUE_INPUT = 4,
+	/* At least one buffer is queued on the output video node. */
+	ISS_PIPELINE_QUEUE_OUTPUT = 8,
+	/* The input entity is idle, ready to be started. */
+	ISS_PIPELINE_IDLE_INPUT = 16,
+	/* The output entity is idle, ready to be started. */
+	ISS_PIPELINE_IDLE_OUTPUT = 32,
+	/* The pipeline is currently streaming. */
+	ISS_PIPELINE_STREAM = 64,
+};
+
+struct iss_pipeline {
+	struct media_pipeline pipe;
+	spinlock_t lock;		/* Pipeline state and queue flags */
+	unsigned int state;
+	enum iss_pipeline_stream_state stream_state;
+	struct iss_video *input;
+	struct iss_video *output;
+	atomic_t frame_number;
+	bool do_propagation; /* of frame number */
+	struct v4l2_fract max_timeperframe;
+};
+
+#define to_iss_pipeline(__e) \
+	container_of((__e)->pipe, struct iss_pipeline, pipe)
+
+static inline int iss_pipeline_ready(struct iss_pipeline *pipe)
+{
+	return pipe->state == (ISS_PIPELINE_STREAM_INPUT |
+			       ISS_PIPELINE_STREAM_OUTPUT |
+			       ISS_PIPELINE_QUEUE_INPUT |
+			       ISS_PIPELINE_QUEUE_OUTPUT |
+			       ISS_PIPELINE_IDLE_INPUT |
+			       ISS_PIPELINE_IDLE_OUTPUT);
+}
+
+/*
+ * struct iss_buffer - ISS buffer
+ * @buffer: ISS video buffer
+ * @iss_addr: Physical address of the buffer.
+ */
+struct iss_buffer {
+	/* common v4l buffer stuff -- must be first */
+	struct vb2_buffer	vb;
+	struct list_head	list;
+	dma_addr_t iss_addr;
+};
+
+#define to_iss_buffer(buf)	container_of(buf, struct iss_buffer, buffer)
+
+enum iss_video_dmaqueue_flags {
+	/* Set if DMA queue becomes empty when ISS_PIPELINE_STREAM_CONTINUOUS */
+	ISS_VIDEO_DMAQUEUE_UNDERRUN = (1 << 0),
+	/* Set when queuing buffer to an empty DMA queue */
+	ISS_VIDEO_DMAQUEUE_QUEUED = (1 << 1),
+};
+
+#define iss_video_dmaqueue_flags_clr(video)	\
+			({ (video)->dmaqueue_flags = 0; })
+
+/*
+ * struct iss_video_operations - ISS video operations
+ * @queue:	Resume streaming when a buffer is queued. Called on VIDIOC_QBUF
+ *		if there was no buffer previously queued.
+ */
+struct iss_video_operations {
+	int(*queue)(struct iss_video *video, struct iss_buffer *buffer);
+};
+
+struct iss_video {
+	struct video_device video;
+	enum v4l2_buf_type type;
+	struct media_pad pad;
+
+	struct mutex mutex;		/* format and crop settings */
+	atomic_t active;
+
+	struct iss_device *iss;
+
+	unsigned int capture_mem;
+	unsigned int bpl_alignment;	/* alignment value */
+	unsigned int bpl_zero_padding;	/* whether the alignment is optional */
+	unsigned int bpl_max;		/* maximum bytes per line value */
+	unsigned int bpl_value;		/* bytes per line value */
+	unsigned int bpl_padding;	/* padding at end of line */
+
+	/* Entity video node streaming */
+	unsigned int streaming:1;
+
+	/* Pipeline state */
+	struct iss_pipeline pipe;
+	struct mutex stream_lock;	/* pipeline and stream states */
+
+	/* Video buffers queue */
+	struct vb2_queue *queue;
+	spinlock_t qlock;	/* Spinlock for dmaqueue */
+	struct list_head dmaqueue;
+	enum iss_video_dmaqueue_flags dmaqueue_flags;
+	struct vb2_alloc_ctx *alloc_ctx;
+
+	const struct iss_video_operations *ops;
+};
+
+#define to_iss_video(vdev)	container_of(vdev, struct iss_video, video)
+
+struct iss_video_fh {
+	struct v4l2_fh vfh;
+	struct iss_video *video;
+	struct vb2_queue queue;
+	struct v4l2_format format;
+	struct v4l2_fract timeperframe;
+};
+
+#define to_iss_video_fh(fh)	container_of(fh, struct iss_video_fh, vfh)
+#define iss_video_queue_to_iss_video_fh(q) \
+				container_of(q, struct iss_video_fh, queue)
+
+int omap4iss_video_init(struct iss_video *video, const char *name);
+int omap4iss_video_register(struct iss_video *video,
+			    struct v4l2_device *vdev);
+void omap4iss_video_unregister(struct iss_video *video);
+struct iss_buffer *omap4iss_video_buffer_next(struct iss_video *video,
+					      unsigned int error);
+struct media_pad *omap4iss_video_remote_pad(struct iss_video *video);
+
+const struct iss_format_info *
+omap4iss_video_format_info(enum v4l2_mbus_pixelcode code);
+
+#endif /* OMAP4_ISS_VIDEO_H */
-- 
1.7.7.4

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

* [PATCH v2 07/11] v4l: Add support for ov5640 sensor
  2011-12-01  0:14 ` Sergio Aguirre
@ 2011-12-01  0:14   ` Sergio Aguirre
  -1 siblings, 0 replies; 65+ messages in thread
From: Sergio Aguirre @ 2011-12-01  0:14 UTC (permalink / raw)
  To: linux-media; +Cc: linux-omap, laurent.pinchart, sakari.ailus, Sergio Aguirre

This adds a very limited driver for ov5640, which
only supports:
 - 2592x1944 @ ~7.5 fps
 - 1920x1080 @ ~15 fps,
 - 1280x720 @ ~24 fps,
 - 640x480 @ ~24 fps,
 - 320x240 @ ~24 fps,

All in YUV422i format, using 1 CSI2 datalane @ 333 MHz.

Signed-off-by: Sergio Aguirre <saaguirre@ti.com>
---
 drivers/media/video/Kconfig     |    6 +
 drivers/media/video/Makefile    |    1 +
 drivers/media/video/ov5640.c    |  972 +++++++++++++++++++++++++++++++++++++++
 include/media/ov5640.h          |   10 +
 include/media/v4l2-chip-ident.h |    1 +
 5 files changed, 990 insertions(+), 0 deletions(-)
 create mode 100644 drivers/media/video/ov5640.c
 create mode 100644 include/media/ov5640.h

diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index ae2a99d..3ee4fc9 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -467,6 +467,12 @@ config VIDEO_OV7670
 	  OV7670 VGA camera.  It currently only works with the M88ALP01
 	  controller.
 
+config VIDEO_OV5640
+	tristate "OmniVision OV5640 sensor support"
+	depends on I2C && VIDEO_V4L2
+	help
+	  This is a ov5640 camera driver
+
 config VIDEO_MT9P031
 	tristate "Aptina MT9P031 support"
 	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index f02a4c4..d296c7b 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -63,6 +63,7 @@ obj-$(CONFIG_VIDEO_CX25840) += cx25840/
 obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o
 obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o
 obj-$(CONFIG_VIDEO_OV7670) 	+= ov7670.o
+obj-$(CONFIG_VIDEO_OV5640)	+= ov5640.o
 obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o
 obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o
 obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o
diff --git a/drivers/media/video/ov5640.c b/drivers/media/video/ov5640.c
new file mode 100644
index 0000000..c93802a
--- /dev/null
+++ b/drivers/media/video/ov5640.c
@@ -0,0 +1,972 @@
+/*
+ * OmniVision OV5640 sensor driver
+ *
+ * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/videodev2.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/log2.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+#include <media/ov5640.h>
+
+#define OV5640_BRIGHTNESS_MIN				0
+#define OV5640_BRIGHTNESS_MAX				200
+#define OV5640_BRIGHTNESS_STEP				100
+#define OV5640_BRIGHTNESS_DEF				100
+
+#define OV5640_CONTRAST_MIN				0
+#define OV5640_CONTRAST_MAX				200
+#define OV5640_CONTRAST_STEP				100
+#define OV5640_CONTRAST_DEF				100
+
+/* OV5640 has only one fixed colorspace per pixelcode */
+struct ov5640_datafmt {
+	enum v4l2_mbus_pixelcode	code;
+	enum v4l2_colorspace		colorspace;
+};
+
+struct ov5640_timing_cfg {
+	u16 x_addr_start;
+	u16 y_addr_start;
+	u16 x_addr_end;
+	u16 y_addr_end;
+	u16 h_output_size;
+	u16 v_output_size;
+	u16 h_total_size;
+	u16 v_total_size;
+	u16 isp_h_offset;
+	u16 isp_v_offset;
+	u8 h_odd_ss_inc;
+	u8 h_even_ss_inc;
+	u8 v_odd_ss_inc;
+	u8 v_even_ss_inc;
+};
+
+enum ov5640_size {
+	OV5640_SIZE_QVGA,
+	OV5640_SIZE_VGA,
+	OV5640_SIZE_720P,
+	OV5640_SIZE_1080P,
+	OV5640_SIZE_5MP,
+	OV5640_SIZE_LAST,
+};
+
+static const struct v4l2_frmsize_discrete ov5640_frmsizes[OV5640_SIZE_LAST] = {
+	{  320,  240 },
+	{  640,  480 },
+	{ 1280,  720 },
+	{ 1920, 1080 },
+	{ 2592, 1944 },
+};
+
+/* Find a frame size in an array */
+static int ov5640_find_framesize(u32 width, u32 height)
+{
+	int i;
+
+	for (i = 0; i < OV5640_SIZE_LAST; i++) {
+		if ((ov5640_frmsizes[i].width >= width) &&
+		    (ov5640_frmsizes[i].height >= height))
+			break;
+	}
+
+	/* If not found, select biggest */
+	if (i >= OV5640_SIZE_LAST)
+		i = OV5640_SIZE_LAST - 1;
+
+	return i;
+}
+
+struct ov5640 {
+	struct v4l2_subdev subdev;
+	struct media_pad pad;
+	struct v4l2_mbus_framefmt format;
+	const struct ov5640_platform_data *pdata;
+	int brightness;
+	int contrast;
+	int colorlevel;
+};
+
+static inline struct ov5640 *to_ov5640(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct ov5640, subdev);
+}
+
+/**
+ * struct ov5640_reg - ov5640 register format
+ * @reg: 16-bit offset to register
+ * @val: 8/16/32-bit register value
+ * @length: length of the register
+ *
+ * Define a structure for OV5640 register initialization values
+ */
+struct ov5640_reg {
+	u16	reg;
+	u8	val;
+};
+
+/* TODO: Divide this properly */
+static const struct ov5640_reg configscript_common1[] = {
+	{ 0x3103, 0x03 },
+	{ 0x3017, 0x00 },
+	{ 0x3018, 0x00 },
+	{ 0x3630, 0x2e },
+	{ 0x3632, 0xe2 },
+	{ 0x3633, 0x23 },
+	{ 0x3634, 0x44 },
+	{ 0x3621, 0xe0 },
+	{ 0x3704, 0xa0 },
+	{ 0x3703, 0x5a },
+	{ 0x3715, 0x78 },
+	{ 0x3717, 0x01 },
+	{ 0x370b, 0x60 },
+	{ 0x3705, 0x1a },
+	{ 0x3905, 0x02 },
+	{ 0x3906, 0x10 },
+	{ 0x3901, 0x0a },
+	{ 0x3731, 0x12 },
+	{ 0x3600, 0x04 },
+	{ 0x3601, 0x22 },
+	{ 0x471c, 0x50 },
+	{ 0x3002, 0x1c },
+	{ 0x3006, 0xc3 },
+	{ 0x300e, 0x05 },
+	{ 0x302e, 0x08 },
+	{ 0x3612, 0x4b },
+	{ 0x3618, 0x04 },
+	{ 0x3034, 0x18 },
+	{ 0x3035, 0x11 },
+	{ 0x3036, 0x54 },
+	{ 0x3037, 0x13 },
+	{ 0x3708, 0x21 },
+	{ 0x3709, 0x12 },
+	{ 0x370c, 0x00 },
+};
+
+/* TODO: Divide this properly */
+static const struct ov5640_reg configscript_common2[] = {
+	{ 0x3a02, 0x01 },
+	{ 0x3a03, 0xec },
+	{ 0x3a08, 0x01 },
+	{ 0x3a09, 0x27 },
+	{ 0x3a0a, 0x00 },
+	{ 0x3a0b, 0xf6 },
+	{ 0x3a0e, 0x06 },
+	{ 0x3a0d, 0x08 },
+	{ 0x3a14, 0x01 },
+	{ 0x3a15, 0xec },
+	{ 0x4001, 0x02 },
+	{ 0x4004, 0x06 },
+	{ 0x460b, 0x37 },
+	{ 0x4750, 0x00 },
+	{ 0x4751, 0x00 },
+	{ 0x4800, 0x24 },
+	{ 0x5a00, 0x08 },
+	{ 0x5a21, 0x00 },
+	{ 0x5a24, 0x00 },
+	{ 0x5000, 0x27 },
+	{ 0x5001, 0x87 },
+	{ 0x3820, 0x40 },
+	{ 0x3821, 0x06 },
+	{ 0x3824, 0x01 },
+	{ 0x5481, 0x08 },
+	{ 0x5482, 0x14 },
+	{ 0x5483, 0x28 },
+	{ 0x5484, 0x51 },
+	{ 0x5485, 0x65 },
+	{ 0x5486, 0x71 },
+	{ 0x5487, 0x7d },
+	{ 0x5488, 0x87 },
+	{ 0x5489, 0x91 },
+	{ 0x548a, 0x9a },
+	{ 0x548b, 0xaa },
+	{ 0x548c, 0xb8 },
+	{ 0x548d, 0xcd },
+	{ 0x548e, 0xdd },
+	{ 0x548f, 0xea },
+	{ 0x5490, 0x1d },
+	{ 0x5381, 0x20 },
+	{ 0x5382, 0x64 },
+	{ 0x5383, 0x08 },
+	{ 0x5384, 0x20 },
+	{ 0x5385, 0x80 },
+	{ 0x5386, 0xa0 },
+	{ 0x5387, 0xa2 },
+	{ 0x5388, 0xa0 },
+	{ 0x5389, 0x02 },
+	{ 0x538a, 0x01 },
+	{ 0x538b, 0x98 },
+	{ 0x5300, 0x08 },
+	{ 0x5301, 0x30 },
+	{ 0x5302, 0x10 },
+	{ 0x5303, 0x00 },
+	{ 0x5304, 0x08 },
+	{ 0x5305, 0x30 },
+	{ 0x5306, 0x08 },
+	{ 0x5307, 0x16 },
+	{ 0x5580, 0x00 },
+	{ 0x5587, 0x00 },
+	{ 0x5588, 0x00 },
+	{ 0x5583, 0x40 },
+	{ 0x5584, 0x10 },
+	{ 0x5589, 0x10 },
+	{ 0x558a, 0x00 },
+	{ 0x558b, 0xf8 },
+	{ 0x3a0f, 0x36 },
+	{ 0x3a10, 0x2e },
+	{ 0x3a1b, 0x38 },
+	{ 0x3a1e, 0x2c },
+	{ 0x3a11, 0x70 },
+	{ 0x3a1f, 0x18 },
+	{ 0x3a18, 0x00 },
+	{ 0x3a19, 0xf8 },
+	{ 0x3003, 0x03 },
+	{ 0x3003, 0x01 },
+};
+
+static const struct ov5640_timing_cfg timing_cfg[OV5640_SIZE_LAST] = {
+	[OV5640_SIZE_QVGA] = {
+		.x_addr_start = 0,
+		.y_addr_start = 0,
+		.x_addr_end = 2623,
+		.y_addr_end = 1951,
+		.h_output_size = 320,
+		.v_output_size = 240,
+		.h_total_size = 2844,
+		.v_total_size = 1968,
+		.isp_h_offset = 16,
+		.isp_v_offset = 6,
+		.h_odd_ss_inc = 1,
+		.h_even_ss_inc = 1,
+		.v_odd_ss_inc = 1,
+		.v_even_ss_inc = 1,
+	},
+	[OV5640_SIZE_VGA] = {
+		.x_addr_start = 0,
+		.y_addr_start = 0,
+		.x_addr_end = 2623,
+		.y_addr_end = 1951,
+		.h_output_size = 640,
+		.v_output_size = 480,
+		.h_total_size = 2844,
+		.v_total_size = 1968,
+		.isp_h_offset = 16,
+		.isp_v_offset = 6,
+		.h_odd_ss_inc = 1,
+		.h_even_ss_inc = 1,
+		.v_odd_ss_inc = 1,
+		.v_even_ss_inc = 1,
+	},
+	[OV5640_SIZE_720P] = {
+		.x_addr_start = 336,
+		.y_addr_start = 434,
+		.x_addr_end = 2287,
+		.y_addr_end = 1522,
+		.h_output_size = 1280,
+		.v_output_size = 720,
+		.h_total_size = 2500,
+		.v_total_size = 1120,
+		.isp_h_offset = 16,
+		.isp_v_offset = 4,
+		.h_odd_ss_inc = 1,
+		.h_even_ss_inc = 1,
+		.v_odd_ss_inc = 1,
+		.v_even_ss_inc = 1,
+	},
+	[OV5640_SIZE_1080P] = {
+		.x_addr_start = 336,
+		.y_addr_start = 434,
+		.x_addr_end = 2287,
+		.y_addr_end = 1522,
+		.h_output_size = 1920,
+		.v_output_size = 1080,
+		.h_total_size = 2500,
+		.v_total_size = 1120,
+		.isp_h_offset = 16,
+		.isp_v_offset = 4,
+		.h_odd_ss_inc = 1,
+		.h_even_ss_inc = 1,
+		.v_odd_ss_inc = 1,
+		.v_even_ss_inc = 1,
+	},
+	[OV5640_SIZE_5MP] = {
+		.x_addr_start = 0,
+		.y_addr_start = 0,
+		.x_addr_end = 2623,
+		.y_addr_end = 1951,
+		.h_output_size = 2592,
+		.v_output_size = 1944,
+		.h_total_size = 2844,
+		.v_total_size = 1968,
+		.isp_h_offset = 16,
+		.isp_v_offset = 6,
+		.h_odd_ss_inc = 1,
+		.h_even_ss_inc = 1,
+		.v_odd_ss_inc = 1,
+		.v_even_ss_inc = 1,
+	},
+};
+
+/**
+ * ov5640_reg_read - Read a value from a register in an ov5640 sensor device
+ * @client: i2c driver client structure
+ * @reg: register address / offset
+ * @val: stores the value that gets read
+ *
+ * Read a value from a register in an ov5640 sensor device.
+ * The value is returned in 'val'.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int ov5640_reg_read(struct i2c_client *client, u16 reg, u8 *val)
+{
+	int ret;
+	u8 data[2] = {0};
+	struct i2c_msg msg = {
+		.addr	= client->addr,
+		.flags	= 0,
+		.len	= 2,
+		.buf	= data,
+	};
+
+	data[0] = (u8)(reg >> 8);
+	data[1] = (u8)(reg & 0xff);
+
+	ret = i2c_transfer(client->adapter, &msg, 1);
+	if (ret < 0)
+		goto err;
+
+	msg.flags = I2C_M_RD;
+	msg.len = 1;
+	ret = i2c_transfer(client->adapter, &msg, 1);
+	if (ret < 0)
+		goto err;
+
+	*val = data[0];
+	return 0;
+
+err:
+	dev_err(&client->dev, "Failed reading register 0x%02x!\n", reg);
+	return ret;
+}
+
+/**
+ * Write a value to a register in ov5640 sensor device.
+ * @client: i2c driver client structure.
+ * @reg: Address of the register to read value from.
+ * @val: Value to be written to a specific register.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int ov5640_reg_write(struct i2c_client *client, u16 reg, u8 val)
+{
+	int ret;
+	unsigned char data[3] = { (u8)(reg >> 8), (u8)(reg & 0xff), val };
+	struct i2c_msg msg = {
+		.addr	= client->addr,
+		.flags	= 0,
+		.len	= 3,
+		.buf	= data,
+	};
+
+	ret = i2c_transfer(client->adapter, &msg, 1);
+	if (ret < 0) {
+		dev_err(&client->dev, "Failed writing register 0x%02x!\n", reg);
+		return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * Initialize a list of ov5640 registers.
+ * The list of registers is terminated by the pair of values
+ * @client: i2c driver client structure.
+ * @reglist[]: List of address of the registers to write data.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int ov5640_reg_writes(struct i2c_client *client,
+			     const struct ov5640_reg reglist[],
+			     int size)
+{
+	int err = 0, i;
+
+	for (i = 0; i < size; i++) {
+		err = ov5640_reg_write(client, reglist[i].reg,
+				reglist[i].val);
+		if (err)
+			return err;
+	}
+	return 0;
+}
+
+static int ov5640_reg_set(struct i2c_client *client, u16 reg, u8 val)
+{
+	int ret;
+	u8 tmpval = 0;
+
+	ret = ov5640_reg_read(client, reg, &tmpval);
+	if (ret)
+		return ret;
+
+	return ov5640_reg_write(client, reg, tmpval | val);
+}
+
+static int ov5640_reg_clr(struct i2c_client *client, u16 reg, u8 val)
+{
+	int ret;
+	u8 tmpval = 0;
+
+	ret = ov5640_reg_read(client, reg, &tmpval);
+	if (ret)
+		return ret;
+
+	return ov5640_reg_write(client, reg, tmpval & ~val);
+}
+
+static int ov5640_config_timing(struct v4l2_subdev *sd)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov5640 *ov5640 = to_ov5640(sd);
+	int ret, i;
+
+	i = ov5640_find_framesize(ov5640->format.width, ov5640->format.height);
+
+	ret = ov5640_reg_write(client,
+			0x3800,
+			(timing_cfg[i].x_addr_start & 0xFF00) >> 8);
+	if (ret)
+		return ret;
+
+	ret = ov5640_reg_write(client,
+			0x3801,
+			timing_cfg[i].x_addr_start & 0xFF);
+	if (ret)
+		return ret;
+
+	ret = ov5640_reg_write(client,
+			0x3802,
+			(timing_cfg[i].y_addr_start & 0xFF00) >> 8);
+	if (ret)
+		return ret;
+
+	ret = ov5640_reg_write(client,
+			0x3803,
+			timing_cfg[i].y_addr_start & 0xFF);
+	if (ret)
+		return ret;
+
+	ret = ov5640_reg_write(client,
+			0x3804,
+			(timing_cfg[i].x_addr_end & 0xFF00) >> 8);
+	if (ret)
+		return ret;
+
+	ret = ov5640_reg_write(client,
+			0x3805,
+			timing_cfg[i].x_addr_end & 0xFF);
+	if (ret)
+		return ret;
+
+	ret = ov5640_reg_write(client,
+			0x3806,
+			(timing_cfg[i].y_addr_end & 0xFF00) >> 8);
+	if (ret)
+		return ret;
+
+	ret = ov5640_reg_write(client,
+			0x3807,
+			timing_cfg[i].y_addr_end & 0xFF);
+	if (ret)
+		return ret;
+
+	ret = ov5640_reg_write(client,
+			0x3808,
+			(timing_cfg[i].h_output_size & 0xFF00) >> 8);
+	if (ret)
+		return ret;
+
+	ret = ov5640_reg_write(client,
+			0x3809,
+			timing_cfg[i].h_output_size & 0xFF);
+	if (ret)
+		return ret;
+
+	ret = ov5640_reg_write(client,
+			0x380A,
+			(timing_cfg[i].v_output_size & 0xFF00) >> 8);
+	if (ret)
+		return ret;
+
+	ret = ov5640_reg_write(client,
+			0x380B,
+			timing_cfg[i].v_output_size & 0xFF);
+	if (ret)
+		return ret;
+
+	ret = ov5640_reg_write(client,
+			0x380C,
+			(timing_cfg[i].h_total_size & 0xFF00) >> 8);
+	if (ret)
+		return ret;
+
+	ret = ov5640_reg_write(client,
+			0x380D,
+			timing_cfg[i].h_total_size & 0xFF);
+	if (ret)
+		return ret;
+
+	ret = ov5640_reg_write(client,
+			0x380E,
+			(timing_cfg[i].v_total_size & 0xFF00) >> 8);
+	if (ret)
+		return ret;
+
+	ret = ov5640_reg_write(client,
+			0x380F,
+			timing_cfg[i].v_total_size & 0xFF);
+	if (ret)
+		return ret;
+
+	ret = ov5640_reg_write(client,
+			0x3810,
+			(timing_cfg[i].isp_h_offset & 0xFF00) >> 8);
+	if (ret)
+		return ret;
+
+	ret = ov5640_reg_write(client,
+			0x3811,
+			timing_cfg[i].isp_h_offset & 0xFF);
+	if (ret)
+		return ret;
+
+	ret = ov5640_reg_write(client,
+			0x3812,
+			(timing_cfg[i].isp_v_offset & 0xFF00) >> 8);
+	if (ret)
+		return ret;
+
+	ret = ov5640_reg_write(client,
+			0x3813,
+			timing_cfg[i].isp_v_offset & 0xFF);
+	if (ret)
+		return ret;
+
+	ret = ov5640_reg_write(client,
+			0x3814,
+			((timing_cfg[i].h_odd_ss_inc & 0xF) << 4) |
+			(timing_cfg[i].h_even_ss_inc & 0xF));
+	if (ret)
+		return ret;
+
+	ret = ov5640_reg_write(client,
+			0x3815,
+			((timing_cfg[i].v_odd_ss_inc & 0xF) << 4) |
+			(timing_cfg[i].v_even_ss_inc & 0xF));
+
+	return ret;
+}
+
+static struct v4l2_mbus_framefmt *
+__ov5640_get_pad_format(struct ov5640 *ov5640, struct v4l2_subdev_fh *fh,
+			 unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+	switch (which) {
+	case V4L2_SUBDEV_FORMAT_TRY:
+		return v4l2_subdev_get_try_format(fh, pad);
+	case V4L2_SUBDEV_FORMAT_ACTIVE:
+		return &ov5640->format;
+	default:
+		return NULL;
+	}
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev internal operations
+ */
+
+static int ov5640_s_power(struct v4l2_subdev *sd, int on)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov5640 *ov5640 = to_ov5640(sd);
+	int ret;
+
+	ret = ov5640->pdata->s_power(sd, on);
+	if (ret)
+		goto out;
+
+	if (on) {
+		/* SW Reset */
+		ret = ov5640_reg_set(client, 0x3008, 0x80);
+		if (ret)
+			goto out;
+
+		msleep(2);
+
+		ret = ov5640_reg_clr(client, 0x3008, 0x80);
+		if (ret)
+			goto out;
+
+		/* SW Powerdown */
+		ret = ov5640_reg_set(client, 0x3008, 0x40);
+		if (ret)
+			goto out;
+
+		ret = ov5640_reg_writes(client, configscript_common1,
+				ARRAY_SIZE(configscript_common1));
+		if (ret)
+			goto out;
+
+		ret = ov5640_reg_writes(client, configscript_common2,
+				ARRAY_SIZE(configscript_common2));
+		if (ret)
+			goto out;
+	}
+
+out:
+	return ret;
+}
+
+static struct v4l2_subdev_core_ops ov5640_subdev_core_ops = {
+	.s_power	= ov5640_s_power,
+};
+
+static int ov5640_g_fmt(struct v4l2_subdev *sd,
+			struct v4l2_subdev_fh *fh,
+			struct v4l2_subdev_format *format)
+{
+	struct ov5640 *ov5640 = to_ov5640(sd);
+
+	format->format = *__ov5640_get_pad_format(ov5640, fh, format->pad,
+						   format->which);
+
+	return 0;
+}
+
+static int ov5640_s_fmt(struct v4l2_subdev *sd,
+			struct v4l2_subdev_fh *fh,
+			struct v4l2_subdev_format *format)
+{
+	struct ov5640 *ov5640 = to_ov5640(sd);
+	struct v4l2_mbus_framefmt *__format;
+
+	__format = __ov5640_get_pad_format(ov5640, fh, format->pad,
+					    format->which);
+
+	format->format = *__format;
+
+	return 0;
+}
+
+static int ov5640_enum_fmt(struct v4l2_subdev *subdev,
+			   struct v4l2_subdev_fh *fh,
+			   struct v4l2_subdev_mbus_code_enum *code)
+{
+	if (code->index >= 2)
+		return -EINVAL;
+
+	switch (code->index) {
+	case 0:
+		code->code = V4L2_MBUS_FMT_UYVY8_1X16;
+		break;
+	case 1:
+		code->code = V4L2_MBUS_FMT_YUYV8_1X16;
+		break;
+	}
+	return 0;
+}
+
+static int ov5640_enum_framesizes(struct v4l2_subdev *subdev,
+				   struct v4l2_subdev_fh *fh,
+				   struct v4l2_subdev_frame_size_enum *fse)
+{
+	if ((fse->index >= OV5640_SIZE_LAST) ||
+	    (fse->code != V4L2_MBUS_FMT_UYVY8_1X16 &&
+	     fse->code != V4L2_MBUS_FMT_YUYV8_1X16))
+		return -EINVAL;
+
+	fse->min_width = ov5640_frmsizes[fse->index].width;
+	fse->max_width = fse->min_width;
+	fse->min_height = ov5640_frmsizes[fse->index].height;
+	fse->max_height = fse->min_height;
+
+	return 0;
+}
+
+static int ov5640_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct ov5640 *ov5640 = to_ov5640(sd);
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	int ret = 0;
+
+	if (enable) {
+		u8 fmtreg = 0, fmtmuxreg = 0;
+		int i;
+
+		switch ((u32)ov5640->format.code) {
+		case V4L2_MBUS_FMT_UYVY8_1X16:
+			fmtreg = 0x32;
+			fmtmuxreg = 0;
+			break;
+		case V4L2_MBUS_FMT_YUYV8_1X16:
+			fmtreg = 0x30;
+			fmtmuxreg = 0;
+			break;
+		default:
+			/* This shouldn't happen */
+			ret = -EINVAL;
+			return ret;
+		}
+
+		ret = ov5640_reg_write(client, 0x4300, fmtreg);
+		if (ret)
+			return ret;
+
+		ret = ov5640_reg_write(client, 0x501F, fmtmuxreg);
+		if (ret)
+			return ret;
+
+		ret = ov5640_config_timing(sd);
+		if (ret)
+			return ret;
+
+		i = ov5640_find_framesize(ov5640->format.width, ov5640->format.height);
+		if ((i == OV5640_SIZE_QVGA) ||
+		    (i == OV5640_SIZE_VGA) ||
+		    (i == OV5640_SIZE_720P)) {
+			ret = ov5640_reg_set(client, 0x5001, 0x20);
+			if (ret)
+				return ret;
+			ret = ov5640_reg_write(client, 0x3108,
+					(i == OV5640_SIZE_720P) ? 0x1 : 0);
+		} else {
+			ret = ov5640_reg_clr(client, 0x5001, 0x20);
+			if (ret)
+				return ret;
+			ret = ov5640_reg_write(client, 0x3108, 0x2);
+		}
+
+		ret = ov5640_reg_clr(client, 0x3008, 0x40);
+		if (ret)
+			goto out;
+	} else {
+		u8 tmpreg = 0;
+
+		ret = ov5640_reg_read(client, 0x3008, &tmpreg);
+		if (ret)
+			goto out;
+
+		ret = ov5640_reg_write(client, 0x3008, tmpreg | 0x40);
+		if (ret)
+			goto out;
+	}
+
+out:
+	return ret;
+}
+
+static struct v4l2_subdev_video_ops ov5640_subdev_video_ops = {
+	.s_stream	= ov5640_s_stream,
+};
+
+static struct v4l2_subdev_pad_ops ov5640_subdev_pad_ops = {
+	.enum_mbus_code = ov5640_enum_fmt,
+	.enum_frame_size = ov5640_enum_framesizes,
+	.get_fmt = ov5640_g_fmt,
+	.set_fmt = ov5640_s_fmt,
+};
+
+static int ov5640_g_skip_frames(struct v4l2_subdev *sd, u32 *frames)
+{
+	/* Quantity of initial bad frames to skip. Revisit. */
+	*frames = 3;
+
+	return 0;
+}
+
+static struct v4l2_subdev_sensor_ops ov5640_subdev_sensor_ops = {
+	.g_skip_frames	= ov5640_g_skip_frames,
+};
+
+static struct v4l2_subdev_ops ov5640_subdev_ops = {
+	.core	= &ov5640_subdev_core_ops,
+	.video	= &ov5640_subdev_video_ops,
+	.pad	= &ov5640_subdev_pad_ops,
+	.sensor	= &ov5640_subdev_sensor_ops,
+};
+
+static int ov5640_registered(struct v4l2_subdev *subdev)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(subdev);
+	int ret = 0;
+	u8 revision = 0;
+
+	ret = ov5640_s_power(subdev, 1);
+	if (ret < 0) {
+		dev_err(&client->dev, "OV5640 power up failed\n");
+		return ret;
+	}
+
+	ret = ov5640_reg_read(client, 0x302A, &revision);
+	if (ret) {
+		dev_err(&client->dev, "Failure to detect OV5640 chip\n");
+		goto out;
+	}
+
+	revision &= 0xF;
+
+	dev_info(&client->dev, "Detected a OV5640 chip, revision %x\n",
+		 revision);
+
+out:
+	ov5640_s_power(subdev, 0);
+
+	return ret;
+}
+
+static int ov5640_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
+{
+	struct v4l2_mbus_framefmt *format;
+
+	format = v4l2_subdev_get_try_format(fh, 0);
+	format->code = V4L2_MBUS_FMT_UYVY8_1X16;
+	format->width = ov5640_frmsizes[OV5640_SIZE_VGA].width;
+	format->height = ov5640_frmsizes[OV5640_SIZE_VGA].height;
+	format->field = V4L2_FIELD_NONE;
+	format->colorspace = V4L2_COLORSPACE_JPEG;
+
+	return 0;
+}
+
+static int ov5640_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
+{
+	return 0;
+}
+
+static struct v4l2_subdev_internal_ops ov5640_subdev_internal_ops = {
+	.registered = ov5640_registered,
+	.open = ov5640_open,
+	.close = ov5640_close,
+};
+
+static int ov5640_init(struct v4l2_subdev *sd)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov5640 *ov5640 = to_ov5640(sd);
+	int ret = 0;
+
+	/* default brightness and contrast */
+	ov5640->brightness = OV5640_BRIGHTNESS_DEF;
+	ov5640->contrast = OV5640_CONTRAST_DEF;
+
+	ov5640->colorlevel = V4L2_COLORFX_NONE;
+
+	dev_dbg(&client->dev, "Sensor initialized\n");
+
+	return ret;
+}
+
+static int ov5640_probe(struct i2c_client *client,
+			 const struct i2c_device_id *did)
+{
+	struct ov5640 *ov5640;
+	int ret;
+
+	if (!client->dev.platform_data) {
+		dev_err(&client->dev, "No platform data!!\n");
+		return -ENODEV;
+	}
+
+	ov5640 = kzalloc(sizeof(*ov5640), GFP_KERNEL);
+	if (!ov5640)
+		return -ENOMEM;
+
+	ov5640->pdata = client->dev.platform_data;
+
+	ov5640->format.code = V4L2_MBUS_FMT_UYVY8_1X16;
+	ov5640->format.width = ov5640_frmsizes[OV5640_SIZE_VGA].width;
+	ov5640->format.height = ov5640_frmsizes[OV5640_SIZE_VGA].height;
+	ov5640->format.field = V4L2_FIELD_NONE;
+	ov5640->format.colorspace = V4L2_COLORSPACE_JPEG;
+
+	v4l2_i2c_subdev_init(&ov5640->subdev, client, &ov5640_subdev_ops);
+	ov5640->subdev.internal_ops = &ov5640_subdev_internal_ops;
+	ov5640->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+	ov5640->pad.flags = MEDIA_PAD_FL_SOURCE;
+	ret = media_entity_init(&ov5640->subdev.entity, 1, &ov5640->pad, 0);
+	if (ret < 0)
+		goto err_mediainit;
+
+	/* init the sensor here */
+	ret = ov5640_init(&ov5640->subdev);
+	if (ret) {
+		dev_err(&client->dev, "Failed to initialize sensor\n");
+		goto err_sensorinit;
+	}
+
+	return ret;
+err_sensorinit:
+	v4l2_device_unregister_subdev(&ov5640->subdev);
+err_mediainit:
+	media_entity_cleanup(&ov5640->subdev.entity);
+	kfree(ov5640);
+	return ret;
+}
+
+static int ov5640_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+	struct ov5640 *ov5640 = to_ov5640(subdev);
+
+	v4l2_device_unregister_subdev(subdev);
+	media_entity_cleanup(&subdev->entity);
+	kfree(ov5640);
+	return 0;
+}
+
+static const struct i2c_device_id ov5640_id[] = {
+	{ "ov5640", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ov5640_id);
+
+static struct i2c_driver ov5640_i2c_driver = {
+	.driver = {
+		.name = "ov5640",
+	},
+	.probe		= ov5640_probe,
+	.remove		= ov5640_remove,
+	.id_table	= ov5640_id,
+};
+
+static int __init ov5640_mod_init(void)
+{
+	return i2c_add_driver(&ov5640_i2c_driver);
+}
+
+static void __exit ov5640_mod_exit(void)
+{
+	i2c_del_driver(&ov5640_i2c_driver);
+}
+
+module_init(ov5640_mod_init);
+module_exit(ov5640_mod_exit);
+
+MODULE_DESCRIPTION("OmniVision OV5640 Camera driver");
+MODULE_AUTHOR("Sergio Aguirre <saaguirre@ti.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/include/media/ov5640.h b/include/media/ov5640.h
new file mode 100644
index 0000000..65625ce
--- /dev/null
+++ b/include/media/ov5640.h
@@ -0,0 +1,10 @@
+#ifndef _MEDIA_OV5640_H
+#define _MEDIA_OV5640_H
+
+#include <media/v4l2-subdev.h>
+
+struct ov5640_platform_data {
+      int (*s_power)(struct v4l2_subdev *subdev, int on);
+};
+
+#endif
diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h
index 810a209..de1d680 100644
--- a/include/media/v4l2-chip-ident.h
+++ b/include/media/v4l2-chip-ident.h
@@ -77,6 +77,7 @@ enum {
 	V4L2_IDENT_OV2640 = 259,
 	V4L2_IDENT_OV9740 = 260,
 	V4L2_IDENT_OV5642 = 261,
+	V4L2_IDENT_OV5640 = 262,
 
 	/* module saa7146: reserved range 300-309 */
 	V4L2_IDENT_SAA7146 = 300,
-- 
1.7.7.4


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

* [PATCH v2 07/11] v4l: Add support for ov5640 sensor
@ 2011-12-01  0:14   ` Sergio Aguirre
  0 siblings, 0 replies; 65+ messages in thread
From: Sergio Aguirre @ 2011-12-01  0:14 UTC (permalink / raw)
  To: linux-media; +Cc: linux-omap, laurent.pinchart, sakari.ailus, Sergio Aguirre

This adds a very limited driver for ov5640, which
only supports:
 - 2592x1944 @ ~7.5 fps
 - 1920x1080 @ ~15 fps,
 - 1280x720 @ ~24 fps,
 - 640x480 @ ~24 fps,
 - 320x240 @ ~24 fps,

All in YUV422i format, using 1 CSI2 datalane @ 333 MHz.

Signed-off-by: Sergio Aguirre <saaguirre@ti.com>
---
 drivers/media/video/Kconfig     |    6 +
 drivers/media/video/Makefile    |    1 +
 drivers/media/video/ov5640.c    |  972 +++++++++++++++++++++++++++++++++++++++
 include/media/ov5640.h          |   10 +
 include/media/v4l2-chip-ident.h |    1 +
 5 files changed, 990 insertions(+), 0 deletions(-)
 create mode 100644 drivers/media/video/ov5640.c
 create mode 100644 include/media/ov5640.h

diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index ae2a99d..3ee4fc9 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -467,6 +467,12 @@ config VIDEO_OV7670
 	  OV7670 VGA camera.  It currently only works with the M88ALP01
 	  controller.
 
+config VIDEO_OV5640
+	tristate "OmniVision OV5640 sensor support"
+	depends on I2C && VIDEO_V4L2
+	help
+	  This is a ov5640 camera driver
+
 config VIDEO_MT9P031
 	tristate "Aptina MT9P031 support"
 	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index f02a4c4..d296c7b 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -63,6 +63,7 @@ obj-$(CONFIG_VIDEO_CX25840) += cx25840/
 obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o
 obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o
 obj-$(CONFIG_VIDEO_OV7670) 	+= ov7670.o
+obj-$(CONFIG_VIDEO_OV5640)	+= ov5640.o
 obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o
 obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o
 obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o
diff --git a/drivers/media/video/ov5640.c b/drivers/media/video/ov5640.c
new file mode 100644
index 0000000..c93802a
--- /dev/null
+++ b/drivers/media/video/ov5640.c
@@ -0,0 +1,972 @@
+/*
+ * OmniVision OV5640 sensor driver
+ *
+ * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/videodev2.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/log2.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+#include <media/ov5640.h>
+
+#define OV5640_BRIGHTNESS_MIN				0
+#define OV5640_BRIGHTNESS_MAX				200
+#define OV5640_BRIGHTNESS_STEP				100
+#define OV5640_BRIGHTNESS_DEF				100
+
+#define OV5640_CONTRAST_MIN				0
+#define OV5640_CONTRAST_MAX				200
+#define OV5640_CONTRAST_STEP				100
+#define OV5640_CONTRAST_DEF				100
+
+/* OV5640 has only one fixed colorspace per pixelcode */
+struct ov5640_datafmt {
+	enum v4l2_mbus_pixelcode	code;
+	enum v4l2_colorspace		colorspace;
+};
+
+struct ov5640_timing_cfg {
+	u16 x_addr_start;
+	u16 y_addr_start;
+	u16 x_addr_end;
+	u16 y_addr_end;
+	u16 h_output_size;
+	u16 v_output_size;
+	u16 h_total_size;
+	u16 v_total_size;
+	u16 isp_h_offset;
+	u16 isp_v_offset;
+	u8 h_odd_ss_inc;
+	u8 h_even_ss_inc;
+	u8 v_odd_ss_inc;
+	u8 v_even_ss_inc;
+};
+
+enum ov5640_size {
+	OV5640_SIZE_QVGA,
+	OV5640_SIZE_VGA,
+	OV5640_SIZE_720P,
+	OV5640_SIZE_1080P,
+	OV5640_SIZE_5MP,
+	OV5640_SIZE_LAST,
+};
+
+static const struct v4l2_frmsize_discrete ov5640_frmsizes[OV5640_SIZE_LAST] = {
+	{  320,  240 },
+	{  640,  480 },
+	{ 1280,  720 },
+	{ 1920, 1080 },
+	{ 2592, 1944 },
+};
+
+/* Find a frame size in an array */
+static int ov5640_find_framesize(u32 width, u32 height)
+{
+	int i;
+
+	for (i = 0; i < OV5640_SIZE_LAST; i++) {
+		if ((ov5640_frmsizes[i].width >= width) &&
+		    (ov5640_frmsizes[i].height >= height))
+			break;
+	}
+
+	/* If not found, select biggest */
+	if (i >= OV5640_SIZE_LAST)
+		i = OV5640_SIZE_LAST - 1;
+
+	return i;
+}
+
+struct ov5640 {
+	struct v4l2_subdev subdev;
+	struct media_pad pad;
+	struct v4l2_mbus_framefmt format;
+	const struct ov5640_platform_data *pdata;
+	int brightness;
+	int contrast;
+	int colorlevel;
+};
+
+static inline struct ov5640 *to_ov5640(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct ov5640, subdev);
+}
+
+/**
+ * struct ov5640_reg - ov5640 register format
+ * @reg: 16-bit offset to register
+ * @val: 8/16/32-bit register value
+ * @length: length of the register
+ *
+ * Define a structure for OV5640 register initialization values
+ */
+struct ov5640_reg {
+	u16	reg;
+	u8	val;
+};
+
+/* TODO: Divide this properly */
+static const struct ov5640_reg configscript_common1[] = {
+	{ 0x3103, 0x03 },
+	{ 0x3017, 0x00 },
+	{ 0x3018, 0x00 },
+	{ 0x3630, 0x2e },
+	{ 0x3632, 0xe2 },
+	{ 0x3633, 0x23 },
+	{ 0x3634, 0x44 },
+	{ 0x3621, 0xe0 },
+	{ 0x3704, 0xa0 },
+	{ 0x3703, 0x5a },
+	{ 0x3715, 0x78 },
+	{ 0x3717, 0x01 },
+	{ 0x370b, 0x60 },
+	{ 0x3705, 0x1a },
+	{ 0x3905, 0x02 },
+	{ 0x3906, 0x10 },
+	{ 0x3901, 0x0a },
+	{ 0x3731, 0x12 },
+	{ 0x3600, 0x04 },
+	{ 0x3601, 0x22 },
+	{ 0x471c, 0x50 },
+	{ 0x3002, 0x1c },
+	{ 0x3006, 0xc3 },
+	{ 0x300e, 0x05 },
+	{ 0x302e, 0x08 },
+	{ 0x3612, 0x4b },
+	{ 0x3618, 0x04 },
+	{ 0x3034, 0x18 },
+	{ 0x3035, 0x11 },
+	{ 0x3036, 0x54 },
+	{ 0x3037, 0x13 },
+	{ 0x3708, 0x21 },
+	{ 0x3709, 0x12 },
+	{ 0x370c, 0x00 },
+};
+
+/* TODO: Divide this properly */
+static const struct ov5640_reg configscript_common2[] = {
+	{ 0x3a02, 0x01 },
+	{ 0x3a03, 0xec },
+	{ 0x3a08, 0x01 },
+	{ 0x3a09, 0x27 },
+	{ 0x3a0a, 0x00 },
+	{ 0x3a0b, 0xf6 },
+	{ 0x3a0e, 0x06 },
+	{ 0x3a0d, 0x08 },
+	{ 0x3a14, 0x01 },
+	{ 0x3a15, 0xec },
+	{ 0x4001, 0x02 },
+	{ 0x4004, 0x06 },
+	{ 0x460b, 0x37 },
+	{ 0x4750, 0x00 },
+	{ 0x4751, 0x00 },
+	{ 0x4800, 0x24 },
+	{ 0x5a00, 0x08 },
+	{ 0x5a21, 0x00 },
+	{ 0x5a24, 0x00 },
+	{ 0x5000, 0x27 },
+	{ 0x5001, 0x87 },
+	{ 0x3820, 0x40 },
+	{ 0x3821, 0x06 },
+	{ 0x3824, 0x01 },
+	{ 0x5481, 0x08 },
+	{ 0x5482, 0x14 },
+	{ 0x5483, 0x28 },
+	{ 0x5484, 0x51 },
+	{ 0x5485, 0x65 },
+	{ 0x5486, 0x71 },
+	{ 0x5487, 0x7d },
+	{ 0x5488, 0x87 },
+	{ 0x5489, 0x91 },
+	{ 0x548a, 0x9a },
+	{ 0x548b, 0xaa },
+	{ 0x548c, 0xb8 },
+	{ 0x548d, 0xcd },
+	{ 0x548e, 0xdd },
+	{ 0x548f, 0xea },
+	{ 0x5490, 0x1d },
+	{ 0x5381, 0x20 },
+	{ 0x5382, 0x64 },
+	{ 0x5383, 0x08 },
+	{ 0x5384, 0x20 },
+	{ 0x5385, 0x80 },
+	{ 0x5386, 0xa0 },
+	{ 0x5387, 0xa2 },
+	{ 0x5388, 0xa0 },
+	{ 0x5389, 0x02 },
+	{ 0x538a, 0x01 },
+	{ 0x538b, 0x98 },
+	{ 0x5300, 0x08 },
+	{ 0x5301, 0x30 },
+	{ 0x5302, 0x10 },
+	{ 0x5303, 0x00 },
+	{ 0x5304, 0x08 },
+	{ 0x5305, 0x30 },
+	{ 0x5306, 0x08 },
+	{ 0x5307, 0x16 },
+	{ 0x5580, 0x00 },
+	{ 0x5587, 0x00 },
+	{ 0x5588, 0x00 },
+	{ 0x5583, 0x40 },
+	{ 0x5584, 0x10 },
+	{ 0x5589, 0x10 },
+	{ 0x558a, 0x00 },
+	{ 0x558b, 0xf8 },
+	{ 0x3a0f, 0x36 },
+	{ 0x3a10, 0x2e },
+	{ 0x3a1b, 0x38 },
+	{ 0x3a1e, 0x2c },
+	{ 0x3a11, 0x70 },
+	{ 0x3a1f, 0x18 },
+	{ 0x3a18, 0x00 },
+	{ 0x3a19, 0xf8 },
+	{ 0x3003, 0x03 },
+	{ 0x3003, 0x01 },
+};
+
+static const struct ov5640_timing_cfg timing_cfg[OV5640_SIZE_LAST] = {
+	[OV5640_SIZE_QVGA] = {
+		.x_addr_start = 0,
+		.y_addr_start = 0,
+		.x_addr_end = 2623,
+		.y_addr_end = 1951,
+		.h_output_size = 320,
+		.v_output_size = 240,
+		.h_total_size = 2844,
+		.v_total_size = 1968,
+		.isp_h_offset = 16,
+		.isp_v_offset = 6,
+		.h_odd_ss_inc = 1,
+		.h_even_ss_inc = 1,
+		.v_odd_ss_inc = 1,
+		.v_even_ss_inc = 1,
+	},
+	[OV5640_SIZE_VGA] = {
+		.x_addr_start = 0,
+		.y_addr_start = 0,
+		.x_addr_end = 2623,
+		.y_addr_end = 1951,
+		.h_output_size = 640,
+		.v_output_size = 480,
+		.h_total_size = 2844,
+		.v_total_size = 1968,
+		.isp_h_offset = 16,
+		.isp_v_offset = 6,
+		.h_odd_ss_inc = 1,
+		.h_even_ss_inc = 1,
+		.v_odd_ss_inc = 1,
+		.v_even_ss_inc = 1,
+	},
+	[OV5640_SIZE_720P] = {
+		.x_addr_start = 336,
+		.y_addr_start = 434,
+		.x_addr_end = 2287,
+		.y_addr_end = 1522,
+		.h_output_size = 1280,
+		.v_output_size = 720,
+		.h_total_size = 2500,
+		.v_total_size = 1120,
+		.isp_h_offset = 16,
+		.isp_v_offset = 4,
+		.h_odd_ss_inc = 1,
+		.h_even_ss_inc = 1,
+		.v_odd_ss_inc = 1,
+		.v_even_ss_inc = 1,
+	},
+	[OV5640_SIZE_1080P] = {
+		.x_addr_start = 336,
+		.y_addr_start = 434,
+		.x_addr_end = 2287,
+		.y_addr_end = 1522,
+		.h_output_size = 1920,
+		.v_output_size = 1080,
+		.h_total_size = 2500,
+		.v_total_size = 1120,
+		.isp_h_offset = 16,
+		.isp_v_offset = 4,
+		.h_odd_ss_inc = 1,
+		.h_even_ss_inc = 1,
+		.v_odd_ss_inc = 1,
+		.v_even_ss_inc = 1,
+	},
+	[OV5640_SIZE_5MP] = {
+		.x_addr_start = 0,
+		.y_addr_start = 0,
+		.x_addr_end = 2623,
+		.y_addr_end = 1951,
+		.h_output_size = 2592,
+		.v_output_size = 1944,
+		.h_total_size = 2844,
+		.v_total_size = 1968,
+		.isp_h_offset = 16,
+		.isp_v_offset = 6,
+		.h_odd_ss_inc = 1,
+		.h_even_ss_inc = 1,
+		.v_odd_ss_inc = 1,
+		.v_even_ss_inc = 1,
+	},
+};
+
+/**
+ * ov5640_reg_read - Read a value from a register in an ov5640 sensor device
+ * @client: i2c driver client structure
+ * @reg: register address / offset
+ * @val: stores the value that gets read
+ *
+ * Read a value from a register in an ov5640 sensor device.
+ * The value is returned in 'val'.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int ov5640_reg_read(struct i2c_client *client, u16 reg, u8 *val)
+{
+	int ret;
+	u8 data[2] = {0};
+	struct i2c_msg msg = {
+		.addr	= client->addr,
+		.flags	= 0,
+		.len	= 2,
+		.buf	= data,
+	};
+
+	data[0] = (u8)(reg >> 8);
+	data[1] = (u8)(reg & 0xff);
+
+	ret = i2c_transfer(client->adapter, &msg, 1);
+	if (ret < 0)
+		goto err;
+
+	msg.flags = I2C_M_RD;
+	msg.len = 1;
+	ret = i2c_transfer(client->adapter, &msg, 1);
+	if (ret < 0)
+		goto err;
+
+	*val = data[0];
+	return 0;
+
+err:
+	dev_err(&client->dev, "Failed reading register 0x%02x!\n", reg);
+	return ret;
+}
+
+/**
+ * Write a value to a register in ov5640 sensor device.
+ * @client: i2c driver client structure.
+ * @reg: Address of the register to read value from.
+ * @val: Value to be written to a specific register.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int ov5640_reg_write(struct i2c_client *client, u16 reg, u8 val)
+{
+	int ret;
+	unsigned char data[3] = { (u8)(reg >> 8), (u8)(reg & 0xff), val };
+	struct i2c_msg msg = {
+		.addr	= client->addr,
+		.flags	= 0,
+		.len	= 3,
+		.buf	= data,
+	};
+
+	ret = i2c_transfer(client->adapter, &msg, 1);
+	if (ret < 0) {
+		dev_err(&client->dev, "Failed writing register 0x%02x!\n", reg);
+		return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * Initialize a list of ov5640 registers.
+ * The list of registers is terminated by the pair of values
+ * @client: i2c driver client structure.
+ * @reglist[]: List of address of the registers to write data.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int ov5640_reg_writes(struct i2c_client *client,
+			     const struct ov5640_reg reglist[],
+			     int size)
+{
+	int err = 0, i;
+
+	for (i = 0; i < size; i++) {
+		err = ov5640_reg_write(client, reglist[i].reg,
+				reglist[i].val);
+		if (err)
+			return err;
+	}
+	return 0;
+}
+
+static int ov5640_reg_set(struct i2c_client *client, u16 reg, u8 val)
+{
+	int ret;
+	u8 tmpval = 0;
+
+	ret = ov5640_reg_read(client, reg, &tmpval);
+	if (ret)
+		return ret;
+
+	return ov5640_reg_write(client, reg, tmpval | val);
+}
+
+static int ov5640_reg_clr(struct i2c_client *client, u16 reg, u8 val)
+{
+	int ret;
+	u8 tmpval = 0;
+
+	ret = ov5640_reg_read(client, reg, &tmpval);
+	if (ret)
+		return ret;
+
+	return ov5640_reg_write(client, reg, tmpval & ~val);
+}
+
+static int ov5640_config_timing(struct v4l2_subdev *sd)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov5640 *ov5640 = to_ov5640(sd);
+	int ret, i;
+
+	i = ov5640_find_framesize(ov5640->format.width, ov5640->format.height);
+
+	ret = ov5640_reg_write(client,
+			0x3800,
+			(timing_cfg[i].x_addr_start & 0xFF00) >> 8);
+	if (ret)
+		return ret;
+
+	ret = ov5640_reg_write(client,
+			0x3801,
+			timing_cfg[i].x_addr_start & 0xFF);
+	if (ret)
+		return ret;
+
+	ret = ov5640_reg_write(client,
+			0x3802,
+			(timing_cfg[i].y_addr_start & 0xFF00) >> 8);
+	if (ret)
+		return ret;
+
+	ret = ov5640_reg_write(client,
+			0x3803,
+			timing_cfg[i].y_addr_start & 0xFF);
+	if (ret)
+		return ret;
+
+	ret = ov5640_reg_write(client,
+			0x3804,
+			(timing_cfg[i].x_addr_end & 0xFF00) >> 8);
+	if (ret)
+		return ret;
+
+	ret = ov5640_reg_write(client,
+			0x3805,
+			timing_cfg[i].x_addr_end & 0xFF);
+	if (ret)
+		return ret;
+
+	ret = ov5640_reg_write(client,
+			0x3806,
+			(timing_cfg[i].y_addr_end & 0xFF00) >> 8);
+	if (ret)
+		return ret;
+
+	ret = ov5640_reg_write(client,
+			0x3807,
+			timing_cfg[i].y_addr_end & 0xFF);
+	if (ret)
+		return ret;
+
+	ret = ov5640_reg_write(client,
+			0x3808,
+			(timing_cfg[i].h_output_size & 0xFF00) >> 8);
+	if (ret)
+		return ret;
+
+	ret = ov5640_reg_write(client,
+			0x3809,
+			timing_cfg[i].h_output_size & 0xFF);
+	if (ret)
+		return ret;
+
+	ret = ov5640_reg_write(client,
+			0x380A,
+			(timing_cfg[i].v_output_size & 0xFF00) >> 8);
+	if (ret)
+		return ret;
+
+	ret = ov5640_reg_write(client,
+			0x380B,
+			timing_cfg[i].v_output_size & 0xFF);
+	if (ret)
+		return ret;
+
+	ret = ov5640_reg_write(client,
+			0x380C,
+			(timing_cfg[i].h_total_size & 0xFF00) >> 8);
+	if (ret)
+		return ret;
+
+	ret = ov5640_reg_write(client,
+			0x380D,
+			timing_cfg[i].h_total_size & 0xFF);
+	if (ret)
+		return ret;
+
+	ret = ov5640_reg_write(client,
+			0x380E,
+			(timing_cfg[i].v_total_size & 0xFF00) >> 8);
+	if (ret)
+		return ret;
+
+	ret = ov5640_reg_write(client,
+			0x380F,
+			timing_cfg[i].v_total_size & 0xFF);
+	if (ret)
+		return ret;
+
+	ret = ov5640_reg_write(client,
+			0x3810,
+			(timing_cfg[i].isp_h_offset & 0xFF00) >> 8);
+	if (ret)
+		return ret;
+
+	ret = ov5640_reg_write(client,
+			0x3811,
+			timing_cfg[i].isp_h_offset & 0xFF);
+	if (ret)
+		return ret;
+
+	ret = ov5640_reg_write(client,
+			0x3812,
+			(timing_cfg[i].isp_v_offset & 0xFF00) >> 8);
+	if (ret)
+		return ret;
+
+	ret = ov5640_reg_write(client,
+			0x3813,
+			timing_cfg[i].isp_v_offset & 0xFF);
+	if (ret)
+		return ret;
+
+	ret = ov5640_reg_write(client,
+			0x3814,
+			((timing_cfg[i].h_odd_ss_inc & 0xF) << 4) |
+			(timing_cfg[i].h_even_ss_inc & 0xF));
+	if (ret)
+		return ret;
+
+	ret = ov5640_reg_write(client,
+			0x3815,
+			((timing_cfg[i].v_odd_ss_inc & 0xF) << 4) |
+			(timing_cfg[i].v_even_ss_inc & 0xF));
+
+	return ret;
+}
+
+static struct v4l2_mbus_framefmt *
+__ov5640_get_pad_format(struct ov5640 *ov5640, struct v4l2_subdev_fh *fh,
+			 unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+	switch (which) {
+	case V4L2_SUBDEV_FORMAT_TRY:
+		return v4l2_subdev_get_try_format(fh, pad);
+	case V4L2_SUBDEV_FORMAT_ACTIVE:
+		return &ov5640->format;
+	default:
+		return NULL;
+	}
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev internal operations
+ */
+
+static int ov5640_s_power(struct v4l2_subdev *sd, int on)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov5640 *ov5640 = to_ov5640(sd);
+	int ret;
+
+	ret = ov5640->pdata->s_power(sd, on);
+	if (ret)
+		goto out;
+
+	if (on) {
+		/* SW Reset */
+		ret = ov5640_reg_set(client, 0x3008, 0x80);
+		if (ret)
+			goto out;
+
+		msleep(2);
+
+		ret = ov5640_reg_clr(client, 0x3008, 0x80);
+		if (ret)
+			goto out;
+
+		/* SW Powerdown */
+		ret = ov5640_reg_set(client, 0x3008, 0x40);
+		if (ret)
+			goto out;
+
+		ret = ov5640_reg_writes(client, configscript_common1,
+				ARRAY_SIZE(configscript_common1));
+		if (ret)
+			goto out;
+
+		ret = ov5640_reg_writes(client, configscript_common2,
+				ARRAY_SIZE(configscript_common2));
+		if (ret)
+			goto out;
+	}
+
+out:
+	return ret;
+}
+
+static struct v4l2_subdev_core_ops ov5640_subdev_core_ops = {
+	.s_power	= ov5640_s_power,
+};
+
+static int ov5640_g_fmt(struct v4l2_subdev *sd,
+			struct v4l2_subdev_fh *fh,
+			struct v4l2_subdev_format *format)
+{
+	struct ov5640 *ov5640 = to_ov5640(sd);
+
+	format->format = *__ov5640_get_pad_format(ov5640, fh, format->pad,
+						   format->which);
+
+	return 0;
+}
+
+static int ov5640_s_fmt(struct v4l2_subdev *sd,
+			struct v4l2_subdev_fh *fh,
+			struct v4l2_subdev_format *format)
+{
+	struct ov5640 *ov5640 = to_ov5640(sd);
+	struct v4l2_mbus_framefmt *__format;
+
+	__format = __ov5640_get_pad_format(ov5640, fh, format->pad,
+					    format->which);
+
+	format->format = *__format;
+
+	return 0;
+}
+
+static int ov5640_enum_fmt(struct v4l2_subdev *subdev,
+			   struct v4l2_subdev_fh *fh,
+			   struct v4l2_subdev_mbus_code_enum *code)
+{
+	if (code->index >= 2)
+		return -EINVAL;
+
+	switch (code->index) {
+	case 0:
+		code->code = V4L2_MBUS_FMT_UYVY8_1X16;
+		break;
+	case 1:
+		code->code = V4L2_MBUS_FMT_YUYV8_1X16;
+		break;
+	}
+	return 0;
+}
+
+static int ov5640_enum_framesizes(struct v4l2_subdev *subdev,
+				   struct v4l2_subdev_fh *fh,
+				   struct v4l2_subdev_frame_size_enum *fse)
+{
+	if ((fse->index >= OV5640_SIZE_LAST) ||
+	    (fse->code != V4L2_MBUS_FMT_UYVY8_1X16 &&
+	     fse->code != V4L2_MBUS_FMT_YUYV8_1X16))
+		return -EINVAL;
+
+	fse->min_width = ov5640_frmsizes[fse->index].width;
+	fse->max_width = fse->min_width;
+	fse->min_height = ov5640_frmsizes[fse->index].height;
+	fse->max_height = fse->min_height;
+
+	return 0;
+}
+
+static int ov5640_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct ov5640 *ov5640 = to_ov5640(sd);
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	int ret = 0;
+
+	if (enable) {
+		u8 fmtreg = 0, fmtmuxreg = 0;
+		int i;
+
+		switch ((u32)ov5640->format.code) {
+		case V4L2_MBUS_FMT_UYVY8_1X16:
+			fmtreg = 0x32;
+			fmtmuxreg = 0;
+			break;
+		case V4L2_MBUS_FMT_YUYV8_1X16:
+			fmtreg = 0x30;
+			fmtmuxreg = 0;
+			break;
+		default:
+			/* This shouldn't happen */
+			ret = -EINVAL;
+			return ret;
+		}
+
+		ret = ov5640_reg_write(client, 0x4300, fmtreg);
+		if (ret)
+			return ret;
+
+		ret = ov5640_reg_write(client, 0x501F, fmtmuxreg);
+		if (ret)
+			return ret;
+
+		ret = ov5640_config_timing(sd);
+		if (ret)
+			return ret;
+
+		i = ov5640_find_framesize(ov5640->format.width, ov5640->format.height);
+		if ((i == OV5640_SIZE_QVGA) ||
+		    (i == OV5640_SIZE_VGA) ||
+		    (i == OV5640_SIZE_720P)) {
+			ret = ov5640_reg_set(client, 0x5001, 0x20);
+			if (ret)
+				return ret;
+			ret = ov5640_reg_write(client, 0x3108,
+					(i == OV5640_SIZE_720P) ? 0x1 : 0);
+		} else {
+			ret = ov5640_reg_clr(client, 0x5001, 0x20);
+			if (ret)
+				return ret;
+			ret = ov5640_reg_write(client, 0x3108, 0x2);
+		}
+
+		ret = ov5640_reg_clr(client, 0x3008, 0x40);
+		if (ret)
+			goto out;
+	} else {
+		u8 tmpreg = 0;
+
+		ret = ov5640_reg_read(client, 0x3008, &tmpreg);
+		if (ret)
+			goto out;
+
+		ret = ov5640_reg_write(client, 0x3008, tmpreg | 0x40);
+		if (ret)
+			goto out;
+	}
+
+out:
+	return ret;
+}
+
+static struct v4l2_subdev_video_ops ov5640_subdev_video_ops = {
+	.s_stream	= ov5640_s_stream,
+};
+
+static struct v4l2_subdev_pad_ops ov5640_subdev_pad_ops = {
+	.enum_mbus_code = ov5640_enum_fmt,
+	.enum_frame_size = ov5640_enum_framesizes,
+	.get_fmt = ov5640_g_fmt,
+	.set_fmt = ov5640_s_fmt,
+};
+
+static int ov5640_g_skip_frames(struct v4l2_subdev *sd, u32 *frames)
+{
+	/* Quantity of initial bad frames to skip. Revisit. */
+	*frames = 3;
+
+	return 0;
+}
+
+static struct v4l2_subdev_sensor_ops ov5640_subdev_sensor_ops = {
+	.g_skip_frames	= ov5640_g_skip_frames,
+};
+
+static struct v4l2_subdev_ops ov5640_subdev_ops = {
+	.core	= &ov5640_subdev_core_ops,
+	.video	= &ov5640_subdev_video_ops,
+	.pad	= &ov5640_subdev_pad_ops,
+	.sensor	= &ov5640_subdev_sensor_ops,
+};
+
+static int ov5640_registered(struct v4l2_subdev *subdev)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(subdev);
+	int ret = 0;
+	u8 revision = 0;
+
+	ret = ov5640_s_power(subdev, 1);
+	if (ret < 0) {
+		dev_err(&client->dev, "OV5640 power up failed\n");
+		return ret;
+	}
+
+	ret = ov5640_reg_read(client, 0x302A, &revision);
+	if (ret) {
+		dev_err(&client->dev, "Failure to detect OV5640 chip\n");
+		goto out;
+	}
+
+	revision &= 0xF;
+
+	dev_info(&client->dev, "Detected a OV5640 chip, revision %x\n",
+		 revision);
+
+out:
+	ov5640_s_power(subdev, 0);
+
+	return ret;
+}
+
+static int ov5640_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
+{
+	struct v4l2_mbus_framefmt *format;
+
+	format = v4l2_subdev_get_try_format(fh, 0);
+	format->code = V4L2_MBUS_FMT_UYVY8_1X16;
+	format->width = ov5640_frmsizes[OV5640_SIZE_VGA].width;
+	format->height = ov5640_frmsizes[OV5640_SIZE_VGA].height;
+	format->field = V4L2_FIELD_NONE;
+	format->colorspace = V4L2_COLORSPACE_JPEG;
+
+	return 0;
+}
+
+static int ov5640_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
+{
+	return 0;
+}
+
+static struct v4l2_subdev_internal_ops ov5640_subdev_internal_ops = {
+	.registered = ov5640_registered,
+	.open = ov5640_open,
+	.close = ov5640_close,
+};
+
+static int ov5640_init(struct v4l2_subdev *sd)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov5640 *ov5640 = to_ov5640(sd);
+	int ret = 0;
+
+	/* default brightness and contrast */
+	ov5640->brightness = OV5640_BRIGHTNESS_DEF;
+	ov5640->contrast = OV5640_CONTRAST_DEF;
+
+	ov5640->colorlevel = V4L2_COLORFX_NONE;
+
+	dev_dbg(&client->dev, "Sensor initialized\n");
+
+	return ret;
+}
+
+static int ov5640_probe(struct i2c_client *client,
+			 const struct i2c_device_id *did)
+{
+	struct ov5640 *ov5640;
+	int ret;
+
+	if (!client->dev.platform_data) {
+		dev_err(&client->dev, "No platform data!!\n");
+		return -ENODEV;
+	}
+
+	ov5640 = kzalloc(sizeof(*ov5640), GFP_KERNEL);
+	if (!ov5640)
+		return -ENOMEM;
+
+	ov5640->pdata = client->dev.platform_data;
+
+	ov5640->format.code = V4L2_MBUS_FMT_UYVY8_1X16;
+	ov5640->format.width = ov5640_frmsizes[OV5640_SIZE_VGA].width;
+	ov5640->format.height = ov5640_frmsizes[OV5640_SIZE_VGA].height;
+	ov5640->format.field = V4L2_FIELD_NONE;
+	ov5640->format.colorspace = V4L2_COLORSPACE_JPEG;
+
+	v4l2_i2c_subdev_init(&ov5640->subdev, client, &ov5640_subdev_ops);
+	ov5640->subdev.internal_ops = &ov5640_subdev_internal_ops;
+	ov5640->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+	ov5640->pad.flags = MEDIA_PAD_FL_SOURCE;
+	ret = media_entity_init(&ov5640->subdev.entity, 1, &ov5640->pad, 0);
+	if (ret < 0)
+		goto err_mediainit;
+
+	/* init the sensor here */
+	ret = ov5640_init(&ov5640->subdev);
+	if (ret) {
+		dev_err(&client->dev, "Failed to initialize sensor\n");
+		goto err_sensorinit;
+	}
+
+	return ret;
+err_sensorinit:
+	v4l2_device_unregister_subdev(&ov5640->subdev);
+err_mediainit:
+	media_entity_cleanup(&ov5640->subdev.entity);
+	kfree(ov5640);
+	return ret;
+}
+
+static int ov5640_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+	struct ov5640 *ov5640 = to_ov5640(subdev);
+
+	v4l2_device_unregister_subdev(subdev);
+	media_entity_cleanup(&subdev->entity);
+	kfree(ov5640);
+	return 0;
+}
+
+static const struct i2c_device_id ov5640_id[] = {
+	{ "ov5640", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ov5640_id);
+
+static struct i2c_driver ov5640_i2c_driver = {
+	.driver = {
+		.name = "ov5640",
+	},
+	.probe		= ov5640_probe,
+	.remove		= ov5640_remove,
+	.id_table	= ov5640_id,
+};
+
+static int __init ov5640_mod_init(void)
+{
+	return i2c_add_driver(&ov5640_i2c_driver);
+}
+
+static void __exit ov5640_mod_exit(void)
+{
+	i2c_del_driver(&ov5640_i2c_driver);
+}
+
+module_init(ov5640_mod_init);
+module_exit(ov5640_mod_exit);
+
+MODULE_DESCRIPTION("OmniVision OV5640 Camera driver");
+MODULE_AUTHOR("Sergio Aguirre <saaguirre@ti.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/include/media/ov5640.h b/include/media/ov5640.h
new file mode 100644
index 0000000..65625ce
--- /dev/null
+++ b/include/media/ov5640.h
@@ -0,0 +1,10 @@
+#ifndef _MEDIA_OV5640_H
+#define _MEDIA_OV5640_H
+
+#include <media/v4l2-subdev.h>
+
+struct ov5640_platform_data {
+      int (*s_power)(struct v4l2_subdev *subdev, int on);
+};
+
+#endif
diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h
index 810a209..de1d680 100644
--- a/include/media/v4l2-chip-ident.h
+++ b/include/media/v4l2-chip-ident.h
@@ -77,6 +77,7 @@ enum {
 	V4L2_IDENT_OV2640 = 259,
 	V4L2_IDENT_OV9740 = 260,
 	V4L2_IDENT_OV5642 = 261,
+	V4L2_IDENT_OV5640 = 262,
 
 	/* module saa7146: reserved range 300-309 */
 	V4L2_IDENT_SAA7146 = 300,
-- 
1.7.7.4


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

* [PATCH v2 08/11] v4l: Add support for ov5650 sensor
  2011-12-01  0:14 ` Sergio Aguirre
@ 2011-12-01  0:14   ` Sergio Aguirre
  -1 siblings, 0 replies; 65+ messages in thread
From: Sergio Aguirre @ 2011-12-01  0:14 UTC (permalink / raw)
  To: linux-media; +Cc: linux-omap, laurent.pinchart, sakari.ailus, Sergio Aguirre

Signed-off-by: Sergio Aguirre <saaguirre@ti.com>
---
 drivers/media/video/Kconfig     |    6 +
 drivers/media/video/Makefile    |    1 +
 drivers/media/video/ov5650.c    |  524 +++++++++++++++++++++++++++++++++++++++
 include/media/ov5650.h          |   10 +
 include/media/v4l2-chip-ident.h |    1 +
 5 files changed, 542 insertions(+), 0 deletions(-)
 create mode 100644 drivers/media/video/ov5650.c
 create mode 100644 include/media/ov5650.h

diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 3ee4fc9..46076a9 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -473,6 +473,12 @@ config VIDEO_OV5640
 	help
 	  This is a ov5640 camera driver
 
+config VIDEO_OV5650
+	tristate "OmniVision OV5650 sensor support"
+	depends on I2C && VIDEO_V4L2
+	help
+	  This is a ov5650 camera driver
+
 config VIDEO_MT9P031
 	tristate "Aptina MT9P031 support"
 	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index d296c7b..9061cfc 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -64,6 +64,7 @@ obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o
 obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o
 obj-$(CONFIG_VIDEO_OV7670) 	+= ov7670.o
 obj-$(CONFIG_VIDEO_OV5640)	+= ov5640.o
+obj-$(CONFIG_VIDEO_OV5650)	+= ov5650.o
 obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o
 obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o
 obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o
diff --git a/drivers/media/video/ov5650.c b/drivers/media/video/ov5650.c
new file mode 100644
index 0000000..092d8bf
--- /dev/null
+++ b/drivers/media/video/ov5650.c
@@ -0,0 +1,524 @@
+/*
+ * OmniVision OV5650 sensor driver
+ *
+ * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/videodev2.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/log2.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+#include <media/ov5650.h>
+
+/* OV5650 has only one fixed colorspace per pixelcode */
+struct ov5650_datafmt {
+	enum v4l2_mbus_pixelcode	code;
+	enum v4l2_colorspace		colorspace;
+};
+
+enum ov5650_size {
+	OV5650_SIZE_5MP,
+	OV5650_SIZE_LAST,
+};
+
+static const struct v4l2_frmsize_discrete ov5650_frmsizes[OV5650_SIZE_LAST] = {
+	{ 2592, 1944 },
+};
+
+struct ov5650 {
+	struct v4l2_subdev subdev;
+	struct media_pad pad;
+
+	struct v4l2_mbus_framefmt format;
+
+	const struct ov5650_platform_data *pdata;
+};
+
+static inline struct ov5650 *to_ov5650(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct ov5650, subdev);
+}
+
+/**
+ * struct ov5650_reg - ov5650 register format
+ * @reg: 16-bit offset to register
+ * @val: 8/16/32-bit register value
+ * @length: length of the register
+ *
+ * Define a structure for OV5650 register initialization values
+ */
+struct ov5650_reg {
+	u16	reg;
+	u8	val;
+};
+
+/* TODO: Divide this properly */
+static const struct ov5650_reg configscript_5MP[] = {
+	{ 0x3008, 0x82 },
+	{ 0x3008, 0x42 },
+	{ 0x3103, 0x93 },
+	{ 0x3b07, 0x0c },
+	{ 0x3017, 0xff },
+	{ 0x3018, 0xfc },
+	{ 0x3706, 0x41 },
+	{ 0x3703, 0xe6 },
+	{ 0x3613, 0x44 },
+	{ 0x3630, 0x22 },
+	{ 0x3605, 0x04 },
+	{ 0x3606, 0x3f },
+	{ 0x3712, 0x13 },
+	{ 0x370e, 0x00 },
+	{ 0x370b, 0x40 },
+	{ 0x3600, 0x54 },
+	{ 0x3601, 0x05 },
+	{ 0x3713, 0x22 },
+	{ 0x3714, 0x27 },
+	{ 0x3631, 0x22 },
+	{ 0x3612, 0x1a },
+	{ 0x3604, 0x40 },
+	{ 0x3705, 0xda },
+	{ 0x370a, 0x80 },
+	{ 0x370c, 0x00 },
+	{ 0x3710, 0x28 },
+	{ 0x3702, 0x3a },
+	{ 0x3704, 0x18 },
+	{ 0x3a18, 0x00 },
+	{ 0x3a19, 0xf8 },
+	{ 0x3a00, 0x38 },
+	{ 0x3800, 0x02 },
+	{ 0x3801, 0x54 },
+	{ 0x3803, 0x0c },
+	{ 0x3808, 0x0a },
+	{ 0x3809, 0x20 },
+	{ 0x380a, 0x07 },
+	{ 0x380b, 0x98 },
+	{ 0x380c, 0x0c },
+	{ 0x380d, 0xb4 },
+	{ 0x380e, 0x07 },
+	{ 0x380f, 0xb0 },
+	{ 0x3830, 0x50 },
+	{ 0x3a08, 0x12 },
+	{ 0x3a09, 0x70 },
+	{ 0x3a0a, 0x0f },
+	{ 0x3a0b, 0x60 },
+	{ 0x3a0d, 0x06 },
+	{ 0x3a0e, 0x06 },
+	{ 0x3a13, 0x54 },
+	{ 0x3815, 0x82 },
+	{ 0x5059, 0x80 },
+	{ 0x505a, 0x0a },
+	{ 0x505b, 0x2e },
+	{ 0x3a1a, 0x06 },
+	{ 0x3503, 0x00 },
+	{ 0x3623, 0x01 },
+	{ 0x3633, 0x24 },
+	{ 0x3c01, 0x34 },
+	{ 0x3c04, 0x28 },
+	{ 0x3c05, 0x98 },
+	{ 0x3c07, 0x07 },
+	{ 0x3c09, 0xc2 },
+	{ 0x4000, 0x05 },
+	{ 0x401d, 0x28 },
+	{ 0x4001, 0x02 },
+	{ 0x401c, 0x46 },
+	{ 0x5046, 0x01 },
+	{ 0x3810, 0x40 },
+	{ 0x3836, 0x41 },
+	{ 0x505f, 0x04 },
+	{ 0x5000, 0x00 },
+	{ 0x5001, 0x00 },
+	{ 0x5002, 0x00 },
+	{ 0x503d, 0x00 },
+	{ 0x5901, 0x00 },
+	{ 0x585a, 0x01 },
+	{ 0x585b, 0x2c },
+	{ 0x585c, 0x01 },
+	{ 0x585d, 0x93 },
+	{ 0x585e, 0x01 },
+	{ 0x585f, 0x90 },
+	{ 0x5860, 0x01 },
+	{ 0x5861, 0x0d },
+	{ 0x5180, 0xc0 },
+	{ 0x5184, 0x00 },
+	{ 0x470a, 0x00 },
+	{ 0x470b, 0x00 },
+	{ 0x470c, 0x00 },
+	{ 0x300f, 0x8e },
+	{ 0x3603, 0xa7 },
+	{ 0x3615, 0x50 },
+	{ 0x3632, 0x55 },
+	{ 0x3620, 0x56 },
+	{ 0x3621, 0x2f },
+	{ 0x381a, 0x3c },
+	{ 0x3818, 0xc0 },
+	{ 0x3631, 0x36 },
+	{ 0x3632, 0x5f },
+	{ 0x3711, 0x24 },
+	{ 0x401f, 0x03 },
+	{ 0x3011, 0x14 },
+	{ 0x3007, 0x3b },
+	{ 0x4801, 0x0f },
+	{ 0x3003, 0x03 },
+	{ 0x300e, 0x0c },
+	{ 0x4803, 0x50 },
+	{ 0x4800, 0x04 },
+	{ 0x300f, 0x8f },
+	{ 0x3815, 0x82 },
+	{ 0x3003, 0x01 },
+	{ 0x3008, 0x02 },
+};
+
+/**
+ * ov5650_reg_read - Read a value from a register in an ov5650 sensor device
+ * @client: i2c driver client structure
+ * @reg: register address / offset
+ * @val: stores the value that gets read
+ *
+ * Read a value from a register in an ov5650 sensor device.
+ * The value is returned in 'val'.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int ov5650_reg_read(struct i2c_client *client, u16 reg, u8 *val)
+{
+	int ret;
+	u8 data[2] = {0};
+	struct i2c_msg msg = {
+		.addr	= client->addr,
+		.flags	= 0,
+		.len	= 2,
+		.buf	= data,
+	};
+
+	data[0] = (u8)(reg >> 8);
+	data[1] = (u8)(reg & 0xff);
+
+	ret = i2c_transfer(client->adapter, &msg, 1);
+	if (ret < 0)
+		goto err;
+
+	msg.flags = I2C_M_RD;
+	msg.len = 1;
+	ret = i2c_transfer(client->adapter, &msg, 1);
+	if (ret < 0)
+		goto err;
+
+	*val = data[0];
+	return 0;
+
+err:
+	dev_err(&client->dev, "Failed reading register 0x%02x!\n", reg);
+	return ret;
+}
+
+/**
+ * Write a value to a register in ov5650 sensor device.
+ * @client: i2c driver client structure.
+ * @reg: Address of the register to read value from.
+ * @val: Value to be written to a specific register.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int ov5650_reg_write(struct i2c_client *client, u16 reg, u8 val)
+{
+	int ret;
+	unsigned char data[3] = { (u8)(reg >> 8), (u8)(reg & 0xff), val };
+	struct i2c_msg msg = {
+		.addr	= client->addr,
+		.flags	= 0,
+		.len	= 3,
+		.buf	= data,
+	};
+
+	ret = i2c_transfer(client->adapter, &msg, 1);
+	if (ret < 0) {
+		dev_err(&client->dev, "Failed writing register 0x%02x!\n", reg);
+		return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * Initialize a list of ov5650 registers.
+ * The list of registers is terminated by the pair of values
+ * @client: i2c driver client structure.
+ * @reglist[]: List of address of the registers to write data.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int ov5650_reg_writes(struct i2c_client *client,
+			     const struct ov5650_reg reglist[],
+			     int size)
+{
+	int err = 0, i;
+
+	for (i = 0; i < size; i++) {
+		err = ov5650_reg_write(client, reglist[i].reg,
+				reglist[i].val);
+		if (err)
+			return err;
+	}
+	return 0;
+}
+
+static struct v4l2_mbus_framefmt *
+__ov5650_get_pad_format(struct ov5650 *ov5650, struct v4l2_subdev_fh *fh,
+			 unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+	switch (which) {
+	case V4L2_SUBDEV_FORMAT_TRY:
+		return v4l2_subdev_get_try_format(fh, pad);
+	case V4L2_SUBDEV_FORMAT_ACTIVE:
+		return &ov5650->format;
+	default:
+		return NULL;
+	}
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev internal operations
+ */
+
+static int ov5650_s_power(struct v4l2_subdev *sd, int on)
+{
+	struct ov5650 *ov5650 = to_ov5650(sd);
+
+	return ov5650->pdata->s_power(sd, on);
+}
+
+static int ov5650_registered(struct v4l2_subdev *subdev)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(subdev);
+	int ret = 0;
+	u8 revision = 0;
+
+	ret = ov5650_s_power(subdev, 1);
+	if (ret < 0) {
+		dev_err(&client->dev, "OV5650 power up failed\n");
+		return ret;
+	}
+
+	ret = ov5650_reg_read(client, 0x302A, &revision);
+	if (ret) {
+		dev_err(&client->dev, "Failure to detect OV5650 chip\n");
+		goto out;
+	}
+
+	revision &= 0xF;
+
+	dev_info(&client->dev, "Detected a OV5650 chip, revision %x\n",
+		 revision);
+
+out:
+	ov5650_s_power(subdev, 0);
+
+	return ret;
+}
+
+static int ov5650_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
+{
+	struct v4l2_mbus_framefmt *format;
+
+	format = v4l2_subdev_get_try_format(fh, 0);
+	format->code = V4L2_MBUS_FMT_SGRBG10_1X10;
+	format->width = ov5650_frmsizes[OV5650_SIZE_5MP].width;
+	format->height = ov5650_frmsizes[OV5650_SIZE_5MP].height;
+	format->field = V4L2_FIELD_NONE;
+	format->colorspace = V4L2_COLORSPACE_SRGB;
+
+	return 0;
+}
+
+static int ov5650_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
+{
+	return 0;
+}
+
+static int ov5650_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	int ret = 0;
+
+	if (enable) {
+		/* HACK: Hardcoding to 5MP! */
+		ret = ov5650_reg_writes(client, configscript_5MP,
+				ARRAY_SIZE(configscript_5MP));
+	}
+
+	return ret;
+}
+
+static int ov5650_g_fmt(struct v4l2_subdev *sd,
+			struct v4l2_subdev_fh *fh,
+			struct v4l2_subdev_format *format)
+{
+	struct ov5650 *ov5650 = to_ov5650(sd);
+
+	format->format = *__ov5650_get_pad_format(ov5650, fh, format->pad,
+						   format->which);
+
+	return 0;
+}
+
+static int ov5650_s_fmt(struct v4l2_subdev *sd,
+			struct v4l2_subdev_fh *fh,
+			struct v4l2_subdev_format *format)
+{
+	struct ov5650 *ov5650 = to_ov5650(sd);
+	struct v4l2_mbus_framefmt *__format;
+
+	__format = __ov5650_get_pad_format(ov5650, fh, format->pad,
+					    format->which);
+
+	/* FIXME: Do an actual change! */
+	format->format = *__format;
+
+	return 0;
+}
+
+static struct v4l2_subdev_internal_ops ov5650_subdev_internal_ops = {
+	.registered = ov5650_registered,
+	.open = ov5650_open,
+	.close = ov5650_close,
+};
+
+static int ov5650_enum_fmt(struct v4l2_subdev *subdev,
+			   struct v4l2_subdev_fh *fh,
+			   struct v4l2_subdev_mbus_code_enum *code)
+{
+	if (code->index > 0)
+		return -EINVAL;
+
+	code->code = V4L2_MBUS_FMT_SGRBG10_1X10;
+	return 0;
+}
+
+static int ov5650_enum_framesizes(struct v4l2_subdev *subdev,
+				   struct v4l2_subdev_fh *fh,
+				   struct v4l2_subdev_frame_size_enum *fse)
+{
+	if (fse->index >= 1 || fse->code != V4L2_MBUS_FMT_SGRBG10_1X10)
+		return -EINVAL;
+
+	fse->min_width = ov5650_frmsizes[fse->index].width;
+	fse->max_width = fse->min_width;
+	fse->min_height = ov5650_frmsizes[fse->index].height;
+	fse->max_height = fse->min_height;
+
+	return 0;
+}
+
+static struct v4l2_subdev_core_ops ov5650_subdev_core_ops = {
+	.s_power	= ov5650_s_power,
+};
+
+static struct v4l2_subdev_video_ops ov5650_subdev_video_ops = {
+	.s_stream	= ov5650_s_stream,
+};
+
+static struct v4l2_subdev_pad_ops ov5650_subdev_pad_ops = {
+	.enum_mbus_code = ov5650_enum_fmt,
+	.enum_frame_size = ov5650_enum_framesizes,
+	.get_fmt = ov5650_g_fmt,
+	.set_fmt = ov5650_s_fmt,
+};
+
+static struct v4l2_subdev_ops ov5650_subdev_ops = {
+	.core	= &ov5650_subdev_core_ops,
+	.video	= &ov5650_subdev_video_ops,
+	.pad	= &ov5650_subdev_pad_ops,
+};
+
+static int ov5650_probe(struct i2c_client *client,
+			 const struct i2c_device_id *did)
+{
+	struct ov5650 *ov5650;
+	int ret;
+
+	if (!client->dev.platform_data) {
+		dev_err(&client->dev, "No platform data!!\n");
+		return -ENODEV;
+	}
+
+	ov5650 = kzalloc(sizeof(*ov5650), GFP_KERNEL);
+	if (!ov5650)
+		return -ENOMEM;
+
+	ov5650->pdata = client->dev.platform_data;
+
+	ov5650->format.code = V4L2_MBUS_FMT_SGRBG10_1X10;
+	ov5650->format.width = ov5650_frmsizes[OV5650_SIZE_5MP].width;
+	ov5650->format.height = ov5650_frmsizes[OV5650_SIZE_5MP].height;
+	ov5650->format.field = V4L2_FIELD_NONE;
+	ov5650->format.colorspace = V4L2_COLORSPACE_SRGB;
+
+	v4l2_i2c_subdev_init(&ov5650->subdev, client, &ov5650_subdev_ops);
+	ov5650->subdev.internal_ops = &ov5650_subdev_internal_ops;
+	ov5650->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+	ov5650->pad.flags = MEDIA_PAD_FL_SOURCE;
+	ret = media_entity_init(&ov5650->subdev.entity, 1, &ov5650->pad, 0);
+
+	if (ret < 0) {
+		media_entity_cleanup(&ov5650->subdev.entity);
+		kfree(ov5650);
+	}
+
+	return ret;
+}
+
+static int ov5650_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+	struct ov5650 *ov5650 = to_ov5650(subdev);
+
+	v4l2_device_unregister_subdev(subdev);
+	media_entity_cleanup(&subdev->entity);
+	kfree(ov5650);
+	return 0;
+}
+
+static const struct i2c_device_id ov5650_id[] = {
+	{ "ov5650", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ov5650_id);
+
+static struct i2c_driver ov5650_i2c_driver = {
+	.driver = {
+		.name = "ov5650",
+	},
+	.probe		= ov5650_probe,
+	.remove		= ov5650_remove,
+	.id_table	= ov5650_id,
+};
+
+static int __init ov5650_mod_init(void)
+{
+	return i2c_add_driver(&ov5650_i2c_driver);
+}
+
+static void __exit ov5650_mod_exit(void)
+{
+	i2c_del_driver(&ov5650_i2c_driver);
+}
+
+module_init(ov5650_mod_init);
+module_exit(ov5650_mod_exit);
+
+MODULE_DESCRIPTION("OmniVision OV5650 Camera driver");
+MODULE_AUTHOR("Sergio Aguirre <saaguirre@ti.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/include/media/ov5650.h b/include/media/ov5650.h
new file mode 100644
index 0000000..287f20f
--- /dev/null
+++ b/include/media/ov5650.h
@@ -0,0 +1,10 @@
+#ifndef _MEDIA_OV5650_H
+#define _MEDIA_OV5650_H
+
+#include <media/v4l2-subdev.h>
+
+struct ov5650_platform_data {
+      int (*s_power)(struct v4l2_subdev *subdev, int on);
+};
+
+#endif
diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h
index de1d680..f76c8be 100644
--- a/include/media/v4l2-chip-ident.h
+++ b/include/media/v4l2-chip-ident.h
@@ -78,6 +78,7 @@ enum {
 	V4L2_IDENT_OV9740 = 260,
 	V4L2_IDENT_OV5642 = 261,
 	V4L2_IDENT_OV5640 = 262,
+	V4L2_IDENT_OV5650 = 263,
 
 	/* module saa7146: reserved range 300-309 */
 	V4L2_IDENT_SAA7146 = 300,
-- 
1.7.7.4


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

* [PATCH v2 08/11] v4l: Add support for ov5650 sensor
@ 2011-12-01  0:14   ` Sergio Aguirre
  0 siblings, 0 replies; 65+ messages in thread
From: Sergio Aguirre @ 2011-12-01  0:14 UTC (permalink / raw)
  To: linux-media; +Cc: linux-omap, laurent.pinchart, sakari.ailus, Sergio Aguirre

Signed-off-by: Sergio Aguirre <saaguirre@ti.com>
---
 drivers/media/video/Kconfig     |    6 +
 drivers/media/video/Makefile    |    1 +
 drivers/media/video/ov5650.c    |  524 +++++++++++++++++++++++++++++++++++++++
 include/media/ov5650.h          |   10 +
 include/media/v4l2-chip-ident.h |    1 +
 5 files changed, 542 insertions(+), 0 deletions(-)
 create mode 100644 drivers/media/video/ov5650.c
 create mode 100644 include/media/ov5650.h

diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 3ee4fc9..46076a9 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -473,6 +473,12 @@ config VIDEO_OV5640
 	help
 	  This is a ov5640 camera driver
 
+config VIDEO_OV5650
+	tristate "OmniVision OV5650 sensor support"
+	depends on I2C && VIDEO_V4L2
+	help
+	  This is a ov5650 camera driver
+
 config VIDEO_MT9P031
 	tristate "Aptina MT9P031 support"
 	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index d296c7b..9061cfc 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -64,6 +64,7 @@ obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o
 obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o
 obj-$(CONFIG_VIDEO_OV7670) 	+= ov7670.o
 obj-$(CONFIG_VIDEO_OV5640)	+= ov5640.o
+obj-$(CONFIG_VIDEO_OV5650)	+= ov5650.o
 obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o
 obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o
 obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o
diff --git a/drivers/media/video/ov5650.c b/drivers/media/video/ov5650.c
new file mode 100644
index 0000000..092d8bf
--- /dev/null
+++ b/drivers/media/video/ov5650.c
@@ -0,0 +1,524 @@
+/*
+ * OmniVision OV5650 sensor driver
+ *
+ * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/videodev2.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/log2.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+#include <media/ov5650.h>
+
+/* OV5650 has only one fixed colorspace per pixelcode */
+struct ov5650_datafmt {
+	enum v4l2_mbus_pixelcode	code;
+	enum v4l2_colorspace		colorspace;
+};
+
+enum ov5650_size {
+	OV5650_SIZE_5MP,
+	OV5650_SIZE_LAST,
+};
+
+static const struct v4l2_frmsize_discrete ov5650_frmsizes[OV5650_SIZE_LAST] = {
+	{ 2592, 1944 },
+};
+
+struct ov5650 {
+	struct v4l2_subdev subdev;
+	struct media_pad pad;
+
+	struct v4l2_mbus_framefmt format;
+
+	const struct ov5650_platform_data *pdata;
+};
+
+static inline struct ov5650 *to_ov5650(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct ov5650, subdev);
+}
+
+/**
+ * struct ov5650_reg - ov5650 register format
+ * @reg: 16-bit offset to register
+ * @val: 8/16/32-bit register value
+ * @length: length of the register
+ *
+ * Define a structure for OV5650 register initialization values
+ */
+struct ov5650_reg {
+	u16	reg;
+	u8	val;
+};
+
+/* TODO: Divide this properly */
+static const struct ov5650_reg configscript_5MP[] = {
+	{ 0x3008, 0x82 },
+	{ 0x3008, 0x42 },
+	{ 0x3103, 0x93 },
+	{ 0x3b07, 0x0c },
+	{ 0x3017, 0xff },
+	{ 0x3018, 0xfc },
+	{ 0x3706, 0x41 },
+	{ 0x3703, 0xe6 },
+	{ 0x3613, 0x44 },
+	{ 0x3630, 0x22 },
+	{ 0x3605, 0x04 },
+	{ 0x3606, 0x3f },
+	{ 0x3712, 0x13 },
+	{ 0x370e, 0x00 },
+	{ 0x370b, 0x40 },
+	{ 0x3600, 0x54 },
+	{ 0x3601, 0x05 },
+	{ 0x3713, 0x22 },
+	{ 0x3714, 0x27 },
+	{ 0x3631, 0x22 },
+	{ 0x3612, 0x1a },
+	{ 0x3604, 0x40 },
+	{ 0x3705, 0xda },
+	{ 0x370a, 0x80 },
+	{ 0x370c, 0x00 },
+	{ 0x3710, 0x28 },
+	{ 0x3702, 0x3a },
+	{ 0x3704, 0x18 },
+	{ 0x3a18, 0x00 },
+	{ 0x3a19, 0xf8 },
+	{ 0x3a00, 0x38 },
+	{ 0x3800, 0x02 },
+	{ 0x3801, 0x54 },
+	{ 0x3803, 0x0c },
+	{ 0x3808, 0x0a },
+	{ 0x3809, 0x20 },
+	{ 0x380a, 0x07 },
+	{ 0x380b, 0x98 },
+	{ 0x380c, 0x0c },
+	{ 0x380d, 0xb4 },
+	{ 0x380e, 0x07 },
+	{ 0x380f, 0xb0 },
+	{ 0x3830, 0x50 },
+	{ 0x3a08, 0x12 },
+	{ 0x3a09, 0x70 },
+	{ 0x3a0a, 0x0f },
+	{ 0x3a0b, 0x60 },
+	{ 0x3a0d, 0x06 },
+	{ 0x3a0e, 0x06 },
+	{ 0x3a13, 0x54 },
+	{ 0x3815, 0x82 },
+	{ 0x5059, 0x80 },
+	{ 0x505a, 0x0a },
+	{ 0x505b, 0x2e },
+	{ 0x3a1a, 0x06 },
+	{ 0x3503, 0x00 },
+	{ 0x3623, 0x01 },
+	{ 0x3633, 0x24 },
+	{ 0x3c01, 0x34 },
+	{ 0x3c04, 0x28 },
+	{ 0x3c05, 0x98 },
+	{ 0x3c07, 0x07 },
+	{ 0x3c09, 0xc2 },
+	{ 0x4000, 0x05 },
+	{ 0x401d, 0x28 },
+	{ 0x4001, 0x02 },
+	{ 0x401c, 0x46 },
+	{ 0x5046, 0x01 },
+	{ 0x3810, 0x40 },
+	{ 0x3836, 0x41 },
+	{ 0x505f, 0x04 },
+	{ 0x5000, 0x00 },
+	{ 0x5001, 0x00 },
+	{ 0x5002, 0x00 },
+	{ 0x503d, 0x00 },
+	{ 0x5901, 0x00 },
+	{ 0x585a, 0x01 },
+	{ 0x585b, 0x2c },
+	{ 0x585c, 0x01 },
+	{ 0x585d, 0x93 },
+	{ 0x585e, 0x01 },
+	{ 0x585f, 0x90 },
+	{ 0x5860, 0x01 },
+	{ 0x5861, 0x0d },
+	{ 0x5180, 0xc0 },
+	{ 0x5184, 0x00 },
+	{ 0x470a, 0x00 },
+	{ 0x470b, 0x00 },
+	{ 0x470c, 0x00 },
+	{ 0x300f, 0x8e },
+	{ 0x3603, 0xa7 },
+	{ 0x3615, 0x50 },
+	{ 0x3632, 0x55 },
+	{ 0x3620, 0x56 },
+	{ 0x3621, 0x2f },
+	{ 0x381a, 0x3c },
+	{ 0x3818, 0xc0 },
+	{ 0x3631, 0x36 },
+	{ 0x3632, 0x5f },
+	{ 0x3711, 0x24 },
+	{ 0x401f, 0x03 },
+	{ 0x3011, 0x14 },
+	{ 0x3007, 0x3b },
+	{ 0x4801, 0x0f },
+	{ 0x3003, 0x03 },
+	{ 0x300e, 0x0c },
+	{ 0x4803, 0x50 },
+	{ 0x4800, 0x04 },
+	{ 0x300f, 0x8f },
+	{ 0x3815, 0x82 },
+	{ 0x3003, 0x01 },
+	{ 0x3008, 0x02 },
+};
+
+/**
+ * ov5650_reg_read - Read a value from a register in an ov5650 sensor device
+ * @client: i2c driver client structure
+ * @reg: register address / offset
+ * @val: stores the value that gets read
+ *
+ * Read a value from a register in an ov5650 sensor device.
+ * The value is returned in 'val'.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int ov5650_reg_read(struct i2c_client *client, u16 reg, u8 *val)
+{
+	int ret;
+	u8 data[2] = {0};
+	struct i2c_msg msg = {
+		.addr	= client->addr,
+		.flags	= 0,
+		.len	= 2,
+		.buf	= data,
+	};
+
+	data[0] = (u8)(reg >> 8);
+	data[1] = (u8)(reg & 0xff);
+
+	ret = i2c_transfer(client->adapter, &msg, 1);
+	if (ret < 0)
+		goto err;
+
+	msg.flags = I2C_M_RD;
+	msg.len = 1;
+	ret = i2c_transfer(client->adapter, &msg, 1);
+	if (ret < 0)
+		goto err;
+
+	*val = data[0];
+	return 0;
+
+err:
+	dev_err(&client->dev, "Failed reading register 0x%02x!\n", reg);
+	return ret;
+}
+
+/**
+ * Write a value to a register in ov5650 sensor device.
+ * @client: i2c driver client structure.
+ * @reg: Address of the register to read value from.
+ * @val: Value to be written to a specific register.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int ov5650_reg_write(struct i2c_client *client, u16 reg, u8 val)
+{
+	int ret;
+	unsigned char data[3] = { (u8)(reg >> 8), (u8)(reg & 0xff), val };
+	struct i2c_msg msg = {
+		.addr	= client->addr,
+		.flags	= 0,
+		.len	= 3,
+		.buf	= data,
+	};
+
+	ret = i2c_transfer(client->adapter, &msg, 1);
+	if (ret < 0) {
+		dev_err(&client->dev, "Failed writing register 0x%02x!\n", reg);
+		return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * Initialize a list of ov5650 registers.
+ * The list of registers is terminated by the pair of values
+ * @client: i2c driver client structure.
+ * @reglist[]: List of address of the registers to write data.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int ov5650_reg_writes(struct i2c_client *client,
+			     const struct ov5650_reg reglist[],
+			     int size)
+{
+	int err = 0, i;
+
+	for (i = 0; i < size; i++) {
+		err = ov5650_reg_write(client, reglist[i].reg,
+				reglist[i].val);
+		if (err)
+			return err;
+	}
+	return 0;
+}
+
+static struct v4l2_mbus_framefmt *
+__ov5650_get_pad_format(struct ov5650 *ov5650, struct v4l2_subdev_fh *fh,
+			 unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+	switch (which) {
+	case V4L2_SUBDEV_FORMAT_TRY:
+		return v4l2_subdev_get_try_format(fh, pad);
+	case V4L2_SUBDEV_FORMAT_ACTIVE:
+		return &ov5650->format;
+	default:
+		return NULL;
+	}
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev internal operations
+ */
+
+static int ov5650_s_power(struct v4l2_subdev *sd, int on)
+{
+	struct ov5650 *ov5650 = to_ov5650(sd);
+
+	return ov5650->pdata->s_power(sd, on);
+}
+
+static int ov5650_registered(struct v4l2_subdev *subdev)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(subdev);
+	int ret = 0;
+	u8 revision = 0;
+
+	ret = ov5650_s_power(subdev, 1);
+	if (ret < 0) {
+		dev_err(&client->dev, "OV5650 power up failed\n");
+		return ret;
+	}
+
+	ret = ov5650_reg_read(client, 0x302A, &revision);
+	if (ret) {
+		dev_err(&client->dev, "Failure to detect OV5650 chip\n");
+		goto out;
+	}
+
+	revision &= 0xF;
+
+	dev_info(&client->dev, "Detected a OV5650 chip, revision %x\n",
+		 revision);
+
+out:
+	ov5650_s_power(subdev, 0);
+
+	return ret;
+}
+
+static int ov5650_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
+{
+	struct v4l2_mbus_framefmt *format;
+
+	format = v4l2_subdev_get_try_format(fh, 0);
+	format->code = V4L2_MBUS_FMT_SGRBG10_1X10;
+	format->width = ov5650_frmsizes[OV5650_SIZE_5MP].width;
+	format->height = ov5650_frmsizes[OV5650_SIZE_5MP].height;
+	format->field = V4L2_FIELD_NONE;
+	format->colorspace = V4L2_COLORSPACE_SRGB;
+
+	return 0;
+}
+
+static int ov5650_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
+{
+	return 0;
+}
+
+static int ov5650_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	int ret = 0;
+
+	if (enable) {
+		/* HACK: Hardcoding to 5MP! */
+		ret = ov5650_reg_writes(client, configscript_5MP,
+				ARRAY_SIZE(configscript_5MP));
+	}
+
+	return ret;
+}
+
+static int ov5650_g_fmt(struct v4l2_subdev *sd,
+			struct v4l2_subdev_fh *fh,
+			struct v4l2_subdev_format *format)
+{
+	struct ov5650 *ov5650 = to_ov5650(sd);
+
+	format->format = *__ov5650_get_pad_format(ov5650, fh, format->pad,
+						   format->which);
+
+	return 0;
+}
+
+static int ov5650_s_fmt(struct v4l2_subdev *sd,
+			struct v4l2_subdev_fh *fh,
+			struct v4l2_subdev_format *format)
+{
+	struct ov5650 *ov5650 = to_ov5650(sd);
+	struct v4l2_mbus_framefmt *__format;
+
+	__format = __ov5650_get_pad_format(ov5650, fh, format->pad,
+					    format->which);
+
+	/* FIXME: Do an actual change! */
+	format->format = *__format;
+
+	return 0;
+}
+
+static struct v4l2_subdev_internal_ops ov5650_subdev_internal_ops = {
+	.registered = ov5650_registered,
+	.open = ov5650_open,
+	.close = ov5650_close,
+};
+
+static int ov5650_enum_fmt(struct v4l2_subdev *subdev,
+			   struct v4l2_subdev_fh *fh,
+			   struct v4l2_subdev_mbus_code_enum *code)
+{
+	if (code->index > 0)
+		return -EINVAL;
+
+	code->code = V4L2_MBUS_FMT_SGRBG10_1X10;
+	return 0;
+}
+
+static int ov5650_enum_framesizes(struct v4l2_subdev *subdev,
+				   struct v4l2_subdev_fh *fh,
+				   struct v4l2_subdev_frame_size_enum *fse)
+{
+	if (fse->index >= 1 || fse->code != V4L2_MBUS_FMT_SGRBG10_1X10)
+		return -EINVAL;
+
+	fse->min_width = ov5650_frmsizes[fse->index].width;
+	fse->max_width = fse->min_width;
+	fse->min_height = ov5650_frmsizes[fse->index].height;
+	fse->max_height = fse->min_height;
+
+	return 0;
+}
+
+static struct v4l2_subdev_core_ops ov5650_subdev_core_ops = {
+	.s_power	= ov5650_s_power,
+};
+
+static struct v4l2_subdev_video_ops ov5650_subdev_video_ops = {
+	.s_stream	= ov5650_s_stream,
+};
+
+static struct v4l2_subdev_pad_ops ov5650_subdev_pad_ops = {
+	.enum_mbus_code = ov5650_enum_fmt,
+	.enum_frame_size = ov5650_enum_framesizes,
+	.get_fmt = ov5650_g_fmt,
+	.set_fmt = ov5650_s_fmt,
+};
+
+static struct v4l2_subdev_ops ov5650_subdev_ops = {
+	.core	= &ov5650_subdev_core_ops,
+	.video	= &ov5650_subdev_video_ops,
+	.pad	= &ov5650_subdev_pad_ops,
+};
+
+static int ov5650_probe(struct i2c_client *client,
+			 const struct i2c_device_id *did)
+{
+	struct ov5650 *ov5650;
+	int ret;
+
+	if (!client->dev.platform_data) {
+		dev_err(&client->dev, "No platform data!!\n");
+		return -ENODEV;
+	}
+
+	ov5650 = kzalloc(sizeof(*ov5650), GFP_KERNEL);
+	if (!ov5650)
+		return -ENOMEM;
+
+	ov5650->pdata = client->dev.platform_data;
+
+	ov5650->format.code = V4L2_MBUS_FMT_SGRBG10_1X10;
+	ov5650->format.width = ov5650_frmsizes[OV5650_SIZE_5MP].width;
+	ov5650->format.height = ov5650_frmsizes[OV5650_SIZE_5MP].height;
+	ov5650->format.field = V4L2_FIELD_NONE;
+	ov5650->format.colorspace = V4L2_COLORSPACE_SRGB;
+
+	v4l2_i2c_subdev_init(&ov5650->subdev, client, &ov5650_subdev_ops);
+	ov5650->subdev.internal_ops = &ov5650_subdev_internal_ops;
+	ov5650->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+	ov5650->pad.flags = MEDIA_PAD_FL_SOURCE;
+	ret = media_entity_init(&ov5650->subdev.entity, 1, &ov5650->pad, 0);
+
+	if (ret < 0) {
+		media_entity_cleanup(&ov5650->subdev.entity);
+		kfree(ov5650);
+	}
+
+	return ret;
+}
+
+static int ov5650_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+	struct ov5650 *ov5650 = to_ov5650(subdev);
+
+	v4l2_device_unregister_subdev(subdev);
+	media_entity_cleanup(&subdev->entity);
+	kfree(ov5650);
+	return 0;
+}
+
+static const struct i2c_device_id ov5650_id[] = {
+	{ "ov5650", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ov5650_id);
+
+static struct i2c_driver ov5650_i2c_driver = {
+	.driver = {
+		.name = "ov5650",
+	},
+	.probe		= ov5650_probe,
+	.remove		= ov5650_remove,
+	.id_table	= ov5650_id,
+};
+
+static int __init ov5650_mod_init(void)
+{
+	return i2c_add_driver(&ov5650_i2c_driver);
+}
+
+static void __exit ov5650_mod_exit(void)
+{
+	i2c_del_driver(&ov5650_i2c_driver);
+}
+
+module_init(ov5650_mod_init);
+module_exit(ov5650_mod_exit);
+
+MODULE_DESCRIPTION("OmniVision OV5650 Camera driver");
+MODULE_AUTHOR("Sergio Aguirre <saaguirre@ti.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/include/media/ov5650.h b/include/media/ov5650.h
new file mode 100644
index 0000000..287f20f
--- /dev/null
+++ b/include/media/ov5650.h
@@ -0,0 +1,10 @@
+#ifndef _MEDIA_OV5650_H
+#define _MEDIA_OV5650_H
+
+#include <media/v4l2-subdev.h>
+
+struct ov5650_platform_data {
+      int (*s_power)(struct v4l2_subdev *subdev, int on);
+};
+
+#endif
diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h
index de1d680..f76c8be 100644
--- a/include/media/v4l2-chip-ident.h
+++ b/include/media/v4l2-chip-ident.h
@@ -78,6 +78,7 @@ enum {
 	V4L2_IDENT_OV9740 = 260,
 	V4L2_IDENT_OV5642 = 261,
 	V4L2_IDENT_OV5640 = 262,
+	V4L2_IDENT_OV5650 = 263,
 
 	/* module saa7146: reserved range 300-309 */
 	V4L2_IDENT_SAA7146 = 300,
-- 
1.7.7.4


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

* [PATCH v2 09/11] arm: omap4430sdp: Add support for omap4iss camera
  2011-12-01  0:14 ` Sergio Aguirre
@ 2011-12-01  0:14   ` Sergio Aguirre
  -1 siblings, 0 replies; 65+ messages in thread
From: Sergio Aguirre @ 2011-12-01  0:14 UTC (permalink / raw)
  To: linux-media; +Cc: linux-omap, laurent.pinchart, sakari.ailus, Sergio Aguirre

This adds support for camera interface with the support for
following sensors:

- OV5640
- OV5650

Signed-off-by: Sergio Aguirre <saaguirre@ti.com>
---
 arch/arm/mach-omap2/Kconfig                |   27 ++++
 arch/arm/mach-omap2/Makefile               |    2 +
 arch/arm/mach-omap2/board-4430sdp-camera.c |  221 ++++++++++++++++++++++++++++
 3 files changed, 250 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-omap2/board-4430sdp-camera.c

diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
index 5034147..f883abb 100644
--- a/arch/arm/mach-omap2/Kconfig
+++ b/arch/arm/mach-omap2/Kconfig
@@ -323,6 +323,33 @@ config MACH_OMAP_4430SDP
 	select OMAP_PACKAGE_CBS
 	select REGULATOR_FIXED_VOLTAGE
 
+config MACH_OMAP_4430SDP_CAMERA_SUPPORT
+	bool "OMAP 4430 SDP board Camera support"
+	depends on MACH_OMAP_4430SDP
+	select MEDIA_SUPPORT
+	select MEDIA_CONTROLLER
+	select VIDEO_DEV
+	select VIDEO_V4L2_SUBDEV_API
+	select VIDEO_OMAP4
+	help
+	  Enable Camera HW support for OMAP 4430 SDP board
+	  This is for using the OMAP4 ISS CSI2A Camera sensor
+	  interface.
+
+choice
+	prompt "Camera sensor to use"
+	depends on MACH_OMAP_4430SDP_CAMERA_SUPPORT
+	default MACH_OMAP_4430SDP_CAM_OV5650
+
+	config MACH_OMAP_4430SDP_CAM_OV5640
+		bool "Use OmniVision OV5640 Camera"
+		select VIDEO_OV5640
+
+	config MACH_OMAP_4430SDP_CAM_OV5650
+		bool "Use OmniVision OV5650 Camera"
+		select VIDEO_OV5650
+endchoice
+
 config MACH_OMAP4_PANDA
 	bool "OMAP4 Panda Board"
 	default y
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index 69ab1c0..8bc446a 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -235,6 +235,8 @@ obj-$(CONFIG_MACH_TI8168EVM)		+= board-ti8168evm.o
 
 # Platform specific device init code
 
+obj-$(CONFIG_MACH_OMAP_4430SDP_CAMERA_SUPPORT)	+= board-4430sdp-camera.o
+
 omap-flash-$(CONFIG_MTD_NAND_OMAP2)	:= board-flash.o
 omap-flash-$(CONFIG_MTD_ONENAND_OMAP2)	:= board-flash.o
 obj-y					+= $(omap-flash-y) $(omap-flash-m)
diff --git a/arch/arm/mach-omap2/board-4430sdp-camera.c b/arch/arm/mach-omap2/board-4430sdp-camera.c
new file mode 100644
index 0000000..e62ee50
--- /dev/null
+++ b/arch/arm/mach-omap2/board-4430sdp-camera.c
@@ -0,0 +1,221 @@
+#include <linux/gpio.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/i2c/twl.h>
+#include <linux/mfd/twl6040.h>
+
+#include <plat/i2c.h>
+#include <plat/omap-pm.h>
+
+#include <asm/mach-types.h>
+
+#include <media/ov5640.h>
+#include <media/ov5650.h>
+
+#include "devices.h"
+#include "../../../drivers/media/video/omap4iss/iss.h"
+
+#include "control.h"
+#include "mux.h"
+
+#define OMAP4430SDP_GPIO_CAM_PDN_C	39
+
+static struct clk *sdp4430_cam_aux_clk;
+
+static int sdp4430_ov5640_power(struct v4l2_subdev *subdev, int on)
+{
+	struct iss_device *iss = v4l2_dev_to_iss_device(subdev->v4l2_dev);
+	int ret = 0;
+	struct iss_csiphy_dphy_cfg dphy;
+	struct iss_csiphy_lanes_cfg lanes;
+#ifdef CONFIG_MACH_OMAP_4430SDP_CAM_OV5650
+	unsigned int ddr_freq = 480; /* FIXME: Do an actual query for this */
+#elif defined(CONFIG_MACH_OMAP_4430SDP_CAM_OV5640)
+	unsigned int ddr_freq = 336; /* FIXME: Do an actual query for this */
+#endif
+
+	memset(&lanes, 0, sizeof(lanes));
+	memset(&dphy, 0, sizeof(dphy));
+
+	lanes.clk.pos = 1;
+	lanes.clk.pol = 0;
+	lanes.data[0].pos = 2;
+	lanes.data[0].pol = 0;
+#ifdef CONFIG_MACH_OMAP_4430SDP_CAM_OV5650
+	lanes.data[1].pos = 3;
+	lanes.data[1].pol = 0;
+#endif
+
+	dphy.ths_term = ((((12500 * ddr_freq + 1000000) / 1000000) - 1) & 0xFF);
+	dphy.ths_settle = ((((90000 * ddr_freq + 1000000) / 1000000) + 3) & 0xFF);
+	dphy.tclk_term = 0;
+	dphy.tclk_miss = 1;
+	dphy.tclk_settle = 14;
+
+	if (on) {
+		u8 gpoctl = 0;
+
+		/* TWL6030_BASEADD_AUX */
+		twl_i2c_write_u8(15, 0x00, 0xB);
+		twl_i2c_write_u8(15, 0x80, 0x1);
+
+		mdelay(50);
+
+		/* TWL6030_BASEADD_PM_SLAVE_MISC */
+		twl_i2c_write_u8(21, 0xFF, 0x5E);
+		twl_i2c_write_u8(21, 0x13, 0x5F);
+
+		mdelay(50);
+
+		twl_i2c_write_u8(15, 0x40, 0x1);
+
+		twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &gpoctl,
+				TWL6040_REG_GPOCTL);
+		twl_i2c_write_u8(TWL_MODULE_AUDIO_VOICE, gpoctl | TWL6040_GPO3,
+				TWL6040_REG_GPOCTL);
+
+		mdelay(10);
+
+		gpio_set_value(OMAP4430SDP_GPIO_CAM_PDN_C, 1);
+		mdelay(10);
+		clk_enable(sdp4430_cam_aux_clk); /* Enable XCLK */
+		mdelay(10);
+
+		iss->platform_cb.csiphy_config(&iss->csiphy1, &dphy, &lanes);
+	} else {
+		clk_disable(sdp4430_cam_aux_clk);
+		mdelay(1);
+		gpio_set_value(OMAP4430SDP_GPIO_CAM_PDN_C, 0);
+	}
+
+	return ret;
+}
+
+#define OV5640_I2C_ADDRESS   (0x3C)
+#define OV5650_I2C_ADDRESS   (0x36)
+
+#ifdef CONFIG_MACH_OMAP_4430SDP_CAM_OV5650
+static struct ov5650_platform_data ov_platform_data = {
+#elif defined(CONFIG_MACH_OMAP_4430SDP_CAM_OV5640)
+static struct ov5640_platform_data ov_platform_data = {
+#endif
+      .s_power = sdp4430_ov5640_power,
+};
+
+static struct i2c_board_info ov_camera_i2c_device = {
+#ifdef CONFIG_MACH_OMAP_4430SDP_CAM_OV5650
+	I2C_BOARD_INFO("ov5650", OV5650_I2C_ADDRESS),
+#elif defined(CONFIG_MACH_OMAP_4430SDP_CAM_OV5640)
+	I2C_BOARD_INFO("ov5640", OV5640_I2C_ADDRESS),
+#endif
+	.platform_data = &ov_platform_data,
+};
+
+static struct iss_subdev_i2c_board_info ov_camera_subdevs[] = {
+	{
+		.board_info = &ov_camera_i2c_device,
+		.i2c_adapter_id = 3,
+	},
+	{ NULL, 0, },
+};
+
+static struct iss_v4l2_subdevs_group sdp4430_camera_subdevs[] = {
+	{
+		.subdevs = ov_camera_subdevs,
+		.interface = ISS_INTERFACE_CSI2A_PHY1,
+	},
+	{ },
+};
+
+static void sdp4430_omap4iss_set_constraints(struct iss_device *iss, bool enable)
+{
+	if (!iss)
+		return;
+
+	/* FIXME: Look for something more precise as a good throughtput limit */
+	omap_pm_set_min_bus_tput(iss->dev, OCP_INITIATOR_AGENT,
+				 enable ? 800000 : -1);
+}
+
+static struct iss_platform_data sdp4430_iss_platform_data = {
+	.subdevs = sdp4430_camera_subdevs,
+	.set_constraints = sdp4430_omap4iss_set_constraints,
+};
+
+static struct omap_device_pad omap4iss_pads[] = {
+	{
+		.name   = "csi21_dx0.csi21_dx0",
+		.enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
+	},
+	{
+		.name   = "csi21_dy0.csi21_dy0",
+		.enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
+	},
+	{
+		.name   = "csi21_dx1.csi21_dx1",
+		.enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
+	},
+	{
+		.name   = "csi21_dy1.csi21_dy1",
+		.enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
+	},
+	{
+		.name   = "csi21_dx2.csi21_dx2",
+		.enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
+	},
+	{
+		.name   = "csi21_dy2.csi21_dy2",
+		.enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
+	},
+};
+
+static struct omap_board_data omap4iss_data = {
+	.id	    		= 1,
+	.pads	 		= omap4iss_pads,
+	.pads_cnt       	= ARRAY_SIZE(omap4iss_pads),
+};
+
+static int __init sdp4430_camera_init(void)
+{
+	if (!machine_is_omap_4430sdp())
+		return 0;
+
+	sdp4430_cam_aux_clk = clk_get(NULL, "auxclk3_ck");
+	if (IS_ERR(sdp4430_cam_aux_clk)) {
+		printk(KERN_ERR "Unable to get auxclk3_ck\n");
+		return -ENODEV;
+	}
+
+	if (clk_set_rate(sdp4430_cam_aux_clk,
+			clk_round_rate(sdp4430_cam_aux_clk, 24000000)))
+		return -EINVAL;
+
+	/*
+	 * CSI2 1(A):
+	 *   LANEENABLE[4:0] = 00111(0x7) - Lanes 0, 1 & 2 enabled
+	 *   CTRLCLKEN = 1 - Active high enable for CTRLCLK
+	 *   CAMMODE = 0 - DPHY mode
+	 */
+	omap4_ctrl_pad_writel((omap4_ctrl_pad_readl(
+				OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_CAMERA_RX) &
+			  ~(OMAP4_CAMERARX_CSI21_LANEENABLE_MASK |
+			    OMAP4_CAMERARX_CSI21_CAMMODE_MASK)) |
+			 (0x7 << OMAP4_CAMERARX_CSI21_LANEENABLE_SHIFT) |
+			 OMAP4_CAMERARX_CSI21_CTRLCLKEN_MASK,
+			 OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_CAMERA_RX);
+
+	omap_mux_init_gpio(OMAP4430SDP_GPIO_CAM_PDN_C, OMAP_PIN_OUTPUT);
+
+	/* Init FREF_CLK3_OUT */
+	omap_mux_init_signal("fref_clk3_out", OMAP_PIN_OUTPUT);
+
+	if (gpio_request(OMAP4430SDP_GPIO_CAM_PDN_C, "CAM_PDN_C"))
+		printk(KERN_WARNING "Cannot request GPIO %d\n",
+			OMAP4430SDP_GPIO_CAM_PDN_C);
+	else
+		gpio_direction_output(OMAP4430SDP_GPIO_CAM_PDN_C, 0);
+
+	omap4_init_camera(&sdp4430_iss_platform_data, &omap4iss_data);
+	return 0;
+}
+late_initcall(sdp4430_camera_init);
-- 
1.7.7.4


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

* [PATCH v2 09/11] arm: omap4430sdp: Add support for omap4iss camera
@ 2011-12-01  0:14   ` Sergio Aguirre
  0 siblings, 0 replies; 65+ messages in thread
From: Sergio Aguirre @ 2011-12-01  0:14 UTC (permalink / raw)
  To: linux-media; +Cc: linux-omap, laurent.pinchart, sakari.ailus, Sergio Aguirre

This adds support for camera interface with the support for
following sensors:

- OV5640
- OV5650

Signed-off-by: Sergio Aguirre <saaguirre@ti.com>
---
 arch/arm/mach-omap2/Kconfig                |   27 ++++
 arch/arm/mach-omap2/Makefile               |    2 +
 arch/arm/mach-omap2/board-4430sdp-camera.c |  221 ++++++++++++++++++++++++++++
 3 files changed, 250 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-omap2/board-4430sdp-camera.c

diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
index 5034147..f883abb 100644
--- a/arch/arm/mach-omap2/Kconfig
+++ b/arch/arm/mach-omap2/Kconfig
@@ -323,6 +323,33 @@ config MACH_OMAP_4430SDP
 	select OMAP_PACKAGE_CBS
 	select REGULATOR_FIXED_VOLTAGE
 
+config MACH_OMAP_4430SDP_CAMERA_SUPPORT
+	bool "OMAP 4430 SDP board Camera support"
+	depends on MACH_OMAP_4430SDP
+	select MEDIA_SUPPORT
+	select MEDIA_CONTROLLER
+	select VIDEO_DEV
+	select VIDEO_V4L2_SUBDEV_API
+	select VIDEO_OMAP4
+	help
+	  Enable Camera HW support for OMAP 4430 SDP board
+	  This is for using the OMAP4 ISS CSI2A Camera sensor
+	  interface.
+
+choice
+	prompt "Camera sensor to use"
+	depends on MACH_OMAP_4430SDP_CAMERA_SUPPORT
+	default MACH_OMAP_4430SDP_CAM_OV5650
+
+	config MACH_OMAP_4430SDP_CAM_OV5640
+		bool "Use OmniVision OV5640 Camera"
+		select VIDEO_OV5640
+
+	config MACH_OMAP_4430SDP_CAM_OV5650
+		bool "Use OmniVision OV5650 Camera"
+		select VIDEO_OV5650
+endchoice
+
 config MACH_OMAP4_PANDA
 	bool "OMAP4 Panda Board"
 	default y
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index 69ab1c0..8bc446a 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -235,6 +235,8 @@ obj-$(CONFIG_MACH_TI8168EVM)		+= board-ti8168evm.o
 
 # Platform specific device init code
 
+obj-$(CONFIG_MACH_OMAP_4430SDP_CAMERA_SUPPORT)	+= board-4430sdp-camera.o
+
 omap-flash-$(CONFIG_MTD_NAND_OMAP2)	:= board-flash.o
 omap-flash-$(CONFIG_MTD_ONENAND_OMAP2)	:= board-flash.o
 obj-y					+= $(omap-flash-y) $(omap-flash-m)
diff --git a/arch/arm/mach-omap2/board-4430sdp-camera.c b/arch/arm/mach-omap2/board-4430sdp-camera.c
new file mode 100644
index 0000000..e62ee50
--- /dev/null
+++ b/arch/arm/mach-omap2/board-4430sdp-camera.c
@@ -0,0 +1,221 @@
+#include <linux/gpio.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/i2c/twl.h>
+#include <linux/mfd/twl6040.h>
+
+#include <plat/i2c.h>
+#include <plat/omap-pm.h>
+
+#include <asm/mach-types.h>
+
+#include <media/ov5640.h>
+#include <media/ov5650.h>
+
+#include "devices.h"
+#include "../../../drivers/media/video/omap4iss/iss.h"
+
+#include "control.h"
+#include "mux.h"
+
+#define OMAP4430SDP_GPIO_CAM_PDN_C	39
+
+static struct clk *sdp4430_cam_aux_clk;
+
+static int sdp4430_ov5640_power(struct v4l2_subdev *subdev, int on)
+{
+	struct iss_device *iss = v4l2_dev_to_iss_device(subdev->v4l2_dev);
+	int ret = 0;
+	struct iss_csiphy_dphy_cfg dphy;
+	struct iss_csiphy_lanes_cfg lanes;
+#ifdef CONFIG_MACH_OMAP_4430SDP_CAM_OV5650
+	unsigned int ddr_freq = 480; /* FIXME: Do an actual query for this */
+#elif defined(CONFIG_MACH_OMAP_4430SDP_CAM_OV5640)
+	unsigned int ddr_freq = 336; /* FIXME: Do an actual query for this */
+#endif
+
+	memset(&lanes, 0, sizeof(lanes));
+	memset(&dphy, 0, sizeof(dphy));
+
+	lanes.clk.pos = 1;
+	lanes.clk.pol = 0;
+	lanes.data[0].pos = 2;
+	lanes.data[0].pol = 0;
+#ifdef CONFIG_MACH_OMAP_4430SDP_CAM_OV5650
+	lanes.data[1].pos = 3;
+	lanes.data[1].pol = 0;
+#endif
+
+	dphy.ths_term = ((((12500 * ddr_freq + 1000000) / 1000000) - 1) & 0xFF);
+	dphy.ths_settle = ((((90000 * ddr_freq + 1000000) / 1000000) + 3) & 0xFF);
+	dphy.tclk_term = 0;
+	dphy.tclk_miss = 1;
+	dphy.tclk_settle = 14;
+
+	if (on) {
+		u8 gpoctl = 0;
+
+		/* TWL6030_BASEADD_AUX */
+		twl_i2c_write_u8(15, 0x00, 0xB);
+		twl_i2c_write_u8(15, 0x80, 0x1);
+
+		mdelay(50);
+
+		/* TWL6030_BASEADD_PM_SLAVE_MISC */
+		twl_i2c_write_u8(21, 0xFF, 0x5E);
+		twl_i2c_write_u8(21, 0x13, 0x5F);
+
+		mdelay(50);
+
+		twl_i2c_write_u8(15, 0x40, 0x1);
+
+		twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &gpoctl,
+				TWL6040_REG_GPOCTL);
+		twl_i2c_write_u8(TWL_MODULE_AUDIO_VOICE, gpoctl | TWL6040_GPO3,
+				TWL6040_REG_GPOCTL);
+
+		mdelay(10);
+
+		gpio_set_value(OMAP4430SDP_GPIO_CAM_PDN_C, 1);
+		mdelay(10);
+		clk_enable(sdp4430_cam_aux_clk); /* Enable XCLK */
+		mdelay(10);
+
+		iss->platform_cb.csiphy_config(&iss->csiphy1, &dphy, &lanes);
+	} else {
+		clk_disable(sdp4430_cam_aux_clk);
+		mdelay(1);
+		gpio_set_value(OMAP4430SDP_GPIO_CAM_PDN_C, 0);
+	}
+
+	return ret;
+}
+
+#define OV5640_I2C_ADDRESS   (0x3C)
+#define OV5650_I2C_ADDRESS   (0x36)
+
+#ifdef CONFIG_MACH_OMAP_4430SDP_CAM_OV5650
+static struct ov5650_platform_data ov_platform_data = {
+#elif defined(CONFIG_MACH_OMAP_4430SDP_CAM_OV5640)
+static struct ov5640_platform_data ov_platform_data = {
+#endif
+      .s_power = sdp4430_ov5640_power,
+};
+
+static struct i2c_board_info ov_camera_i2c_device = {
+#ifdef CONFIG_MACH_OMAP_4430SDP_CAM_OV5650
+	I2C_BOARD_INFO("ov5650", OV5650_I2C_ADDRESS),
+#elif defined(CONFIG_MACH_OMAP_4430SDP_CAM_OV5640)
+	I2C_BOARD_INFO("ov5640", OV5640_I2C_ADDRESS),
+#endif
+	.platform_data = &ov_platform_data,
+};
+
+static struct iss_subdev_i2c_board_info ov_camera_subdevs[] = {
+	{
+		.board_info = &ov_camera_i2c_device,
+		.i2c_adapter_id = 3,
+	},
+	{ NULL, 0, },
+};
+
+static struct iss_v4l2_subdevs_group sdp4430_camera_subdevs[] = {
+	{
+		.subdevs = ov_camera_subdevs,
+		.interface = ISS_INTERFACE_CSI2A_PHY1,
+	},
+	{ },
+};
+
+static void sdp4430_omap4iss_set_constraints(struct iss_device *iss, bool enable)
+{
+	if (!iss)
+		return;
+
+	/* FIXME: Look for something more precise as a good throughtput limit */
+	omap_pm_set_min_bus_tput(iss->dev, OCP_INITIATOR_AGENT,
+				 enable ? 800000 : -1);
+}
+
+static struct iss_platform_data sdp4430_iss_platform_data = {
+	.subdevs = sdp4430_camera_subdevs,
+	.set_constraints = sdp4430_omap4iss_set_constraints,
+};
+
+static struct omap_device_pad omap4iss_pads[] = {
+	{
+		.name   = "csi21_dx0.csi21_dx0",
+		.enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
+	},
+	{
+		.name   = "csi21_dy0.csi21_dy0",
+		.enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
+	},
+	{
+		.name   = "csi21_dx1.csi21_dx1",
+		.enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
+	},
+	{
+		.name   = "csi21_dy1.csi21_dy1",
+		.enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
+	},
+	{
+		.name   = "csi21_dx2.csi21_dx2",
+		.enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
+	},
+	{
+		.name   = "csi21_dy2.csi21_dy2",
+		.enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
+	},
+};
+
+static struct omap_board_data omap4iss_data = {
+	.id	    		= 1,
+	.pads	 		= omap4iss_pads,
+	.pads_cnt       	= ARRAY_SIZE(omap4iss_pads),
+};
+
+static int __init sdp4430_camera_init(void)
+{
+	if (!machine_is_omap_4430sdp())
+		return 0;
+
+	sdp4430_cam_aux_clk = clk_get(NULL, "auxclk3_ck");
+	if (IS_ERR(sdp4430_cam_aux_clk)) {
+		printk(KERN_ERR "Unable to get auxclk3_ck\n");
+		return -ENODEV;
+	}
+
+	if (clk_set_rate(sdp4430_cam_aux_clk,
+			clk_round_rate(sdp4430_cam_aux_clk, 24000000)))
+		return -EINVAL;
+
+	/*
+	 * CSI2 1(A):
+	 *   LANEENABLE[4:0] = 00111(0x7) - Lanes 0, 1 & 2 enabled
+	 *   CTRLCLKEN = 1 - Active high enable for CTRLCLK
+	 *   CAMMODE = 0 - DPHY mode
+	 */
+	omap4_ctrl_pad_writel((omap4_ctrl_pad_readl(
+				OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_CAMERA_RX) &
+			  ~(OMAP4_CAMERARX_CSI21_LANEENABLE_MASK |
+			    OMAP4_CAMERARX_CSI21_CAMMODE_MASK)) |
+			 (0x7 << OMAP4_CAMERARX_CSI21_LANEENABLE_SHIFT) |
+			 OMAP4_CAMERARX_CSI21_CTRLCLKEN_MASK,
+			 OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_CAMERA_RX);
+
+	omap_mux_init_gpio(OMAP4430SDP_GPIO_CAM_PDN_C, OMAP_PIN_OUTPUT);
+
+	/* Init FREF_CLK3_OUT */
+	omap_mux_init_signal("fref_clk3_out", OMAP_PIN_OUTPUT);
+
+	if (gpio_request(OMAP4430SDP_GPIO_CAM_PDN_C, "CAM_PDN_C"))
+		printk(KERN_WARNING "Cannot request GPIO %d\n",
+			OMAP4430SDP_GPIO_CAM_PDN_C);
+	else
+		gpio_direction_output(OMAP4430SDP_GPIO_CAM_PDN_C, 0);
+
+	omap4_init_camera(&sdp4430_iss_platform_data, &omap4iss_data);
+	return 0;
+}
+late_initcall(sdp4430_camera_init);
-- 
1.7.7.4

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

* [PATCH v2 10/11] arm: omap4panda: Add support for omap4iss camera
  2011-12-01  0:14 ` Sergio Aguirre
@ 2011-12-01  0:14   ` Sergio Aguirre
  -1 siblings, 0 replies; 65+ messages in thread
From: Sergio Aguirre @ 2011-12-01  0:14 UTC (permalink / raw)
  To: linux-media; +Cc: linux-omap, laurent.pinchart, sakari.ailus, Sergio Aguirre

This adds support for camera interface with the support for
following sensors:

- OV5640
- OV5650

Signed-off-by: Sergio Aguirre <saaguirre@ti.com>
---
 arch/arm/mach-omap2/Kconfig                   |   27 ++++
 arch/arm/mach-omap2/Makefile                  |    1 +
 arch/arm/mach-omap2/board-omap4panda-camera.c |  198 +++++++++++++++++++++++++
 3 files changed, 226 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-omap2/board-omap4panda-camera.c

diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
index f883abb..0fc5ce9 100644
--- a/arch/arm/mach-omap2/Kconfig
+++ b/arch/arm/mach-omap2/Kconfig
@@ -358,6 +358,33 @@ config MACH_OMAP4_PANDA
 	select OMAP_PACKAGE_CBS
 	select REGULATOR_FIXED_VOLTAGE
 
+config MACH_OMAP4_PANDA_CAMERA_SUPPORT
+	bool "OMAP4 Panda Board Camera support"
+	depends on MACH_OMAP4_PANDA
+	select MEDIA_SUPPORT
+	select MEDIA_CONTROLLER
+	select VIDEO_DEV
+	select VIDEO_V4L2_SUBDEV_API
+	select VIDEO_OMAP4
+	help
+	  Enable Camera HW support for PandaBoard.
+	  This is for using the OMAP4 ISS CSI2A Camera sensor
+	  interface.
+
+choice
+	prompt "Camera sensor to use"
+	depends on MACH_OMAP4_PANDA_CAMERA_SUPPORT
+	default MACH_OMAP4_PANDA_CAM_OV5650
+
+	config MACH_OMAP4_PANDA_CAM_OV5640
+		bool "Use OmniVision OV5640 Camera"
+		select VIDEO_OV5640
+
+	config MACH_OMAP4_PANDA_CAM_OV5650
+		bool "Use OmniVision OV5650 Camera"
+		select VIDEO_OV5650
+endchoice
+
 config OMAP3_EMU
 	bool "OMAP3 debugging peripherals"
 	depends on ARCH_OMAP3
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index 8bc446a..e80724d 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -236,6 +236,7 @@ obj-$(CONFIG_MACH_TI8168EVM)		+= board-ti8168evm.o
 # Platform specific device init code
 
 obj-$(CONFIG_MACH_OMAP_4430SDP_CAMERA_SUPPORT)	+= board-4430sdp-camera.o
+obj-$(CONFIG_MACH_OMAP4_PANDA_CAMERA_SUPPORT)	+= board-omap4panda-camera.o
 
 omap-flash-$(CONFIG_MTD_NAND_OMAP2)	:= board-flash.o
 omap-flash-$(CONFIG_MTD_ONENAND_OMAP2)	:= board-flash.o
diff --git a/arch/arm/mach-omap2/board-omap4panda-camera.c b/arch/arm/mach-omap2/board-omap4panda-camera.c
new file mode 100644
index 0000000..02ef36e
--- /dev/null
+++ b/arch/arm/mach-omap2/board-omap4panda-camera.c
@@ -0,0 +1,198 @@
+#include <linux/gpio.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+
+#include <plat/i2c.h>
+#include <plat/omap-pm.h>
+
+#include <asm/mach-types.h>
+
+#include <media/ov5640.h>
+#include <media/ov5650.h>
+
+#include "devices.h"
+#include "../../../drivers/media/video/omap4iss/iss.h"
+
+#include "control.h"
+#include "mux.h"
+
+#define PANDA_GPIO_CAM_PWRDN		45
+#define PANDA_GPIO_CAM_RESET		83
+
+static struct clk *panda_cam_aux_clk;
+
+static int panda_ov5640_power(struct v4l2_subdev *subdev, int on)
+{
+	struct iss_device *iss = v4l2_dev_to_iss_device(subdev->v4l2_dev);
+	int ret = 0;
+	struct iss_csiphy_dphy_cfg dphy;
+	struct iss_csiphy_lanes_cfg lanes;
+	unsigned int ddr_freq = 480; /* FIXME: Do an actual query for this */
+
+	memset(&lanes, 0, sizeof(lanes));
+	memset(&dphy, 0, sizeof(dphy));
+
+	lanes.clk.pos = 1;
+	lanes.clk.pol = 0;
+	lanes.data[0].pos = 2;
+	lanes.data[0].pol = 0;
+	lanes.data[1].pos = 3;
+	lanes.data[1].pol = 0;
+
+	dphy.ths_term = ((((12500 * ddr_freq + 1000000) / 1000000) - 1) & 0xFF);
+	dphy.ths_settle = ((((90000 * ddr_freq + 1000000) / 1000000) + 3) & 0xFF);
+	dphy.tclk_term = 0;
+	dphy.tclk_miss = 1;
+	dphy.tclk_settle = 14;
+
+	if (on) {
+		gpio_set_value(PANDA_GPIO_CAM_PWRDN, 0);
+		clk_enable(panda_cam_aux_clk);
+		mdelay(2);
+
+		iss->platform_cb.csiphy_config(&iss->csiphy1, &dphy, &lanes);
+	} else {
+		clk_disable(panda_cam_aux_clk);
+		gpio_set_value(PANDA_GPIO_CAM_PWRDN, 1);
+	}
+
+	return ret;
+}
+
+#define OV5640_I2C_ADDRESS   (0x3C)
+#define OV5650_I2C_ADDRESS   (0x36)
+
+#ifdef CONFIG_MACH_OMAP4_PANDA_CAM_OV5650
+static struct ov5650_platform_data ov_platform_data = {
+#elif defined(CONFIG_MACH_OMAP4_PANDA_CAM_OV5640)
+static struct ov5640_platform_data ov_platform_data = {
+#endif
+      .s_power = panda_ov5640_power,
+};
+
+static struct i2c_board_info ov_camera_i2c_device = {
+#ifdef CONFIG_MACH_OMAP4_PANDA_CAM_OV5650
+	I2C_BOARD_INFO("ov5650", OV5650_I2C_ADDRESS),
+#elif defined(CONFIG_MACH_OMAP4_PANDA_CAM_OV5640)
+	I2C_BOARD_INFO("ov5640", OV5640_I2C_ADDRESS),
+#endif
+	.platform_data = &ov_platform_data,
+};
+
+static struct iss_subdev_i2c_board_info ov_camera_subdevs[] = {
+	{
+		.board_info = &ov_camera_i2c_device,
+		.i2c_adapter_id = 3,
+	},
+	{ NULL, 0, },
+};
+
+static struct iss_v4l2_subdevs_group panda_camera_subdevs[] = {
+	{
+		.subdevs = ov_camera_subdevs,
+		.interface = ISS_INTERFACE_CSI2A_PHY1,
+	},
+	{ },
+};
+
+static void panda_omap4iss_set_constraints(struct iss_device *iss, bool enable)
+{
+	if (!iss)
+		return;
+
+	/* FIXME: Look for something more precise as a good throughtput limit */
+	omap_pm_set_min_bus_tput(iss->dev, OCP_INITIATOR_AGENT,
+				 enable ? 800000 : -1);
+}
+
+static struct iss_platform_data panda_iss_platform_data = {
+	.subdevs = panda_camera_subdevs,
+	.set_constraints = panda_omap4iss_set_constraints,
+};
+
+
+static struct omap_device_pad omap4iss_pads[] = {
+	{
+		.name   = "csi21_dx0.csi21_dx0",
+		.enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
+	},
+	{
+		.name   = "csi21_dy0.csi21_dy0",
+		.enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
+	},
+	{
+		.name   = "csi21_dx1.csi21_dx1",
+		.enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
+	},
+	{
+		.name   = "csi21_dy1.csi21_dy1",
+		.enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
+	},
+	{
+		.name   = "csi21_dx2.csi21_dx2",
+		.enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
+	},
+	{
+		.name   = "csi21_dy2.csi21_dy2",
+		.enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
+	},
+};
+
+static struct omap_board_data omap4iss_data = {
+	.id	    		= 1,
+	.pads	 		= omap4iss_pads,
+	.pads_cnt       	= ARRAY_SIZE(omap4iss_pads),
+};
+
+static int __init panda_camera_init(void)
+{
+	if (!machine_is_omap4_panda())
+		return 0;
+
+	panda_cam_aux_clk = clk_get(NULL, "auxclk1_ck");
+	if (IS_ERR(panda_cam_aux_clk)) {
+		printk(KERN_ERR "Unable to get auxclk1_ck\n");
+		return -ENODEV;
+	}
+
+	if (clk_set_rate(panda_cam_aux_clk,
+			clk_round_rate(panda_cam_aux_clk, 24000000)))
+		return -EINVAL;
+
+	/*
+	 * CSI2 1(A):
+	 *   LANEENABLE[4:0] = 00111(0x7) - Lanes 0, 1 & 2 enabled
+	 *   CTRLCLKEN = 1 - Active high enable for CTRLCLK
+	 *   CAMMODE = 0 - DPHY mode
+	 */
+	omap4_ctrl_pad_writel((omap4_ctrl_pad_readl(
+				OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_CAMERA_RX) &
+			  ~(OMAP4_CAMERARX_CSI21_LANEENABLE_MASK |
+			    OMAP4_CAMERARX_CSI21_CAMMODE_MASK)) |
+			 (0x7 << OMAP4_CAMERARX_CSI21_LANEENABLE_SHIFT) |
+			 OMAP4_CAMERARX_CSI21_CTRLCLKEN_MASK,
+			 OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_CAMERA_RX);
+
+	/* Select GPIO 45 */
+	omap_mux_init_gpio(PANDA_GPIO_CAM_PWRDN, OMAP_PIN_OUTPUT);
+
+	/* Select GPIO 83 */
+	omap_mux_init_gpio(PANDA_GPIO_CAM_RESET, OMAP_PIN_OUTPUT);
+
+	/* Init FREF_CLK1_OUT */
+	omap_mux_init_signal("fref_clk1_out", OMAP_PIN_OUTPUT);
+
+	if (gpio_request_one(PANDA_GPIO_CAM_PWRDN, GPIOF_OUT_INIT_HIGH,
+			     "CAM_PWRDN"))
+		printk(KERN_WARNING "Cannot request GPIO %d\n",
+			PANDA_GPIO_CAM_PWRDN);
+
+	if (gpio_request_one(PANDA_GPIO_CAM_RESET, GPIOF_OUT_INIT_HIGH,
+			     "CAM_RESET"))
+		printk(KERN_WARNING "Cannot request GPIO %d\n",
+			PANDA_GPIO_CAM_RESET);
+
+	omap4_init_camera(&panda_iss_platform_data, &omap4iss_data);
+	return 0;
+}
+late_initcall(panda_camera_init);
-- 
1.7.7.4


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

* [PATCH v2 10/11] arm: omap4panda: Add support for omap4iss camera
@ 2011-12-01  0:14   ` Sergio Aguirre
  0 siblings, 0 replies; 65+ messages in thread
From: Sergio Aguirre @ 2011-12-01  0:14 UTC (permalink / raw)
  To: linux-media; +Cc: linux-omap, laurent.pinchart, sakari.ailus, Sergio Aguirre

This adds support for camera interface with the support for
following sensors:

- OV5640
- OV5650

Signed-off-by: Sergio Aguirre <saaguirre@ti.com>
---
 arch/arm/mach-omap2/Kconfig                   |   27 ++++
 arch/arm/mach-omap2/Makefile                  |    1 +
 arch/arm/mach-omap2/board-omap4panda-camera.c |  198 +++++++++++++++++++++++++
 3 files changed, 226 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-omap2/board-omap4panda-camera.c

diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
index f883abb..0fc5ce9 100644
--- a/arch/arm/mach-omap2/Kconfig
+++ b/arch/arm/mach-omap2/Kconfig
@@ -358,6 +358,33 @@ config MACH_OMAP4_PANDA
 	select OMAP_PACKAGE_CBS
 	select REGULATOR_FIXED_VOLTAGE
 
+config MACH_OMAP4_PANDA_CAMERA_SUPPORT
+	bool "OMAP4 Panda Board Camera support"
+	depends on MACH_OMAP4_PANDA
+	select MEDIA_SUPPORT
+	select MEDIA_CONTROLLER
+	select VIDEO_DEV
+	select VIDEO_V4L2_SUBDEV_API
+	select VIDEO_OMAP4
+	help
+	  Enable Camera HW support for PandaBoard.
+	  This is for using the OMAP4 ISS CSI2A Camera sensor
+	  interface.
+
+choice
+	prompt "Camera sensor to use"
+	depends on MACH_OMAP4_PANDA_CAMERA_SUPPORT
+	default MACH_OMAP4_PANDA_CAM_OV5650
+
+	config MACH_OMAP4_PANDA_CAM_OV5640
+		bool "Use OmniVision OV5640 Camera"
+		select VIDEO_OV5640
+
+	config MACH_OMAP4_PANDA_CAM_OV5650
+		bool "Use OmniVision OV5650 Camera"
+		select VIDEO_OV5650
+endchoice
+
 config OMAP3_EMU
 	bool "OMAP3 debugging peripherals"
 	depends on ARCH_OMAP3
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index 8bc446a..e80724d 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -236,6 +236,7 @@ obj-$(CONFIG_MACH_TI8168EVM)		+= board-ti8168evm.o
 # Platform specific device init code
 
 obj-$(CONFIG_MACH_OMAP_4430SDP_CAMERA_SUPPORT)	+= board-4430sdp-camera.o
+obj-$(CONFIG_MACH_OMAP4_PANDA_CAMERA_SUPPORT)	+= board-omap4panda-camera.o
 
 omap-flash-$(CONFIG_MTD_NAND_OMAP2)	:= board-flash.o
 omap-flash-$(CONFIG_MTD_ONENAND_OMAP2)	:= board-flash.o
diff --git a/arch/arm/mach-omap2/board-omap4panda-camera.c b/arch/arm/mach-omap2/board-omap4panda-camera.c
new file mode 100644
index 0000000..02ef36e
--- /dev/null
+++ b/arch/arm/mach-omap2/board-omap4panda-camera.c
@@ -0,0 +1,198 @@
+#include <linux/gpio.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+
+#include <plat/i2c.h>
+#include <plat/omap-pm.h>
+
+#include <asm/mach-types.h>
+
+#include <media/ov5640.h>
+#include <media/ov5650.h>
+
+#include "devices.h"
+#include "../../../drivers/media/video/omap4iss/iss.h"
+
+#include "control.h"
+#include "mux.h"
+
+#define PANDA_GPIO_CAM_PWRDN		45
+#define PANDA_GPIO_CAM_RESET		83
+
+static struct clk *panda_cam_aux_clk;
+
+static int panda_ov5640_power(struct v4l2_subdev *subdev, int on)
+{
+	struct iss_device *iss = v4l2_dev_to_iss_device(subdev->v4l2_dev);
+	int ret = 0;
+	struct iss_csiphy_dphy_cfg dphy;
+	struct iss_csiphy_lanes_cfg lanes;
+	unsigned int ddr_freq = 480; /* FIXME: Do an actual query for this */
+
+	memset(&lanes, 0, sizeof(lanes));
+	memset(&dphy, 0, sizeof(dphy));
+
+	lanes.clk.pos = 1;
+	lanes.clk.pol = 0;
+	lanes.data[0].pos = 2;
+	lanes.data[0].pol = 0;
+	lanes.data[1].pos = 3;
+	lanes.data[1].pol = 0;
+
+	dphy.ths_term = ((((12500 * ddr_freq + 1000000) / 1000000) - 1) & 0xFF);
+	dphy.ths_settle = ((((90000 * ddr_freq + 1000000) / 1000000) + 3) & 0xFF);
+	dphy.tclk_term = 0;
+	dphy.tclk_miss = 1;
+	dphy.tclk_settle = 14;
+
+	if (on) {
+		gpio_set_value(PANDA_GPIO_CAM_PWRDN, 0);
+		clk_enable(panda_cam_aux_clk);
+		mdelay(2);
+
+		iss->platform_cb.csiphy_config(&iss->csiphy1, &dphy, &lanes);
+	} else {
+		clk_disable(panda_cam_aux_clk);
+		gpio_set_value(PANDA_GPIO_CAM_PWRDN, 1);
+	}
+
+	return ret;
+}
+
+#define OV5640_I2C_ADDRESS   (0x3C)
+#define OV5650_I2C_ADDRESS   (0x36)
+
+#ifdef CONFIG_MACH_OMAP4_PANDA_CAM_OV5650
+static struct ov5650_platform_data ov_platform_data = {
+#elif defined(CONFIG_MACH_OMAP4_PANDA_CAM_OV5640)
+static struct ov5640_platform_data ov_platform_data = {
+#endif
+      .s_power = panda_ov5640_power,
+};
+
+static struct i2c_board_info ov_camera_i2c_device = {
+#ifdef CONFIG_MACH_OMAP4_PANDA_CAM_OV5650
+	I2C_BOARD_INFO("ov5650", OV5650_I2C_ADDRESS),
+#elif defined(CONFIG_MACH_OMAP4_PANDA_CAM_OV5640)
+	I2C_BOARD_INFO("ov5640", OV5640_I2C_ADDRESS),
+#endif
+	.platform_data = &ov_platform_data,
+};
+
+static struct iss_subdev_i2c_board_info ov_camera_subdevs[] = {
+	{
+		.board_info = &ov_camera_i2c_device,
+		.i2c_adapter_id = 3,
+	},
+	{ NULL, 0, },
+};
+
+static struct iss_v4l2_subdevs_group panda_camera_subdevs[] = {
+	{
+		.subdevs = ov_camera_subdevs,
+		.interface = ISS_INTERFACE_CSI2A_PHY1,
+	},
+	{ },
+};
+
+static void panda_omap4iss_set_constraints(struct iss_device *iss, bool enable)
+{
+	if (!iss)
+		return;
+
+	/* FIXME: Look for something more precise as a good throughtput limit */
+	omap_pm_set_min_bus_tput(iss->dev, OCP_INITIATOR_AGENT,
+				 enable ? 800000 : -1);
+}
+
+static struct iss_platform_data panda_iss_platform_data = {
+	.subdevs = panda_camera_subdevs,
+	.set_constraints = panda_omap4iss_set_constraints,
+};
+
+
+static struct omap_device_pad omap4iss_pads[] = {
+	{
+		.name   = "csi21_dx0.csi21_dx0",
+		.enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
+	},
+	{
+		.name   = "csi21_dy0.csi21_dy0",
+		.enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
+	},
+	{
+		.name   = "csi21_dx1.csi21_dx1",
+		.enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
+	},
+	{
+		.name   = "csi21_dy1.csi21_dy1",
+		.enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
+	},
+	{
+		.name   = "csi21_dx2.csi21_dx2",
+		.enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
+	},
+	{
+		.name   = "csi21_dy2.csi21_dy2",
+		.enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
+	},
+};
+
+static struct omap_board_data omap4iss_data = {
+	.id	    		= 1,
+	.pads	 		= omap4iss_pads,
+	.pads_cnt       	= ARRAY_SIZE(omap4iss_pads),
+};
+
+static int __init panda_camera_init(void)
+{
+	if (!machine_is_omap4_panda())
+		return 0;
+
+	panda_cam_aux_clk = clk_get(NULL, "auxclk1_ck");
+	if (IS_ERR(panda_cam_aux_clk)) {
+		printk(KERN_ERR "Unable to get auxclk1_ck\n");
+		return -ENODEV;
+	}
+
+	if (clk_set_rate(panda_cam_aux_clk,
+			clk_round_rate(panda_cam_aux_clk, 24000000)))
+		return -EINVAL;
+
+	/*
+	 * CSI2 1(A):
+	 *   LANEENABLE[4:0] = 00111(0x7) - Lanes 0, 1 & 2 enabled
+	 *   CTRLCLKEN = 1 - Active high enable for CTRLCLK
+	 *   CAMMODE = 0 - DPHY mode
+	 */
+	omap4_ctrl_pad_writel((omap4_ctrl_pad_readl(
+				OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_CAMERA_RX) &
+			  ~(OMAP4_CAMERARX_CSI21_LANEENABLE_MASK |
+			    OMAP4_CAMERARX_CSI21_CAMMODE_MASK)) |
+			 (0x7 << OMAP4_CAMERARX_CSI21_LANEENABLE_SHIFT) |
+			 OMAP4_CAMERARX_CSI21_CTRLCLKEN_MASK,
+			 OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_CAMERA_RX);
+
+	/* Select GPIO 45 */
+	omap_mux_init_gpio(PANDA_GPIO_CAM_PWRDN, OMAP_PIN_OUTPUT);
+
+	/* Select GPIO 83 */
+	omap_mux_init_gpio(PANDA_GPIO_CAM_RESET, OMAP_PIN_OUTPUT);
+
+	/* Init FREF_CLK1_OUT */
+	omap_mux_init_signal("fref_clk1_out", OMAP_PIN_OUTPUT);
+
+	if (gpio_request_one(PANDA_GPIO_CAM_PWRDN, GPIOF_OUT_INIT_HIGH,
+			     "CAM_PWRDN"))
+		printk(KERN_WARNING "Cannot request GPIO %d\n",
+			PANDA_GPIO_CAM_PWRDN);
+
+	if (gpio_request_one(PANDA_GPIO_CAM_RESET, GPIOF_OUT_INIT_HIGH,
+			     "CAM_RESET"))
+		printk(KERN_WARNING "Cannot request GPIO %d\n",
+			PANDA_GPIO_CAM_RESET);
+
+	omap4_init_camera(&panda_iss_platform_data, &omap4iss_data);
+	return 0;
+}
+late_initcall(panda_camera_init);
-- 
1.7.7.4


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

* [PATCH v2 11/11] arm: Add support for CMA for omap4iss driver
  2011-12-01  0:14 ` Sergio Aguirre
@ 2011-12-01  0:15   ` Sergio Aguirre
  -1 siblings, 0 replies; 65+ messages in thread
From: Sergio Aguirre @ 2011-12-01  0:15 UTC (permalink / raw)
  To: linux-media; +Cc: linux-omap, laurent.pinchart, sakari.ailus, Sergio Aguirre

Signed-off-by: Sergio Aguirre <saaguirre@ti.com>
---
 arch/arm/mach-omap2/devices.c |    8 ++++++++
 1 files changed, 8 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-omap2/devices.c b/arch/arm/mach-omap2/devices.c
index b48aeea..e411c4e 100644
--- a/arch/arm/mach-omap2/devices.c
+++ b/arch/arm/mach-omap2/devices.c
@@ -17,6 +17,9 @@
 #include <linux/err.h>
 #include <linux/slab.h>
 #include <linux/of.h>
+#ifdef CONFIG_CMA
+#include <linux/dma-contiguous.h>
+#endif
 
 #include <mach/hardware.h>
 #include <mach/irqs.h>
@@ -246,6 +249,11 @@ int omap4_init_camera(struct iss_platform_data *pdata, struct omap_board_data *b
 
 	oh->mux = omap_hwmod_mux_init(bdata->pads, bdata->pads_cnt);
 
+#ifdef CONFIG_CMA
+	/* Create private 32MiB contiguous memory area for omap4iss device */
+	dma_declare_contiguous(&pdev->dev, 32*SZ_1M, 0, 0);
+#endif
+
 	return 0;
 }
 
-- 
1.7.7.4


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

* [PATCH v2 11/11] arm: Add support for CMA for omap4iss driver
@ 2011-12-01  0:15   ` Sergio Aguirre
  0 siblings, 0 replies; 65+ messages in thread
From: Sergio Aguirre @ 2011-12-01  0:15 UTC (permalink / raw)
  To: linux-media; +Cc: linux-omap, laurent.pinchart, sakari.ailus, Sergio Aguirre

Signed-off-by: Sergio Aguirre <saaguirre@ti.com>
---
 arch/arm/mach-omap2/devices.c |    8 ++++++++
 1 files changed, 8 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-omap2/devices.c b/arch/arm/mach-omap2/devices.c
index b48aeea..e411c4e 100644
--- a/arch/arm/mach-omap2/devices.c
+++ b/arch/arm/mach-omap2/devices.c
@@ -17,6 +17,9 @@
 #include <linux/err.h>
 #include <linux/slab.h>
 #include <linux/of.h>
+#ifdef CONFIG_CMA
+#include <linux/dma-contiguous.h>
+#endif
 
 #include <mach/hardware.h>
 #include <mach/irqs.h>
@@ -246,6 +249,11 @@ int omap4_init_camera(struct iss_platform_data *pdata, struct omap_board_data *b
 
 	oh->mux = omap_hwmod_mux_init(bdata->pads, bdata->pads_cnt);
 
+#ifdef CONFIG_CMA
+	/* Create private 32MiB contiguous memory area for omap4iss device */
+	dma_declare_contiguous(&pdev->dev, 32*SZ_1M, 0, 0);
+#endif
+
 	return 0;
 }
 
-- 
1.7.7.4


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

* Re: [PATCH v2 00/11] v4l2: OMAP4 ISS driver + Sensor + Board support
  2011-12-01  0:14 ` Sergio Aguirre
@ 2011-12-01  4:05   ` Aguirre, Sergio
  -1 siblings, 0 replies; 65+ messages in thread
From: Aguirre, Sergio @ 2011-12-01  4:05 UTC (permalink / raw)
  To: linux-media; +Cc: linux-omap, laurent.pinchart, sakari.ailus, Sergio Aguirre

Hi again,

On Wed, Nov 30, 2011 at 6:14 PM, Sergio Aguirre <saaguirre@ti.com> wrote:
> Hi everyone,
>
> This is the second version of the OMAP4 ISS driver,
> now ported to the Media Controller framework AND supporting
> videobuf2 framework.
>
> This patchset should apply cleanly on top of v3.2-rc3 kernel tag.
>
> This driver attempts to provide an fully open source solution to
> control the OMAP4 Imaging SubSystem (a.k.a. ISS).
>
> Starts with just CSI2-A interface support, and pretends to be
> ready for expansion to add support to the many ISS block modules
> as possible.
>
> Please see newly added documentation for more details:
>
> Documentation/video4linux/omap4_camera.txt
>
> Any comments/complaints are welcome. :)
>
> Changes since v1:
> - Simplification of auxclk handling in board files. (Pointed out by: Roger Quadros)
> - Cleanup of Camera support enablement for 4430sdp & panda. (Pointed out by: Roger Quadros)
> - Use of HWMOD declaration for assisted platform_device creation. (Pointed out by: Felipe Balbi)
> - Videobuf2 migration (Removal of custom iss_queue buffer handling driver)
> - Proper GPO3 handling for CAM_SEL in 4430sdp.

Also, you can pull this as a branch in my git tree here:

Web URL: http://gitorious.org/omap4-v4l2-camera/omap4-v4l2-camera

git URL: git://gitorious.org/omap4-v4l2-camera/omap4-v4l2-camera.git
Branch: for3.2-rc3

Regards,
Sergio

>
> Sergio Aguirre (10):
>  TWL6030: Add mapping for auxiliary regs
>  mfd: twl6040: Fix wrong TWL6040_GPO3 bitfield value
>  OMAP4: hwmod: Include CSI2A and CSIPHY1 memory sections
>  OMAP4: Add base addresses for ISS
>  v4l: Add support for omap4iss driver
>  v4l: Add support for ov5640 sensor
>  v4l: Add support for ov5650 sensor
>  arm: omap4430sdp: Add support for omap4iss camera
>  arm: omap4panda: Add support for omap4iss camera
>  arm: Add support for CMA for omap4iss driver
>
> Stanimir Varbanov (1):
>  v4l: Introduce sensor operation for getting interface configuration
>
>  Documentation/video4linux/omap4_camera.txt    |   60 ++
>  arch/arm/mach-omap2/Kconfig                   |   54 +
>  arch/arm/mach-omap2/Makefile                  |    3 +
>  arch/arm/mach-omap2/board-4430sdp-camera.c    |  221 ++++
>  arch/arm/mach-omap2/board-omap4panda-camera.c |  198 ++++
>  arch/arm/mach-omap2/devices.c                 |   40 +
>  arch/arm/mach-omap2/omap_hwmod_44xx_data.c    |   16 +-
>  arch/arm/plat-omap/include/plat/omap4-iss.h   |   42 +
>  arch/arm/plat-omap/include/plat/omap44xx.h    |    9 +
>  drivers/media/video/Kconfig                   |   25 +
>  drivers/media/video/Makefile                  |    3 +
>  drivers/media/video/omap4iss/Makefile         |    6 +
>  drivers/media/video/omap4iss/iss.c            | 1179 ++++++++++++++++++++++
>  drivers/media/video/omap4iss/iss.h            |  133 +++
>  drivers/media/video/omap4iss/iss_csi2.c       | 1324 +++++++++++++++++++++++++
>  drivers/media/video/omap4iss/iss_csi2.h       |  166 +++
>  drivers/media/video/omap4iss/iss_csiphy.c     |  215 ++++
>  drivers/media/video/omap4iss/iss_csiphy.h     |   69 ++
>  drivers/media/video/omap4iss/iss_regs.h       |  238 +++++
>  drivers/media/video/omap4iss/iss_video.c      | 1192 ++++++++++++++++++++++
>  drivers/media/video/omap4iss/iss_video.h      |  205 ++++
>  drivers/media/video/ov5640.c                  |  972 ++++++++++++++++++
>  drivers/media/video/ov5650.c                  |  524 ++++++++++
>  drivers/mfd/twl-core.c                        |    2 +-
>  include/linux/mfd/twl6040.h                   |    2 +-
>  include/media/ov5640.h                        |   10 +
>  include/media/ov5650.h                        |   10 +
>  include/media/v4l2-chip-ident.h               |    2 +
>  include/media/v4l2-subdev.h                   |   42 +
>  29 files changed, 6957 insertions(+), 5 deletions(-)
>  create mode 100644 Documentation/video4linux/omap4_camera.txt
>  create mode 100644 arch/arm/mach-omap2/board-4430sdp-camera.c
>  create mode 100644 arch/arm/mach-omap2/board-omap4panda-camera.c
>  create mode 100644 arch/arm/plat-omap/include/plat/omap4-iss.h
>  create mode 100644 drivers/media/video/omap4iss/Makefile
>  create mode 100644 drivers/media/video/omap4iss/iss.c
>  create mode 100644 drivers/media/video/omap4iss/iss.h
>  create mode 100644 drivers/media/video/omap4iss/iss_csi2.c
>  create mode 100644 drivers/media/video/omap4iss/iss_csi2.h
>  create mode 100644 drivers/media/video/omap4iss/iss_csiphy.c
>  create mode 100644 drivers/media/video/omap4iss/iss_csiphy.h
>  create mode 100644 drivers/media/video/omap4iss/iss_regs.h
>  create mode 100644 drivers/media/video/omap4iss/iss_video.c
>  create mode 100644 drivers/media/video/omap4iss/iss_video.h
>  create mode 100644 drivers/media/video/ov5640.c
>  create mode 100644 drivers/media/video/ov5650.c
>  create mode 100644 include/media/ov5640.h
>  create mode 100644 include/media/ov5650.h
>
> --
> 1.7.7.4
>

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

* Re: [PATCH v2 00/11] v4l2: OMAP4 ISS driver + Sensor + Board support
@ 2011-12-01  4:05   ` Aguirre, Sergio
  0 siblings, 0 replies; 65+ messages in thread
From: Aguirre, Sergio @ 2011-12-01  4:05 UTC (permalink / raw)
  To: linux-media; +Cc: linux-omap, laurent.pinchart, sakari.ailus, Sergio Aguirre

Hi again,

On Wed, Nov 30, 2011 at 6:14 PM, Sergio Aguirre <saaguirre@ti.com> wrote:
> Hi everyone,
>
> This is the second version of the OMAP4 ISS driver,
> now ported to the Media Controller framework AND supporting
> videobuf2 framework.
>
> This patchset should apply cleanly on top of v3.2-rc3 kernel tag.
>
> This driver attempts to provide an fully open source solution to
> control the OMAP4 Imaging SubSystem (a.k.a. ISS).
>
> Starts with just CSI2-A interface support, and pretends to be
> ready for expansion to add support to the many ISS block modules
> as possible.
>
> Please see newly added documentation for more details:
>
> Documentation/video4linux/omap4_camera.txt
>
> Any comments/complaints are welcome. :)
>
> Changes since v1:
> - Simplification of auxclk handling in board files. (Pointed out by: Roger Quadros)
> - Cleanup of Camera support enablement for 4430sdp & panda. (Pointed out by: Roger Quadros)
> - Use of HWMOD declaration for assisted platform_device creation. (Pointed out by: Felipe Balbi)
> - Videobuf2 migration (Removal of custom iss_queue buffer handling driver)
> - Proper GPO3 handling for CAM_SEL in 4430sdp.

Also, you can pull this as a branch in my git tree here:

Web URL: http://gitorious.org/omap4-v4l2-camera/omap4-v4l2-camera

git URL: git://gitorious.org/omap4-v4l2-camera/omap4-v4l2-camera.git
Branch: for3.2-rc3

Regards,
Sergio

>
> Sergio Aguirre (10):
>  TWL6030: Add mapping for auxiliary regs
>  mfd: twl6040: Fix wrong TWL6040_GPO3 bitfield value
>  OMAP4: hwmod: Include CSI2A and CSIPHY1 memory sections
>  OMAP4: Add base addresses for ISS
>  v4l: Add support for omap4iss driver
>  v4l: Add support for ov5640 sensor
>  v4l: Add support for ov5650 sensor
>  arm: omap4430sdp: Add support for omap4iss camera
>  arm: omap4panda: Add support for omap4iss camera
>  arm: Add support for CMA for omap4iss driver
>
> Stanimir Varbanov (1):
>  v4l: Introduce sensor operation for getting interface configuration
>
>  Documentation/video4linux/omap4_camera.txt    |   60 ++
>  arch/arm/mach-omap2/Kconfig                   |   54 +
>  arch/arm/mach-omap2/Makefile                  |    3 +
>  arch/arm/mach-omap2/board-4430sdp-camera.c    |  221 ++++
>  arch/arm/mach-omap2/board-omap4panda-camera.c |  198 ++++
>  arch/arm/mach-omap2/devices.c                 |   40 +
>  arch/arm/mach-omap2/omap_hwmod_44xx_data.c    |   16 +-
>  arch/arm/plat-omap/include/plat/omap4-iss.h   |   42 +
>  arch/arm/plat-omap/include/plat/omap44xx.h    |    9 +
>  drivers/media/video/Kconfig                   |   25 +
>  drivers/media/video/Makefile                  |    3 +
>  drivers/media/video/omap4iss/Makefile         |    6 +
>  drivers/media/video/omap4iss/iss.c            | 1179 ++++++++++++++++++++++
>  drivers/media/video/omap4iss/iss.h            |  133 +++
>  drivers/media/video/omap4iss/iss_csi2.c       | 1324 +++++++++++++++++++++++++
>  drivers/media/video/omap4iss/iss_csi2.h       |  166 +++
>  drivers/media/video/omap4iss/iss_csiphy.c     |  215 ++++
>  drivers/media/video/omap4iss/iss_csiphy.h     |   69 ++
>  drivers/media/video/omap4iss/iss_regs.h       |  238 +++++
>  drivers/media/video/omap4iss/iss_video.c      | 1192 ++++++++++++++++++++++
>  drivers/media/video/omap4iss/iss_video.h      |  205 ++++
>  drivers/media/video/ov5640.c                  |  972 ++++++++++++++++++
>  drivers/media/video/ov5650.c                  |  524 ++++++++++
>  drivers/mfd/twl-core.c                        |    2 +-
>  include/linux/mfd/twl6040.h                   |    2 +-
>  include/media/ov5640.h                        |   10 +
>  include/media/ov5650.h                        |   10 +
>  include/media/v4l2-chip-ident.h               |    2 +
>  include/media/v4l2-subdev.h                   |   42 +
>  29 files changed, 6957 insertions(+), 5 deletions(-)
>  create mode 100644 Documentation/video4linux/omap4_camera.txt
>  create mode 100644 arch/arm/mach-omap2/board-4430sdp-camera.c
>  create mode 100644 arch/arm/mach-omap2/board-omap4panda-camera.c
>  create mode 100644 arch/arm/plat-omap/include/plat/omap4-iss.h
>  create mode 100644 drivers/media/video/omap4iss/Makefile
>  create mode 100644 drivers/media/video/omap4iss/iss.c
>  create mode 100644 drivers/media/video/omap4iss/iss.h
>  create mode 100644 drivers/media/video/omap4iss/iss_csi2.c
>  create mode 100644 drivers/media/video/omap4iss/iss_csi2.h
>  create mode 100644 drivers/media/video/omap4iss/iss_csiphy.c
>  create mode 100644 drivers/media/video/omap4iss/iss_csiphy.h
>  create mode 100644 drivers/media/video/omap4iss/iss_regs.h
>  create mode 100644 drivers/media/video/omap4iss/iss_video.c
>  create mode 100644 drivers/media/video/omap4iss/iss_video.h
>  create mode 100644 drivers/media/video/ov5640.c
>  create mode 100644 drivers/media/video/ov5650.c
>  create mode 100644 include/media/ov5640.h
>  create mode 100644 include/media/ov5650.h
>
> --
> 1.7.7.4
>
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* RE: [PATCH v2 10/11] arm: omap4panda: Add support for omap4iss camera
  2011-12-01  0:14   ` Sergio Aguirre
  (?)
@ 2011-12-01  6:24   ` Hiremath, Vaibhav
  2011-12-01 13:34       ` Aguirre, Sergio
  -1 siblings, 1 reply; 65+ messages in thread
From: Hiremath, Vaibhav @ 2011-12-01  6:24 UTC (permalink / raw)
  To: Aguirre, Sergio, linux-media; +Cc: linux-omap, laurent.pinchart, sakari.ailus


> -----Original Message-----
> From: linux-omap-owner@vger.kernel.org [mailto:linux-omap-
> owner@vger.kernel.org] On Behalf Of Aguirre, Sergio
> Sent: Thursday, December 01, 2011 5:45 AM
> To: linux-media@vger.kernel.org
> Cc: linux-omap@vger.kernel.org; laurent.pinchart@ideasonboard.com;
> sakari.ailus@iki.fi; Aguirre, Sergio
> Subject: [PATCH v2 10/11] arm: omap4panda: Add support for omap4iss camera
> 
> This adds support for camera interface with the support for
> following sensors:
> 
> - OV5640
> - OV5650
> 
> Signed-off-by: Sergio Aguirre <saaguirre@ti.com>
> ---
>  arch/arm/mach-omap2/Kconfig                   |   27 ++++
>  arch/arm/mach-omap2/Makefile                  |    1 +
>  arch/arm/mach-omap2/board-omap4panda-camera.c |  198
> +++++++++++++++++++++++++
>  3 files changed, 226 insertions(+), 0 deletions(-)
>  create mode 100644 arch/arm/mach-omap2/board-omap4panda-camera.c
> 
> diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
> index f883abb..0fc5ce9 100644
> --- a/arch/arm/mach-omap2/Kconfig
> +++ b/arch/arm/mach-omap2/Kconfig
> @@ -358,6 +358,33 @@ config MACH_OMAP4_PANDA
>  	select OMAP_PACKAGE_CBS
>  	select REGULATOR_FIXED_VOLTAGE
> 
> +config MACH_OMAP4_PANDA_CAMERA_SUPPORT
> +	bool "OMAP4 Panda Board Camera support"
> +	depends on MACH_OMAP4_PANDA
> +	select MEDIA_SUPPORT
> +	select MEDIA_CONTROLLER
> +	select VIDEO_DEV
> +	select VIDEO_V4L2_SUBDEV_API
> +	select VIDEO_OMAP4
> +	help
> +	  Enable Camera HW support for PandaBoard.
> +	  This is for using the OMAP4 ISS CSI2A Camera sensor
> +	  interface.
> +
> +choice
> +	prompt "Camera sensor to use"
> +	depends on MACH_OMAP4_PANDA_CAMERA_SUPPORT
> +	default MACH_OMAP4_PANDA_CAM_OV5650
> +
> +	config MACH_OMAP4_PANDA_CAM_OV5640
> +		bool "Use OmniVision OV5640 Camera"
> +		select VIDEO_OV5640
> +
> +	config MACH_OMAP4_PANDA_CAM_OV5650
> +		bool "Use OmniVision OV5650 Camera"
> +		select VIDEO_OV5650
> +endchoice
> +
>  config OMAP3_EMU
>  	bool "OMAP3 debugging peripherals"
>  	depends on ARCH_OMAP3
> diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
> index 8bc446a..e80724d 100644
> --- a/arch/arm/mach-omap2/Makefile
> +++ b/arch/arm/mach-omap2/Makefile
> @@ -236,6 +236,7 @@ obj-$(CONFIG_MACH_TI8168EVM)		+= board-
> ti8168evm.o
>  # Platform specific device init code
> 
>  obj-$(CONFIG_MACH_OMAP_4430SDP_CAMERA_SUPPORT)	+= board-4430sdp-camera.o
> +obj-$(CONFIG_MACH_OMAP4_PANDA_CAMERA_SUPPORT)	+= board-omap4panda-
> camera.o
> 
Can't this be merged into single file? Do we really need separate file for every board here?

I am sure you would have thought about this.

>  omap-flash-$(CONFIG_MTD_NAND_OMAP2)	:= board-flash.o
>  omap-flash-$(CONFIG_MTD_ONENAND_OMAP2)	:= board-flash.o
> diff --git a/arch/arm/mach-omap2/board-omap4panda-camera.c
> b/arch/arm/mach-omap2/board-omap4panda-camera.c
> new file mode 100644
> index 0000000..02ef36e
> --- /dev/null
> +++ b/arch/arm/mach-omap2/board-omap4panda-camera.c
> @@ -0,0 +1,198 @@
> +#include <linux/gpio.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +
> +#include <plat/i2c.h>
> +#include <plat/omap-pm.h>
> +
> +#include <asm/mach-types.h>
> +
> +#include <media/ov5640.h>
> +#include <media/ov5650.h>
> +
> +#include "devices.h"
> +#include "../../../drivers/media/video/omap4iss/iss.h"
> +
I believe this is not good practice to include files directly from drivers folder. You should divide the header file such that, driver specific information stays in driver/... and platform specific goes into include/...

> +#include "control.h"
> +#include "mux.h"
> +
> +#define PANDA_GPIO_CAM_PWRDN		45
> +#define PANDA_GPIO_CAM_RESET		83
> +
> +static struct clk *panda_cam_aux_clk;
> +
> +static int panda_ov5640_power(struct v4l2_subdev *subdev, int on)
> +{
> +	struct iss_device *iss = v4l2_dev_to_iss_device(subdev->v4l2_dev);
> +	int ret = 0;
You are not using this variable at all, you can get rid of this.

> +	struct iss_csiphy_dphy_cfg dphy;
> +	struct iss_csiphy_lanes_cfg lanes;
> +	unsigned int ddr_freq = 480; /* FIXME: Do an actual query for this
> */
> +
> +	memset(&lanes, 0, sizeof(lanes));
> +	memset(&dphy, 0, sizeof(dphy));
> +
> +	lanes.clk.pos = 1;
> +	lanes.clk.pol = 0;
> +	lanes.data[0].pos = 2;
> +	lanes.data[0].pol = 0;
> +	lanes.data[1].pos = 3;
> +	lanes.data[1].pol = 0;
> +
> +	dphy.ths_term = ((((12500 * ddr_freq + 1000000) / 1000000) - 1) &
> 0xFF);
> +	dphy.ths_settle = ((((90000 * ddr_freq + 1000000) / 1000000) + 3) &
> 0xFF);
> +	dphy.tclk_term = 0;
> +	dphy.tclk_miss = 1;
> +	dphy.tclk_settle = 14;
> +
> +	if (on) {
> +		gpio_set_value(PANDA_GPIO_CAM_PWRDN, 0);
> +		clk_enable(panda_cam_aux_clk);
> +		mdelay(2);
> +
> +		iss->platform_cb.csiphy_config(&iss->csiphy1, &dphy, &lanes);
> +	} else {
> +		clk_disable(panda_cam_aux_clk);
> +		gpio_set_value(PANDA_GPIO_CAM_PWRDN, 1);
> +	}
You may want to check return values for above API's.

> +
> +	return ret;
> +}
> +
> +#define OV5640_I2C_ADDRESS   (0x3C)
> +#define OV5650_I2C_ADDRESS   (0x36)
> +
> +#ifdef CONFIG_MACH_OMAP4_PANDA_CAM_OV5650
> +static struct ov5650_platform_data ov_platform_data = {
> +#elif defined(CONFIG_MACH_OMAP4_PANDA_CAM_OV5640)
> +static struct ov5640_platform_data ov_platform_data = {
> +#endif
> +      .s_power = panda_ov5640_power,
> +};
> +
> +static struct i2c_board_info ov_camera_i2c_device = {
> +#ifdef CONFIG_MACH_OMAP4_PANDA_CAM_OV5650
> +	I2C_BOARD_INFO("ov5650", OV5650_I2C_ADDRESS),
> +#elif defined(CONFIG_MACH_OMAP4_PANDA_CAM_OV5640)
> +	I2C_BOARD_INFO("ov5640", OV5640_I2C_ADDRESS),
> +#endif
> +	.platform_data = &ov_platform_data,
> +};
> +
I may be wrong, but don't you think, we can clean all this #ifdefs here. 
The drivers will get connected cleanly without any issues based on probing and ID (i2c_device_id defined in driver).

Thanks,
Vaibhav

> +static struct iss_subdev_i2c_board_info ov_camera_subdevs[] = {
> +	{
> +		.board_info = &ov_camera_i2c_device,
> +		.i2c_adapter_id = 3,
> +	},
> +	{ NULL, 0, },
> +};
> +
> +static struct iss_v4l2_subdevs_group panda_camera_subdevs[] = {
> +	{
> +		.subdevs = ov_camera_subdevs,
> +		.interface = ISS_INTERFACE_CSI2A_PHY1,
> +	},
> +	{ },
> +};
> +
> +static void panda_omap4iss_set_constraints(struct iss_device *iss, bool
> enable)
> +{
> +	if (!iss)
> +		return;
> +
> +	/* FIXME: Look for something more precise as a good throughtput
> limit */
> +	omap_pm_set_min_bus_tput(iss->dev, OCP_INITIATOR_AGENT,
> +				 enable ? 800000 : -1);
> +}
> +
> +static struct iss_platform_data panda_iss_platform_data = {
> +	.subdevs = panda_camera_subdevs,
> +	.set_constraints = panda_omap4iss_set_constraints,
> +};
> +
> +
> +static struct omap_device_pad omap4iss_pads[] = {
> +	{
> +		.name   = "csi21_dx0.csi21_dx0",
> +		.enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
> +	},
> +	{
> +		.name   = "csi21_dy0.csi21_dy0",
> +		.enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
> +	},
> +	{
> +		.name   = "csi21_dx1.csi21_dx1",
> +		.enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
> +	},
> +	{
> +		.name   = "csi21_dy1.csi21_dy1",
> +		.enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
> +	},
> +	{
> +		.name   = "csi21_dx2.csi21_dx2",
> +		.enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
> +	},
> +	{
> +		.name   = "csi21_dy2.csi21_dy2",
> +		.enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
> +	},
> +};
> +
> +static struct omap_board_data omap4iss_data = {
> +	.id	    		= 1,
> +	.pads	 		= omap4iss_pads,
> +	.pads_cnt       	= ARRAY_SIZE(omap4iss_pads),
> +};
> +
> +static int __init panda_camera_init(void)
> +{
> +	if (!machine_is_omap4_panda())
> +		return 0;
> +
> +	panda_cam_aux_clk = clk_get(NULL, "auxclk1_ck");
> +	if (IS_ERR(panda_cam_aux_clk)) {
> +		printk(KERN_ERR "Unable to get auxclk1_ck\n");
> +		return -ENODEV;
> +	}
> +
> +	if (clk_set_rate(panda_cam_aux_clk,
> +			clk_round_rate(panda_cam_aux_clk, 24000000)))
> +		return -EINVAL;
> +
> +	/*
> +	 * CSI2 1(A):
> +	 *   LANEENABLE[4:0] = 00111(0x7) - Lanes 0, 1 & 2 enabled
> +	 *   CTRLCLKEN = 1 - Active high enable for CTRLCLK
> +	 *   CAMMODE = 0 - DPHY mode
> +	 */
> +	omap4_ctrl_pad_writel((omap4_ctrl_pad_readl(
> +				OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_CAMERA_RX) &
> +			  ~(OMAP4_CAMERARX_CSI21_LANEENABLE_MASK |
> +			    OMAP4_CAMERARX_CSI21_CAMMODE_MASK)) |
> +			 (0x7 << OMAP4_CAMERARX_CSI21_LANEENABLE_SHIFT) |
> +			 OMAP4_CAMERARX_CSI21_CTRLCLKEN_MASK,
> +			 OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_CAMERA_RX);
> +
> +	/* Select GPIO 45 */
> +	omap_mux_init_gpio(PANDA_GPIO_CAM_PWRDN, OMAP_PIN_OUTPUT);
> +
> +	/* Select GPIO 83 */
> +	omap_mux_init_gpio(PANDA_GPIO_CAM_RESET, OMAP_PIN_OUTPUT);
> +
> +	/* Init FREF_CLK1_OUT */
> +	omap_mux_init_signal("fref_clk1_out", OMAP_PIN_OUTPUT);
> +
> +	if (gpio_request_one(PANDA_GPIO_CAM_PWRDN, GPIOF_OUT_INIT_HIGH,
> +			     "CAM_PWRDN"))
> +		printk(KERN_WARNING "Cannot request GPIO %d\n",
> +			PANDA_GPIO_CAM_PWRDN);
> +
> +	if (gpio_request_one(PANDA_GPIO_CAM_RESET, GPIOF_OUT_INIT_HIGH,
> +			     "CAM_RESET"))
> +		printk(KERN_WARNING "Cannot request GPIO %d\n",
> +			PANDA_GPIO_CAM_RESET);
> +
> +	omap4_init_camera(&panda_iss_platform_data, &omap4iss_data);
> +	return 0;
> +}
> +late_initcall(panda_camera_init);
> --
> 1.7.7.4
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* RE: [PATCH v2 04/11] OMAP4: hwmod: Include CSI2A and CSIPHY1 memory sections
  2011-12-01  0:14   ` Sergio Aguirre
  (?)
@ 2011-12-01  6:34   ` Hiremath, Vaibhav
  2011-12-01 13:17       ` Aguirre, Sergio
  -1 siblings, 1 reply; 65+ messages in thread
From: Hiremath, Vaibhav @ 2011-12-01  6:34 UTC (permalink / raw)
  To: Aguirre, Sergio, linux-media; +Cc: linux-omap, laurent.pinchart, sakari.ailus


> -----Original Message-----
> From: linux-media-owner@vger.kernel.org [mailto:linux-media-
> owner@vger.kernel.org] On Behalf Of Aguirre, Sergio
> Sent: Thursday, December 01, 2011 5:45 AM
> To: linux-media@vger.kernel.org
> Cc: linux-omap@vger.kernel.org; laurent.pinchart@ideasonboard.com;
> sakari.ailus@iki.fi; Aguirre, Sergio
> Subject: [PATCH v2 04/11] OMAP4: hwmod: Include CSI2A and CSIPHY1 memory
> sections
> 
> Signed-off-by: Sergio Aguirre <saaguirre@ti.com>
> ---
>  arch/arm/mach-omap2/omap_hwmod_44xx_data.c |   16 +++++++++++++---
>  1 files changed, 13 insertions(+), 3 deletions(-)
> 
> diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-
> omap2/omap_hwmod_44xx_data.c
> index 7695e5d..1b59e2f 100644
> --- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
> +++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
> @@ -2623,8 +2623,18 @@ static struct omap_hwmod_ocp_if
> *omap44xx_iss_masters[] = {
> 
>  static struct omap_hwmod_addr_space omap44xx_iss_addrs[] = {
>  	{
> -		.pa_start	= 0x52000000,
> -		.pa_end		= 0x520000ff,
> +		.pa_start	= OMAP44XX_ISS_TOP_BASE,
> +		.pa_end		= OMAP44XX_ISS_TOP_END,
> +		.flags		= ADDR_TYPE_RT
> +	},
> +	{
> +		.pa_start	= OMAP44XX_ISS_CSI2_A_REGS1_BASE,
> +		.pa_end		= OMAP44XX_ISS_CSI2_A_REGS1_END,
> +		.flags		= ADDR_TYPE_RT
> +	},
> +	{
> +		.pa_start	= OMAP44XX_ISS_CAMERARX_CORE1_BASE,
> +		.pa_end		= OMAP44XX_ISS_CAMERARX_CORE1_END,
>  		.flags		= ADDR_TYPE_RT
>  	},
This patch will result in build failure, because, the above base addresses
are getting defined in the next patch

[PATCH v2 05/11] OMAP4: Add base addresses for ISS

Thanks,
Vaibhav

>  	{ }
> @@ -5350,7 +5360,7 @@ static __initdata struct omap_hwmod
> *omap44xx_hwmods[] = {
>  	&omap44xx_ipu_c1_hwmod,
> 
>  	/* iss class */
> -/*	&omap44xx_iss_hwmod, */
> +	&omap44xx_iss_hwmod,
> 
>  	/* iva class */
>  	&omap44xx_iva_hwmod,
> --
> 1.7.7.4
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH v2 04/11] OMAP4: hwmod: Include CSI2A and CSIPHY1 memory sections
  2011-12-01  6:34   ` Hiremath, Vaibhav
@ 2011-12-01 13:17       ` Aguirre, Sergio
  0 siblings, 0 replies; 65+ messages in thread
From: Aguirre, Sergio @ 2011-12-01 13:17 UTC (permalink / raw)
  To: Hiremath, Vaibhav; +Cc: linux-media, linux-omap, laurent.pinchart, sakari.ailus

Hi Vaibhav,

Thanks for the comments.

On Thu, Dec 1, 2011 at 12:34 AM, Hiremath, Vaibhav <hvaibhav@ti.com> wrote:
>
>> -----Original Message-----
>> From: linux-media-owner@vger.kernel.org [mailto:linux-media-
>> owner@vger.kernel.org] On Behalf Of Aguirre, Sergio
>> Sent: Thursday, December 01, 2011 5:45 AM
>> To: linux-media@vger.kernel.org
>> Cc: linux-omap@vger.kernel.org; laurent.pinchart@ideasonboard.com;
>> sakari.ailus@iki.fi; Aguirre, Sergio
>> Subject: [PATCH v2 04/11] OMAP4: hwmod: Include CSI2A and CSIPHY1 memory
>> sections
>>
>> Signed-off-by: Sergio Aguirre <saaguirre@ti.com>
>> ---
>>  arch/arm/mach-omap2/omap_hwmod_44xx_data.c |   16 +++++++++++++---
>>  1 files changed, 13 insertions(+), 3 deletions(-)
>>
>> diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-
>> omap2/omap_hwmod_44xx_data.c
>> index 7695e5d..1b59e2f 100644
>> --- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
>> +++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
>> @@ -2623,8 +2623,18 @@ static struct omap_hwmod_ocp_if
>> *omap44xx_iss_masters[] = {
>>
>>  static struct omap_hwmod_addr_space omap44xx_iss_addrs[] = {
>>       {
>> -             .pa_start       = 0x52000000,
>> -             .pa_end         = 0x520000ff,
>> +             .pa_start       = OMAP44XX_ISS_TOP_BASE,
>> +             .pa_end         = OMAP44XX_ISS_TOP_END,
>> +             .flags          = ADDR_TYPE_RT
>> +     },
>> +     {
>> +             .pa_start       = OMAP44XX_ISS_CSI2_A_REGS1_BASE,
>> +             .pa_end         = OMAP44XX_ISS_CSI2_A_REGS1_END,
>> +             .flags          = ADDR_TYPE_RT
>> +     },
>> +     {
>> +             .pa_start       = OMAP44XX_ISS_CAMERARX_CORE1_BASE,
>> +             .pa_end         = OMAP44XX_ISS_CAMERARX_CORE1_END,
>>               .flags          = ADDR_TYPE_RT
>>       },
> This patch will result in build failure, because, the above base addresses
> are getting defined in the next patch
>
> [PATCH v2 05/11] OMAP4: Add base addresses for ISS

Agreed. Will revisit "git-bisectability" of the patch series. Will fix.

Regards,
Sergio

>
> Thanks,
> Vaibhav
>
>>       { }
>> @@ -5350,7 +5360,7 @@ static __initdata struct omap_hwmod
>> *omap44xx_hwmods[] = {
>>       &omap44xx_ipu_c1_hwmod,
>>
>>       /* iss class */
>> -/*   &omap44xx_iss_hwmod, */
>> +     &omap44xx_iss_hwmod,
>>
>>       /* iva class */
>>       &omap44xx_iva_hwmod,
>> --
>> 1.7.7.4
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-media" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH v2 04/11] OMAP4: hwmod: Include CSI2A and CSIPHY1 memory sections
@ 2011-12-01 13:17       ` Aguirre, Sergio
  0 siblings, 0 replies; 65+ messages in thread
From: Aguirre, Sergio @ 2011-12-01 13:17 UTC (permalink / raw)
  To: Hiremath, Vaibhav; +Cc: linux-media, linux-omap, laurent.pinchart, sakari.ailus

Hi Vaibhav,

Thanks for the comments.

On Thu, Dec 1, 2011 at 12:34 AM, Hiremath, Vaibhav <hvaibhav@ti.com> wrote:
>
>> -----Original Message-----
>> From: linux-media-owner@vger.kernel.org [mailto:linux-media-
>> owner@vger.kernel.org] On Behalf Of Aguirre, Sergio
>> Sent: Thursday, December 01, 2011 5:45 AM
>> To: linux-media@vger.kernel.org
>> Cc: linux-omap@vger.kernel.org; laurent.pinchart@ideasonboard.com;
>> sakari.ailus@iki.fi; Aguirre, Sergio
>> Subject: [PATCH v2 04/11] OMAP4: hwmod: Include CSI2A and CSIPHY1 memory
>> sections
>>
>> Signed-off-by: Sergio Aguirre <saaguirre@ti.com>
>> ---
>>  arch/arm/mach-omap2/omap_hwmod_44xx_data.c |   16 +++++++++++++---
>>  1 files changed, 13 insertions(+), 3 deletions(-)
>>
>> diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-
>> omap2/omap_hwmod_44xx_data.c
>> index 7695e5d..1b59e2f 100644
>> --- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
>> +++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
>> @@ -2623,8 +2623,18 @@ static struct omap_hwmod_ocp_if
>> *omap44xx_iss_masters[] = {
>>
>>  static struct omap_hwmod_addr_space omap44xx_iss_addrs[] = {
>>       {
>> -             .pa_start       = 0x52000000,
>> -             .pa_end         = 0x520000ff,
>> +             .pa_start       = OMAP44XX_ISS_TOP_BASE,
>> +             .pa_end         = OMAP44XX_ISS_TOP_END,
>> +             .flags          = ADDR_TYPE_RT
>> +     },
>> +     {
>> +             .pa_start       = OMAP44XX_ISS_CSI2_A_REGS1_BASE,
>> +             .pa_end         = OMAP44XX_ISS_CSI2_A_REGS1_END,
>> +             .flags          = ADDR_TYPE_RT
>> +     },
>> +     {
>> +             .pa_start       = OMAP44XX_ISS_CAMERARX_CORE1_BASE,
>> +             .pa_end         = OMAP44XX_ISS_CAMERARX_CORE1_END,
>>               .flags          = ADDR_TYPE_RT
>>       },
> This patch will result in build failure, because, the above base addresses
> are getting defined in the next patch
>
> [PATCH v2 05/11] OMAP4: Add base addresses for ISS

Agreed. Will revisit "git-bisectability" of the patch series. Will fix.

Regards,
Sergio

>
> Thanks,
> Vaibhav
>
>>       { }
>> @@ -5350,7 +5360,7 @@ static __initdata struct omap_hwmod
>> *omap44xx_hwmods[] = {
>>       &omap44xx_ipu_c1_hwmod,
>>
>>       /* iss class */
>> -/*   &omap44xx_iss_hwmod, */
>> +     &omap44xx_iss_hwmod,
>>
>>       /* iva class */
>>       &omap44xx_iva_hwmod,
>> --
>> 1.7.7.4
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-media" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH v2 10/11] arm: omap4panda: Add support for omap4iss camera
  2011-12-01  6:24   ` Hiremath, Vaibhav
@ 2011-12-01 13:34       ` Aguirre, Sergio
  0 siblings, 0 replies; 65+ messages in thread
From: Aguirre, Sergio @ 2011-12-01 13:34 UTC (permalink / raw)
  To: Hiremath, Vaibhav; +Cc: linux-media, linux-omap, laurent.pinchart, sakari.ailus

Hi Vaibhav,

Thanks for the comments.

On Thu, Dec 1, 2011 at 12:24 AM, Hiremath, Vaibhav <hvaibhav@ti.com> wrote:
>
>> -----Original Message-----
>> From: linux-omap-owner@vger.kernel.org [mailto:linux-omap-
>> owner@vger.kernel.org] On Behalf Of Aguirre, Sergio
>> Sent: Thursday, December 01, 2011 5:45 AM
>> To: linux-media@vger.kernel.org
>> Cc: linux-omap@vger.kernel.org; laurent.pinchart@ideasonboard.com;
>> sakari.ailus@iki.fi; Aguirre, Sergio
>> Subject: [PATCH v2 10/11] arm: omap4panda: Add support for omap4iss camera
>>
>> This adds support for camera interface with the support for
>> following sensors:
>>
>> - OV5640
>> - OV5650
>>
>> Signed-off-by: Sergio Aguirre <saaguirre@ti.com>
>> ---
>>  arch/arm/mach-omap2/Kconfig                   |   27 ++++
>>  arch/arm/mach-omap2/Makefile                  |    1 +
>>  arch/arm/mach-omap2/board-omap4panda-camera.c |  198
>> +++++++++++++++++++++++++
>>  3 files changed, 226 insertions(+), 0 deletions(-)
>>  create mode 100644 arch/arm/mach-omap2/board-omap4panda-camera.c
>>
>> diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
>> index f883abb..0fc5ce9 100644
>> --- a/arch/arm/mach-omap2/Kconfig
>> +++ b/arch/arm/mach-omap2/Kconfig
>> @@ -358,6 +358,33 @@ config MACH_OMAP4_PANDA
>>       select OMAP_PACKAGE_CBS
>>       select REGULATOR_FIXED_VOLTAGE
>>
>> +config MACH_OMAP4_PANDA_CAMERA_SUPPORT
>> +     bool "OMAP4 Panda Board Camera support"
>> +     depends on MACH_OMAP4_PANDA
>> +     select MEDIA_SUPPORT
>> +     select MEDIA_CONTROLLER
>> +     select VIDEO_DEV
>> +     select VIDEO_V4L2_SUBDEV_API
>> +     select VIDEO_OMAP4
>> +     help
>> +       Enable Camera HW support for PandaBoard.
>> +       This is for using the OMAP4 ISS CSI2A Camera sensor
>> +       interface.
>> +
>> +choice
>> +     prompt "Camera sensor to use"
>> +     depends on MACH_OMAP4_PANDA_CAMERA_SUPPORT
>> +     default MACH_OMAP4_PANDA_CAM_OV5650
>> +
>> +     config MACH_OMAP4_PANDA_CAM_OV5640
>> +             bool "Use OmniVision OV5640 Camera"
>> +             select VIDEO_OV5640
>> +
>> +     config MACH_OMAP4_PANDA_CAM_OV5650
>> +             bool "Use OmniVision OV5650 Camera"
>> +             select VIDEO_OV5650
>> +endchoice
>> +
>>  config OMAP3_EMU
>>       bool "OMAP3 debugging peripherals"
>>       depends on ARCH_OMAP3
>> diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
>> index 8bc446a..e80724d 100644
>> --- a/arch/arm/mach-omap2/Makefile
>> +++ b/arch/arm/mach-omap2/Makefile
>> @@ -236,6 +236,7 @@ obj-$(CONFIG_MACH_TI8168EVM)              += board-
>> ti8168evm.o
>>  # Platform specific device init code
>>
>>  obj-$(CONFIG_MACH_OMAP_4430SDP_CAMERA_SUPPORT)       += board-4430sdp-camera.o
>> +obj-$(CONFIG_MACH_OMAP4_PANDA_CAMERA_SUPPORT)        += board-omap4panda-
>> camera.o
>>
> Can't this be merged into single file? Do we really need separate file for every board here?
>
> I am sure you would have thought about this.

:)

Well, not really, to be honest.

I could have a single file, but wouldn't that make it more complex?

I mean, every board has it's own HW connection specs, like sensor MCLK source,
and different power up sequences. How would you probe for all, without messing
with an invalid GPIO on that board?

Have you resolved a similar case for other OMAP/Davinci boards?

>
>>  omap-flash-$(CONFIG_MTD_NAND_OMAP2)  := board-flash.o
>>  omap-flash-$(CONFIG_MTD_ONENAND_OMAP2)       := board-flash.o
>> diff --git a/arch/arm/mach-omap2/board-omap4panda-camera.c
>> b/arch/arm/mach-omap2/board-omap4panda-camera.c
>> new file mode 100644
>> index 0000000..02ef36e
>> --- /dev/null
>> +++ b/arch/arm/mach-omap2/board-omap4panda-camera.c
>> @@ -0,0 +1,198 @@
>> +#include <linux/gpio.h>
>> +#include <linux/clk.h>
>> +#include <linux/delay.h>
>> +
>> +#include <plat/i2c.h>
>> +#include <plat/omap-pm.h>
>> +
>> +#include <asm/mach-types.h>
>> +
>> +#include <media/ov5640.h>
>> +#include <media/ov5650.h>
>> +
>> +#include "devices.h"
>> +#include "../../../drivers/media/video/omap4iss/iss.h"
>> +
> I believe this is not good practice to include files directly from drivers folder. You should divide the header file such that, driver specific information stays in driver/... and platform specific goes into include/...

Right. Will fix.

>
>> +#include "control.h"
>> +#include "mux.h"
>> +
>> +#define PANDA_GPIO_CAM_PWRDN         45
>> +#define PANDA_GPIO_CAM_RESET         83
>> +
>> +static struct clk *panda_cam_aux_clk;
>> +
>> +static int panda_ov5640_power(struct v4l2_subdev *subdev, int on)
>> +{
>> +     struct iss_device *iss = v4l2_dev_to_iss_device(subdev->v4l2_dev);
>> +     int ret = 0;
> You are not using this variable at all, you can get rid of this.

Right. Will fix.

>
>> +     struct iss_csiphy_dphy_cfg dphy;
>> +     struct iss_csiphy_lanes_cfg lanes;
>> +     unsigned int ddr_freq = 480; /* FIXME: Do an actual query for this
>> */
>> +
>> +     memset(&lanes, 0, sizeof(lanes));
>> +     memset(&dphy, 0, sizeof(dphy));
>> +
>> +     lanes.clk.pos = 1;
>> +     lanes.clk.pol = 0;
>> +     lanes.data[0].pos = 2;
>> +     lanes.data[0].pol = 0;
>> +     lanes.data[1].pos = 3;
>> +     lanes.data[1].pol = 0;
>> +
>> +     dphy.ths_term = ((((12500 * ddr_freq + 1000000) / 1000000) - 1) &
>> 0xFF);
>> +     dphy.ths_settle = ((((90000 * ddr_freq + 1000000) / 1000000) + 3) &
>> 0xFF);
>> +     dphy.tclk_term = 0;
>> +     dphy.tclk_miss = 1;
>> +     dphy.tclk_settle = 14;
>> +
>> +     if (on) {
>> +             gpio_set_value(PANDA_GPIO_CAM_PWRDN, 0);
>> +             clk_enable(panda_cam_aux_clk);
>> +             mdelay(2);
>> +
>> +             iss->platform_cb.csiphy_config(&iss->csiphy1, &dphy, &lanes);
>> +     } else {
>> +             clk_disable(panda_cam_aux_clk);
>> +             gpio_set_value(PANDA_GPIO_CAM_PWRDN, 1);
>> +     }
> You may want to check return values for above API's.

Ok. Will fix.

>
>> +
>> +     return ret;
>> +}
>> +
>> +#define OV5640_I2C_ADDRESS   (0x3C)
>> +#define OV5650_I2C_ADDRESS   (0x36)
>> +
>> +#ifdef CONFIG_MACH_OMAP4_PANDA_CAM_OV5650
>> +static struct ov5650_platform_data ov_platform_data = {
>> +#elif defined(CONFIG_MACH_OMAP4_PANDA_CAM_OV5640)
>> +static struct ov5640_platform_data ov_platform_data = {
>> +#endif
>> +      .s_power = panda_ov5640_power,
>> +};
>> +
>> +static struct i2c_board_info ov_camera_i2c_device = {
>> +#ifdef CONFIG_MACH_OMAP4_PANDA_CAM_OV5650
>> +     I2C_BOARD_INFO("ov5650", OV5650_I2C_ADDRESS),
>> +#elif defined(CONFIG_MACH_OMAP4_PANDA_CAM_OV5640)
>> +     I2C_BOARD_INFO("ov5640", OV5640_I2C_ADDRESS),
>> +#endif
>> +     .platform_data = &ov_platform_data,
>> +};
>> +
> I may be wrong, but don't you think, we can clean all this #ifdefs here.
> The drivers will get connected cleanly without any issues based on probing and ID (i2c_device_id defined in driver).

Will think about it.

Thanks again!

Regards,
Sergio

>
> Thanks,
> Vaibhav
>
>> +static struct iss_subdev_i2c_board_info ov_camera_subdevs[] = {
>> +     {
>> +             .board_info = &ov_camera_i2c_device,
>> +             .i2c_adapter_id = 3,
>> +     },
>> +     { NULL, 0, },
>> +};
>> +
>> +static struct iss_v4l2_subdevs_group panda_camera_subdevs[] = {
>> +     {
>> +             .subdevs = ov_camera_subdevs,
>> +             .interface = ISS_INTERFACE_CSI2A_PHY1,
>> +     },
>> +     { },
>> +};
>> +
>> +static void panda_omap4iss_set_constraints(struct iss_device *iss, bool
>> enable)
>> +{
>> +     if (!iss)
>> +             return;
>> +
>> +     /* FIXME: Look for something more precise as a good throughtput
>> limit */
>> +     omap_pm_set_min_bus_tput(iss->dev, OCP_INITIATOR_AGENT,
>> +                              enable ? 800000 : -1);
>> +}
>> +
>> +static struct iss_platform_data panda_iss_platform_data = {
>> +     .subdevs = panda_camera_subdevs,
>> +     .set_constraints = panda_omap4iss_set_constraints,
>> +};
>> +
>> +
>> +static struct omap_device_pad omap4iss_pads[] = {
>> +     {
>> +             .name   = "csi21_dx0.csi21_dx0",
>> +             .enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
>> +     },
>> +     {
>> +             .name   = "csi21_dy0.csi21_dy0",
>> +             .enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
>> +     },
>> +     {
>> +             .name   = "csi21_dx1.csi21_dx1",
>> +             .enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
>> +     },
>> +     {
>> +             .name   = "csi21_dy1.csi21_dy1",
>> +             .enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
>> +     },
>> +     {
>> +             .name   = "csi21_dx2.csi21_dx2",
>> +             .enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
>> +     },
>> +     {
>> +             .name   = "csi21_dy2.csi21_dy2",
>> +             .enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
>> +     },
>> +};
>> +
>> +static struct omap_board_data omap4iss_data = {
>> +     .id                     = 1,
>> +     .pads                   = omap4iss_pads,
>> +     .pads_cnt               = ARRAY_SIZE(omap4iss_pads),
>> +};
>> +
>> +static int __init panda_camera_init(void)
>> +{
>> +     if (!machine_is_omap4_panda())
>> +             return 0;
>> +
>> +     panda_cam_aux_clk = clk_get(NULL, "auxclk1_ck");
>> +     if (IS_ERR(panda_cam_aux_clk)) {
>> +             printk(KERN_ERR "Unable to get auxclk1_ck\n");
>> +             return -ENODEV;
>> +     }
>> +
>> +     if (clk_set_rate(panda_cam_aux_clk,
>> +                     clk_round_rate(panda_cam_aux_clk, 24000000)))
>> +             return -EINVAL;
>> +
>> +     /*
>> +      * CSI2 1(A):
>> +      *   LANEENABLE[4:0] = 00111(0x7) - Lanes 0, 1 & 2 enabled
>> +      *   CTRLCLKEN = 1 - Active high enable for CTRLCLK
>> +      *   CAMMODE = 0 - DPHY mode
>> +      */
>> +     omap4_ctrl_pad_writel((omap4_ctrl_pad_readl(
>> +                             OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_CAMERA_RX) &
>> +                       ~(OMAP4_CAMERARX_CSI21_LANEENABLE_MASK |
>> +                         OMAP4_CAMERARX_CSI21_CAMMODE_MASK)) |
>> +                      (0x7 << OMAP4_CAMERARX_CSI21_LANEENABLE_SHIFT) |
>> +                      OMAP4_CAMERARX_CSI21_CTRLCLKEN_MASK,
>> +                      OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_CAMERA_RX);
>> +
>> +     /* Select GPIO 45 */
>> +     omap_mux_init_gpio(PANDA_GPIO_CAM_PWRDN, OMAP_PIN_OUTPUT);
>> +
>> +     /* Select GPIO 83 */
>> +     omap_mux_init_gpio(PANDA_GPIO_CAM_RESET, OMAP_PIN_OUTPUT);
>> +
>> +     /* Init FREF_CLK1_OUT */
>> +     omap_mux_init_signal("fref_clk1_out", OMAP_PIN_OUTPUT);
>> +
>> +     if (gpio_request_one(PANDA_GPIO_CAM_PWRDN, GPIOF_OUT_INIT_HIGH,
>> +                          "CAM_PWRDN"))
>> +             printk(KERN_WARNING "Cannot request GPIO %d\n",
>> +                     PANDA_GPIO_CAM_PWRDN);
>> +
>> +     if (gpio_request_one(PANDA_GPIO_CAM_RESET, GPIOF_OUT_INIT_HIGH,
>> +                          "CAM_RESET"))
>> +             printk(KERN_WARNING "Cannot request GPIO %d\n",
>> +                     PANDA_GPIO_CAM_RESET);
>> +
>> +     omap4_init_camera(&panda_iss_platform_data, &omap4iss_data);
>> +     return 0;
>> +}
>> +late_initcall(panda_camera_init);
>> --
>> 1.7.7.4
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH v2 10/11] arm: omap4panda: Add support for omap4iss camera
@ 2011-12-01 13:34       ` Aguirre, Sergio
  0 siblings, 0 replies; 65+ messages in thread
From: Aguirre, Sergio @ 2011-12-01 13:34 UTC (permalink / raw)
  To: Hiremath, Vaibhav; +Cc: linux-media, linux-omap, laurent.pinchart, sakari.ailus

Hi Vaibhav,

Thanks for the comments.

On Thu, Dec 1, 2011 at 12:24 AM, Hiremath, Vaibhav <hvaibhav@ti.com> wrote:
>
>> -----Original Message-----
>> From: linux-omap-owner@vger.kernel.org [mailto:linux-omap-
>> owner@vger.kernel.org] On Behalf Of Aguirre, Sergio
>> Sent: Thursday, December 01, 2011 5:45 AM
>> To: linux-media@vger.kernel.org
>> Cc: linux-omap@vger.kernel.org; laurent.pinchart@ideasonboard.com;
>> sakari.ailus@iki.fi; Aguirre, Sergio
>> Subject: [PATCH v2 10/11] arm: omap4panda: Add support for omap4iss camera
>>
>> This adds support for camera interface with the support for
>> following sensors:
>>
>> - OV5640
>> - OV5650
>>
>> Signed-off-by: Sergio Aguirre <saaguirre@ti.com>
>> ---
>>  arch/arm/mach-omap2/Kconfig                   |   27 ++++
>>  arch/arm/mach-omap2/Makefile                  |    1 +
>>  arch/arm/mach-omap2/board-omap4panda-camera.c |  198
>> +++++++++++++++++++++++++
>>  3 files changed, 226 insertions(+), 0 deletions(-)
>>  create mode 100644 arch/arm/mach-omap2/board-omap4panda-camera.c
>>
>> diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
>> index f883abb..0fc5ce9 100644
>> --- a/arch/arm/mach-omap2/Kconfig
>> +++ b/arch/arm/mach-omap2/Kconfig
>> @@ -358,6 +358,33 @@ config MACH_OMAP4_PANDA
>>       select OMAP_PACKAGE_CBS
>>       select REGULATOR_FIXED_VOLTAGE
>>
>> +config MACH_OMAP4_PANDA_CAMERA_SUPPORT
>> +     bool "OMAP4 Panda Board Camera support"
>> +     depends on MACH_OMAP4_PANDA
>> +     select MEDIA_SUPPORT
>> +     select MEDIA_CONTROLLER
>> +     select VIDEO_DEV
>> +     select VIDEO_V4L2_SUBDEV_API
>> +     select VIDEO_OMAP4
>> +     help
>> +       Enable Camera HW support for PandaBoard.
>> +       This is for using the OMAP4 ISS CSI2A Camera sensor
>> +       interface.
>> +
>> +choice
>> +     prompt "Camera sensor to use"
>> +     depends on MACH_OMAP4_PANDA_CAMERA_SUPPORT
>> +     default MACH_OMAP4_PANDA_CAM_OV5650
>> +
>> +     config MACH_OMAP4_PANDA_CAM_OV5640
>> +             bool "Use OmniVision OV5640 Camera"
>> +             select VIDEO_OV5640
>> +
>> +     config MACH_OMAP4_PANDA_CAM_OV5650
>> +             bool "Use OmniVision OV5650 Camera"
>> +             select VIDEO_OV5650
>> +endchoice
>> +
>>  config OMAP3_EMU
>>       bool "OMAP3 debugging peripherals"
>>       depends on ARCH_OMAP3
>> diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
>> index 8bc446a..e80724d 100644
>> --- a/arch/arm/mach-omap2/Makefile
>> +++ b/arch/arm/mach-omap2/Makefile
>> @@ -236,6 +236,7 @@ obj-$(CONFIG_MACH_TI8168EVM)              += board-
>> ti8168evm.o
>>  # Platform specific device init code
>>
>>  obj-$(CONFIG_MACH_OMAP_4430SDP_CAMERA_SUPPORT)       += board-4430sdp-camera.o
>> +obj-$(CONFIG_MACH_OMAP4_PANDA_CAMERA_SUPPORT)        += board-omap4panda-
>> camera.o
>>
> Can't this be merged into single file? Do we really need separate file for every board here?
>
> I am sure you would have thought about this.

:)

Well, not really, to be honest.

I could have a single file, but wouldn't that make it more complex?

I mean, every board has it's own HW connection specs, like sensor MCLK source,
and different power up sequences. How would you probe for all, without messing
with an invalid GPIO on that board?

Have you resolved a similar case for other OMAP/Davinci boards?

>
>>  omap-flash-$(CONFIG_MTD_NAND_OMAP2)  := board-flash.o
>>  omap-flash-$(CONFIG_MTD_ONENAND_OMAP2)       := board-flash.o
>> diff --git a/arch/arm/mach-omap2/board-omap4panda-camera.c
>> b/arch/arm/mach-omap2/board-omap4panda-camera.c
>> new file mode 100644
>> index 0000000..02ef36e
>> --- /dev/null
>> +++ b/arch/arm/mach-omap2/board-omap4panda-camera.c
>> @@ -0,0 +1,198 @@
>> +#include <linux/gpio.h>
>> +#include <linux/clk.h>
>> +#include <linux/delay.h>
>> +
>> +#include <plat/i2c.h>
>> +#include <plat/omap-pm.h>
>> +
>> +#include <asm/mach-types.h>
>> +
>> +#include <media/ov5640.h>
>> +#include <media/ov5650.h>
>> +
>> +#include "devices.h"
>> +#include "../../../drivers/media/video/omap4iss/iss.h"
>> +
> I believe this is not good practice to include files directly from drivers folder. You should divide the header file such that, driver specific information stays in driver/... and platform specific goes into include/...

Right. Will fix.

>
>> +#include "control.h"
>> +#include "mux.h"
>> +
>> +#define PANDA_GPIO_CAM_PWRDN         45
>> +#define PANDA_GPIO_CAM_RESET         83
>> +
>> +static struct clk *panda_cam_aux_clk;
>> +
>> +static int panda_ov5640_power(struct v4l2_subdev *subdev, int on)
>> +{
>> +     struct iss_device *iss = v4l2_dev_to_iss_device(subdev->v4l2_dev);
>> +     int ret = 0;
> You are not using this variable at all, you can get rid of this.

Right. Will fix.

>
>> +     struct iss_csiphy_dphy_cfg dphy;
>> +     struct iss_csiphy_lanes_cfg lanes;
>> +     unsigned int ddr_freq = 480; /* FIXME: Do an actual query for this
>> */
>> +
>> +     memset(&lanes, 0, sizeof(lanes));
>> +     memset(&dphy, 0, sizeof(dphy));
>> +
>> +     lanes.clk.pos = 1;
>> +     lanes.clk.pol = 0;
>> +     lanes.data[0].pos = 2;
>> +     lanes.data[0].pol = 0;
>> +     lanes.data[1].pos = 3;
>> +     lanes.data[1].pol = 0;
>> +
>> +     dphy.ths_term = ((((12500 * ddr_freq + 1000000) / 1000000) - 1) &
>> 0xFF);
>> +     dphy.ths_settle = ((((90000 * ddr_freq + 1000000) / 1000000) + 3) &
>> 0xFF);
>> +     dphy.tclk_term = 0;
>> +     dphy.tclk_miss = 1;
>> +     dphy.tclk_settle = 14;
>> +
>> +     if (on) {
>> +             gpio_set_value(PANDA_GPIO_CAM_PWRDN, 0);
>> +             clk_enable(panda_cam_aux_clk);
>> +             mdelay(2);
>> +
>> +             iss->platform_cb.csiphy_config(&iss->csiphy1, &dphy, &lanes);
>> +     } else {
>> +             clk_disable(panda_cam_aux_clk);
>> +             gpio_set_value(PANDA_GPIO_CAM_PWRDN, 1);
>> +     }
> You may want to check return values for above API's.

Ok. Will fix.

>
>> +
>> +     return ret;
>> +}
>> +
>> +#define OV5640_I2C_ADDRESS   (0x3C)
>> +#define OV5650_I2C_ADDRESS   (0x36)
>> +
>> +#ifdef CONFIG_MACH_OMAP4_PANDA_CAM_OV5650
>> +static struct ov5650_platform_data ov_platform_data = {
>> +#elif defined(CONFIG_MACH_OMAP4_PANDA_CAM_OV5640)
>> +static struct ov5640_platform_data ov_platform_data = {
>> +#endif
>> +      .s_power = panda_ov5640_power,
>> +};
>> +
>> +static struct i2c_board_info ov_camera_i2c_device = {
>> +#ifdef CONFIG_MACH_OMAP4_PANDA_CAM_OV5650
>> +     I2C_BOARD_INFO("ov5650", OV5650_I2C_ADDRESS),
>> +#elif defined(CONFIG_MACH_OMAP4_PANDA_CAM_OV5640)
>> +     I2C_BOARD_INFO("ov5640", OV5640_I2C_ADDRESS),
>> +#endif
>> +     .platform_data = &ov_platform_data,
>> +};
>> +
> I may be wrong, but don't you think, we can clean all this #ifdefs here.
> The drivers will get connected cleanly without any issues based on probing and ID (i2c_device_id defined in driver).

Will think about it.

Thanks again!

Regards,
Sergio

>
> Thanks,
> Vaibhav
>
>> +static struct iss_subdev_i2c_board_info ov_camera_subdevs[] = {
>> +     {
>> +             .board_info = &ov_camera_i2c_device,
>> +             .i2c_adapter_id = 3,
>> +     },
>> +     { NULL, 0, },
>> +};
>> +
>> +static struct iss_v4l2_subdevs_group panda_camera_subdevs[] = {
>> +     {
>> +             .subdevs = ov_camera_subdevs,
>> +             .interface = ISS_INTERFACE_CSI2A_PHY1,
>> +     },
>> +     { },
>> +};
>> +
>> +static void panda_omap4iss_set_constraints(struct iss_device *iss, bool
>> enable)
>> +{
>> +     if (!iss)
>> +             return;
>> +
>> +     /* FIXME: Look for something more precise as a good throughtput
>> limit */
>> +     omap_pm_set_min_bus_tput(iss->dev, OCP_INITIATOR_AGENT,
>> +                              enable ? 800000 : -1);
>> +}
>> +
>> +static struct iss_platform_data panda_iss_platform_data = {
>> +     .subdevs = panda_camera_subdevs,
>> +     .set_constraints = panda_omap4iss_set_constraints,
>> +};
>> +
>> +
>> +static struct omap_device_pad omap4iss_pads[] = {
>> +     {
>> +             .name   = "csi21_dx0.csi21_dx0",
>> +             .enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
>> +     },
>> +     {
>> +             .name   = "csi21_dy0.csi21_dy0",
>> +             .enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
>> +     },
>> +     {
>> +             .name   = "csi21_dx1.csi21_dx1",
>> +             .enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
>> +     },
>> +     {
>> +             .name   = "csi21_dy1.csi21_dy1",
>> +             .enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
>> +     },
>> +     {
>> +             .name   = "csi21_dx2.csi21_dx2",
>> +             .enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
>> +     },
>> +     {
>> +             .name   = "csi21_dy2.csi21_dy2",
>> +             .enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
>> +     },
>> +};
>> +
>> +static struct omap_board_data omap4iss_data = {
>> +     .id                     = 1,
>> +     .pads                   = omap4iss_pads,
>> +     .pads_cnt               = ARRAY_SIZE(omap4iss_pads),
>> +};
>> +
>> +static int __init panda_camera_init(void)
>> +{
>> +     if (!machine_is_omap4_panda())
>> +             return 0;
>> +
>> +     panda_cam_aux_clk = clk_get(NULL, "auxclk1_ck");
>> +     if (IS_ERR(panda_cam_aux_clk)) {
>> +             printk(KERN_ERR "Unable to get auxclk1_ck\n");
>> +             return -ENODEV;
>> +     }
>> +
>> +     if (clk_set_rate(panda_cam_aux_clk,
>> +                     clk_round_rate(panda_cam_aux_clk, 24000000)))
>> +             return -EINVAL;
>> +
>> +     /*
>> +      * CSI2 1(A):
>> +      *   LANEENABLE[4:0] = 00111(0x7) - Lanes 0, 1 & 2 enabled
>> +      *   CTRLCLKEN = 1 - Active high enable for CTRLCLK
>> +      *   CAMMODE = 0 - DPHY mode
>> +      */
>> +     omap4_ctrl_pad_writel((omap4_ctrl_pad_readl(
>> +                             OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_CAMERA_RX) &
>> +                       ~(OMAP4_CAMERARX_CSI21_LANEENABLE_MASK |
>> +                         OMAP4_CAMERARX_CSI21_CAMMODE_MASK)) |
>> +                      (0x7 << OMAP4_CAMERARX_CSI21_LANEENABLE_SHIFT) |
>> +                      OMAP4_CAMERARX_CSI21_CTRLCLKEN_MASK,
>> +                      OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_CAMERA_RX);
>> +
>> +     /* Select GPIO 45 */
>> +     omap_mux_init_gpio(PANDA_GPIO_CAM_PWRDN, OMAP_PIN_OUTPUT);
>> +
>> +     /* Select GPIO 83 */
>> +     omap_mux_init_gpio(PANDA_GPIO_CAM_RESET, OMAP_PIN_OUTPUT);
>> +
>> +     /* Init FREF_CLK1_OUT */
>> +     omap_mux_init_signal("fref_clk1_out", OMAP_PIN_OUTPUT);
>> +
>> +     if (gpio_request_one(PANDA_GPIO_CAM_PWRDN, GPIOF_OUT_INIT_HIGH,
>> +                          "CAM_PWRDN"))
>> +             printk(KERN_WARNING "Cannot request GPIO %d\n",
>> +                     PANDA_GPIO_CAM_PWRDN);
>> +
>> +     if (gpio_request_one(PANDA_GPIO_CAM_RESET, GPIOF_OUT_INIT_HIGH,
>> +                          "CAM_RESET"))
>> +             printk(KERN_WARNING "Cannot request GPIO %d\n",
>> +                     PANDA_GPIO_CAM_RESET);
>> +
>> +     omap4_init_camera(&panda_iss_platform_data, &omap4iss_data);
>> +     return 0;
>> +}
>> +late_initcall(panda_camera_init);
>> --
>> 1.7.7.4
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH v2 01/11] TWL6030: Add mapping for auxiliary regs
  2011-12-01  0:14   ` Sergio Aguirre
  (?)
@ 2011-12-01 15:58   ` T Krishnamoorthy, Balaji
  2011-12-01 17:47       ` Aguirre, Sergio
  -1 siblings, 1 reply; 65+ messages in thread
From: T Krishnamoorthy, Balaji @ 2011-12-01 15:58 UTC (permalink / raw)
  To: Sergio Aguirre; +Cc: linux-media, linux-omap, laurent.pinchart, sakari.ailus

On Thu, Dec 1, 2011 at 5:44 AM, Sergio Aguirre <saaguirre@ti.com> wrote:
> Signed-off-by: Sergio Aguirre <saaguirre@ti.com>
> ---
>  drivers/mfd/twl-core.c |    2 +-
>  1 files changed, 1 insertions(+), 1 deletions(-)
>
> diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c
> index bfbd660..e26b564 100644
> --- a/drivers/mfd/twl-core.c
> +++ b/drivers/mfd/twl-core.c
> @@ -323,7 +323,7 @@ static struct twl_mapping twl6030_map[] = {
>        { SUB_CHIP_ID0, TWL6030_BASEADD_ZERO },
>        { SUB_CHIP_ID1, TWL6030_BASEADD_ZERO },
>
> -       { SUB_CHIP_ID2, TWL6030_BASEADD_ZERO },
> +       { SUB_CHIP_ID1, TWL6030_BASEADD_AUX },

Instead you can use TWL6030_MODULE_ID1, with base address as
zero for all registers in auxiliaries register map.

>        { SUB_CHIP_ID2, TWL6030_BASEADD_ZERO },
>        { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
>        { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
> --
> 1.7.7.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH v2 00/11] v4l2: OMAP4 ISS driver + Sensor + Board support
  2011-12-01  0:14 ` Sergio Aguirre
                   ` (12 preceding siblings ...)
  (?)
@ 2011-12-01 16:14 ` Sakari Ailus
  2011-12-01 17:41   ` Aguirre, Sergio
  -1 siblings, 1 reply; 65+ messages in thread
From: Sakari Ailus @ 2011-12-01 16:14 UTC (permalink / raw)
  To: Sergio Aguirre; +Cc: linux-media, linux-omap, laurent.pinchart

Hi Sergio,

Thanks for the patchset!!

On Wed, Nov 30, 2011 at 06:14:49PM -0600, Sergio Aguirre wrote:
> Hi everyone,
> 
> This is the second version of the OMAP4 ISS driver,
> now ported to the Media Controller framework AND supporting
> videobuf2 framework.
> 
> This patchset should apply cleanly on top of v3.2-rc3 kernel tag.
> 
> This driver attempts to provide an fully open source solution to
> control the OMAP4 Imaging SubSystem (a.k.a. ISS).
> 
> Starts with just CSI2-A interface support, and pretends to be
> ready for expansion to add support to the many ISS block modules
> as possible.
> 
> Please see newly added documentation for more details:
> 
> Documentation/video4linux/omap4_camera.txt

I propose s/omap4_camera/omap4iss/, according to the path name in the
drivers/media/video directory.

> Any comments/complaints are welcome. :)
> 
> Changes since v1:
> - Simplification of auxclk handling in board files. (Pointed out by: Roger Quadros)
> - Cleanup of Camera support enablement for 4430sdp & panda. (Pointed out by: Roger Quadros)
> - Use of HWMOD declaration for assisted platform_device creation. (Pointed out by: Felipe Balbi)
> - Videobuf2 migration (Removal of custom iss_queue buffer handling driver)

I'm happy to see it's using videobuf2!

I have no other comments quite yet. :-)

Cheers,

-- 
Sakari Ailus
e-mail: sakari.ailus@iki.fi	jabber/XMPP/Gmail: sailus@retiisi.org.uk

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

* Re: [PATCH v2 05/11] OMAP4: Add base addresses for ISS
  2011-12-01  0:14   ` Sergio Aguirre
  (?)
@ 2011-12-01 17:24   ` Laurent Pinchart
  2011-12-01 17:30     ` Aguirre, Sergio
  -1 siblings, 1 reply; 65+ messages in thread
From: Laurent Pinchart @ 2011-12-01 17:24 UTC (permalink / raw)
  To: Sergio Aguirre; +Cc: linux-media, linux-omap, sakari.ailus

Hi Sergio,

On Thursday 01 December 2011 01:14:54 Sergio Aguirre wrote:
> NOTE: This isn't the whole list of features that the
> ISS supports, but the only ones supported at the moment.
> 
> Signed-off-by: Sergio Aguirre <saaguirre@ti.com>
> ---
>  arch/arm/mach-omap2/devices.c              |   32
> ++++++++++++++++++++++++++++ arch/arm/plat-omap/include/plat/omap44xx.h | 
>   9 +++++++
>  2 files changed, 41 insertions(+), 0 deletions(-)
> 
> diff --git a/arch/arm/mach-omap2/devices.c b/arch/arm/mach-omap2/devices.c
> index c15cfad..b48aeea 100644
> --- a/arch/arm/mach-omap2/devices.c
> +++ b/arch/arm/mach-omap2/devices.c
> @@ -32,6 +32,7 @@
>  #include <plat/omap_hwmod.h>
>  #include <plat/omap_device.h>
>  #include <plat/omap4-keypad.h>
> +#include <plat/omap4-iss.h>

I try to keep headers sorted alphabetically when possible, but that might just 
be me.

> 
>  #include "mux.h"
>  #include "control.h"
> @@ -217,6 +218,37 @@ int omap3_init_camera(struct isp_platform_data *pdata)
>  	return platform_device_register(&omap3isp_device);
>  }
> 
> +int omap4_init_camera(struct iss_platform_data *pdata, struct
> omap_board_data *bdata)
> +{
> +	struct platform_device *pdev;
> +	struct omap_hwmod *oh;
> +	struct iss_platform_data *omap4iss_pdata;
> +	char *oh_name = "iss";
> +	char *name = "omap4iss";

Would const char or static const char help the compiler putting the strings to 
a read-only section ?

> +	unsigned int id = -1;
> +
> +	oh = omap_hwmod_lookup(oh_name);
> +	if (!oh) {
> +		pr_err("Could not look up %s\n", oh_name);
> +		return -ENODEV;
> +	}
> +
> +	omap4iss_pdata = pdata;
> +
> +	pdev = omap_device_build(name, id, oh, omap4iss_pdata,
> +			sizeof(struct iss_platform_data), NULL, 0, 0);

This is the only location where id is used, maybe you could pass -1 directly 
to the function ?

> +
> +	if (IS_ERR(pdev)) {
> +		WARN(1, "Can't build omap_device for %s:%s.\n",
> +						name, oh->name);
> +		return PTR_ERR(pdev);
> +	}
> +
> +	oh->mux = omap_hwmod_mux_init(bdata->pads, bdata->pads_cnt);
> +
> +	return 0;
> +}
> +
>  static inline void omap_init_camera(void)
>  {
>  #if defined(CONFIG_VIDEO_OMAP2) || defined(CONFIG_VIDEO_OMAP2_MODULE)
> diff --git a/arch/arm/plat-omap/include/plat/omap44xx.h
> b/arch/arm/plat-omap/include/plat/omap44xx.h index ea2b8a6..31432aa 100644
> --- a/arch/arm/plat-omap/include/plat/omap44xx.h
> +++ b/arch/arm/plat-omap/include/plat/omap44xx.h
> @@ -49,6 +49,15 @@
>  #define OMAP44XX_MAILBOX_BASE		(L4_44XX_BASE + 0xF4000)
>  #define OMAP44XX_HSUSB_OTG_BASE		(L4_44XX_BASE + 0xAB000)
> 
> +#define OMAP44XX_ISS_BASE			0x52000000
> +#define OMAP44XX_ISS_TOP_BASE			(OMAP44XX_ISS_BASE + 0x0)
> +#define OMAP44XX_ISS_CSI2_A_REGS1_BASE		(OMAP44XX_ISS_BASE + 0x1000)
> +#define OMAP44XX_ISS_CAMERARX_CORE1_BASE	(OMAP44XX_ISS_BASE + 0x1170)
> +
> +#define OMAP44XX_ISS_TOP_END			(OMAP44XX_ISS_TOP_BASE + 256 - 1)
> +#define OMAP44XX_ISS_CSI2_A_REGS1_END		(OMAP44XX_ISS_CSI2_A_REGS1_BASE +
> 368 - 1) +#define
> OMAP44XX_ISS_CAMERARX_CORE1_END		(OMAP44XX_ISS_CAMERARX_CORE1_BASE + 32 -
> 1) +
>  #define OMAP4_MMU1_BASE			0x55082000
>  #define OMAP4_MMU2_BASE			0x4A066000

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v2 02/11] mfd: twl6040: Fix wrong TWL6040_GPO3 bitfield value
  2011-12-01  0:14   ` Sergio Aguirre
  (?)
@ 2011-12-01 17:24   ` Laurent Pinchart
  2011-12-01 17:35     ` Aguirre, Sergio
  -1 siblings, 1 reply; 65+ messages in thread
From: Laurent Pinchart @ 2011-12-01 17:24 UTC (permalink / raw)
  To: Sergio Aguirre; +Cc: linux-media, linux-omap, sakari.ailus

Hi Sergio,

On Thursday 01 December 2011 01:14:51 Sergio Aguirre wrote:
> The define should be the result of 1 << Bit number.
> 
> Bit number for GPOCTL.GPO3 field is 2, which results
> in 0x4 value.
> 
> Signed-off-by: Sergio Aguirre <saaguirre@ti.com>
> ---
>  include/linux/mfd/twl6040.h |    2 +-
>  1 files changed, 1 insertions(+), 1 deletions(-)
> 
> diff --git a/include/linux/mfd/twl6040.h b/include/linux/mfd/twl6040.h
> index 2463c261..2a7ff16 100644
> --- a/include/linux/mfd/twl6040.h
> +++ b/include/linux/mfd/twl6040.h
> @@ -142,7 +142,7 @@
> 
>  #define TWL6040_GPO1			0x01
>  #define TWL6040_GPO2			0x02
> -#define TWL6040_GPO3			0x03
> +#define TWL6040_GPO3			0x04

What about defining the fields as (1 << x) instead then ?

> 
>  /* ACCCTL (0x2D) fields */

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v2 00/11] v4l2: OMAP4 ISS driver + Sensor + Board support
  2011-12-01  0:14 ` Sergio Aguirre
                   ` (13 preceding siblings ...)
  (?)
@ 2011-12-01 17:26 ` Laurent Pinchart
  2011-12-01 17:43     ` Aguirre, Sergio
  -1 siblings, 1 reply; 65+ messages in thread
From: Laurent Pinchart @ 2011-12-01 17:26 UTC (permalink / raw)
  To: Sergio Aguirre; +Cc: linux-media, linux-omap, sakari.ailus

Hi Sergio,

On Thursday 01 December 2011 01:14:49 Sergio Aguirre wrote:
> Hi everyone,
> 
> This is the second version of the OMAP4 ISS driver,
> now ported to the Media Controller framework AND supporting
> videobuf2 framework.

Thanks a lot for working on this.

> This patchset should apply cleanly on top of v3.2-rc3 kernel tag.
> 
> This driver attempts to provide an fully open source solution to
> control the OMAP4 Imaging SubSystem (a.k.a. ISS).
> 
> Starts with just CSI2-A interface support, and pretends to be
> ready for expansion to add support to the many ISS block modules
> as possible.
> 
> Please see newly added documentation for more details:
> 
> Documentation/video4linux/omap4_camera.txt
> 
> Any comments/complaints are welcome. :)

I've started reviewing the patches, but it might take some time as I got lots 
on my plate at the moment. I will concentrate on the ISS patch (06/11) first. 
The sensor drivers are needed now for testing purpose, but can get their share 
of love later.

> Changes since v1:
> - Simplification of auxclk handling in board files. (Pointed out by: Roger
> Quadros) - Cleanup of Camera support enablement for 4430sdp & panda.
> (Pointed out by: Roger Quadros) - Use of HWMOD declaration for assisted
> platform_device creation. (Pointed out by: Felipe Balbi) - Videobuf2
> migration (Removal of custom iss_queue buffer handling driver) - Proper
> GPO3 handling for CAM_SEL in 4430sdp.
> 
> Sergio Aguirre (10):
>   TWL6030: Add mapping for auxiliary regs
>   mfd: twl6040: Fix wrong TWL6040_GPO3 bitfield value
>   OMAP4: hwmod: Include CSI2A and CSIPHY1 memory sections
>   OMAP4: Add base addresses for ISS
>   v4l: Add support for omap4iss driver
>   v4l: Add support for ov5640 sensor
>   v4l: Add support for ov5650 sensor
>   arm: omap4430sdp: Add support for omap4iss camera
>   arm: omap4panda: Add support for omap4iss camera
>   arm: Add support for CMA for omap4iss driver
> 
> Stanimir Varbanov (1):
>   v4l: Introduce sensor operation for getting interface configuration
> 
>  Documentation/video4linux/omap4_camera.txt    |   60 ++
>  arch/arm/mach-omap2/Kconfig                   |   54 +
>  arch/arm/mach-omap2/Makefile                  |    3 +
>  arch/arm/mach-omap2/board-4430sdp-camera.c    |  221 ++++
>  arch/arm/mach-omap2/board-omap4panda-camera.c |  198 ++++
>  arch/arm/mach-omap2/devices.c                 |   40 +
>  arch/arm/mach-omap2/omap_hwmod_44xx_data.c    |   16 +-
>  arch/arm/plat-omap/include/plat/omap4-iss.h   |   42 +
>  arch/arm/plat-omap/include/plat/omap44xx.h    |    9 +
>  drivers/media/video/Kconfig                   |   25 +
>  drivers/media/video/Makefile                  |    3 +
>  drivers/media/video/omap4iss/Makefile         |    6 +
>  drivers/media/video/omap4iss/iss.c            | 1179
> ++++++++++++++++++++++ drivers/media/video/omap4iss/iss.h            | 
> 133 +++
>  drivers/media/video/omap4iss/iss_csi2.c       | 1324
> +++++++++++++++++++++++++ drivers/media/video/omap4iss/iss_csi2.h       | 
> 166 +++
>  drivers/media/video/omap4iss/iss_csiphy.c     |  215 ++++
>  drivers/media/video/omap4iss/iss_csiphy.h     |   69 ++
>  drivers/media/video/omap4iss/iss_regs.h       |  238 +++++
>  drivers/media/video/omap4iss/iss_video.c      | 1192
> ++++++++++++++++++++++ drivers/media/video/omap4iss/iss_video.h      | 
> 205 ++++
>  drivers/media/video/ov5640.c                  |  972 ++++++++++++++++++
>  drivers/media/video/ov5650.c                  |  524 ++++++++++
>  drivers/mfd/twl-core.c                        |    2 +-
>  include/linux/mfd/twl6040.h                   |    2 +-
>  include/media/ov5640.h                        |   10 +
>  include/media/ov5650.h                        |   10 +
>  include/media/v4l2-chip-ident.h               |    2 +
>  include/media/v4l2-subdev.h                   |   42 +
>  29 files changed, 6957 insertions(+), 5 deletions(-)
>  create mode 100644 Documentation/video4linux/omap4_camera.txt
>  create mode 100644 arch/arm/mach-omap2/board-4430sdp-camera.c
>  create mode 100644 arch/arm/mach-omap2/board-omap4panda-camera.c
>  create mode 100644 arch/arm/plat-omap/include/plat/omap4-iss.h
>  create mode 100644 drivers/media/video/omap4iss/Makefile
>  create mode 100644 drivers/media/video/omap4iss/iss.c
>  create mode 100644 drivers/media/video/omap4iss/iss.h
>  create mode 100644 drivers/media/video/omap4iss/iss_csi2.c
>  create mode 100644 drivers/media/video/omap4iss/iss_csi2.h
>  create mode 100644 drivers/media/video/omap4iss/iss_csiphy.c
>  create mode 100644 drivers/media/video/omap4iss/iss_csiphy.h
>  create mode 100644 drivers/media/video/omap4iss/iss_regs.h
>  create mode 100644 drivers/media/video/omap4iss/iss_video.c
>  create mode 100644 drivers/media/video/omap4iss/iss_video.h
>  create mode 100644 drivers/media/video/ov5640.c
>  create mode 100644 drivers/media/video/ov5650.c
>  create mode 100644 include/media/ov5640.h
>  create mode 100644 include/media/ov5650.h

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v2 05/11] OMAP4: Add base addresses for ISS
  2011-12-01 17:24   ` Laurent Pinchart
@ 2011-12-01 17:30     ` Aguirre, Sergio
  0 siblings, 0 replies; 65+ messages in thread
From: Aguirre, Sergio @ 2011-12-01 17:30 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: linux-media, linux-omap, sakari.ailus

Hi Laurent,

Thanks for the review.

On Thu, Dec 1, 2011 at 11:24 AM, Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
> Hi Sergio,
>
> On Thursday 01 December 2011 01:14:54 Sergio Aguirre wrote:
>> NOTE: This isn't the whole list of features that the
>> ISS supports, but the only ones supported at the moment.
>>
>> Signed-off-by: Sergio Aguirre <saaguirre@ti.com>
>> ---
>>  arch/arm/mach-omap2/devices.c              |   32
>> ++++++++++++++++++++++++++++ arch/arm/plat-omap/include/plat/omap44xx.h |
>>   9 +++++++
>>  2 files changed, 41 insertions(+), 0 deletions(-)
>>
>> diff --git a/arch/arm/mach-omap2/devices.c b/arch/arm/mach-omap2/devices.c
>> index c15cfad..b48aeea 100644
>> --- a/arch/arm/mach-omap2/devices.c
>> +++ b/arch/arm/mach-omap2/devices.c
>> @@ -32,6 +32,7 @@
>>  #include <plat/omap_hwmod.h>
>>  #include <plat/omap_device.h>
>>  #include <plat/omap4-keypad.h>
>> +#include <plat/omap4-iss.h>
>
> I try to keep headers sorted alphabetically when possible, but that might just
> be me.

No problem. I can change it.

>
>>
>>  #include "mux.h"
>>  #include "control.h"
>> @@ -217,6 +218,37 @@ int omap3_init_camera(struct isp_platform_data *pdata)
>>       return platform_device_register(&omap3isp_device);
>>  }
>>
>> +int omap4_init_camera(struct iss_platform_data *pdata, struct
>> omap_board_data *bdata)
>> +{
>> +     struct platform_device *pdev;
>> +     struct omap_hwmod *oh;
>> +     struct iss_platform_data *omap4iss_pdata;
>> +     char *oh_name = "iss";
>> +     char *name = "omap4iss";
>
> Would const char or static const char help the compiler putting the strings to
> a read-only section ?

Right. Will fix.

>
>> +     unsigned int id = -1;
>> +
>> +     oh = omap_hwmod_lookup(oh_name);
>> +     if (!oh) {
>> +             pr_err("Could not look up %s\n", oh_name);
>> +             return -ENODEV;
>> +     }
>> +
>> +     omap4iss_pdata = pdata;
>> +
>> +     pdev = omap_device_build(name, id, oh, omap4iss_pdata,
>> +                     sizeof(struct iss_platform_data), NULL, 0, 0);
>
> This is the only location where id is used, maybe you could pass -1 directly
> to the function ?

Ditto.

Thanks and regards,
Sergio

>
>> +
>> +     if (IS_ERR(pdev)) {
>> +             WARN(1, "Can't build omap_device for %s:%s.\n",
>> +                                             name, oh->name);
>> +             return PTR_ERR(pdev);
>> +     }
>> +
>> +     oh->mux = omap_hwmod_mux_init(bdata->pads, bdata->pads_cnt);
>> +
>> +     return 0;
>> +}
>> +
>>  static inline void omap_init_camera(void)
>>  {
>>  #if defined(CONFIG_VIDEO_OMAP2) || defined(CONFIG_VIDEO_OMAP2_MODULE)
>> diff --git a/arch/arm/plat-omap/include/plat/omap44xx.h
>> b/arch/arm/plat-omap/include/plat/omap44xx.h index ea2b8a6..31432aa 100644
>> --- a/arch/arm/plat-omap/include/plat/omap44xx.h
>> +++ b/arch/arm/plat-omap/include/plat/omap44xx.h
>> @@ -49,6 +49,15 @@
>>  #define OMAP44XX_MAILBOX_BASE                (L4_44XX_BASE + 0xF4000)
>>  #define OMAP44XX_HSUSB_OTG_BASE              (L4_44XX_BASE + 0xAB000)
>>
>> +#define OMAP44XX_ISS_BASE                    0x52000000
>> +#define OMAP44XX_ISS_TOP_BASE                        (OMAP44XX_ISS_BASE + 0x0)
>> +#define OMAP44XX_ISS_CSI2_A_REGS1_BASE               (OMAP44XX_ISS_BASE + 0x1000)
>> +#define OMAP44XX_ISS_CAMERARX_CORE1_BASE     (OMAP44XX_ISS_BASE + 0x1170)
>> +
>> +#define OMAP44XX_ISS_TOP_END                 (OMAP44XX_ISS_TOP_BASE + 256 - 1)
>> +#define OMAP44XX_ISS_CSI2_A_REGS1_END                (OMAP44XX_ISS_CSI2_A_REGS1_BASE +
>> 368 - 1) +#define
>> OMAP44XX_ISS_CAMERARX_CORE1_END               (OMAP44XX_ISS_CAMERARX_CORE1_BASE + 32 -
>> 1) +
>>  #define OMAP4_MMU1_BASE                      0x55082000
>>  #define OMAP4_MMU2_BASE                      0x4A066000
>
> --
> Regards,
>
> Laurent Pinchart

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

* Re: [PATCH v2 02/11] mfd: twl6040: Fix wrong TWL6040_GPO3 bitfield value
  2011-12-01 17:24   ` Laurent Pinchart
@ 2011-12-01 17:35     ` Aguirre, Sergio
  2011-12-02  1:08       ` Laurent Pinchart
  0 siblings, 1 reply; 65+ messages in thread
From: Aguirre, Sergio @ 2011-12-01 17:35 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: linux-media, linux-omap, sakari.ailus

Hi Laurent,

Thanks for the review.

On Thu, Dec 1, 2011 at 11:24 AM, Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
> Hi Sergio,
>
> On Thursday 01 December 2011 01:14:51 Sergio Aguirre wrote:
>> The define should be the result of 1 << Bit number.
>>
>> Bit number for GPOCTL.GPO3 field is 2, which results
>> in 0x4 value.
>>
>> Signed-off-by: Sergio Aguirre <saaguirre@ti.com>
>> ---
>>  include/linux/mfd/twl6040.h |    2 +-
>>  1 files changed, 1 insertions(+), 1 deletions(-)
>>
>> diff --git a/include/linux/mfd/twl6040.h b/include/linux/mfd/twl6040.h
>> index 2463c261..2a7ff16 100644
>> --- a/include/linux/mfd/twl6040.h
>> +++ b/include/linux/mfd/twl6040.h
>> @@ -142,7 +142,7 @@
>>
>>  #define TWL6040_GPO1                 0x01
>>  #define TWL6040_GPO2                 0x02
>> -#define TWL6040_GPO3                 0x03
>> +#define TWL6040_GPO3                 0x04
>
> What about defining the fields as (1 << x) instead then ?

I thought about that, but I guess I just wanted to keep it
consistent with the rest of the file.

Maybe I can create a separate patch for changing all these bitwise
flags to use BIT() macros instead.

Thanks and Regards,
Sergio

>
>>
>>  /* ACCCTL (0x2D) fields */
>
> --
> Regards,
>
> Laurent Pinchart

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

* Re: [PATCH v2 00/11] v4l2: OMAP4 ISS driver + Sensor + Board support
  2011-12-01 16:14 ` Sakari Ailus
@ 2011-12-01 17:41   ` Aguirre, Sergio
  0 siblings, 0 replies; 65+ messages in thread
From: Aguirre, Sergio @ 2011-12-01 17:41 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: linux-media, linux-omap, laurent.pinchart

Hi Sakari,

On Thu, Dec 1, 2011 at 10:14 AM, Sakari Ailus <sakari.ailus@iki.fi> wrote:
> Hi Sergio,
>
> Thanks for the patchset!!

And thanks for your attention :)

>
> On Wed, Nov 30, 2011 at 06:14:49PM -0600, Sergio Aguirre wrote:
>> Hi everyone,
>>
>> This is the second version of the OMAP4 ISS driver,
>> now ported to the Media Controller framework AND supporting
>> videobuf2 framework.
>>
>> This patchset should apply cleanly on top of v3.2-rc3 kernel tag.
>>
>> This driver attempts to provide an fully open source solution to
>> control the OMAP4 Imaging SubSystem (a.k.a. ISS).
>>
>> Starts with just CSI2-A interface support, and pretends to be
>> ready for expansion to add support to the many ISS block modules
>> as possible.
>>
>> Please see newly added documentation for more details:
>>
>> Documentation/video4linux/omap4_camera.txt
>
> I propose s/omap4_camera/omap4iss/, according to the path name in the
> drivers/media/video directory.

Makes sense. Will fix.

>
>> Any comments/complaints are welcome. :)
>>
>> Changes since v1:
>> - Simplification of auxclk handling in board files. (Pointed out by: Roger Quadros)
>> - Cleanup of Camera support enablement for 4430sdp & panda. (Pointed out by: Roger Quadros)
>> - Use of HWMOD declaration for assisted platform_device creation. (Pointed out by: Felipe Balbi)
>> - Videobuf2 migration (Removal of custom iss_queue buffer handling driver)
>
> I'm happy to see it's using videobuf2!

Yeah, I'll definitely need it for multi-planar buffer handling for
NV12 buffer capturing.

Resizer can color convert from YUV422->YUV420 NV12 now, and expects 2 pointers
(1 for Y, and 1 for UV 2x2 sampled) to be programmed in HW.

>
> I have no other comments quite yet. :-)

Ok, let me know if you find something eye-popping ugly in there. I'll
be happy to fix it. :)

Thanks and Regards,
Sergio

>
> Cheers,
>
> --
> Sakari Ailus
> e-mail: sakari.ailus@iki.fi     jabber/XMPP/Gmail: sailus@retiisi.org.uk

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

* Re: [PATCH v2 00/11] v4l2: OMAP4 ISS driver + Sensor + Board support
  2011-12-01 17:26 ` Laurent Pinchart
@ 2011-12-01 17:43     ` Aguirre, Sergio
  0 siblings, 0 replies; 65+ messages in thread
From: Aguirre, Sergio @ 2011-12-01 17:43 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: linux-media, linux-omap, sakari.ailus

Hi Laurent,

On Thu, Dec 1, 2011 at 11:26 AM, Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
> Hi Sergio,
>
> On Thursday 01 December 2011 01:14:49 Sergio Aguirre wrote:
>> Hi everyone,
>>
>> This is the second version of the OMAP4 ISS driver,
>> now ported to the Media Controller framework AND supporting
>> videobuf2 framework.
>
> Thanks a lot for working on this.

And thanks to you for the patience and interest. I hope to make faster
updates from now on :)

>
>> This patchset should apply cleanly on top of v3.2-rc3 kernel tag.
>>
>> This driver attempts to provide an fully open source solution to
>> control the OMAP4 Imaging SubSystem (a.k.a. ISS).
>>
>> Starts with just CSI2-A interface support, and pretends to be
>> ready for expansion to add support to the many ISS block modules
>> as possible.
>>
>> Please see newly added documentation for more details:
>>
>> Documentation/video4linux/omap4_camera.txt
>>
>> Any comments/complaints are welcome. :)
>
> I've started reviewing the patches, but it might take some time as I got lots
> on my plate at the moment. I will concentrate on the ISS patch (06/11) first.
> The sensor drivers are needed now for testing purpose, but can get their share
> of love later.

Brilliant! That's fine with me.

Thanks for your time. I really appreciate it.

Regards,
Sergio

>
>> Changes since v1:
>> - Simplification of auxclk handling in board files. (Pointed out by: Roger
>> Quadros) - Cleanup of Camera support enablement for 4430sdp & panda.
>> (Pointed out by: Roger Quadros) - Use of HWMOD declaration for assisted
>> platform_device creation. (Pointed out by: Felipe Balbi) - Videobuf2
>> migration (Removal of custom iss_queue buffer handling driver) - Proper
>> GPO3 handling for CAM_SEL in 4430sdp.
>>
>> Sergio Aguirre (10):
>>   TWL6030: Add mapping for auxiliary regs
>>   mfd: twl6040: Fix wrong TWL6040_GPO3 bitfield value
>>   OMAP4: hwmod: Include CSI2A and CSIPHY1 memory sections
>>   OMAP4: Add base addresses for ISS
>>   v4l: Add support for omap4iss driver
>>   v4l: Add support for ov5640 sensor
>>   v4l: Add support for ov5650 sensor
>>   arm: omap4430sdp: Add support for omap4iss camera
>>   arm: omap4panda: Add support for omap4iss camera
>>   arm: Add support for CMA for omap4iss driver
>>
>> Stanimir Varbanov (1):
>>   v4l: Introduce sensor operation for getting interface configuration
>>
>>  Documentation/video4linux/omap4_camera.txt    |   60 ++
>>  arch/arm/mach-omap2/Kconfig                   |   54 +
>>  arch/arm/mach-omap2/Makefile                  |    3 +
>>  arch/arm/mach-omap2/board-4430sdp-camera.c    |  221 ++++
>>  arch/arm/mach-omap2/board-omap4panda-camera.c |  198 ++++
>>  arch/arm/mach-omap2/devices.c                 |   40 +
>>  arch/arm/mach-omap2/omap_hwmod_44xx_data.c    |   16 +-
>>  arch/arm/plat-omap/include/plat/omap4-iss.h   |   42 +
>>  arch/arm/plat-omap/include/plat/omap44xx.h    |    9 +
>>  drivers/media/video/Kconfig                   |   25 +
>>  drivers/media/video/Makefile                  |    3 +
>>  drivers/media/video/omap4iss/Makefile         |    6 +
>>  drivers/media/video/omap4iss/iss.c            | 1179
>> ++++++++++++++++++++++ drivers/media/video/omap4iss/iss.h            |
>> 133 +++
>>  drivers/media/video/omap4iss/iss_csi2.c       | 1324
>> +++++++++++++++++++++++++ drivers/media/video/omap4iss/iss_csi2.h       |
>> 166 +++
>>  drivers/media/video/omap4iss/iss_csiphy.c     |  215 ++++
>>  drivers/media/video/omap4iss/iss_csiphy.h     |   69 ++
>>  drivers/media/video/omap4iss/iss_regs.h       |  238 +++++
>>  drivers/media/video/omap4iss/iss_video.c      | 1192
>> ++++++++++++++++++++++ drivers/media/video/omap4iss/iss_video.h      |
>> 205 ++++
>>  drivers/media/video/ov5640.c                  |  972 ++++++++++++++++++
>>  drivers/media/video/ov5650.c                  |  524 ++++++++++
>>  drivers/mfd/twl-core.c                        |    2 +-
>>  include/linux/mfd/twl6040.h                   |    2 +-
>>  include/media/ov5640.h                        |   10 +
>>  include/media/ov5650.h                        |   10 +
>>  include/media/v4l2-chip-ident.h               |    2 +
>>  include/media/v4l2-subdev.h                   |   42 +
>>  29 files changed, 6957 insertions(+), 5 deletions(-)
>>  create mode 100644 Documentation/video4linux/omap4_camera.txt
>>  create mode 100644 arch/arm/mach-omap2/board-4430sdp-camera.c
>>  create mode 100644 arch/arm/mach-omap2/board-omap4panda-camera.c
>>  create mode 100644 arch/arm/plat-omap/include/plat/omap4-iss.h
>>  create mode 100644 drivers/media/video/omap4iss/Makefile
>>  create mode 100644 drivers/media/video/omap4iss/iss.c
>>  create mode 100644 drivers/media/video/omap4iss/iss.h
>>  create mode 100644 drivers/media/video/omap4iss/iss_csi2.c
>>  create mode 100644 drivers/media/video/omap4iss/iss_csi2.h
>>  create mode 100644 drivers/media/video/omap4iss/iss_csiphy.c
>>  create mode 100644 drivers/media/video/omap4iss/iss_csiphy.h
>>  create mode 100644 drivers/media/video/omap4iss/iss_regs.h
>>  create mode 100644 drivers/media/video/omap4iss/iss_video.c
>>  create mode 100644 drivers/media/video/omap4iss/iss_video.h
>>  create mode 100644 drivers/media/video/ov5640.c
>>  create mode 100644 drivers/media/video/ov5650.c
>>  create mode 100644 include/media/ov5640.h
>>  create mode 100644 include/media/ov5650.h
>
> --
> Regards,
>
> Laurent Pinchart

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

* Re: [PATCH v2 00/11] v4l2: OMAP4 ISS driver + Sensor + Board support
@ 2011-12-01 17:43     ` Aguirre, Sergio
  0 siblings, 0 replies; 65+ messages in thread
From: Aguirre, Sergio @ 2011-12-01 17:43 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: linux-media, linux-omap, sakari.ailus

Hi Laurent,

On Thu, Dec 1, 2011 at 11:26 AM, Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
> Hi Sergio,
>
> On Thursday 01 December 2011 01:14:49 Sergio Aguirre wrote:
>> Hi everyone,
>>
>> This is the second version of the OMAP4 ISS driver,
>> now ported to the Media Controller framework AND supporting
>> videobuf2 framework.
>
> Thanks a lot for working on this.

And thanks to you for the patience and interest. I hope to make faster
updates from now on :)

>
>> This patchset should apply cleanly on top of v3.2-rc3 kernel tag.
>>
>> This driver attempts to provide an fully open source solution to
>> control the OMAP4 Imaging SubSystem (a.k.a. ISS).
>>
>> Starts with just CSI2-A interface support, and pretends to be
>> ready for expansion to add support to the many ISS block modules
>> as possible.
>>
>> Please see newly added documentation for more details:
>>
>> Documentation/video4linux/omap4_camera.txt
>>
>> Any comments/complaints are welcome. :)
>
> I've started reviewing the patches, but it might take some time as I got lots
> on my plate at the moment. I will concentrate on the ISS patch (06/11) first.
> The sensor drivers are needed now for testing purpose, but can get their share
> of love later.

Brilliant! That's fine with me.

Thanks for your time. I really appreciate it.

Regards,
Sergio

>
>> Changes since v1:
>> - Simplification of auxclk handling in board files. (Pointed out by: Roger
>> Quadros) - Cleanup of Camera support enablement for 4430sdp & panda.
>> (Pointed out by: Roger Quadros) - Use of HWMOD declaration for assisted
>> platform_device creation. (Pointed out by: Felipe Balbi) - Videobuf2
>> migration (Removal of custom iss_queue buffer handling driver) - Proper
>> GPO3 handling for CAM_SEL in 4430sdp.
>>
>> Sergio Aguirre (10):
>>   TWL6030: Add mapping for auxiliary regs
>>   mfd: twl6040: Fix wrong TWL6040_GPO3 bitfield value
>>   OMAP4: hwmod: Include CSI2A and CSIPHY1 memory sections
>>   OMAP4: Add base addresses for ISS
>>   v4l: Add support for omap4iss driver
>>   v4l: Add support for ov5640 sensor
>>   v4l: Add support for ov5650 sensor
>>   arm: omap4430sdp: Add support for omap4iss camera
>>   arm: omap4panda: Add support for omap4iss camera
>>   arm: Add support for CMA for omap4iss driver
>>
>> Stanimir Varbanov (1):
>>   v4l: Introduce sensor operation for getting interface configuration
>>
>>  Documentation/video4linux/omap4_camera.txt    |   60 ++
>>  arch/arm/mach-omap2/Kconfig                   |   54 +
>>  arch/arm/mach-omap2/Makefile                  |    3 +
>>  arch/arm/mach-omap2/board-4430sdp-camera.c    |  221 ++++
>>  arch/arm/mach-omap2/board-omap4panda-camera.c |  198 ++++
>>  arch/arm/mach-omap2/devices.c                 |   40 +
>>  arch/arm/mach-omap2/omap_hwmod_44xx_data.c    |   16 +-
>>  arch/arm/plat-omap/include/plat/omap4-iss.h   |   42 +
>>  arch/arm/plat-omap/include/plat/omap44xx.h    |    9 +
>>  drivers/media/video/Kconfig                   |   25 +
>>  drivers/media/video/Makefile                  |    3 +
>>  drivers/media/video/omap4iss/Makefile         |    6 +
>>  drivers/media/video/omap4iss/iss.c            | 1179
>> ++++++++++++++++++++++ drivers/media/video/omap4iss/iss.h            |
>> 133 +++
>>  drivers/media/video/omap4iss/iss_csi2.c       | 1324
>> +++++++++++++++++++++++++ drivers/media/video/omap4iss/iss_csi2.h       |
>> 166 +++
>>  drivers/media/video/omap4iss/iss_csiphy.c     |  215 ++++
>>  drivers/media/video/omap4iss/iss_csiphy.h     |   69 ++
>>  drivers/media/video/omap4iss/iss_regs.h       |  238 +++++
>>  drivers/media/video/omap4iss/iss_video.c      | 1192
>> ++++++++++++++++++++++ drivers/media/video/omap4iss/iss_video.h      |
>> 205 ++++
>>  drivers/media/video/ov5640.c                  |  972 ++++++++++++++++++
>>  drivers/media/video/ov5650.c                  |  524 ++++++++++
>>  drivers/mfd/twl-core.c                        |    2 +-
>>  include/linux/mfd/twl6040.h                   |    2 +-
>>  include/media/ov5640.h                        |   10 +
>>  include/media/ov5650.h                        |   10 +
>>  include/media/v4l2-chip-ident.h               |    2 +
>>  include/media/v4l2-subdev.h                   |   42 +
>>  29 files changed, 6957 insertions(+), 5 deletions(-)
>>  create mode 100644 Documentation/video4linux/omap4_camera.txt
>>  create mode 100644 arch/arm/mach-omap2/board-4430sdp-camera.c
>>  create mode 100644 arch/arm/mach-omap2/board-omap4panda-camera.c
>>  create mode 100644 arch/arm/plat-omap/include/plat/omap4-iss.h
>>  create mode 100644 drivers/media/video/omap4iss/Makefile
>>  create mode 100644 drivers/media/video/omap4iss/iss.c
>>  create mode 100644 drivers/media/video/omap4iss/iss.h
>>  create mode 100644 drivers/media/video/omap4iss/iss_csi2.c
>>  create mode 100644 drivers/media/video/omap4iss/iss_csi2.h
>>  create mode 100644 drivers/media/video/omap4iss/iss_csiphy.c
>>  create mode 100644 drivers/media/video/omap4iss/iss_csiphy.h
>>  create mode 100644 drivers/media/video/omap4iss/iss_regs.h
>>  create mode 100644 drivers/media/video/omap4iss/iss_video.c
>>  create mode 100644 drivers/media/video/omap4iss/iss_video.h
>>  create mode 100644 drivers/media/video/ov5640.c
>>  create mode 100644 drivers/media/video/ov5650.c
>>  create mode 100644 include/media/ov5640.h
>>  create mode 100644 include/media/ov5650.h
>
> --
> Regards,
>
> Laurent Pinchart
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH v2 01/11] TWL6030: Add mapping for auxiliary regs
  2011-12-01 15:58   ` T Krishnamoorthy, Balaji
@ 2011-12-01 17:47       ` Aguirre, Sergio
  0 siblings, 0 replies; 65+ messages in thread
From: Aguirre, Sergio @ 2011-12-01 17:47 UTC (permalink / raw)
  To: T Krishnamoorthy, Balaji
  Cc: linux-media, linux-omap, laurent.pinchart, sakari.ailus

Hi Balaji,

Thanks for the review.

On Thu, Dec 1, 2011 at 9:58 AM, T Krishnamoorthy, Balaji
<balajitk@ti.com> wrote:
> On Thu, Dec 1, 2011 at 5:44 AM, Sergio Aguirre <saaguirre@ti.com> wrote:
>> Signed-off-by: Sergio Aguirre <saaguirre@ti.com>
>> ---
>>  drivers/mfd/twl-core.c |    2 +-
>>  1 files changed, 1 insertions(+), 1 deletions(-)
>>
>> diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c
>> index bfbd660..e26b564 100644
>> --- a/drivers/mfd/twl-core.c
>> +++ b/drivers/mfd/twl-core.c
>> @@ -323,7 +323,7 @@ static struct twl_mapping twl6030_map[] = {
>>        { SUB_CHIP_ID0, TWL6030_BASEADD_ZERO },
>>        { SUB_CHIP_ID1, TWL6030_BASEADD_ZERO },
>>
>> -       { SUB_CHIP_ID2, TWL6030_BASEADD_ZERO },
>> +       { SUB_CHIP_ID1, TWL6030_BASEADD_AUX },
>
> Instead you can use TWL6030_MODULE_ID1, with base address as
> zero for all registers in auxiliaries register map.

Ok.

I'm actually thinking about this, and in the process on reviewing the
need to access those registers.

I should probably be using the regulator framework to control VAUX3 instead...

Thanks for your inputs.

Regards,
Sergio

>
>>        { SUB_CHIP_ID2, TWL6030_BASEADD_ZERO },
>>        { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
>>        { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
>> --
>> 1.7.7.4
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH v2 01/11] TWL6030: Add mapping for auxiliary regs
@ 2011-12-01 17:47       ` Aguirre, Sergio
  0 siblings, 0 replies; 65+ messages in thread
From: Aguirre, Sergio @ 2011-12-01 17:47 UTC (permalink / raw)
  To: T Krishnamoorthy, Balaji
  Cc: linux-media, linux-omap, laurent.pinchart, sakari.ailus

Hi Balaji,

Thanks for the review.

On Thu, Dec 1, 2011 at 9:58 AM, T Krishnamoorthy, Balaji
<balajitk@ti.com> wrote:
> On Thu, Dec 1, 2011 at 5:44 AM, Sergio Aguirre <saaguirre@ti.com> wrote:
>> Signed-off-by: Sergio Aguirre <saaguirre@ti.com>
>> ---
>>  drivers/mfd/twl-core.c |    2 +-
>>  1 files changed, 1 insertions(+), 1 deletions(-)
>>
>> diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c
>> index bfbd660..e26b564 100644
>> --- a/drivers/mfd/twl-core.c
>> +++ b/drivers/mfd/twl-core.c
>> @@ -323,7 +323,7 @@ static struct twl_mapping twl6030_map[] = {
>>        { SUB_CHIP_ID0, TWL6030_BASEADD_ZERO },
>>        { SUB_CHIP_ID1, TWL6030_BASEADD_ZERO },
>>
>> -       { SUB_CHIP_ID2, TWL6030_BASEADD_ZERO },
>> +       { SUB_CHIP_ID1, TWL6030_BASEADD_AUX },
>
> Instead you can use TWL6030_MODULE_ID1, with base address as
> zero for all registers in auxiliaries register map.

Ok.

I'm actually thinking about this, and in the process on reviewing the
need to access those registers.

I should probably be using the regulator framework to control VAUX3 instead...

Thanks for your inputs.

Regards,
Sergio

>
>>        { SUB_CHIP_ID2, TWL6030_BASEADD_ZERO },
>>        { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
>>        { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
>> --
>> 1.7.7.4
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH v2 02/11] mfd: twl6040: Fix wrong TWL6040_GPO3 bitfield value
  2011-12-01 17:35     ` Aguirre, Sergio
@ 2011-12-02  1:08       ` Laurent Pinchart
  0 siblings, 0 replies; 65+ messages in thread
From: Laurent Pinchart @ 2011-12-02  1:08 UTC (permalink / raw)
  To: Aguirre, Sergio; +Cc: linux-media, linux-omap, sakari.ailus

Hi Sergio,

On Thursday 01 December 2011 18:35:20 Aguirre, Sergio wrote:
> On Thu, Dec 1, 2011 at 11:24 AM, Laurent Pinchart wrote:
> > On Thursday 01 December 2011 01:14:51 Sergio Aguirre wrote:
> >> The define should be the result of 1 << Bit number.
> >> 
> >> Bit number for GPOCTL.GPO3 field is 2, which results
> >> in 0x4 value.
> >> 
> >> Signed-off-by: Sergio Aguirre <saaguirre@ti.com>
> >> ---
> >>  include/linux/mfd/twl6040.h |    2 +-
> >>  1 files changed, 1 insertions(+), 1 deletions(-)
> >> 
> >> diff --git a/include/linux/mfd/twl6040.h b/include/linux/mfd/twl6040.h
> >> index 2463c261..2a7ff16 100644
> >> --- a/include/linux/mfd/twl6040.h
> >> +++ b/include/linux/mfd/twl6040.h
> >> @@ -142,7 +142,7 @@
> >> 
> >>  #define TWL6040_GPO1                 0x01
> >>  #define TWL6040_GPO2                 0x02
> >> -#define TWL6040_GPO3                 0x03
> >> +#define TWL6040_GPO3                 0x04
> > 
> > What about defining the fields as (1 << x) instead then ?
> 
> I thought about that, but I guess I just wanted to keep it consistent with
> the rest of the file.

I've seen that as well. I'm fine with keeping the above defines if you prefer.

> Maybe I can create a separate patch for changing all these bitwise flags to
> use BIT() macros instead.

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v2 03/11] v4l: Introduce sensor operation for getting interface configuration
  2011-12-01  0:14   ` Sergio Aguirre
  (?)
@ 2011-12-02 13:32   ` Stanimir Varbanov
  2011-12-02 16:04     ` Aguirre, Sergio
  -1 siblings, 1 reply; 65+ messages in thread
From: Stanimir Varbanov @ 2011-12-02 13:32 UTC (permalink / raw)
  To: Sergio Aguirre; +Cc: linux-media, linux-omap, laurent.pinchart, sakari.ailus

Hi, Sergio

This change in interface is not used from the omap4iss driver.

You could drop it from the patch set, if so.

On 12/01/2011 02:14 AM, Sergio Aguirre wrote:
> From: Stanimir Varbanov <svarbanov@mm-sol.com>
> 
> Introduce g_interface_parms sensor operation for getting sensor
> interface parameters. These parameters are needed from the host side
> to determine it's own configuration.
> 
> Signed-off-by: Stanimir Varbanov <svarbanov@mm-sol.com>
> ---
>  include/media/v4l2-subdev.h |   42 ++++++++++++++++++++++++++++++++++++++++++
>  1 files changed, 42 insertions(+), 0 deletions(-)
> 
> diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h
> index f0f3358..0d322ed 100644
> --- a/include/media/v4l2-subdev.h
> +++ b/include/media/v4l2-subdev.h
> @@ -362,6 +362,42 @@ struct v4l2_subdev_vbi_ops {
>  	int (*s_sliced_fmt)(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt);
>  };
>  
> +/* Which interface the sensor use to provide it's image data */
> +enum v4l2_subdev_sensor_iface {
> +	V4L2_SUBDEV_SENSOR_PARALLEL,
> +	V4L2_SUBDEV_SENSOR_SERIAL,
> +};
> +
> +/* Each interface could use the following modes */
> +/* Image sensor provides horizontal and vertical sync signals */
> +#define V4L2_SUBDEV_SENSOR_MODE_PARALLEL_SYNC	0
> +/* BT.656 interface. Embedded sync */
> +#define V4L2_SUBDEV_SENSOR_MODE_PARALLEL_ITU	1
> +/* MIPI CSI1 */
> +#define V4L2_SUBDEV_SENSOR_MODE_SERIAL_CSI1	2
> +/* MIPI CSI2 */
> +#define V4L2_SUBDEV_SENSOR_MODE_SERIAL_CSI2	3
> +
> +struct v4l2_subdev_sensor_serial_parms {
> +	unsigned char lanes;		/* number of lanes used */
> +	unsigned char channel;		/* virtual channel */
> +	unsigned int phy_rate;		/* output rate at CSI phy in bps */
> +	unsigned int pix_clk;		/* pixel clock in Hz */
> +};
> +
> +struct v4l2_subdev_sensor_parallel_parms {
> +	unsigned int pix_clk;		/* pixel clock in Hz */
> +};
> +
> +struct v4l2_subdev_sensor_interface_parms {
> +	enum v4l2_subdev_sensor_iface if_type;
> +	unsigned int if_mode;
> +	union {
> +		struct v4l2_subdev_sensor_serial_parms serial;
> +		struct v4l2_subdev_sensor_parallel_parms parallel;
> +	} parms;
> +};
> +
>  /**
>   * struct v4l2_subdev_sensor_ops - v4l2-subdev sensor operations
>   * @g_skip_top_lines: number of lines at the top of the image to be skipped.
> @@ -371,10 +407,16 @@ struct v4l2_subdev_vbi_ops {
>   * @g_skip_frames: number of frames to skip at stream start. This is needed for
>   *		   buggy sensors that generate faulty frames when they are
>   *		   turned on.
> + * @g_interface_parms: get sensor interface parameters. The sensor subdev should
> + *		       fill this structure with current interface params. These
> + *		       interface parameters are needed on host side to configure
> + *		       it's own hardware receivers.
>   */
>  struct v4l2_subdev_sensor_ops {
>  	int (*g_skip_top_lines)(struct v4l2_subdev *sd, u32 *lines);
>  	int (*g_skip_frames)(struct v4l2_subdev *sd, u32 *frames);
> +	int (*g_interface_parms)(struct v4l2_subdev *sd,
> +			struct v4l2_subdev_sensor_interface_parms *parms);
>  };
>  
>  /*


-- 
best regards,
Stan

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

* Re: [PATCH v2 03/11] v4l: Introduce sensor operation for getting interface configuration
  2011-12-02 13:32   ` Stanimir Varbanov
@ 2011-12-02 16:04     ` Aguirre, Sergio
  0 siblings, 0 replies; 65+ messages in thread
From: Aguirre, Sergio @ 2011-12-02 16:04 UTC (permalink / raw)
  To: Stanimir Varbanov; +Cc: linux-media, linux-omap, laurent.pinchart, sakari.ailus

Hi Stan,

On Fri, Dec 2, 2011 at 7:32 AM, Stanimir Varbanov <svarbanov@mm-sol.com> wrote:
> Hi, Sergio
>
> This change in interface is not used from the omap4iss driver.
>
> You could drop it from the patch set, if so.

Oops, yes... I used to depend on this for my soc_camera implementation before...

You're absolutely right, i'll remove it in the next iteration.

Merci!

>
> On 12/01/2011 02:14 AM, Sergio Aguirre wrote:
>> From: Stanimir Varbanov <svarbanov@mm-sol.com>
>>
>> Introduce g_interface_parms sensor operation for getting sensor
>> interface parameters. These parameters are needed from the host side
>> to determine it's own configuration.
>>
>> Signed-off-by: Stanimir Varbanov <svarbanov@mm-sol.com>
>> ---
>>  include/media/v4l2-subdev.h |   42 ++++++++++++++++++++++++++++++++++++++++++
>>  1 files changed, 42 insertions(+), 0 deletions(-)
>>
>> diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h
>> index f0f3358..0d322ed 100644
>> --- a/include/media/v4l2-subdev.h
>> +++ b/include/media/v4l2-subdev.h
>> @@ -362,6 +362,42 @@ struct v4l2_subdev_vbi_ops {
>>       int (*s_sliced_fmt)(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt);
>>  };
>>
>> +/* Which interface the sensor use to provide it's image data */
>> +enum v4l2_subdev_sensor_iface {
>> +     V4L2_SUBDEV_SENSOR_PARALLEL,
>> +     V4L2_SUBDEV_SENSOR_SERIAL,
>> +};
>> +
>> +/* Each interface could use the following modes */
>> +/* Image sensor provides horizontal and vertical sync signals */
>> +#define V4L2_SUBDEV_SENSOR_MODE_PARALLEL_SYNC        0
>> +/* BT.656 interface. Embedded sync */
>> +#define V4L2_SUBDEV_SENSOR_MODE_PARALLEL_ITU 1
>> +/* MIPI CSI1 */
>> +#define V4L2_SUBDEV_SENSOR_MODE_SERIAL_CSI1  2
>> +/* MIPI CSI2 */
>> +#define V4L2_SUBDEV_SENSOR_MODE_SERIAL_CSI2  3
>> +
>> +struct v4l2_subdev_sensor_serial_parms {
>> +     unsigned char lanes;            /* number of lanes used */
>> +     unsigned char channel;          /* virtual channel */
>> +     unsigned int phy_rate;          /* output rate at CSI phy in bps */
>> +     unsigned int pix_clk;           /* pixel clock in Hz */
>> +};
>> +
>> +struct v4l2_subdev_sensor_parallel_parms {
>> +     unsigned int pix_clk;           /* pixel clock in Hz */
>> +};
>> +
>> +struct v4l2_subdev_sensor_interface_parms {
>> +     enum v4l2_subdev_sensor_iface if_type;
>> +     unsigned int if_mode;
>> +     union {
>> +             struct v4l2_subdev_sensor_serial_parms serial;
>> +             struct v4l2_subdev_sensor_parallel_parms parallel;
>> +     } parms;
>> +};
>> +
>>  /**
>>   * struct v4l2_subdev_sensor_ops - v4l2-subdev sensor operations
>>   * @g_skip_top_lines: number of lines at the top of the image to be skipped.
>> @@ -371,10 +407,16 @@ struct v4l2_subdev_vbi_ops {
>>   * @g_skip_frames: number of frames to skip at stream start. This is needed for
>>   *              buggy sensors that generate faulty frames when they are
>>   *              turned on.
>> + * @g_interface_parms: get sensor interface parameters. The sensor subdev should
>> + *                  fill this structure with current interface params. These
>> + *                  interface parameters are needed on host side to configure
>> + *                  it's own hardware receivers.
>>   */
>>  struct v4l2_subdev_sensor_ops {
>>       int (*g_skip_top_lines)(struct v4l2_subdev *sd, u32 *lines);
>>       int (*g_skip_frames)(struct v4l2_subdev *sd, u32 *frames);
>> +     int (*g_interface_parms)(struct v4l2_subdev *sd,
>> +                     struct v4l2_subdev_sensor_interface_parms *parms);
>>  };
>>
>>  /*
>
>
> --
> best regards,
> Stan

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

* Re: [PATCH v2 05/11] OMAP4: Add base addresses for ISS
  2011-12-01  0:14   ` Sergio Aguirre
@ 2011-12-02 22:45     ` Kevin Hilman
  -1 siblings, 0 replies; 65+ messages in thread
From: Kevin Hilman @ 2011-12-02 22:45 UTC (permalink / raw)
  To: Sergio Aguirre; +Cc: linux-media, linux-omap, laurent.pinchart, sakari.ailus

Sergio Aguirre <saaguirre@ti.com> writes:

> NOTE: This isn't the whole list of features that the
> ISS supports, but the only ones supported at the moment.
>
> Signed-off-by: Sergio Aguirre <saaguirre@ti.com>

[...]

> diff --git a/arch/arm/plat-omap/include/plat/omap44xx.h b/arch/arm/plat-omap/include/plat/omap44xx.h
> index ea2b8a6..31432aa 100644
> --- a/arch/arm/plat-omap/include/plat/omap44xx.h
> +++ b/arch/arm/plat-omap/include/plat/omap44xx.h
> @@ -49,6 +49,15 @@
>  #define OMAP44XX_MAILBOX_BASE		(L4_44XX_BASE + 0xF4000)
>  #define OMAP44XX_HSUSB_OTG_BASE		(L4_44XX_BASE + 0xAB000)
>  
> +#define OMAP44XX_ISS_BASE			0x52000000
> +#define OMAP44XX_ISS_TOP_BASE			(OMAP44XX_ISS_BASE + 0x0)
> +#define OMAP44XX_ISS_CSI2_A_REGS1_BASE		(OMAP44XX_ISS_BASE + 0x1000)
> +#define OMAP44XX_ISS_CAMERARX_CORE1_BASE	(OMAP44XX_ISS_BASE + 0x1170)
> +
> +#define OMAP44XX_ISS_TOP_END			(OMAP44XX_ISS_TOP_BASE + 256 - 1)
> +#define OMAP44XX_ISS_CSI2_A_REGS1_END		(OMAP44XX_ISS_CSI2_A_REGS1_BASE + 368 - 1)
> +#define OMAP44XX_ISS_CAMERARX_CORE1_END		(OMAP44XX_ISS_CAMERARX_CORE1_BASE + 32 - 1)
> +
>  #define OMAP4_MMU1_BASE			0x55082000
>  #define OMAP4_MMU2_BASE			0x4A066000

Who are the users of thes address ranges?

IMO, we shouldn't ned to add anymore based address definitions.  These
should be done in the hwmod data, and drivers get base addresses using
the standard ways of getting resources (DT or platform_get_resource())

Kevin

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

* Re: [PATCH v2 05/11] OMAP4: Add base addresses for ISS
@ 2011-12-02 22:45     ` Kevin Hilman
  0 siblings, 0 replies; 65+ messages in thread
From: Kevin Hilman @ 2011-12-02 22:45 UTC (permalink / raw)
  To: Sergio Aguirre; +Cc: linux-media, linux-omap, laurent.pinchart, sakari.ailus

Sergio Aguirre <saaguirre@ti.com> writes:

> NOTE: This isn't the whole list of features that the
> ISS supports, but the only ones supported at the moment.
>
> Signed-off-by: Sergio Aguirre <saaguirre@ti.com>

[...]

> diff --git a/arch/arm/plat-omap/include/plat/omap44xx.h b/arch/arm/plat-omap/include/plat/omap44xx.h
> index ea2b8a6..31432aa 100644
> --- a/arch/arm/plat-omap/include/plat/omap44xx.h
> +++ b/arch/arm/plat-omap/include/plat/omap44xx.h
> @@ -49,6 +49,15 @@
>  #define OMAP44XX_MAILBOX_BASE		(L4_44XX_BASE + 0xF4000)
>  #define OMAP44XX_HSUSB_OTG_BASE		(L4_44XX_BASE + 0xAB000)
>  
> +#define OMAP44XX_ISS_BASE			0x52000000
> +#define OMAP44XX_ISS_TOP_BASE			(OMAP44XX_ISS_BASE + 0x0)
> +#define OMAP44XX_ISS_CSI2_A_REGS1_BASE		(OMAP44XX_ISS_BASE + 0x1000)
> +#define OMAP44XX_ISS_CAMERARX_CORE1_BASE	(OMAP44XX_ISS_BASE + 0x1170)
> +
> +#define OMAP44XX_ISS_TOP_END			(OMAP44XX_ISS_TOP_BASE + 256 - 1)
> +#define OMAP44XX_ISS_CSI2_A_REGS1_END		(OMAP44XX_ISS_CSI2_A_REGS1_BASE + 368 - 1)
> +#define OMAP44XX_ISS_CAMERARX_CORE1_END		(OMAP44XX_ISS_CAMERARX_CORE1_BASE + 32 - 1)
> +
>  #define OMAP4_MMU1_BASE			0x55082000
>  #define OMAP4_MMU2_BASE			0x4A066000

Who are the users of thes address ranges?

IMO, we shouldn't ned to add anymore based address definitions.  These
should be done in the hwmod data, and drivers get base addresses using
the standard ways of getting resources (DT or platform_get_resource())

Kevin

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

* Re: [PATCH v2 04/11] OMAP4: hwmod: Include CSI2A and CSIPHY1 memory sections
  2011-12-01 13:17       ` Aguirre, Sergio
@ 2011-12-02 22:49         ` Kevin Hilman
  -1 siblings, 0 replies; 65+ messages in thread
From: Kevin Hilman @ 2011-12-02 22:49 UTC (permalink / raw)
  To: Aguirre, Sergio
  Cc: Hiremath, Vaibhav, linux-media, linux-omap, laurent.pinchart,
	sakari.ailus, Benoit Cousson

+Benoit,

"Aguirre, Sergio" <saaguirre@ti.com> writes:

> Hi Vaibhav,
>
> Thanks for the comments.
>
> On Thu, Dec 1, 2011 at 12:34 AM, Hiremath, Vaibhav <hvaibhav@ti.com> wrote:
>>
>>> -----Original Message-----
>>> From: linux-media-owner@vger.kernel.org [mailto:linux-media-
>>> owner@vger.kernel.org] On Behalf Of Aguirre, Sergio
>>> Sent: Thursday, December 01, 2011 5:45 AM
>>> To: linux-media@vger.kernel.org
>>> Cc: linux-omap@vger.kernel.org; laurent.pinchart@ideasonboard.com;
>>> sakari.ailus@iki.fi; Aguirre, Sergio
>>> Subject: [PATCH v2 04/11] OMAP4: hwmod: Include CSI2A and CSIPHY1 memory
>>> sections
>>>
>>> Signed-off-by: Sergio Aguirre <saaguirre@ti.com>
>>> ---
>>>  arch/arm/mach-omap2/omap_hwmod_44xx_data.c |   16 +++++++++++++---
>>>  1 files changed, 13 insertions(+), 3 deletions(-)
>>>
>>> diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-
>>> omap2/omap_hwmod_44xx_data.c
>>> index 7695e5d..1b59e2f 100644
>>> --- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
>>> +++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
>>> @@ -2623,8 +2623,18 @@ static struct omap_hwmod_ocp_if
>>> *omap44xx_iss_masters[] = {
>>>
>>>  static struct omap_hwmod_addr_space omap44xx_iss_addrs[] = {
>>>       {
>>> -             .pa_start       = 0x52000000,
>>> -             .pa_end         = 0x520000ff,
>>> +             .pa_start       = OMAP44XX_ISS_TOP_BASE,
>>> +             .pa_end         = OMAP44XX_ISS_TOP_END,
>>> +             .flags          = ADDR_TYPE_RT
>>> +     },
>>> +     {
>>> +             .pa_start       = OMAP44XX_ISS_CSI2_A_REGS1_BASE,
>>> +             .pa_end         = OMAP44XX_ISS_CSI2_A_REGS1_END,
>>> +             .flags          = ADDR_TYPE_RT
>>> +     },
>>> +     {
>>> +             .pa_start       = OMAP44XX_ISS_CAMERARX_CORE1_BASE,
>>> +             .pa_end         = OMAP44XX_ISS_CAMERARX_CORE1_END,
>>>               .flags          = ADDR_TYPE_RT
>>>       },
>> This patch will result in build failure, because, the above base addresses
>> are getting defined in the next patch
>>
>> [PATCH v2 05/11] OMAP4: Add base addresses for ISS
>
> Agreed. Will revisit "git-bisectability" of the patch series. Will fix.

To fix this, just drop the #defines from the header, and use raw
addresses directly.

Also, work with Benoit to make sure at the scripts that autogenerate
this data are updated to include these two regions.

Kevin

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

* Re: [PATCH v2 04/11] OMAP4: hwmod: Include CSI2A and CSIPHY1 memory sections
@ 2011-12-02 22:49         ` Kevin Hilman
  0 siblings, 0 replies; 65+ messages in thread
From: Kevin Hilman @ 2011-12-02 22:49 UTC (permalink / raw)
  To: Aguirre, Sergio
  Cc: Hiremath, Vaibhav, linux-media, linux-omap, laurent.pinchart,
	sakari.ailus, Benoit Cousson

+Benoit,

"Aguirre, Sergio" <saaguirre@ti.com> writes:

> Hi Vaibhav,
>
> Thanks for the comments.
>
> On Thu, Dec 1, 2011 at 12:34 AM, Hiremath, Vaibhav <hvaibhav@ti.com> wrote:
>>
>>> -----Original Message-----
>>> From: linux-media-owner@vger.kernel.org [mailto:linux-media-
>>> owner@vger.kernel.org] On Behalf Of Aguirre, Sergio
>>> Sent: Thursday, December 01, 2011 5:45 AM
>>> To: linux-media@vger.kernel.org
>>> Cc: linux-omap@vger.kernel.org; laurent.pinchart@ideasonboard.com;
>>> sakari.ailus@iki.fi; Aguirre, Sergio
>>> Subject: [PATCH v2 04/11] OMAP4: hwmod: Include CSI2A and CSIPHY1 memory
>>> sections
>>>
>>> Signed-off-by: Sergio Aguirre <saaguirre@ti.com>
>>> ---
>>>  arch/arm/mach-omap2/omap_hwmod_44xx_data.c |   16 +++++++++++++---
>>>  1 files changed, 13 insertions(+), 3 deletions(-)
>>>
>>> diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-
>>> omap2/omap_hwmod_44xx_data.c
>>> index 7695e5d..1b59e2f 100644
>>> --- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
>>> +++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
>>> @@ -2623,8 +2623,18 @@ static struct omap_hwmod_ocp_if
>>> *omap44xx_iss_masters[] = {
>>>
>>>  static struct omap_hwmod_addr_space omap44xx_iss_addrs[] = {
>>>       {
>>> -             .pa_start       = 0x52000000,
>>> -             .pa_end         = 0x520000ff,
>>> +             .pa_start       = OMAP44XX_ISS_TOP_BASE,
>>> +             .pa_end         = OMAP44XX_ISS_TOP_END,
>>> +             .flags          = ADDR_TYPE_RT
>>> +     },
>>> +     {
>>> +             .pa_start       = OMAP44XX_ISS_CSI2_A_REGS1_BASE,
>>> +             .pa_end         = OMAP44XX_ISS_CSI2_A_REGS1_END,
>>> +             .flags          = ADDR_TYPE_RT
>>> +     },
>>> +     {
>>> +             .pa_start       = OMAP44XX_ISS_CAMERARX_CORE1_BASE,
>>> +             .pa_end         = OMAP44XX_ISS_CAMERARX_CORE1_END,
>>>               .flags          = ADDR_TYPE_RT
>>>       },
>> This patch will result in build failure, because, the above base addresses
>> are getting defined in the next patch
>>
>> [PATCH v2 05/11] OMAP4: Add base addresses for ISS
>
> Agreed. Will revisit "git-bisectability" of the patch series. Will fix.

To fix this, just drop the #defines from the header, and use raw
addresses directly.

Also, work with Benoit to make sure at the scripts that autogenerate
this data are updated to include these two regions.

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

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

* Re: [PATCH v2 04/11] OMAP4: hwmod: Include CSI2A and CSIPHY1 memory sections
  2011-12-02 22:49         ` Kevin Hilman
  (?)
@ 2011-12-02 22:59         ` Aguirre, Sergio
  2011-12-02 23:18             ` Kevin Hilman
  -1 siblings, 1 reply; 65+ messages in thread
From: Aguirre, Sergio @ 2011-12-02 22:59 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Hiremath, Vaibhav, linux-media, linux-omap, laurent.pinchart,
	sakari.ailus, Benoit Cousson

Hi Kevin,

Thanks for the review.

On Fri, Dec 2, 2011 at 4:49 PM, Kevin Hilman <khilman@ti.com> wrote:
> +Benoit,
>
> "Aguirre, Sergio" <saaguirre@ti.com> writes:
>
>> Hi Vaibhav,
>>
>> Thanks for the comments.
>>
>> On Thu, Dec 1, 2011 at 12:34 AM, Hiremath, Vaibhav <hvaibhav@ti.com> wrote:
>>>
>>>> -----Original Message-----
>>>> From: linux-media-owner@vger.kernel.org [mailto:linux-media-
>>>> owner@vger.kernel.org] On Behalf Of Aguirre, Sergio
>>>> Sent: Thursday, December 01, 2011 5:45 AM
>>>> To: linux-media@vger.kernel.org
>>>> Cc: linux-omap@vger.kernel.org; laurent.pinchart@ideasonboard.com;
>>>> sakari.ailus@iki.fi; Aguirre, Sergio
>>>> Subject: [PATCH v2 04/11] OMAP4: hwmod: Include CSI2A and CSIPHY1 memory
>>>> sections
>>>>
>>>> Signed-off-by: Sergio Aguirre <saaguirre@ti.com>
>>>> ---
>>>>  arch/arm/mach-omap2/omap_hwmod_44xx_data.c |   16 +++++++++++++---
>>>>  1 files changed, 13 insertions(+), 3 deletions(-)
>>>>
>>>> diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-
>>>> omap2/omap_hwmod_44xx_data.c
>>>> index 7695e5d..1b59e2f 100644
>>>> --- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
>>>> +++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
>>>> @@ -2623,8 +2623,18 @@ static struct omap_hwmod_ocp_if
>>>> *omap44xx_iss_masters[] = {
>>>>
>>>>  static struct omap_hwmod_addr_space omap44xx_iss_addrs[] = {
>>>>       {
>>>> -             .pa_start       = 0x52000000,
>>>> -             .pa_end         = 0x520000ff,
>>>> +             .pa_start       = OMAP44XX_ISS_TOP_BASE,
>>>> +             .pa_end         = OMAP44XX_ISS_TOP_END,
>>>> +             .flags          = ADDR_TYPE_RT
>>>> +     },
>>>> +     {
>>>> +             .pa_start       = OMAP44XX_ISS_CSI2_A_REGS1_BASE,
>>>> +             .pa_end         = OMAP44XX_ISS_CSI2_A_REGS1_END,
>>>> +             .flags          = ADDR_TYPE_RT
>>>> +     },
>>>> +     {
>>>> +             .pa_start       = OMAP44XX_ISS_CAMERARX_CORE1_BASE,
>>>> +             .pa_end         = OMAP44XX_ISS_CAMERARX_CORE1_END,
>>>>               .flags          = ADDR_TYPE_RT
>>>>       },
>>> This patch will result in build failure, because, the above base addresses
>>> are getting defined in the next patch
>>>
>>> [PATCH v2 05/11] OMAP4: Add base addresses for ISS
>>
>> Agreed. Will revisit "git-bisectability" of the patch series. Will fix.
>
> To fix this, just drop the #defines from the header, and use raw
> addresses directly.

Ok, i'll drop patch #5 in this series.

>
> Also, work with Benoit to make sure at the scripts that autogenerate
> this data are updated to include these two regions.

Ok.

As a side note, I might need more addresses for the rest of the ISP
components later on. I'll enable more subsystems once the CSI2-A only
initial version is in an acceptable state.

Regards,
Sergio

>
> Kevin

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

* Re: [PATCH v2 05/11] OMAP4: Add base addresses for ISS
  2011-12-02 22:45     ` Kevin Hilman
@ 2011-12-02 23:01       ` Aguirre, Sergio
  -1 siblings, 0 replies; 65+ messages in thread
From: Aguirre, Sergio @ 2011-12-02 23:01 UTC (permalink / raw)
  To: Kevin Hilman; +Cc: linux-media, linux-omap, laurent.pinchart, sakari.ailus

Hi Kevin,

Thanks for the review.

On Fri, Dec 2, 2011 at 4:45 PM, Kevin Hilman <khilman@ti.com> wrote:
> Sergio Aguirre <saaguirre@ti.com> writes:
>
>> NOTE: This isn't the whole list of features that the
>> ISS supports, but the only ones supported at the moment.
>>
>> Signed-off-by: Sergio Aguirre <saaguirre@ti.com>
>
> [...]
>
>> diff --git a/arch/arm/plat-omap/include/plat/omap44xx.h b/arch/arm/plat-omap/include/plat/omap44xx.h
>> index ea2b8a6..31432aa 100644
>> --- a/arch/arm/plat-omap/include/plat/omap44xx.h
>> +++ b/arch/arm/plat-omap/include/plat/omap44xx.h
>> @@ -49,6 +49,15 @@
>>  #define OMAP44XX_MAILBOX_BASE                (L4_44XX_BASE + 0xF4000)
>>  #define OMAP44XX_HSUSB_OTG_BASE              (L4_44XX_BASE + 0xAB000)
>>
>> +#define OMAP44XX_ISS_BASE                    0x52000000
>> +#define OMAP44XX_ISS_TOP_BASE                        (OMAP44XX_ISS_BASE + 0x0)
>> +#define OMAP44XX_ISS_CSI2_A_REGS1_BASE               (OMAP44XX_ISS_BASE + 0x1000)
>> +#define OMAP44XX_ISS_CAMERARX_CORE1_BASE     (OMAP44XX_ISS_BASE + 0x1170)
>> +
>> +#define OMAP44XX_ISS_TOP_END                 (OMAP44XX_ISS_TOP_BASE + 256 - 1)
>> +#define OMAP44XX_ISS_CSI2_A_REGS1_END                (OMAP44XX_ISS_CSI2_A_REGS1_BASE + 368 - 1)
>> +#define OMAP44XX_ISS_CAMERARX_CORE1_END              (OMAP44XX_ISS_CAMERARX_CORE1_BASE + 32 - 1)
>> +
>>  #define OMAP4_MMU1_BASE                      0x55082000
>>  #define OMAP4_MMU2_BASE                      0x4A066000
>
> Who are the users of thes address ranges?
>
> IMO, we shouldn't ned to add anymore based address definitions.  These
> should be done in the hwmod data, and drivers get base addresses using
> the standard ways of getting resources (DT or platform_get_resource())

I see... I get your point now.

Will remove them from this patch series then.

Regards,
Sergio

>
> Kevin

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

* Re: [PATCH v2 05/11] OMAP4: Add base addresses for ISS
@ 2011-12-02 23:01       ` Aguirre, Sergio
  0 siblings, 0 replies; 65+ messages in thread
From: Aguirre, Sergio @ 2011-12-02 23:01 UTC (permalink / raw)
  To: Kevin Hilman; +Cc: linux-media, linux-omap, laurent.pinchart, sakari.ailus

Hi Kevin,

Thanks for the review.

On Fri, Dec 2, 2011 at 4:45 PM, Kevin Hilman <khilman@ti.com> wrote:
> Sergio Aguirre <saaguirre@ti.com> writes:
>
>> NOTE: This isn't the whole list of features that the
>> ISS supports, but the only ones supported at the moment.
>>
>> Signed-off-by: Sergio Aguirre <saaguirre@ti.com>
>
> [...]
>
>> diff --git a/arch/arm/plat-omap/include/plat/omap44xx.h b/arch/arm/plat-omap/include/plat/omap44xx.h
>> index ea2b8a6..31432aa 100644
>> --- a/arch/arm/plat-omap/include/plat/omap44xx.h
>> +++ b/arch/arm/plat-omap/include/plat/omap44xx.h
>> @@ -49,6 +49,15 @@
>>  #define OMAP44XX_MAILBOX_BASE                (L4_44XX_BASE + 0xF4000)
>>  #define OMAP44XX_HSUSB_OTG_BASE              (L4_44XX_BASE + 0xAB000)
>>
>> +#define OMAP44XX_ISS_BASE                    0x52000000
>> +#define OMAP44XX_ISS_TOP_BASE                        (OMAP44XX_ISS_BASE + 0x0)
>> +#define OMAP44XX_ISS_CSI2_A_REGS1_BASE               (OMAP44XX_ISS_BASE + 0x1000)
>> +#define OMAP44XX_ISS_CAMERARX_CORE1_BASE     (OMAP44XX_ISS_BASE + 0x1170)
>> +
>> +#define OMAP44XX_ISS_TOP_END                 (OMAP44XX_ISS_TOP_BASE + 256 - 1)
>> +#define OMAP44XX_ISS_CSI2_A_REGS1_END                (OMAP44XX_ISS_CSI2_A_REGS1_BASE + 368 - 1)
>> +#define OMAP44XX_ISS_CAMERARX_CORE1_END              (OMAP44XX_ISS_CAMERARX_CORE1_BASE + 32 - 1)
>> +
>>  #define OMAP4_MMU1_BASE                      0x55082000
>>  #define OMAP4_MMU2_BASE                      0x4A066000
>
> Who are the users of thes address ranges?
>
> IMO, we shouldn't ned to add anymore based address definitions.  These
> should be done in the hwmod data, and drivers get base addresses using
> the standard ways of getting resources (DT or platform_get_resource())

I see... I get your point now.

Will remove them from this patch series then.

Regards,
Sergio

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

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

* Re: [PATCH v2 04/11] OMAP4: hwmod: Include CSI2A and CSIPHY1 memory sections
  2011-12-02 22:59         ` Aguirre, Sergio
@ 2011-12-02 23:18             ` Kevin Hilman
  0 siblings, 0 replies; 65+ messages in thread
From: Kevin Hilman @ 2011-12-02 23:18 UTC (permalink / raw)
  To: Aguirre, Sergio
  Cc: Hiremath, Vaibhav, linux-media, linux-omap, laurent.pinchart,
	sakari.ailus, Benoit Cousson

"Aguirre, Sergio" <saaguirre@ti.com> writes:

[...]

>>
>> Also, work with Benoit to make sure at the scripts that autogenerate
>> this data are updated to include these two regions.
>
> Ok.
>
> As a side note, I might need more addresses for the rest of the ISP
> components later on. I'll enable more subsystems once the CSI2-A only
> initial version is in an acceptable state.

That's fine, just work with Benoit to be sure that the autogen scripts
are updated for all the subsystems.

Kevin

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

* Re: [PATCH v2 04/11] OMAP4: hwmod: Include CSI2A and CSIPHY1 memory sections
@ 2011-12-02 23:18             ` Kevin Hilman
  0 siblings, 0 replies; 65+ messages in thread
From: Kevin Hilman @ 2011-12-02 23:18 UTC (permalink / raw)
  To: Aguirre, Sergio
  Cc: Hiremath, Vaibhav, linux-media, linux-omap, laurent.pinchart,
	sakari.ailus, Benoit Cousson

"Aguirre, Sergio" <saaguirre@ti.com> writes:

[...]

>>
>> Also, work with Benoit to make sure at the scripts that autogenerate
>> this data are updated to include these two regions.
>
> Ok.
>
> As a side note, I might need more addresses for the rest of the ISP
> components later on. I'll enable more subsystems once the CSI2-A only
> initial version is in an acceptable state.

That's fine, just work with Benoit to be sure that the autogen scripts
are updated for all the subsystems.

Kevin

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

* Re: [PATCH v2 00/11] v4l2: OMAP4 ISS driver + Sensor + Board support
  2011-12-01  0:14 ` Sergio Aguirre
                   ` (14 preceding siblings ...)
  (?)
@ 2011-12-07 21:50 ` Tony Lindgren
  -1 siblings, 0 replies; 65+ messages in thread
From: Tony Lindgren @ 2011-12-07 21:50 UTC (permalink / raw)
  To: Sergio Aguirre; +Cc: linux-media, linux-omap, laurent.pinchart, sakari.ailus

* Sergio Aguirre <saaguirre@ti.com> [111130 15:40]:
> Hi everyone,
> 
> This is the second version of the OMAP4 ISS driver,
> now ported to the Media Controller framework AND supporting
> videobuf2 framework.

I suggest you do a device tree only binding for new drivers.

>  arch/arm/mach-omap2/board-4430sdp-camera.c    |  221 ++++
>  arch/arm/mach-omap2/board-omap4panda-camera.c |  198 ++++
>  arch/arm/mach-omap2/devices.c                 |   40 +

That leaves out most of the code above.

Regards,

Tony

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

* Re: [PATCH v2 09/11] arm: omap4430sdp: Add support for omap4iss camera
  2011-12-01  0:14   ` Sergio Aguirre
  (?)
@ 2011-12-11  8:05   ` Sakari Ailus
  -1 siblings, 0 replies; 65+ messages in thread
From: Sakari Ailus @ 2011-12-11  8:05 UTC (permalink / raw)
  To: Sergio Aguirre; +Cc: linux-media, linux-omap, laurent.pinchart

Hi Sergio,

Thanks for the patches!!

I think the general direction for such configurations is that the device
tree is favoured over the platform data.

On Wed, Nov 30, 2011 at 06:14:58PM -0600, Sergio Aguirre wrote:
> This adds support for camera interface with the support for
> following sensors:
> 
> - OV5640
> - OV5650
> 
> Signed-off-by: Sergio Aguirre <saaguirre@ti.com>
> ---
>  arch/arm/mach-omap2/Kconfig                |   27 ++++
>  arch/arm/mach-omap2/Makefile               |    2 +
>  arch/arm/mach-omap2/board-4430sdp-camera.c |  221 ++++++++++++++++++++++++++++
>  3 files changed, 250 insertions(+), 0 deletions(-)
>  create mode 100644 arch/arm/mach-omap2/board-4430sdp-camera.c
> 
> diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
> index 5034147..f883abb 100644
> --- a/arch/arm/mach-omap2/Kconfig
> +++ b/arch/arm/mach-omap2/Kconfig
> @@ -323,6 +323,33 @@ config MACH_OMAP_4430SDP
>  	select OMAP_PACKAGE_CBS
>  	select REGULATOR_FIXED_VOLTAGE
>  
> +config MACH_OMAP_4430SDP_CAMERA_SUPPORT
> +	bool "OMAP 4430 SDP board Camera support"
> +	depends on MACH_OMAP_4430SDP
> +	select MEDIA_SUPPORT
> +	select MEDIA_CONTROLLER
> +	select VIDEO_DEV
> +	select VIDEO_V4L2_SUBDEV_API
> +	select VIDEO_OMAP4
> +	help
> +	  Enable Camera HW support for OMAP 4430 SDP board
> +	  This is for using the OMAP4 ISS CSI2A Camera sensor
> +	  interface.
> +
> +choice
> +	prompt "Camera sensor to use"
> +	depends on MACH_OMAP_4430SDP_CAMERA_SUPPORT
> +	default MACH_OMAP_4430SDP_CAM_OV5650
> +
> +	config MACH_OMAP_4430SDP_CAM_OV5640
> +		bool "Use OmniVision OV5640 Camera"
> +		select VIDEO_OV5640
> +
> +	config MACH_OMAP_4430SDP_CAM_OV5650
> +		bool "Use OmniVision OV5650 Camera"
> +		select VIDEO_OV5650
> +endchoice
> +
>  config MACH_OMAP4_PANDA
>  	bool "OMAP4 Panda Board"
>  	default y
> diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
> index 69ab1c0..8bc446a 100644
> --- a/arch/arm/mach-omap2/Makefile
> +++ b/arch/arm/mach-omap2/Makefile
> @@ -235,6 +235,8 @@ obj-$(CONFIG_MACH_TI8168EVM)		+= board-ti8168evm.o
>  
>  # Platform specific device init code
>  
> +obj-$(CONFIG_MACH_OMAP_4430SDP_CAMERA_SUPPORT)	+= board-4430sdp-camera.o
> +
>  omap-flash-$(CONFIG_MTD_NAND_OMAP2)	:= board-flash.o
>  omap-flash-$(CONFIG_MTD_ONENAND_OMAP2)	:= board-flash.o
>  obj-y					+= $(omap-flash-y) $(omap-flash-m)
> diff --git a/arch/arm/mach-omap2/board-4430sdp-camera.c b/arch/arm/mach-omap2/board-4430sdp-camera.c
> new file mode 100644
> index 0000000..e62ee50
> --- /dev/null
> +++ b/arch/arm/mach-omap2/board-4430sdp-camera.c
> @@ -0,0 +1,221 @@
> +#include <linux/gpio.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/i2c/twl.h>
> +#include <linux/mfd/twl6040.h>
> +
> +#include <plat/i2c.h>
> +#include <plat/omap-pm.h>
> +
> +#include <asm/mach-types.h>
> +
> +#include <media/ov5640.h>
> +#include <media/ov5650.h>
> +
> +#include "devices.h"
> +#include "../../../drivers/media/video/omap4iss/iss.h"
> +
> +#include "control.h"
> +#include "mux.h"
> +
> +#define OMAP4430SDP_GPIO_CAM_PDN_C	39
> +
> +static struct clk *sdp4430_cam_aux_clk;
> +
> +static int sdp4430_ov5640_power(struct v4l2_subdev *subdev, int on)
> +{
> +	struct iss_device *iss = v4l2_dev_to_iss_device(subdev->v4l2_dev);
> +	int ret = 0;
> +	struct iss_csiphy_dphy_cfg dphy;
> +	struct iss_csiphy_lanes_cfg lanes;
> +#ifdef CONFIG_MACH_OMAP_4430SDP_CAM_OV5650
> +	unsigned int ddr_freq = 480; /* FIXME: Do an actual query for this */
> +#elif defined(CONFIG_MACH_OMAP_4430SDP_CAM_OV5640)
> +	unsigned int ddr_freq = 336; /* FIXME: Do an actual query for this */
> +#endif
> +
> +	memset(&lanes, 0, sizeof(lanes));
> +	memset(&dphy, 0, sizeof(dphy));
> +
> +	lanes.clk.pos = 1;
> +	lanes.clk.pol = 0;
> +	lanes.data[0].pos = 2;
> +	lanes.data[0].pol = 0;
> +#ifdef CONFIG_MACH_OMAP_4430SDP_CAM_OV5650
> +	lanes.data[1].pos = 3;
> +	lanes.data[1].pol = 0;
> +#endif

Lane configuration should be static and be set by the driver according to
data from DT.

I don't know about OMAP 4 but I think it should work on OMAP 3. I still need
to try that out myself! :p

> +
> +	dphy.ths_term = ((((12500 * ddr_freq + 1000000) / 1000000) - 1) & 0xFF);
> +	dphy.ths_settle = ((((90000 * ddr_freq + 1000000) / 1000000) + 3) & 0xFF);
> +	dphy.tclk_term = 0;
> +	dphy.tclk_miss = 1;
> +	dphy.tclk_settle = 14;

How sensor / board dependent is the dphy configuration? Could this make it
to the CSI-2 receiver driver instead? I'm actually working on the same on
the OMAP 3. :)

> +
> +	if (on) {
> +		u8 gpoctl = 0;
> +
> +		/* TWL6030_BASEADD_AUX */
> +		twl_i2c_write_u8(15, 0x00, 0xB);
> +		twl_i2c_write_u8(15, 0x80, 0x1);

The regulators should be added to twl6030. The power sequences also should
be part of the sensor driver.

> +
> +		mdelay(50);

s/delay/sleep/. mdelay is busy-loop.

> +
> +		/* TWL6030_BASEADD_PM_SLAVE_MISC */
> +		twl_i2c_write_u8(21, 0xFF, 0x5E);
> +		twl_i2c_write_u8(21, 0x13, 0x5F);
> +
> +		mdelay(50);
> +
> +		twl_i2c_write_u8(15, 0x40, 0x1);
> +
> +		twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &gpoctl,
> +				TWL6040_REG_GPOCTL);
> +		twl_i2c_write_u8(TWL_MODULE_AUDIO_VOICE, gpoctl | TWL6040_GPO3,
> +				TWL6040_REG_GPOCTL);
> +
> +		mdelay(10);
> +
> +		gpio_set_value(OMAP4430SDP_GPIO_CAM_PDN_C, 1);
> +		mdelay(10);
> +		clk_enable(sdp4430_cam_aux_clk); /* Enable XCLK */
> +		mdelay(10);
> +
> +		iss->platform_cb.csiphy_config(&iss->csiphy1, &dphy, &lanes);
> +	} else {
> +		clk_disable(sdp4430_cam_aux_clk);
> +		mdelay(1);
> +		gpio_set_value(OMAP4430SDP_GPIO_CAM_PDN_C, 0);
> +	}
> +
> +	return ret;
> +}
> +
> +#define OV5640_I2C_ADDRESS   (0x3C)
> +#define OV5650_I2C_ADDRESS   (0x36)
> +
> +#ifdef CONFIG_MACH_OMAP_4430SDP_CAM_OV5650
> +static struct ov5650_platform_data ov_platform_data = {
> +#elif defined(CONFIG_MACH_OMAP_4430SDP_CAM_OV5640)
> +static struct ov5640_platform_data ov_platform_data = {
> +#endif
> +      .s_power = sdp4430_ov5640_power,
> +};
> +
> +static struct i2c_board_info ov_camera_i2c_device = {
> +#ifdef CONFIG_MACH_OMAP_4430SDP_CAM_OV5650
> +	I2C_BOARD_INFO("ov5650", OV5650_I2C_ADDRESS),
> +#elif defined(CONFIG_MACH_OMAP_4430SDP_CAM_OV5640)
> +	I2C_BOARD_INFO("ov5640", OV5640_I2C_ADDRESS),
> +#endif
> +	.platform_data = &ov_platform_data,
> +};
> +
> +static struct iss_subdev_i2c_board_info ov_camera_subdevs[] = {
> +	{
> +		.board_info = &ov_camera_i2c_device,
> +		.i2c_adapter_id = 3,
> +	},
> +	{ NULL, 0, },
> +};
> +
> +static struct iss_v4l2_subdevs_group sdp4430_camera_subdevs[] = {
> +	{
> +		.subdevs = ov_camera_subdevs,
> +		.interface = ISS_INTERFACE_CSI2A_PHY1,
> +	},
> +	{ },
> +};
> +
> +static void sdp4430_omap4iss_set_constraints(struct iss_device *iss, bool enable)
> +{
> +	if (!iss)
> +		return;
> +
> +	/* FIXME: Look for something more precise as a good throughtput limit */
> +	omap_pm_set_min_bus_tput(iss->dev, OCP_INITIATOR_AGENT,
> +				 enable ? 800000 : -1);

Are there still no generic APIs to do this? :I

> +}
> +
> +static struct iss_platform_data sdp4430_iss_platform_data = {
> +	.subdevs = sdp4430_camera_subdevs,
> +	.set_constraints = sdp4430_omap4iss_set_constraints,
> +};
> +
> +static struct omap_device_pad omap4iss_pads[] = {
> +	{
> +		.name   = "csi21_dx0.csi21_dx0",
> +		.enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
> +	},
> +	{
> +		.name   = "csi21_dy0.csi21_dy0",
> +		.enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
> +	},
> +	{
> +		.name   = "csi21_dx1.csi21_dx1",
> +		.enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
> +	},
> +	{
> +		.name   = "csi21_dy1.csi21_dy1",
> +		.enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
> +	},
> +	{
> +		.name   = "csi21_dx2.csi21_dx2",
> +		.enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
> +	},
> +	{
> +		.name   = "csi21_dy2.csi21_dy2",
> +		.enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
> +	},
> +};
> +
> +static struct omap_board_data omap4iss_data = {
> +	.id	    		= 1,
> +	.pads	 		= omap4iss_pads,
> +	.pads_cnt       	= ARRAY_SIZE(omap4iss_pads),
> +};
> +
> +static int __init sdp4430_camera_init(void)
> +{
> +	if (!machine_is_omap_4430sdp())
> +		return 0;
> +
> +	sdp4430_cam_aux_clk = clk_get(NULL, "auxclk3_ck");
> +	if (IS_ERR(sdp4430_cam_aux_clk)) {
> +		printk(KERN_ERR "Unable to get auxclk3_ck\n");
> +		return -ENODEV;
> +	}
> +
> +	if (clk_set_rate(sdp4430_cam_aux_clk,
> +			clk_round_rate(sdp4430_cam_aux_clk, 24000000)))
> +		return -EINVAL;
> +
> +	/*
> +	 * CSI2 1(A):
> +	 *   LANEENABLE[4:0] = 00111(0x7) - Lanes 0, 1 & 2 enabled
> +	 *   CTRLCLKEN = 1 - Active high enable for CTRLCLK
> +	 *   CAMMODE = 0 - DPHY mode
> +	 */
> +	omap4_ctrl_pad_writel((omap4_ctrl_pad_readl(
> +				OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_CAMERA_RX) &
> +			  ~(OMAP4_CAMERARX_CSI21_LANEENABLE_MASK |
> +			    OMAP4_CAMERARX_CSI21_CAMMODE_MASK)) |
> +			 (0x7 << OMAP4_CAMERARX_CSI21_LANEENABLE_SHIFT) |
> +			 OMAP4_CAMERARX_CSI21_CTRLCLKEN_MASK,
> +			 OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_CAMERA_RX);
> +
> +	omap_mux_init_gpio(OMAP4430SDP_GPIO_CAM_PDN_C, OMAP_PIN_OUTPUT);
> +
> +	/* Init FREF_CLK3_OUT */
> +	omap_mux_init_signal("fref_clk3_out", OMAP_PIN_OUTPUT);
> +
> +	if (gpio_request(OMAP4430SDP_GPIO_CAM_PDN_C, "CAM_PDN_C"))
> +		printk(KERN_WARNING "Cannot request GPIO %d\n",
> +			OMAP4430SDP_GPIO_CAM_PDN_C);
> +	else
> +		gpio_direction_output(OMAP4430SDP_GPIO_CAM_PDN_C, 0);
> +
> +	omap4_init_camera(&sdp4430_iss_platform_data, &omap4iss_data);
> +	return 0;
> +}
> +late_initcall(sdp4430_camera_init);
> -- 
> 1.7.7.4
> 

-- 
Sakari Ailus
e-mail: sakari.ailus@iki.fi	jabber/XMPP/Gmail: sailus@retiisi.org.uk

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

* Re: [PATCH v2 06/11] v4l: Add support for omap4iss driver
  2011-12-01  0:14   ` Sergio Aguirre
  (?)
@ 2011-12-11  9:11   ` Sakari Ailus
  2012-01-23  9:10     ` Aguirre, Sergio
  -1 siblings, 1 reply; 65+ messages in thread
From: Sakari Ailus @ 2011-12-11  9:11 UTC (permalink / raw)
  To: Sergio Aguirre; +Cc: linux-media, linux-omap, laurent.pinchart

Hi sergio,

On Wed, Nov 30, 2011 at 06:14:55PM -0600, Sergio Aguirre wrote:
> This adds a very simplistic driver to utilize the
> CSI2A interface inside the ISS subsystem in OMAP4,
> and dump the data to memory.
> 
> Tested so far on omap4430sdp w/ ES2.1 GP & w/ ES2.2 EMU.
> 
> Check newly added Documentation/video4linux/omap4_camera.txt
> for details.
> 
> Signed-off-by: Sergio Aguirre <saaguirre@ti.com>
> ---
>  Documentation/video4linux/omap4_camera.txt  |   60 ++
>  arch/arm/plat-omap/include/plat/omap4-iss.h |   42 +

Perhaps this could be moved to include/media/omap4iss.h?

>  drivers/media/video/Kconfig                 |   13 +
>  drivers/media/video/Makefile                |    1 +
>  drivers/media/video/omap4iss/Makefile       |    6 +
>  drivers/media/video/omap4iss/iss.c          | 1179 ++++++++++++++++++++++++
>  drivers/media/video/omap4iss/iss.h          |  133 +++
>  drivers/media/video/omap4iss/iss_csi2.c     | 1324 +++++++++++++++++++++++++++
>  drivers/media/video/omap4iss/iss_csi2.h     |  166 ++++
>  drivers/media/video/omap4iss/iss_csiphy.c   |  215 +++++
>  drivers/media/video/omap4iss/iss_csiphy.h   |   69 ++
>  drivers/media/video/omap4iss/iss_regs.h     |  238 +++++
>  drivers/media/video/omap4iss/iss_video.c    | 1192 ++++++++++++++++++++++++
>  drivers/media/video/omap4iss/iss_video.h    |  205 +++++
>  14 files changed, 4843 insertions(+), 0 deletions(-)
>  create mode 100644 Documentation/video4linux/omap4_camera.txt
>  create mode 100644 arch/arm/plat-omap/include/plat/omap4-iss.h
>  create mode 100644 drivers/media/video/omap4iss/Makefile
>  create mode 100644 drivers/media/video/omap4iss/iss.c
>  create mode 100644 drivers/media/video/omap4iss/iss.h
>  create mode 100644 drivers/media/video/omap4iss/iss_csi2.c
>  create mode 100644 drivers/media/video/omap4iss/iss_csi2.h
>  create mode 100644 drivers/media/video/omap4iss/iss_csiphy.c
>  create mode 100644 drivers/media/video/omap4iss/iss_csiphy.h
>  create mode 100644 drivers/media/video/omap4iss/iss_regs.h
>  create mode 100644 drivers/media/video/omap4iss/iss_video.c
>  create mode 100644 drivers/media/video/omap4iss/iss_video.h
> 
> diff --git a/Documentation/video4linux/omap4_camera.txt b/Documentation/video4linux/omap4_camera.txt
> new file mode 100644
> index 0000000..a60c80f
> --- /dev/null
> +++ b/Documentation/video4linux/omap4_camera.txt
> @@ -0,0 +1,60 @@
> +                              OMAP4 ISS Driver
> +                              ================
> +
> +Introduction
> +------------
> +
> +The OMAP44XX family of chips contains the Imaging SubSystem (a.k.a. ISS),
> +Which contains several components that can be categorized in 3 big groups:
> +
> +- Interfaces (2 Interfaces: CSI2-A & CSI2-B/CCP2)
> +- ISP (Image Signal Processor)
> +- SIMCOP (Still Image Coprocessor)
> +
> +For more information, please look in [1] for latest version of:
> +	"OMAP4430 Multimedia Device Silicon Revision 2.x"
> +
> +As of Revision L, the ISS is described in detail in section 8.
> +
> +This driver is supporting _only_ the CSI2-A interface for now.
> +
> +It makes use of the Media Controller framework [2], and inherited most of the
> +code from OMAP3 ISP driver (found under drivers/media/video/omap3isp/*), except
> +that it doesn't need an IOMMU now for ISS buffers memory mapping.
> +
> +Supports usage of MMAP buffers only (for now).
> +
> +IMPORTANT: It is recommended to have this patchset:
> +  Contiguous Memory Allocator (v15) [3].
> +
> +Tested platforms
> +----------------
> +
> +- OMAP4430SDP, w/ ES2.1 GP & SEVM4430-CAM-V1-0 (Contains IMX060 & OV5640, in
> +  which only the last one is supported, outputting YUV422 frames).
> +
> +- PandaBoard, Rev. A2, w/ OMAP4430 ES2.1 GP & OV adapter board, tested with
> +  following sensors:
> +  * OV5640
> +  * OV5650
> +
> +- Tested on mainline kernel:
> +
> +	http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=summary
> +
> +  Tag: v3.0 (commit 02f8c6aee8df3cdc935e9bdd4f2d020306035dbe)
> +
> +File list
> +---------
> +drivers/media/video/omap4iss/
> +
> +References
> +----------
> +
> +[1] http://focus.ti.com/general/docs/wtbu/wtbudocumentcenter.tsp?navigationId=12037&templateId=6123#62
> +[2] http://lwn.net/Articles/420485/
> +[3] http://lwn.net/Articles/455801/
> +--
> +Author: Sergio Aguirre <saaguirre@ti.com>
> +Copyright (C) 2011, Texas Instruments
> +
> diff --git a/arch/arm/plat-omap/include/plat/omap4-iss.h b/arch/arm/plat-omap/include/plat/omap4-iss.h
> new file mode 100644
> index 0000000..3a1c6b6
> --- /dev/null
> +++ b/arch/arm/plat-omap/include/plat/omap4-iss.h
> @@ -0,0 +1,42 @@
> +#ifndef ARCH_ARM_PLAT_OMAP4_ISS_H
> +#define ARCH_ARM_PLAT_OMAP4_ISS_H
> +
> +#include <linux/i2c.h>
> +
> +struct iss_device;
> +
> +enum iss_interface_type {
> +	ISS_INTERFACE_CSI2A_PHY1,
> +};
> +
> +/**
> + * struct iss_csi2_platform_data - CSI2 interface platform data
> + * @crc: Enable the cyclic redundancy check
> + * @vpclk_div: Video port output clock control
> + */
> +struct iss_csi2_platform_data {
> +	unsigned crc:1;
> +	unsigned vpclk_div:2;
> +};
> +
> +struct iss_subdev_i2c_board_info {
> +	struct i2c_board_info *board_info;
> +	int i2c_adapter_id;
> +};
> +
> +struct iss_v4l2_subdevs_group {
> +	struct iss_subdev_i2c_board_info *subdevs;
> +	enum iss_interface_type interface;
> +	union {
> +		struct iss_csi2_platform_data csi2;
> +	} bus; /* gcc < 4.6.0 chokes on anonymous union initializers */
> +};
> +
> +struct iss_platform_data {
> +	struct iss_v4l2_subdevs_group *subdevs;
> +	void (*set_constraints)(struct iss_device *iss, bool enable);
> +};

If you could describe constraints in the data rather than having the
callback, the driver could use the data and moving to DT would be easier.

> +extern int omap4_init_camera(struct iss_platform_data *pdata,
> +			     struct omap_board_data *bdata);
> +#endif
> diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
> index b303a3f..ae2a99d 100644
> --- a/drivers/media/video/Kconfig
> +++ b/drivers/media/video/Kconfig
> @@ -796,6 +796,19 @@ config VIDEO_OMAP3_DEBUG
>  	---help---
>  	  Enable debug messages on OMAP 3 camera controller driver.
>  
> +config VIDEO_OMAP4
> +	tristate "OMAP 4 Camera support (EXPERIMENTAL)"
> +	depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP4 && EXPERIMENTAL
> +	select VIDEOBUF2_DMA_CONTIG
> +	---help---
> +	  Driver for an OMAP 4 ISS controller.
> +
> +config VIDEO_OMAP4_DEBUG
> +	bool "OMAP 4 Camera debug messages"
> +	depends on VIDEO_OMAP4
> +	---help---
> +	  Enable debug messages on OMAP 4 ISS controller driver.
> +
>  config SOC_CAMERA
>  	tristate "SoC camera support"
>  	depends on VIDEO_V4L2 && HAS_DMA && I2C
> diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
> index 117f9c4..f02a4c4 100644
> --- a/drivers/media/video/Makefile
> +++ b/drivers/media/video/Makefile
> @@ -140,6 +140,7 @@ obj-$(CONFIG_VIDEO_MMP_CAMERA) += marvell-ccic/
>  obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o
>  
>  obj-$(CONFIG_VIDEO_OMAP3)	+= omap3isp/
> +obj-$(CONFIG_VIDEO_OMAP4)	+= omap4iss/
>  
>  obj-$(CONFIG_USB_ZR364XX)       += zr364xx.o
>  obj-$(CONFIG_USB_STKWEBCAM)     += stkwebcam.o
> diff --git a/drivers/media/video/omap4iss/Makefile b/drivers/media/video/omap4iss/Makefile
> new file mode 100644
> index 0000000..1d3b0a7
> --- /dev/null
> +++ b/drivers/media/video/omap4iss/Makefile
> @@ -0,0 +1,6 @@
> +# Makefile for OMAP4 ISS driver
> +
> +omap4-iss-objs += \
> +	iss.o iss_csi2.o iss_csiphy.o iss_video.o
> +
> +obj-$(CONFIG_VIDEO_OMAP4) += omap4-iss.o
> diff --git a/drivers/media/video/omap4iss/iss.c b/drivers/media/video/omap4iss/iss.c
> new file mode 100644
> index 0000000..255738b
> --- /dev/null
> +++ b/drivers/media/video/omap4iss/iss.c
> @@ -0,0 +1,1179 @@
> +/*
> + * V4L2 Driver for OMAP4 ISS
> + *
> + * Copyright (C) 2011, Texas Instruments
> + *
> + * Author: Sergio Aguirre <saaguirre@ti.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/i2c.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/sched.h>
> +#include <linux/vmalloc.h>
> +
> +#include <media/v4l2-common.h>
> +#include <media/v4l2-device.h>
> +
> +#include "iss.h"
> +#include "iss_regs.h"
> +
> +static void iss_save_ctx(struct iss_device *iss);
> +
> +static void iss_restore_ctx(struct iss_device *iss);
> +
> +/* Structure for saving/restoring ISS module registers */
> +static struct iss_reg iss_reg_list[] = {
> +	{OMAP4_ISS_MEM_TOP, ISS_HL_SYSCONFIG, 0},
> +	{OMAP4_ISS_MEM_TOP, ISS_CTRL, 0},
> +	{OMAP4_ISS_MEM_TOP, ISS_CLKCTRL, 0},
> +	{0, ISS_TOK_TERM, 0}
> +};
> +
> +/*
> + * omap4iss_flush - Post pending L3 bus writes by doing a register readback
> + * @iss: OMAP4 ISS device
> + *
> + * In order to force posting of pending writes, we need to write and
> + * readback the same register, in this case the revision register.
> + *
> + * See this link for reference:
> + *   http://www.mail-archive.com/linux-omap@vger.kernel.org/msg08149.html
> + */
> +void omap4iss_flush(struct iss_device *iss)
> +{
> +	writel(0, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_REVISION);
> +	readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_REVISION);
> +}
> +
> +/*
> + * iss_enable_interrupts - Enable ISS interrupts.
> + * @iss: OMAP4 ISS device
> + */
> +static void iss_enable_interrupts(struct iss_device *iss)
> +{
> +	static const u32 irq = ISS_HL_IRQ_CSIA;
> +
> +	/* Enable HL interrupts */
> +	writel(irq, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQSTATUS_5);
> +	writel(irq, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQENABLE_5_SET);
> +}
> +
> +/*
> + * iss_disable_interrupts - Disable ISS interrupts.
> + * @iss: OMAP4 ISS device
> + */
> +static void iss_disable_interrupts(struct iss_device *iss)
> +{
> +	writel(-1, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQENABLE_5_CLR);
> +}
> +
> +static inline void iss_isr_dbg(struct iss_device *iss, u32 irqstatus)
> +{
> +	static const char *name[] = {
> +		"ISP_IRQ0",
> +		"ISP_IRQ1",
> +		"ISP_IRQ2",
> +		"ISP_IRQ3",
> +		"CSIA_IRQ",
> +		"CSIB_IRQ",
> +		"CCP2_IRQ0",
> +		"CCP2_IRQ1",
> +		"CCP2_IRQ2",
> +		"CCP2_IRQ3",
> +		"CBUFF_IRQ",
> +		"BTE_IRQ",
> +		"SIMCOP_IRQ0",
> +		"SIMCOP_IRQ1",
> +		"SIMCOP_IRQ2",
> +		"SIMCOP_IRQ3",
> +		"CCP2_IRQ8",
> +		"HS_VS_IRQ",
> +		"res18",
> +		"res19",
> +		"res20",
> +		"res21",
> +		"res22",
> +		"res23",
> +		"res24",
> +		"res25",
> +		"res26",
> +		"res27",
> +		"res28",
> +		"res29",
> +		"res30",
> +		"res31",
> +	};
> +	int i;
> +
> +	dev_dbg(iss->dev, "ISS IRQ: ");
> +
> +	for (i = 0; i < ARRAY_SIZE(name); i++) {
> +		if ((1 << i) & irqstatus)
> +			printk(KERN_CONT "%s ", name[i]);
> +	}
> +	printk(KERN_CONT "\n");

pr_cont might be prettier, but I guess it's mostly the matter of personal
preference.

> +}
> +
> +/*
> + * iss_isr - Interrupt Service Routine for ISS module.
> + * @irq: Not used currently.
> + * @_iss: Pointer to the OMAP4 ISS device
> + *
> + * Handles the corresponding callback if plugged in.
> + *
> + * Returns IRQ_HANDLED when IRQ was correctly handled, or IRQ_NONE when the
> + * IRQ wasn't handled.
> + */
> +static irqreturn_t iss_isr(int irq, void *_iss)
> +{
> +	struct iss_device *iss = _iss;
> +	u32 irqstatus;
> +
> +	irqstatus = readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQSTATUS_5);
> +	writel(irqstatus, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQSTATUS_5);
> +
> +	if (irqstatus & ISS_HL_IRQ_CSIA)
> +		omap4iss_csi2_isr(&iss->csi2a);
> +
> +	omap4iss_flush(iss);
> +
> +#if defined(DEBUG) && defined(ISS_ISR_DEBUG)
> +	iss_isr_dbg(iss, irqstatus);
> +#endif
> +
> +	return IRQ_HANDLED;
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * Pipeline power management
> + *
> + * Entities must be powered up when part of a pipeline that contains at least
> + * one open video device node.
> + *
> + * To achieve this use the entity use_count field to track the number of users.
> + * For entities corresponding to video device nodes the use_count field stores
> + * the users count of the node. For entities corresponding to subdevs the
> + * use_count field stores the total number of users of all video device nodes
> + * in the pipeline.
> + *
> + * The omap4iss_pipeline_pm_use() function must be called in the open() and
> + * close() handlers of video device nodes. It increments or decrements the use
> + * count of all subdev entities in the pipeline.
> + *
> + * To react to link management on powered pipelines, the link setup notification
> + * callback updates the use count of all entities in the source and sink sides
> + * of the link.
> + */
> +
> +/*
> + * iss_pipeline_pm_use_count - Count the number of users of a pipeline
> + * @entity: The entity
> + *
> + * Return the total number of users of all video device nodes in the pipeline.
> + */
> +static int iss_pipeline_pm_use_count(struct media_entity *entity)
> +{
> +	struct media_entity_graph graph;
> +	int use = 0;
> +
> +	media_entity_graph_walk_start(&graph, entity);
> +
> +	while ((entity = media_entity_graph_walk_next(&graph))) {
> +		if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE)
> +			use += entity->use_count;
> +	}
> +
> +	return use;
> +}
> +
> +/*
> + * iss_pipeline_pm_power_one - Apply power change to an entity
> + * @entity: The entity
> + * @change: Use count change
> + *
> + * Change the entity use count by @change. If the entity is a subdev update its
> + * power state by calling the core::s_power operation when the use count goes
> + * from 0 to != 0 or from != 0 to 0.
> + *
> + * Return 0 on success or a negative error code on failure.
> + */
> +static int iss_pipeline_pm_power_one(struct media_entity *entity, int change)
> +{
> +	struct v4l2_subdev *subdev;
> +
> +	subdev = media_entity_type(entity) == MEDIA_ENT_T_V4L2_SUBDEV
> +	       ? media_entity_to_v4l2_subdev(entity) : NULL;
> +
> +	if (entity->use_count == 0 && change > 0 && subdev != NULL) {
> +		int ret;
> +
> +		ret = v4l2_subdev_call(subdev, core, s_power, 1);
> +		if (ret < 0 && ret != -ENOIOCTLCMD)
> +			return ret;
> +	}
> +
> +	entity->use_count += change;
> +	WARN_ON(entity->use_count < 0);
> +
> +	if (entity->use_count == 0 && change < 0 && subdev != NULL)
> +		v4l2_subdev_call(subdev, core, s_power, 0);
> +
> +	return 0;
> +}
> +
> +/*
> + * iss_pipeline_pm_power - Apply power change to all entities in a pipeline
> + * @entity: The entity
> + * @change: Use count change
> + *
> + * Walk the pipeline to update the use count and the power state of all non-node
> + * entities.
> + *
> + * Return 0 on success or a negative error code on failure.
> + */
> +static int iss_pipeline_pm_power(struct media_entity *entity, int change)
> +{
> +	struct media_entity_graph graph;
> +	struct media_entity *first = entity;
> +	int ret = 0;
> +
> +	if (!change)
> +		return 0;
> +
> +	media_entity_graph_walk_start(&graph, entity);
> +
> +	while (!ret && (entity = media_entity_graph_walk_next(&graph)))
> +		if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE)
> +			ret = iss_pipeline_pm_power_one(entity, change);
> +
> +	if (!ret)
> +		return 0;
> +
> +	media_entity_graph_walk_start(&graph, first);
> +
> +	while ((first = media_entity_graph_walk_next(&graph))
> +	       && first != entity)
> +		if (media_entity_type(first) != MEDIA_ENT_T_DEVNODE)
> +			iss_pipeline_pm_power_one(first, -change);
> +
> +	return ret;
> +}
> +
> +/*
> + * omap4iss_pipeline_pm_use - Update the use count of an entity
> + * @entity: The entity
> + * @use: Use (1) or stop using (0) the entity
> + *
> + * Update the use count of all entities in the pipeline and power entities on or
> + * off accordingly.
> + *
> + * Return 0 on success or a negative error code on failure. Powering entities
> + * off is assumed to never fail. No failure can occur when the use parameter is
> + * set to 0.
> + */
> +int omap4iss_pipeline_pm_use(struct media_entity *entity, int use)
> +{
> +	int change = use ? 1 : -1;
> +	int ret;
> +
> +	mutex_lock(&entity->parent->graph_mutex);
> +
> +	/* Apply use count to node. */
> +	entity->use_count += change;
> +	WARN_ON(entity->use_count < 0);
> +
> +	/* Apply power change to connected non-nodes. */
> +	ret = iss_pipeline_pm_power(entity, change);
> +	if (ret < 0)
> +		entity->use_count -= change;
> +
> +	mutex_unlock(&entity->parent->graph_mutex);
> +
> +	return ret;
> +}
> +
> +/*
> + * iss_pipeline_link_notify - Link management notification callback
> + * @source: Pad at the start of the link
> + * @sink: Pad at the end of the link
> + * @flags: New link flags that will be applied
> + *
> + * React to link management on powered pipelines by updating the use count of
> + * all entities in the source and sink sides of the link. Entities are powered
> + * on or off accordingly.
> + *
> + * Return 0 on success or a negative error code on failure. Powering entities
> + * off is assumed to never fail. This function will not fail for disconnection
> + * events.
> + */
> +static int iss_pipeline_link_notify(struct media_pad *source,
> +				    struct media_pad *sink, u32 flags)
> +{
> +	int source_use = iss_pipeline_pm_use_count(source->entity);
> +	int sink_use = iss_pipeline_pm_use_count(sink->entity);
> +	int ret;
> +
> +	if (!(flags & MEDIA_LNK_FL_ENABLED)) {
> +		/* Powering off entities is assumed to never fail. */
> +		iss_pipeline_pm_power(source->entity, -sink_use);
> +		iss_pipeline_pm_power(sink->entity, -source_use);
> +		return 0;
> +	}
> +
> +	ret = iss_pipeline_pm_power(source->entity, sink_use);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = iss_pipeline_pm_power(sink->entity, source_use);
> +	if (ret < 0)
> +		iss_pipeline_pm_power(source->entity, -sink_use);
> +
> +	return ret;
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * Pipeline stream management
> + */
> +
> +/*
> + * iss_pipeline_enable - Enable streaming on a pipeline
> + * @pipe: ISS pipeline
> + * @mode: Stream mode (single shot or continuous)
> + *
> + * Walk the entities chain starting at the pipeline output video node and start
> + * all modules in the chain in the given mode.
> + *
> + * Return 0 if successful, or the return value of the failed video::s_stream
> + * operation otherwise.
> + */
> +static int iss_pipeline_enable(struct iss_pipeline *pipe,
> +			       enum iss_pipeline_stream_state mode)
> +{
> +	struct media_entity *entity;
> +	struct media_pad *pad;
> +	struct v4l2_subdev *subdev;
> +	unsigned long flags;
> +	int ret = 0;
> +
> +	spin_lock_irqsave(&pipe->lock, flags);
> +	pipe->state &= ~(ISS_PIPELINE_IDLE_INPUT | ISS_PIPELINE_IDLE_OUTPUT);
> +	spin_unlock_irqrestore(&pipe->lock, flags);
> +
> +	pipe->do_propagation = false;
> +
> +	entity = &pipe->output->video.entity;
> +	while (1) {
> +		pad = &entity->pads[0];
> +		if (!(pad->flags & MEDIA_PAD_FL_SINK))
> +			break;
> +
> +		pad = media_entity_remote_source(pad);
> +		if (pad == NULL ||
> +		    media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
> +			break;
> +
> +		entity = pad->entity;
> +		subdev = media_entity_to_v4l2_subdev(entity);
> +
> +		ret = v4l2_subdev_call(subdev, video, s_stream, mode);
> +		if (ret < 0 && ret != -ENOIOCTLCMD)
> +			break;
> +	}
> +
> +	return ret;
> +}
> +
> +/*
> + * iss_pipeline_disable - Disable streaming on a pipeline
> + * @pipe: ISS pipeline
> + *
> + * Walk the entities chain starting at the pipeline output video node and stop
> + * all modules in the chain. Wait synchronously for the modules to be stopped if
> + * necessary.
> + */
> +static int iss_pipeline_disable(struct iss_pipeline *pipe)
> +{
> +	struct media_entity *entity;
> +	struct media_pad *pad;
> +	struct v4l2_subdev *subdev;
> +	int failure = 0;
> +
> +	entity = &pipe->output->video.entity;
> +	while (1) {
> +		pad = &entity->pads[0];
> +		if (!(pad->flags & MEDIA_PAD_FL_SINK))
> +			break;
> +
> +		pad = media_entity_remote_source(pad);
> +		if (pad == NULL ||
> +		    media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
> +			break;
> +
> +		entity = pad->entity;
> +		subdev = media_entity_to_v4l2_subdev(entity);
> +
> +		v4l2_subdev_call(subdev, video, s_stream, 0);
> +	}
> +
> +	return failure;
> +}
> +
> +/*
> + * omap4iss_pipeline_set_stream - Enable/disable streaming on a pipeline
> + * @pipe: ISS pipeline
> + * @state: Stream state (stopped, single shot or continuous)
> + *
> + * Set the pipeline to the given stream state. Pipelines can be started in
> + * single-shot or continuous mode.
> + *
> + * Return 0 if successful, or the return value of the failed video::s_stream
> + * operation otherwise. The pipeline state is not updated when the operation
> + * fails, except when stopping the pipeline.
> + */
> +int omap4iss_pipeline_set_stream(struct iss_pipeline *pipe,
> +				 enum iss_pipeline_stream_state state)
> +{
> +	int ret;
> +
> +	if (state == ISS_PIPELINE_STREAM_STOPPED)
> +		ret = iss_pipeline_disable(pipe);
> +	else
> +		ret = iss_pipeline_enable(pipe, state);
> +
> +	if (ret == 0 || state == ISS_PIPELINE_STREAM_STOPPED)
> +		pipe->stream_state = state;
> +
> +	return ret;
> +}
> +
> +/*
> + * iss_pipeline_is_last - Verify if entity has an enabled link to the output
> + *			  video node
> + * @me: ISS module's media entity
> + *
> + * Returns 1 if the entity has an enabled link to the output video node or 0
> + * otherwise. It's true only while pipeline can have no more than one output
> + * node.
> + */
> +static int iss_pipeline_is_last(struct media_entity *me)
> +{
> +	struct iss_pipeline *pipe;
> +	struct media_pad *pad;
> +
> +	if (!me->pipe)
> +		return 0;
> +	pipe = to_iss_pipeline(me);
> +	if (pipe->stream_state == ISS_PIPELINE_STREAM_STOPPED)
> +		return 0;
> +	pad = media_entity_remote_source(&pipe->output->pad);
> +	return pad->entity == me;
> +}
> +
> +static int iss_reset(struct iss_device *iss)
> +{
> +	unsigned long timeout = 0;
> +
> +	writel(readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_SYSCONFIG) |
> +		ISS_HL_SYSCONFIG_SOFTRESET,
> +		iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_SYSCONFIG);
> +
> +	while (readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_SYSCONFIG) &
> +			ISS_HL_SYSCONFIG_SOFTRESET) {
> +		if (timeout++ > 10000) {
> +			dev_alert(iss->dev, "cannot reset ISS\n");
> +			return -ETIMEDOUT;
> +		}
> +		udelay(1);
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * iss_save_context - Saves the values of the ISS module registers.
> + * @iss: OMAP4 ISS device
> + * @reg_list: Structure containing pairs of register address and value to
> + *            modify on OMAP.
> + */
> +static void
> +iss_save_context(struct iss_device *iss, struct iss_reg *reg_list)
> +{
> +	struct iss_reg *next = reg_list;
> +
> +	for (; next->reg != ISS_TOK_TERM; next++)
> +		next->val = readl(iss->regs[next->mmio_range] + next->reg);
> +}
> +
> +/*
> + * iss_restore_context - Restores the values of the ISS module registers.
> + * @iss: OMAP4 ISS device
> + * @reg_list: Structure containing pairs of register address and value to
> + *            modify on OMAP.
> + */
> +static void
> +iss_restore_context(struct iss_device *iss, struct iss_reg *reg_list)
> +{
> +	struct iss_reg *next = reg_list;
> +
> +	for (; next->reg != ISS_TOK_TERM; next++)
> +		writel(next->val, iss->regs[next->mmio_range] + next->reg);
> +}
> +
> +/*
> + * iss_save_ctx - Saves ISS context.
> + * @iss: OMAP4 ISS device
> + *
> + * Routine for saving the context of each module in the ISS.
> + */
> +static void iss_save_ctx(struct iss_device *iss)
> +{
> +	iss_save_context(iss, iss_reg_list);
> +}
> +
> +/*
> + * iss_restore_ctx - Restores ISS context.
> + * @iss: OMAP4 ISS device
> + *
> + * Routine for restoring the context of each module in the ISS.
> + */
> +static void iss_restore_ctx(struct iss_device *iss)
> +{
> +	iss_restore_context(iss, iss_reg_list);
> +}
> +
> +/*
> + * iss_module_sync_idle - Helper to sync module with its idle state
> + * @me: ISS submodule's media entity
> + * @wait: ISS submodule's wait queue for streamoff/interrupt synchronization
> + * @stopping: flag which tells module wants to stop
> + *
> + * This function checks if ISS submodule needs to wait for next interrupt. If
> + * yes, makes the caller to sleep while waiting for such event.
> + */
> +int omap4iss_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait,
> +			      atomic_t *stopping)
> +{
> +	struct iss_pipeline *pipe = to_iss_pipeline(me);
> +
> +	if (pipe->stream_state == ISS_PIPELINE_STREAM_STOPPED ||
> +	    (pipe->stream_state == ISS_PIPELINE_STREAM_SINGLESHOT &&
> +	     !iss_pipeline_ready(pipe)))
> +		return 0;
> +
> +	/*
> +	 * atomic_set() doesn't include memory barrier on ARM platform for SMP
> +	 * scenario. We'll call it here to avoid race conditions.
> +	 */
> +	atomic_set(stopping, 1);
> +	smp_mb();

It was new to me atomic_t requires memory barriers. But shouldn't this be
wmb() instead?

> +
> +	/*
> +	 * If module is the last one, it's writing to memory. In this case,
> +	 * it's necessary to check if the module is already paused due to
> +	 * DMA queue underrun or if it has to wait for next interrupt to be
> +	 * idle.
> +	 * If it isn't the last one, the function won't sleep but *stopping
> +	 * will still be set to warn next submodule caller's interrupt the
> +	 * module wants to be idle.
> +	 */
> +	if (iss_pipeline_is_last(me)) {

You could return here if the function returns zero.

> +		struct iss_video *video = pipe->output;
> +		unsigned long flags;
> +
> +		spin_lock_irqsave(&video->qlock, flags);
> +		if (video->dmaqueue_flags & ISS_VIDEO_DMAQUEUE_UNDERRUN) {
> +			spin_unlock_irqrestore(&video->qlock, flags);
> +			atomic_set(stopping, 0);
> +			smp_mb();
> +			return 0;
> +		}
> +		spin_unlock_irqrestore(&video->qlock, flags);
> +		if (!wait_event_timeout(*wait, !atomic_read(stopping),
> +					msecs_to_jiffies(1000))) {
> +			atomic_set(stopping, 0);
> +			smp_mb();
> +			return -ETIMEDOUT;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * omap4iss_module_sync_is_stopped - Helper to verify if module was stopping
> + * @wait: ISS submodule's wait queue for streamoff/interrupt synchronization
> + * @stopping: flag which tells module wants to stop
> + *
> + * This function checks if ISS submodule was stopping. In case of yes, it
> + * notices the caller by setting stopping to 0 and waking up the wait queue.
> + * Returns 1 if it was stopping or 0 otherwise.
> + */
> +int omap4iss_module_sync_is_stopping(wait_queue_head_t *wait,
> +				     atomic_t *stopping)
> +{
> +	if (atomic_cmpxchg(stopping, 1, 0)) {
> +		wake_up(wait);
> +		return 1;
> +	}
> +
> +	return 0;
> +}
> +
> +/* --------------------------------------------------------------------------
> + * Clock management
> + */
> +
> +#define ISS_CLKCTRL_MASK	(ISS_CLKCTRL_CSI2_A)
> +
> +static int __iss_subclk_update(struct iss_device *iss)
> +{
> +	u32 clk = 0;
> +	int ret = 0, timeout = 1000;
> +
> +	if (iss->subclk_resources & OMAP4_ISS_SUBCLK_CSI2_A)
> +		clk |= ISS_CLKCTRL_CSI2_A;
> +
> +	writel((readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_CLKCTRL) &
> +		~ISS_CLKCTRL_MASK) | clk,
> +		iss->regs[OMAP4_ISS_MEM_TOP] + ISS_CLKCTRL);
> +
> +	/* Wait for HW assertion */
> +	while (timeout-- > 0) {
> +		udelay(1);
> +		if ((readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_CLKSTAT) &
> +		     ISS_CLKCTRL_MASK) == clk)
> +			break;
> +	}
> +
> +	if (!timeout)
> +		ret = -EBUSY;
> +
> +	return ret;
> +}
> +
> +int omap4iss_subclk_enable(struct iss_device *iss,
> +			    enum iss_subclk_resource res)
> +{
> +	iss->subclk_resources |= res;
> +
> +	return __iss_subclk_update(iss);
> +}
> +
> +int omap4iss_subclk_disable(struct iss_device *iss,
> +			     enum iss_subclk_resource res)
> +{
> +	iss->subclk_resources &= ~res;
> +
> +	return __iss_subclk_update(iss);
> +}
> +
> +/*
> + * iss_enable_clocks - Enable ISS clocks
> + * @iss: OMAP4 ISS device
> + *
> + * Return 0 if successful, or clk_enable return value if any of tthem fails.
> + */
> +static int iss_enable_clocks(struct iss_device *iss)
> +{
> +	int r;
> +
> +	r = clk_enable(iss->iss_fck);
> +	if (r) {
> +		dev_err(iss->dev, "clk_enable iss_fck failed\n");
> +		goto out_clk_enable_fck;
> +	}
> +
> +	r = clk_enable(iss->iss_ctrlclk);
> +	if (r) {
> +		dev_err(iss->dev, "clk_enable iss_ctrlclk failed\n");
> +		goto out_clk_enable_ctrlclk;
> +	}
> +	return 0;
> +
> +out_clk_enable_ctrlclk:
> +	clk_disable(iss->iss_fck);
> +out_clk_enable_fck:
> +	return r;
> +}
> +
> +/*
> + * iss_disable_clocks - Disable ISS clocks
> + * @iss: OMAP4 ISS device
> + */
> +static void iss_disable_clocks(struct iss_device *iss)
> +{
> +	clk_disable(iss->iss_ctrlclk);
> +	clk_disable(iss->iss_fck);
> +}
> +
> +static void iss_put_clocks(struct iss_device *iss)
> +{
> +	if (iss->iss_fck) {
> +		clk_put(iss->iss_fck);
> +		iss->iss_fck = NULL;
> +	}
> +
> +	if (iss->iss_ctrlclk) {
> +		clk_put(iss->iss_ctrlclk);
> +		iss->iss_ctrlclk = NULL;
> +	}
> +}
> +
> +static int iss_get_clocks(struct iss_device *iss)
> +{
> +	iss->iss_fck = clk_get(iss->dev, "iss_fck");
> +	if (IS_ERR(iss->iss_fck)) {
> +		dev_err(iss->dev, "Unable to get iss_fck clock info\n");
> +		iss_put_clocks(iss);
> +		return PTR_ERR(iss->iss_fck);
> +	}
> +
> +	iss->iss_ctrlclk = clk_get(iss->dev, "iss_ctrlclk");
> +	if (IS_ERR(iss->iss_ctrlclk)) {
> +		dev_err(iss->dev, "Unable to get iss_ctrlclk clock info\n");
> +		iss_put_clocks(iss);
> +		return PTR_ERR(iss->iss_fck);
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * omap4iss_get - Acquire the ISS resource.
> + *
> + * Initializes the clocks for the first acquire.
> + *
> + * Increment the reference count on the ISS. If the first reference is taken,
> + * enable clocks and power-up all submodules.
> + *
> + * Return a pointer to the ISS device structure, or NULL if an error occurred.
> + */
> +struct iss_device *omap4iss_get(struct iss_device *iss)
> +{
> +	struct iss_device *__iss = iss;
> +
> +	if (iss == NULL)
> +		return NULL;
> +
> +	mutex_lock(&iss->iss_mutex);
> +	if (iss->ref_count > 0)
> +		goto out;
> +
> +	if (iss_enable_clocks(iss) < 0) {
> +		__iss = NULL;
> +		goto out;
> +	}
> +
> +	/* We don't want to restore context before saving it! */
> +	if (iss->has_context)
> +		iss_restore_ctx(iss);
> +	else
> +		iss->has_context = 1;
> +
> +	iss_enable_interrupts(iss);
> +
> +out:
> +	if (__iss != NULL)
> +		iss->ref_count++;
> +	mutex_unlock(&iss->iss_mutex);
> +
> +	return __iss;
> +}
> +
> +/*
> + * omap4iss_put - Release the ISS
> + *
> + * Decrement the reference count on the ISS. If the last reference is released,
> + * power-down all submodules, disable clocks and free temporary buffers.
> + */
> +void omap4iss_put(struct iss_device *iss)
> +{
> +	if (iss == NULL)
> +		return;
> +
> +	mutex_lock(&iss->iss_mutex);
> +	BUG_ON(iss->ref_count == 0);
> +	if (--iss->ref_count == 0) {
> +		iss_disable_interrupts(iss);
> +		iss_save_ctx(iss);
> +		iss_disable_clocks(iss);
> +	}
> +	mutex_unlock(&iss->iss_mutex);
> +}
> +
> +static int iss_map_mem_resource(struct platform_device *pdev,
> +				struct iss_device *iss,
> +				enum iss_mem_resources res)
> +{
> +	struct resource *mem;
> +
> +	/* request the mem region for the camera registers */
> +
> +	mem = platform_get_resource(pdev, IORESOURCE_MEM, res);
> +	if (!mem) {
> +		dev_err(iss->dev, "no mem resource?\n");
> +		return -ENODEV;
> +	}
> +
> +	if (!request_mem_region(mem->start, resource_size(mem), pdev->name)) {
> +		dev_err(iss->dev,
> +			"cannot reserve camera register I/O region\n");
> +		return -ENODEV;
> +	}
> +	iss->res[res] = mem;
> +
> +	/* map the region */
> +	iss->regs[res] = ioremap_nocache(mem->start, resource_size(mem));
> +	if (!iss->regs[res]) {
> +		dev_err(iss->dev, "cannot map camera register I/O region\n");
> +		return -ENODEV;
> +	}
> +
> +	return 0;
> +}
> +
> +static void iss_unregister_entities(struct iss_device *iss)
> +{
> +	omap4iss_csi2_unregister_entities(&iss->csi2a);
> +
> +	v4l2_device_unregister(&iss->v4l2_dev);
> +	media_device_unregister(&iss->media_dev);
> +}
> +
> +/*
> + * iss_register_subdev_group - Register a group of subdevices
> + * @iss: OMAP4 ISS device
> + * @board_info: I2C subdevs board information array
> + *
> + * Register all I2C subdevices in the board_info array. The array must be
> + * terminated by a NULL entry, and the first entry must be the sensor.
> + *
> + * Return a pointer to the sensor media entity if it has been successfully
> + * registered, or NULL otherwise.
> + */
> +static struct v4l2_subdev *
> +iss_register_subdev_group(struct iss_device *iss,
> +		     struct iss_subdev_i2c_board_info *board_info)
> +{
> +	struct v4l2_subdev *sensor = NULL;
> +	unsigned int first;
> +
> +	if (board_info->board_info == NULL)
> +		return NULL;
> +
> +	for (first = 1; board_info->board_info; ++board_info, first = 0) {
> +		struct v4l2_subdev *subdev;
> +		struct i2c_adapter *adapter;
> +
> +		adapter = i2c_get_adapter(board_info->i2c_adapter_id);
> +		if (adapter == NULL) {
> +			printk(KERN_ERR "%s: Unable to get I2C adapter %d for "
> +				"device %s\n", __func__,
> +				board_info->i2c_adapter_id,
> +				board_info->board_info->type);
> +			continue;
> +		}
> +
> +		subdev = v4l2_i2c_new_subdev_board(&iss->v4l2_dev, adapter,
> +				board_info->board_info, NULL);
> +		if (subdev == NULL) {
> +			printk(KERN_ERR "%s: Unable to register subdev %s\n",
> +				__func__, board_info->board_info->type);
> +			continue;
> +		}
> +
> +		if (first)
> +			sensor = subdev;
> +	}
> +
> +	return sensor;
> +}
> +
> +static int iss_register_entities(struct iss_device *iss)
> +{
> +	struct iss_platform_data *pdata = iss->pdata;
> +	struct iss_v4l2_subdevs_group *subdevs;
> +	int ret;
> +
> +	iss->media_dev.dev = iss->dev;
> +	strlcpy(iss->media_dev.model, "TI OMAP4 ISS",
> +		sizeof(iss->media_dev.model));
> +	iss->media_dev.link_notify = iss_pipeline_link_notify;
> +	ret = media_device_register(&iss->media_dev);
> +	if (ret < 0) {
> +		printk(KERN_ERR "%s: Media device registration failed (%d)\n",
> +			__func__, ret);
> +		return ret;
> +	}
> +
> +	iss->v4l2_dev.mdev = &iss->media_dev;
> +	ret = v4l2_device_register(iss->dev, &iss->v4l2_dev);
> +	if (ret < 0) {
> +		printk(KERN_ERR "%s: V4L2 device registration failed (%d)\n",
> +			__func__, ret);
> +		goto done;
> +	}
> +
> +	/* Register internal entities */
> +	ret = omap4iss_csi2_register_entities(&iss->csi2a, &iss->v4l2_dev);
> +	if (ret < 0)
> +		goto done;
> +
> +	/* Register external entities */
> +	for (subdevs = pdata->subdevs; subdevs && subdevs->subdevs; ++subdevs) {
> +		struct v4l2_subdev *sensor;
> +		struct media_entity *input;
> +		unsigned int flags;
> +		unsigned int pad;
> +
> +		sensor = iss_register_subdev_group(iss, subdevs->subdevs);
> +		if (sensor == NULL)
> +			continue;
> +
> +		sensor->host_priv = subdevs;
> +
> +		/* Connect the sensor to the correct interface module.
> +		 * CSI2a receiver through CSIPHY1.
> +		 */
> +		switch (subdevs->interface) {
> +		case ISS_INTERFACE_CSI2A_PHY1:
> +			input = &iss->csi2a.subdev.entity;
> +			pad = CSI2_PAD_SINK;
> +			flags = MEDIA_LNK_FL_IMMUTABLE
> +			      | MEDIA_LNK_FL_ENABLED;
> +			break;
> +
> +		default:
> +			printk(KERN_ERR "%s: invalid interface type %u\n",
> +			       __func__, subdevs->interface);
> +			ret = -EINVAL;
> +			goto done;
> +		}
> +
> +		ret = media_entity_create_link(&sensor->entity, 0, input, pad,
> +					       flags);
> +		if (ret < 0)
> +			goto done;
> +	}
> +
> +	ret = v4l2_device_register_subdev_nodes(&iss->v4l2_dev);
> +
> +done:
> +	if (ret < 0)
> +		iss_unregister_entities(iss);
> +
> +	return ret;
> +}
> +
> +static void iss_cleanup_modules(struct iss_device *iss)
> +{
> +	omap4iss_csi2_cleanup(iss);
> +}
> +
> +static int iss_initialize_modules(struct iss_device *iss)
> +{
> +	int ret;
> +
> +	ret = omap4iss_csiphy_init(iss);
> +	if (ret < 0) {
> +		dev_err(iss->dev, "CSI PHY initialization failed\n");
> +		goto error_csiphy;
> +	}
> +
> +	ret = omap4iss_csi2_init(iss);
> +	if (ret < 0) {
> +		dev_err(iss->dev, "CSI2 initialization failed\n");
> +		goto error_csi2;
> +	}
> +
> +	return 0;
> +
> +error_csi2:
> +error_csiphy:
> +	return ret;
> +}
> +
> +static int iss_probe(struct platform_device *pdev)
> +{
> +	struct iss_platform_data *pdata = pdev->dev.platform_data;
> +	struct iss_device *iss;
> +	int i, ret;
> +
> +	if (pdata == NULL)
> +		return -EINVAL;
> +
> +	iss = kzalloc(sizeof(*iss), GFP_KERNEL);
> +	if (!iss) {
> +		dev_err(&pdev->dev, "Could not allocate memory\n");
> +		return -ENOMEM;
> +	}
> +
> +	mutex_init(&iss->iss_mutex);
> +
> +	iss->dev = &pdev->dev;
> +	iss->pdata = pdata;
> +	iss->ref_count = 0;
> +
> +	iss->raw_dmamask = DMA_BIT_MASK(32);
> +	iss->dev->dma_mask = &iss->raw_dmamask;
> +	iss->dev->coherent_dma_mask = DMA_BIT_MASK(32);
> +
> +	platform_set_drvdata(pdev, iss);
> +
> +	/* Clocks */
> +	ret = iss_map_mem_resource(pdev, iss, OMAP4_ISS_MEM_TOP);
> +	if (ret < 0)
> +		goto error;
> +
> +	ret = iss_get_clocks(iss);
> +	if (ret < 0)
> +		goto error;
> +
> +	if (omap4iss_get(iss) == NULL)
> +		goto error;
> +
> +	ret = iss_reset(iss);
> +	if (ret < 0)
> +		goto error_iss;
> +
> +	iss->revision = readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_REVISION);
> +	dev_info(iss->dev, "Revision %08x found\n", iss->revision);
> +
> +	for (i = 1; i < OMAP4_ISS_MEM_LAST; i++) {
> +		ret = iss_map_mem_resource(pdev, iss, i);
> +		if (ret)
> +			goto error_iss;
> +	}
> +
> +	/* Interrupt */
> +	iss->irq_num = platform_get_irq(pdev, 0);
> +	if (iss->irq_num <= 0) {
> +		dev_err(iss->dev, "No IRQ resource\n");
> +		ret = -ENODEV;
> +		goto error_iss;
> +	}
> +
> +	if (request_irq(iss->irq_num, iss_isr, IRQF_SHARED, "OMAP4 ISS", iss)) {
> +		dev_err(iss->dev, "Unable to request IRQ\n");
> +		ret = -EINVAL;
> +		goto error_iss;
> +	}
> +
> +	/* Entities */
> +	ret = iss_initialize_modules(iss);
> +	if (ret < 0)
> +		goto error_irq;
> +
> +	ret = iss_register_entities(iss);
> +	if (ret < 0)
> +		goto error_modules;
> +
> +	omap4iss_put(iss);
> +
> +	return 0;
> +
> +error_modules:
> +	iss_cleanup_modules(iss);
> +error_irq:
> +	free_irq(iss->irq_num, iss);
> +error_iss:
> +	omap4iss_put(iss);
> +error:
> +	iss_put_clocks(iss);
> +
> +	for (i = 0; i < OMAP4_ISS_MEM_LAST; i++) {
> +		if (iss->regs[i]) {
> +			iounmap(iss->regs[i]);
> +			iss->regs[i] = NULL;
> +		}
> +
> +		if (iss->res[i]) {
> +			release_mem_region(iss->res[i]->start,
> +					   resource_size(iss->res[i]));
> +			iss->res[i] = NULL;
> +		}
> +	}
> +	platform_set_drvdata(pdev, NULL);
> +	kfree(iss);
> +
> +	return ret;
> +}
> +
> +static int iss_remove(struct platform_device *pdev)
> +{
> +	struct iss_device *iss = platform_get_drvdata(pdev);
> +	int i;
> +
> +	iss_unregister_entities(iss);
> +	iss_cleanup_modules(iss);
> +
> +	free_irq(iss->irq_num, iss);
> +	iss_put_clocks(iss);
> +
> +	for (i = 0; i < OMAP4_ISS_MEM_LAST; i++) {
> +		if (iss->regs[i]) {
> +			iounmap(iss->regs[i]);
> +			iss->regs[i] = NULL;
> +		}
> +
> +		if (iss->res[i]) {
> +			release_mem_region(iss->res[i]->start,
> +					   resource_size(iss->res[i]));
> +			iss->res[i] = NULL;
> +		}
> +	}
> +
> +	kfree(iss);
> +
> +	return 0;
> +}
> +
> +static struct platform_device_id omap4iss_id_table[] = {
> +	{ "omap4iss", 0 },
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(platform, omap4iss_id_table);
> +
> +static struct platform_driver iss_driver = {
> +	.probe		= iss_probe,
> +	.remove		= iss_remove,
> +	.id_table	= omap4iss_id_table,
> +	.driver = {
> +		.owner	= THIS_MODULE,
> +		.name	= "omap4iss",
> +	},
> +};
> +
> +static int __init iss_init(void)
> +{
> +	return platform_driver_register(&iss_driver);
> +}
> +
> +static void __exit iss_exit(void)
> +{
> +	platform_driver_unregister(&iss_driver);
> +}
> +
> +/*
> + * FIXME: Had to make it late_initcall. Strangely while being module_init,
> + * The I2C communication was failing in the sensor, because no XCLK was
> + * provided.
> + */
> +late_initcall(iss_init);
> +module_exit(iss_exit);
> +
> +MODULE_DESCRIPTION("TI OMAP4 ISS driver");
> +MODULE_AUTHOR("Sergio Aguirre <saaguirre@ti.com>");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/media/video/omap4iss/iss.h b/drivers/media/video/omap4iss/iss.h
> new file mode 100644
> index 0000000..8346c80
> --- /dev/null
> +++ b/drivers/media/video/omap4iss/iss.h
> @@ -0,0 +1,133 @@
> +/*
> + * iss.h
> + *
> + * Copyright (C) 2011 Texas Instruments.
> + *
> + * Author: Sergio Aguirre <saaguirre@ti.com>
> + */
> +
> +#ifndef _OMAP4_ISS_H_
> +#define _OMAP4_ISS_H_
> +
> +#include <media/v4l2-device.h>
> +#include <linux/device.h>
> +#include <linux/io.h>
> +#include <linux/platform_device.h>
> +#include <linux/wait.h>
> +
> +#include <plat/omap4-iss.h>
> +
> +#include "iss_regs.h"
> +#include "iss_csiphy.h"
> +#include "iss_csi2.h"
> +
> +#define ISS_TOK_TERM		0xFFFFFFFF	/*
> +						 * terminating token for ISS
> +						 * modules reg list
> +						 */
> +#define to_iss_device(ptr_module)				\
> +	container_of(ptr_module, struct iss_device, ptr_module)
> +#define to_device(ptr_module)						\
> +	(to_iss_device(ptr_module)->dev)
> +
> +enum iss_mem_resources {
> +	OMAP4_ISS_MEM_TOP,
> +	OMAP4_ISS_MEM_CSI2_A_REGS1,
> +	OMAP4_ISS_MEM_CAMERARX_CORE1,
> +	OMAP4_ISS_MEM_LAST,
> +};
> +
> +enum iss_subclk_resource {
> +	OMAP4_ISS_SUBCLK_SIMCOP		= (1 << 0),
> +	OMAP4_ISS_SUBCLK_ISP		= (1 << 1),
> +	OMAP4_ISS_SUBCLK_CSI2_A		= (1 << 2),
> +	OMAP4_ISS_SUBCLK_CSI2_B		= (1 << 3),
> +	OMAP4_ISS_SUBCLK_CCP2		= (1 << 4),
> +};
> +
> +/*
> + * struct iss_reg - Structure for ISS register values.
> + * @reg: 32-bit Register address.
> + * @val: 32-bit Register value.
> + */
> +struct iss_reg {
> +	enum iss_mem_resources mmio_range;
> +	u32 reg;
> +	u32 val;
> +};
> +
> +struct iss_platform_callback {
> +	int (*csiphy_config)(struct iss_csiphy *phy,
> +			     struct iss_csiphy_dphy_cfg *dphy,
> +			     struct iss_csiphy_lanes_cfg *lanes);
> +};
> +
> +struct iss_device {
> +	struct v4l2_device v4l2_dev;
> +	struct media_device media_dev;
> +	struct device *dev;
> +	u32 revision;
> +
> +	/* platform HW resources */
> +	struct iss_platform_data *pdata;
> +	unsigned int irq_num;
> +
> +	struct resource *res[OMAP4_ISS_MEM_LAST];
> +	void __iomem *regs[OMAP4_ISS_MEM_LAST];
> +
> +	u64 raw_dmamask;
> +
> +	struct mutex iss_mutex;	/* For handling ref_count field */
> +	int has_context;
> +	int ref_count;
> +
> +	struct clk *iss_fck;
> +	struct clk *iss_ctrlclk;
> +
> +	/* ISS modules */
> +	struct iss_csi2_device csi2a;
> +	struct iss_csiphy csiphy1;
> +
> +	unsigned int subclk_resources;
> +
> +	struct iss_platform_callback platform_cb;
> +};
> +
> +#define v4l2_dev_to_iss_device(dev) \
> +	container_of(dev, struct iss_device, v4l2_dev)
> +
> +int omap4iss_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait,
> +			      atomic_t *stopping);
> +
> +int omap4iss_module_sync_is_stopping(wait_queue_head_t *wait,
> +				     atomic_t *stopping);
> +
> +int omap4iss_pipeline_set_stream(struct iss_pipeline *pipe,
> +				 enum iss_pipeline_stream_state state);
> +
> +struct iss_device *omap4iss_get(struct iss_device *iss);
> +void omap4iss_put(struct iss_device *iss);
> +int omap4iss_subclk_enable(struct iss_device *iss,
> +			   enum iss_subclk_resource res);
> +int omap4iss_subclk_disable(struct iss_device *iss,
> +			    enum iss_subclk_resource res);
> +
> +int omap4iss_pipeline_pm_use(struct media_entity *entity, int use);
> +
> +int omap4iss_register_entities(struct platform_device *pdev,
> +			       struct v4l2_device *v4l2_dev);
> +void omap4iss_unregister_entities(struct platform_device *pdev);
> +
> +static inline enum v4l2_buf_type
> +iss_pad_buffer_type(const struct v4l2_subdev *subdev, int pad)
> +{
> +	if (pad >= subdev->entity.num_pads)
> +		return 0;
> +
> +	if (subdev->entity.pads[pad].flags & MEDIA_PAD_FL_SINK)
> +		return V4L2_BUF_TYPE_VIDEO_OUTPUT;
> +	else
> +		return V4L2_BUF_TYPE_VIDEO_CAPTURE;
> +}
> +
> +#endif /* _OMAP4_ISS_H_ */
> diff --git a/drivers/media/video/omap4iss/iss_csi2.c b/drivers/media/video/omap4iss/iss_csi2.c
> new file mode 100644
> index 0000000..916d5ef
> --- /dev/null
> +++ b/drivers/media/video/omap4iss/iss_csi2.c
> @@ -0,0 +1,1324 @@
> +/*
> + * iss_csi2.c
> + *
> + * TI OMAP4 ISS - CSI PHY module
> + *
> + * Copyright (C) 2011 Texas Instruments, Inc.
> + *
> + * Contacts: Sergio Aguirre <saaguirre@ti.com>
> + *
> + * 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.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
> + * 02110-1301 USA
> + */
> +#include <linux/delay.h>
> +#include <media/v4l2-common.h>
> +#include <linux/v4l2-mediabus.h>
> +#include <linux/mm.h>
> +
> +#include "iss.h"
> +#include "iss_regs.h"
> +#include "iss_csi2.h"
> +
> +/*
> + * csi2_if_enable - Enable CSI2 Receiver interface.
> + * @enable: enable flag
> + *
> + */
> +static void csi2_if_enable(struct iss_csi2_device *csi2, u8 enable)
> +{
> +	struct iss_csi2_ctrl_cfg *currctrl = &csi2->ctrl;
> +
> +	writel((readl(csi2->regs1 + CSI2_CTRL) & ~CSI2_CTRL_IF_EN) |
> +		(enable ? CSI2_CTRL_IF_EN : 0),
> +		csi2->regs1 + CSI2_CTRL);
> +
> +	currctrl->if_enable = enable;
> +}
> +
> +/*
> + * csi2_recv_config - CSI2 receiver module configuration.
> + * @currctrl: iss_csi2_ctrl_cfg structure
> + *
> + */
> +static void csi2_recv_config(struct iss_csi2_device *csi2,
> +			     struct iss_csi2_ctrl_cfg *currctrl)
> +{
> +	u32 reg;
> +
> +	reg = readl(csi2->regs1 + CSI2_CTRL);
> +
> +	if (currctrl->frame_mode)
> +		reg |= CSI2_CTRL_FRAME;
> +	else
> +		reg &= ~CSI2_CTRL_FRAME;
> +
> +	if (currctrl->vp_clk_enable)
> +		reg |= CSI2_CTRL_VP_CLK_EN;
> +	else
> +		reg &= ~CSI2_CTRL_VP_CLK_EN;
> +
> +	if (currctrl->vp_only_enable)
> +		reg |= CSI2_CTRL_VP_ONLY_EN;
> +	else
> +		reg &= ~CSI2_CTRL_VP_ONLY_EN;
> +
> +	reg &= ~CSI2_CTRL_VP_OUT_CTRL_MASK;
> +	reg |= currctrl->vp_out_ctrl << CSI2_CTRL_VP_OUT_CTRL_SHIFT;
> +
> +	if (currctrl->ecc_enable)
> +		reg |= CSI2_CTRL_ECC_EN;
> +	else
> +		reg &= ~CSI2_CTRL_ECC_EN;
> +
> +	/*
> +	 * Set MFlag assertion boundaries to:
> +	 * Low: 4/8 of FIFO size
> +	 * High: 6/8 of FIFO size
> +	 */
> +	reg &= ~(CSI2_CTRL_MFLAG_LEVH_MASK | CSI2_CTRL_MFLAG_LEVL_MASK);
> +	reg |= (2 << CSI2_CTRL_MFLAG_LEVH_SHIFT) |
> +	       (4 << CSI2_CTRL_MFLAG_LEVL_SHIFT);
> +
> +	/* Generation of 16x64-bit bursts (Recommended) */
> +	reg |= CSI2_CTRL_BURST_SIZE_EXPAND;
> +
> +	/* Do Non-Posted writes (Recommended) */
> +	reg |= CSI2_CTRL_NON_POSTED_WRITE;
> +
> +	/*
> +	 * Enforce Little endian for all formats, including:
> +	 * YUV4:2:2 8-bit and YUV4:2:0 Legacy
> +	 */
> +	reg |= CSI2_CTRL_ENDIANNESS;
> +
> +	writel(reg, csi2->regs1 + CSI2_CTRL);
> +}
> +
> +static const unsigned int csi2_input_fmts[] = {
> +	V4L2_MBUS_FMT_SGRBG10_1X10,
> +	V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8,
> +	V4L2_MBUS_FMT_SRGGB10_1X10,
> +	V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8,
> +	V4L2_MBUS_FMT_SBGGR10_1X10,
> +	V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8,
> +	V4L2_MBUS_FMT_SGBRG10_1X10,
> +	V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8,
> +	V4L2_MBUS_FMT_UYVY8_1X16,
> +	V4L2_MBUS_FMT_YUYV8_1X16,
> +};
> +
> +/* To set the format on the CSI2 requires a mapping function that takes
> + * the following inputs:
> + * - 3 different formats (at this time)
> + * - 2 destinations (mem, vp+mem) (vp only handled separately)
> + * - 2 decompression options (on, off)
> + * Output should be CSI2 frame format code
> + * Array indices as follows: [format][dest][decompr]
> + * Not all combinations are valid. 0 means invalid.
> + */
> +static const u16 __csi2_fmt_map[][2][2] = {
> +	/* RAW10 formats */
> +	{
> +		/* Output to memory */
> +		{
> +			/* No DPCM decompression */
> +			CSI2_PIX_FMT_RAW10_EXP16,
> +			/* DPCM decompression */
> +			0,
> +		},
> +		/* Output to both */
> +		{
> +			/* No DPCM decompression */
> +			CSI2_PIX_FMT_RAW10_EXP16_VP,
> +			/* DPCM decompression */
> +			0,
> +		},
> +	},
> +	/* RAW10 DPCM8 formats */
> +	{
> +		/* Output to memory */
> +		{
> +			/* No DPCM decompression */
> +			CSI2_USERDEF_8BIT_DATA1,
> +			/* DPCM decompression */
> +			CSI2_USERDEF_8BIT_DATA1_DPCM10,
> +		},
> +		/* Output to both */
> +		{
> +			/* No DPCM decompression */
> +			CSI2_PIX_FMT_RAW8_VP,
> +			/* DPCM decompression */
> +			CSI2_USERDEF_8BIT_DATA1_DPCM10_VP,
> +		},
> +	},
> +	/* YUV422 formats */
> +	{
> +		/* Output to memory */
> +		{
> +			/* No DPCM decompression */
> +			CSI2_PIX_FMT_YUV422_8BIT,
> +			/* DPCM decompression */
> +			0,
> +		},
> +		/* Output to both */
> +		{
> +			/* No DPCM decompression */
> +			CSI2_PIX_FMT_YUV422_8BIT_VP,
> +			/* DPCM decompression */
> +			0,
> +		},
> +	},
> +};
> +
> +/*
> + * csi2_ctx_map_format - Map CSI2 sink media bus format to CSI2 format ID
> + * @csi2: ISS CSI2 device
> + *
> + * Returns CSI2 physical format id
> + */
> +static u16 csi2_ctx_map_format(struct iss_csi2_device *csi2)
> +{
> +	const struct v4l2_mbus_framefmt *fmt = &csi2->formats[CSI2_PAD_SINK];
> +	int fmtidx, destidx;
> +
> +	switch (fmt->code) {
> +	case V4L2_MBUS_FMT_SGRBG10_1X10:
> +	case V4L2_MBUS_FMT_SRGGB10_1X10:
> +	case V4L2_MBUS_FMT_SBGGR10_1X10:
> +	case V4L2_MBUS_FMT_SGBRG10_1X10:
> +		fmtidx = 0;
> +		break;
> +	case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8:
> +	case V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8:
> +	case V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8:
> +	case V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8:
> +		fmtidx = 1;
> +		break;
> +	case V4L2_MBUS_FMT_UYVY8_1X16:
> +	case V4L2_MBUS_FMT_YUYV8_1X16:
> +		fmtidx = 2;
> +		break;
> +	default:
> +		WARN(1, KERN_ERR "CSI2: pixel format %08x unsupported!\n",
> +		     fmt->code);
> +		return 0;
> +	}
> +
> +	if (!(csi2->output & CSI2_OUTPUT_IPIPEIF) &&
> +	    !(csi2->output & CSI2_OUTPUT_MEMORY)) {
> +		/* Neither output enabled is a valid combination */
> +		return CSI2_PIX_FMT_OTHERS;
> +	}
> +
> +	/* If we need to skip frames at the beginning of the stream disable the
> +	 * video port to avoid sending the skipped frames to the IPIPEIF.
> +	 */
> +	destidx = csi2->frame_skip ? 0 : !!(csi2->output & CSI2_OUTPUT_IPIPEIF);
> +
> +	return __csi2_fmt_map[fmtidx][destidx][csi2->dpcm_decompress];
> +}
> +
> +/*
> + * csi2_set_outaddr - Set memory address to save output image
> + * @csi2: Pointer to ISS CSI2a device.
> + * @addr: 32-bit memory address aligned on 32 byte boundary.
> + *
> + * Sets the memory address where the output will be saved.
> + *
> + * Returns 0 if successful, or -EINVAL if the address is not in the 32 byte
> + * boundary.
> + */
> +static void csi2_set_outaddr(struct iss_csi2_device *csi2, u32 addr)
> +{
> +	struct iss_csi2_ctx_cfg *ctx = &csi2->contexts[0];
> +
> +	ctx->ping_addr = addr;
> +	ctx->pong_addr = addr;
> +	writel(ctx->ping_addr,
> +	       csi2->regs1 + CSI2_CTX_PING_ADDR(ctx->ctxnum));
> +	writel(ctx->pong_addr,
> +	       csi2->regs1 + CSI2_CTX_PONG_ADDR(ctx->ctxnum));
> +}
> +
> +/*
> + * is_usr_def_mapping - Checks whether USER_DEF_MAPPING should
> + *			be enabled by CSI2.
> + * @format_id: mapped format id
> + *
> + */
> +static inline int is_usr_def_mapping(u32 format_id)
> +{
> +	return (format_id & 0x40) ? 1 : 0;
> +}
> +
> +/*
> + * csi2_ctx_enable - Enable specified CSI2 context
> + * @ctxnum: Context number, valid between 0 and 7 values.
> + * @enable: enable
> + *
> + */
> +static void csi2_ctx_enable(struct iss_csi2_device *csi2, u8 ctxnum, u8 enable)
> +{
> +	struct iss_csi2_ctx_cfg *ctx = &csi2->contexts[ctxnum];
> +	u32 reg;
> +
> +	reg = readl(csi2->regs1 + CSI2_CTX_CTRL1(ctxnum));
> +
> +	if (enable) {
> +		unsigned int skip = 0;
> +
> +		if (csi2->frame_skip)
> +			skip = csi2->frame_skip;
> +		else if (csi2->output & CSI2_OUTPUT_MEMORY)
> +			skip = 1;
> +
> +		reg &= ~CSI2_CTX_CTRL1_COUNT_MASK;
> +		reg |= CSI2_CTX_CTRL1_COUNT_UNLOCK
> +		    |  (skip << CSI2_CTX_CTRL1_COUNT_SHIFT)
> +		    |  CSI2_CTX_CTRL1_CTX_EN;
> +	} else {
> +		reg &= ~CSI2_CTX_CTRL1_CTX_EN;
> +	}
> +
> +	writel(reg, csi2->regs1 + CSI2_CTX_CTRL1(ctxnum));
> +	ctx->enabled = enable;
> +}
> +
> +/*
> + * csi2_ctx_config - CSI2 context configuration.
> + * @ctx: context configuration
> + *
> + */
> +static void csi2_ctx_config(struct iss_csi2_device *csi2,
> +			    struct iss_csi2_ctx_cfg *ctx)
> +{
> +	u32 reg;
> +
> +	/* Set up CSI2_CTx_CTRL1 */
> +	reg = readl(csi2->regs1 + CSI2_CTX_CTRL1(ctx->ctxnum));
> +
> +	if (ctx->eof_enabled)
> +		reg |= CSI2_CTX_CTRL1_EOF_EN;
> +	else
> +		reg &= ~CSI2_CTX_CTRL1_EOF_EN;
> +
> +	if (ctx->eol_enabled)
> +		reg |= CSI2_CTX_CTRL1_EOL_EN;
> +	else
> +		reg &= ~CSI2_CTX_CTRL1_EOL_EN;
> +
> +	if (ctx->checksum_enabled)
> +		reg |= CSI2_CTX_CTRL1_CS_EN;
> +	else
> +		reg &= ~CSI2_CTX_CTRL1_CS_EN;
> +
> +	writel(reg, csi2->regs1 + CSI2_CTX_CTRL1(ctx->ctxnum));
> +
> +	/* Set up CSI2_CTx_CTRL2 */
> +	reg = readl(csi2->regs1 + CSI2_CTX_CTRL2(ctx->ctxnum));
> +
> +	reg &= ~(CSI2_CTX_CTRL2_VIRTUAL_ID_MASK);
> +	reg |= ctx->virtual_id << CSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT;
> +
> +	reg &= ~(CSI2_CTX_CTRL2_FORMAT_MASK);
> +	reg |= ctx->format_id << CSI2_CTX_CTRL2_FORMAT_SHIFT;
> +
> +	if (ctx->dpcm_decompress) {
> +		if (ctx->dpcm_predictor)
> +			reg |= CSI2_CTX_CTRL2_DPCM_PRED;
> +		else
> +			reg &= ~CSI2_CTX_CTRL2_DPCM_PRED;
> +	}
> +
> +	if (is_usr_def_mapping(ctx->format_id)) {
> +		reg &= ~CSI2_CTX_CTRL2_USER_DEF_MAP_MASK;
> +		reg |= 2 << CSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT;
> +	}
> +
> +	writel(reg, csi2->regs1 + CSI2_CTX_CTRL2(ctx->ctxnum));
> +
> +	/* Set up CSI2_CTx_CTRL3 */
> +	reg = readl(csi2->regs1 + CSI2_CTX_CTRL3(ctx->ctxnum));
> +	reg &= ~(CSI2_CTX_CTRL3_ALPHA_MASK);
> +	reg |= (ctx->alpha << CSI2_CTX_CTRL3_ALPHA_SHIFT);
> +
> +	writel(reg, csi2->regs1 + CSI2_CTX_CTRL3(ctx->ctxnum));
> +
> +	/* Set up CSI2_CTx_DAT_OFST */
> +	reg = readl(csi2->regs1 + CSI2_CTX_DAT_OFST(ctx->ctxnum));
> +	reg &= ~CSI2_CTX_DAT_OFST_MASK;
> +	reg |= ctx->data_offset;
> +	writel(reg, csi2->regs1 + CSI2_CTX_DAT_OFST(ctx->ctxnum));
> +
> +	writel(ctx->ping_addr,
> +		       csi2->regs1 + CSI2_CTX_PING_ADDR(ctx->ctxnum));
> +
> +	writel(ctx->pong_addr,
> +		       csi2->regs1 + CSI2_CTX_PONG_ADDR(ctx->ctxnum));
> +}
> +
> +/*
> + * csi2_timing_config - CSI2 timing configuration.
> + * @timing: csi2_timing_cfg structure
> + */
> +static void csi2_timing_config(struct iss_csi2_device *csi2,
> +			       struct iss_csi2_timing_cfg *timing)
> +{
> +	u32 reg;
> +
> +	reg = readl(csi2->regs1 + CSI2_TIMING);
> +
> +	if (timing->force_rx_mode)
> +		reg |= CSI2_TIMING_FORCE_RX_MODE_IO1;
> +	else
> +		reg &= ~CSI2_TIMING_FORCE_RX_MODE_IO1;
> +
> +	if (timing->stop_state_16x)
> +		reg |= CSI2_TIMING_STOP_STATE_X16_IO1;
> +	else
> +		reg &= ~CSI2_TIMING_STOP_STATE_X16_IO1;
> +
> +	if (timing->stop_state_4x)
> +		reg |= CSI2_TIMING_STOP_STATE_X4_IO1;
> +	else
> +		reg &= ~CSI2_TIMING_STOP_STATE_X4_IO1;
> +
> +	reg &= ~CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK;
> +	reg |= timing->stop_state_counter <<
> +	       CSI2_TIMING_STOP_STATE_COUNTER_IO1_SHIFT;
> +
> +	writel(reg, csi2->regs1 + CSI2_TIMING);
> +}
> +
> +/*
> + * csi2_irq_ctx_set - Enables CSI2 Context IRQs.
> + * @enable: Enable/disable CSI2 Context interrupts
> + */
> +static void csi2_irq_ctx_set(struct iss_csi2_device *csi2, int enable)
> +{
> +	u32 reg = CSI2_CTX_IRQ_FE;
> +	int i;
> +
> +	if (csi2->use_fs_irq)
> +		reg |= CSI2_CTX_IRQ_FS;
> +
> +	for (i = 0; i < 8; i++) {
> +		writel(reg, csi2->regs1 + CSI2_CTX_IRQSTATUS(i));
> +		if (enable)
> +			writel(readl(csi2->regs1 + CSI2_CTX_IRQENABLE(i)) | reg,
> +				csi2->regs1 + CSI2_CTX_IRQENABLE(i));
> +		else
> +			writel(readl(csi2->regs1 + CSI2_CTX_IRQENABLE(i)) &
> +				~reg,
> +				csi2->regs1 + CSI2_CTX_IRQENABLE(i));
> +	}
> +}
> +
> +/*
> + * csi2_irq_complexio1_set - Enables CSI2 ComplexIO IRQs.
> + * @enable: Enable/disable CSI2 ComplexIO #1 interrupts
> + */
> +static void csi2_irq_complexio1_set(struct iss_csi2_device *csi2, int enable)
> +{
> +	u32 reg;
> +	reg = CSI2_COMPLEXIO_IRQ_STATEALLULPMEXIT |
> +		CSI2_COMPLEXIO_IRQ_STATEALLULPMENTER |
> +		CSI2_COMPLEXIO_IRQ_STATEULPM5 |
> +		CSI2_COMPLEXIO_IRQ_ERRCONTROL5 |
> +		CSI2_COMPLEXIO_IRQ_ERRESC5 |
> +		CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS5 |
> +		CSI2_COMPLEXIO_IRQ_ERRSOTHS5 |
> +		CSI2_COMPLEXIO_IRQ_STATEULPM4 |
> +		CSI2_COMPLEXIO_IRQ_ERRCONTROL4 |
> +		CSI2_COMPLEXIO_IRQ_ERRESC4 |
> +		CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS4 |
> +		CSI2_COMPLEXIO_IRQ_ERRSOTHS4 |
> +		CSI2_COMPLEXIO_IRQ_STATEULPM3 |
> +		CSI2_COMPLEXIO_IRQ_ERRCONTROL3 |
> +		CSI2_COMPLEXIO_IRQ_ERRESC3 |
> +		CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS3 |
> +		CSI2_COMPLEXIO_IRQ_ERRSOTHS3 |
> +		CSI2_COMPLEXIO_IRQ_STATEULPM2 |
> +		CSI2_COMPLEXIO_IRQ_ERRCONTROL2 |
> +		CSI2_COMPLEXIO_IRQ_ERRESC2 |
> +		CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS2 |
> +		CSI2_COMPLEXIO_IRQ_ERRSOTHS2 |
> +		CSI2_COMPLEXIO_IRQ_STATEULPM1 |
> +		CSI2_COMPLEXIO_IRQ_ERRCONTROL1 |
> +		CSI2_COMPLEXIO_IRQ_ERRESC1 |
> +		CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS1 |
> +		CSI2_COMPLEXIO_IRQ_ERRSOTHS1;
> +	writel(reg, csi2->regs1 + CSI2_COMPLEXIO_IRQSTATUS);
> +	if (enable)
> +		reg |= readl(csi2->regs1 + CSI2_COMPLEXIO_IRQENABLE);
> +	else
> +		reg = 0;
> +	writel(reg, csi2->regs1 + CSI2_COMPLEXIO_IRQENABLE);
> +}
> +
> +/*
> + * csi2_irq_status_set - Enables CSI2 Status IRQs.
> + * @enable: Enable/disable CSI2 Status interrupts
> + */
> +static void csi2_irq_status_set(struct iss_csi2_device *csi2, int enable)
> +{
> +	u32 reg;
> +	reg = CSI2_IRQ_OCP_ERR |
> +		CSI2_IRQ_SHORT_PACKET |
> +		CSI2_IRQ_ECC_CORRECTION |
> +		CSI2_IRQ_ECC_NO_CORRECTION |
> +		CSI2_IRQ_COMPLEXIO_ERR |
> +		CSI2_IRQ_FIFO_OVF |
> +		CSI2_IRQ_CONTEXT0;
> +	writel(reg, csi2->regs1 + CSI2_IRQSTATUS);
> +	if (enable)
> +		reg |= readl(csi2->regs1 + CSI2_IRQENABLE);
> +	else
> +		reg = 0;
> +
> +	writel(reg, csi2->regs1 + CSI2_IRQENABLE);
> +}
> +
> +/*
> + * omap4iss_csi2_reset - Resets the CSI2 module.
> + *
> + * Must be called with the phy lock held.
> + *
> + * Returns 0 if successful, or -EBUSY if power command didn't respond.
> + */
> +int omap4iss_csi2_reset(struct iss_csi2_device *csi2)
> +{
> +	u8 soft_reset_retries = 0;
> +	u32 reg;
> +	int i;
> +
> +	if (!csi2->available)
> +		return -ENODEV;
> +
> +	if (csi2->phy->phy_in_use)
> +		return -EBUSY;
> +
> +	writel(readl(csi2->regs1 + CSI2_SYSCONFIG) |
> +		CSI2_SYSCONFIG_SOFT_RESET,
> +		csi2->regs1 + CSI2_SYSCONFIG);
> +
> +	do {
> +		reg = readl(csi2->regs1 + CSI2_SYSSTATUS) &
> +				    CSI2_SYSSTATUS_RESET_DONE;
> +		if (reg == CSI2_SYSSTATUS_RESET_DONE)
> +			break;
> +		soft_reset_retries++;
> +		if (soft_reset_retries < 5)
> +			udelay(100);
> +	} while (soft_reset_retries < 5);
> +
> +	if (soft_reset_retries == 5) {
> +		printk(KERN_ERR "CSI2: Soft reset try count exceeded!\n");
> +		return -EBUSY;
> +	}
> +
> +	writel(readl(csi2->regs1 + CSI2_COMPLEXIO_CFG) |
> +		CSI2_COMPLEXIO_CFG_RESET_CTRL,
> +		csi2->regs1 + CSI2_COMPLEXIO_CFG);
> +
> +	i = 100;
> +	do {
> +		reg = readl(csi2->phy->phy_regs + REGISTER1)
> +		    & REGISTER1_RESET_DONE_CTRLCLK;
> +		if (reg == REGISTER1_RESET_DONE_CTRLCLK)
> +			break;
> +		udelay(100);
> +	} while (--i > 0);
> +
> +	if (i == 0) {
> +		printk(KERN_ERR
> +		       "CSI2: Reset for CSI2_96M_FCLK domain Failed!\n");
> +		return -EBUSY;
> +	}
> +
> +	writel((readl(csi2->regs1 + CSI2_SYSCONFIG) &
> +		~(CSI2_SYSCONFIG_MSTANDBY_MODE_MASK |
> +		  CSI2_SYSCONFIG_AUTO_IDLE)) |
> +		CSI2_SYSCONFIG_MSTANDBY_MODE_NO,
> +		csi2->regs1 + CSI2_SYSCONFIG);
> +
> +	return 0;
> +}
> +
> +static int csi2_configure(struct iss_csi2_device *csi2)
> +{
> +	const struct iss_v4l2_subdevs_group *pdata;
> +	struct iss_csi2_timing_cfg *timing = &csi2->timing[0];
> +	struct v4l2_subdev *sensor;
> +	struct media_pad *pad;
> +
> +	/*
> +	 * CSI2 fields that can be updated while the context has
> +	 * been enabled or the interface has been enabled are not
> +	 * updated dynamically currently. So we do not allow to
> +	 * reconfigure if either has been enabled
> +	 */
> +	if (csi2->contexts[0].enabled || csi2->ctrl.if_enable)
> +		return -EBUSY;
> +
> +	pad = media_entity_remote_source(&csi2->pads[CSI2_PAD_SINK]);
> +	sensor = media_entity_to_v4l2_subdev(pad->entity);
> +	pdata = sensor->host_priv;
> +
> +	csi2->frame_skip = 0;
> +	v4l2_subdev_call(sensor, sensor, g_skip_frames, &csi2->frame_skip);
> +
> +	csi2->ctrl.vp_out_ctrl = pdata->bus.csi2.vpclk_div;
> +	csi2->ctrl.frame_mode = ISS_CSI2_FRAME_IMMEDIATE;
> +	csi2->ctrl.ecc_enable = pdata->bus.csi2.crc;
> +
> +	timing->force_rx_mode = 1;
> +	timing->stop_state_16x = 1;
> +	timing->stop_state_4x = 1;
> +	timing->stop_state_counter = 0x1FF;
> +
> +	/*
> +	 * The CSI2 receiver can't do any format conversion except DPCM
> +	 * decompression, so every set_format call configures both pads
> +	 * and enables DPCM decompression as a special case:
> +	 */
> +	if (csi2->formats[CSI2_PAD_SINK].code !=
> +	    csi2->formats[CSI2_PAD_SOURCE].code)
> +		csi2->dpcm_decompress = true;
> +	else
> +		csi2->dpcm_decompress = false;
> +
> +	csi2->contexts[0].format_id = csi2_ctx_map_format(csi2);
> +
> +	if (csi2->video_out.bpl_padding == 0)
> +		csi2->contexts[0].data_offset = 0;
> +	else
> +		csi2->contexts[0].data_offset = csi2->video_out.bpl_value;
> +
> +	/*
> +	 * Enable end of frame and end of line signals generation for
> +	 * context 0. These signals are generated from CSI2 receiver to
> +	 * qualify the last pixel of a frame and the last pixel of a line.
> +	 * Without enabling the signals CSI2 receiver writes data to memory
> +	 * beyond buffer size and/or data line offset is not handled correctly.
> +	 */
> +	csi2->contexts[0].eof_enabled = 1;
> +	csi2->contexts[0].eol_enabled = 1;
> +
> +	csi2_irq_complexio1_set(csi2, 1);
> +	csi2_irq_ctx_set(csi2, 1);
> +	csi2_irq_status_set(csi2, 1);
> +
> +	/* Set configuration (timings, format and links) */
> +	csi2_timing_config(csi2, timing);
> +	csi2_recv_config(csi2, &csi2->ctrl);
> +	csi2_ctx_config(csi2, &csi2->contexts[0]);
> +
> +	return 0;
> +}
> +
> +/*
> + * csi2_print_status - Prints CSI2 debug information.
> + */
> +#define CSI2_PRINT_REGISTER(iss, regs, name)\
> +	dev_dbg(iss->dev, "###CSI2 " #name "=0x%08x\n", \
> +		readl(regs + CSI2_##name))
> +
> +static void csi2_print_status(struct iss_csi2_device *csi2)
> +{
> +	struct iss_device *iss = csi2->iss;
> +
> +	if (!csi2->available)
> +		return;
> +
> +	dev_dbg(iss->dev, "-------------CSI2 Register dump-------------\n");
> +
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, SYSCONFIG);
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, SYSSTATUS);
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, IRQENABLE);
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, IRQSTATUS);
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, CTRL);
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, DBG_H);
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, COMPLEXIO_CFG);
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, COMPLEXIO_IRQSTATUS);
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, SHORT_PACKET);
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, COMPLEXIO_IRQENABLE);
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, DBG_P);
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, TIMING);
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_CTRL1(0));
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_CTRL2(0));
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_DAT_OFST(0));
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_PING_ADDR(0));
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_PONG_ADDR(0));
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_IRQENABLE(0));
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_IRQSTATUS(0));
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_CTRL3(0));
> +
> +	dev_dbg(iss->dev, "--------------------------------------------\n");
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * Interrupt handling
> + */
> +
> +/*
> + * csi2_isr_buffer - Does buffer handling at end-of-frame
> + * when writing to memory.
> + */
> +static void csi2_isr_buffer(struct iss_csi2_device *csi2)
> +{
> +	struct iss_buffer *buffer;
> +
> +	csi2_ctx_enable(csi2, 0, 0);
> +
> +	buffer = omap4iss_video_buffer_next(&csi2->video_out, 0);
> +
> +	/*
> +	 * Let video queue operation restart engine if there is an underrun
> +	 * condition.
> +	 */
> +	if (buffer == NULL)
> +		return;
> +
> +	csi2_set_outaddr(csi2, buffer->iss_addr);
> +	csi2_ctx_enable(csi2, 0, 1);
> +}
> +
> +static void csi2_isr_ctx(struct iss_csi2_device *csi2,
> +			 struct iss_csi2_ctx_cfg *ctx)
> +{
> +	unsigned int n = ctx->ctxnum;
> +	u32 status;
> +
> +	status = readl(csi2->regs1 + CSI2_CTX_IRQSTATUS(n));
> +	writel(status, csi2->regs1 + CSI2_CTX_IRQSTATUS(n));
> +
> +	/* Propagate frame number */
> +	if (status & CSI2_CTX_IRQ_FS) {
> +		struct iss_pipeline *pipe =
> +				     to_iss_pipeline(&csi2->subdev.entity);
> +		if (pipe->do_propagation)
> +			atomic_inc(&pipe->frame_number);
> +	}
> +
> +	if (!(status & CSI2_CTX_IRQ_FE))
> +		return;
> +
> +	/* Skip interrupts until we reach the frame skip count. The CSI2 will be
> +	 * automatically disabled, as the frame skip count has been programmed
> +	 * in the CSI2_CTx_CTRL1::COUNT field, so reenable it.
> +	 *
> +	 * It would have been nice to rely on the FRAME_NUMBER interrupt instead
> +	 * but it turned out that the interrupt is only generated when the CSI2
> +	 * writes to memory (the CSI2_CTx_CTRL1::COUNT field is decreased
> +	 * correctly and reaches 0 when data is forwarded to the video port only
> +	 * but no interrupt arrives). Maybe a CSI2 hardware bug.
> +	 */
> +	if (csi2->frame_skip) {
> +		csi2->frame_skip--;
> +		if (csi2->frame_skip == 0) {
> +			ctx->format_id = csi2_ctx_map_format(csi2);
> +			csi2_ctx_config(csi2, ctx);
> +			csi2_ctx_enable(csi2, n, 1);
> +		}
> +		return;
> +	}
> +
> +	if (csi2->output & CSI2_OUTPUT_MEMORY)
> +		csi2_isr_buffer(csi2);
> +}
> +
> +/*
> + * omap4iss_csi2_isr - CSI2 interrupt handling.
> + *
> + * Return -EIO on Transmission error
> + */
> +int omap4iss_csi2_isr(struct iss_csi2_device *csi2)
> +{
> +	u32 csi2_irqstatus, cpxio1_irqstatus;
> +	struct iss_device *iss = csi2->iss;
> +	int retval = 0;
> +
> +	if (!csi2->available)
> +		return -ENODEV;
> +
> +	csi2_irqstatus = readl(csi2->regs1 + CSI2_IRQSTATUS);
> +	writel(csi2_irqstatus, csi2->regs1 + CSI2_IRQSTATUS);
> +
> +	/* Failure Cases */
> +	if (csi2_irqstatus & CSI2_IRQ_COMPLEXIO_ERR) {
> +		cpxio1_irqstatus = readl(csi2->regs1 +
> +					 CSI2_COMPLEXIO_IRQSTATUS);
> +		writel(cpxio1_irqstatus,
> +			csi2->regs1 + CSI2_COMPLEXIO_IRQSTATUS);
> +		dev_dbg(iss->dev, "CSI2: ComplexIO Error IRQ "
> +			"%x\n", cpxio1_irqstatus);
> +		retval = -EIO;
> +	}
> +
> +	if (csi2_irqstatus & (CSI2_IRQ_OCP_ERR |
> +			      CSI2_IRQ_SHORT_PACKET |
> +			      CSI2_IRQ_ECC_NO_CORRECTION |
> +			      CSI2_IRQ_COMPLEXIO_ERR |
> +			      CSI2_IRQ_FIFO_OVF)) {
> +		dev_dbg(iss->dev, "CSI2 Err:"
> +			" OCP:%d,"
> +			" Short_pack:%d,"
> +			" ECC:%d,"
> +			" CPXIO:%d,"
> +			" FIFO_OVF:%d,"
> +			"\n",
> +			(csi2_irqstatus &
> +			 CSI2_IRQ_OCP_ERR) ? 1 : 0,
> +			(csi2_irqstatus &
> +			 CSI2_IRQ_SHORT_PACKET) ? 1 : 0,
> +			(csi2_irqstatus &
> +			 CSI2_IRQ_ECC_NO_CORRECTION) ? 1 : 0,
> +			(csi2_irqstatus &
> +			 CSI2_IRQ_COMPLEXIO_ERR) ? 1 : 0,
> +			(csi2_irqstatus &
> +			 CSI2_IRQ_FIFO_OVF) ? 1 : 0);
> +		retval = -EIO;
> +	}
> +
> +	if (omap4iss_module_sync_is_stopping(&csi2->wait, &csi2->stopping))
> +		return 0;
> +
> +	/* Successful cases */
> +	if (csi2_irqstatus & CSI2_IRQ_CONTEXT0)
> +		csi2_isr_ctx(csi2, &csi2->contexts[0]);
> +
> +	if (csi2_irqstatus & CSI2_IRQ_ECC_CORRECTION)
> +		dev_dbg(iss->dev, "CSI2: ECC correction done\n");
> +
> +	return retval;
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * ISS video operations
> + */
> +
> +/*
> + * csi2_queue - Queues the first buffer when using memory output
> + * @video: The video node
> + * @buffer: buffer to queue
> + */
> +static int csi2_queue(struct iss_video *video, struct iss_buffer *buffer)
> +{
> +	struct iss_device *iss = video->iss;
> +	struct iss_csi2_device *csi2 = &iss->csi2a;
> +
> +	csi2_set_outaddr(csi2, buffer->iss_addr);
> +
> +	/*
> +	 * If streaming was enabled before there was a buffer queued
> +	 * or underrun happened in the ISR, the hardware was not enabled
> +	 * and DMA queue flag ISS_VIDEO_DMAQUEUE_UNDERRUN is still set.
> +	 * Enable it now.
> +	 */
> +	if (csi2->video_out.dmaqueue_flags & ISS_VIDEO_DMAQUEUE_UNDERRUN) {
> +		/* Enable / disable context 0 and IRQs */
> +		csi2_if_enable(csi2, 1);
> +		csi2_ctx_enable(csi2, 0, 1);
> +		iss_video_dmaqueue_flags_clr(&csi2->video_out);
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct iss_video_operations csi2_issvideo_ops = {
> +	.queue = csi2_queue,
> +};
> +
> +/* -----------------------------------------------------------------------------
> + * V4L2 subdev operations
> + */
> +
> +static struct v4l2_mbus_framefmt *
> +__csi2_get_format(struct iss_csi2_device *csi2, struct v4l2_subdev_fh *fh,
> +		  unsigned int pad, enum v4l2_subdev_format_whence which)
> +{
> +	if (which == V4L2_SUBDEV_FORMAT_TRY)
> +		return v4l2_subdev_get_try_format(fh, pad);
> +	else
> +		return &csi2->formats[pad];
> +}
> +
> +static void
> +csi2_try_format(struct iss_csi2_device *csi2, struct v4l2_subdev_fh *fh,
> +		unsigned int pad, struct v4l2_mbus_framefmt *fmt,
> +		enum v4l2_subdev_format_whence which)
> +{
> +	enum v4l2_mbus_pixelcode pixelcode;
> +	struct v4l2_mbus_framefmt *format;
> +	const struct iss_format_info *info;
> +	unsigned int i;
> +
> +	switch (pad) {
> +	case CSI2_PAD_SINK:
> +		/* Clamp the width and height to valid range (1-8191). */
> +		for (i = 0; i < ARRAY_SIZE(csi2_input_fmts); i++) {
> +			if (fmt->code == csi2_input_fmts[i])
> +				break;
> +		}
> +
> +		/* If not found, use SGRBG10 as default */
> +		if (i >= ARRAY_SIZE(csi2_input_fmts))
> +			fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10;
> +
> +		fmt->width = clamp_t(u32, fmt->width, 1, 8191);
> +		fmt->height = clamp_t(u32, fmt->height, 1, 8191);
> +		break;
> +
> +	case CSI2_PAD_SOURCE:
> +		/* Source format same as sink format, except for DPCM
> +		 * compression.
> +		 */
> +		pixelcode = fmt->code;
> +		format = __csi2_get_format(csi2, fh, CSI2_PAD_SINK, which);
> +		memcpy(fmt, format, sizeof(*fmt));
> +
> +		/*
> +		 * Only Allow DPCM decompression, and check that the
> +		 * pattern is preserved
> +		 */
> +		info = omap4iss_video_format_info(fmt->code);
> +		if (info->uncompressed == pixelcode)
> +			fmt->code = pixelcode;
> +		break;
> +	}
> +
> +	/* RGB, non-interlaced */
> +	fmt->colorspace = V4L2_COLORSPACE_SRGB;
> +	fmt->field = V4L2_FIELD_NONE;
> +}
> +
> +/*
> + * csi2_enum_mbus_code - Handle pixel format enumeration
> + * @sd     : pointer to v4l2 subdev structure
> + * @fh     : V4L2 subdev file handle
> + * @code   : pointer to v4l2_subdev_mbus_code_enum structure
> + * return -EINVAL or zero on success
> + */
> +static int csi2_enum_mbus_code(struct v4l2_subdev *sd,
> +			       struct v4l2_subdev_fh *fh,
> +			       struct v4l2_subdev_mbus_code_enum *code)
> +{
> +	struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
> +	struct v4l2_mbus_framefmt *format;
> +	const struct iss_format_info *info;
> +
> +	if (code->pad == CSI2_PAD_SINK) {
> +		if (code->index >= ARRAY_SIZE(csi2_input_fmts))
> +			return -EINVAL;
> +
> +		code->code = csi2_input_fmts[code->index];
> +	} else {
> +		format = __csi2_get_format(csi2, fh, CSI2_PAD_SINK,
> +					   V4L2_SUBDEV_FORMAT_TRY);
> +		switch (code->index) {
> +		case 0:
> +			/* Passthrough sink pad code */
> +			code->code = format->code;
> +			break;
> +		case 1:
> +			/* Uncompressed code */
> +			info = omap4iss_video_format_info(format->code);
> +			if (info->uncompressed == format->code)
> +				return -EINVAL;
> +
> +			code->code = info->uncompressed;
> +			break;
> +		default:
> +			return -EINVAL;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int csi2_enum_frame_size(struct v4l2_subdev *sd,
> +				struct v4l2_subdev_fh *fh,
> +				struct v4l2_subdev_frame_size_enum *fse)
> +{
> +	struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
> +	struct v4l2_mbus_framefmt format;
> +
> +	if (fse->index != 0)
> +		return -EINVAL;
> +
> +	format.code = fse->code;
> +	format.width = 1;
> +	format.height = 1;
> +	csi2_try_format(csi2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
> +	fse->min_width = format.width;
> +	fse->min_height = format.height;
> +
> +	if (format.code != fse->code)
> +		return -EINVAL;
> +
> +	format.code = fse->code;
> +	format.width = -1;
> +	format.height = -1;
> +	csi2_try_format(csi2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
> +	fse->max_width = format.width;
> +	fse->max_height = format.height;
> +
> +	return 0;
> +}
> +
> +/*
> + * csi2_get_format - Handle get format by pads subdev method
> + * @sd : pointer to v4l2 subdev structure
> + * @fh : V4L2 subdev file handle
> + * @fmt: pointer to v4l2 subdev format structure
> + * return -EINVAL or zero on success
> + */
> +static int csi2_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
> +			   struct v4l2_subdev_format *fmt)
> +{
> +	struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
> +	struct v4l2_mbus_framefmt *format;
> +
> +	format = __csi2_get_format(csi2, fh, fmt->pad, fmt->which);
> +	if (format == NULL)
> +		return -EINVAL;
> +
> +	fmt->format = *format;
> +	return 0;
> +}
> +
> +/*
> + * csi2_set_format - Handle set format by pads subdev method
> + * @sd : pointer to v4l2 subdev structure
> + * @fh : V4L2 subdev file handle
> + * @fmt: pointer to v4l2 subdev format structure
> + * return -EINVAL or zero on success
> + */
> +static int csi2_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
> +			   struct v4l2_subdev_format *fmt)
> +{
> +	struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
> +	struct v4l2_mbus_framefmt *format;
> +
> +	format = __csi2_get_format(csi2, fh, fmt->pad, fmt->which);
> +	if (format == NULL)
> +		return -EINVAL;
> +
> +	csi2_try_format(csi2, fh, fmt->pad, &fmt->format, fmt->which);
> +	*format = fmt->format;
> +
> +	/* Propagate the format from sink to source */
> +	if (fmt->pad == CSI2_PAD_SINK) {
> +		format = __csi2_get_format(csi2, fh, CSI2_PAD_SOURCE,
> +					   fmt->which);
> +		*format = fmt->format;
> +		csi2_try_format(csi2, fh, CSI2_PAD_SOURCE, format, fmt->which);
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * csi2_init_formats - Initialize formats on all pads
> + * @sd: ISS CSI2 V4L2 subdevice
> + * @fh: V4L2 subdev file handle
> + *
> + * Initialize all pad formats with default values. If fh is not NULL, try
> + * formats are initialized on the file handle. Otherwise active formats are
> + * initialized on the device.
> + */
> +static int csi2_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
> +{
> +	struct v4l2_subdev_format format;
> +
> +	memset(&format, 0, sizeof(format));
> +	format.pad = CSI2_PAD_SINK;
> +	format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
> +	format.format.code = V4L2_MBUS_FMT_SGRBG10_1X10;
> +	format.format.width = 4096;
> +	format.format.height = 4096;
> +	csi2_set_format(sd, fh, &format);
> +
> +	return 0;
> +}
> +
> +/*
> + * csi2_set_stream - Enable/Disable streaming on the CSI2 module
> + * @sd: ISS CSI2 V4L2 subdevice
> + * @enable: ISS pipeline stream state
> + *
> + * Return 0 on success or a negative error code otherwise.
> + */
> +static int csi2_set_stream(struct v4l2_subdev *sd, int enable)
> +{
> +	struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
> +	struct iss_device *iss = csi2->iss;
> +	struct iss_pipeline *pipe = to_iss_pipeline(&csi2->subdev.entity);
> +	struct iss_video *video_out = &csi2->video_out;
> +
> +	if (csi2->state == ISS_PIPELINE_STREAM_STOPPED) {
> +		if (enable == ISS_PIPELINE_STREAM_STOPPED)
> +			return 0;
> +
> +		omap4iss_subclk_enable(iss, OMAP4_ISS_SUBCLK_CSI2_A);
> +	}
> +
> +	switch (enable) {
> +	case ISS_PIPELINE_STREAM_CONTINUOUS:
> +		if (omap4iss_csiphy_acquire(csi2->phy) < 0)
> +			return -ENODEV;
> +		csi2->use_fs_irq = pipe->do_propagation;
> +		csi2_configure(csi2);
> +		csi2_print_status(csi2);
> +
> +		/*
> +		 * When outputting to memory with no buffer available, let the
> +		 * buffer queue handler start the hardware. A DMA queue flag
> +		 * ISS_VIDEO_DMAQUEUE_QUEUED will be set as soon as there is
> +		 * a buffer available.
> +		 */
> +		if (csi2->output & CSI2_OUTPUT_MEMORY &&
> +		    !(video_out->dmaqueue_flags & ISS_VIDEO_DMAQUEUE_QUEUED))
> +			break;
> +		/* Enable context 0 and IRQs */
> +		atomic_set(&csi2->stopping, 0);
> +		csi2_ctx_enable(csi2, 0, 1);
> +		csi2_if_enable(csi2, 1);
> +		iss_video_dmaqueue_flags_clr(video_out);
> +		break;
> +
> +	case ISS_PIPELINE_STREAM_STOPPED:
> +		if (csi2->state == ISS_PIPELINE_STREAM_STOPPED)
> +			return 0;
> +		if (omap4iss_module_sync_idle(&sd->entity, &csi2->wait,
> +					      &csi2->stopping))
> +			dev_dbg(iss->dev, "%s: module stop timeout.\n",
> +				sd->name);
> +		csi2_ctx_enable(csi2, 0, 0);
> +		csi2_if_enable(csi2, 0);
> +		csi2_irq_ctx_set(csi2, 0);
> +		omap4iss_csiphy_release(csi2->phy);
> +		omap4iss_subclk_disable(iss, OMAP4_ISS_SUBCLK_CSI2_A);
> +		iss_video_dmaqueue_flags_clr(video_out);
> +		break;
> +	}
> +
> +	csi2->state = enable;
> +	return 0;
> +}
> +
> +/* subdev video operations */
> +static const struct v4l2_subdev_video_ops csi2_video_ops = {
> +	.s_stream = csi2_set_stream,
> +};
> +
> +/* subdev pad operations */
> +static const struct v4l2_subdev_pad_ops csi2_pad_ops = {
> +	.enum_mbus_code = csi2_enum_mbus_code,
> +	.enum_frame_size = csi2_enum_frame_size,
> +	.get_fmt = csi2_get_format,
> +	.set_fmt = csi2_set_format,
> +};
> +
> +/* subdev operations */
> +static const struct v4l2_subdev_ops csi2_ops = {
> +	.video = &csi2_video_ops,
> +	.pad = &csi2_pad_ops,
> +};
> +
> +/* subdev internal operations */
> +static const struct v4l2_subdev_internal_ops csi2_internal_ops = {
> +	.open = csi2_init_formats,
> +};
> +
> +/* -----------------------------------------------------------------------------
> + * Media entity operations
> + */
> +
> +/*
> + * csi2_link_setup - Setup CSI2 connections.
> + * @entity : Pointer to media entity structure
> + * @local  : Pointer to local pad array
> + * @remote : Pointer to remote pad array
> + * @flags  : Link flags
> + * return -EINVAL or zero on success
> + */
> +static int csi2_link_setup(struct media_entity *entity,
> +			   const struct media_pad *local,
> +			   const struct media_pad *remote, u32 flags)
> +{
> +	struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
> +	struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
> +	struct iss_csi2_ctrl_cfg *ctrl = &csi2->ctrl;
> +
> +	/*
> +	 * The ISS core doesn't support pipelines with multiple video outputs.
> +	 * Revisit this when it will be implemented, and return -EBUSY for now.
> +	 */
> +
> +	switch (local->index | media_entity_type(remote->entity)) {
> +	case CSI2_PAD_SOURCE | MEDIA_ENT_T_DEVNODE:
> +		if (flags & MEDIA_LNK_FL_ENABLED) {
> +			if (csi2->output & ~CSI2_OUTPUT_MEMORY)
> +				return -EBUSY;
> +			csi2->output |= CSI2_OUTPUT_MEMORY;
> +		} else {
> +			csi2->output &= ~CSI2_OUTPUT_MEMORY;
> +		}
> +		break;
> +
> +	case CSI2_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV:
> +		if (flags & MEDIA_LNK_FL_ENABLED) {
> +			if (csi2->output & ~CSI2_OUTPUT_IPIPEIF)
> +				return -EBUSY;
> +			csi2->output |= CSI2_OUTPUT_IPIPEIF;
> +		} else {
> +			csi2->output &= ~CSI2_OUTPUT_IPIPEIF;
> +		}
> +		break;
> +
> +	default:
> +		/* Link from camera to CSI2 is fixed... */
> +		return -EINVAL;
> +	}
> +
> +	ctrl->vp_only_enable =
> +		(csi2->output & CSI2_OUTPUT_MEMORY) ? false : true;
> +	ctrl->vp_clk_enable = !!(csi2->output & CSI2_OUTPUT_IPIPEIF);
> +
> +	return 0;
> +}
> +
> +/* media operations */
> +static const struct media_entity_operations csi2_media_ops = {
> +	.link_setup = csi2_link_setup,
> +};
> +
> +/*
> + * csi2_init_entities - Initialize subdev and media entity.
> + * @csi2: Pointer to csi2 structure.
> + * return -ENOMEM or zero on success
> + */
> +static int csi2_init_entities(struct iss_csi2_device *csi2)
> +{
> +	struct v4l2_subdev *sd = &csi2->subdev;
> +	struct media_pad *pads = csi2->pads;
> +	struct media_entity *me = &sd->entity;
> +	int ret;
> +
> +	v4l2_subdev_init(sd, &csi2_ops);
> +	sd->internal_ops = &csi2_internal_ops;
> +	strlcpy(sd->name, "OMAP4 ISS CSI2a", sizeof(sd->name));
> +
> +	sd->grp_id = 1 << 16;	/* group ID for iss subdevs */
> +	v4l2_set_subdevdata(sd, csi2);
> +	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> +
> +	pads[CSI2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
> +	pads[CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
> +
> +	me->ops = &csi2_media_ops;
> +	ret = media_entity_init(me, CSI2_PADS_NUM, pads, 0);
> +	if (ret < 0)
> +		return ret;
> +
> +	csi2_init_formats(sd, NULL);
> +
> +	/* Video device node */
> +	csi2->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> +	csi2->video_out.ops = &csi2_issvideo_ops;
> +	csi2->video_out.bpl_alignment = 32;
> +	csi2->video_out.bpl_zero_padding = 1;
> +	csi2->video_out.bpl_max = 0x1ffe0;
> +	csi2->video_out.iss = csi2->iss;
> +	csi2->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 3;
> +
> +	ret = omap4iss_video_init(&csi2->video_out, "CSI2a");
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Connect the CSI2 subdev to the video node. */
> +	ret = media_entity_create_link(&csi2->subdev.entity, CSI2_PAD_SOURCE,
> +				       &csi2->video_out.video.entity, 0, 0);
> +	if (ret < 0)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +void omap4iss_csi2_unregister_entities(struct iss_csi2_device *csi2)
> +{
> +	media_entity_cleanup(&csi2->subdev.entity);
> +
> +	v4l2_device_unregister_subdev(&csi2->subdev);
> +	omap4iss_video_unregister(&csi2->video_out);
> +}
> +
> +int omap4iss_csi2_register_entities(struct iss_csi2_device *csi2,
> +				    struct v4l2_device *vdev)
> +{
> +	int ret;
> +
> +	/* Register the subdev and video nodes. */
> +	ret = v4l2_device_register_subdev(vdev, &csi2->subdev);
> +	if (ret < 0)
> +		goto error;
> +
> +	ret = omap4iss_video_register(&csi2->video_out, vdev);
> +	if (ret < 0)
> +		goto error;
> +
> +	return 0;
> +
> +error:
> +	omap4iss_csi2_unregister_entities(csi2);
> +	return ret;
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * ISS CSI2 initialisation and cleanup
> + */
> +
> +/*
> + * omap4iss_csi2_cleanup - Routine for module driver cleanup
> + */
> +void omap4iss_csi2_cleanup(struct iss_device *iss)
> +{
> +}
> +
> +/*
> + * omap4iss_csi2_init - Routine for module driver init
> + */
> +int omap4iss_csi2_init(struct iss_device *iss)
> +{
> +	struct iss_csi2_device *csi2a = &iss->csi2a;
> +	int ret;
> +
> +	csi2a->iss = iss;
> +	csi2a->available = 1;
> +	csi2a->regs1 = iss->regs[OMAP4_ISS_MEM_CSI2_A_REGS1];
> +	csi2a->phy = &iss->csiphy1;
> +	csi2a->state = ISS_PIPELINE_STREAM_STOPPED;
> +	init_waitqueue_head(&csi2a->wait);
> +
> +	ret = csi2_init_entities(csi2a);
> +	if (ret < 0)
> +		goto fail;
> +
> +	return 0;
> +fail:
> +	omap4iss_csi2_cleanup(iss);
> +	return ret;
> +}
> diff --git a/drivers/media/video/omap4iss/iss_csi2.h b/drivers/media/video/omap4iss/iss_csi2.h
> new file mode 100644
> index 0000000..4fa94cf
> --- /dev/null
> +++ b/drivers/media/video/omap4iss/iss_csi2.h
> @@ -0,0 +1,166 @@
> +/*
> + * iss_csi2.h
> + *
> + * TI OMAP4 ISS - CSI2 module
> + *
> + * Copyright (C) 2011 Texas Instruments, Inc.
> + *
> + * Contacts: Sergio Aguirre <saaguirre@ti.com>
> + *
> + * 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.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
> + * 02110-1301 USA
> + */
> +
> +#ifndef OMAP4_ISS_CSI2_H
> +#define OMAP4_ISS_CSI2_H
> +
> +#include <linux/types.h>
> +#include <linux/videodev2.h>
> +
> +#include "iss_video.h"
> +
> +struct iss_csiphy;
> +
> +/* This is not an exhaustive list */
> +enum iss_csi2_pix_formats {
> +	CSI2_PIX_FMT_OTHERS = 0,
> +	CSI2_PIX_FMT_YUV422_8BIT = 0x1e,
> +	CSI2_PIX_FMT_YUV422_8BIT_VP = 0x9e,
> +	CSI2_PIX_FMT_RAW10_EXP16 = 0xab,
> +	CSI2_PIX_FMT_RAW10_EXP16_VP = 0x12f,
> +	CSI2_PIX_FMT_RAW8 = 0x2a,
> +	CSI2_PIX_FMT_RAW8_DPCM10_EXP16 = 0x2aa,
> +	CSI2_PIX_FMT_RAW8_DPCM10_VP = 0x32a,
> +	CSI2_PIX_FMT_RAW8_VP = 0x12a,
> +	CSI2_USERDEF_8BIT_DATA1_DPCM10_VP = 0x340,
> +	CSI2_USERDEF_8BIT_DATA1_DPCM10 = 0x2c0,
> +	CSI2_USERDEF_8BIT_DATA1 = 0x40,
> +};
> +
> +enum iss_csi2_irqevents {
> +	OCP_ERR_IRQ = 0x4000,
> +	SHORT_PACKET_IRQ = 0x2000,
> +	ECC_CORRECTION_IRQ = 0x1000,
> +	ECC_NO_CORRECTION_IRQ = 0x800,
> +	COMPLEXIO2_ERR_IRQ = 0x400,
> +	COMPLEXIO1_ERR_IRQ = 0x200,
> +	FIFO_OVF_IRQ = 0x100,
> +	CONTEXT7 = 0x80,
> +	CONTEXT6 = 0x40,
> +	CONTEXT5 = 0x20,
> +	CONTEXT4 = 0x10,
> +	CONTEXT3 = 0x8,
> +	CONTEXT2 = 0x4,
> +	CONTEXT1 = 0x2,
> +	CONTEXT0 = 0x1,
> +};
> +
> +enum iss_csi2_ctx_irqevents {
> +	CTX_ECC_CORRECTION = 0x100,
> +	CTX_LINE_NUMBER = 0x80,
> +	CTX_FRAME_NUMBER = 0x40,
> +	CTX_CS = 0x20,
> +	CTX_LE = 0x8,
> +	CTX_LS = 0x4,
> +	CTX_FE = 0x2,
> +	CTX_FS = 0x1,
> +};
> +
> +enum iss_csi2_frame_mode {
> +	ISS_CSI2_FRAME_IMMEDIATE,
> +	ISS_CSI2_FRAME_AFTERFEC,
> +};
> +
> +#define ISS_CSI2_MAX_CTX_NUM	7
> +
> +struct iss_csi2_ctx_cfg {
> +	u8 ctxnum;		/* context number 0 - 7 */
> +	u8 dpcm_decompress;
> +
> +	/* Fields in CSI2_CTx_CTRL2 - locked by CSI2_CTx_CTRL1.CTX_EN */
> +	u8 virtual_id;
> +	u16 format_id;		/* as in CSI2_CTx_CTRL2[9:0] */
> +	u8 dpcm_predictor;	/* 1: simple, 0: advanced */
> +
> +	/* Fields in CSI2_CTx_CTRL1/3 - Shadowed */
> +	u16 alpha;
> +	u16 data_offset;
> +	u32 ping_addr;
> +	u32 pong_addr;
> +	u8 eof_enabled;
> +	u8 eol_enabled;
> +	u8 checksum_enabled;
> +	u8 enabled;
> +};
> +
> +struct iss_csi2_timing_cfg {
> +	u8 ionum;			/* IO1 or IO2 as in CSI2_TIMING */
> +	unsigned force_rx_mode:1;
> +	unsigned stop_state_16x:1;
> +	unsigned stop_state_4x:1;
> +	u16 stop_state_counter;
> +};
> +
> +struct iss_csi2_ctrl_cfg {
> +	bool vp_clk_enable;
> +	bool vp_only_enable;
> +	u8 vp_out_ctrl;
> +	enum iss_csi2_frame_mode frame_mode;
> +	bool ecc_enable;
> +	bool if_enable;
> +};
> +
> +#define CSI2_PAD_SINK		0
> +#define CSI2_PAD_SOURCE		1
> +#define CSI2_PADS_NUM		2
> +
> +#define CSI2_OUTPUT_IPIPEIF	(1 << 0)
> +#define CSI2_OUTPUT_MEMORY	(1 << 1)
> +
> +struct iss_csi2_device {
> +	struct v4l2_subdev subdev;
> +	struct media_pad pads[CSI2_PADS_NUM];
> +	struct v4l2_mbus_framefmt formats[CSI2_PADS_NUM];
> +
> +	struct iss_video video_out;
> +	struct iss_device *iss;
> +
> +	u8 available;		/* Is the IP present on the silicon? */
> +
> +	/* Pointer to register remaps into kernel space */
> +	void __iomem *regs1;
> +	void __iomem *regs2;
> +
> +	u32 output; /* output to IPIPEIF, memory or both? */
> +	bool dpcm_decompress;
> +	unsigned int frame_skip;
> +	bool use_fs_irq;
> +
> +	struct iss_csiphy *phy;
> +	struct iss_csi2_ctx_cfg contexts[ISS_CSI2_MAX_CTX_NUM + 1];
> +	struct iss_csi2_timing_cfg timing[2];
> +	struct iss_csi2_ctrl_cfg ctrl;
> +	enum iss_pipeline_stream_state state;
> +	wait_queue_head_t wait;
> +	atomic_t stopping;
> +};
> +
> +int omap4iss_csi2_isr(struct iss_csi2_device *csi2);
> +int omap4iss_csi2_reset(struct iss_csi2_device *csi2);
> +int omap4iss_csi2_init(struct iss_device *iss);
> +void omap4iss_csi2_cleanup(struct iss_device *iss);
> +void omap4iss_csi2_unregister_entities(struct iss_csi2_device *csi2);
> +int omap4iss_csi2_register_entities(struct iss_csi2_device *csi2,
> +				    struct v4l2_device *vdev);
> +#endif	/* OMAP4_ISS_CSI2_H */
> diff --git a/drivers/media/video/omap4iss/iss_csiphy.c b/drivers/media/video/omap4iss/iss_csiphy.c
> new file mode 100644
> index 0000000..9545622
> --- /dev/null
> +++ b/drivers/media/video/omap4iss/iss_csiphy.c
> @@ -0,0 +1,215 @@
> +/*
> + * iss_csiphy.c
> + *
> + * TI OMAP4 ISS - CSI PHY module
> + *
> + * Copyright (C) 2011 Texas Instruments, Inc.
> + *
> + * Contacts: Sergio Aguirre <saaguirre@ti.com>
> + *
> + * 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.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
> + * 02110-1301 USA
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +
> +#include "iss.h"
> +#include "iss_regs.h"
> +#include "iss_csiphy.h"
> +
> +/*
> + * csiphy_lanes_config - Configuration of CSIPHY lanes.
> + *
> + * Updates HW configuration.
> + * Called with phy->mutex taken.
> + */
> +static void csiphy_lanes_config(struct iss_csiphy *phy)
> +{
> +	unsigned int i;
> +	u32 reg;
> +
> +	reg = readl(phy->cfg_regs + CSI2_COMPLEXIO_CFG);
> +
> +	for (i = 0; i < phy->num_data_lanes; i++) {
> +		reg &= ~(CSI2_COMPLEXIO_CFG_DATA_POL(i + 1) |
> +			 CSI2_COMPLEXIO_CFG_DATA_POSITION_MASK(i + 1));
> +		reg |= (phy->lanes.data[i].pol ?
> +			CSI2_COMPLEXIO_CFG_DATA_POL(i + 1) : 0);
> +		reg |= (phy->lanes.data[i].pos <<
> +			CSI2_COMPLEXIO_CFG_DATA_POSITION_SHIFT(i + 1));
> +	}
> +
> +	reg &= ~(CSI2_COMPLEXIO_CFG_CLOCK_POL |
> +		 CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK);
> +	reg |= phy->lanes.clk.pol ? CSI2_COMPLEXIO_CFG_CLOCK_POL : 0;
> +	reg |= phy->lanes.clk.pos << CSI2_COMPLEXIO_CFG_CLOCK_POSITION_SHIFT;
> +
> +	writel(reg, phy->cfg_regs + CSI2_COMPLEXIO_CFG);
> +}
> +
> +/*
> + * csiphy_set_power
> + * @power: Power state to be set.
> + *
> + * Returns 0 if successful, or -EBUSY if the retry count is exceeded.
> + */
> +static int csiphy_set_power(struct iss_csiphy *phy, u32 power)
> +{
> +	u32 reg;
> +	u8 retry_count;
> +
> +	writel((readl(phy->cfg_regs + CSI2_COMPLEXIO_CFG) &
> +		~CSI2_COMPLEXIO_CFG_PWD_CMD_MASK) |
> +		power,
> +		phy->cfg_regs + CSI2_COMPLEXIO_CFG);
> +
> +	retry_count = 0;
> +	do {
> +		udelay(50);
> +		reg = readl(phy->cfg_regs + CSI2_COMPLEXIO_CFG) &
> +				CSI2_COMPLEXIO_CFG_PWD_STATUS_MASK;
> +
> +		if (reg != power >> 2)
> +			retry_count++;
> +
> +	} while ((reg != power >> 2) && (retry_count < 100));
> +
> +	if (retry_count == 100) {
> +		printk(KERN_ERR "CSI2 CIO set power failed!\n");
> +		return -EBUSY;
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * csiphy_dphy_config - Configure CSI2 D-PHY parameters.
> + *
> + * Called with phy->mutex taken.
> + */
> +static void csiphy_dphy_config(struct iss_csiphy *phy)
> +{
> +	u32 reg;
> +
> +	/* Set up REGISTER0 */
> +	reg = readl(phy->phy_regs + REGISTER0);
> +
> +	reg &= ~(REGISTER0_THS_TERM_MASK |
> +		 REGISTER0_THS_SETTLE_MASK);
> +	reg |= phy->dphy.ths_term << REGISTER0_THS_TERM_SHIFT;
> +	reg |= phy->dphy.ths_settle << REGISTER0_THS_SETTLE_SHIFT;
> +
> +	writel(reg, phy->phy_regs + REGISTER0);
> +
> +	/* Set up REGISTER1 */
> +	reg = readl(phy->phy_regs + REGISTER1);
> +
> +	reg &= ~(REGISTER1_TCLK_TERM_MASK |
> +		 REGISTER1_CTRLCLK_DIV_FACTOR_MASK |
> +		 REGISTER1_TCLK_SETTLE_MASK);
> +	reg |= phy->dphy.tclk_term << REGISTER1_TCLK_TERM_SHIFT;
> +	reg |= phy->dphy.tclk_miss << REGISTER1_CTRLCLK_DIV_FACTOR_SHIFT;
> +	reg |= phy->dphy.tclk_settle << REGISTER1_TCLK_SETTLE_SHIFT;
> +
> +	writel(reg, phy->phy_regs + REGISTER1);
> +}
> +
> +static int csiphy_config(struct iss_csiphy *phy,
> +			 struct iss_csiphy_dphy_cfg *dphy,
> +			 struct iss_csiphy_lanes_cfg *lanes)
> +{
> +	unsigned int used_lanes = 0;
> +	unsigned int i;
> +
> +	/* Clock and data lanes verification */
> +	for (i = 0; i < phy->num_data_lanes; i++) {
> +		if (lanes->data[i].pos == 0)
> +			continue;
> +
> +		if (lanes->data[i].pol > 1 || lanes->data[i].pos > 5)
> +			return -EINVAL;
> +
> +		if (used_lanes & (1 << lanes->data[i].pos))
> +			return -EINVAL;
> +
> +		used_lanes |= 1 << lanes->data[i].pos;
> +	}
> +
> +	if (lanes->clk.pol > 1 || lanes->clk.pos > 4)
> +		return -EINVAL;
> +
> +	if (lanes->clk.pos == 0 || used_lanes & (1 << lanes->clk.pos))
> +		return -EINVAL;
> +
> +	mutex_lock(&phy->mutex);
> +	phy->dphy = *dphy;
> +	phy->lanes = *lanes;
> +	mutex_unlock(&phy->mutex);
> +
> +	return 0;
> +}
> +
> +int omap4iss_csiphy_acquire(struct iss_csiphy *phy)
> +{
> +	int rval;
> +
> +	mutex_lock(&phy->mutex);
> +
> +	rval = omap4iss_csi2_reset(phy->csi2);
> +	if (rval)
> +		goto done;
> +
> +	csiphy_dphy_config(phy);
> +	csiphy_lanes_config(phy);
> +
> +	rval = csiphy_set_power(phy, CSI2_COMPLEXIO_CFG_PWD_CMD_ON);
> +	if (rval)
> +		goto done;
> +
> +	phy->phy_in_use = 1;
> +
> +done:
> +	mutex_unlock(&phy->mutex);
> +	return rval;
> +}
> +
> +void omap4iss_csiphy_release(struct iss_csiphy *phy)
> +{
> +	mutex_lock(&phy->mutex);
> +	if (phy->phy_in_use) {
> +		csiphy_set_power(phy, CSI2_COMPLEXIO_CFG_PWD_CMD_OFF);
> +		phy->phy_in_use = 0;
> +	}
> +	mutex_unlock(&phy->mutex);
> +}
> +
> +/*
> + * omap4iss_csiphy_init - Initialize the CSI PHY frontends
> + */
> +int omap4iss_csiphy_init(struct iss_device *iss)
> +{
> +	struct iss_csiphy *phy1 = &iss->csiphy1;
> +
> +	iss->platform_cb.csiphy_config = csiphy_config;
> +
> +	phy1->iss = iss;
> +	phy1->csi2 = &iss->csi2a;
> +	phy1->num_data_lanes = ISS_CSIPHY1_NUM_DATA_LANES;
> +	phy1->cfg_regs = iss->regs[OMAP4_ISS_MEM_CSI2_A_REGS1];
> +	phy1->phy_regs = iss->regs[OMAP4_ISS_MEM_CAMERARX_CORE1];
> +	mutex_init(&phy1->mutex);
> +
> +	return 0;
> +}
> diff --git a/drivers/media/video/omap4iss/iss_csiphy.h b/drivers/media/video/omap4iss/iss_csiphy.h
> new file mode 100644
> index 0000000..c513ba8
> --- /dev/null
> +++ b/drivers/media/video/omap4iss/iss_csiphy.h
> @@ -0,0 +1,69 @@
> +/*
> + * iss_csiphy.h
> + *
> + * TI OMAP4 ISS - CSI PHY module
> + *
> + * Copyright (C) 2011 Texas Instruments, Inc.
> + *
> + * Contacts: Sergio Aguirre <saaguirre@ti.com>
> + *
> + * 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.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
> + * 02110-1301 USA
> + */
> +
> +#ifndef OMAP4_ISS_CSI_PHY_H
> +#define OMAP4_ISS_CSI_PHY_H
> +
> +struct iss_csi2_device;
> +
> +struct csiphy_lane {
> +	u8 pos;
> +	u8 pol;
> +};
> +
> +#define ISS_CSIPHY1_NUM_DATA_LANES	4
> +
> +struct iss_csiphy_lanes_cfg {
> +	struct csiphy_lane data[ISS_CSIPHY1_NUM_DATA_LANES];
> +	struct csiphy_lane clk;
> +};
> +
> +struct iss_csiphy_dphy_cfg {
> +	u8 ths_term;
> +	u8 ths_settle;
> +	u8 tclk_term;
> +	unsigned tclk_miss:1;
> +	u8 tclk_settle;
> +};
> +
> +struct iss_csiphy {
> +	struct iss_device *iss;
> +	struct mutex mutex;	/* serialize csiphy configuration */
> +	u8 phy_in_use;
> +	struct iss_csi2_device *csi2;
> +
> +	/* Pointer to register remaps into kernel space */
> +	void __iomem *cfg_regs;
> +	void __iomem *phy_regs;
> +
> +	u8 num_data_lanes;	/* number of CSI2 Data Lanes supported */
> +	struct iss_csiphy_lanes_cfg lanes;
> +	struct iss_csiphy_dphy_cfg dphy;
> +};
> +
> +int omap4iss_csiphy_acquire(struct iss_csiphy *phy);
> +void omap4iss_csiphy_release(struct iss_csiphy *phy);
> +int omap4iss_csiphy_init(struct iss_device *iss);
> +
> +#endif	/* OMAP4_ISS_CSI_PHY_H */
> diff --git a/drivers/media/video/omap4iss/iss_regs.h b/drivers/media/video/omap4iss/iss_regs.h
> new file mode 100644
> index 0000000..0bd70ac
> --- /dev/null
> +++ b/drivers/media/video/omap4iss/iss_regs.h
> @@ -0,0 +1,238 @@
> +/*
> + * iss_regs.h
> + *
> + * Copyright (C) 2011 Texas Instruments.
> + *
> + * Author: Sergio Aguirre <saaguirre@ti.com>
> + */
> +
> +#ifndef _OMAP4_ISS_REGS_H_
> +#define _OMAP4_ISS_REGS_H_
> +
> +/* ISS */
> +#define ISS_HL_REVISION					(0x0)
> +
> +#define ISS_HL_SYSCONFIG				(0x10)
> +#define ISS_HL_SYSCONFIG_IDLEMODE_SHIFT			2
> +#define ISS_HL_SYSCONFIG_IDLEMODE_FORCEIDLE		0x0
> +#define ISS_HL_SYSCONFIG_IDLEMODE_NOIDLE		0x1
> +#define ISS_HL_SYSCONFIG_IDLEMODE_SMARTIDLE		0x2
> +#define ISS_HL_SYSCONFIG_SOFTRESET			(1 << 0)
> +
> +#define ISS_HL_IRQSTATUS_5				(0x24 + (0x10 * 5))
> +#define ISS_HL_IRQENABLE_5_SET				(0x28 + (0x10 * 5))
> +#define ISS_HL_IRQENABLE_5_CLR				(0x2C + (0x10 * 5))
> +
> +#define ISS_HL_IRQ_BTE					(1 << 11)
> +#define ISS_HL_IRQ_CBUFF				(1 << 10)
> +#define ISS_HL_IRQ_CSIA					(1 << 4)
> +
> +#define ISS_CTRL					(0x80)
> +
> +#define ISS_CLKCTRL					(0x84)
> +#define ISS_CLKCTRL_VPORT2_CLK				(1 << 30)
> +#define ISS_CLKCTRL_VPORT1_CLK				(1 << 29)
> +#define ISS_CLKCTRL_VPORT0_CLK				(1 << 28)
> +#define ISS_CLKCTRL_CCP2				(1 << 4)
> +#define ISS_CLKCTRL_CSI2_B				(1 << 3)
> +#define ISS_CLKCTRL_CSI2_A				(1 << 2)
> +#define ISS_CLKCTRL_ISP					(1 << 1)
> +#define ISS_CLKCTRL_SIMCOP				(1 << 0)
> +
> +#define ISS_CLKSTAT					(0x88)
> +#define ISS_CLKSTAT_VPORT2_CLK				(1 << 30)
> +#define ISS_CLKSTAT_VPORT1_CLK				(1 << 29)
> +#define ISS_CLKSTAT_VPORT0_CLK				(1 << 28)
> +#define ISS_CLKSTAT_CCP2				(1 << 4)
> +#define ISS_CLKSTAT_CSI2_B				(1 << 3)
> +#define ISS_CLKSTAT_CSI2_A				(1 << 2)
> +#define ISS_CLKSTAT_ISP					(1 << 1)
> +#define ISS_CLKSTAT_SIMCOP				(1 << 0)
> +
> +#define ISS_PM_STATUS					(0x8C)
> +#define ISS_PM_STATUS_CBUFF_PM_MASK			(3 << 12)
> +#define ISS_PM_STATUS_BTE_PM_MASK			(3 << 10)
> +#define ISS_PM_STATUS_SIMCOP_PM_MASK			(3 << 8)
> +#define ISS_PM_STATUS_ISP_PM_MASK			(3 << 6)
> +#define ISS_PM_STATUS_CCP2_PM_MASK			(3 << 4)
> +#define ISS_PM_STATUS_CSI2_B_PM_MASK			(3 << 2)
> +#define ISS_PM_STATUS_CSI2_A_PM_MASK			(3 << 0)
> +
> +#define REGISTER0					(0x0)
> +#define REGISTER0_HSCLOCKCONFIG				(1 << 24)
> +#define REGISTER0_THS_TERM_MASK				(0xFF << 8)
> +#define REGISTER0_THS_TERM_SHIFT			8
> +#define REGISTER0_THS_SETTLE_MASK			(0xFF << 0)
> +#define REGISTER0_THS_SETTLE_SHIFT			0
> +
> +#define REGISTER1					(0x4)
> +#define REGISTER1_RESET_DONE_CTRLCLK			(1 << 29)
> +#define REGISTER1_CLOCK_MISS_DETECTOR_STATUS		(1 << 25)
> +#define REGISTER1_TCLK_TERM_MASK			(0x3F << 18)
> +#define REGISTER1_TCLK_TERM_SHIFT			18
> +#define REGISTER1_DPHY_HS_SYNC_PATTERN_MASK		(0xFF << 10)
> +#define REGISTER1_CTRLCLK_DIV_FACTOR_MASK		(0x3 << 8)
> +#define REGISTER1_CTRLCLK_DIV_FACTOR_SHIFT		8
> +#define REGISTER1_TCLK_SETTLE_MASK			(0xFF << 0)
> +#define REGISTER1_TCLK_SETTLE_SHIFT			0
> +
> +#define REGISTER2					(0x8)
> +
> +#define CSI2_SYSCONFIG					(0x10)
> +#define CSI2_SYSCONFIG_MSTANDBY_MODE_MASK		(3 << 12)
> +#define CSI2_SYSCONFIG_MSTANDBY_MODE_FORCE		(0 << 12)
> +#define CSI2_SYSCONFIG_MSTANDBY_MODE_NO			(1 << 12)
> +#define CSI2_SYSCONFIG_MSTANDBY_MODE_SMART		(2 << 12)
> +#define CSI2_SYSCONFIG_SOFT_RESET			(1 << 1)
> +#define CSI2_SYSCONFIG_AUTO_IDLE			(1 << 0)
> +
> +#define CSI2_SYSSTATUS					(0x14)
> +#define CSI2_SYSSTATUS_RESET_DONE			(1 << 0)
> +
> +#define CSI2_IRQSTATUS					(0x18)
> +#define CSI2_IRQENABLE					(0x1C)
> +
> +/* Shared bits across CSI2_IRQENABLE and IRQSTATUS */
> +
> +#define CSI2_IRQ_OCP_ERR				(1 << 14)
> +#define CSI2_IRQ_SHORT_PACKET				(1 << 13)
> +#define CSI2_IRQ_ECC_CORRECTION				(1 << 12)
> +#define CSI2_IRQ_ECC_NO_CORRECTION			(1 << 11)
> +#define CSI2_IRQ_COMPLEXIO_ERR				(1 << 9)
> +#define CSI2_IRQ_FIFO_OVF				(1 << 8)
> +#define CSI2_IRQ_CONTEXT0				(1 << 0)
> +
> +#define CSI2_CTRL					(0x40)
> +#define CSI2_CTRL_MFLAG_LEVH_MASK			(7 << 20)
> +#define CSI2_CTRL_MFLAG_LEVH_SHIFT			20
> +#define CSI2_CTRL_MFLAG_LEVL_MASK			(7 << 17)
> +#define CSI2_CTRL_MFLAG_LEVL_SHIFT			17
> +#define CSI2_CTRL_BURST_SIZE_EXPAND			(1 << 16)
> +#define CSI2_CTRL_VP_CLK_EN				(1 << 15)
> +#define CSI2_CTRL_NON_POSTED_WRITE			(1 << 13)
> +#define CSI2_CTRL_VP_ONLY_EN				(1 << 11)
> +#define CSI2_CTRL_VP_OUT_CTRL_MASK			(3 << 8)
> +#define CSI2_CTRL_VP_OUT_CTRL_SHIFT			8
> +#define CSI2_CTRL_DBG_EN				(1 << 7)
> +#define CSI2_CTRL_BURST_SIZE_MASK			(3 << 5)
> +#define CSI2_CTRL_ENDIANNESS				(1 << 4)
> +#define CSI2_CTRL_FRAME					(1 << 3)
> +#define CSI2_CTRL_ECC_EN				(1 << 2)
> +#define CSI2_CTRL_IF_EN					(1 << 0)
> +
> +#define CSI2_DBG_H					(0x44)
> +
> +#define CSI2_COMPLEXIO_CFG				(0x50)
> +#define CSI2_COMPLEXIO_CFG_RESET_CTRL			(1 << 30)
> +#define CSI2_COMPLEXIO_CFG_RESET_DONE			(1 << 29)
> +#define CSI2_COMPLEXIO_CFG_PWD_CMD_MASK			(3 << 27)
> +#define CSI2_COMPLEXIO_CFG_PWD_CMD_OFF			(0 << 27)
> +#define CSI2_COMPLEXIO_CFG_PWD_CMD_ON			(1 << 27)
> +#define CSI2_COMPLEXIO_CFG_PWD_CMD_ULP			(2 << 27)
> +#define CSI2_COMPLEXIO_CFG_PWD_STATUS_MASK		(3 << 25)
> +#define CSI2_COMPLEXIO_CFG_PWD_STATUS_OFF		(0 << 25)
> +#define CSI2_COMPLEXIO_CFG_PWD_STATUS_ON		(1 << 25)
> +#define CSI2_COMPLEXIO_CFG_PWD_STATUS_ULP		(2 << 25)
> +#define CSI2_COMPLEXIO_CFG_PWR_AUTO			(1 << 24)
> +#define CSI2_COMPLEXIO_CFG_DATA_POL(i)			(1 << (((i) * 4) + 3))
> +#define CSI2_COMPLEXIO_CFG_DATA_POSITION_MASK(i)	(7 << ((i) * 4))
> +#define CSI2_COMPLEXIO_CFG_DATA_POSITION_SHIFT(i)	((i) * 4)
> +#define CSI2_COMPLEXIO_CFG_CLOCK_POL			(1 << 3)
> +#define CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK		(7 << 0)
> +#define CSI2_COMPLEXIO_CFG_CLOCK_POSITION_SHIFT		0
> +
> +#define CSI2_COMPLEXIO_IRQSTATUS			(0x54)
> +
> +#define CSI2_SHORT_PACKET				(0x5C)
> +
> +#define CSI2_COMPLEXIO_IRQENABLE			(0x60)
> +
> +/* Shared bits across CSI2_COMPLEXIO_IRQENABLE and IRQSTATUS */
> +#define CSI2_COMPLEXIO_IRQ_STATEALLULPMEXIT		(1 << 26)
> +#define CSI2_COMPLEXIO_IRQ_STATEALLULPMENTER		(1 << 25)
> +#define CSI2_COMPLEXIO_IRQ_STATEULPM5			(1 << 24)
> +#define CSI2_COMPLEXIO_IRQ_STATEULPM4			(1 << 23)
> +#define CSI2_COMPLEXIO_IRQ_STATEULPM3			(1 << 22)
> +#define CSI2_COMPLEXIO_IRQ_STATEULPM2			(1 << 21)
> +#define CSI2_COMPLEXIO_IRQ_STATEULPM1			(1 << 20)
> +#define CSI2_COMPLEXIO_IRQ_ERRCONTROL5			(1 << 19)
> +#define CSI2_COMPLEXIO_IRQ_ERRCONTROL4			(1 << 18)
> +#define CSI2_COMPLEXIO_IRQ_ERRCONTROL3			(1 << 17)
> +#define CSI2_COMPLEXIO_IRQ_ERRCONTROL2			(1 << 16)
> +#define CSI2_COMPLEXIO_IRQ_ERRCONTROL1			(1 << 15)
> +#define CSI2_COMPLEXIO_IRQ_ERRESC5			(1 << 14)
> +#define CSI2_COMPLEXIO_IRQ_ERRESC4			(1 << 13)
> +#define CSI2_COMPLEXIO_IRQ_ERRESC3			(1 << 12)
> +#define CSI2_COMPLEXIO_IRQ_ERRESC2			(1 << 11)
> +#define CSI2_COMPLEXIO_IRQ_ERRESC1			(1 << 10)
> +#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS5		(1 << 9)
> +#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS4		(1 << 8)
> +#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS3		(1 << 7)
> +#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS2		(1 << 6)
> +#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS1		(1 << 5)
> +#define CSI2_COMPLEXIO_IRQ_ERRSOTHS5			(1 << 4)
> +#define CSI2_COMPLEXIO_IRQ_ERRSOTHS4			(1 << 3)
> +#define CSI2_COMPLEXIO_IRQ_ERRSOTHS3			(1 << 2)
> +#define CSI2_COMPLEXIO_IRQ_ERRSOTHS2			(1 << 1)
> +#define CSI2_COMPLEXIO_IRQ_ERRSOTHS1			(1 << 0)
> +
> +#define CSI2_DBG_P					(0x68)
> +
> +#define CSI2_TIMING					(0x6C)
> +#define CSI2_TIMING_FORCE_RX_MODE_IO1			(1 << 15)
> +#define CSI2_TIMING_STOP_STATE_X16_IO1			(1 << 14)
> +#define CSI2_TIMING_STOP_STATE_X4_IO1			(1 << 13)
> +#define CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK		(0x1FFF << 0)
> +#define CSI2_TIMING_STOP_STATE_COUNTER_IO1_SHIFT	0
> +
> +#define CSI2_CTX_CTRL1(i)				(0x70 + (0x20 * i))
> +#define CSI2_CTX_CTRL1_GENERIC				(1 << 30)
> +#define CSI2_CTX_CTRL1_TRANSCODE			(0xF << 24)
> +#define CSI2_CTX_CTRL1_FEC_NUMBER_MASK			(0xFF << 16)
> +#define CSI2_CTX_CTRL1_COUNT_MASK			(0xFF << 8)
> +#define CSI2_CTX_CTRL1_COUNT_SHIFT			8
> +#define CSI2_CTX_CTRL1_EOF_EN				(1 << 7)
> +#define CSI2_CTX_CTRL1_EOL_EN				(1 << 6)
> +#define CSI2_CTX_CTRL1_CS_EN				(1 << 5)
> +#define CSI2_CTX_CTRL1_COUNT_UNLOCK			(1 << 4)
> +#define CSI2_CTX_CTRL1_PING_PONG			(1 << 3)
> +#define CSI2_CTX_CTRL1_CTX_EN				(1 << 0)
> +
> +#define CSI2_CTX_CTRL2(i)				(0x74 + (0x20 * i))
> +#define CSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT		13
> +#define CSI2_CTX_CTRL2_USER_DEF_MAP_MASK		\
> +		(0x3 << CSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT)
> +#define CSI2_CTX_CTRL2_VIRTUAL_ID_MASK			(3 << 11)
> +#define CSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT			11
> +#define CSI2_CTX_CTRL2_DPCM_PRED			(1 << 10)
> +#define CSI2_CTX_CTRL2_FORMAT_MASK			(0x3FF << 0)
> +#define CSI2_CTX_CTRL2_FORMAT_SHIFT			0
> +
> +#define CSI2_CTX_DAT_OFST(i)				(0x78 + (0x20 * i))
> +#define CSI2_CTX_DAT_OFST_MASK				(0xFFF << 5)
> +
> +#define CSI2_CTX_PING_ADDR(i)				(0x7C + (0x20 * i))
> +#define CSI2_CTX_PING_ADDR_MASK				0xFFFFFFE0
> +
> +#define CSI2_CTX_PONG_ADDR(i)				(0x80 + (0x20 * i))
> +#define CSI2_CTX_PONG_ADDR_MASK				CSI2_CTX_PING_ADDR_MASK
> +
> +#define CSI2_CTX_IRQENABLE(i)				(0x84 + (0x20 * i))
> +#define CSI2_CTX_IRQSTATUS(i)				(0x88 + (0x20 * i))
> +
> +#define CSI2_CTX_CTRL3(i)				(0x8C + (0x20 * i))
> +#define CSI2_CTX_CTRL3_ALPHA_SHIFT			5
> +#define CSI2_CTX_CTRL3_ALPHA_MASK			\
> +		(0x3fff << CSI2_CTX_CTRL3_ALPHA_SHIFT)
> +
> +/* Shared bits across CSI2_CTX_IRQENABLE and IRQSTATUS */
> +#define CSI2_CTX_IRQ_ECC_CORRECTION			(1 << 8)
> +#define CSI2_CTX_IRQ_LINE_NUMBER			(1 << 7)
> +#define CSI2_CTX_IRQ_FRAME_NUMBER			(1 << 6)
> +#define CSI2_CTX_IRQ_CS					(1 << 5)
> +#define CSI2_CTX_IRQ_LE					(1 << 3)
> +#define CSI2_CTX_IRQ_LS					(1 << 2)
> +#define CSI2_CTX_IRQ_FE					(1 << 1)
> +#define CSI2_CTX_IRQ_FS					(1 << 0)
> +
> +#endif /* _OMAP4_CAMERA_REGS_H_ */
> diff --git a/drivers/media/video/omap4iss/iss_video.c b/drivers/media/video/omap4iss/iss_video.c
> new file mode 100644
> index 0000000..3248711
> --- /dev/null
> +++ b/drivers/media/video/omap4iss/iss_video.c
> @@ -0,0 +1,1192 @@
> +/*
> + * iss_video.c
> + *
> + * TI OMAP4 ISS - Generic video node
> + *
> + * Copyright (C) 2011 Texas Instruments, Inc.
> + *
> + * Contacts: Sergio Aguirre <saaguirre@ti.com>
> + *
> + * 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.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
> + * 02110-1301 USA
> + */
> +
> +#include <asm/cacheflush.h>
> +#include <linux/clk.h>
> +#include <linux/mm.h>
> +#include <linux/pagemap.h>
> +#include <linux/sched.h>
> +#include <linux/slab.h>
> +#include <linux/vmalloc.h>
> +#include <linux/module.h>
> +#include <media/v4l2-dev.h>
> +#include <media/v4l2-ioctl.h>
> +#include <plat/omap-pm.h>
> +
> +#include "iss_video.h"
> +#include "iss.h"
> +
> +
> +/* -----------------------------------------------------------------------------
> + * Helper functions
> + */
> +
> +static struct iss_format_info formats[] = {
> +	{ V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8,
> +	  V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8,
> +	  V4L2_PIX_FMT_GREY, 8, },
> +	{ V4L2_MBUS_FMT_Y10_1X10, V4L2_MBUS_FMT_Y10_1X10,
> +	  V4L2_MBUS_FMT_Y10_1X10, V4L2_MBUS_FMT_Y8_1X8,
> +	  V4L2_PIX_FMT_Y10, 10, },
> +	{ V4L2_MBUS_FMT_Y12_1X12, V4L2_MBUS_FMT_Y10_1X10,
> +	  V4L2_MBUS_FMT_Y12_1X12, V4L2_MBUS_FMT_Y8_1X8,
> +	  V4L2_PIX_FMT_Y12, 12, },
> +	{ V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_MBUS_FMT_SBGGR8_1X8,
> +	  V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_MBUS_FMT_SBGGR8_1X8,
> +	  V4L2_PIX_FMT_SBGGR8, 8, },
> +	{ V4L2_MBUS_FMT_SGBRG8_1X8, V4L2_MBUS_FMT_SGBRG8_1X8,
> +	  V4L2_MBUS_FMT_SGBRG8_1X8, V4L2_MBUS_FMT_SGBRG8_1X8,
> +	  V4L2_PIX_FMT_SGBRG8, 8, },
> +	{ V4L2_MBUS_FMT_SGRBG8_1X8, V4L2_MBUS_FMT_SGRBG8_1X8,
> +	  V4L2_MBUS_FMT_SGRBG8_1X8, V4L2_MBUS_FMT_SGRBG8_1X8,
> +	  V4L2_PIX_FMT_SGRBG8, 8, },
> +	{ V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8,
> +	  V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8,
> +	  V4L2_PIX_FMT_SRGGB8, 8, },
> +	{ V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8,
> +	  V4L2_MBUS_FMT_SGRBG10_1X10, 0,
> +	  V4L2_PIX_FMT_SGRBG10DPCM8, 8, },
> +	{ V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR10_1X10,
> +	  V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR8_1X8,
> +	  V4L2_PIX_FMT_SBGGR10, 10, },
> +	{ V4L2_MBUS_FMT_SGBRG10_1X10, V4L2_MBUS_FMT_SGBRG10_1X10,
> +	  V4L2_MBUS_FMT_SGBRG10_1X10, V4L2_MBUS_FMT_SGBRG8_1X8,
> +	  V4L2_PIX_FMT_SGBRG10, 10, },
> +	{ V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_MBUS_FMT_SGRBG10_1X10,
> +	  V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_MBUS_FMT_SGRBG8_1X8,
> +	  V4L2_PIX_FMT_SGRBG10, 10, },
> +	{ V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_MBUS_FMT_SRGGB10_1X10,
> +	  V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_MBUS_FMT_SRGGB8_1X8,
> +	  V4L2_PIX_FMT_SRGGB10, 10, },
> +	{ V4L2_MBUS_FMT_SBGGR12_1X12, V4L2_MBUS_FMT_SBGGR10_1X10,
> +	  V4L2_MBUS_FMT_SBGGR12_1X12, V4L2_MBUS_FMT_SBGGR8_1X8,
> +	  V4L2_PIX_FMT_SBGGR12, 12, },
> +	{ V4L2_MBUS_FMT_SGBRG12_1X12, V4L2_MBUS_FMT_SGBRG10_1X10,
> +	  V4L2_MBUS_FMT_SGBRG12_1X12, V4L2_MBUS_FMT_SGBRG8_1X8,
> +	  V4L2_PIX_FMT_SGBRG12, 12, },
> +	{ V4L2_MBUS_FMT_SGRBG12_1X12, V4L2_MBUS_FMT_SGRBG10_1X10,
> +	  V4L2_MBUS_FMT_SGRBG12_1X12, V4L2_MBUS_FMT_SGRBG8_1X8,
> +	  V4L2_PIX_FMT_SGRBG12, 12, },
> +	{ V4L2_MBUS_FMT_SRGGB12_1X12, V4L2_MBUS_FMT_SRGGB10_1X10,
> +	  V4L2_MBUS_FMT_SRGGB12_1X12, V4L2_MBUS_FMT_SRGGB8_1X8,
> +	  V4L2_PIX_FMT_SRGGB12, 12, },
> +	{ V4L2_MBUS_FMT_UYVY8_1X16, V4L2_MBUS_FMT_UYVY8_1X16,
> +	  V4L2_MBUS_FMT_UYVY8_1X16, 0,
> +	  V4L2_PIX_FMT_UYVY, 16, },
> +	{ V4L2_MBUS_FMT_YUYV8_1X16, V4L2_MBUS_FMT_YUYV8_1X16,
> +	  V4L2_MBUS_FMT_YUYV8_1X16, 0,
> +	  V4L2_PIX_FMT_YUYV, 16, },
> +};
> +
> +const struct iss_format_info *
> +omap4iss_video_format_info(enum v4l2_mbus_pixelcode code)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(formats); ++i) {
> +		if (formats[i].code == code)
> +			return &formats[i];
> +	}
> +
> +	return NULL;
> +}
> +
> +/*
> + * iss_video_mbus_to_pix - Convert v4l2_mbus_framefmt to v4l2_pix_format
> + * @video: ISS video instance
> + * @mbus: v4l2_mbus_framefmt format (input)
> + * @pix: v4l2_pix_format format (output)
> + *
> + * Fill the output pix structure with information from the input mbus format.
> + * The bytesperline and sizeimage fields are computed from the requested bytes
> + * per line value in the pix format and information from the video instance.
> + *
> + * Return the number of padding bytes at end of line.
> + */
> +static unsigned int iss_video_mbus_to_pix(const struct iss_video *video,
> +					  const struct v4l2_mbus_framefmt *mbus,
> +					  struct v4l2_pix_format *pix)
> +{
> +	unsigned int bpl = pix->bytesperline;
> +	unsigned int min_bpl;
> +	unsigned int i;
> +
> +	memset(pix, 0, sizeof(*pix));
> +	pix->width = mbus->width;
> +	pix->height = mbus->height;
> +
> +	for (i = 0; i < ARRAY_SIZE(formats); ++i) {
> +		if (formats[i].code == mbus->code)
> +			break;
> +	}
> +
> +	if (WARN_ON(i == ARRAY_SIZE(formats)))
> +		return 0;
> +
> +	min_bpl = pix->width * ALIGN(formats[i].bpp, 8) / 8;
> +
> +	/* Clamp the requested bytes per line value. If the maximum bytes per
> +	 * line value is zero, the module doesn't support user configurable line
> +	 * sizes. Override the requested value with the minimum in that case.
> +	 */
> +	if (video->bpl_max)
> +		bpl = clamp(bpl, min_bpl, video->bpl_max);
> +	else
> +		bpl = min_bpl;
> +
> +	if (!video->bpl_zero_padding || bpl != min_bpl)
> +		bpl = ALIGN(bpl, video->bpl_alignment);
> +
> +	pix->pixelformat = formats[i].pixelformat;
> +	pix->bytesperline = bpl;
> +	pix->sizeimage = pix->bytesperline * pix->height;
> +	pix->colorspace = mbus->colorspace;
> +	pix->field = mbus->field;
> +
> +	return bpl - min_bpl;
> +}
> +
> +static void iss_video_pix_to_mbus(const struct v4l2_pix_format *pix,
> +				  struct v4l2_mbus_framefmt *mbus)
> +{
> +	unsigned int i;
> +
> +	memset(mbus, 0, sizeof(*mbus));
> +	mbus->width = pix->width;
> +	mbus->height = pix->height;
> +
> +	for (i = 0; i < ARRAY_SIZE(formats); ++i) {
> +		if (formats[i].pixelformat == pix->pixelformat)
> +			break;
> +	}
> +
> +	if (WARN_ON(i == ARRAY_SIZE(formats)))
> +		return;
> +
> +	mbus->code = formats[i].code;
> +	mbus->colorspace = pix->colorspace;
> +	mbus->field = pix->field;
> +}
> +
> +static struct v4l2_subdev *
> +iss_video_remote_subdev(struct iss_video *video, u32 *pad)
> +{
> +	struct media_pad *remote;
> +
> +	remote = media_entity_remote_source(&video->pad);
> +
> +	if (remote == NULL ||
> +	    media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
> +		return NULL;
> +
> +	if (pad)
> +		*pad = remote->index;
> +
> +	return media_entity_to_v4l2_subdev(remote->entity);
> +}
> +
> +/* Return a pointer to the ISS video instance at the far end of the pipeline. */
> +static struct iss_video *
> +iss_video_far_end(struct iss_video *video)
> +{
> +	struct media_entity_graph graph;
> +	struct media_entity *entity = &video->video.entity;
> +	struct media_device *mdev = entity->parent;
> +	struct iss_video *far_end = NULL;
> +
> +	mutex_lock(&mdev->graph_mutex);
> +	media_entity_graph_walk_start(&graph, entity);
> +
> +	while ((entity = media_entity_graph_walk_next(&graph))) {
> +		if (entity == &video->video.entity)
> +			continue;
> +
> +		if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE)
> +			continue;
> +
> +		far_end = to_iss_video(media_entity_to_video_device(entity));
> +		if (far_end->type != video->type)
> +			break;
> +
> +		far_end = NULL;
> +	}
> +
> +	mutex_unlock(&mdev->graph_mutex);
> +	return far_end;
> +}
> +
> +/*
> + * Validate a pipeline by checking both ends of all links for format
> + * discrepancies.
> + *
> + * Compute the minimum time per frame value as the maximum of time per frame
> + * limits reported by every block in the pipeline.
> + *
> + * Return 0 if all formats match, or -EPIPE if at least one link is found with
> + * different formats on its two ends.
> + */
> +static int iss_video_validate_pipeline(struct iss_pipeline *pipe)
> +{
> +	struct v4l2_subdev_format fmt_source;
> +	struct v4l2_subdev_format fmt_sink;
> +	struct media_pad *pad;
> +	struct v4l2_subdev *subdev;
> +	int ret;
> +
> +	subdev = iss_video_remote_subdev(pipe->output, NULL);
> +	if (subdev == NULL)
> +		return -EPIPE;
> +
> +	while (1) {
> +		/* Retrieve the sink format */
> +		pad = &subdev->entity.pads[0];
> +		if (!(pad->flags & MEDIA_PAD_FL_SINK))
> +			break;
> +
> +		fmt_sink.pad = pad->index;
> +		fmt_sink.which = V4L2_SUBDEV_FORMAT_ACTIVE;
> +		ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt_sink);
> +		if (ret < 0 && ret != -ENOIOCTLCMD)
> +			return -EPIPE;
> +
> +		/* Retrieve the source format */
> +		pad = media_entity_remote_source(pad);
> +		if (pad == NULL ||
> +		    media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
> +			break;
> +
> +		subdev = media_entity_to_v4l2_subdev(pad->entity);
> +
> +		fmt_source.pad = pad->index;
> +		fmt_source.which = V4L2_SUBDEV_FORMAT_ACTIVE;
> +		ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt_source);
> +		if (ret < 0 && ret != -ENOIOCTLCMD)
> +			return -EPIPE;
> +
> +		/* Check if the two ends match */
> +		if (fmt_source.format.width != fmt_sink.format.width ||
> +		    fmt_source.format.height != fmt_sink.format.height)
> +			return -EPIPE;
> +
> +		if (fmt_source.format.code != fmt_sink.format.code)
> +			return -EPIPE;
> +	}
> +
> +	return 0;
> +}
> +
> +static int
> +__iss_video_get_format(struct iss_video *video, struct v4l2_format *format)
> +{
> +	struct v4l2_subdev_format fmt;
> +	struct v4l2_subdev *subdev;
> +	u32 pad;
> +	int ret;
> +
> +	subdev = iss_video_remote_subdev(video, &pad);
> +	if (subdev == NULL)
> +		return -EINVAL;
> +
> +	mutex_lock(&video->mutex);
> +
> +	fmt.pad = pad;
> +	fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
> +	ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt);
> +	if (ret == -ENOIOCTLCMD)
> +		ret = -EINVAL;
> +
> +	mutex_unlock(&video->mutex);
> +
> +	if (ret)
> +		return ret;
> +
> +	format->type = video->type;
> +	return iss_video_mbus_to_pix(video, &fmt.format, &format->fmt.pix);
> +}
> +
> +static int
> +iss_video_check_format(struct iss_video *video, struct iss_video_fh *vfh)
> +{
> +	struct v4l2_format format;
> +	int ret;
> +
> +	memcpy(&format, &vfh->format, sizeof(format));
> +	ret = __iss_video_get_format(video, &format);
> +	if (ret < 0)
> +		return ret;
> +
> +	if (vfh->format.fmt.pix.pixelformat != format.fmt.pix.pixelformat ||
> +	    vfh->format.fmt.pix.height != format.fmt.pix.height ||
> +	    vfh->format.fmt.pix.width != format.fmt.pix.width ||
> +	    vfh->format.fmt.pix.bytesperline != format.fmt.pix.bytesperline ||
> +	    vfh->format.fmt.pix.sizeimage != format.fmt.pix.sizeimage)
> +		return -EINVAL;
> +
> +	return ret;
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * Video queue operations
> + */
> +
> +static int iss_video_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
> +				 unsigned int *count, unsigned int *num_planes,
> +				 unsigned int sizes[], void *alloc_ctxs[])
> +{
> +	struct iss_video_fh *vfh = vb2_get_drv_priv(vq);
> +	struct iss_video *video = vfh->video;
> +
> +	/* Revisit multi-planar support for NV12 */
> +	*num_planes = 1;
> +
> +	sizes[0] = vfh->format.fmt.pix.sizeimage;
> +	if (sizes[0] == 0)
> +		return -EINVAL;
> +
> +	alloc_ctxs[0] = video->alloc_ctx;
> +
> +	*count = min(*count, (unsigned int)(video->capture_mem / PAGE_ALIGN(sizes[0])));
> +
> +	return 0;
> +}
> +
> +static void iss_video_buf_cleanup(struct vb2_buffer *vb)
> +{
> +	struct iss_buffer *buffer = container_of(vb, struct iss_buffer, vb);
> +
> +	if (buffer->iss_addr)
> +		buffer->iss_addr = 0;
> +}
> +
> +static int iss_video_buf_prepare(struct vb2_buffer *vb)
> +{
> +	struct iss_video_fh *vfh = vb2_get_drv_priv(vb->vb2_queue);
> +	struct iss_buffer *buffer = container_of(vb, struct iss_buffer, vb);
> +	struct iss_video *video = vfh->video;
> +	unsigned long size = vfh->format.fmt.pix.sizeimage;
> +	dma_addr_t addr;
> +
> +	if (vb2_plane_size(vb, 0) < size)
> +		return -ENOBUFS;
> +
> +	addr = vb2_dma_contig_plane_dma_addr(vb, 0);
> +	if (!IS_ALIGNED(addr, 32)) {
> +		dev_dbg(video->iss->dev, "Buffer address must be "
> +			"aligned to 32 bytes boundary.\n");
> +		return -EINVAL;
> +	}
> +
> +	vb2_set_plane_payload(vb, 0, size);
> +	buffer->iss_addr = addr;
> +	return 0;
> +}
> +
> +static void iss_video_buf_queue(struct vb2_buffer *vb)
> +{
> +	struct iss_video_fh *vfh = vb2_get_drv_priv(vb->vb2_queue);
> +	struct iss_video *video = vfh->video;
> +	struct iss_buffer *buffer = container_of(vb, struct iss_buffer, vb);
> +	struct iss_pipeline *pipe = to_iss_pipeline(&video->video.entity);
> +	unsigned int empty;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&video->qlock, flags);
> +	empty = list_empty(&video->dmaqueue);
> +	list_add_tail(&buffer->list, &video->dmaqueue);
> +	spin_unlock_irqrestore(&video->qlock, flags);
> +
> +	if (empty) {
> +		enum iss_pipeline_state state;
> +		unsigned int start;
> +
> +		if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
> +			state = ISS_PIPELINE_QUEUE_OUTPUT;
> +		else
> +			state = ISS_PIPELINE_QUEUE_INPUT;
> +
> +		spin_lock_irqsave(&pipe->lock, flags);
> +		pipe->state |= state;
> +		video->ops->queue(video, buffer);
> +		video->dmaqueue_flags |= ISS_VIDEO_DMAQUEUE_QUEUED;
> +
> +		start = iss_pipeline_ready(pipe);
> +		if (start)
> +			pipe->state |= ISS_PIPELINE_STREAM;
> +		spin_unlock_irqrestore(&pipe->lock, flags);
> +
> +		if (start)
> +			omap4iss_pipeline_set_stream(pipe,
> +						ISS_PIPELINE_STREAM_SINGLESHOT);
> +	}
> +}
> +
> +static struct vb2_ops iss_video_vb2ops = {
> +	.queue_setup	= iss_video_queue_setup,
> +	.buf_prepare	= iss_video_buf_prepare,
> +	.buf_queue	= iss_video_buf_queue,
> +	.buf_cleanup	= iss_video_buf_cleanup,
> +};
> +
> +/*
> + * omap4iss_video_buffer_next - Complete the current buffer and return the next
> + * @video: ISS video object
> + * @error: Whether an error occurred during capture
> + *
> + * Remove the current video buffer from the DMA queue and fill its timestamp,
> + * field count and state fields before waking up its completion handler.
> + *
> + * The buffer state is set to VIDEOBUF_DONE if no error occurred (@error is 0)
> + * or VIDEOBUF_ERROR otherwise (@error is non-zero).
> + *
> + * The DMA queue is expected to contain at least one buffer.
> + *
> + * Return a pointer to the next buffer in the DMA queue, or NULL if the queue is
> + * empty.
> + */
> +struct iss_buffer *omap4iss_video_buffer_next(struct iss_video *video,
> +					      unsigned int error)
> +{
> +	struct iss_pipeline *pipe = to_iss_pipeline(&video->video.entity);
> +	enum iss_pipeline_state state;
> +	struct iss_buffer *buf;
> +	unsigned long flags;
> +	struct timespec ts;
> +
> +	spin_lock_irqsave(&video->qlock, flags);
> +	if (WARN_ON(list_empty(&video->dmaqueue))) {
> +		spin_unlock_irqrestore(&video->qlock, flags);
> +		return NULL;
> +	}
> +
> +	buf = list_first_entry(&video->dmaqueue, struct iss_buffer,
> +			       list);
> +	list_del(&buf->list);
> +	spin_unlock_irqrestore(&video->qlock, flags);
> +
> +	ktime_get_ts(&ts);
> +	buf->vb.v4l2_buf.timestamp.tv_sec = ts.tv_sec;
> +	buf->vb.v4l2_buf.timestamp.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
> +
> +	/* Do frame number propagation only if this is the output video node.
> +	 * Frame number either comes from the CSI receivers or it gets
> +	 * incremented here if H3A is not active.
> +	 * Note: There is no guarantee that the output buffer will finish
> +	 * first, so the input number might lag behind by 1 in some cases.
> +	 */
> +	if (video == pipe->output && !pipe->do_propagation)
> +		buf->vb.v4l2_buf.sequence = atomic_inc_return(&pipe->frame_number);
> +	else
> +		buf->vb.v4l2_buf.sequence = atomic_read(&pipe->frame_number);
> +
> +	vb2_buffer_done(&buf->vb, error < 0 ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
> +
> +	spin_lock_irqsave(&video->qlock, flags);
> +	if (list_empty(&video->dmaqueue)) {
> +		spin_unlock_irqrestore(&video->qlock, flags);
> +		if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
> +			state = ISS_PIPELINE_QUEUE_OUTPUT
> +			      | ISS_PIPELINE_STREAM;
> +		else
> +			state = ISS_PIPELINE_QUEUE_INPUT
> +			      | ISS_PIPELINE_STREAM;
> +
> +		spin_lock_irqsave(&pipe->lock, flags);
> +		pipe->state &= ~state;
> +		if (video->pipe.stream_state == ISS_PIPELINE_STREAM_CONTINUOUS)
> +			video->dmaqueue_flags |= ISS_VIDEO_DMAQUEUE_UNDERRUN;
> +		spin_unlock_irqrestore(&pipe->lock, flags);
> +		return NULL;
> +	}
> +
> +	if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->input != NULL) {
> +		spin_lock_irqsave(&pipe->lock, flags);
> +		pipe->state &= ~ISS_PIPELINE_STREAM;
> +		spin_unlock_irqrestore(&pipe->lock, flags);
> +	}
> +
> +	buf = list_first_entry(&video->dmaqueue, struct iss_buffer,
> +			       list);
> +	spin_unlock_irqrestore(&video->qlock, flags);
> +	buf->vb.state = VB2_BUF_STATE_ACTIVE;
> +	return buf;
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * V4L2 ioctls
> + */
> +
> +static int
> +iss_video_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
> +{
> +	struct iss_video *video = video_drvdata(file);
> +
> +	strlcpy(cap->driver, ISS_VIDEO_DRIVER_NAME, sizeof(cap->driver));
> +	strlcpy(cap->card, video->video.name, sizeof(cap->card));
> +	strlcpy(cap->bus_info, "media", sizeof(cap->bus_info));
> +	cap->version = ISS_VIDEO_DRIVER_VERSION;
> +
> +	if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
> +		cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
> +	else
> +		cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
> +
> +	return 0;
> +}
> +
> +static int
> +iss_video_get_format(struct file *file, void *fh, struct v4l2_format *format)
> +{
> +	struct iss_video_fh *vfh = to_iss_video_fh(fh);
> +	struct iss_video *video = video_drvdata(file);
> +
> +	if (format->type != video->type)
> +		return -EINVAL;
> +
> +	mutex_lock(&video->mutex);
> +	*format = vfh->format;
> +	mutex_unlock(&video->mutex);
> +
> +	return 0;
> +}
> +
> +static int
> +iss_video_set_format(struct file *file, void *fh, struct v4l2_format *format)
> +{
> +	struct iss_video_fh *vfh = to_iss_video_fh(fh);
> +	struct iss_video *video = video_drvdata(file);
> +	struct v4l2_mbus_framefmt fmt;
> +
> +	if (format->type != video->type)
> +		return -EINVAL;
> +
> +	mutex_lock(&video->mutex);
> +
> +	/* Fill the bytesperline and sizeimage fields by converting to media bus
> +	 * format and back to pixel format.
> +	 */
> +	iss_video_pix_to_mbus(&format->fmt.pix, &fmt);
> +	iss_video_mbus_to_pix(video, &fmt, &format->fmt.pix);
> +
> +	vfh->format = *format;
> +
> +	mutex_unlock(&video->mutex);
> +	return 0;
> +}
> +
> +static int
> +iss_video_try_format(struct file *file, void *fh, struct v4l2_format *format)
> +{
> +	struct iss_video *video = video_drvdata(file);
> +	struct v4l2_subdev_format fmt;
> +	struct v4l2_subdev *subdev;
> +	u32 pad;
> +	int ret;
> +
> +	if (format->type != video->type)
> +		return -EINVAL;
> +
> +	subdev = iss_video_remote_subdev(video, &pad);
> +	if (subdev == NULL)
> +		return -EINVAL;
> +
> +	iss_video_pix_to_mbus(&format->fmt.pix, &fmt.format);
> +
> +	fmt.pad = pad;
> +	fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
> +	ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt);
> +	if (ret)
> +		return ret == -ENOIOCTLCMD ? -EINVAL : ret;
> +
> +	iss_video_mbus_to_pix(video, &fmt.format, &format->fmt.pix);
> +	return 0;
> +}
> +
> +static int
> +iss_video_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cropcap)
> +{
> +	struct iss_video *video = video_drvdata(file);
> +	struct v4l2_subdev *subdev;
> +	int ret;
> +
> +	subdev = iss_video_remote_subdev(video, NULL);
> +	if (subdev == NULL)
> +		return -EINVAL;
> +
> +	mutex_lock(&video->mutex);
> +	ret = v4l2_subdev_call(subdev, video, cropcap, cropcap);
> +	mutex_unlock(&video->mutex);
> +
> +	return ret == -ENOIOCTLCMD ? -EINVAL : ret;
> +}
> +
> +static int
> +iss_video_get_crop(struct file *file, void *fh, struct v4l2_crop *crop)
> +{
> +	struct iss_video *video = video_drvdata(file);
> +	struct v4l2_subdev_format format;
> +	struct v4l2_subdev *subdev;
> +	u32 pad;
> +	int ret;
> +
> +	subdev = iss_video_remote_subdev(video, &pad);
> +	if (subdev == NULL)
> +		return -EINVAL;
> +
> +	/* Try the get crop operation first and fallback to get format if not
> +	 * implemented.
> +	 */
> +	ret = v4l2_subdev_call(subdev, video, g_crop, crop);
> +	if (ret != -ENOIOCTLCMD)
> +		return ret;
> +
> +	format.pad = pad;
> +	format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
> +	ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &format);
> +	if (ret < 0)
> +		return ret == -ENOIOCTLCMD ? -EINVAL : ret;
> +
> +	crop->c.left = 0;
> +	crop->c.top = 0;
> +	crop->c.width = format.format.width;
> +	crop->c.height = format.format.height;
> +
> +	return 0;
> +}
> +
> +static int
> +iss_video_set_crop(struct file *file, void *fh, struct v4l2_crop *crop)
> +{
> +	struct iss_video *video = video_drvdata(file);
> +	struct v4l2_subdev *subdev;
> +	int ret;
> +
> +	subdev = iss_video_remote_subdev(video, NULL);
> +	if (subdev == NULL)
> +		return -EINVAL;
> +
> +	mutex_lock(&video->mutex);
> +	ret = v4l2_subdev_call(subdev, video, s_crop, crop);
> +	mutex_unlock(&video->mutex);
> +
> +	return ret == -ENOIOCTLCMD ? -EINVAL : ret;
> +}
> +
> +static int
> +iss_video_get_param(struct file *file, void *fh, struct v4l2_streamparm *a)
> +{
> +	struct iss_video_fh *vfh = to_iss_video_fh(fh);
> +	struct iss_video *video = video_drvdata(file);
> +
> +	if (video->type != V4L2_BUF_TYPE_VIDEO_OUTPUT ||
> +	    video->type != a->type)
> +		return -EINVAL;
> +
> +	memset(a, 0, sizeof(*a));
> +	a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
> +	a->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
> +	a->parm.output.timeperframe = vfh->timeperframe;
> +
> +	return 0;
> +}
> +
> +static int
> +iss_video_set_param(struct file *file, void *fh, struct v4l2_streamparm *a)
> +{
> +	struct iss_video_fh *vfh = to_iss_video_fh(fh);
> +	struct iss_video *video = video_drvdata(file);
> +
> +	if (video->type != V4L2_BUF_TYPE_VIDEO_OUTPUT ||
> +	    video->type != a->type)
> +		return -EINVAL;
> +
> +	if (a->parm.output.timeperframe.denominator == 0)
> +		a->parm.output.timeperframe.denominator = 1;
> +
> +	vfh->timeperframe = a->parm.output.timeperframe;
> +
> +	return 0;
> +}
> +
> +static int
> +iss_video_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *rb)
> +{
> +	struct iss_video_fh *vfh = to_iss_video_fh(fh);
> +
> +	return vb2_reqbufs(&vfh->queue, rb);
> +}
> +
> +static int
> +iss_video_querybuf(struct file *file, void *fh, struct v4l2_buffer *b)
> +{
> +	struct iss_video_fh *vfh = to_iss_video_fh(fh);
> +
> +	return vb2_querybuf(&vfh->queue, b);
> +}
> +
> +static int
> +iss_video_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
> +{
> +	struct iss_video_fh *vfh = to_iss_video_fh(fh);
> +
> +	return vb2_qbuf(&vfh->queue, b);
> +}
> +
> +static int
> +iss_video_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
> +{
> +	struct iss_video_fh *vfh = to_iss_video_fh(fh);
> +
> +	return vb2_dqbuf(&vfh->queue, b, file->f_flags & O_NONBLOCK);
> +}
> +
> +/*
> + * Stream management
> + *
> + * Every ISS pipeline has a single input and a single output. The input can be
> + * either a sensor or a video node. The output is always a video node.
> + *
> + * As every pipeline has an output video node, the ISS video objects at the
> + * pipeline output stores the pipeline state. It tracks the streaming state of
> + * both the input and output, as well as the availability of buffers.
> + *
> + * In sensor-to-memory mode, frames are always available at the pipeline input.
> + * Starting the sensor usually requires I2C transfers and must be done in
> + * interruptible context. The pipeline is started and stopped synchronously
> + * to the stream on/off commands. All modules in the pipeline will get their
> + * subdev set stream handler called. The module at the end of the pipeline must
> + * delay starting the hardware until buffers are available at its output.
> + *
> + * In memory-to-memory mode, starting/stopping the stream requires
> + * synchronization between the input and output. ISS modules can't be stopped
> + * in the middle of a frame, and at least some of the modules seem to become
> + * busy as soon as they're started, even if they don't receive a frame start
> + * event. For that reason frames need to be processed in single-shot mode. The
> + * driver needs to wait until a frame is completely processed and written to
> + * memory before restarting the pipeline for the next frame. Pipelined
> + * processing might be possible but requires more testing.
> + *
> + * Stream start must be delayed until buffers are available at both the input
> + * and output. The pipeline must be started in the videobuf queue callback with
> + * the buffers queue spinlock held. The modules subdev set stream operation must
> + * not sleep.
> + */
> +static int
> +iss_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
> +{
> +	struct iss_video_fh *vfh = to_iss_video_fh(fh);
> +	struct iss_video *video = video_drvdata(file);
> +	enum iss_pipeline_state state;
> +	struct iss_pipeline *pipe;
> +	struct iss_video *far_end;
> +	unsigned long flags;
> +	int ret;
> +
> +	if (type != video->type)
> +		return -EINVAL;
> +
> +	mutex_lock(&video->stream_lock);
> +
> +	if (video->streaming) {
> +		mutex_unlock(&video->stream_lock);
> +		return -EBUSY;
> +	}
> +
> +	/* Start streaming on the pipeline. No link touching an entity in the
> +	 * pipeline can be activated or deactivated once streaming is started.
> +	 */
> +	pipe = video->video.entity.pipe
> +	     ? to_iss_pipeline(&video->video.entity) : &video->pipe;
> +	media_entity_pipeline_start(&video->video.entity, &pipe->pipe);
> +
> +	/* Verify that the currently configured format matches the output of
> +	 * the connected subdev.
> +	 */
> +	ret = iss_video_check_format(video, vfh);
> +	if (ret < 0)
> +		goto error;
> +
> +	video->bpl_padding = ret;
> +	video->bpl_value = vfh->format.fmt.pix.bytesperline;
> +
> +	/* Find the ISS video node connected at the far end of the pipeline and
> +	 * update the pipeline.
> +	 */
> +	far_end = iss_video_far_end(video);
> +
> +	if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
> +		state = ISS_PIPELINE_STREAM_OUTPUT | ISS_PIPELINE_IDLE_OUTPUT;
> +		pipe->input = far_end;
> +		pipe->output = video;
> +	} else {
> +		if (far_end == NULL) {
> +			ret = -EPIPE;
> +			goto error;
> +		}
> +
> +		state = ISS_PIPELINE_STREAM_INPUT | ISS_PIPELINE_IDLE_INPUT;
> +		pipe->input = video;
> +		pipe->output = far_end;
> +	}
> +
> +	if (video->iss->pdata->set_constraints)
> +		video->iss->pdata->set_constraints(video->iss, true);
> +
> +	/* Validate the pipeline and update its state. */
> +	ret = iss_video_validate_pipeline(pipe);
> +	if (ret < 0)
> +		goto error;
> +
> +	spin_lock_irqsave(&pipe->lock, flags);
> +	pipe->state &= ~ISS_PIPELINE_STREAM;
> +	pipe->state |= state;
> +	spin_unlock_irqrestore(&pipe->lock, flags);
> +
> +	/* Set the maximum time per frame as the value requested by userspace.
> +	 * This is a soft limit that can be overridden if the hardware doesn't
> +	 * support the request limit.
> +	 */
> +	if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
> +		pipe->max_timeperframe = vfh->timeperframe;
> +
> +	video->queue = &vfh->queue;
> +	INIT_LIST_HEAD(&video->dmaqueue);
> +	spin_lock_init(&video->qlock);
> +	atomic_set(&pipe->frame_number, -1);
> +
> +	ret = vb2_streamon(&vfh->queue, type);
> +	if (ret < 0)
> +		goto error;
> +
> +	/* In sensor-to-memory mode, the stream can be started synchronously
> +	 * to the stream on command. In memory-to-memory mode, it will be
> +	 * started when buffers are queued on both the input and output.
> +	 */
> +	if (pipe->input == NULL) {
> +		unsigned long flags;
> +		ret = omap4iss_pipeline_set_stream(pipe,
> +					      ISS_PIPELINE_STREAM_CONTINUOUS);
> +		if (ret < 0)
> +			goto error;
> +		spin_lock_irqsave(&video->qlock, flags);
> +		if (list_empty(&video->dmaqueue))
> +			video->dmaqueue_flags |= ISS_VIDEO_DMAQUEUE_UNDERRUN;
> +		spin_unlock_irqrestore(&video->qlock, flags);
> +	}
> +
> +error:
> +	if (ret < 0) {
> +		vb2_streamoff(&vfh->queue, type);
> +		if (video->iss->pdata->set_constraints)
> +			video->iss->pdata->set_constraints(video->iss, false);
> +		media_entity_pipeline_stop(&video->video.entity);
> +		video->queue = NULL;
> +	}
> +
> +	if (!ret)
> +		video->streaming = 1;
> +
> +	mutex_unlock(&video->stream_lock);
> +	return ret;
> +}
> +
> +static int
> +iss_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type)
> +{
> +	struct iss_video_fh *vfh = to_iss_video_fh(fh);
> +	struct iss_video *video = video_drvdata(file);
> +	struct iss_pipeline *pipe = to_iss_pipeline(&video->video.entity);
> +	enum iss_pipeline_state state;
> +	unsigned long flags;
> +
> +	if (type != video->type)
> +		return -EINVAL;
> +
> +	mutex_lock(&video->stream_lock);
> +
> +	if (!vb2_is_streaming(&vfh->queue))
> +		goto done;
> +
> +	/* Update the pipeline state. */
> +	if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
> +		state = ISS_PIPELINE_STREAM_OUTPUT
> +		      | ISS_PIPELINE_QUEUE_OUTPUT;
> +	else
> +		state = ISS_PIPELINE_STREAM_INPUT
> +		      | ISS_PIPELINE_QUEUE_INPUT;
> +
> +	spin_lock_irqsave(&pipe->lock, flags);
> +	pipe->state &= ~state;
> +	spin_unlock_irqrestore(&pipe->lock, flags);
> +
> +	/* Stop the stream. */
> +	omap4iss_pipeline_set_stream(pipe, ISS_PIPELINE_STREAM_STOPPED);
> +	vb2_streamoff(&vfh->queue, type);
> +	video->queue = NULL;
> +	video->streaming = 0;
> +
> +	if (video->iss->pdata->set_constraints)
> +		video->iss->pdata->set_constraints(video->iss, false);
> +	media_entity_pipeline_stop(&video->video.entity);
> +
> +done:
> +	mutex_unlock(&video->stream_lock);
> +	return 0;
> +}
> +
> +static int
> +iss_video_enum_input(struct file *file, void *fh, struct v4l2_input *input)
> +{
> +	if (input->index > 0)
> +		return -EINVAL;
> +
> +	strlcpy(input->name, "camera", sizeof(input->name));
> +	input->type = V4L2_INPUT_TYPE_CAMERA;
> +
> +	return 0;
> +}
> +
> +static int
> +iss_video_g_input(struct file *file, void *fh, unsigned int *input)
> +{
> +	*input = 0;
> +
> +	return 0;
> +}
> +
> +static int
> +iss_video_s_input(struct file *file, void *fh, unsigned int input)
> +{
> +	return input == 0 ? 0 : -EINVAL;
> +}
> +
> +static const struct v4l2_ioctl_ops iss_video_ioctl_ops = {
> +	.vidioc_querycap		= iss_video_querycap,
> +	.vidioc_g_fmt_vid_cap		= iss_video_get_format,
> +	.vidioc_s_fmt_vid_cap		= iss_video_set_format,
> +	.vidioc_try_fmt_vid_cap		= iss_video_try_format,
> +	.vidioc_g_fmt_vid_out		= iss_video_get_format,
> +	.vidioc_s_fmt_vid_out		= iss_video_set_format,
> +	.vidioc_try_fmt_vid_out		= iss_video_try_format,
> +	.vidioc_cropcap			= iss_video_cropcap,
> +	.vidioc_g_crop			= iss_video_get_crop,
> +	.vidioc_s_crop			= iss_video_set_crop,
> +	.vidioc_g_parm			= iss_video_get_param,
> +	.vidioc_s_parm			= iss_video_set_param,
> +	.vidioc_reqbufs			= iss_video_reqbufs,
> +	.vidioc_querybuf		= iss_video_querybuf,
> +	.vidioc_qbuf			= iss_video_qbuf,
> +	.vidioc_dqbuf			= iss_video_dqbuf,
> +	.vidioc_streamon		= iss_video_streamon,
> +	.vidioc_streamoff		= iss_video_streamoff,
> +	.vidioc_enum_input		= iss_video_enum_input,
> +	.vidioc_g_input			= iss_video_g_input,
> +	.vidioc_s_input			= iss_video_s_input,
> +};
> +
> +/* -----------------------------------------------------------------------------
> + * V4L2 file operations
> + */
> +
> +static int iss_video_open(struct file *file)
> +{
> +	struct iss_video *video = video_drvdata(file);
> +	struct iss_video_fh *handle;
> +	struct vb2_queue *q;
> +	int ret = 0;
> +
> +	handle = kzalloc(sizeof(*handle), GFP_KERNEL);
> +	if (handle == NULL)
> +		return -ENOMEM;
> +
> +	v4l2_fh_init(&handle->vfh, &video->video);
> +	v4l2_fh_add(&handle->vfh);
> +
> +	/* If this is the first user, initialise the pipeline. */
> +	if (omap4iss_get(video->iss) == NULL) {
> +		ret = -EBUSY;
> +		goto done;
> +	}
> +
> +	ret = omap4iss_pipeline_pm_use(&video->video.entity, 1);
> +	if (ret < 0) {
> +		omap4iss_put(video->iss);
> +		goto done;
> +	}
> +
> +	video->alloc_ctx = vb2_dma_contig_init_ctx(video->iss->dev);
> +	if (IS_ERR(video->alloc_ctx)) {
> +		ret = PTR_ERR(video->alloc_ctx);
> +		omap4iss_put(video->iss);
> +		goto done;
> +	}
> +
> +	q = &handle->queue;
> +
> +	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> +	q->io_modes = VB2_MMAP;
> +	q->drv_priv = handle;
> +	q->ops = &iss_video_vb2ops;
> +	q->mem_ops = &vb2_dma_contig_memops;
> +	q->buf_struct_size = sizeof(struct iss_buffer);
> +
> +	ret = vb2_queue_init(q);
> +	if (ret) {
> +		omap4iss_put(video->iss);
> +		goto done;
> +	}
> +
> +	memset(&handle->format, 0, sizeof(handle->format));
> +	handle->format.type = video->type;
> +	handle->timeperframe.denominator = 1;
> +
> +	handle->video = video;
> +	file->private_data = &handle->vfh;
> +
> +done:
> +	if (ret < 0) {
> +		v4l2_fh_del(&handle->vfh);
> +		kfree(handle);
> +	}
> +
> +	return ret;
> +}
> +
> +static int iss_video_release(struct file *file)
> +{
> +	struct iss_video *video = video_drvdata(file);
> +	struct v4l2_fh *vfh = file->private_data;
> +	struct iss_video_fh *handle = to_iss_video_fh(vfh);
> +
> +	/* Disable streaming and free the buffers queue resources. */
> +	iss_video_streamoff(file, vfh, video->type);
> +
> +	omap4iss_pipeline_pm_use(&video->video.entity, 0);
> +
> +	/* Release the file handle. */
> +	v4l2_fh_del(vfh);
> +	kfree(handle);
> +	file->private_data = NULL;
> +
> +	omap4iss_put(video->iss);
> +
> +	return 0;
> +}
> +
> +static unsigned int iss_video_poll(struct file *file, poll_table *wait)
> +{
> +	struct iss_video_fh *vfh = to_iss_video_fh(file->private_data);
> +
> +	return vb2_poll(&vfh->queue, file, wait);
> +}
> +
> +static int iss_video_mmap(struct file *file, struct vm_area_struct *vma)
> +{
> +	struct iss_video_fh *vfh = to_iss_video_fh(file->private_data);
> +
> +	return vb2_mmap(&vfh->queue, vma);;
> +}
> +
> +static struct v4l2_file_operations iss_video_fops = {
> +	.owner = THIS_MODULE,
> +	.unlocked_ioctl = video_ioctl2,
> +	.open = iss_video_open,
> +	.release = iss_video_release,
> +	.poll = iss_video_poll,
> +	.mmap = iss_video_mmap,
> +};
> +
> +/* -----------------------------------------------------------------------------
> + * ISS video core
> + */
> +
> +static const struct iss_video_operations iss_video_dummy_ops = {
> +};
> +
> +int omap4iss_video_init(struct iss_video *video, const char *name)
> +{
> +	const char *direction;
> +	int ret;
> +
> +	switch (video->type) {
> +	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> +		direction = "output";
> +		video->pad.flags = MEDIA_PAD_FL_SINK;
> +		break;
> +	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> +		direction = "input";
> +		video->pad.flags = MEDIA_PAD_FL_SOURCE;
> +		break;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	ret = media_entity_init(&video->video.entity, 1, &video->pad, 0);
> +	if (ret < 0)
> +		return ret;
> +
> +	mutex_init(&video->mutex);
> +	atomic_set(&video->active, 0);
> +
> +	spin_lock_init(&video->pipe.lock);
> +	mutex_init(&video->stream_lock);
> +
> +	/* Initialize the video device. */
> +	if (video->ops == NULL)
> +		video->ops = &iss_video_dummy_ops;
> +
> +	video->video.fops = &iss_video_fops;
> +	snprintf(video->video.name, sizeof(video->video.name),
> +		 "OMAP4 ISS %s %s", name, direction);
> +	video->video.vfl_type = VFL_TYPE_GRABBER;
> +	video->video.release = video_device_release_empty;
> +	video->video.ioctl_ops = &iss_video_ioctl_ops;
> +	video->pipe.stream_state = ISS_PIPELINE_STREAM_STOPPED;
> +
> +	video_set_drvdata(&video->video, video);
> +
> +	return 0;
> +}
> +
> +int omap4iss_video_register(struct iss_video *video, struct v4l2_device *vdev)
> +{
> +	int ret;
> +
> +	video->video.v4l2_dev = vdev;
> +
> +	ret = video_register_device(&video->video, VFL_TYPE_GRABBER, -1);
> +	if (ret < 0)
> +		printk(KERN_ERR "%s: could not register video device (%d)\n",
> +			__func__, ret);
> +
> +	return ret;
> +}
> +
> +void omap4iss_video_unregister(struct iss_video *video)
> +{
> +	if (video_is_registered(&video->video)) {
> +		media_entity_cleanup(&video->video.entity);
> +		video_unregister_device(&video->video);
> +	}
> +}
> diff --git a/drivers/media/video/omap4iss/iss_video.h b/drivers/media/video/omap4iss/iss_video.h
> new file mode 100644
> index 0000000..fc123b0
> --- /dev/null
> +++ b/drivers/media/video/omap4iss/iss_video.h
> @@ -0,0 +1,205 @@
> +/*
> + * iss_video.h
> + *
> + * TI OMAP4 ISS - Generic video node
> + *
> + * Copyright (C) 2011 Texas Instruments, Inc.
> + *
> + * Contacts: Sergio Aguirre <saaguirre@ti.com>
> + *
> + * 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.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
> + * 02110-1301 USA
> + */
> +
> +#ifndef OMAP4_ISS_VIDEO_H
> +#define OMAP4_ISS_VIDEO_H
> +
> +#include <linux/v4l2-mediabus.h>
> +#include <linux/version.h>
> +#include <media/media-entity.h>
> +#include <media/v4l2-dev.h>
> +#include <media/v4l2-fh.h>
> +#include <media/videobuf2-core.h>
> +#include <media/videobuf2-dma-contig.h>
> +
> +#define ISS_VIDEO_DRIVER_NAME		"issvideo"
> +#define ISS_VIDEO_DRIVER_VERSION	KERNEL_VERSION(0, 0, 1)
> +
> +struct iss_device;
> +struct iss_video;
> +struct v4l2_mbus_framefmt;
> +struct v4l2_pix_format;
> +
> +/*
> + * struct iss_format_info - ISS media bus format information
> + * @code: V4L2 media bus format code
> + * @truncated: V4L2 media bus format code for the same format truncated to 10
> + *	bits. Identical to @code if the format is 10 bits wide or less.
> + * @uncompressed: V4L2 media bus format code for the corresponding uncompressed
> + *	format. Identical to @code if the format is not DPCM compressed.
> + * @flavor: V4L2 media bus format code for the same pixel layout but
> + *	shifted to be 8 bits per pixel. =0 if format is not shiftable.
> + * @pixelformat: V4L2 pixel format FCC identifier
> + * @bpp: Bits per pixel
> + */
> +struct iss_format_info {
> +	enum v4l2_mbus_pixelcode code;
> +	enum v4l2_mbus_pixelcode truncated;
> +	enum v4l2_mbus_pixelcode uncompressed;
> +	enum v4l2_mbus_pixelcode flavor;
> +	u32 pixelformat;
> +	unsigned int bpp;
> +};
> +
> +enum iss_pipeline_stream_state {
> +	ISS_PIPELINE_STREAM_STOPPED = 0,
> +	ISS_PIPELINE_STREAM_CONTINUOUS = 1,
> +	ISS_PIPELINE_STREAM_SINGLESHOT = 2,
> +};
> +
> +enum iss_pipeline_state {
> +	/* The stream has been started on the input video node. */
> +	ISS_PIPELINE_STREAM_INPUT = 1,
> +	/* The stream has been started on the output video node. */
> +	ISS_PIPELINE_STREAM_OUTPUT = 2,
> +	/* At least one buffer is queued on the input video node. */
> +	ISS_PIPELINE_QUEUE_INPUT = 4,
> +	/* At least one buffer is queued on the output video node. */
> +	ISS_PIPELINE_QUEUE_OUTPUT = 8,
> +	/* The input entity is idle, ready to be started. */
> +	ISS_PIPELINE_IDLE_INPUT = 16,
> +	/* The output entity is idle, ready to be started. */
> +	ISS_PIPELINE_IDLE_OUTPUT = 32,
> +	/* The pipeline is currently streaming. */
> +	ISS_PIPELINE_STREAM = 64,
> +};
> +
> +struct iss_pipeline {
> +	struct media_pipeline pipe;
> +	spinlock_t lock;		/* Pipeline state and queue flags */
> +	unsigned int state;
> +	enum iss_pipeline_stream_state stream_state;
> +	struct iss_video *input;
> +	struct iss_video *output;
> +	atomic_t frame_number;
> +	bool do_propagation; /* of frame number */
> +	struct v4l2_fract max_timeperframe;
> +};
> +
> +#define to_iss_pipeline(__e) \
> +	container_of((__e)->pipe, struct iss_pipeline, pipe)
> +
> +static inline int iss_pipeline_ready(struct iss_pipeline *pipe)
> +{
> +	return pipe->state == (ISS_PIPELINE_STREAM_INPUT |
> +			       ISS_PIPELINE_STREAM_OUTPUT |
> +			       ISS_PIPELINE_QUEUE_INPUT |
> +			       ISS_PIPELINE_QUEUE_OUTPUT |
> +			       ISS_PIPELINE_IDLE_INPUT |
> +			       ISS_PIPELINE_IDLE_OUTPUT);
> +}
> +
> +/*
> + * struct iss_buffer - ISS buffer
> + * @buffer: ISS video buffer
> + * @iss_addr: Physical address of the buffer.
> + */
> +struct iss_buffer {
> +	/* common v4l buffer stuff -- must be first */
> +	struct vb2_buffer	vb;
> +	struct list_head	list;
> +	dma_addr_t iss_addr;
> +};
> +
> +#define to_iss_buffer(buf)	container_of(buf, struct iss_buffer, buffer)
> +
> +enum iss_video_dmaqueue_flags {
> +	/* Set if DMA queue becomes empty when ISS_PIPELINE_STREAM_CONTINUOUS */
> +	ISS_VIDEO_DMAQUEUE_UNDERRUN = (1 << 0),
> +	/* Set when queuing buffer to an empty DMA queue */
> +	ISS_VIDEO_DMAQUEUE_QUEUED = (1 << 1),
> +};
> +
> +#define iss_video_dmaqueue_flags_clr(video)	\
> +			({ (video)->dmaqueue_flags = 0; })
> +
> +/*
> + * struct iss_video_operations - ISS video operations
> + * @queue:	Resume streaming when a buffer is queued. Called on VIDIOC_QBUF
> + *		if there was no buffer previously queued.
> + */
> +struct iss_video_operations {
> +	int(*queue)(struct iss_video *video, struct iss_buffer *buffer);
> +};
> +
> +struct iss_video {
> +	struct video_device video;
> +	enum v4l2_buf_type type;
> +	struct media_pad pad;
> +
> +	struct mutex mutex;		/* format and crop settings */
> +	atomic_t active;
> +
> +	struct iss_device *iss;
> +
> +	unsigned int capture_mem;
> +	unsigned int bpl_alignment;	/* alignment value */
> +	unsigned int bpl_zero_padding;	/* whether the alignment is optional */
> +	unsigned int bpl_max;		/* maximum bytes per line value */
> +	unsigned int bpl_value;		/* bytes per line value */
> +	unsigned int bpl_padding;	/* padding at end of line */
> +
> +	/* Entity video node streaming */
> +	unsigned int streaming:1;
> +
> +	/* Pipeline state */
> +	struct iss_pipeline pipe;
> +	struct mutex stream_lock;	/* pipeline and stream states */
> +
> +	/* Video buffers queue */
> +	struct vb2_queue *queue;
> +	spinlock_t qlock;	/* Spinlock for dmaqueue */
> +	struct list_head dmaqueue;
> +	enum iss_video_dmaqueue_flags dmaqueue_flags;
> +	struct vb2_alloc_ctx *alloc_ctx;
> +
> +	const struct iss_video_operations *ops;
> +};
> +
> +#define to_iss_video(vdev)	container_of(vdev, struct iss_video, video)
> +
> +struct iss_video_fh {
> +	struct v4l2_fh vfh;
> +	struct iss_video *video;
> +	struct vb2_queue queue;
> +	struct v4l2_format format;
> +	struct v4l2_fract timeperframe;
> +};
> +
> +#define to_iss_video_fh(fh)	container_of(fh, struct iss_video_fh, vfh)
> +#define iss_video_queue_to_iss_video_fh(q) \
> +				container_of(q, struct iss_video_fh, queue)
> +
> +int omap4iss_video_init(struct iss_video *video, const char *name);
> +int omap4iss_video_register(struct iss_video *video,
> +			    struct v4l2_device *vdev);
> +void omap4iss_video_unregister(struct iss_video *video);
> +struct iss_buffer *omap4iss_video_buffer_next(struct iss_video *video,
> +					      unsigned int error);
> +struct media_pad *omap4iss_video_remote_pad(struct iss_video *video);
> +
> +const struct iss_format_info *
> +omap4iss_video_format_info(enum v4l2_mbus_pixelcode code);
> +
> +#endif /* OMAP4_ISS_VIDEO_H */
> -- 
> 1.7.7.4
> 

-- 
Sakari Ailus
e-mail: sakari.ailus@iki.fi	jabber/XMPP/Gmail: sailus@retiisi.org.uk

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

* Re: [PATCH v2 06/11] v4l: Add support for omap4iss driver
  2011-12-01  0:14   ` Sergio Aguirre
  (?)
  (?)
@ 2012-01-14 17:51   ` Sakari Ailus
  2012-01-25  8:58     ` Aguirre, Sergio
  -1 siblings, 1 reply; 65+ messages in thread
From: Sakari Ailus @ 2012-01-14 17:51 UTC (permalink / raw)
  To: Sergio Aguirre, Cc:linux-media; +Cc: linux-omap, laurent.pinchart

Hi Sergio,

Thanks again for the patch! My previous review escaped me too fast. The rest
is here.

On Wed, Nov 30, 2011 at 06:14:55PM -0600, Sergio Aguirre wrote:
> This adds a very simplistic driver to utilize the
> CSI2A interface inside the ISS subsystem in OMAP4,
> and dump the data to memory.
> 
> Tested so far on omap4430sdp w/ ES2.1 GP & w/ ES2.2 EMU.
> 
> Check newly added Documentation/video4linux/omap4_camera.txt
> for details.
> 
> Signed-off-by: Sergio Aguirre <saaguirre@ti.com>
> ---
>  Documentation/video4linux/omap4_camera.txt  |   60 ++
>  arch/arm/plat-omap/include/plat/omap4-iss.h |   42 +
>  drivers/media/video/Kconfig                 |   13 +
>  drivers/media/video/Makefile                |    1 +
>  drivers/media/video/omap4iss/Makefile       |    6 +
>  drivers/media/video/omap4iss/iss.c          | 1179 ++++++++++++++++++++++++
>  drivers/media/video/omap4iss/iss.h          |  133 +++
>  drivers/media/video/omap4iss/iss_csi2.c     | 1324 +++++++++++++++++++++++++++
>  drivers/media/video/omap4iss/iss_csi2.h     |  166 ++++
>  drivers/media/video/omap4iss/iss_csiphy.c   |  215 +++++
>  drivers/media/video/omap4iss/iss_csiphy.h   |   69 ++
>  drivers/media/video/omap4iss/iss_regs.h     |  238 +++++
>  drivers/media/video/omap4iss/iss_video.c    | 1192 ++++++++++++++++++++++++
>  drivers/media/video/omap4iss/iss_video.h    |  205 +++++
>  14 files changed, 4843 insertions(+), 0 deletions(-)
>  create mode 100644 Documentation/video4linux/omap4_camera.txt
>  create mode 100644 arch/arm/plat-omap/include/plat/omap4-iss.h
>  create mode 100644 drivers/media/video/omap4iss/Makefile
>  create mode 100644 drivers/media/video/omap4iss/iss.c
>  create mode 100644 drivers/media/video/omap4iss/iss.h
>  create mode 100644 drivers/media/video/omap4iss/iss_csi2.c
>  create mode 100644 drivers/media/video/omap4iss/iss_csi2.h
>  create mode 100644 drivers/media/video/omap4iss/iss_csiphy.c
>  create mode 100644 drivers/media/video/omap4iss/iss_csiphy.h
>  create mode 100644 drivers/media/video/omap4iss/iss_regs.h
>  create mode 100644 drivers/media/video/omap4iss/iss_video.c
>  create mode 100644 drivers/media/video/omap4iss/iss_video.h
> 
> diff --git a/Documentation/video4linux/omap4_camera.txt b/Documentation/video4linux/omap4_camera.txt
> new file mode 100644
> index 0000000..a60c80f
> --- /dev/null
> +++ b/Documentation/video4linux/omap4_camera.txt
> @@ -0,0 +1,60 @@
> +                              OMAP4 ISS Driver
> +                              ================
> +
> +Introduction
> +------------
> +
> +The OMAP44XX family of chips contains the Imaging SubSystem (a.k.a. ISS),
> +Which contains several components that can be categorized in 3 big groups:
> +
> +- Interfaces (2 Interfaces: CSI2-A & CSI2-B/CCP2)
> +- ISP (Image Signal Processor)
> +- SIMCOP (Still Image Coprocessor)
> +
> +For more information, please look in [1] for latest version of:
> +	"OMAP4430 Multimedia Device Silicon Revision 2.x"
> +
> +As of Revision L, the ISS is described in detail in section 8.
> +
> +This driver is supporting _only_ the CSI2-A interface for now.
> +
> +It makes use of the Media Controller framework [2], and inherited most of the
> +code from OMAP3 ISP driver (found under drivers/media/video/omap3isp/*), except
> +that it doesn't need an IOMMU now for ISS buffers memory mapping.
> +
> +Supports usage of MMAP buffers only (for now).
> +
> +IMPORTANT: It is recommended to have this patchset:
> +  Contiguous Memory Allocator (v15) [3].
> +
> +Tested platforms
> +----------------
> +
> +- OMAP4430SDP, w/ ES2.1 GP & SEVM4430-CAM-V1-0 (Contains IMX060 & OV5640, in
> +  which only the last one is supported, outputting YUV422 frames).
> +
> +- PandaBoard, Rev. A2, w/ OMAP4430 ES2.1 GP & OV adapter board, tested with
> +  following sensors:
> +  * OV5640
> +  * OV5650
> +
> +- Tested on mainline kernel:
> +
> +	http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=summary
> +
> +  Tag: v3.0 (commit 02f8c6aee8df3cdc935e9bdd4f2d020306035dbe)
> +
> +File list
> +---------
> +drivers/media/video/omap4iss/
> +
> +References
> +----------
> +
> +[1] http://focus.ti.com/general/docs/wtbu/wtbudocumentcenter.tsp?navigationId=12037&templateId=6123#62
> +[2] http://lwn.net/Articles/420485/
> +[3] http://lwn.net/Articles/455801/
> +--
> +Author: Sergio Aguirre <saaguirre@ti.com>
> +Copyright (C) 2011, Texas Instruments
> +
> diff --git a/arch/arm/plat-omap/include/plat/omap4-iss.h b/arch/arm/plat-omap/include/plat/omap4-iss.h
> new file mode 100644
> index 0000000..3a1c6b6
> --- /dev/null
> +++ b/arch/arm/plat-omap/include/plat/omap4-iss.h
> @@ -0,0 +1,42 @@
> +#ifndef ARCH_ARM_PLAT_OMAP4_ISS_H
> +#define ARCH_ARM_PLAT_OMAP4_ISS_H
> +
> +#include <linux/i2c.h>
> +
> +struct iss_device;
> +
> +enum iss_interface_type {
> +	ISS_INTERFACE_CSI2A_PHY1,
> +};
> +
> +/**
> + * struct iss_csi2_platform_data - CSI2 interface platform data
> + * @crc: Enable the cyclic redundancy check
> + * @vpclk_div: Video port output clock control
> + */
> +struct iss_csi2_platform_data {
> +	unsigned crc:1;
> +	unsigned vpclk_div:2;
> +};
> +
> +struct iss_subdev_i2c_board_info {
> +	struct i2c_board_info *board_info;
> +	int i2c_adapter_id;
> +};
> +
> +struct iss_v4l2_subdevs_group {
> +	struct iss_subdev_i2c_board_info *subdevs;
> +	enum iss_interface_type interface;
> +	union {
> +		struct iss_csi2_platform_data csi2;
> +	} bus; /* gcc < 4.6.0 chokes on anonymous union initializers */
> +};
> +
> +struct iss_platform_data {
> +	struct iss_v4l2_subdevs_group *subdevs;
> +	void (*set_constraints)(struct iss_device *iss, bool enable);
> +};
> +
> +extern int omap4_init_camera(struct iss_platform_data *pdata,
> +			     struct omap_board_data *bdata);
> +#endif
> diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
> index b303a3f..ae2a99d 100644
> --- a/drivers/media/video/Kconfig
> +++ b/drivers/media/video/Kconfig
> @@ -796,6 +796,19 @@ config VIDEO_OMAP3_DEBUG
>  	---help---
>  	  Enable debug messages on OMAP 3 camera controller driver.
>  
> +config VIDEO_OMAP4
> +	tristate "OMAP 4 Camera support (EXPERIMENTAL)"
> +	depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP4 && EXPERIMENTAL
> +	select VIDEOBUF2_DMA_CONTIG
> +	---help---
> +	  Driver for an OMAP 4 ISS controller.
> +
> +config VIDEO_OMAP4_DEBUG
> +	bool "OMAP 4 Camera debug messages"
> +	depends on VIDEO_OMAP4
> +	---help---
> +	  Enable debug messages on OMAP 4 ISS controller driver.
> +
>  config SOC_CAMERA
>  	tristate "SoC camera support"
>  	depends on VIDEO_V4L2 && HAS_DMA && I2C
> diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
> index 117f9c4..f02a4c4 100644
> --- a/drivers/media/video/Makefile
> +++ b/drivers/media/video/Makefile
> @@ -140,6 +140,7 @@ obj-$(CONFIG_VIDEO_MMP_CAMERA) += marvell-ccic/
>  obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o
>  
>  obj-$(CONFIG_VIDEO_OMAP3)	+= omap3isp/
> +obj-$(CONFIG_VIDEO_OMAP4)	+= omap4iss/
>  
>  obj-$(CONFIG_USB_ZR364XX)       += zr364xx.o
>  obj-$(CONFIG_USB_STKWEBCAM)     += stkwebcam.o
> diff --git a/drivers/media/video/omap4iss/Makefile b/drivers/media/video/omap4iss/Makefile
> new file mode 100644
> index 0000000..1d3b0a7
> --- /dev/null
> +++ b/drivers/media/video/omap4iss/Makefile
> @@ -0,0 +1,6 @@
> +# Makefile for OMAP4 ISS driver
> +
> +omap4-iss-objs += \
> +	iss.o iss_csi2.o iss_csiphy.o iss_video.o
> +
> +obj-$(CONFIG_VIDEO_OMAP4) += omap4-iss.o
> diff --git a/drivers/media/video/omap4iss/iss.c b/drivers/media/video/omap4iss/iss.c
> new file mode 100644
> index 0000000..255738b
> --- /dev/null
> +++ b/drivers/media/video/omap4iss/iss.c
> @@ -0,0 +1,1179 @@
> +/*
> + * V4L2 Driver for OMAP4 ISS
> + *
> + * Copyright (C) 2011, Texas Instruments
> + *
> + * Author: Sergio Aguirre <saaguirre@ti.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/i2c.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/sched.h>
> +#include <linux/vmalloc.h>
> +
> +#include <media/v4l2-common.h>
> +#include <media/v4l2-device.h>
> +
> +#include "iss.h"
> +#include "iss_regs.h"
> +
> +static void iss_save_ctx(struct iss_device *iss);
> +
> +static void iss_restore_ctx(struct iss_device *iss);
> +
> +/* Structure for saving/restoring ISS module registers */
> +static struct iss_reg iss_reg_list[] = {
> +	{OMAP4_ISS_MEM_TOP, ISS_HL_SYSCONFIG, 0},
> +	{OMAP4_ISS_MEM_TOP, ISS_CTRL, 0},
> +	{OMAP4_ISS_MEM_TOP, ISS_CLKCTRL, 0},
> +	{0, ISS_TOK_TERM, 0}
> +};
> +
> +/*
> + * omap4iss_flush - Post pending L3 bus writes by doing a register readback
> + * @iss: OMAP4 ISS device
> + *
> + * In order to force posting of pending writes, we need to write and
> + * readback the same register, in this case the revision register.
> + *
> + * See this link for reference:
> + *   http://www.mail-archive.com/linux-omap@vger.kernel.org/msg08149.html
> + */
> +void omap4iss_flush(struct iss_device *iss)
> +{
> +	writel(0, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_REVISION);
> +	readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_REVISION);
> +}
> +
> +/*
> + * iss_enable_interrupts - Enable ISS interrupts.
> + * @iss: OMAP4 ISS device
> + */
> +static void iss_enable_interrupts(struct iss_device *iss)
> +{
> +	static const u32 irq = ISS_HL_IRQ_CSIA;
> +
> +	/* Enable HL interrupts */
> +	writel(irq, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQSTATUS_5);
> +	writel(irq, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQENABLE_5_SET);
> +}
> +
> +/*
> + * iss_disable_interrupts - Disable ISS interrupts.
> + * @iss: OMAP4 ISS device
> + */
> +static void iss_disable_interrupts(struct iss_device *iss)
> +{
> +	writel(-1, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQENABLE_5_CLR);
> +}
> +
> +static inline void iss_isr_dbg(struct iss_device *iss, u32 irqstatus)
> +{
> +	static const char *name[] = {
> +		"ISP_IRQ0",
> +		"ISP_IRQ1",
> +		"ISP_IRQ2",
> +		"ISP_IRQ3",
> +		"CSIA_IRQ",
> +		"CSIB_IRQ",
> +		"CCP2_IRQ0",
> +		"CCP2_IRQ1",
> +		"CCP2_IRQ2",
> +		"CCP2_IRQ3",
> +		"CBUFF_IRQ",
> +		"BTE_IRQ",
> +		"SIMCOP_IRQ0",
> +		"SIMCOP_IRQ1",
> +		"SIMCOP_IRQ2",
> +		"SIMCOP_IRQ3",
> +		"CCP2_IRQ8",
> +		"HS_VS_IRQ",
> +		"res18",
> +		"res19",
> +		"res20",
> +		"res21",
> +		"res22",
> +		"res23",
> +		"res24",
> +		"res25",
> +		"res26",
> +		"res27",
> +		"res28",
> +		"res29",
> +		"res30",
> +		"res31",
> +	};
> +	int i;
> +
> +	dev_dbg(iss->dev, "ISS IRQ: ");
> +
> +	for (i = 0; i < ARRAY_SIZE(name); i++) {
> +		if ((1 << i) & irqstatus)
> +			printk(KERN_CONT "%s ", name[i]);
> +	}
> +	printk(KERN_CONT "\n");
> +}
> +
> +/*
> + * iss_isr - Interrupt Service Routine for ISS module.
> + * @irq: Not used currently.
> + * @_iss: Pointer to the OMAP4 ISS device
> + *
> + * Handles the corresponding callback if plugged in.
> + *
> + * Returns IRQ_HANDLED when IRQ was correctly handled, or IRQ_NONE when the
> + * IRQ wasn't handled.
> + */
> +static irqreturn_t iss_isr(int irq, void *_iss)
> +{
> +	struct iss_device *iss = _iss;
> +	u32 irqstatus;
> +
> +	irqstatus = readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQSTATUS_5);
> +	writel(irqstatus, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQSTATUS_5);
> +
> +	if (irqstatus & ISS_HL_IRQ_CSIA)
> +		omap4iss_csi2_isr(&iss->csi2a);
> +
> +	omap4iss_flush(iss);
> +
> +#if defined(DEBUG) && defined(ISS_ISR_DEBUG)
> +	iss_isr_dbg(iss, irqstatus);
> +#endif
> +
> +	return IRQ_HANDLED;
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * Pipeline power management
> + *
> + * Entities must be powered up when part of a pipeline that contains at least
> + * one open video device node.
> + *
> + * To achieve this use the entity use_count field to track the number of users.
> + * For entities corresponding to video device nodes the use_count field stores
> + * the users count of the node. For entities corresponding to subdevs the
> + * use_count field stores the total number of users of all video device nodes
> + * in the pipeline.
> + *
> + * The omap4iss_pipeline_pm_use() function must be called in the open() and
> + * close() handlers of video device nodes. It increments or decrements the use
> + * count of all subdev entities in the pipeline.
> + *
> + * To react to link management on powered pipelines, the link setup notification
> + * callback updates the use count of all entities in the source and sink sides
> + * of the link.
> + */
> +
> +/*
> + * iss_pipeline_pm_use_count - Count the number of users of a pipeline
> + * @entity: The entity
> + *
> + * Return the total number of users of all video device nodes in the pipeline.
> + */
> +static int iss_pipeline_pm_use_count(struct media_entity *entity)
> +{
> +	struct media_entity_graph graph;
> +	int use = 0;
> +
> +	media_entity_graph_walk_start(&graph, entity);
> +
> +	while ((entity = media_entity_graph_walk_next(&graph))) {
> +		if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE)
> +			use += entity->use_count;
> +	}
> +
> +	return use;
> +}
> +
> +/*
> + * iss_pipeline_pm_power_one - Apply power change to an entity
> + * @entity: The entity
> + * @change: Use count change
> + *
> + * Change the entity use count by @change. If the entity is a subdev update its
> + * power state by calling the core::s_power operation when the use count goes
> + * from 0 to != 0 or from != 0 to 0.
> + *
> + * Return 0 on success or a negative error code on failure.
> + */
> +static int iss_pipeline_pm_power_one(struct media_entity *entity, int change)
> +{
> +	struct v4l2_subdev *subdev;
> +
> +	subdev = media_entity_type(entity) == MEDIA_ENT_T_V4L2_SUBDEV
> +	       ? media_entity_to_v4l2_subdev(entity) : NULL;
> +
> +	if (entity->use_count == 0 && change > 0 && subdev != NULL) {
> +		int ret;
> +
> +		ret = v4l2_subdev_call(subdev, core, s_power, 1);
> +		if (ret < 0 && ret != -ENOIOCTLCMD)
> +			return ret;
> +	}
> +
> +	entity->use_count += change;
> +	WARN_ON(entity->use_count < 0);
> +
> +	if (entity->use_count == 0 && change < 0 && subdev != NULL)
> +		v4l2_subdev_call(subdev, core, s_power, 0);
> +
> +	return 0;
> +}
> +
> +/*
> + * iss_pipeline_pm_power - Apply power change to all entities in a pipeline
> + * @entity: The entity
> + * @change: Use count change
> + *
> + * Walk the pipeline to update the use count and the power state of all non-node
> + * entities.
> + *
> + * Return 0 on success or a negative error code on failure.
> + */
> +static int iss_pipeline_pm_power(struct media_entity *entity, int change)
> +{
> +	struct media_entity_graph graph;
> +	struct media_entity *first = entity;
> +	int ret = 0;
> +
> +	if (!change)
> +		return 0;
> +
> +	media_entity_graph_walk_start(&graph, entity);
> +
> +	while (!ret && (entity = media_entity_graph_walk_next(&graph)))
> +		if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE)
> +			ret = iss_pipeline_pm_power_one(entity, change);
> +
> +	if (!ret)
> +		return 0;
> +
> +	media_entity_graph_walk_start(&graph, first);
> +
> +	while ((first = media_entity_graph_walk_next(&graph))
> +	       && first != entity)
> +		if (media_entity_type(first) != MEDIA_ENT_T_DEVNODE)
> +			iss_pipeline_pm_power_one(first, -change);
> +
> +	return ret;
> +}
> +
> +/*
> + * omap4iss_pipeline_pm_use - Update the use count of an entity
> + * @entity: The entity
> + * @use: Use (1) or stop using (0) the entity
> + *
> + * Update the use count of all entities in the pipeline and power entities on or
> + * off accordingly.
> + *
> + * Return 0 on success or a negative error code on failure. Powering entities
> + * off is assumed to never fail. No failure can occur when the use parameter is
> + * set to 0.
> + */
> +int omap4iss_pipeline_pm_use(struct media_entity *entity, int use)
> +{
> +	int change = use ? 1 : -1;
> +	int ret;
> +
> +	mutex_lock(&entity->parent->graph_mutex);
> +
> +	/* Apply use count to node. */
> +	entity->use_count += change;
> +	WARN_ON(entity->use_count < 0);
> +
> +	/* Apply power change to connected non-nodes. */
> +	ret = iss_pipeline_pm_power(entity, change);
> +	if (ret < 0)
> +		entity->use_count -= change;
> +
> +	mutex_unlock(&entity->parent->graph_mutex);
> +
> +	return ret;
> +}
> +
> +/*
> + * iss_pipeline_link_notify - Link management notification callback
> + * @source: Pad at the start of the link
> + * @sink: Pad at the end of the link
> + * @flags: New link flags that will be applied
> + *
> + * React to link management on powered pipelines by updating the use count of
> + * all entities in the source and sink sides of the link. Entities are powered
> + * on or off accordingly.
> + *
> + * Return 0 on success or a negative error code on failure. Powering entities
> + * off is assumed to never fail. This function will not fail for disconnection
> + * events.
> + */
> +static int iss_pipeline_link_notify(struct media_pad *source,
> +				    struct media_pad *sink, u32 flags)
> +{
> +	int source_use = iss_pipeline_pm_use_count(source->entity);
> +	int sink_use = iss_pipeline_pm_use_count(sink->entity);
> +	int ret;
> +
> +	if (!(flags & MEDIA_LNK_FL_ENABLED)) {
> +		/* Powering off entities is assumed to never fail. */
> +		iss_pipeline_pm_power(source->entity, -sink_use);
> +		iss_pipeline_pm_power(sink->entity, -source_use);
> +		return 0;
> +	}
> +
> +	ret = iss_pipeline_pm_power(source->entity, sink_use);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = iss_pipeline_pm_power(sink->entity, source_use);
> +	if (ret < 0)
> +		iss_pipeline_pm_power(source->entity, -sink_use);
> +
> +	return ret;
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * Pipeline stream management
> + */
> +
> +/*
> + * iss_pipeline_enable - Enable streaming on a pipeline
> + * @pipe: ISS pipeline
> + * @mode: Stream mode (single shot or continuous)
> + *
> + * Walk the entities chain starting at the pipeline output video node and start
> + * all modules in the chain in the given mode.
> + *
> + * Return 0 if successful, or the return value of the failed video::s_stream
> + * operation otherwise.
> + */
> +static int iss_pipeline_enable(struct iss_pipeline *pipe,
> +			       enum iss_pipeline_stream_state mode)
> +{
> +	struct media_entity *entity;
> +	struct media_pad *pad;
> +	struct v4l2_subdev *subdev;
> +	unsigned long flags;
> +	int ret = 0;
> +
> +	spin_lock_irqsave(&pipe->lock, flags);
> +	pipe->state &= ~(ISS_PIPELINE_IDLE_INPUT | ISS_PIPELINE_IDLE_OUTPUT);
> +	spin_unlock_irqrestore(&pipe->lock, flags);
> +
> +	pipe->do_propagation = false;
> +
> +	entity = &pipe->output->video.entity;
> +	while (1) {
> +		pad = &entity->pads[0];
> +		if (!(pad->flags & MEDIA_PAD_FL_SINK))
> +			break;
> +
> +		pad = media_entity_remote_source(pad);
> +		if (pad == NULL ||
> +		    media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
> +			break;
> +
> +		entity = pad->entity;
> +		subdev = media_entity_to_v4l2_subdev(entity);
> +
> +		ret = v4l2_subdev_call(subdev, video, s_stream, mode);
> +		if (ret < 0 && ret != -ENOIOCTLCMD)
> +			break;
> +	}
> +
> +	return ret;
> +}
> +
> +/*
> + * iss_pipeline_disable - Disable streaming on a pipeline
> + * @pipe: ISS pipeline
> + *
> + * Walk the entities chain starting at the pipeline output video node and stop
> + * all modules in the chain. Wait synchronously for the modules to be stopped if
> + * necessary.
> + */
> +static int iss_pipeline_disable(struct iss_pipeline *pipe)
> +{
> +	struct media_entity *entity;
> +	struct media_pad *pad;
> +	struct v4l2_subdev *subdev;
> +	int failure = 0;
> +
> +	entity = &pipe->output->video.entity;
> +	while (1) {
> +		pad = &entity->pads[0];
> +		if (!(pad->flags & MEDIA_PAD_FL_SINK))
> +			break;
> +
> +		pad = media_entity_remote_source(pad);
> +		if (pad == NULL ||
> +		    media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
> +			break;
> +
> +		entity = pad->entity;
> +		subdev = media_entity_to_v4l2_subdev(entity);
> +
> +		v4l2_subdev_call(subdev, video, s_stream, 0);
> +	}
> +
> +	return failure;
> +}
> +
> +/*
> + * omap4iss_pipeline_set_stream - Enable/disable streaming on a pipeline
> + * @pipe: ISS pipeline
> + * @state: Stream state (stopped, single shot or continuous)
> + *
> + * Set the pipeline to the given stream state. Pipelines can be started in
> + * single-shot or continuous mode.
> + *
> + * Return 0 if successful, or the return value of the failed video::s_stream
> + * operation otherwise. The pipeline state is not updated when the operation
> + * fails, except when stopping the pipeline.
> + */
> +int omap4iss_pipeline_set_stream(struct iss_pipeline *pipe,
> +				 enum iss_pipeline_stream_state state)
> +{
> +	int ret;
> +
> +	if (state == ISS_PIPELINE_STREAM_STOPPED)
> +		ret = iss_pipeline_disable(pipe);
> +	else
> +		ret = iss_pipeline_enable(pipe, state);
> +
> +	if (ret == 0 || state == ISS_PIPELINE_STREAM_STOPPED)
> +		pipe->stream_state = state;
> +
> +	return ret;
> +}
> +
> +/*
> + * iss_pipeline_is_last - Verify if entity has an enabled link to the output
> + *			  video node
> + * @me: ISS module's media entity
> + *
> + * Returns 1 if the entity has an enabled link to the output video node or 0
> + * otherwise. It's true only while pipeline can have no more than one output
> + * node.
> + */
> +static int iss_pipeline_is_last(struct media_entity *me)
> +{
> +	struct iss_pipeline *pipe;
> +	struct media_pad *pad;
> +
> +	if (!me->pipe)
> +		return 0;
> +	pipe = to_iss_pipeline(me);
> +	if (pipe->stream_state == ISS_PIPELINE_STREAM_STOPPED)
> +		return 0;
> +	pad = media_entity_remote_source(&pipe->output->pad);
> +	return pad->entity == me;
> +}
> +
> +static int iss_reset(struct iss_device *iss)
> +{
> +	unsigned long timeout = 0;
> +
> +	writel(readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_SYSCONFIG) |
> +		ISS_HL_SYSCONFIG_SOFTRESET,
> +		iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_SYSCONFIG);
> +
> +	while (readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_SYSCONFIG) &
> +			ISS_HL_SYSCONFIG_SOFTRESET) {
> +		if (timeout++ > 10000) {
> +			dev_alert(iss->dev, "cannot reset ISS\n");
> +			return -ETIMEDOUT;
> +		}
> +		udelay(1);
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * iss_save_context - Saves the values of the ISS module registers.
> + * @iss: OMAP4 ISS device
> + * @reg_list: Structure containing pairs of register address and value to
> + *            modify on OMAP.
> + */
> +static void
> +iss_save_context(struct iss_device *iss, struct iss_reg *reg_list)
> +{
> +	struct iss_reg *next = reg_list;
> +
> +	for (; next->reg != ISS_TOK_TERM; next++)
> +		next->val = readl(iss->regs[next->mmio_range] + next->reg);
> +}
> +
> +/*
> + * iss_restore_context - Restores the values of the ISS module registers.
> + * @iss: OMAP4 ISS device
> + * @reg_list: Structure containing pairs of register address and value to
> + *            modify on OMAP.
> + */
> +static void
> +iss_restore_context(struct iss_device *iss, struct iss_reg *reg_list)
> +{
> +	struct iss_reg *next = reg_list;
> +
> +	for (; next->reg != ISS_TOK_TERM; next++)
> +		writel(next->val, iss->regs[next->mmio_range] + next->reg);
> +}
> +
> +/*
> + * iss_save_ctx - Saves ISS context.
> + * @iss: OMAP4 ISS device
> + *
> + * Routine for saving the context of each module in the ISS.
> + */
> +static void iss_save_ctx(struct iss_device *iss)
> +{
> +	iss_save_context(iss, iss_reg_list);
> +}

Do you really, really need to save context related data? Couldn't you
initialise the device the usual way when e.g. returning from suspended
state?

> +/*
> + * iss_restore_ctx - Restores ISS context.
> + * @iss: OMAP4 ISS device
> + *
> + * Routine for restoring the context of each module in the ISS.
> + */
> +static void iss_restore_ctx(struct iss_device *iss)
> +{
> +	iss_restore_context(iss, iss_reg_list);
> +}
> +
> +/*
> + * iss_module_sync_idle - Helper to sync module with its idle state
> + * @me: ISS submodule's media entity
> + * @wait: ISS submodule's wait queue for streamoff/interrupt synchronization
> + * @stopping: flag which tells module wants to stop
> + *
> + * This function checks if ISS submodule needs to wait for next interrupt. If
> + * yes, makes the caller to sleep while waiting for such event.
> + */
> +int omap4iss_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait,
> +			      atomic_t *stopping)
> +{
> +	struct iss_pipeline *pipe = to_iss_pipeline(me);
> +
> +	if (pipe->stream_state == ISS_PIPELINE_STREAM_STOPPED ||
> +	    (pipe->stream_state == ISS_PIPELINE_STREAM_SINGLESHOT &&
> +	     !iss_pipeline_ready(pipe)))
> +		return 0;
> +
> +	/*
> +	 * atomic_set() doesn't include memory barrier on ARM platform for SMP
> +	 * scenario. We'll call it here to avoid race conditions.
> +	 */
> +	atomic_set(stopping, 1);
> +	smp_mb();
> +
> +	/*
> +	 * If module is the last one, it's writing to memory. In this case,
> +	 * it's necessary to check if the module is already paused due to
> +	 * DMA queue underrun or if it has to wait for next interrupt to be
> +	 * idle.
> +	 * If it isn't the last one, the function won't sleep but *stopping
> +	 * will still be set to warn next submodule caller's interrupt the
> +	 * module wants to be idle.
> +	 */
> +	if (iss_pipeline_is_last(me)) {
> +		struct iss_video *video = pipe->output;
> +		unsigned long flags;
> +
> +		spin_lock_irqsave(&video->qlock, flags);
> +		if (video->dmaqueue_flags & ISS_VIDEO_DMAQUEUE_UNDERRUN) {
> +			spin_unlock_irqrestore(&video->qlock, flags);
> +			atomic_set(stopping, 0);
> +			smp_mb();
> +			return 0;
> +		}
> +		spin_unlock_irqrestore(&video->qlock, flags);
> +		if (!wait_event_timeout(*wait, !atomic_read(stopping),
> +					msecs_to_jiffies(1000))) {
> +			atomic_set(stopping, 0);
> +			smp_mb();
> +			return -ETIMEDOUT;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * omap4iss_module_sync_is_stopped - Helper to verify if module was stopping
> + * @wait: ISS submodule's wait queue for streamoff/interrupt synchronization
> + * @stopping: flag which tells module wants to stop
> + *
> + * This function checks if ISS submodule was stopping. In case of yes, it
> + * notices the caller by setting stopping to 0 and waking up the wait queue.
> + * Returns 1 if it was stopping or 0 otherwise.
> + */
> +int omap4iss_module_sync_is_stopping(wait_queue_head_t *wait,
> +				     atomic_t *stopping)
> +{
> +	if (atomic_cmpxchg(stopping, 1, 0)) {
> +		wake_up(wait);
> +		return 1;
> +	}
> +
> +	return 0;
> +}
> +
> +/* --------------------------------------------------------------------------
> + * Clock management
> + */
> +
> +#define ISS_CLKCTRL_MASK	(ISS_CLKCTRL_CSI2_A)
> +
> +static int __iss_subclk_update(struct iss_device *iss)
> +{
> +	u32 clk = 0;
> +	int ret = 0, timeout = 1000;
> +
> +	if (iss->subclk_resources & OMAP4_ISS_SUBCLK_CSI2_A)
> +		clk |= ISS_CLKCTRL_CSI2_A;
> +
> +	writel((readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_CLKCTRL) &
> +		~ISS_CLKCTRL_MASK) | clk,
> +		iss->regs[OMAP4_ISS_MEM_TOP] + ISS_CLKCTRL);
> +
> +	/* Wait for HW assertion */
> +	while (timeout-- > 0) {

The value must be decremented before the comparison. Otherwise the result
could be negative, and the check a few lines below will fail even if the
timeout happened.

I guess it's not a crime to use a for loop either in situations like this.

> +		udelay(1);
> +		if ((readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_CLKSTAT) &
> +		     ISS_CLKCTRL_MASK) == clk)
> +			break;
> +	}
> +
> +	if (!timeout)
> +		ret = -EBUSY;
> +
> +	return ret;
> +}
> +
> +int omap4iss_subclk_enable(struct iss_device *iss,
> +			    enum iss_subclk_resource res)
> +{
> +	iss->subclk_resources |= res;
> +
> +	return __iss_subclk_update(iss);
> +}
> +
> +int omap4iss_subclk_disable(struct iss_device *iss,
> +			     enum iss_subclk_resource res)
> +{
> +	iss->subclk_resources &= ~res;
> +
> +	return __iss_subclk_update(iss);
> +}
> +
> +/*
> + * iss_enable_clocks - Enable ISS clocks
> + * @iss: OMAP4 ISS device
> + *
> + * Return 0 if successful, or clk_enable return value if any of tthem fails.
> + */
> +static int iss_enable_clocks(struct iss_device *iss)
> +{
> +	int r;
> +
> +	r = clk_enable(iss->iss_fck);
> +	if (r) {
> +		dev_err(iss->dev, "clk_enable iss_fck failed\n");
> +		goto out_clk_enable_fck;

You can return the error here and your error handling below becomes more
simple.

> +	}
> +
> +	r = clk_enable(iss->iss_ctrlclk);
> +	if (r) {
> +		dev_err(iss->dev, "clk_enable iss_ctrlclk failed\n");
> +		goto out_clk_enable_ctrlclk;
> +	}
> +	return 0;
> +
> +out_clk_enable_ctrlclk:
> +	clk_disable(iss->iss_fck);
> +out_clk_enable_fck:
> +	return r;
> +}
> +
> +/*
> + * iss_disable_clocks - Disable ISS clocks
> + * @iss: OMAP4 ISS device
> + */
> +static void iss_disable_clocks(struct iss_device *iss)
> +{
> +	clk_disable(iss->iss_ctrlclk);
> +	clk_disable(iss->iss_fck);
> +}
> +
> +static void iss_put_clocks(struct iss_device *iss)
> +{
> +	if (iss->iss_fck) {
> +		clk_put(iss->iss_fck);
> +		iss->iss_fck = NULL;
> +	}
> +
> +	if (iss->iss_ctrlclk) {
> +		clk_put(iss->iss_ctrlclk);
> +		iss->iss_ctrlclk = NULL;
> +	}
> +}
> +
> +static int iss_get_clocks(struct iss_device *iss)
> +{
> +	iss->iss_fck = clk_get(iss->dev, "iss_fck");
> +	if (IS_ERR(iss->iss_fck)) {
> +		dev_err(iss->dev, "Unable to get iss_fck clock info\n");
> +		iss_put_clocks(iss);
> +		return PTR_ERR(iss->iss_fck);
> +	}
> +
> +	iss->iss_ctrlclk = clk_get(iss->dev, "iss_ctrlclk");
> +	if (IS_ERR(iss->iss_ctrlclk)) {
> +		dev_err(iss->dev, "Unable to get iss_ctrlclk clock info\n");
> +		iss_put_clocks(iss);
> +		return PTR_ERR(iss->iss_fck);
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * omap4iss_get - Acquire the ISS resource.
> + *
> + * Initializes the clocks for the first acquire.
> + *
> + * Increment the reference count on the ISS. If the first reference is taken,
> + * enable clocks and power-up all submodules.
> + *
> + * Return a pointer to the ISS device structure, or NULL if an error occurred.
> + */
> +struct iss_device *omap4iss_get(struct iss_device *iss)
> +{
> +	struct iss_device *__iss = iss;
> +
> +	if (iss == NULL)
> +		return NULL;
> +
> +	mutex_lock(&iss->iss_mutex);
> +	if (iss->ref_count > 0)
> +		goto out;
> +
> +	if (iss_enable_clocks(iss) < 0) {
> +		__iss = NULL;
> +		goto out;
> +	}
> +
> +	/* We don't want to restore context before saving it! */
> +	if (iss->has_context)
> +		iss_restore_ctx(iss);
> +	else
> +		iss->has_context = 1;
> +
> +	iss_enable_interrupts(iss);
> +
> +out:
> +	if (__iss != NULL)
> +		iss->ref_count++;
> +	mutex_unlock(&iss->iss_mutex);
> +
> +	return __iss;
> +}
> +
> +/*
> + * omap4iss_put - Release the ISS
> + *
> + * Decrement the reference count on the ISS. If the last reference is released,
> + * power-down all submodules, disable clocks and free temporary buffers.
> + */
> +void omap4iss_put(struct iss_device *iss)
> +{
> +	if (iss == NULL)
> +		return;
> +
> +	mutex_lock(&iss->iss_mutex);
> +	BUG_ON(iss->ref_count == 0);
> +	if (--iss->ref_count == 0) {
> +		iss_disable_interrupts(iss);
> +		iss_save_ctx(iss);
> +		iss_disable_clocks(iss);
> +	}
> +	mutex_unlock(&iss->iss_mutex);
> +}
> +
> +static int iss_map_mem_resource(struct platform_device *pdev,
> +				struct iss_device *iss,
> +				enum iss_mem_resources res)
> +{
> +	struct resource *mem;
> +
> +	/* request the mem region for the camera registers */
> +
> +	mem = platform_get_resource(pdev, IORESOURCE_MEM, res);
> +	if (!mem) {
> +		dev_err(iss->dev, "no mem resource?\n");
> +		return -ENODEV;
> +	}
> +
> +	if (!request_mem_region(mem->start, resource_size(mem), pdev->name)) {
> +		dev_err(iss->dev,
> +			"cannot reserve camera register I/O region\n");
> +		return -ENODEV;
> +	}
> +	iss->res[res] = mem;
> +
> +	/* map the region */
> +	iss->regs[res] = ioremap_nocache(mem->start, resource_size(mem));
> +	if (!iss->regs[res]) {
> +		dev_err(iss->dev, "cannot map camera register I/O region\n");
> +		return -ENODEV;
> +	}
> +
> +	return 0;
> +}
> +
> +static void iss_unregister_entities(struct iss_device *iss)
> +{
> +	omap4iss_csi2_unregister_entities(&iss->csi2a);
> +
> +	v4l2_device_unregister(&iss->v4l2_dev);
> +	media_device_unregister(&iss->media_dev);
> +}
> +
> +/*
> + * iss_register_subdev_group - Register a group of subdevices
> + * @iss: OMAP4 ISS device
> + * @board_info: I2C subdevs board information array
> + *
> + * Register all I2C subdevices in the board_info array. The array must be
> + * terminated by a NULL entry, and the first entry must be the sensor.
> + *
> + * Return a pointer to the sensor media entity if it has been successfully
> + * registered, or NULL otherwise.
> + */
> +static struct v4l2_subdev *
> +iss_register_subdev_group(struct iss_device *iss,
> +		     struct iss_subdev_i2c_board_info *board_info)
> +{
> +	struct v4l2_subdev *sensor = NULL;
> +	unsigned int first;
> +
> +	if (board_info->board_info == NULL)
> +		return NULL;
> +
> +	for (first = 1; board_info->board_info; ++board_info, first = 0) {
> +		struct v4l2_subdev *subdev;
> +		struct i2c_adapter *adapter;
> +
> +		adapter = i2c_get_adapter(board_info->i2c_adapter_id);
> +		if (adapter == NULL) {
> +			printk(KERN_ERR "%s: Unable to get I2C adapter %d for "
> +				"device %s\n", __func__,
> +				board_info->i2c_adapter_id,
> +				board_info->board_info->type);

Why printk? I think you already have access to struct device.

> +			continue;
> +		}
> +
> +		subdev = v4l2_i2c_new_subdev_board(&iss->v4l2_dev, adapter,
> +				board_info->board_info, NULL);
> +		if (subdev == NULL) {
> +			printk(KERN_ERR "%s: Unable to register subdev %s\n",
> +				__func__, board_info->board_info->type);
> +			continue;
> +		}
> +
> +		if (first)
> +			sensor = subdev;
> +	}
> +
> +	return sensor;
> +}
> +
> +static int iss_register_entities(struct iss_device *iss)
> +{
> +	struct iss_platform_data *pdata = iss->pdata;
> +	struct iss_v4l2_subdevs_group *subdevs;
> +	int ret;
> +
> +	iss->media_dev.dev = iss->dev;
> +	strlcpy(iss->media_dev.model, "TI OMAP4 ISS",
> +		sizeof(iss->media_dev.model));
> +	iss->media_dev.link_notify = iss_pipeline_link_notify;
> +	ret = media_device_register(&iss->media_dev);
> +	if (ret < 0) {
> +		printk(KERN_ERR "%s: Media device registration failed (%d)\n",
> +			__func__, ret);
> +		return ret;
> +	}
> +
> +	iss->v4l2_dev.mdev = &iss->media_dev;
> +	ret = v4l2_device_register(iss->dev, &iss->v4l2_dev);
> +	if (ret < 0) {
> +		printk(KERN_ERR "%s: V4L2 device registration failed (%d)\n",
> +			__func__, ret);
> +		goto done;
> +	}
> +
> +	/* Register internal entities */
> +	ret = omap4iss_csi2_register_entities(&iss->csi2a, &iss->v4l2_dev);
> +	if (ret < 0)
> +		goto done;
> +
> +	/* Register external entities */
> +	for (subdevs = pdata->subdevs; subdevs && subdevs->subdevs; ++subdevs) {
> +		struct v4l2_subdev *sensor;
> +		struct media_entity *input;
> +		unsigned int flags;
> +		unsigned int pad;
> +
> +		sensor = iss_register_subdev_group(iss, subdevs->subdevs);
> +		if (sensor == NULL)
> +			continue;
> +
> +		sensor->host_priv = subdevs;
> +
> +		/* Connect the sensor to the correct interface module.
> +		 * CSI2a receiver through CSIPHY1.
> +		 */
> +		switch (subdevs->interface) {
> +		case ISS_INTERFACE_CSI2A_PHY1:
> +			input = &iss->csi2a.subdev.entity;
> +			pad = CSI2_PAD_SINK;
> +			flags = MEDIA_LNK_FL_IMMUTABLE
> +			      | MEDIA_LNK_FL_ENABLED;
> +			break;
> +
> +		default:
> +			printk(KERN_ERR "%s: invalid interface type %u\n",
> +			       __func__, subdevs->interface);
> +			ret = -EINVAL;
> +			goto done;
> +		}
> +
> +		ret = media_entity_create_link(&sensor->entity, 0, input, pad,
> +					       flags);
> +		if (ret < 0)
> +			goto done;
> +	}
> +
> +	ret = v4l2_device_register_subdev_nodes(&iss->v4l2_dev);
> +
> +done:
> +	if (ret < 0)
> +		iss_unregister_entities(iss);
> +
> +	return ret;
> +}
> +
> +static void iss_cleanup_modules(struct iss_device *iss)
> +{
> +	omap4iss_csi2_cleanup(iss);
> +}
> +
> +static int iss_initialize_modules(struct iss_device *iss)
> +{
> +	int ret;
> +
> +	ret = omap4iss_csiphy_init(iss);
> +	if (ret < 0) {
> +		dev_err(iss->dev, "CSI PHY initialization failed\n");
> +		goto error_csiphy;
> +	}
> +
> +	ret = omap4iss_csi2_init(iss);
> +	if (ret < 0) {
> +		dev_err(iss->dev, "CSI2 initialization failed\n");
> +		goto error_csi2;
> +	}
> +
> +	return 0;
> +
> +error_csi2:
> +error_csiphy:
> +	return ret;

If there's nothing special you need to do here, I suggest to return the
error where it happened.

> +}
> +
> +static int iss_probe(struct platform_device *pdev)
> +{
> +	struct iss_platform_data *pdata = pdev->dev.platform_data;
> +	struct iss_device *iss;
> +	int i, ret;
> +
> +	if (pdata == NULL)
> +		return -EINVAL;
> +
> +	iss = kzalloc(sizeof(*iss), GFP_KERNEL);
> +	if (!iss) {
> +		dev_err(&pdev->dev, "Could not allocate memory\n");
> +		return -ENOMEM;
> +	}
> +
> +	mutex_init(&iss->iss_mutex);
> +
> +	iss->dev = &pdev->dev;
> +	iss->pdata = pdata;
> +	iss->ref_count = 0;
> +
> +	iss->raw_dmamask = DMA_BIT_MASK(32);
> +	iss->dev->dma_mask = &iss->raw_dmamask;
> +	iss->dev->coherent_dma_mask = DMA_BIT_MASK(32);
> +
> +	platform_set_drvdata(pdev, iss);
> +
> +	/* Clocks */
> +	ret = iss_map_mem_resource(pdev, iss, OMAP4_ISS_MEM_TOP);
> +	if (ret < 0)
> +		goto error;
> +
> +	ret = iss_get_clocks(iss);
> +	if (ret < 0)
> +		goto error;
> +
> +	if (omap4iss_get(iss) == NULL)
> +		goto error;
> +
> +	ret = iss_reset(iss);
> +	if (ret < 0)
> +		goto error_iss;
> +
> +	iss->revision = readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_REVISION);
> +	dev_info(iss->dev, "Revision %08x found\n", iss->revision);
> +
> +	for (i = 1; i < OMAP4_ISS_MEM_LAST; i++) {
> +		ret = iss_map_mem_resource(pdev, iss, i);
> +		if (ret)
> +			goto error_iss;
> +	}
> +
> +	/* Interrupt */
> +	iss->irq_num = platform_get_irq(pdev, 0);
> +	if (iss->irq_num <= 0) {
> +		dev_err(iss->dev, "No IRQ resource\n");
> +		ret = -ENODEV;
> +		goto error_iss;
> +	}
> +
> +	if (request_irq(iss->irq_num, iss_isr, IRQF_SHARED, "OMAP4 ISS", iss)) {
> +		dev_err(iss->dev, "Unable to request IRQ\n");
> +		ret = -EINVAL;
> +		goto error_iss;
> +	}
> +
> +	/* Entities */
> +	ret = iss_initialize_modules(iss);
> +	if (ret < 0)
> +		goto error_irq;
> +
> +	ret = iss_register_entities(iss);
> +	if (ret < 0)
> +		goto error_modules;
> +
> +	omap4iss_put(iss);
> +
> +	return 0;
> +
> +error_modules:
> +	iss_cleanup_modules(iss);
> +error_irq:
> +	free_irq(iss->irq_num, iss);
> +error_iss:
> +	omap4iss_put(iss);
> +error:
> +	iss_put_clocks(iss);
> +
> +	for (i = 0; i < OMAP4_ISS_MEM_LAST; i++) {
> +		if (iss->regs[i]) {
> +			iounmap(iss->regs[i]);
> +			iss->regs[i] = NULL;
> +		}
> +
> +		if (iss->res[i]) {
> +			release_mem_region(iss->res[i]->start,
> +					   resource_size(iss->res[i]));
> +			iss->res[i] = NULL;
> +		}
> +	}
> +	platform_set_drvdata(pdev, NULL);
> +	kfree(iss);
> +
> +	return ret;
> +}
> +
> +static int iss_remove(struct platform_device *pdev)
> +{
> +	struct iss_device *iss = platform_get_drvdata(pdev);
> +	int i;
> +
> +	iss_unregister_entities(iss);
> +	iss_cleanup_modules(iss);
> +
> +	free_irq(iss->irq_num, iss);
> +	iss_put_clocks(iss);
> +
> +	for (i = 0; i < OMAP4_ISS_MEM_LAST; i++) {
> +		if (iss->regs[i]) {
> +			iounmap(iss->regs[i]);
> +			iss->regs[i] = NULL;
> +		}
> +
> +		if (iss->res[i]) {
> +			release_mem_region(iss->res[i]->start,
> +					   resource_size(iss->res[i]));
> +			iss->res[i] = NULL;
> +		}
> +	}
> +
> +	kfree(iss);
> +
> +	return 0;
> +}
> +
> +static struct platform_device_id omap4iss_id_table[] = {
> +	{ "omap4iss", 0 },
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(platform, omap4iss_id_table);
> +
> +static struct platform_driver iss_driver = {
> +	.probe		= iss_probe,
> +	.remove		= iss_remove,
> +	.id_table	= omap4iss_id_table,
> +	.driver = {
> +		.owner	= THIS_MODULE,
> +		.name	= "omap4iss",
> +	},
> +};
> +
> +static int __init iss_init(void)
> +{
> +	return platform_driver_register(&iss_driver);
> +}
> +
> +static void __exit iss_exit(void)
> +{
> +	platform_driver_unregister(&iss_driver);
> +}
> +
> +/*
> + * FIXME: Had to make it late_initcall. Strangely while being module_init,
> + * The I2C communication was failing in the sensor, because no XCLK was
> + * provided.
> + */
> +late_initcall(iss_init);
> +module_exit(iss_exit);
> +
> +MODULE_DESCRIPTION("TI OMAP4 ISS driver");
> +MODULE_AUTHOR("Sergio Aguirre <saaguirre@ti.com>");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/media/video/omap4iss/iss.h b/drivers/media/video/omap4iss/iss.h
> new file mode 100644
> index 0000000..8346c80
> --- /dev/null
> +++ b/drivers/media/video/omap4iss/iss.h
> @@ -0,0 +1,133 @@
> +/*
> + * iss.h
> + *
> + * Copyright (C) 2011 Texas Instruments.
> + *
> + * Author: Sergio Aguirre <saaguirre@ti.com>
> + */

Have you intentionally left out the more verbose notice? It's also 2012 now.
:)

> +#ifndef _OMAP4_ISS_H_
> +#define _OMAP4_ISS_H_
> +
> +#include <media/v4l2-device.h>
> +#include <linux/device.h>
> +#include <linux/io.h>
> +#include <linux/platform_device.h>
> +#include <linux/wait.h>
> +
> +#include <plat/omap4-iss.h>
> +
> +#include "iss_regs.h"
> +#include "iss_csiphy.h"
> +#include "iss_csi2.h"
> +
> +#define ISS_TOK_TERM		0xFFFFFFFF	/*

Minor thing, but lowercase hex are preferred.

> +						 * terminating token for ISS
> +						 * modules reg list
> +						 */
> +#define to_iss_device(ptr_module)				\
> +	container_of(ptr_module, struct iss_device, ptr_module)
> +#define to_device(ptr_module)						\
> +	(to_iss_device(ptr_module)->dev)
> +
> +enum iss_mem_resources {
> +	OMAP4_ISS_MEM_TOP,
> +	OMAP4_ISS_MEM_CSI2_A_REGS1,
> +	OMAP4_ISS_MEM_CAMERARX_CORE1,
> +	OMAP4_ISS_MEM_LAST,
> +};
> +
> +enum iss_subclk_resource {
> +	OMAP4_ISS_SUBCLK_SIMCOP		= (1 << 0),
> +	OMAP4_ISS_SUBCLK_ISP		= (1 << 1),
> +	OMAP4_ISS_SUBCLK_CSI2_A		= (1 << 2),
> +	OMAP4_ISS_SUBCLK_CSI2_B		= (1 << 3),
> +	OMAP4_ISS_SUBCLK_CCP2		= (1 << 4),
> +};
> +
> +/*
> + * struct iss_reg - Structure for ISS register values.
> + * @reg: 32-bit Register address.
> + * @val: 32-bit Register value.
> + */
> +struct iss_reg {
> +	enum iss_mem_resources mmio_range;
> +	u32 reg;
> +	u32 val;
> +};
> +
> +struct iss_platform_callback {
> +	int (*csiphy_config)(struct iss_csiphy *phy,
> +			     struct iss_csiphy_dphy_cfg *dphy,
> +			     struct iss_csiphy_lanes_cfg *lanes);
> +};

You might want to look my recent patchset (a few days ago). You can probably
get rid of this callback easily.

> +struct iss_device {
> +	struct v4l2_device v4l2_dev;
> +	struct media_device media_dev;
> +	struct device *dev;
> +	u32 revision;
> +
> +	/* platform HW resources */
> +	struct iss_platform_data *pdata;
> +	unsigned int irq_num;
> +
> +	struct resource *res[OMAP4_ISS_MEM_LAST];
> +	void __iomem *regs[OMAP4_ISS_MEM_LAST];
> +
> +	u64 raw_dmamask;
> +
> +	struct mutex iss_mutex;	/* For handling ref_count field */
> +	int has_context;
> +	int ref_count;
> +
> +	struct clk *iss_fck;
> +	struct clk *iss_ctrlclk;
> +
> +	/* ISS modules */
> +	struct iss_csi2_device csi2a;
> +	struct iss_csiphy csiphy1;
> +
> +	unsigned int subclk_resources;
> +
> +	struct iss_platform_callback platform_cb;
> +};
> +
> +#define v4l2_dev_to_iss_device(dev) \
> +	container_of(dev, struct iss_device, v4l2_dev)
> +
> +int omap4iss_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait,
> +			      atomic_t *stopping);
> +
> +int omap4iss_module_sync_is_stopping(wait_queue_head_t *wait,
> +				     atomic_t *stopping);
> +
> +int omap4iss_pipeline_set_stream(struct iss_pipeline *pipe,
> +				 enum iss_pipeline_stream_state state);
> +
> +struct iss_device *omap4iss_get(struct iss_device *iss);
> +void omap4iss_put(struct iss_device *iss);
> +int omap4iss_subclk_enable(struct iss_device *iss,
> +			   enum iss_subclk_resource res);
> +int omap4iss_subclk_disable(struct iss_device *iss,
> +			    enum iss_subclk_resource res);
> +
> +int omap4iss_pipeline_pm_use(struct media_entity *entity, int use);
> +
> +int omap4iss_register_entities(struct platform_device *pdev,
> +			       struct v4l2_device *v4l2_dev);
> +void omap4iss_unregister_entities(struct platform_device *pdev);
> +
> +static inline enum v4l2_buf_type
> +iss_pad_buffer_type(const struct v4l2_subdev *subdev, int pad)
> +{
> +	if (pad >= subdev->entity.num_pads)
> +		return 0;

Hmm. Is it just me or is this function unused?

> +	if (subdev->entity.pads[pad].flags & MEDIA_PAD_FL_SINK)
> +		return V4L2_BUF_TYPE_VIDEO_OUTPUT;
> +	else
> +		return V4L2_BUF_TYPE_VIDEO_CAPTURE;
> +}
> +
> +#endif /* _OMAP4_ISS_H_ */
> diff --git a/drivers/media/video/omap4iss/iss_csi2.c b/drivers/media/video/omap4iss/iss_csi2.c
> new file mode 100644
> index 0000000..916d5ef
> --- /dev/null
> +++ b/drivers/media/video/omap4iss/iss_csi2.c
> @@ -0,0 +1,1324 @@
> +/*
> + * iss_csi2.c
> + *
> + * TI OMAP4 ISS - CSI PHY module
> + *
> + * Copyright (C) 2011 Texas Instruments, Inc.
> + *
> + * Contacts: Sergio Aguirre <saaguirre@ti.com>
> + *
> + * 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.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
> + * 02110-1301 USA
> + */
> +#include <linux/delay.h>
> +#include <media/v4l2-common.h>
> +#include <linux/v4l2-mediabus.h>
> +#include <linux/mm.h>
> +
> +#include "iss.h"
> +#include "iss_regs.h"
> +#include "iss_csi2.h"
> +
> +/*
> + * csi2_if_enable - Enable CSI2 Receiver interface.
> + * @enable: enable flag
> + *
> + */
> +static void csi2_if_enable(struct iss_csi2_device *csi2, u8 enable)
> +{
> +	struct iss_csi2_ctrl_cfg *currctrl = &csi2->ctrl;
> +
> +	writel((readl(csi2->regs1 + CSI2_CTRL) & ~CSI2_CTRL_IF_EN) |
> +		(enable ? CSI2_CTRL_IF_EN : 0),
> +		csi2->regs1 + CSI2_CTRL);
> +
> +	currctrl->if_enable = enable;
> +}
> +
> +/*
> + * csi2_recv_config - CSI2 receiver module configuration.
> + * @currctrl: iss_csi2_ctrl_cfg structure
> + *
> + */
> +static void csi2_recv_config(struct iss_csi2_device *csi2,
> +			     struct iss_csi2_ctrl_cfg *currctrl)
> +{
> +	u32 reg;
> +
> +	reg = readl(csi2->regs1 + CSI2_CTRL);
> +
> +	if (currctrl->frame_mode)
> +		reg |= CSI2_CTRL_FRAME;
> +	else
> +		reg &= ~CSI2_CTRL_FRAME;
> +
> +	if (currctrl->vp_clk_enable)
> +		reg |= CSI2_CTRL_VP_CLK_EN;
> +	else
> +		reg &= ~CSI2_CTRL_VP_CLK_EN;
> +
> +	if (currctrl->vp_only_enable)
> +		reg |= CSI2_CTRL_VP_ONLY_EN;
> +	else
> +		reg &= ~CSI2_CTRL_VP_ONLY_EN;
> +
> +	reg &= ~CSI2_CTRL_VP_OUT_CTRL_MASK;
> +	reg |= currctrl->vp_out_ctrl << CSI2_CTRL_VP_OUT_CTRL_SHIFT;
> +
> +	if (currctrl->ecc_enable)
> +		reg |= CSI2_CTRL_ECC_EN;
> +	else
> +		reg &= ~CSI2_CTRL_ECC_EN;
> +
> +	/*
> +	 * Set MFlag assertion boundaries to:
> +	 * Low: 4/8 of FIFO size
> +	 * High: 6/8 of FIFO size
> +	 */
> +	reg &= ~(CSI2_CTRL_MFLAG_LEVH_MASK | CSI2_CTRL_MFLAG_LEVL_MASK);
> +	reg |= (2 << CSI2_CTRL_MFLAG_LEVH_SHIFT) |
> +	       (4 << CSI2_CTRL_MFLAG_LEVL_SHIFT);
> +
> +	/* Generation of 16x64-bit bursts (Recommended) */
> +	reg |= CSI2_CTRL_BURST_SIZE_EXPAND;
> +
> +	/* Do Non-Posted writes (Recommended) */
> +	reg |= CSI2_CTRL_NON_POSTED_WRITE;
> +
> +	/*
> +	 * Enforce Little endian for all formats, including:
> +	 * YUV4:2:2 8-bit and YUV4:2:0 Legacy
> +	 */
> +	reg |= CSI2_CTRL_ENDIANNESS;
> +
> +	writel(reg, csi2->regs1 + CSI2_CTRL);

Is this register accessed elsewhere? If not, you could skip reading it and
just write the value there. This was very common in the original OMAP 3 ISP
driver. :)

> +}
> +
> +static const unsigned int csi2_input_fmts[] = {
> +	V4L2_MBUS_FMT_SGRBG10_1X10,
> +	V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8,
> +	V4L2_MBUS_FMT_SRGGB10_1X10,
> +	V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8,
> +	V4L2_MBUS_FMT_SBGGR10_1X10,
> +	V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8,
> +	V4L2_MBUS_FMT_SGBRG10_1X10,
> +	V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8,
> +	V4L2_MBUS_FMT_UYVY8_1X16,
> +	V4L2_MBUS_FMT_YUYV8_1X16,
> +};
> +
> +/* To set the format on the CSI2 requires a mapping function that takes
> + * the following inputs:
> + * - 3 different formats (at this time)
> + * - 2 destinations (mem, vp+mem) (vp only handled separately)
> + * - 2 decompression options (on, off)
> + * Output should be CSI2 frame format code
> + * Array indices as follows: [format][dest][decompr]
> + * Not all combinations are valid. 0 means invalid.
> + */
> +static const u16 __csi2_fmt_map[][2][2] = {
> +	/* RAW10 formats */
> +	{
> +		/* Output to memory */
> +		{
> +			/* No DPCM decompression */
> +			CSI2_PIX_FMT_RAW10_EXP16,
> +			/* DPCM decompression */
> +			0,
> +		},
> +		/* Output to both */
> +		{
> +			/* No DPCM decompression */
> +			CSI2_PIX_FMT_RAW10_EXP16_VP,
> +			/* DPCM decompression */
> +			0,
> +		},
> +	},
> +	/* RAW10 DPCM8 formats */
> +	{
> +		/* Output to memory */
> +		{
> +			/* No DPCM decompression */
> +			CSI2_USERDEF_8BIT_DATA1,
> +			/* DPCM decompression */
> +			CSI2_USERDEF_8BIT_DATA1_DPCM10,
> +		},
> +		/* Output to both */
> +		{
> +			/* No DPCM decompression */
> +			CSI2_PIX_FMT_RAW8_VP,
> +			/* DPCM decompression */
> +			CSI2_USERDEF_8BIT_DATA1_DPCM10_VP,
> +		},
> +	},
> +	/* YUV422 formats */
> +	{
> +		/* Output to memory */
> +		{
> +			/* No DPCM decompression */
> +			CSI2_PIX_FMT_YUV422_8BIT,
> +			/* DPCM decompression */
> +			0,
> +		},
> +		/* Output to both */
> +		{
> +			/* No DPCM decompression */
> +			CSI2_PIX_FMT_YUV422_8BIT_VP,
> +			/* DPCM decompression */
> +			0,
> +		},
> +	},
> +};
> +
> +/*
> + * csi2_ctx_map_format - Map CSI2 sink media bus format to CSI2 format ID
> + * @csi2: ISS CSI2 device
> + *
> + * Returns CSI2 physical format id
> + */
> +static u16 csi2_ctx_map_format(struct iss_csi2_device *csi2)
> +{
> +	const struct v4l2_mbus_framefmt *fmt = &csi2->formats[CSI2_PAD_SINK];
> +	int fmtidx, destidx;
> +
> +	switch (fmt->code) {
> +	case V4L2_MBUS_FMT_SGRBG10_1X10:
> +	case V4L2_MBUS_FMT_SRGGB10_1X10:
> +	case V4L2_MBUS_FMT_SBGGR10_1X10:
> +	case V4L2_MBUS_FMT_SGBRG10_1X10:
> +		fmtidx = 0;
> +		break;
> +	case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8:
> +	case V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8:
> +	case V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8:
> +	case V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8:
> +		fmtidx = 1;
> +		break;
> +	case V4L2_MBUS_FMT_UYVY8_1X16:
> +	case V4L2_MBUS_FMT_YUYV8_1X16:
> +		fmtidx = 2;
> +		break;
> +	default:
> +		WARN(1, KERN_ERR "CSI2: pixel format %08x unsupported!\n",
> +		     fmt->code);
> +		return 0;
> +	}
> +
> +	if (!(csi2->output & CSI2_OUTPUT_IPIPEIF) &&
> +	    !(csi2->output & CSI2_OUTPUT_MEMORY)) {
> +		/* Neither output enabled is a valid combination */
> +		return CSI2_PIX_FMT_OTHERS;
> +	}
> +
> +	/* If we need to skip frames at the beginning of the stream disable the
> +	 * video port to avoid sending the skipped frames to the IPIPEIF.
> +	 */
> +	destidx = csi2->frame_skip ? 0 : !!(csi2->output & CSI2_OUTPUT_IPIPEIF);
> +
> +	return __csi2_fmt_map[fmtidx][destidx][csi2->dpcm_decompress];
> +}
> +
> +/*
> + * csi2_set_outaddr - Set memory address to save output image
> + * @csi2: Pointer to ISS CSI2a device.
> + * @addr: 32-bit memory address aligned on 32 byte boundary.
> + *
> + * Sets the memory address where the output will be saved.
> + *
> + * Returns 0 if successful, or -EINVAL if the address is not in the 32 byte
> + * boundary.
> + */
> +static void csi2_set_outaddr(struct iss_csi2_device *csi2, u32 addr)
> +{
> +	struct iss_csi2_ctx_cfg *ctx = &csi2->contexts[0];
> +
> +	ctx->ping_addr = addr;
> +	ctx->pong_addr = addr;
> +	writel(ctx->ping_addr,
> +	       csi2->regs1 + CSI2_CTX_PING_ADDR(ctx->ctxnum));
> +	writel(ctx->pong_addr,
> +	       csi2->regs1 + CSI2_CTX_PONG_ADDR(ctx->ctxnum));
> +}
> +
> +/*
> + * is_usr_def_mapping - Checks whether USER_DEF_MAPPING should
> + *			be enabled by CSI2.
> + * @format_id: mapped format id
> + *
> + */
> +static inline int is_usr_def_mapping(u32 format_id)
> +{
> +	return (format_id & 0x40) ? 1 : 0;
> +}
> +
> +/*
> + * csi2_ctx_enable - Enable specified CSI2 context
> + * @ctxnum: Context number, valid between 0 and 7 values.
> + * @enable: enable
> + *
> + */
> +static void csi2_ctx_enable(struct iss_csi2_device *csi2, u8 ctxnum, u8 enable)
> +{
> +	struct iss_csi2_ctx_cfg *ctx = &csi2->contexts[ctxnum];
> +	u32 reg;
> +
> +	reg = readl(csi2->regs1 + CSI2_CTX_CTRL1(ctxnum));
> +
> +	if (enable) {
> +		unsigned int skip = 0;
> +
> +		if (csi2->frame_skip)
> +			skip = csi2->frame_skip;
> +		else if (csi2->output & CSI2_OUTPUT_MEMORY)
> +			skip = 1;
> +
> +		reg &= ~CSI2_CTX_CTRL1_COUNT_MASK;
> +		reg |= CSI2_CTX_CTRL1_COUNT_UNLOCK
> +		    |  (skip << CSI2_CTX_CTRL1_COUNT_SHIFT)
> +		    |  CSI2_CTX_CTRL1_CTX_EN;
> +	} else {
> +		reg &= ~CSI2_CTX_CTRL1_CTX_EN;
> +	}
> +
> +	writel(reg, csi2->regs1 + CSI2_CTX_CTRL1(ctxnum));
> +	ctx->enabled = enable;
> +}
> +
> +/*
> + * csi2_ctx_config - CSI2 context configuration.
> + * @ctx: context configuration
> + *
> + */
> +static void csi2_ctx_config(struct iss_csi2_device *csi2,
> +			    struct iss_csi2_ctx_cfg *ctx)
> +{
> +	u32 reg;
> +
> +	/* Set up CSI2_CTx_CTRL1 */
> +	reg = readl(csi2->regs1 + CSI2_CTX_CTRL1(ctx->ctxnum));
> 
> +	if (ctx->eof_enabled)
> +		reg |= CSI2_CTX_CTRL1_EOF_EN;
> +	else
> +		reg &= ~CSI2_CTX_CTRL1_EOF_EN;
> +
> +	if (ctx->eol_enabled)
> +		reg |= CSI2_CTX_CTRL1_EOL_EN;
> +	else
> +		reg &= ~CSI2_CTX_CTRL1_EOL_EN;
> +
> +	if (ctx->checksum_enabled)
> +		reg |= CSI2_CTX_CTRL1_CS_EN;
> +	else
> +		reg &= ~CSI2_CTX_CTRL1_CS_EN;
> +
> +	writel(reg, csi2->regs1 + CSI2_CTX_CTRL1(ctx->ctxnum));
> +
> +	/* Set up CSI2_CTx_CTRL2 */
> +	reg = readl(csi2->regs1 + CSI2_CTX_CTRL2(ctx->ctxnum));

CTX_CTRL2 is only used in this function. I don't think you'd need to read it
back. How about other CTX_CTRL regs?

> +	reg &= ~(CSI2_CTX_CTRL2_VIRTUAL_ID_MASK);
> +	reg |= ctx->virtual_id << CSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT;
> +
> +	reg &= ~(CSI2_CTX_CTRL2_FORMAT_MASK);
> +	reg |= ctx->format_id << CSI2_CTX_CTRL2_FORMAT_SHIFT;
> +
> +	if (ctx->dpcm_decompress) {
> +		if (ctx->dpcm_predictor)
> +			reg |= CSI2_CTX_CTRL2_DPCM_PRED;
> +		else
> +			reg &= ~CSI2_CTX_CTRL2_DPCM_PRED;
> +	}
> +
> +	if (is_usr_def_mapping(ctx->format_id)) {
> +		reg &= ~CSI2_CTX_CTRL2_USER_DEF_MAP_MASK;
> +		reg |= 2 << CSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT;
> +	}
> +
> +	writel(reg, csi2->regs1 + CSI2_CTX_CTRL2(ctx->ctxnum));
> +
> +	/* Set up CSI2_CTx_CTRL3 */
> +	reg = readl(csi2->regs1 + CSI2_CTX_CTRL3(ctx->ctxnum));
> +	reg &= ~(CSI2_CTX_CTRL3_ALPHA_MASK);
> +	reg |= (ctx->alpha << CSI2_CTX_CTRL3_ALPHA_SHIFT);
> +
> +	writel(reg, csi2->regs1 + CSI2_CTX_CTRL3(ctx->ctxnum));
> +
> +	/* Set up CSI2_CTx_DAT_OFST */
> +	reg = readl(csi2->regs1 + CSI2_CTX_DAT_OFST(ctx->ctxnum));
> +	reg &= ~CSI2_CTX_DAT_OFST_MASK;
> +	reg |= ctx->data_offset;
> +	writel(reg, csi2->regs1 + CSI2_CTX_DAT_OFST(ctx->ctxnum));
> +
> +	writel(ctx->ping_addr,
> +		       csi2->regs1 + CSI2_CTX_PING_ADDR(ctx->ctxnum));
> +
> +	writel(ctx->pong_addr,
> +		       csi2->regs1 + CSI2_CTX_PONG_ADDR(ctx->ctxnum));
> +}
> +
> +/*
> + * csi2_timing_config - CSI2 timing configuration.
> + * @timing: csi2_timing_cfg structure
> + */
> +static void csi2_timing_config(struct iss_csi2_device *csi2,
> +			       struct iss_csi2_timing_cfg *timing)
> +{
> +	u32 reg;
> +
> +	reg = readl(csi2->regs1 + CSI2_TIMING);
> +
> +	if (timing->force_rx_mode)
> +		reg |= CSI2_TIMING_FORCE_RX_MODE_IO1;
> +	else
> +		reg &= ~CSI2_TIMING_FORCE_RX_MODE_IO1;
> +
> +	if (timing->stop_state_16x)
> +		reg |= CSI2_TIMING_STOP_STATE_X16_IO1;
> +	else
> +		reg &= ~CSI2_TIMING_STOP_STATE_X16_IO1;
> +
> +	if (timing->stop_state_4x)
> +		reg |= CSI2_TIMING_STOP_STATE_X4_IO1;
> +	else
> +		reg &= ~CSI2_TIMING_STOP_STATE_X4_IO1;
> +
> +	reg &= ~CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK;
> +	reg |= timing->stop_state_counter <<
> +	       CSI2_TIMING_STOP_STATE_COUNTER_IO1_SHIFT;
> +
> +	writel(reg, csi2->regs1 + CSI2_TIMING);
> +}
> +
> +/*
> + * csi2_irq_ctx_set - Enables CSI2 Context IRQs.
> + * @enable: Enable/disable CSI2 Context interrupts
> + */
> +static void csi2_irq_ctx_set(struct iss_csi2_device *csi2, int enable)
> +{
> +	u32 reg = CSI2_CTX_IRQ_FE;
> +	int i;
> +
> +	if (csi2->use_fs_irq)
> +		reg |= CSI2_CTX_IRQ_FS;
> +
> +	for (i = 0; i < 8; i++) {

8 == number of contexts?

> +		writel(reg, csi2->regs1 + CSI2_CTX_IRQSTATUS(i));
> +		if (enable)
> +			writel(readl(csi2->regs1 + CSI2_CTX_IRQENABLE(i)) | reg,
> +				csi2->regs1 + CSI2_CTX_IRQENABLE(i));
> +		else
> +			writel(readl(csi2->regs1 + CSI2_CTX_IRQENABLE(i)) &
> +				~reg,
> +				csi2->regs1 + CSI2_CTX_IRQENABLE(i));
> +	}
> +}
> +
> +/*
> + * csi2_irq_complexio1_set - Enables CSI2 ComplexIO IRQs.
> + * @enable: Enable/disable CSI2 ComplexIO #1 interrupts
> + */
> +static void csi2_irq_complexio1_set(struct iss_csi2_device *csi2, int enable)
> +{
> +	u32 reg;
> +	reg = CSI2_COMPLEXIO_IRQ_STATEALLULPMEXIT |
> +		CSI2_COMPLEXIO_IRQ_STATEALLULPMENTER |
> +		CSI2_COMPLEXIO_IRQ_STATEULPM5 |
> +		CSI2_COMPLEXIO_IRQ_ERRCONTROL5 |
> +		CSI2_COMPLEXIO_IRQ_ERRESC5 |
> +		CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS5 |
> +		CSI2_COMPLEXIO_IRQ_ERRSOTHS5 |
> +		CSI2_COMPLEXIO_IRQ_STATEULPM4 |
> +		CSI2_COMPLEXIO_IRQ_ERRCONTROL4 |
> +		CSI2_COMPLEXIO_IRQ_ERRESC4 |
> +		CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS4 |
> +		CSI2_COMPLEXIO_IRQ_ERRSOTHS4 |
> +		CSI2_COMPLEXIO_IRQ_STATEULPM3 |
> +		CSI2_COMPLEXIO_IRQ_ERRCONTROL3 |
> +		CSI2_COMPLEXIO_IRQ_ERRESC3 |
> +		CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS3 |
> +		CSI2_COMPLEXIO_IRQ_ERRSOTHS3 |
> +		CSI2_COMPLEXIO_IRQ_STATEULPM2 |
> +		CSI2_COMPLEXIO_IRQ_ERRCONTROL2 |
> +		CSI2_COMPLEXIO_IRQ_ERRESC2 |
> +		CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS2 |
> +		CSI2_COMPLEXIO_IRQ_ERRSOTHS2 |
> +		CSI2_COMPLEXIO_IRQ_STATEULPM1 |
> +		CSI2_COMPLEXIO_IRQ_ERRCONTROL1 |
> +		CSI2_COMPLEXIO_IRQ_ERRESC1 |
> +		CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS1 |
> +		CSI2_COMPLEXIO_IRQ_ERRSOTHS1;
> +	writel(reg, csi2->regs1 + CSI2_COMPLEXIO_IRQSTATUS);
> +	if (enable)
> +		reg |= readl(csi2->regs1 + CSI2_COMPLEXIO_IRQENABLE);
> +	else
> +		reg = 0;
> +	writel(reg, csi2->regs1 + CSI2_COMPLEXIO_IRQENABLE);
> +}
> +
> +/*
> + * csi2_irq_status_set - Enables CSI2 Status IRQs.
> + * @enable: Enable/disable CSI2 Status interrupts
> + */
> +static void csi2_irq_status_set(struct iss_csi2_device *csi2, int enable)
> +{
> +	u32 reg;
> +	reg = CSI2_IRQ_OCP_ERR |
> +		CSI2_IRQ_SHORT_PACKET |
> +		CSI2_IRQ_ECC_CORRECTION |
> +		CSI2_IRQ_ECC_NO_CORRECTION |
> +		CSI2_IRQ_COMPLEXIO_ERR |
> +		CSI2_IRQ_FIFO_OVF |
> +		CSI2_IRQ_CONTEXT0;
> +	writel(reg, csi2->regs1 + CSI2_IRQSTATUS);
> +	if (enable)
> +		reg |= readl(csi2->regs1 + CSI2_IRQENABLE);
> +	else
> +		reg = 0;
> +
> +	writel(reg, csi2->regs1 + CSI2_IRQENABLE);
> +}
> +
> +/*
> + * omap4iss_csi2_reset - Resets the CSI2 module.
> + *
> + * Must be called with the phy lock held.
> + *
> + * Returns 0 if successful, or -EBUSY if power command didn't respond.
> + */
> +int omap4iss_csi2_reset(struct iss_csi2_device *csi2)
> +{
> +	u8 soft_reset_retries = 0;
> +	u32 reg;
> +	int i;
> +
> +	if (!csi2->available)
> +		return -ENODEV;
> +
> +	if (csi2->phy->phy_in_use)
> +		return -EBUSY;
> +
> +	writel(readl(csi2->regs1 + CSI2_SYSCONFIG) |
> +		CSI2_SYSCONFIG_SOFT_RESET,
> +		csi2->regs1 + CSI2_SYSCONFIG);
> +
> +	do {
> +		reg = readl(csi2->regs1 + CSI2_SYSSTATUS) &
> +				    CSI2_SYSSTATUS_RESET_DONE;
> +		if (reg == CSI2_SYSSTATUS_RESET_DONE)
> +			break;
> +		soft_reset_retries++;
> +		if (soft_reset_retries < 5)
> +			udelay(100);

How about usleep_range() here? Or omit such a long busyloop. Hardware
typically resets quite fast.

> +	} while (soft_reset_retries < 5);
> +
> +	if (soft_reset_retries == 5) {
> +		printk(KERN_ERR "CSI2: Soft reset try count exceeded!\n");
> +		return -EBUSY;
> +	}
> +
> +	writel(readl(csi2->regs1 + CSI2_COMPLEXIO_CFG) |
> +		CSI2_COMPLEXIO_CFG_RESET_CTRL,
> +		csi2->regs1 + CSI2_COMPLEXIO_CFG);
> +
> +	i = 100;
> +	do {
> +		reg = readl(csi2->phy->phy_regs + REGISTER1)
> +		    & REGISTER1_RESET_DONE_CTRLCLK;
> +		if (reg == REGISTER1_RESET_DONE_CTRLCLK)
> +			break;
> +		udelay(100);

Same here.

> +	} while (--i > 0);
> +
> +	if (i == 0) {
> +		printk(KERN_ERR
> +		       "CSI2: Reset for CSI2_96M_FCLK domain Failed!\n");
> +		return -EBUSY;
> +	}
> +
> +	writel((readl(csi2->regs1 + CSI2_SYSCONFIG) &
> +		~(CSI2_SYSCONFIG_MSTANDBY_MODE_MASK |
> +		  CSI2_SYSCONFIG_AUTO_IDLE)) |
> +		CSI2_SYSCONFIG_MSTANDBY_MODE_NO,
> +		csi2->regs1 + CSI2_SYSCONFIG);
> +
> +	return 0;
> +}
> +
> +static int csi2_configure(struct iss_csi2_device *csi2)
> +{
> +	const struct iss_v4l2_subdevs_group *pdata;
> +	struct iss_csi2_timing_cfg *timing = &csi2->timing[0];
> +	struct v4l2_subdev *sensor;
> +	struct media_pad *pad;
> +
> +	/*
> +	 * CSI2 fields that can be updated while the context has
> +	 * been enabled or the interface has been enabled are not
> +	 * updated dynamically currently. So we do not allow to
> +	 * reconfigure if either has been enabled
> +	 */
> +	if (csi2->contexts[0].enabled || csi2->ctrl.if_enable)
> +		return -EBUSY;
> +
> +	pad = media_entity_remote_source(&csi2->pads[CSI2_PAD_SINK]);
> +	sensor = media_entity_to_v4l2_subdev(pad->entity);
> +	pdata = sensor->host_priv;
> +
> +	csi2->frame_skip = 0;
> +	v4l2_subdev_call(sensor, sensor, g_skip_frames, &csi2->frame_skip);
> +
> +	csi2->ctrl.vp_out_ctrl = pdata->bus.csi2.vpclk_div;
> +	csi2->ctrl.frame_mode = ISS_CSI2_FRAME_IMMEDIATE;
> +	csi2->ctrl.ecc_enable = pdata->bus.csi2.crc;
> +
> +	timing->force_rx_mode = 1;
> +	timing->stop_state_16x = 1;
> +	timing->stop_state_4x = 1;
> +	timing->stop_state_counter = 0x1FF;
> +
> +	/*
> +	 * The CSI2 receiver can't do any format conversion except DPCM
> +	 * decompression, so every set_format call configures both pads
> +	 * and enables DPCM decompression as a special case:
> +	 */
> +	if (csi2->formats[CSI2_PAD_SINK].code !=
> +	    csi2->formats[CSI2_PAD_SOURCE].code)
> +		csi2->dpcm_decompress = true;
> +	else
> +		csi2->dpcm_decompress = false;
> +
> +	csi2->contexts[0].format_id = csi2_ctx_map_format(csi2);
> +
> +	if (csi2->video_out.bpl_padding == 0)
> +		csi2->contexts[0].data_offset = 0;
> +	else
> +		csi2->contexts[0].data_offset = csi2->video_out.bpl_value;
> +
> +	/*
> +	 * Enable end of frame and end of line signals generation for
> +	 * context 0. These signals are generated from CSI2 receiver to
> +	 * qualify the last pixel of a frame and the last pixel of a line.
> +	 * Without enabling the signals CSI2 receiver writes data to memory
> +	 * beyond buffer size and/or data line offset is not handled correctly.
> +	 */
> +	csi2->contexts[0].eof_enabled = 1;
> +	csi2->contexts[0].eol_enabled = 1;
> +
> +	csi2_irq_complexio1_set(csi2, 1);
> +	csi2_irq_ctx_set(csi2, 1);
> +	csi2_irq_status_set(csi2, 1);
> +
> +	/* Set configuration (timings, format and links) */
> +	csi2_timing_config(csi2, timing);
> +	csi2_recv_config(csi2, &csi2->ctrl);
> +	csi2_ctx_config(csi2, &csi2->contexts[0]);
> +
> +	return 0;
> +}
> +
> +/*
> + * csi2_print_status - Prints CSI2 debug information.
> + */
> +#define CSI2_PRINT_REGISTER(iss, regs, name)\
> +	dev_dbg(iss->dev, "###CSI2 " #name "=0x%08x\n", \
> +		readl(regs + CSI2_##name))
> +
> +static void csi2_print_status(struct iss_csi2_device *csi2)
> +{
> +	struct iss_device *iss = csi2->iss;
> +
> +	if (!csi2->available)
> +		return;
> +
> +	dev_dbg(iss->dev, "-------------CSI2 Register dump-------------\n");
> +
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, SYSCONFIG);
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, SYSSTATUS);
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, IRQENABLE);
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, IRQSTATUS);
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, CTRL);
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, DBG_H);
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, COMPLEXIO_CFG);
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, COMPLEXIO_IRQSTATUS);
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, SHORT_PACKET);
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, COMPLEXIO_IRQENABLE);
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, DBG_P);
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, TIMING);
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_CTRL1(0));
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_CTRL2(0));
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_DAT_OFST(0));
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_PING_ADDR(0));
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_PONG_ADDR(0));
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_IRQENABLE(0));
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_IRQSTATUS(0));
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_CTRL3(0));
> +
> +	dev_dbg(iss->dev, "--------------------------------------------\n");

_If_ this is user-triggered, you might want to consider using debugfs for
the same. Just FYI.

> +}
> +
> +/* -----------------------------------------------------------------------------
> + * Interrupt handling
> + */
> +
> +/*
> + * csi2_isr_buffer - Does buffer handling at end-of-frame
> + * when writing to memory.
> + */
> +static void csi2_isr_buffer(struct iss_csi2_device *csi2)
> +{
> +	struct iss_buffer *buffer;
> +
> +	csi2_ctx_enable(csi2, 0, 0);
> +
> +	buffer = omap4iss_video_buffer_next(&csi2->video_out, 0);
> +
> +	/*
> +	 * Let video queue operation restart engine if there is an underrun
> +	 * condition.
> +	 */
> +	if (buffer == NULL)
> +		return;
> +
> +	csi2_set_outaddr(csi2, buffer->iss_addr);
> +	csi2_ctx_enable(csi2, 0, 1);
> +}
> +
> +static void csi2_isr_ctx(struct iss_csi2_device *csi2,
> +			 struct iss_csi2_ctx_cfg *ctx)
> +{
> +	unsigned int n = ctx->ctxnum;
> +	u32 status;
> +
> +	status = readl(csi2->regs1 + CSI2_CTX_IRQSTATUS(n));
> +	writel(status, csi2->regs1 + CSI2_CTX_IRQSTATUS(n));
> +
> +	/* Propagate frame number */
> +	if (status & CSI2_CTX_IRQ_FS) {
> +		struct iss_pipeline *pipe =
> +				     to_iss_pipeline(&csi2->subdev.entity);
> +		if (pipe->do_propagation)
> +			atomic_inc(&pipe->frame_number);
> +	}
> +
> +	if (!(status & CSI2_CTX_IRQ_FE))
> +		return;
> +
> +	/* Skip interrupts until we reach the frame skip count. The CSI2 will be
> +	 * automatically disabled, as the frame skip count has been programmed
> +	 * in the CSI2_CTx_CTRL1::COUNT field, so reenable it.
> +	 *
> +	 * It would have been nice to rely on the FRAME_NUMBER interrupt instead
> +	 * but it turned out that the interrupt is only generated when the CSI2
> +	 * writes to memory (the CSI2_CTx_CTRL1::COUNT field is decreased
> +	 * correctly and reaches 0 when data is forwarded to the video port only
> +	 * but no interrupt arrives). Maybe a CSI2 hardware bug.
> +	 */
> +	if (csi2->frame_skip) {
> +		csi2->frame_skip--;
> +		if (csi2->frame_skip == 0) {
> +			ctx->format_id = csi2_ctx_map_format(csi2);
> +			csi2_ctx_config(csi2, ctx);

Is configuration of the context needed elsewhere than in streamon? What
changes while streaming?

> +			csi2_ctx_enable(csi2, n, 1);
> +		}
> +		return;
> +	}
> +
> +	if (csi2->output & CSI2_OUTPUT_MEMORY)
> +		csi2_isr_buffer(csi2);
> +}
> +
> +/*
> + * omap4iss_csi2_isr - CSI2 interrupt handling.
> + *
> + * Return -EIO on Transmission error
> + */
> +int omap4iss_csi2_isr(struct iss_csi2_device *csi2)
> +{
> +	u32 csi2_irqstatus, cpxio1_irqstatus;
> +	struct iss_device *iss = csi2->iss;
> +	int retval = 0;
> +
> +	if (!csi2->available)
> +		return -ENODEV;
> +
> +	csi2_irqstatus = readl(csi2->regs1 + CSI2_IRQSTATUS);
> +	writel(csi2_irqstatus, csi2->regs1 + CSI2_IRQSTATUS);
> +
> +	/* Failure Cases */
> +	if (csi2_irqstatus & CSI2_IRQ_COMPLEXIO_ERR) {
> +		cpxio1_irqstatus = readl(csi2->regs1 +
> +					 CSI2_COMPLEXIO_IRQSTATUS);
> +		writel(cpxio1_irqstatus,
> +			csi2->regs1 + CSI2_COMPLEXIO_IRQSTATUS);
> +		dev_dbg(iss->dev, "CSI2: ComplexIO Error IRQ "
> +			"%x\n", cpxio1_irqstatus);
> +		retval = -EIO;
> +	}
> +
> +	if (csi2_irqstatus & (CSI2_IRQ_OCP_ERR |
> +			      CSI2_IRQ_SHORT_PACKET |
> +			      CSI2_IRQ_ECC_NO_CORRECTION |
> +			      CSI2_IRQ_COMPLEXIO_ERR |
> +			      CSI2_IRQ_FIFO_OVF)) {
> +		dev_dbg(iss->dev, "CSI2 Err:"
> +			" OCP:%d,"
> +			" Short_pack:%d,"
> +			" ECC:%d,"
> +			" CPXIO:%d,"
> +			" FIFO_OVF:%d,"
> +			"\n",
> +			(csi2_irqstatus &
> +			 CSI2_IRQ_OCP_ERR) ? 1 : 0,
> +			(csi2_irqstatus &
> +			 CSI2_IRQ_SHORT_PACKET) ? 1 : 0,
> +			(csi2_irqstatus &
> +			 CSI2_IRQ_ECC_NO_CORRECTION) ? 1 : 0,
> +			(csi2_irqstatus &
> +			 CSI2_IRQ_COMPLEXIO_ERR) ? 1 : 0,
> +			(csi2_irqstatus &
> +			 CSI2_IRQ_FIFO_OVF) ? 1 : 0);
> +		retval = -EIO;
> +	}
> +
> +	if (omap4iss_module_sync_is_stopping(&csi2->wait, &csi2->stopping))
> +		return 0;
> +
> +	/* Successful cases */
> +	if (csi2_irqstatus & CSI2_IRQ_CONTEXT0)
> +		csi2_isr_ctx(csi2, &csi2->contexts[0]);
> +
> +	if (csi2_irqstatus & CSI2_IRQ_ECC_CORRECTION)
> +		dev_dbg(iss->dev, "CSI2: ECC correction done\n");
> +
> +	return retval;
> +}
> 
> +/* -----------------------------------------------------------------------------
> + * ISS video operations
> + */
> +
> +/*
> + * csi2_queue - Queues the first buffer when using memory output
> + * @video: The video node
> + * @buffer: buffer to queue
> + */
> +static int csi2_queue(struct iss_video *video, struct iss_buffer *buffer)
> +{
> +	struct iss_device *iss = video->iss;
> +	struct iss_csi2_device *csi2 = &iss->csi2a;
> +
> +	csi2_set_outaddr(csi2, buffer->iss_addr);
> +
> +	/*
> +	 * If streaming was enabled before there was a buffer queued
> +	 * or underrun happened in the ISR, the hardware was not enabled
> +	 * and DMA queue flag ISS_VIDEO_DMAQUEUE_UNDERRUN is still set.
> +	 * Enable it now.
> +	 */
> +	if (csi2->video_out.dmaqueue_flags & ISS_VIDEO_DMAQUEUE_UNDERRUN) {
> +		/* Enable / disable context 0 and IRQs */
> +		csi2_if_enable(csi2, 1);
> +		csi2_ctx_enable(csi2, 0, 1);
> +		iss_video_dmaqueue_flags_clr(&csi2->video_out);
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct iss_video_operations csi2_issvideo_ops = {
> +	.queue = csi2_queue,
> +};
> +
> +/* -----------------------------------------------------------------------------
> + * V4L2 subdev operations
> + */
> +
> +static struct v4l2_mbus_framefmt *
> +__csi2_get_format(struct iss_csi2_device *csi2, struct v4l2_subdev_fh *fh,
> +		  unsigned int pad, enum v4l2_subdev_format_whence which)
> +{
> +	if (which == V4L2_SUBDEV_FORMAT_TRY)
> +		return v4l2_subdev_get_try_format(fh, pad);
> +	else
> +		return &csi2->formats[pad];
> +}
> +
> +static void
> +csi2_try_format(struct iss_csi2_device *csi2, struct v4l2_subdev_fh *fh,
> +		unsigned int pad, struct v4l2_mbus_framefmt *fmt,
> +		enum v4l2_subdev_format_whence which)
> +{
> +	enum v4l2_mbus_pixelcode pixelcode;
> +	struct v4l2_mbus_framefmt *format;
> +	const struct iss_format_info *info;
> +	unsigned int i;
> +
> +	switch (pad) {
> +	case CSI2_PAD_SINK:
> +		/* Clamp the width and height to valid range (1-8191). */
> +		for (i = 0; i < ARRAY_SIZE(csi2_input_fmts); i++) {
> +			if (fmt->code == csi2_input_fmts[i])
> +				break;
> +		}
> +
> +		/* If not found, use SGRBG10 as default */
> +		if (i >= ARRAY_SIZE(csi2_input_fmts))
> +			fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10;
> +
> +		fmt->width = clamp_t(u32, fmt->width, 1, 8191);
> +		fmt->height = clamp_t(u32, fmt->height, 1, 8191);
> +		break;
> +
> +	case CSI2_PAD_SOURCE:
> +		/* Source format same as sink format, except for DPCM
> +		 * compression.
> +		 */
> +		pixelcode = fmt->code;
> +		format = __csi2_get_format(csi2, fh, CSI2_PAD_SINK, which);
> +		memcpy(fmt, format, sizeof(*fmt));
> +
> +		/*
> +		 * Only Allow DPCM decompression, and check that the
> +		 * pattern is preserved
> +		 */
> +		info = omap4iss_video_format_info(fmt->code);
> +		if (info->uncompressed == pixelcode)
> +			fmt->code = pixelcode;
> +		break;
> +	}
> +
> +	/* RGB, non-interlaced */
> +	fmt->colorspace = V4L2_COLORSPACE_SRGB;
> +	fmt->field = V4L2_FIELD_NONE;
> +}
> +
> +/*
> + * csi2_enum_mbus_code - Handle pixel format enumeration
> + * @sd     : pointer to v4l2 subdev structure
> + * @fh     : V4L2 subdev file handle
> + * @code   : pointer to v4l2_subdev_mbus_code_enum structure
> + * return -EINVAL or zero on success
> + */
> +static int csi2_enum_mbus_code(struct v4l2_subdev *sd,
> +			       struct v4l2_subdev_fh *fh,
> +			       struct v4l2_subdev_mbus_code_enum *code)
> +{
> +	struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
> +	struct v4l2_mbus_framefmt *format;
> +	const struct iss_format_info *info;
> +
> +	if (code->pad == CSI2_PAD_SINK) {
> +		if (code->index >= ARRAY_SIZE(csi2_input_fmts))
> +			return -EINVAL;
> +
> +		code->code = csi2_input_fmts[code->index];
> +	} else {
> +		format = __csi2_get_format(csi2, fh, CSI2_PAD_SINK,
> +					   V4L2_SUBDEV_FORMAT_TRY);
> +		switch (code->index) {
> +		case 0:
> +			/* Passthrough sink pad code */
> +			code->code = format->code;
> +			break;
> +		case 1:
> +			/* Uncompressed code */
> +			info = omap4iss_video_format_info(format->code);
> +			if (info->uncompressed == format->code)
> +				return -EINVAL;
> +
> +			code->code = info->uncompressed;
> +			break;
> +		default:
> +			return -EINVAL;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int csi2_enum_frame_size(struct v4l2_subdev *sd,
> +				struct v4l2_subdev_fh *fh,
> +				struct v4l2_subdev_frame_size_enum *fse)
> +{
> +	struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
> +	struct v4l2_mbus_framefmt format;
> +
> +	if (fse->index != 0)
> +		return -EINVAL;
> +
> +	format.code = fse->code;
> +	format.width = 1;
> +	format.height = 1;
> +	csi2_try_format(csi2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
> +	fse->min_width = format.width;
> +	fse->min_height = format.height;
> +
> +	if (format.code != fse->code)
> +		return -EINVAL;
> +
> +	format.code = fse->code;
> +	format.width = -1;
> +	format.height = -1;
> +	csi2_try_format(csi2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
> +	fse->max_width = format.width;
> +	fse->max_height = format.height;
> +
> +	return 0;
> +}
> +
> +/*
> + * csi2_get_format - Handle get format by pads subdev method
> + * @sd : pointer to v4l2 subdev structure
> + * @fh : V4L2 subdev file handle
> + * @fmt: pointer to v4l2 subdev format structure
> + * return -EINVAL or zero on success
> + */
> +static int csi2_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
> +			   struct v4l2_subdev_format *fmt)
> +{
> +	struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
> +	struct v4l2_mbus_framefmt *format;
> +
> +	format = __csi2_get_format(csi2, fh, fmt->pad, fmt->which);
> +	if (format == NULL)
> +		return -EINVAL;
> +
> +	fmt->format = *format;
> +	return 0;
> +}
> +
> +/*
> + * csi2_set_format - Handle set format by pads subdev method
> + * @sd : pointer to v4l2 subdev structure
> + * @fh : V4L2 subdev file handle
> + * @fmt: pointer to v4l2 subdev format structure
> + * return -EINVAL or zero on success
> + */
> +static int csi2_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
> +			   struct v4l2_subdev_format *fmt)
> +{
> +	struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
> +	struct v4l2_mbus_framefmt *format;
> +
> +	format = __csi2_get_format(csi2, fh, fmt->pad, fmt->which);
> +	if (format == NULL)
> +		return -EINVAL;
> +
> +	csi2_try_format(csi2, fh, fmt->pad, &fmt->format, fmt->which);
> +	*format = fmt->format;
> +
> +	/* Propagate the format from sink to source */
> +	if (fmt->pad == CSI2_PAD_SINK) {
> +		format = __csi2_get_format(csi2, fh, CSI2_PAD_SOURCE,
> +					   fmt->which);
> +		*format = fmt->format;
> +		csi2_try_format(csi2, fh, CSI2_PAD_SOURCE, format, fmt->which);
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * csi2_init_formats - Initialize formats on all pads
> + * @sd: ISS CSI2 V4L2 subdevice
> + * @fh: V4L2 subdev file handle
> + *
> + * Initialize all pad formats with default values. If fh is not NULL, try
> + * formats are initialized on the file handle. Otherwise active formats are
> + * initialized on the device.
> + */
> +static int csi2_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
> +{
> +	struct v4l2_subdev_format format;
> +
> +	memset(&format, 0, sizeof(format));
> +	format.pad = CSI2_PAD_SINK;
> +	format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
> +	format.format.code = V4L2_MBUS_FMT_SGRBG10_1X10;
> +	format.format.width = 4096;
> +	format.format.height = 4096;
> +	csi2_set_format(sd, fh, &format);
> +
> +	return 0;
> +}
> +
> +/*
> + * csi2_set_stream - Enable/Disable streaming on the CSI2 module
> + * @sd: ISS CSI2 V4L2 subdevice
> + * @enable: ISS pipeline stream state
> + *
> + * Return 0 on success or a negative error code otherwise.
> + */
> +static int csi2_set_stream(struct v4l2_subdev *sd, int enable)
> +{
> +	struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
> +	struct iss_device *iss = csi2->iss;
> +	struct iss_pipeline *pipe = to_iss_pipeline(&csi2->subdev.entity);
> +	struct iss_video *video_out = &csi2->video_out;
> +
> +	if (csi2->state == ISS_PIPELINE_STREAM_STOPPED) {
> +		if (enable == ISS_PIPELINE_STREAM_STOPPED)
> +			return 0;
> +
> +		omap4iss_subclk_enable(iss, OMAP4_ISS_SUBCLK_CSI2_A);
> +	}
> +
> +	switch (enable) {
> +	case ISS_PIPELINE_STREAM_CONTINUOUS:
> +		if (omap4iss_csiphy_acquire(csi2->phy) < 0)
> +			return -ENODEV;
> +		csi2->use_fs_irq = pipe->do_propagation;
> +		csi2_configure(csi2);
> +		csi2_print_status(csi2);
> +
> +		/*
> +		 * When outputting to memory with no buffer available, let the
> +		 * buffer queue handler start the hardware. A DMA queue flag
> +		 * ISS_VIDEO_DMAQUEUE_QUEUED will be set as soon as there is
> +		 * a buffer available.
> +		 */
> +		if (csi2->output & CSI2_OUTPUT_MEMORY &&
> +		    !(video_out->dmaqueue_flags & ISS_VIDEO_DMAQUEUE_QUEUED))
> +			break;
> +		/* Enable context 0 and IRQs */
> +		atomic_set(&csi2->stopping, 0);
> +		csi2_ctx_enable(csi2, 0, 1);
> +		csi2_if_enable(csi2, 1);
> +		iss_video_dmaqueue_flags_clr(video_out);
> +		break;
> +
> +	case ISS_PIPELINE_STREAM_STOPPED:
> +		if (csi2->state == ISS_PIPELINE_STREAM_STOPPED)
> +			return 0;
> +		if (omap4iss_module_sync_idle(&sd->entity, &csi2->wait,
> +					      &csi2->stopping))
> +			dev_dbg(iss->dev, "%s: module stop timeout.\n",
> +				sd->name);
> +		csi2_ctx_enable(csi2, 0, 0);
> +		csi2_if_enable(csi2, 0);
> +		csi2_irq_ctx_set(csi2, 0);
> +		omap4iss_csiphy_release(csi2->phy);
> +		omap4iss_subclk_disable(iss, OMAP4_ISS_SUBCLK_CSI2_A);
> +		iss_video_dmaqueue_flags_clr(video_out);
> +		break;
> +	}
> +
> +	csi2->state = enable;
> +	return 0;
> +}
> +
> +/* subdev video operations */
> +static const struct v4l2_subdev_video_ops csi2_video_ops = {
> +	.s_stream = csi2_set_stream,
> +};
> +
> +/* subdev pad operations */
> +static const struct v4l2_subdev_pad_ops csi2_pad_ops = {
> +	.enum_mbus_code = csi2_enum_mbus_code,
> +	.enum_frame_size = csi2_enum_frame_size,
> +	.get_fmt = csi2_get_format,
> +	.set_fmt = csi2_set_format,
> +};
> +
> +/* subdev operations */
> +static const struct v4l2_subdev_ops csi2_ops = {
> +	.video = &csi2_video_ops,
> +	.pad = &csi2_pad_ops,
> +};
> +
> +/* subdev internal operations */
> +static const struct v4l2_subdev_internal_ops csi2_internal_ops = {
> +	.open = csi2_init_formats,
> +};
> +
> +/* -----------------------------------------------------------------------------
> + * Media entity operations
> + */
> +
> +/*
> + * csi2_link_setup - Setup CSI2 connections.
> + * @entity : Pointer to media entity structure
> + * @local  : Pointer to local pad array
> + * @remote : Pointer to remote pad array
> + * @flags  : Link flags
> + * return -EINVAL or zero on success
> + */
> +static int csi2_link_setup(struct media_entity *entity,
> +			   const struct media_pad *local,
> +			   const struct media_pad *remote, u32 flags)
> +{
> +	struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
> +	struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
> +	struct iss_csi2_ctrl_cfg *ctrl = &csi2->ctrl;
> +
> +	/*
> +	 * The ISS core doesn't support pipelines with multiple video outputs.
> +	 * Revisit this when it will be implemented, and return -EBUSY for now.
> +	 */
> +
> +	switch (local->index | media_entity_type(remote->entity)) {
> +	case CSI2_PAD_SOURCE | MEDIA_ENT_T_DEVNODE:
> +		if (flags & MEDIA_LNK_FL_ENABLED) {
> +			if (csi2->output & ~CSI2_OUTPUT_MEMORY)
> +				return -EBUSY;
> +			csi2->output |= CSI2_OUTPUT_MEMORY;
> +		} else {
> +			csi2->output &= ~CSI2_OUTPUT_MEMORY;
> +		}
> +		break;
> +
> +	case CSI2_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV:
> +		if (flags & MEDIA_LNK_FL_ENABLED) {
> +			if (csi2->output & ~CSI2_OUTPUT_IPIPEIF)
> +				return -EBUSY;
> +			csi2->output |= CSI2_OUTPUT_IPIPEIF;
> +		} else {
> +			csi2->output &= ~CSI2_OUTPUT_IPIPEIF;
> +		}
> +		break;
> +
> +	default:
> +		/* Link from camera to CSI2 is fixed... */
> +		return -EINVAL;
> +	}
> +
> +	ctrl->vp_only_enable =
> +		(csi2->output & CSI2_OUTPUT_MEMORY) ? false : true;
> +	ctrl->vp_clk_enable = !!(csi2->output & CSI2_OUTPUT_IPIPEIF);
> +
> +	return 0;
> +}
> +
> +/* media operations */
> +static const struct media_entity_operations csi2_media_ops = {
> +	.link_setup = csi2_link_setup,
> +};
> +
> +/*
> + * csi2_init_entities - Initialize subdev and media entity.
> + * @csi2: Pointer to csi2 structure.
> + * return -ENOMEM or zero on success
> + */
> +static int csi2_init_entities(struct iss_csi2_device *csi2)
> +{
> +	struct v4l2_subdev *sd = &csi2->subdev;
> +	struct media_pad *pads = csi2->pads;
> +	struct media_entity *me = &sd->entity;
> +	int ret;
> +
> +	v4l2_subdev_init(sd, &csi2_ops);
> +	sd->internal_ops = &csi2_internal_ops;
> +	strlcpy(sd->name, "OMAP4 ISS CSI2a", sizeof(sd->name));
> +
> +	sd->grp_id = 1 << 16;	/* group ID for iss subdevs */
> +	v4l2_set_subdevdata(sd, csi2);
> +	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> +
> +	pads[CSI2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
> +	pads[CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
> +
> +	me->ops = &csi2_media_ops;
> +	ret = media_entity_init(me, CSI2_PADS_NUM, pads, 0);
> +	if (ret < 0)
> +		return ret;
> +
> +	csi2_init_formats(sd, NULL);
> +
> +	/* Video device node */
> +	csi2->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> +	csi2->video_out.ops = &csi2_issvideo_ops;
> +	csi2->video_out.bpl_alignment = 32;
> +	csi2->video_out.bpl_zero_padding = 1;
> +	csi2->video_out.bpl_max = 0x1ffe0;
> +	csi2->video_out.iss = csi2->iss;
> +	csi2->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 3;
> +
> +	ret = omap4iss_video_init(&csi2->video_out, "CSI2a");
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Connect the CSI2 subdev to the video node. */
> +	ret = media_entity_create_link(&csi2->subdev.entity, CSI2_PAD_SOURCE,
> +				       &csi2->video_out.video.entity, 0, 0);
> +	if (ret < 0)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +void omap4iss_csi2_unregister_entities(struct iss_csi2_device *csi2)
> +{
> +	media_entity_cleanup(&csi2->subdev.entity);
> +
> +	v4l2_device_unregister_subdev(&csi2->subdev);
> +	omap4iss_video_unregister(&csi2->video_out);
> +}
> +
> +int omap4iss_csi2_register_entities(struct iss_csi2_device *csi2,
> +				    struct v4l2_device *vdev)
> +{
> +	int ret;
> +
> +	/* Register the subdev and video nodes. */
> +	ret = v4l2_device_register_subdev(vdev, &csi2->subdev);
> +	if (ret < 0)
> +		goto error;
> +
> +	ret = omap4iss_video_register(&csi2->video_out, vdev);
> +	if (ret < 0)
> +		goto error;
> +
> +	return 0;
> +
> +error:
> +	omap4iss_csi2_unregister_entities(csi2);
> +	return ret;
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * ISS CSI2 initialisation and cleanup
> + */
> +
> +/*
> + * omap4iss_csi2_cleanup - Routine for module driver cleanup
> + */
> +void omap4iss_csi2_cleanup(struct iss_device *iss)
> +{
> +}
> +
> +/*
> + * omap4iss_csi2_init - Routine for module driver init
> + */
> +int omap4iss_csi2_init(struct iss_device *iss)
> +{
> +	struct iss_csi2_device *csi2a = &iss->csi2a;
> +	int ret;
> +
> +	csi2a->iss = iss;
> +	csi2a->available = 1;
> +	csi2a->regs1 = iss->regs[OMAP4_ISS_MEM_CSI2_A_REGS1];
> +	csi2a->phy = &iss->csiphy1;
> +	csi2a->state = ISS_PIPELINE_STREAM_STOPPED;
> +	init_waitqueue_head(&csi2a->wait);
> +
> +	ret = csi2_init_entities(csi2a);
> +	if (ret < 0)
> +		goto fail;
> +
> +	return 0;
> +fail:
> +	omap4iss_csi2_cleanup(iss);
> +	return ret;
> +}
> diff --git a/drivers/media/video/omap4iss/iss_csi2.h b/drivers/media/video/omap4iss/iss_csi2.h
> new file mode 100644
> index 0000000..4fa94cf
> --- /dev/null
> +++ b/drivers/media/video/omap4iss/iss_csi2.h
> @@ -0,0 +1,166 @@
> +/*
> + * iss_csi2.h
> + *
> + * TI OMAP4 ISS - CSI2 module
> + *
> + * Copyright (C) 2011 Texas Instruments, Inc.
> + *
> + * Contacts: Sergio Aguirre <saaguirre@ti.com>
> + *
> + * 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.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
> + * 02110-1301 USA
> + */
> +
> +#ifndef OMAP4_ISS_CSI2_H
> +#define OMAP4_ISS_CSI2_H
> +
> +#include <linux/types.h>
> +#include <linux/videodev2.h>
> +
> +#include "iss_video.h"
> +
> +struct iss_csiphy;
> +
> +/* This is not an exhaustive list */
> +enum iss_csi2_pix_formats {
> +	CSI2_PIX_FMT_OTHERS = 0,
> +	CSI2_PIX_FMT_YUV422_8BIT = 0x1e,
> +	CSI2_PIX_FMT_YUV422_8BIT_VP = 0x9e,
> +	CSI2_PIX_FMT_RAW10_EXP16 = 0xab,
> +	CSI2_PIX_FMT_RAW10_EXP16_VP = 0x12f,
> +	CSI2_PIX_FMT_RAW8 = 0x2a,
> +	CSI2_PIX_FMT_RAW8_DPCM10_EXP16 = 0x2aa,
> +	CSI2_PIX_FMT_RAW8_DPCM10_VP = 0x32a,
> +	CSI2_PIX_FMT_RAW8_VP = 0x12a,
> +	CSI2_USERDEF_8BIT_DATA1_DPCM10_VP = 0x340,
> +	CSI2_USERDEF_8BIT_DATA1_DPCM10 = 0x2c0,
> +	CSI2_USERDEF_8BIT_DATA1 = 0x40,

What are the USERDEF formats?

> +};
> +
> +enum iss_csi2_irqevents {
> +	OCP_ERR_IRQ = 0x4000,
> +	SHORT_PACKET_IRQ = 0x2000,
> +	ECC_CORRECTION_IRQ = 0x1000,
> +	ECC_NO_CORRECTION_IRQ = 0x800,
> +	COMPLEXIO2_ERR_IRQ = 0x400,
> +	COMPLEXIO1_ERR_IRQ = 0x200,
> +	FIFO_OVF_IRQ = 0x100,
> +	CONTEXT7 = 0x80,
> +	CONTEXT6 = 0x40,
> +	CONTEXT5 = 0x20,
> +	CONTEXT4 = 0x10,
> +	CONTEXT3 = 0x8,
> +	CONTEXT2 = 0x4,
> +	CONTEXT1 = 0x2,
> +	CONTEXT0 = 0x1,
> +};
> +
> +enum iss_csi2_ctx_irqevents {
> +	CTX_ECC_CORRECTION = 0x100,
> +	CTX_LINE_NUMBER = 0x80,
> +	CTX_FRAME_NUMBER = 0x40,
> +	CTX_CS = 0x20,
> +	CTX_LE = 0x8,
> +	CTX_LS = 0x4,
> +	CTX_FE = 0x2,
> +	CTX_FS = 0x1,
> +};
> +
> +enum iss_csi2_frame_mode {
> +	ISS_CSI2_FRAME_IMMEDIATE,
> +	ISS_CSI2_FRAME_AFTERFEC,
> +};
> +
> +#define ISS_CSI2_MAX_CTX_NUM	7
> +
> +struct iss_csi2_ctx_cfg {
> +	u8 ctxnum;		/* context number 0 - 7 */
> +	u8 dpcm_decompress;
> +
> +	/* Fields in CSI2_CTx_CTRL2 - locked by CSI2_CTx_CTRL1.CTX_EN */
> +	u8 virtual_id;
> +	u16 format_id;		/* as in CSI2_CTx_CTRL2[9:0] */
> +	u8 dpcm_predictor;	/* 1: simple, 0: advanced */
> +
> +	/* Fields in CSI2_CTx_CTRL1/3 - Shadowed */
> +	u16 alpha;
> +	u16 data_offset;
> +	u32 ping_addr;
> +	u32 pong_addr;
> +	u8 eof_enabled;
> +	u8 eol_enabled;
> +	u8 checksum_enabled;
> +	u8 enabled;
> +};
> +
> +struct iss_csi2_timing_cfg {
> +	u8 ionum;			/* IO1 or IO2 as in CSI2_TIMING */
> +	unsigned force_rx_mode:1;
> +	unsigned stop_state_16x:1;
> +	unsigned stop_state_4x:1;
> +	u16 stop_state_counter;
> +};
> +
> +struct iss_csi2_ctrl_cfg {
> +	bool vp_clk_enable;
> +	bool vp_only_enable;
> +	u8 vp_out_ctrl;
> +	enum iss_csi2_frame_mode frame_mode;
> +	bool ecc_enable;
> +	bool if_enable;
> +};
> +
> +#define CSI2_PAD_SINK		0
> +#define CSI2_PAD_SOURCE		1
> +#define CSI2_PADS_NUM		2
> +
> +#define CSI2_OUTPUT_IPIPEIF	(1 << 0)
> +#define CSI2_OUTPUT_MEMORY	(1 << 1)
> +
> +struct iss_csi2_device {
> +	struct v4l2_subdev subdev;
> +	struct media_pad pads[CSI2_PADS_NUM];
> +	struct v4l2_mbus_framefmt formats[CSI2_PADS_NUM];
> +
> +	struct iss_video video_out;
> +	struct iss_device *iss;
> +
> +	u8 available;		/* Is the IP present on the silicon? */
> +
> +	/* Pointer to register remaps into kernel space */
> +	void __iomem *regs1;
> +	void __iomem *regs2;
> +
> +	u32 output; /* output to IPIPEIF, memory or both? */
> +	bool dpcm_decompress;
> +	unsigned int frame_skip;
> +	bool use_fs_irq;
> +
> +	struct iss_csiphy *phy;
> +	struct iss_csi2_ctx_cfg contexts[ISS_CSI2_MAX_CTX_NUM + 1];
> +	struct iss_csi2_timing_cfg timing[2];
> +	struct iss_csi2_ctrl_cfg ctrl;
> +	enum iss_pipeline_stream_state state;
> +	wait_queue_head_t wait;
> +	atomic_t stopping;
> +};
> +
> +int omap4iss_csi2_isr(struct iss_csi2_device *csi2);
> +int omap4iss_csi2_reset(struct iss_csi2_device *csi2);
> +int omap4iss_csi2_init(struct iss_device *iss);
> +void omap4iss_csi2_cleanup(struct iss_device *iss);
> +void omap4iss_csi2_unregister_entities(struct iss_csi2_device *csi2);
> +int omap4iss_csi2_register_entities(struct iss_csi2_device *csi2,
> +				    struct v4l2_device *vdev);
> +#endif	/* OMAP4_ISS_CSI2_H */
> diff --git a/drivers/media/video/omap4iss/iss_csiphy.c b/drivers/media/video/omap4iss/iss_csiphy.c
> new file mode 100644
> index 0000000..9545622
> --- /dev/null
> +++ b/drivers/media/video/omap4iss/iss_csiphy.c
> @@ -0,0 +1,215 @@
> +/*
> + * iss_csiphy.c
> + *
> + * TI OMAP4 ISS - CSI PHY module
> + *
> + * Copyright (C) 2011 Texas Instruments, Inc.
> + *
> + * Contacts: Sergio Aguirre <saaguirre@ti.com>
> + *
> + * 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.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
> + * 02110-1301 USA
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +
> +#include "iss.h"
> +#include "iss_regs.h"
> +#include "iss_csiphy.h"
> +
> +/*
> + * csiphy_lanes_config - Configuration of CSIPHY lanes.
> + *
> + * Updates HW configuration.
> + * Called with phy->mutex taken.
> + */
> +static void csiphy_lanes_config(struct iss_csiphy *phy)
> +{
> +	unsigned int i;
> +	u32 reg;
> +
> +	reg = readl(phy->cfg_regs + CSI2_COMPLEXIO_CFG);
> +
> +	for (i = 0; i < phy->num_data_lanes; i++) {
> +		reg &= ~(CSI2_COMPLEXIO_CFG_DATA_POL(i + 1) |
> +			 CSI2_COMPLEXIO_CFG_DATA_POSITION_MASK(i + 1));
> +		reg |= (phy->lanes.data[i].pol ?
> +			CSI2_COMPLEXIO_CFG_DATA_POL(i + 1) : 0);
> +		reg |= (phy->lanes.data[i].pos <<
> +			CSI2_COMPLEXIO_CFG_DATA_POSITION_SHIFT(i + 1));
> +	}
> +
> +	reg &= ~(CSI2_COMPLEXIO_CFG_CLOCK_POL |
> +		 CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK);
> +	reg |= phy->lanes.clk.pol ? CSI2_COMPLEXIO_CFG_CLOCK_POL : 0;
> +	reg |= phy->lanes.clk.pos << CSI2_COMPLEXIO_CFG_CLOCK_POSITION_SHIFT;
> +
> +	writel(reg, phy->cfg_regs + CSI2_COMPLEXIO_CFG);
> +}
> +
> +/*
> + * csiphy_set_power
> + * @power: Power state to be set.
> + *
> + * Returns 0 if successful, or -EBUSY if the retry count is exceeded.
> + */
> +static int csiphy_set_power(struct iss_csiphy *phy, u32 power)
> +{
> +	u32 reg;
> +	u8 retry_count;
> +
> +	writel((readl(phy->cfg_regs + CSI2_COMPLEXIO_CFG) &
> +		~CSI2_COMPLEXIO_CFG_PWD_CMD_MASK) |
> +		power,
> +		phy->cfg_regs + CSI2_COMPLEXIO_CFG);
> +
> +	retry_count = 0;
> +	do {
> +		udelay(50);

I guess udelay(1) should do. There's no reason to busyloop longer, if at
all. How much is a typical value for retry_count?

> +		reg = readl(phy->cfg_regs + CSI2_COMPLEXIO_CFG) &
> +				CSI2_COMPLEXIO_CFG_PWD_STATUS_MASK;
> +
> +		if (reg != power >> 2)
> +			retry_count++;
> +
> +	} while ((reg != power >> 2) && (retry_count < 100));
> +
> +	if (retry_count == 100) {
> +		printk(KERN_ERR "CSI2 CIO set power failed!\n");
> +		return -EBUSY;
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * csiphy_dphy_config - Configure CSI2 D-PHY parameters.
> + *
> + * Called with phy->mutex taken.
> + */
> +static void csiphy_dphy_config(struct iss_csiphy *phy)
> +{
> +	u32 reg;
> +
> +	/* Set up REGISTER0 */
> +	reg = readl(phy->phy_regs + REGISTER0);
> +
> +	reg &= ~(REGISTER0_THS_TERM_MASK |
> +		 REGISTER0_THS_SETTLE_MASK);
> +	reg |= phy->dphy.ths_term << REGISTER0_THS_TERM_SHIFT;
> +	reg |= phy->dphy.ths_settle << REGISTER0_THS_SETTLE_SHIFT;
> +
> +	writel(reg, phy->phy_regs + REGISTER0);
> +
> +	/* Set up REGISTER1 */
> +	reg = readl(phy->phy_regs + REGISTER1);
> +
> +	reg &= ~(REGISTER1_TCLK_TERM_MASK |
> +		 REGISTER1_CTRLCLK_DIV_FACTOR_MASK |
> +		 REGISTER1_TCLK_SETTLE_MASK);
> +	reg |= phy->dphy.tclk_term << REGISTER1_TCLK_TERM_SHIFT;
> +	reg |= phy->dphy.tclk_miss << REGISTER1_CTRLCLK_DIV_FACTOR_SHIFT;
> +	reg |= phy->dphy.tclk_settle << REGISTER1_TCLK_SETTLE_SHIFT;
> +
> +	writel(reg, phy->phy_regs + REGISTER1);

REGISTER0 or 1 aren't really used elsewhere. You could omit reading them
back.

> +}
> +
> +static int csiphy_config(struct iss_csiphy *phy,
> +			 struct iss_csiphy_dphy_cfg *dphy,
> +			 struct iss_csiphy_lanes_cfg *lanes)
> +{
> +	unsigned int used_lanes = 0;
> +	unsigned int i;
> +
> +	/* Clock and data lanes verification */
> +	for (i = 0; i < phy->num_data_lanes; i++) {
> +		if (lanes->data[i].pos == 0)
> +			continue;
> +
> +		if (lanes->data[i].pol > 1 || lanes->data[i].pos > 5)
> +			return -EINVAL;
> +
> +		if (used_lanes & (1 << lanes->data[i].pos))
> +			return -EINVAL;
> +
> +		used_lanes |= 1 << lanes->data[i].pos;
> +	}
> +
> +	if (lanes->clk.pol > 1 || lanes->clk.pos > 4)
> +		return -EINVAL;
> +
> +	if (lanes->clk.pos == 0 || used_lanes & (1 << lanes->clk.pos))
> +		return -EINVAL;
> +
> +	mutex_lock(&phy->mutex);
> +	phy->dphy = *dphy;
> +	phy->lanes = *lanes;
> +	mutex_unlock(&phy->mutex);
> +
> +	return 0;
> +}
> +
> +int omap4iss_csiphy_acquire(struct iss_csiphy *phy)
> +{
> +	int rval;
> +
> +	mutex_lock(&phy->mutex);
> +
> +	rval = omap4iss_csi2_reset(phy->csi2);
> +	if (rval)
> +		goto done;
> +
> +	csiphy_dphy_config(phy);
> +	csiphy_lanes_config(phy);
> +
> +	rval = csiphy_set_power(phy, CSI2_COMPLEXIO_CFG_PWD_CMD_ON);
> +	if (rval)
> +		goto done;
> +
> +	phy->phy_in_use = 1;
> +
> +done:
> +	mutex_unlock(&phy->mutex);
> +	return rval;
> +}
> +
> +void omap4iss_csiphy_release(struct iss_csiphy *phy)
> +{
> +	mutex_lock(&phy->mutex);
> +	if (phy->phy_in_use) {
> +		csiphy_set_power(phy, CSI2_COMPLEXIO_CFG_PWD_CMD_OFF);
> +		phy->phy_in_use = 0;
> +	}
> +	mutex_unlock(&phy->mutex);
> +}
> +
> +/*
> + * omap4iss_csiphy_init - Initialize the CSI PHY frontends
> + */
> +int omap4iss_csiphy_init(struct iss_device *iss)
> +{
> +	struct iss_csiphy *phy1 = &iss->csiphy1;
> +
> +	iss->platform_cb.csiphy_config = csiphy_config;
> +
> +	phy1->iss = iss;
> +	phy1->csi2 = &iss->csi2a;
> +	phy1->num_data_lanes = ISS_CSIPHY1_NUM_DATA_LANES;
> +	phy1->cfg_regs = iss->regs[OMAP4_ISS_MEM_CSI2_A_REGS1];
> +	phy1->phy_regs = iss->regs[OMAP4_ISS_MEM_CAMERARX_CORE1];
> +	mutex_init(&phy1->mutex);
> +
> +	return 0;
> +}
> diff --git a/drivers/media/video/omap4iss/iss_csiphy.h b/drivers/media/video/omap4iss/iss_csiphy.h
> new file mode 100644
> index 0000000..c513ba8
> --- /dev/null
> +++ b/drivers/media/video/omap4iss/iss_csiphy.h
> @@ -0,0 +1,69 @@
> +/*
> + * iss_csiphy.h
> + *
> + * TI OMAP4 ISS - CSI PHY module
> + *
> + * Copyright (C) 2011 Texas Instruments, Inc.
> + *
> + * Contacts: Sergio Aguirre <saaguirre@ti.com>
> + *
> + * 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.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
> + * 02110-1301 USA
> + */
> +
> +#ifndef OMAP4_ISS_CSI_PHY_H
> +#define OMAP4_ISS_CSI_PHY_H
> +
> +struct iss_csi2_device;
> +
> +struct csiphy_lane {
> +	u8 pos;
> +	u8 pol;
> +};
> +
> +#define ISS_CSIPHY1_NUM_DATA_LANES	4
> +
> +struct iss_csiphy_lanes_cfg {
> +	struct csiphy_lane data[ISS_CSIPHY1_NUM_DATA_LANES];
> +	struct csiphy_lane clk;
> +};
> +
> +struct iss_csiphy_dphy_cfg {
> +	u8 ths_term;
> +	u8 ths_settle;
> +	u8 tclk_term;
> +	unsigned tclk_miss:1;
> +	u8 tclk_settle;
> +};
> +
> +struct iss_csiphy {
> +	struct iss_device *iss;
> +	struct mutex mutex;	/* serialize csiphy configuration */
> +	u8 phy_in_use;
> +	struct iss_csi2_device *csi2;
> +
> +	/* Pointer to register remaps into kernel space */
> +	void __iomem *cfg_regs;
> +	void __iomem *phy_regs;
> +
> +	u8 num_data_lanes;	/* number of CSI2 Data Lanes supported */
> +	struct iss_csiphy_lanes_cfg lanes;
> +	struct iss_csiphy_dphy_cfg dphy;
> +};
> +
> +int omap4iss_csiphy_acquire(struct iss_csiphy *phy);
> +void omap4iss_csiphy_release(struct iss_csiphy *phy);
> +int omap4iss_csiphy_init(struct iss_device *iss);
> +
> +#endif	/* OMAP4_ISS_CSI_PHY_H */
> diff --git a/drivers/media/video/omap4iss/iss_regs.h b/drivers/media/video/omap4iss/iss_regs.h
> new file mode 100644
> index 0000000..0bd70ac
> --- /dev/null
> +++ b/drivers/media/video/omap4iss/iss_regs.h
> @@ -0,0 +1,238 @@
> +/*
> + * iss_regs.h
> + *
> + * Copyright (C) 2011 Texas Instruments.
> + *
> + * Author: Sergio Aguirre <saaguirre@ti.com>
> + */
> +
> +#ifndef _OMAP4_ISS_REGS_H_
> +#define _OMAP4_ISS_REGS_H_
> +
> +/* ISS */
> +#define ISS_HL_REVISION					(0x0)

No need for parenthesis for hexadecimals.

> +#define ISS_HL_SYSCONFIG				(0x10)
> +#define ISS_HL_SYSCONFIG_IDLEMODE_SHIFT			2
> +#define ISS_HL_SYSCONFIG_IDLEMODE_FORCEIDLE		0x0
> +#define ISS_HL_SYSCONFIG_IDLEMODE_NOIDLE		0x1
> +#define ISS_HL_SYSCONFIG_IDLEMODE_SMARTIDLE		0x2
> +#define ISS_HL_SYSCONFIG_SOFTRESET			(1 << 0)
> +
> +#define ISS_HL_IRQSTATUS_5				(0x24 + (0x10 * 5))
> +#define ISS_HL_IRQENABLE_5_SET				(0x28 + (0x10 * 5))
> +#define ISS_HL_IRQENABLE_5_CLR				(0x2C + (0x10 * 5))
> +
> +#define ISS_HL_IRQ_BTE					(1 << 11)
> +#define ISS_HL_IRQ_CBUFF				(1 << 10)
> +#define ISS_HL_IRQ_CSIA					(1 << 4)
> +
> +#define ISS_CTRL					(0x80)
> +
> +#define ISS_CLKCTRL					(0x84)
> +#define ISS_CLKCTRL_VPORT2_CLK				(1 << 30)
> +#define ISS_CLKCTRL_VPORT1_CLK				(1 << 29)
> +#define ISS_CLKCTRL_VPORT0_CLK				(1 << 28)
> +#define ISS_CLKCTRL_CCP2				(1 << 4)
> +#define ISS_CLKCTRL_CSI2_B				(1 << 3)
> +#define ISS_CLKCTRL_CSI2_A				(1 << 2)
> +#define ISS_CLKCTRL_ISP					(1 << 1)
> +#define ISS_CLKCTRL_SIMCOP				(1 << 0)
> +
> +#define ISS_CLKSTAT					(0x88)
> +#define ISS_CLKSTAT_VPORT2_CLK				(1 << 30)
> +#define ISS_CLKSTAT_VPORT1_CLK				(1 << 29)
> +#define ISS_CLKSTAT_VPORT0_CLK				(1 << 28)
> +#define ISS_CLKSTAT_CCP2				(1 << 4)
> +#define ISS_CLKSTAT_CSI2_B				(1 << 3)
> +#define ISS_CLKSTAT_CSI2_A				(1 << 2)
> +#define ISS_CLKSTAT_ISP					(1 << 1)
> +#define ISS_CLKSTAT_SIMCOP				(1 << 0)
> +
> +#define ISS_PM_STATUS					(0x8C)
> +#define ISS_PM_STATUS_CBUFF_PM_MASK			(3 << 12)
> +#define ISS_PM_STATUS_BTE_PM_MASK			(3 << 10)
> +#define ISS_PM_STATUS_SIMCOP_PM_MASK			(3 << 8)
> +#define ISS_PM_STATUS_ISP_PM_MASK			(3 << 6)
> +#define ISS_PM_STATUS_CCP2_PM_MASK			(3 << 4)
> +#define ISS_PM_STATUS_CSI2_B_PM_MASK			(3 << 2)
> +#define ISS_PM_STATUS_CSI2_A_PM_MASK			(3 << 0)
> +
> +#define REGISTER0					(0x0)
> +#define REGISTER0_HSCLOCKCONFIG				(1 << 24)
> +#define REGISTER0_THS_TERM_MASK				(0xFF << 8)
> +#define REGISTER0_THS_TERM_SHIFT			8
> +#define REGISTER0_THS_SETTLE_MASK			(0xFF << 0)
> +#define REGISTER0_THS_SETTLE_SHIFT			0
> +
> +#define REGISTER1					(0x4)
> +#define REGISTER1_RESET_DONE_CTRLCLK			(1 << 29)
> +#define REGISTER1_CLOCK_MISS_DETECTOR_STATUS		(1 << 25)
> +#define REGISTER1_TCLK_TERM_MASK			(0x3F << 18)
> +#define REGISTER1_TCLK_TERM_SHIFT			18
> +#define REGISTER1_DPHY_HS_SYNC_PATTERN_MASK		(0xFF << 10)
> +#define REGISTER1_CTRLCLK_DIV_FACTOR_MASK		(0x3 << 8)
> +#define REGISTER1_CTRLCLK_DIV_FACTOR_SHIFT		8
> +#define REGISTER1_TCLK_SETTLE_MASK			(0xFF << 0)
> +#define REGISTER1_TCLK_SETTLE_SHIFT			0
> +
> +#define REGISTER2					(0x8)
> +
> +#define CSI2_SYSCONFIG					(0x10)
> +#define CSI2_SYSCONFIG_MSTANDBY_MODE_MASK		(3 << 12)
> +#define CSI2_SYSCONFIG_MSTANDBY_MODE_FORCE		(0 << 12)
> +#define CSI2_SYSCONFIG_MSTANDBY_MODE_NO			(1 << 12)
> +#define CSI2_SYSCONFIG_MSTANDBY_MODE_SMART		(2 << 12)
> +#define CSI2_SYSCONFIG_SOFT_RESET			(1 << 1)
> +#define CSI2_SYSCONFIG_AUTO_IDLE			(1 << 0)
> +
> +#define CSI2_SYSSTATUS					(0x14)
> +#define CSI2_SYSSTATUS_RESET_DONE			(1 << 0)
> +
> +#define CSI2_IRQSTATUS					(0x18)
> +#define CSI2_IRQENABLE					(0x1C)
> +
> +/* Shared bits across CSI2_IRQENABLE and IRQSTATUS */
> +
> +#define CSI2_IRQ_OCP_ERR				(1 << 14)
> +#define CSI2_IRQ_SHORT_PACKET				(1 << 13)
> +#define CSI2_IRQ_ECC_CORRECTION				(1 << 12)
> +#define CSI2_IRQ_ECC_NO_CORRECTION			(1 << 11)
> +#define CSI2_IRQ_COMPLEXIO_ERR				(1 << 9)
> +#define CSI2_IRQ_FIFO_OVF				(1 << 8)
> +#define CSI2_IRQ_CONTEXT0				(1 << 0)
> +
> +#define CSI2_CTRL					(0x40)
> +#define CSI2_CTRL_MFLAG_LEVH_MASK			(7 << 20)
> +#define CSI2_CTRL_MFLAG_LEVH_SHIFT			20
> +#define CSI2_CTRL_MFLAG_LEVL_MASK			(7 << 17)
> +#define CSI2_CTRL_MFLAG_LEVL_SHIFT			17
> +#define CSI2_CTRL_BURST_SIZE_EXPAND			(1 << 16)
> +#define CSI2_CTRL_VP_CLK_EN				(1 << 15)
> +#define CSI2_CTRL_NON_POSTED_WRITE			(1 << 13)
> +#define CSI2_CTRL_VP_ONLY_EN				(1 << 11)
> +#define CSI2_CTRL_VP_OUT_CTRL_MASK			(3 << 8)
> +#define CSI2_CTRL_VP_OUT_CTRL_SHIFT			8
> +#define CSI2_CTRL_DBG_EN				(1 << 7)
> +#define CSI2_CTRL_BURST_SIZE_MASK			(3 << 5)
> +#define CSI2_CTRL_ENDIANNESS				(1 << 4)
> +#define CSI2_CTRL_FRAME					(1 << 3)
> +#define CSI2_CTRL_ECC_EN				(1 << 2)
> +#define CSI2_CTRL_IF_EN					(1 << 0)
> +
> +#define CSI2_DBG_H					(0x44)
> +
> +#define CSI2_COMPLEXIO_CFG				(0x50)
> +#define CSI2_COMPLEXIO_CFG_RESET_CTRL			(1 << 30)
> +#define CSI2_COMPLEXIO_CFG_RESET_DONE			(1 << 29)
> +#define CSI2_COMPLEXIO_CFG_PWD_CMD_MASK			(3 << 27)
> +#define CSI2_COMPLEXIO_CFG_PWD_CMD_OFF			(0 << 27)
> +#define CSI2_COMPLEXIO_CFG_PWD_CMD_ON			(1 << 27)
> +#define CSI2_COMPLEXIO_CFG_PWD_CMD_ULP			(2 << 27)
> +#define CSI2_COMPLEXIO_CFG_PWD_STATUS_MASK		(3 << 25)
> +#define CSI2_COMPLEXIO_CFG_PWD_STATUS_OFF		(0 << 25)
> +#define CSI2_COMPLEXIO_CFG_PWD_STATUS_ON		(1 << 25)
> +#define CSI2_COMPLEXIO_CFG_PWD_STATUS_ULP		(2 << 25)
> +#define CSI2_COMPLEXIO_CFG_PWR_AUTO			(1 << 24)
> +#define CSI2_COMPLEXIO_CFG_DATA_POL(i)			(1 << (((i) * 4) + 3))
> +#define CSI2_COMPLEXIO_CFG_DATA_POSITION_MASK(i)	(7 << ((i) * 4))
> +#define CSI2_COMPLEXIO_CFG_DATA_POSITION_SHIFT(i)	((i) * 4)
> +#define CSI2_COMPLEXIO_CFG_CLOCK_POL			(1 << 3)
> +#define CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK		(7 << 0)
> +#define CSI2_COMPLEXIO_CFG_CLOCK_POSITION_SHIFT		0
> +
> +#define CSI2_COMPLEXIO_IRQSTATUS			(0x54)
> +
> +#define CSI2_SHORT_PACKET				(0x5C)
> +
> +#define CSI2_COMPLEXIO_IRQENABLE			(0x60)
> +
> +/* Shared bits across CSI2_COMPLEXIO_IRQENABLE and IRQSTATUS */
> +#define CSI2_COMPLEXIO_IRQ_STATEALLULPMEXIT		(1 << 26)
> +#define CSI2_COMPLEXIO_IRQ_STATEALLULPMENTER		(1 << 25)
> +#define CSI2_COMPLEXIO_IRQ_STATEULPM5			(1 << 24)
> +#define CSI2_COMPLEXIO_IRQ_STATEULPM4			(1 << 23)
> +#define CSI2_COMPLEXIO_IRQ_STATEULPM3			(1 << 22)
> +#define CSI2_COMPLEXIO_IRQ_STATEULPM2			(1 << 21)
> +#define CSI2_COMPLEXIO_IRQ_STATEULPM1			(1 << 20)
> +#define CSI2_COMPLEXIO_IRQ_ERRCONTROL5			(1 << 19)
> +#define CSI2_COMPLEXIO_IRQ_ERRCONTROL4			(1 << 18)
> +#define CSI2_COMPLEXIO_IRQ_ERRCONTROL3			(1 << 17)
> +#define CSI2_COMPLEXIO_IRQ_ERRCONTROL2			(1 << 16)
> +#define CSI2_COMPLEXIO_IRQ_ERRCONTROL1			(1 << 15)
> +#define CSI2_COMPLEXIO_IRQ_ERRESC5			(1 << 14)
> +#define CSI2_COMPLEXIO_IRQ_ERRESC4			(1 << 13)
> +#define CSI2_COMPLEXIO_IRQ_ERRESC3			(1 << 12)
> +#define CSI2_COMPLEXIO_IRQ_ERRESC2			(1 << 11)
> +#define CSI2_COMPLEXIO_IRQ_ERRESC1			(1 << 10)
> +#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS5		(1 << 9)
> +#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS4		(1 << 8)
> +#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS3		(1 << 7)
> +#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS2		(1 << 6)
> +#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS1		(1 << 5)
> +#define CSI2_COMPLEXIO_IRQ_ERRSOTHS5			(1 << 4)
> +#define CSI2_COMPLEXIO_IRQ_ERRSOTHS4			(1 << 3)
> +#define CSI2_COMPLEXIO_IRQ_ERRSOTHS3			(1 << 2)
> +#define CSI2_COMPLEXIO_IRQ_ERRSOTHS2			(1 << 1)
> +#define CSI2_COMPLEXIO_IRQ_ERRSOTHS1			(1 << 0)
> +
> +#define CSI2_DBG_P					(0x68)
> +
> +#define CSI2_TIMING					(0x6C)
> +#define CSI2_TIMING_FORCE_RX_MODE_IO1			(1 << 15)
> +#define CSI2_TIMING_STOP_STATE_X16_IO1			(1 << 14)
> +#define CSI2_TIMING_STOP_STATE_X4_IO1			(1 << 13)
> +#define CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK		(0x1FFF << 0)
> +#define CSI2_TIMING_STOP_STATE_COUNTER_IO1_SHIFT	0
> +
> +#define CSI2_CTX_CTRL1(i)				(0x70 + (0x20 * i))
> +#define CSI2_CTX_CTRL1_GENERIC				(1 << 30)
> +#define CSI2_CTX_CTRL1_TRANSCODE			(0xF << 24)
> +#define CSI2_CTX_CTRL1_FEC_NUMBER_MASK			(0xFF << 16)
> +#define CSI2_CTX_CTRL1_COUNT_MASK			(0xFF << 8)
> +#define CSI2_CTX_CTRL1_COUNT_SHIFT			8
> +#define CSI2_CTX_CTRL1_EOF_EN				(1 << 7)
> +#define CSI2_CTX_CTRL1_EOL_EN				(1 << 6)
> +#define CSI2_CTX_CTRL1_CS_EN				(1 << 5)
> +#define CSI2_CTX_CTRL1_COUNT_UNLOCK			(1 << 4)
> +#define CSI2_CTX_CTRL1_PING_PONG			(1 << 3)
> +#define CSI2_CTX_CTRL1_CTX_EN				(1 << 0)
> +
> +#define CSI2_CTX_CTRL2(i)				(0x74 + (0x20 * i))
> +#define CSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT		13
> +#define CSI2_CTX_CTRL2_USER_DEF_MAP_MASK		\
> +		(0x3 << CSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT)
> +#define CSI2_CTX_CTRL2_VIRTUAL_ID_MASK			(3 << 11)
> +#define CSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT			11
> +#define CSI2_CTX_CTRL2_DPCM_PRED			(1 << 10)
> +#define CSI2_CTX_CTRL2_FORMAT_MASK			(0x3FF << 0)
> +#define CSI2_CTX_CTRL2_FORMAT_SHIFT			0
> +
> +#define CSI2_CTX_DAT_OFST(i)				(0x78 + (0x20 * i))
> +#define CSI2_CTX_DAT_OFST_MASK				(0xFFF << 5)
> +
> +#define CSI2_CTX_PING_ADDR(i)				(0x7C + (0x20 * i))
> +#define CSI2_CTX_PING_ADDR_MASK				0xFFFFFFE0
> +
> +#define CSI2_CTX_PONG_ADDR(i)				(0x80 + (0x20 * i))
> +#define CSI2_CTX_PONG_ADDR_MASK				CSI2_CTX_PING_ADDR_MASK
> +
> +#define CSI2_CTX_IRQENABLE(i)				(0x84 + (0x20 * i))
> +#define CSI2_CTX_IRQSTATUS(i)				(0x88 + (0x20 * i))
> +
> +#define CSI2_CTX_CTRL3(i)				(0x8C + (0x20 * i))
> +#define CSI2_CTX_CTRL3_ALPHA_SHIFT			5
> +#define CSI2_CTX_CTRL3_ALPHA_MASK			\
> +		(0x3fff << CSI2_CTX_CTRL3_ALPHA_SHIFT)
> +
> +/* Shared bits across CSI2_CTX_IRQENABLE and IRQSTATUS */
> +#define CSI2_CTX_IRQ_ECC_CORRECTION			(1 << 8)
> +#define CSI2_CTX_IRQ_LINE_NUMBER			(1 << 7)
> +#define CSI2_CTX_IRQ_FRAME_NUMBER			(1 << 6)
> +#define CSI2_CTX_IRQ_CS					(1 << 5)
> +#define CSI2_CTX_IRQ_LE					(1 << 3)
> +#define CSI2_CTX_IRQ_LS					(1 << 2)
> +#define CSI2_CTX_IRQ_FE					(1 << 1)
> +#define CSI2_CTX_IRQ_FS					(1 << 0)
> +
> +#endif /* _OMAP4_CAMERA_REGS_H_ */
> diff --git a/drivers/media/video/omap4iss/iss_video.c b/drivers/media/video/omap4iss/iss_video.c
> new file mode 100644
> index 0000000..3248711
> --- /dev/null
> +++ b/drivers/media/video/omap4iss/iss_video.c
> @@ -0,0 +1,1192 @@
> +/*
> + * iss_video.c
> + *
> + * TI OMAP4 ISS - Generic video node
> + *
> + * Copyright (C) 2011 Texas Instruments, Inc.
> + *
> + * Contacts: Sergio Aguirre <saaguirre@ti.com>
> + *
> + * 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.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
> + * 02110-1301 USA
> + */
> +
> +#include <asm/cacheflush.h>
> +#include <linux/clk.h>
> +#include <linux/mm.h>
> +#include <linux/pagemap.h>
> +#include <linux/sched.h>
> +#include <linux/slab.h>
> +#include <linux/vmalloc.h>
> +#include <linux/module.h>
> +#include <media/v4l2-dev.h>
> +#include <media/v4l2-ioctl.h>
> +#include <plat/omap-pm.h>
> +
> +#include "iss_video.h"
> +#include "iss.h"
> +
> +
> +/* -----------------------------------------------------------------------------
> + * Helper functions
> + */
> +
> +static struct iss_format_info formats[] = {
> +	{ V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8,
> +	  V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8,
> +	  V4L2_PIX_FMT_GREY, 8, },
> +	{ V4L2_MBUS_FMT_Y10_1X10, V4L2_MBUS_FMT_Y10_1X10,
> +	  V4L2_MBUS_FMT_Y10_1X10, V4L2_MBUS_FMT_Y8_1X8,
> +	  V4L2_PIX_FMT_Y10, 10, },
> +	{ V4L2_MBUS_FMT_Y12_1X12, V4L2_MBUS_FMT_Y10_1X10,
> +	  V4L2_MBUS_FMT_Y12_1X12, V4L2_MBUS_FMT_Y8_1X8,
> +	  V4L2_PIX_FMT_Y12, 12, },
> +	{ V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_MBUS_FMT_SBGGR8_1X8,
> +	  V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_MBUS_FMT_SBGGR8_1X8,
> +	  V4L2_PIX_FMT_SBGGR8, 8, },
> +	{ V4L2_MBUS_FMT_SGBRG8_1X8, V4L2_MBUS_FMT_SGBRG8_1X8,
> +	  V4L2_MBUS_FMT_SGBRG8_1X8, V4L2_MBUS_FMT_SGBRG8_1X8,
> +	  V4L2_PIX_FMT_SGBRG8, 8, },
> +	{ V4L2_MBUS_FMT_SGRBG8_1X8, V4L2_MBUS_FMT_SGRBG8_1X8,
> +	  V4L2_MBUS_FMT_SGRBG8_1X8, V4L2_MBUS_FMT_SGRBG8_1X8,
> +	  V4L2_PIX_FMT_SGRBG8, 8, },
> +	{ V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8,
> +	  V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8,
> +	  V4L2_PIX_FMT_SRGGB8, 8, },
> +	{ V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8,
> +	  V4L2_MBUS_FMT_SGRBG10_1X10, 0,
> +	  V4L2_PIX_FMT_SGRBG10DPCM8, 8, },

I had a few patches (not acked yet) to add more supported media bus formats.
It might make sense to add support for those for this driver as well once
we've discussed 4cc's with Hans.

> +	{ V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR10_1X10,
> +	  V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR8_1X8,
> +	  V4L2_PIX_FMT_SBGGR10, 10, },
> +	{ V4L2_MBUS_FMT_SGBRG10_1X10, V4L2_MBUS_FMT_SGBRG10_1X10,
> +	  V4L2_MBUS_FMT_SGBRG10_1X10, V4L2_MBUS_FMT_SGBRG8_1X8,
> +	  V4L2_PIX_FMT_SGBRG10, 10, },
> +	{ V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_MBUS_FMT_SGRBG10_1X10,
> +	  V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_MBUS_FMT_SGRBG8_1X8,
> +	  V4L2_PIX_FMT_SGRBG10, 10, },
> +	{ V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_MBUS_FMT_SRGGB10_1X10,
> +	  V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_MBUS_FMT_SRGGB8_1X8,
> +	  V4L2_PIX_FMT_SRGGB10, 10, },
> +	{ V4L2_MBUS_FMT_SBGGR12_1X12, V4L2_MBUS_FMT_SBGGR10_1X10,
> +	  V4L2_MBUS_FMT_SBGGR12_1X12, V4L2_MBUS_FMT_SBGGR8_1X8,
> +	  V4L2_PIX_FMT_SBGGR12, 12, },
> +	{ V4L2_MBUS_FMT_SGBRG12_1X12, V4L2_MBUS_FMT_SGBRG10_1X10,
> +	  V4L2_MBUS_FMT_SGBRG12_1X12, V4L2_MBUS_FMT_SGBRG8_1X8,
> +	  V4L2_PIX_FMT_SGBRG12, 12, },
> +	{ V4L2_MBUS_FMT_SGRBG12_1X12, V4L2_MBUS_FMT_SGRBG10_1X10,
> +	  V4L2_MBUS_FMT_SGRBG12_1X12, V4L2_MBUS_FMT_SGRBG8_1X8,
> +	  V4L2_PIX_FMT_SGRBG12, 12, },
> +	{ V4L2_MBUS_FMT_SRGGB12_1X12, V4L2_MBUS_FMT_SRGGB10_1X10,
> +	  V4L2_MBUS_FMT_SRGGB12_1X12, V4L2_MBUS_FMT_SRGGB8_1X8,
> +	  V4L2_PIX_FMT_SRGGB12, 12, },
> +	{ V4L2_MBUS_FMT_UYVY8_1X16, V4L2_MBUS_FMT_UYVY8_1X16,
> +	  V4L2_MBUS_FMT_UYVY8_1X16, 0,
> +	  V4L2_PIX_FMT_UYVY, 16, },
> +	{ V4L2_MBUS_FMT_YUYV8_1X16, V4L2_MBUS_FMT_YUYV8_1X16,
> +	  V4L2_MBUS_FMT_YUYV8_1X16, 0,
> +	  V4L2_PIX_FMT_YUYV, 16, },
> +};
> +
> +const struct iss_format_info *
> +omap4iss_video_format_info(enum v4l2_mbus_pixelcode code)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(formats); ++i) {
> +		if (formats[i].code == code)
> +			return &formats[i];
> +	}
> +
> +	return NULL;
> +}
> +
> +/*
> + * iss_video_mbus_to_pix - Convert v4l2_mbus_framefmt to v4l2_pix_format
> + * @video: ISS video instance
> + * @mbus: v4l2_mbus_framefmt format (input)
> + * @pix: v4l2_pix_format format (output)
> + *
> + * Fill the output pix structure with information from the input mbus format.
> + * The bytesperline and sizeimage fields are computed from the requested bytes
> + * per line value in the pix format and information from the video instance.
> + *
> + * Return the number of padding bytes at end of line.
> + */
> +static unsigned int iss_video_mbus_to_pix(const struct iss_video *video,
> +					  const struct v4l2_mbus_framefmt *mbus,
> +					  struct v4l2_pix_format *pix)
> +{
> +	unsigned int bpl = pix->bytesperline;
> +	unsigned int min_bpl;
> +	unsigned int i;
> +
> +	memset(pix, 0, sizeof(*pix));
> +	pix->width = mbus->width;
> +	pix->height = mbus->height;
> +
> +	for (i = 0; i < ARRAY_SIZE(formats); ++i) {
> +		if (formats[i].code == mbus->code)
> +			break;
> +	}
> +
> +	if (WARN_ON(i == ARRAY_SIZE(formats)))
> +		return 0;
> +
> +	min_bpl = pix->width * ALIGN(formats[i].bpp, 8) / 8;
> +
> +	/* Clamp the requested bytes per line value. If the maximum bytes per
> +	 * line value is zero, the module doesn't support user configurable line
> +	 * sizes. Override the requested value with the minimum in that case.
> +	 */
> +	if (video->bpl_max)
> +		bpl = clamp(bpl, min_bpl, video->bpl_max);
> +	else
> +		bpl = min_bpl;
> +
> +	if (!video->bpl_zero_padding || bpl != min_bpl)
> +		bpl = ALIGN(bpl, video->bpl_alignment);
> +
> +	pix->pixelformat = formats[i].pixelformat;
> +	pix->bytesperline = bpl;
> +	pix->sizeimage = pix->bytesperline * pix->height;
> +	pix->colorspace = mbus->colorspace;
> +	pix->field = mbus->field;
> +
> +	return bpl - min_bpl;
> +}
> +
> +static void iss_video_pix_to_mbus(const struct v4l2_pix_format *pix,
> +				  struct v4l2_mbus_framefmt *mbus)
> +{
> +	unsigned int i;
> +
> +	memset(mbus, 0, sizeof(*mbus));
> +	mbus->width = pix->width;
> +	mbus->height = pix->height;
> +
> +	for (i = 0; i < ARRAY_SIZE(formats); ++i) {
> +		if (formats[i].pixelformat == pix->pixelformat)
> +			break;
> +	}
> +
> +	if (WARN_ON(i == ARRAY_SIZE(formats)))
> +		return;
> +
> +	mbus->code = formats[i].code;
> +	mbus->colorspace = pix->colorspace;
> +	mbus->field = pix->field;
> +}
> +
> +static struct v4l2_subdev *
> +iss_video_remote_subdev(struct iss_video *video, u32 *pad)
> +{
> +	struct media_pad *remote;
> +
> +	remote = media_entity_remote_source(&video->pad);
> +
> +	if (remote == NULL ||
> +	    media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
> +		return NULL;
> +
> +	if (pad)
> +		*pad = remote->index;
> +
> +	return media_entity_to_v4l2_subdev(remote->entity);
> +}
> +
> +/* Return a pointer to the ISS video instance at the far end of the pipeline. */
> +static struct iss_video *
> +iss_video_far_end(struct iss_video *video)
> +{
> +	struct media_entity_graph graph;
> +	struct media_entity *entity = &video->video.entity;
> +	struct media_device *mdev = entity->parent;
> +	struct iss_video *far_end = NULL;
> +
> +	mutex_lock(&mdev->graph_mutex);
> +	media_entity_graph_walk_start(&graph, entity);
> +
> +	while ((entity = media_entity_graph_walk_next(&graph))) {
> +		if (entity == &video->video.entity)
> +			continue;
> +
> +		if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE)
> +			continue;
> +
> +		far_end = to_iss_video(media_entity_to_video_device(entity));
> +		if (far_end->type != video->type)
> +			break;
> +
> +		far_end = NULL;
> +	}
> +
> +	mutex_unlock(&mdev->graph_mutex);
> +	return far_end;
> +}
> +
> +/*
> + * Validate a pipeline by checking both ends of all links for format
> + * discrepancies.
> + *
> + * Compute the minimum time per frame value as the maximum of time per frame
> + * limits reported by every block in the pipeline.
> + *
> + * Return 0 if all formats match, or -EPIPE if at least one link is found with
> + * different formats on its two ends.
> + */
> +static int iss_video_validate_pipeline(struct iss_pipeline *pipe)
> +{
> +	struct v4l2_subdev_format fmt_source;
> +	struct v4l2_subdev_format fmt_sink;
> +	struct media_pad *pad;
> +	struct v4l2_subdev *subdev;
> +	int ret;
> +
> +	subdev = iss_video_remote_subdev(pipe->output, NULL);
> +	if (subdev == NULL)
> +		return -EPIPE;
> +
> +	while (1) {
> +		/* Retrieve the sink format */
> +		pad = &subdev->entity.pads[0];
> +		if (!(pad->flags & MEDIA_PAD_FL_SINK))
> +			break;
> +
> +		fmt_sink.pad = pad->index;
> +		fmt_sink.which = V4L2_SUBDEV_FORMAT_ACTIVE;
> +		ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt_sink);
> +		if (ret < 0 && ret != -ENOIOCTLCMD)
> +			return -EPIPE;
> +
> +		/* Retrieve the source format */
> +		pad = media_entity_remote_source(pad);
> +		if (pad == NULL ||
> +		    media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
> +			break;
> +
> +		subdev = media_entity_to_v4l2_subdev(pad->entity);
> +
> +		fmt_source.pad = pad->index;
> +		fmt_source.which = V4L2_SUBDEV_FORMAT_ACTIVE;
> +		ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt_source);
> +		if (ret < 0 && ret != -ENOIOCTLCMD)
> +			return -EPIPE;
> +
> +		/* Check if the two ends match */
> +		if (fmt_source.format.width != fmt_sink.format.width ||
> +		    fmt_source.format.height != fmt_sink.format.height)
> +			return -EPIPE;
> +
> +		if (fmt_source.format.code != fmt_sink.format.code)
> +			return -EPIPE;
> +	}
> +
> +	return 0;
> +}

You won't need pipeline validation function anymore. This will be moved to
media controller. The patches for the OMAP 3 ISP are one the list:

<URL:http://www.spinics.net/lists/linux-media/msg42991.html>

> +static int
> +__iss_video_get_format(struct iss_video *video, struct v4l2_format *format)
> +{
> +	struct v4l2_subdev_format fmt;
> +	struct v4l2_subdev *subdev;
> +	u32 pad;
> +	int ret;
> +
> +	subdev = iss_video_remote_subdev(video, &pad);
> +	if (subdev == NULL)
> +		return -EINVAL;
> +
> +	mutex_lock(&video->mutex);
> +
> +	fmt.pad = pad;
> +	fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
> +	ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt);
> +	if (ret == -ENOIOCTLCMD)
> +		ret = -EINVAL;
> +
> +	mutex_unlock(&video->mutex);
> +
> +	if (ret)
> +		return ret;
> +
> +	format->type = video->type;
> +	return iss_video_mbus_to_pix(video, &fmt.format, &format->fmt.pix);
> +}
> +
> +static int
> +iss_video_check_format(struct iss_video *video, struct iss_video_fh *vfh)
> +{
> +	struct v4l2_format format;
> +	int ret;
> +
> +	memcpy(&format, &vfh->format, sizeof(format));
> +	ret = __iss_video_get_format(video, &format);
> +	if (ret < 0)
> +		return ret;
> +
> +	if (vfh->format.fmt.pix.pixelformat != format.fmt.pix.pixelformat ||
> +	    vfh->format.fmt.pix.height != format.fmt.pix.height ||
> +	    vfh->format.fmt.pix.width != format.fmt.pix.width ||
> +	    vfh->format.fmt.pix.bytesperline != format.fmt.pix.bytesperline ||
> +	    vfh->format.fmt.pix.sizeimage != format.fmt.pix.sizeimage)
> +		return -EINVAL;
> +
> +	return ret;
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * Video queue operations
> + */
> +
> +static int iss_video_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
> +				 unsigned int *count, unsigned int *num_planes,
> +				 unsigned int sizes[], void *alloc_ctxs[])
> +{
> +	struct iss_video_fh *vfh = vb2_get_drv_priv(vq);
> +	struct iss_video *video = vfh->video;
> +
> +	/* Revisit multi-planar support for NV12 */
> +	*num_planes = 1;
> +
> +	sizes[0] = vfh->format.fmt.pix.sizeimage;
> +	if (sizes[0] == 0)
> +		return -EINVAL;
> +
> +	alloc_ctxs[0] = video->alloc_ctx;
> +
> +	*count = min(*count, (unsigned int)(video->capture_mem / PAGE_ALIGN(sizes[0])));
> +
> +	return 0;
> +}
> +
> +static void iss_video_buf_cleanup(struct vb2_buffer *vb)
> +{
> +	struct iss_buffer *buffer = container_of(vb, struct iss_buffer, vb);
> +
> +	if (buffer->iss_addr)
> +		buffer->iss_addr = 0;
> +}
> +
> +static int iss_video_buf_prepare(struct vb2_buffer *vb)
> +{
> +	struct iss_video_fh *vfh = vb2_get_drv_priv(vb->vb2_queue);
> +	struct iss_buffer *buffer = container_of(vb, struct iss_buffer, vb);
> +	struct iss_video *video = vfh->video;
> +	unsigned long size = vfh->format.fmt.pix.sizeimage;
> +	dma_addr_t addr;
> +
> +	if (vb2_plane_size(vb, 0) < size)
> +		return -ENOBUFS;
> +
> +	addr = vb2_dma_contig_plane_dma_addr(vb, 0);
> +	if (!IS_ALIGNED(addr, 32)) {
> +		dev_dbg(video->iss->dev, "Buffer address must be "
> +			"aligned to 32 bytes boundary.\n");
> +		return -EINVAL;
> +	}
> +
> +	vb2_set_plane_payload(vb, 0, size);
> +	buffer->iss_addr = addr;
> +	return 0;
> +}
> +
> +static void iss_video_buf_queue(struct vb2_buffer *vb)
> +{
> +	struct iss_video_fh *vfh = vb2_get_drv_priv(vb->vb2_queue);
> +	struct iss_video *video = vfh->video;
> +	struct iss_buffer *buffer = container_of(vb, struct iss_buffer, vb);
> +	struct iss_pipeline *pipe = to_iss_pipeline(&video->video.entity);
> +	unsigned int empty;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&video->qlock, flags);
> +	empty = list_empty(&video->dmaqueue);
> +	list_add_tail(&buffer->list, &video->dmaqueue);
> +	spin_unlock_irqrestore(&video->qlock, flags);
> +
> +	if (empty) {
> +		enum iss_pipeline_state state;
> +		unsigned int start;
> +
> +		if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
> +			state = ISS_PIPELINE_QUEUE_OUTPUT;
> +		else
> +			state = ISS_PIPELINE_QUEUE_INPUT;
> +
> +		spin_lock_irqsave(&pipe->lock, flags);
> +		pipe->state |= state;
> +		video->ops->queue(video, buffer);
> +		video->dmaqueue_flags |= ISS_VIDEO_DMAQUEUE_QUEUED;
> +
> +		start = iss_pipeline_ready(pipe);
> +		if (start)
> +			pipe->state |= ISS_PIPELINE_STREAM;
> +		spin_unlock_irqrestore(&pipe->lock, flags);
> +
> +		if (start)
> +			omap4iss_pipeline_set_stream(pipe,
> +						ISS_PIPELINE_STREAM_SINGLESHOT);
> +	}
> +}
> +
> +static struct vb2_ops iss_video_vb2ops = {
> +	.queue_setup	= iss_video_queue_setup,
> +	.buf_prepare	= iss_video_buf_prepare,
> +	.buf_queue	= iss_video_buf_queue,
> +	.buf_cleanup	= iss_video_buf_cleanup,
> +};
> +
> +/*
> + * omap4iss_video_buffer_next - Complete the current buffer and return the next
> + * @video: ISS video object
> + * @error: Whether an error occurred during capture
> + *
> + * Remove the current video buffer from the DMA queue and fill its timestamp,
> + * field count and state fields before waking up its completion handler.
> + *
> + * The buffer state is set to VIDEOBUF_DONE if no error occurred (@error is 0)
> + * or VIDEOBUF_ERROR otherwise (@error is non-zero).
> + *
> + * The DMA queue is expected to contain at least one buffer.
> + *
> + * Return a pointer to the next buffer in the DMA queue, or NULL if the queue is
> + * empty.
> + */
> +struct iss_buffer *omap4iss_video_buffer_next(struct iss_video *video,
> +					      unsigned int error)
> +{
> +	struct iss_pipeline *pipe = to_iss_pipeline(&video->video.entity);
> +	enum iss_pipeline_state state;
> +	struct iss_buffer *buf;
> +	unsigned long flags;
> +	struct timespec ts;
> +
> +	spin_lock_irqsave(&video->qlock, flags);
> +	if (WARN_ON(list_empty(&video->dmaqueue))) {
> +		spin_unlock_irqrestore(&video->qlock, flags);
> +		return NULL;
> +	}
> +
> +	buf = list_first_entry(&video->dmaqueue, struct iss_buffer,
> +			       list);
> +	list_del(&buf->list);
> +	spin_unlock_irqrestore(&video->qlock, flags);
> +
> +	ktime_get_ts(&ts);
> +	buf->vb.v4l2_buf.timestamp.tv_sec = ts.tv_sec;
> +	buf->vb.v4l2_buf.timestamp.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
> +
> +	/* Do frame number propagation only if this is the output video node.
> +	 * Frame number either comes from the CSI receivers or it gets
> +	 * incremented here if H3A is not active.
> +	 * Note: There is no guarantee that the output buffer will finish
> +	 * first, so the input number might lag behind by 1 in some cases.
> +	 */
> +	if (video == pipe->output && !pipe->do_propagation)
> +		buf->vb.v4l2_buf.sequence = atomic_inc_return(&pipe->frame_number);
> +	else
> +		buf->vb.v4l2_buf.sequence = atomic_read(&pipe->frame_number);
> +
> +	vb2_buffer_done(&buf->vb, error < 0 ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
> +
> +	spin_lock_irqsave(&video->qlock, flags);
> +	if (list_empty(&video->dmaqueue)) {
> +		spin_unlock_irqrestore(&video->qlock, flags);
> +		if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
> +			state = ISS_PIPELINE_QUEUE_OUTPUT
> +			      | ISS_PIPELINE_STREAM;
> +		else
> +			state = ISS_PIPELINE_QUEUE_INPUT
> +			      | ISS_PIPELINE_STREAM;
> +
> +		spin_lock_irqsave(&pipe->lock, flags);
> +		pipe->state &= ~state;
> +		if (video->pipe.stream_state == ISS_PIPELINE_STREAM_CONTINUOUS)
> +			video->dmaqueue_flags |= ISS_VIDEO_DMAQUEUE_UNDERRUN;
> +		spin_unlock_irqrestore(&pipe->lock, flags);
> +		return NULL;
> +	}
> +
> +	if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->input != NULL) {
> +		spin_lock_irqsave(&pipe->lock, flags);
> +		pipe->state &= ~ISS_PIPELINE_STREAM;
> +		spin_unlock_irqrestore(&pipe->lock, flags);
> +	}
> +
> +	buf = list_first_entry(&video->dmaqueue, struct iss_buffer,
> +			       list);
> +	spin_unlock_irqrestore(&video->qlock, flags);
> +	buf->vb.state = VB2_BUF_STATE_ACTIVE;
> +	return buf;
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * V4L2 ioctls
> + */
> +
> +static int
> +iss_video_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
> +{
> +	struct iss_video *video = video_drvdata(file);
> +
> +	strlcpy(cap->driver, ISS_VIDEO_DRIVER_NAME, sizeof(cap->driver));
> +	strlcpy(cap->card, video->video.name, sizeof(cap->card));
> +	strlcpy(cap->bus_info, "media", sizeof(cap->bus_info));
> +	cap->version = ISS_VIDEO_DRIVER_VERSION;
> +
> +	if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
> +		cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
> +	else
> +		cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
> +
> +	return 0;
> +}
> +
> +static int
> +iss_video_get_format(struct file *file, void *fh, struct v4l2_format *format)
> +{
> +	struct iss_video_fh *vfh = to_iss_video_fh(fh);
> +	struct iss_video *video = video_drvdata(file);
> +
> +	if (format->type != video->type)
> +		return -EINVAL;
> +
> +	mutex_lock(&video->mutex);
> +	*format = vfh->format;
> +	mutex_unlock(&video->mutex);
> +
> +	return 0;
> +}
> +
> +static int
> +iss_video_set_format(struct file *file, void *fh, struct v4l2_format *format)
> +{
> +	struct iss_video_fh *vfh = to_iss_video_fh(fh);
> +	struct iss_video *video = video_drvdata(file);
> +	struct v4l2_mbus_framefmt fmt;
> +
> +	if (format->type != video->type)
> +		return -EINVAL;
> +
> +	mutex_lock(&video->mutex);
> +
> +	/* Fill the bytesperline and sizeimage fields by converting to media bus
> +	 * format and back to pixel format.
> +	 */
> +	iss_video_pix_to_mbus(&format->fmt.pix, &fmt);
> +	iss_video_mbus_to_pix(video, &fmt, &format->fmt.pix);
> +
> +	vfh->format = *format;
> +
> +	mutex_unlock(&video->mutex);
> +	return 0;
> +}
> +
> +static int
> +iss_video_try_format(struct file *file, void *fh, struct v4l2_format *format)
> +{
> +	struct iss_video *video = video_drvdata(file);
> +	struct v4l2_subdev_format fmt;
> +	struct v4l2_subdev *subdev;
> +	u32 pad;
> +	int ret;
> +
> +	if (format->type != video->type)
> +		return -EINVAL;
> +
> +	subdev = iss_video_remote_subdev(video, &pad);
> +	if (subdev == NULL)
> +		return -EINVAL;
> +
> +	iss_video_pix_to_mbus(&format->fmt.pix, &fmt.format);
> +
> +	fmt.pad = pad;
> +	fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
> +	ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt);
> +	if (ret)
> +		return ret == -ENOIOCTLCMD ? -EINVAL : ret;
> +
> +	iss_video_mbus_to_pix(video, &fmt.format, &format->fmt.pix);
> +	return 0;
> +}
> +
> +static int
> +iss_video_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cropcap)
> +{
> +	struct iss_video *video = video_drvdata(file);
> +	struct v4l2_subdev *subdev;
> +	int ret;
> +
> +	subdev = iss_video_remote_subdev(video, NULL);
> +	if (subdev == NULL)
> +		return -EINVAL;
> +
> +	mutex_lock(&video->mutex);
> +	ret = v4l2_subdev_call(subdev, video, cropcap, cropcap);
> +	mutex_unlock(&video->mutex);
> +
> +	return ret == -ENOIOCTLCMD ? -EINVAL : ret;
> +}
> +
> +static int
> +iss_video_get_crop(struct file *file, void *fh, struct v4l2_crop *crop)
> +{
> +	struct iss_video *video = video_drvdata(file);
> +	struct v4l2_subdev_format format;
> +	struct v4l2_subdev *subdev;
> +	u32 pad;
> +	int ret;
> +
> +	subdev = iss_video_remote_subdev(video, &pad);
> +	if (subdev == NULL)
> +		return -EINVAL;
> +
> +	/* Try the get crop operation first and fallback to get format if not
> +	 * implemented.
> +	 */
> +	ret = v4l2_subdev_call(subdev, video, g_crop, crop);
> +	if (ret != -ENOIOCTLCMD)
> +		return ret;
> +
> +	format.pad = pad;
> +	format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
> +	ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &format);
> +	if (ret < 0)
> +		return ret == -ENOIOCTLCMD ? -EINVAL : ret;
> +
> +	crop->c.left = 0;
> +	crop->c.top = 0;
> +	crop->c.width = format.format.width;
> +	crop->c.height = format.format.height;
> +
> +	return 0;
> +}
> +
> +static int
> +iss_video_set_crop(struct file *file, void *fh, struct v4l2_crop *crop)
> +{
> +	struct iss_video *video = video_drvdata(file);
> +	struct v4l2_subdev *subdev;
> +	int ret;
> +
> +	subdev = iss_video_remote_subdev(video, NULL);
> +	if (subdev == NULL)
> +		return -EINVAL;
> +
> +	mutex_lock(&video->mutex);
> +	ret = v4l2_subdev_call(subdev, video, s_crop, crop);
> +	mutex_unlock(&video->mutex);
> +
> +	return ret == -ENOIOCTLCMD ? -EINVAL : ret;
> +}
> +
> +static int
> +iss_video_get_param(struct file *file, void *fh, struct v4l2_streamparm *a)
> +{
> +	struct iss_video_fh *vfh = to_iss_video_fh(fh);
> +	struct iss_video *video = video_drvdata(file);
> +
> +	if (video->type != V4L2_BUF_TYPE_VIDEO_OUTPUT ||
> +	    video->type != a->type)
> +		return -EINVAL;
> +
> +	memset(a, 0, sizeof(*a));
> +	a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
> +	a->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
> +	a->parm.output.timeperframe = vfh->timeperframe;
> +
> +	return 0;
> +}
> +
> +static int
> +iss_video_set_param(struct file *file, void *fh, struct v4l2_streamparm *a)
> +{
> +	struct iss_video_fh *vfh = to_iss_video_fh(fh);
> +	struct iss_video *video = video_drvdata(file);
> +
> +	if (video->type != V4L2_BUF_TYPE_VIDEO_OUTPUT ||
> +	    video->type != a->type)
> +		return -EINVAL;
> +
> +	if (a->parm.output.timeperframe.denominator == 0)
> +		a->parm.output.timeperframe.denominator = 1;
> +
> +	vfh->timeperframe = a->parm.output.timeperframe;
> +
> +	return 0;
> +}
> +
> +static int
> +iss_video_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *rb)
> +{
> +	struct iss_video_fh *vfh = to_iss_video_fh(fh);
> +
> +	return vb2_reqbufs(&vfh->queue, rb);
> +}
> +
> +static int
> +iss_video_querybuf(struct file *file, void *fh, struct v4l2_buffer *b)
> +{
> +	struct iss_video_fh *vfh = to_iss_video_fh(fh);
> +
> +	return vb2_querybuf(&vfh->queue, b);
> +}
> +
> +static int
> +iss_video_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
> +{
> +	struct iss_video_fh *vfh = to_iss_video_fh(fh);
> +
> +	return vb2_qbuf(&vfh->queue, b);
> +}
> +
> +static int
> +iss_video_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
> +{
> +	struct iss_video_fh *vfh = to_iss_video_fh(fh);
> +
> +	return vb2_dqbuf(&vfh->queue, b, file->f_flags & O_NONBLOCK);
> +}
> +
> +/*
> + * Stream management
> + *
> + * Every ISS pipeline has a single input and a single output. The input can be
> + * either a sensor or a video node. The output is always a video node.
> + *
> + * As every pipeline has an output video node, the ISS video objects at the
> + * pipeline output stores the pipeline state. It tracks the streaming state of
> + * both the input and output, as well as the availability of buffers.
> + *
> + * In sensor-to-memory mode, frames are always available at the pipeline input.
> + * Starting the sensor usually requires I2C transfers and must be done in
> + * interruptible context. The pipeline is started and stopped synchronously
> + * to the stream on/off commands. All modules in the pipeline will get their
> + * subdev set stream handler called. The module at the end of the pipeline must
> + * delay starting the hardware until buffers are available at its output.
> + *
> + * In memory-to-memory mode, starting/stopping the stream requires
> + * synchronization between the input and output. ISS modules can't be stopped
> + * in the middle of a frame, and at least some of the modules seem to become
> + * busy as soon as they're started, even if they don't receive a frame start
> + * event. For that reason frames need to be processed in single-shot mode. The
> + * driver needs to wait until a frame is completely processed and written to
> + * memory before restarting the pipeline for the next frame. Pipelined
> + * processing might be possible but requires more testing.
> + *
> + * Stream start must be delayed until buffers are available at both the input
> + * and output. The pipeline must be started in the videobuf queue callback with
> + * the buffers queue spinlock held. The modules subdev set stream operation must
> + * not sleep.
> + */
> +static int
> +iss_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
> +{
> +	struct iss_video_fh *vfh = to_iss_video_fh(fh);
> +	struct iss_video *video = video_drvdata(file);
> +	enum iss_pipeline_state state;
> +	struct iss_pipeline *pipe;
> +	struct iss_video *far_end;
> +	unsigned long flags;
> +	int ret;
> +
> +	if (type != video->type)
> +		return -EINVAL;
> +
> +	mutex_lock(&video->stream_lock);
> +
> +	if (video->streaming) {
> +		mutex_unlock(&video->stream_lock);
> +		return -EBUSY;
> +	}
> +
> +	/* Start streaming on the pipeline. No link touching an entity in the
> +	 * pipeline can be activated or deactivated once streaming is started.
> +	 */
> +	pipe = video->video.entity.pipe
> +	     ? to_iss_pipeline(&video->video.entity) : &video->pipe;
> +	media_entity_pipeline_start(&video->video.entity, &pipe->pipe);
> +
> +	/* Verify that the currently configured format matches the output of
> +	 * the connected subdev.
> +	 */
> +	ret = iss_video_check_format(video, vfh);
> +	if (ret < 0)
> +		goto error;
> +
> +	video->bpl_padding = ret;
> +	video->bpl_value = vfh->format.fmt.pix.bytesperline;
> +
> +	/* Find the ISS video node connected at the far end of the pipeline and
> +	 * update the pipeline.
> +	 */
> +	far_end = iss_video_far_end(video);
> +
> +	if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
> +		state = ISS_PIPELINE_STREAM_OUTPUT | ISS_PIPELINE_IDLE_OUTPUT;
> +		pipe->input = far_end;
> +		pipe->output = video;
> +	} else {
> +		if (far_end == NULL) {
> +			ret = -EPIPE;
> +			goto error;
> +		}
> +
> +		state = ISS_PIPELINE_STREAM_INPUT | ISS_PIPELINE_IDLE_INPUT;
> +		pipe->input = video;
> +		pipe->output = far_end;
> +	}
> +
> +	if (video->iss->pdata->set_constraints)
> +		video->iss->pdata->set_constraints(video->iss, true);
> +
> +	/* Validate the pipeline and update its state. */
> +	ret = iss_video_validate_pipeline(pipe);
> +	if (ret < 0)
> +		goto error;
> +
> +	spin_lock_irqsave(&pipe->lock, flags);
> +	pipe->state &= ~ISS_PIPELINE_STREAM;
> +	pipe->state |= state;
> +	spin_unlock_irqrestore(&pipe->lock, flags);
> +
> +	/* Set the maximum time per frame as the value requested by userspace.
> +	 * This is a soft limit that can be overridden if the hardware doesn't
> +	 * support the request limit.
> +	 */
> +	if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
> +		pipe->max_timeperframe = vfh->timeperframe;
> +
> +	video->queue = &vfh->queue;
> +	INIT_LIST_HEAD(&video->dmaqueue);
> +	spin_lock_init(&video->qlock);
> +	atomic_set(&pipe->frame_number, -1);
> +
> +	ret = vb2_streamon(&vfh->queue, type);
> +	if (ret < 0)
> +		goto error;
> +
> +	/* In sensor-to-memory mode, the stream can be started synchronously
> +	 * to the stream on command. In memory-to-memory mode, it will be
> +	 * started when buffers are queued on both the input and output.
> +	 */
> +	if (pipe->input == NULL) {
> +		unsigned long flags;
> +		ret = omap4iss_pipeline_set_stream(pipe,
> +					      ISS_PIPELINE_STREAM_CONTINUOUS);
> +		if (ret < 0)
> +			goto error;
> +		spin_lock_irqsave(&video->qlock, flags);
> +		if (list_empty(&video->dmaqueue))
> +			video->dmaqueue_flags |= ISS_VIDEO_DMAQUEUE_UNDERRUN;
> +		spin_unlock_irqrestore(&video->qlock, flags);
> +	}
> +
> +error:
> +	if (ret < 0) {
> +		vb2_streamoff(&vfh->queue, type);
> +		if (video->iss->pdata->set_constraints)
> +			video->iss->pdata->set_constraints(video->iss, false);
> +		media_entity_pipeline_stop(&video->video.entity);
> +		video->queue = NULL;
> +	}
> +
> +	if (!ret)
> +		video->streaming = 1;
> +
> +	mutex_unlock(&video->stream_lock);
> +	return ret;
> +}
> +
> +static int
> +iss_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type)
> +{
> +	struct iss_video_fh *vfh = to_iss_video_fh(fh);
> +	struct iss_video *video = video_drvdata(file);
> +	struct iss_pipeline *pipe = to_iss_pipeline(&video->video.entity);
> +	enum iss_pipeline_state state;
> +	unsigned long flags;
> +
> +	if (type != video->type)
> +		return -EINVAL;
> +
> +	mutex_lock(&video->stream_lock);
> +
> +	if (!vb2_is_streaming(&vfh->queue))
> +		goto done;
> +
> +	/* Update the pipeline state. */
> +	if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
> +		state = ISS_PIPELINE_STREAM_OUTPUT
> +		      | ISS_PIPELINE_QUEUE_OUTPUT;
> +	else
> +		state = ISS_PIPELINE_STREAM_INPUT
> +		      | ISS_PIPELINE_QUEUE_INPUT;
> +
> +	spin_lock_irqsave(&pipe->lock, flags);
> +	pipe->state &= ~state;
> +	spin_unlock_irqrestore(&pipe->lock, flags);
> +
> +	/* Stop the stream. */
> +	omap4iss_pipeline_set_stream(pipe, ISS_PIPELINE_STREAM_STOPPED);
> +	vb2_streamoff(&vfh->queue, type);
> +	video->queue = NULL;
> +	video->streaming = 0;
> +
> +	if (video->iss->pdata->set_constraints)
> +		video->iss->pdata->set_constraints(video->iss, false);
> +	media_entity_pipeline_stop(&video->video.entity);
> +
> +done:
> +	mutex_unlock(&video->stream_lock);
> +	return 0;
> +}
> +
> +static int
> +iss_video_enum_input(struct file *file, void *fh, struct v4l2_input *input)
> +{
> +	if (input->index > 0)
> +		return -EINVAL;
> +
> +	strlcpy(input->name, "camera", sizeof(input->name));
> +	input->type = V4L2_INPUT_TYPE_CAMERA;
> +
> +	return 0;
> +}
> +
> +static int
> +iss_video_g_input(struct file *file, void *fh, unsigned int *input)
> +{
> +	*input = 0;
> +
> +	return 0;
> +}
> +
> +static int
> +iss_video_s_input(struct file *file, void *fh, unsigned int input)
> +{
> +	return input == 0 ? 0 : -EINVAL;
> +}

While it's possible to support s_input support it provides no useful
functionality for the user. The OMAP 3 ISP driver also supports it. I'd just
remove it.

The same will be properly provided to apps by the libv4l2pile(auto) in the
future.

> +static const struct v4l2_ioctl_ops iss_video_ioctl_ops = {
> +	.vidioc_querycap		= iss_video_querycap,
> +	.vidioc_g_fmt_vid_cap		= iss_video_get_format,
> +	.vidioc_s_fmt_vid_cap		= iss_video_set_format,
> +	.vidioc_try_fmt_vid_cap		= iss_video_try_format,
> +	.vidioc_g_fmt_vid_out		= iss_video_get_format,
> +	.vidioc_s_fmt_vid_out		= iss_video_set_format,
> +	.vidioc_try_fmt_vid_out		= iss_video_try_format,
> +	.vidioc_cropcap			= iss_video_cropcap,
> +	.vidioc_g_crop			= iss_video_get_crop,
> +	.vidioc_s_crop			= iss_video_set_crop,
> +	.vidioc_g_parm			= iss_video_get_param,
> +	.vidioc_s_parm			= iss_video_set_param,
> +	.vidioc_reqbufs			= iss_video_reqbufs,
> +	.vidioc_querybuf		= iss_video_querybuf,
> +	.vidioc_qbuf			= iss_video_qbuf,
> +	.vidioc_dqbuf			= iss_video_dqbuf,
> +	.vidioc_streamon		= iss_video_streamon,
> +	.vidioc_streamoff		= iss_video_streamoff,
> +	.vidioc_enum_input		= iss_video_enum_input,
> +	.vidioc_g_input			= iss_video_g_input,
> +	.vidioc_s_input			= iss_video_s_input,
> +};
> +
> +/* -----------------------------------------------------------------------------
> + * V4L2 file operations
> + */
> +
> +static int iss_video_open(struct file *file)
> +{
> +	struct iss_video *video = video_drvdata(file);
> +	struct iss_video_fh *handle;
> +	struct vb2_queue *q;
> +	int ret = 0;
> +
> +	handle = kzalloc(sizeof(*handle), GFP_KERNEL);
> +	if (handle == NULL)
> +		return -ENOMEM;
> +
> +	v4l2_fh_init(&handle->vfh, &video->video);
> +	v4l2_fh_add(&handle->vfh);
> +
> +	/* If this is the first user, initialise the pipeline. */
> +	if (omap4iss_get(video->iss) == NULL) {
> +		ret = -EBUSY;
> +		goto done;
> +	}
> +
> +	ret = omap4iss_pipeline_pm_use(&video->video.entity, 1);
> +	if (ret < 0) {
> +		omap4iss_put(video->iss);
> +		goto done;
> +	}
> +
> +	video->alloc_ctx = vb2_dma_contig_init_ctx(video->iss->dev);
> +	if (IS_ERR(video->alloc_ctx)) {
> +		ret = PTR_ERR(video->alloc_ctx);
> +		omap4iss_put(video->iss);
> +		goto done;
> +	}
> +
> +	q = &handle->queue;
> +
> +	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> +	q->io_modes = VB2_MMAP;
> +	q->drv_priv = handle;
> +	q->ops = &iss_video_vb2ops;
> +	q->mem_ops = &vb2_dma_contig_memops;
> +	q->buf_struct_size = sizeof(struct iss_buffer);
> +
> +	ret = vb2_queue_init(q);
> +	if (ret) {
> +		omap4iss_put(video->iss);
> +		goto done;
> +	}
> +
> +	memset(&handle->format, 0, sizeof(handle->format));
> +	handle->format.type = video->type;
> +	handle->timeperframe.denominator = 1;
> +
> +	handle->video = video;
> +	file->private_data = &handle->vfh;
> +
> +done:
> +	if (ret < 0) {
> +		v4l2_fh_del(&handle->vfh);
> +		kfree(handle);
> +	}
> +
> +	return ret;
> +}
> +
> +static int iss_video_release(struct file *file)
> +{
> +	struct iss_video *video = video_drvdata(file);
> +	struct v4l2_fh *vfh = file->private_data;
> +	struct iss_video_fh *handle = to_iss_video_fh(vfh);
> +
> +	/* Disable streaming and free the buffers queue resources. */
> +	iss_video_streamoff(file, vfh, video->type);
> +
> +	omap4iss_pipeline_pm_use(&video->video.entity, 0);
> +
> +	/* Release the file handle. */
> +	v4l2_fh_del(vfh);
> +	kfree(handle);
> +	file->private_data = NULL;
> +
> +	omap4iss_put(video->iss);
> +
> +	return 0;
> +}
> +
> +static unsigned int iss_video_poll(struct file *file, poll_table *wait)
> +{
> +	struct iss_video_fh *vfh = to_iss_video_fh(file->private_data);
> +
> +	return vb2_poll(&vfh->queue, file, wait);
> +}
> +
> +static int iss_video_mmap(struct file *file, struct vm_area_struct *vma)
> +{
> +	struct iss_video_fh *vfh = to_iss_video_fh(file->private_data);
> +
> +	return vb2_mmap(&vfh->queue, vma);;
> +}
> +
> +static struct v4l2_file_operations iss_video_fops = {
> +	.owner = THIS_MODULE,
> +	.unlocked_ioctl = video_ioctl2,
> +	.open = iss_video_open,
> +	.release = iss_video_release,
> +	.poll = iss_video_poll,
> +	.mmap = iss_video_mmap,
> +};
> +
> +/* -----------------------------------------------------------------------------
> + * ISS video core
> + */
> +
> +static const struct iss_video_operations iss_video_dummy_ops = {
> +};
> +
> +int omap4iss_video_init(struct iss_video *video, const char *name)
> +{
> +	const char *direction;
> +	int ret;
> +
> +	switch (video->type) {
> +	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> +		direction = "output";
> +		video->pad.flags = MEDIA_PAD_FL_SINK;
> +		break;
> +	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> +		direction = "input";
> +		video->pad.flags = MEDIA_PAD_FL_SOURCE;
> +		break;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	ret = media_entity_init(&video->video.entity, 1, &video->pad, 0);
> +	if (ret < 0)
> +		return ret;
> +
> +	mutex_init(&video->mutex);
> +	atomic_set(&video->active, 0);
> +
> +	spin_lock_init(&video->pipe.lock);
> +	mutex_init(&video->stream_lock);
> +
> +	/* Initialize the video device. */
> +	if (video->ops == NULL)
> +		video->ops = &iss_video_dummy_ops;
> +
> +	video->video.fops = &iss_video_fops;
> +	snprintf(video->video.name, sizeof(video->video.name),
> +		 "OMAP4 ISS %s %s", name, direction);
> +	video->video.vfl_type = VFL_TYPE_GRABBER;
> +	video->video.release = video_device_release_empty;
> +	video->video.ioctl_ops = &iss_video_ioctl_ops;
> +	video->pipe.stream_state = ISS_PIPELINE_STREAM_STOPPED;
> +
> +	video_set_drvdata(&video->video, video);
> +
> +	return 0;
> +}
> +
> +int omap4iss_video_register(struct iss_video *video, struct v4l2_device *vdev)
> +{
> +	int ret;
> +
> +	video->video.v4l2_dev = vdev;
> +
> +	ret = video_register_device(&video->video, VFL_TYPE_GRABBER, -1);
> +	if (ret < 0)
> +		printk(KERN_ERR "%s: could not register video device (%d)\n",
> +			__func__, ret);
> +
> +	return ret;
> +}
> +
> +void omap4iss_video_unregister(struct iss_video *video)
> +{
> +	if (video_is_registered(&video->video)) {
> +		media_entity_cleanup(&video->video.entity);
> +		video_unregister_device(&video->video);
> +	}
> +}
> diff --git a/drivers/media/video/omap4iss/iss_video.h b/drivers/media/video/omap4iss/iss_video.h
> new file mode 100644
> index 0000000..fc123b0
> --- /dev/null
> +++ b/drivers/media/video/omap4iss/iss_video.h
> @@ -0,0 +1,205 @@
> +/*
> + * iss_video.h
> + *
> + * TI OMAP4 ISS - Generic video node
> + *
> + * Copyright (C) 2011 Texas Instruments, Inc.
> + *
> + * Contacts: Sergio Aguirre <saaguirre@ti.com>
> + *
> + * 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.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
> + * 02110-1301 USA
> + */
> +
> +#ifndef OMAP4_ISS_VIDEO_H
> +#define OMAP4_ISS_VIDEO_H
> +
> +#include <linux/v4l2-mediabus.h>
> +#include <linux/version.h>
> +#include <media/media-entity.h>
> +#include <media/v4l2-dev.h>
> +#include <media/v4l2-fh.h>
> +#include <media/videobuf2-core.h>
> +#include <media/videobuf2-dma-contig.h>
> +
> +#define ISS_VIDEO_DRIVER_NAME		"issvideo"
> +#define ISS_VIDEO_DRIVER_VERSION	KERNEL_VERSION(0, 0, 1)
> +
> +struct iss_device;
> +struct iss_video;
> +struct v4l2_mbus_framefmt;
> +struct v4l2_pix_format;
> +
> +/*
> + * struct iss_format_info - ISS media bus format information
> + * @code: V4L2 media bus format code
> + * @truncated: V4L2 media bus format code for the same format truncated to 10
> + *	bits. Identical to @code if the format is 10 bits wide or less.
> + * @uncompressed: V4L2 media bus format code for the corresponding uncompressed
> + *	format. Identical to @code if the format is not DPCM compressed.
> + * @flavor: V4L2 media bus format code for the same pixel layout but
> + *	shifted to be 8 bits per pixel. =0 if format is not shiftable.
> + * @pixelformat: V4L2 pixel format FCC identifier
> + * @bpp: Bits per pixel
> + */
> +struct iss_format_info {
> +	enum v4l2_mbus_pixelcode code;
> +	enum v4l2_mbus_pixelcode truncated;
> +	enum v4l2_mbus_pixelcode uncompressed;
> +	enum v4l2_mbus_pixelcode flavor;
> +	u32 pixelformat;
> +	unsigned int bpp;
> +};
> +
> +enum iss_pipeline_stream_state {
> +	ISS_PIPELINE_STREAM_STOPPED = 0,
> +	ISS_PIPELINE_STREAM_CONTINUOUS = 1,
> +	ISS_PIPELINE_STREAM_SINGLESHOT = 2,
> +};
> +
> +enum iss_pipeline_state {
> +	/* The stream has been started on the input video node. */
> +	ISS_PIPELINE_STREAM_INPUT = 1,
> +	/* The stream has been started on the output video node. */
> +	ISS_PIPELINE_STREAM_OUTPUT = 2,
> +	/* At least one buffer is queued on the input video node. */
> +	ISS_PIPELINE_QUEUE_INPUT = 4,
> +	/* At least one buffer is queued on the output video node. */
> +	ISS_PIPELINE_QUEUE_OUTPUT = 8,
> +	/* The input entity is idle, ready to be started. */
> +	ISS_PIPELINE_IDLE_INPUT = 16,
> +	/* The output entity is idle, ready to be started. */
> +	ISS_PIPELINE_IDLE_OUTPUT = 32,
> +	/* The pipeline is currently streaming. */
> +	ISS_PIPELINE_STREAM = 64,

I'd suggest using 1 << n to define flags instead.

> +};
> +
> +struct iss_pipeline {
> +	struct media_pipeline pipe;
> +	spinlock_t lock;		/* Pipeline state and queue flags */
> +	unsigned int state;
> +	enum iss_pipeline_stream_state stream_state;
> +	struct iss_video *input;
> +	struct iss_video *output;
> +	atomic_t frame_number;
> +	bool do_propagation; /* of frame number */
> +	struct v4l2_fract max_timeperframe;
> +};
> +
> +#define to_iss_pipeline(__e) \
> +	container_of((__e)->pipe, struct iss_pipeline, pipe)
> +
> +static inline int iss_pipeline_ready(struct iss_pipeline *pipe)
> +{
> +	return pipe->state == (ISS_PIPELINE_STREAM_INPUT |
> +			       ISS_PIPELINE_STREAM_OUTPUT |
> +			       ISS_PIPELINE_QUEUE_INPUT |
> +			       ISS_PIPELINE_QUEUE_OUTPUT |
> +			       ISS_PIPELINE_IDLE_INPUT |
> +			       ISS_PIPELINE_IDLE_OUTPUT);
> +}
> +
> +/*
> + * struct iss_buffer - ISS buffer
> + * @buffer: ISS video buffer
> + * @iss_addr: Physical address of the buffer.
> + */
> +struct iss_buffer {
> +	/* common v4l buffer stuff -- must be first */
> +	struct vb2_buffer	vb;
> +	struct list_head	list;
> +	dma_addr_t iss_addr;
> +};
> +
> +#define to_iss_buffer(buf)	container_of(buf, struct iss_buffer, buffer)
> +
> +enum iss_video_dmaqueue_flags {
> +	/* Set if DMA queue becomes empty when ISS_PIPELINE_STREAM_CONTINUOUS */
> +	ISS_VIDEO_DMAQUEUE_UNDERRUN = (1 << 0),
> +	/* Set when queuing buffer to an empty DMA queue */
> +	ISS_VIDEO_DMAQUEUE_QUEUED = (1 << 1),
> +};
> +
> +#define iss_video_dmaqueue_flags_clr(video)	\
> +			({ (video)->dmaqueue_flags = 0; })
> +
> +/*
> + * struct iss_video_operations - ISS video operations
> + * @queue:	Resume streaming when a buffer is queued. Called on VIDIOC_QBUF
> + *		if there was no buffer previously queued.
> + */
> +struct iss_video_operations {
> +	int(*queue)(struct iss_video *video, struct iss_buffer *buffer);
> +};
> +
> +struct iss_video {
> +	struct video_device video;
> +	enum v4l2_buf_type type;
> +	struct media_pad pad;
> +
> +	struct mutex mutex;		/* format and crop settings */
> +	atomic_t active;
> +
> +	struct iss_device *iss;
> +
> +	unsigned int capture_mem;
> +	unsigned int bpl_alignment;	/* alignment value */
> +	unsigned int bpl_zero_padding;	/* whether the alignment is optional */
> +	unsigned int bpl_max;		/* maximum bytes per line value */
> +	unsigned int bpl_value;		/* bytes per line value */
> +	unsigned int bpl_padding;	/* padding at end of line */
> +
> +	/* Entity video node streaming */
> +	unsigned int streaming:1;
> +
> +	/* Pipeline state */
> +	struct iss_pipeline pipe;
> +	struct mutex stream_lock;	/* pipeline and stream states */
> +
> +	/* Video buffers queue */
> +	struct vb2_queue *queue;
> +	spinlock_t qlock;	/* Spinlock for dmaqueue */
> +	struct list_head dmaqueue;
> +	enum iss_video_dmaqueue_flags dmaqueue_flags;
> +	struct vb2_alloc_ctx *alloc_ctx;
> +
> +	const struct iss_video_operations *ops;
> +};
> +
> +#define to_iss_video(vdev)	container_of(vdev, struct iss_video, video)
> +
> +struct iss_video_fh {
> +	struct v4l2_fh vfh;
> +	struct iss_video *video;
> +	struct vb2_queue queue;
> +	struct v4l2_format format;

Format shouldn't be part of the file handle anymore. There was a reason for
it in the past before PREPARE_BUF --- which is also supported by videobuf2.

> +	struct v4l2_fract timeperframe;
> +};
> +
> +#define to_iss_video_fh(fh)	container_of(fh, struct iss_video_fh, vfh)
> +#define iss_video_queue_to_iss_video_fh(q) \
> +				container_of(q, struct iss_video_fh, queue)
> +
> +int omap4iss_video_init(struct iss_video *video, const char *name);
> +int omap4iss_video_register(struct iss_video *video,
> +			    struct v4l2_device *vdev);
> +void omap4iss_video_unregister(struct iss_video *video);
> +struct iss_buffer *omap4iss_video_buffer_next(struct iss_video *video,
> +					      unsigned int error);
> +struct media_pad *omap4iss_video_remote_pad(struct iss_video *video);
> +
> +const struct iss_format_info *
> +omap4iss_video_format_info(enum v4l2_mbus_pixelcode code);
> +
> +#endif /* OMAP4_ISS_VIDEO_H */

Cheers,

-- 
Sakari Ailus
e-mail: sakari.ailus@iki.fi	jabber/XMPP/Gmail: sailus@retiisi.org.uk

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

* Re: [PATCH v2 06/11] v4l: Add support for omap4iss driver
  2011-12-11  9:11   ` Sakari Ailus
@ 2012-01-23  9:10     ` Aguirre, Sergio
  0 siblings, 0 replies; 65+ messages in thread
From: Aguirre, Sergio @ 2012-01-23  9:10 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: linux-media, linux-omap, laurent.pinchart

Hi Sakari,

Sorry for the very late reply.

On Sun, Dec 11, 2011 at 3:11 AM, Sakari Ailus <sakari.ailus@iki.fi> wrote:
> Hi sergio,
>
> On Wed, Nov 30, 2011 at 06:14:55PM -0600, Sergio Aguirre wrote:
>> This adds a very simplistic driver to utilize the
>> CSI2A interface inside the ISS subsystem in OMAP4,
>> and dump the data to memory.
>>
>> Tested so far on omap4430sdp w/ ES2.1 GP & w/ ES2.2 EMU.
>>
>> Check newly added Documentation/video4linux/omap4_camera.txt
>> for details.
>>
>> Signed-off-by: Sergio Aguirre <saaguirre@ti.com>
>> ---
>>  Documentation/video4linux/omap4_camera.txt  |   60 ++
>>  arch/arm/plat-omap/include/plat/omap4-iss.h |   42 +
>
> Perhaps this could be moved to include/media/omap4iss.h?

Sure. I'll do it.

>
>>  drivers/media/video/Kconfig                 |   13 +
>>  drivers/media/video/Makefile                |    1 +
>>  drivers/media/video/omap4iss/Makefile       |    6 +
>>  drivers/media/video/omap4iss/iss.c          | 1179 ++++++++++++++++++++++++
>>  drivers/media/video/omap4iss/iss.h          |  133 +++
>>  drivers/media/video/omap4iss/iss_csi2.c     | 1324 +++++++++++++++++++++++++++
>>  drivers/media/video/omap4iss/iss_csi2.h     |  166 ++++
>>  drivers/media/video/omap4iss/iss_csiphy.c   |  215 +++++
>>  drivers/media/video/omap4iss/iss_csiphy.h   |   69 ++
>>  drivers/media/video/omap4iss/iss_regs.h     |  238 +++++
>>  drivers/media/video/omap4iss/iss_video.c    | 1192 ++++++++++++++++++++++++
>>  drivers/media/video/omap4iss/iss_video.h    |  205 +++++
>>  14 files changed, 4843 insertions(+), 0 deletions(-)
>>  create mode 100644 Documentation/video4linux/omap4_camera.txt
>>  create mode 100644 arch/arm/plat-omap/include/plat/omap4-iss.h
>>  create mode 100644 drivers/media/video/omap4iss/Makefile
>>  create mode 100644 drivers/media/video/omap4iss/iss.c
>>  create mode 100644 drivers/media/video/omap4iss/iss.h
>>  create mode 100644 drivers/media/video/omap4iss/iss_csi2.c
>>  create mode 100644 drivers/media/video/omap4iss/iss_csi2.h
>>  create mode 100644 drivers/media/video/omap4iss/iss_csiphy.c
>>  create mode 100644 drivers/media/video/omap4iss/iss_csiphy.h
>>  create mode 100644 drivers/media/video/omap4iss/iss_regs.h
>>  create mode 100644 drivers/media/video/omap4iss/iss_video.c
>>  create mode 100644 drivers/media/video/omap4iss/iss_video.h
>>
>> diff --git a/Documentation/video4linux/omap4_camera.txt b/Documentation/video4linux/omap4_camera.txt
>> new file mode 100644
>> index 0000000..a60c80f
>> --- /dev/null
>> +++ b/Documentation/video4linux/omap4_camera.txt
>> @@ -0,0 +1,60 @@
>> +                              OMAP4 ISS Driver
>> +                              ================
>> +
>> +Introduction
>> +------------
>> +
>> +The OMAP44XX family of chips contains the Imaging SubSystem (a.k.a. ISS),
>> +Which contains several components that can be categorized in 3 big groups:
>> +
>> +- Interfaces (2 Interfaces: CSI2-A & CSI2-B/CCP2)
>> +- ISP (Image Signal Processor)
>> +- SIMCOP (Still Image Coprocessor)
>> +
>> +For more information, please look in [1] for latest version of:
>> +     "OMAP4430 Multimedia Device Silicon Revision 2.x"
>> +
>> +As of Revision L, the ISS is described in detail in section 8.
>> +
>> +This driver is supporting _only_ the CSI2-A interface for now.
>> +
>> +It makes use of the Media Controller framework [2], and inherited most of the
>> +code from OMAP3 ISP driver (found under drivers/media/video/omap3isp/*), except
>> +that it doesn't need an IOMMU now for ISS buffers memory mapping.
>> +
>> +Supports usage of MMAP buffers only (for now).
>> +
>> +IMPORTANT: It is recommended to have this patchset:
>> +  Contiguous Memory Allocator (v15) [3].
>> +
>> +Tested platforms
>> +----------------
>> +
>> +- OMAP4430SDP, w/ ES2.1 GP & SEVM4430-CAM-V1-0 (Contains IMX060 & OV5640, in
>> +  which only the last one is supported, outputting YUV422 frames).
>> +
>> +- PandaBoard, Rev. A2, w/ OMAP4430 ES2.1 GP & OV adapter board, tested with
>> +  following sensors:
>> +  * OV5640
>> +  * OV5650
>> +
>> +- Tested on mainline kernel:
>> +
>> +     http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=summary
>> +
>> +  Tag: v3.0 (commit 02f8c6aee8df3cdc935e9bdd4f2d020306035dbe)
>> +
>> +File list
>> +---------
>> +drivers/media/video/omap4iss/
>> +
>> +References
>> +----------
>> +
>> +[1] http://focus.ti.com/general/docs/wtbu/wtbudocumentcenter.tsp?navigationId=12037&templateId=6123#62
>> +[2] http://lwn.net/Articles/420485/
>> +[3] http://lwn.net/Articles/455801/
>> +--
>> +Author: Sergio Aguirre <saaguirre@ti.com>
>> +Copyright (C) 2011, Texas Instruments
>> +
>> diff --git a/arch/arm/plat-omap/include/plat/omap4-iss.h b/arch/arm/plat-omap/include/plat/omap4-iss.h
>> new file mode 100644
>> index 0000000..3a1c6b6
>> --- /dev/null
>> +++ b/arch/arm/plat-omap/include/plat/omap4-iss.h
>> @@ -0,0 +1,42 @@
>> +#ifndef ARCH_ARM_PLAT_OMAP4_ISS_H
>> +#define ARCH_ARM_PLAT_OMAP4_ISS_H
>> +
>> +#include <linux/i2c.h>
>> +
>> +struct iss_device;
>> +
>> +enum iss_interface_type {
>> +     ISS_INTERFACE_CSI2A_PHY1,
>> +};
>> +
>> +/**
>> + * struct iss_csi2_platform_data - CSI2 interface platform data
>> + * @crc: Enable the cyclic redundancy check
>> + * @vpclk_div: Video port output clock control
>> + */
>> +struct iss_csi2_platform_data {
>> +     unsigned crc:1;
>> +     unsigned vpclk_div:2;
>> +};
>> +
>> +struct iss_subdev_i2c_board_info {
>> +     struct i2c_board_info *board_info;
>> +     int i2c_adapter_id;
>> +};
>> +
>> +struct iss_v4l2_subdevs_group {
>> +     struct iss_subdev_i2c_board_info *subdevs;
>> +     enum iss_interface_type interface;
>> +     union {
>> +             struct iss_csi2_platform_data csi2;
>> +     } bus; /* gcc < 4.6.0 chokes on anonymous union initializers */
>> +};
>> +
>> +struct iss_platform_data {
>> +     struct iss_v4l2_subdevs_group *subdevs;
>> +     void (*set_constraints)(struct iss_device *iss, bool enable);
>> +};
>
> If you could describe constraints in the data rather than having the
> callback, the driver could use the data and moving to DT would be easier.

I'm not sure what you mean here.. sorry.

Do you mean that the driver should explicitly use a callback that receives
the actual throughput constraint in bits/sec to be set?

>
>> +extern int omap4_init_camera(struct iss_platform_data *pdata,
>> +                          struct omap_board_data *bdata);
>> +#endif
>> diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
>> index b303a3f..ae2a99d 100644
>> --- a/drivers/media/video/Kconfig
>> +++ b/drivers/media/video/Kconfig
>> @@ -796,6 +796,19 @@ config VIDEO_OMAP3_DEBUG
>>       ---help---
>>         Enable debug messages on OMAP 3 camera controller driver.
>>
>> +config VIDEO_OMAP4
>> +     tristate "OMAP 4 Camera support (EXPERIMENTAL)"
>> +     depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP4 && EXPERIMENTAL
>> +     select VIDEOBUF2_DMA_CONTIG
>> +     ---help---
>> +       Driver for an OMAP 4 ISS controller.
>> +
>> +config VIDEO_OMAP4_DEBUG
>> +     bool "OMAP 4 Camera debug messages"
>> +     depends on VIDEO_OMAP4
>> +     ---help---
>> +       Enable debug messages on OMAP 4 ISS controller driver.
>> +
>>  config SOC_CAMERA
>>       tristate "SoC camera support"
>>       depends on VIDEO_V4L2 && HAS_DMA && I2C
>> diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
>> index 117f9c4..f02a4c4 100644
>> --- a/drivers/media/video/Makefile
>> +++ b/drivers/media/video/Makefile
>> @@ -140,6 +140,7 @@ obj-$(CONFIG_VIDEO_MMP_CAMERA) += marvell-ccic/
>>  obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o
>>
>>  obj-$(CONFIG_VIDEO_OMAP3)    += omap3isp/
>> +obj-$(CONFIG_VIDEO_OMAP4)    += omap4iss/
>>
>>  obj-$(CONFIG_USB_ZR364XX)       += zr364xx.o
>>  obj-$(CONFIG_USB_STKWEBCAM)     += stkwebcam.o
>> diff --git a/drivers/media/video/omap4iss/Makefile b/drivers/media/video/omap4iss/Makefile
>> new file mode 100644
>> index 0000000..1d3b0a7
>> --- /dev/null
>> +++ b/drivers/media/video/omap4iss/Makefile
>> @@ -0,0 +1,6 @@
>> +# Makefile for OMAP4 ISS driver
>> +
>> +omap4-iss-objs += \
>> +     iss.o iss_csi2.o iss_csiphy.o iss_video.o
>> +
>> +obj-$(CONFIG_VIDEO_OMAP4) += omap4-iss.o
>> diff --git a/drivers/media/video/omap4iss/iss.c b/drivers/media/video/omap4iss/iss.c
>> new file mode 100644
>> index 0000000..255738b
>> --- /dev/null
>> +++ b/drivers/media/video/omap4iss/iss.c
>> @@ -0,0 +1,1179 @@
>> +/*
>> + * V4L2 Driver for OMAP4 ISS
>> + *
>> + * Copyright (C) 2011, Texas Instruments
>> + *
>> + * Author: Sergio Aguirre <saaguirre@ti.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/delay.h>
>> +#include <linux/device.h>
>> +#include <linux/dma-mapping.h>
>> +#include <linux/i2c.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/module.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/slab.h>
>> +#include <linux/sched.h>
>> +#include <linux/vmalloc.h>
>> +
>> +#include <media/v4l2-common.h>
>> +#include <media/v4l2-device.h>
>> +
>> +#include "iss.h"
>> +#include "iss_regs.h"
>> +
>> +static void iss_save_ctx(struct iss_device *iss);
>> +
>> +static void iss_restore_ctx(struct iss_device *iss);
>> +
>> +/* Structure for saving/restoring ISS module registers */
>> +static struct iss_reg iss_reg_list[] = {
>> +     {OMAP4_ISS_MEM_TOP, ISS_HL_SYSCONFIG, 0},
>> +     {OMAP4_ISS_MEM_TOP, ISS_CTRL, 0},
>> +     {OMAP4_ISS_MEM_TOP, ISS_CLKCTRL, 0},
>> +     {0, ISS_TOK_TERM, 0}
>> +};
>> +
>> +/*
>> + * omap4iss_flush - Post pending L3 bus writes by doing a register readback
>> + * @iss: OMAP4 ISS device
>> + *
>> + * In order to force posting of pending writes, we need to write and
>> + * readback the same register, in this case the revision register.
>> + *
>> + * See this link for reference:
>> + *   http://www.mail-archive.com/linux-omap@vger.kernel.org/msg08149.html
>> + */
>> +void omap4iss_flush(struct iss_device *iss)
>> +{
>> +     writel(0, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_REVISION);
>> +     readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_REVISION);
>> +}
>> +
>> +/*
>> + * iss_enable_interrupts - Enable ISS interrupts.
>> + * @iss: OMAP4 ISS device
>> + */
>> +static void iss_enable_interrupts(struct iss_device *iss)
>> +{
>> +     static const u32 irq = ISS_HL_IRQ_CSIA;
>> +
>> +     /* Enable HL interrupts */
>> +     writel(irq, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQSTATUS_5);
>> +     writel(irq, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQENABLE_5_SET);
>> +}
>> +
>> +/*
>> + * iss_disable_interrupts - Disable ISS interrupts.
>> + * @iss: OMAP4 ISS device
>> + */
>> +static void iss_disable_interrupts(struct iss_device *iss)
>> +{
>> +     writel(-1, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQENABLE_5_CLR);
>> +}
>> +
>> +static inline void iss_isr_dbg(struct iss_device *iss, u32 irqstatus)
>> +{
>> +     static const char *name[] = {
>> +             "ISP_IRQ0",
>> +             "ISP_IRQ1",
>> +             "ISP_IRQ2",
>> +             "ISP_IRQ3",
>> +             "CSIA_IRQ",
>> +             "CSIB_IRQ",
>> +             "CCP2_IRQ0",
>> +             "CCP2_IRQ1",
>> +             "CCP2_IRQ2",
>> +             "CCP2_IRQ3",
>> +             "CBUFF_IRQ",
>> +             "BTE_IRQ",
>> +             "SIMCOP_IRQ0",
>> +             "SIMCOP_IRQ1",
>> +             "SIMCOP_IRQ2",
>> +             "SIMCOP_IRQ3",
>> +             "CCP2_IRQ8",
>> +             "HS_VS_IRQ",
>> +             "res18",
>> +             "res19",
>> +             "res20",
>> +             "res21",
>> +             "res22",
>> +             "res23",
>> +             "res24",
>> +             "res25",
>> +             "res26",
>> +             "res27",
>> +             "res28",
>> +             "res29",
>> +             "res30",
>> +             "res31",
>> +     };
>> +     int i;
>> +
>> +     dev_dbg(iss->dev, "ISS IRQ: ");
>> +
>> +     for (i = 0; i < ARRAY_SIZE(name); i++) {
>> +             if ((1 << i) & irqstatus)
>> +                     printk(KERN_CONT "%s ", name[i]);
>> +     }
>> +     printk(KERN_CONT "\n");
>
> pr_cont might be prettier, but I guess it's mostly the matter of personal
> preference.

Agreed. I'll take your suggestion. Thanks.

>
>> +}
>> +
>> +/*
>> + * iss_isr - Interrupt Service Routine for ISS module.
>> + * @irq: Not used currently.
>> + * @_iss: Pointer to the OMAP4 ISS device
>> + *
>> + * Handles the corresponding callback if plugged in.
>> + *
>> + * Returns IRQ_HANDLED when IRQ was correctly handled, or IRQ_NONE when the
>> + * IRQ wasn't handled.
>> + */
>> +static irqreturn_t iss_isr(int irq, void *_iss)
>> +{
>> +     struct iss_device *iss = _iss;
>> +     u32 irqstatus;
>> +
>> +     irqstatus = readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQSTATUS_5);
>> +     writel(irqstatus, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQSTATUS_5);
>> +
>> +     if (irqstatus & ISS_HL_IRQ_CSIA)
>> +             omap4iss_csi2_isr(&iss->csi2a);
>> +
>> +     omap4iss_flush(iss);
>> +
>> +#if defined(DEBUG) && defined(ISS_ISR_DEBUG)
>> +     iss_isr_dbg(iss, irqstatus);
>> +#endif
>> +
>> +     return IRQ_HANDLED;
>> +}
>> +
>> +/* -----------------------------------------------------------------------------
>> + * Pipeline power management
>> + *
>> + * Entities must be powered up when part of a pipeline that contains at least
>> + * one open video device node.
>> + *
>> + * To achieve this use the entity use_count field to track the number of users.
>> + * For entities corresponding to video device nodes the use_count field stores
>> + * the users count of the node. For entities corresponding to subdevs the
>> + * use_count field stores the total number of users of all video device nodes
>> + * in the pipeline.
>> + *
>> + * The omap4iss_pipeline_pm_use() function must be called in the open() and
>> + * close() handlers of video device nodes. It increments or decrements the use
>> + * count of all subdev entities in the pipeline.
>> + *
>> + * To react to link management on powered pipelines, the link setup notification
>> + * callback updates the use count of all entities in the source and sink sides
>> + * of the link.
>> + */
>> +
>> +/*
>> + * iss_pipeline_pm_use_count - Count the number of users of a pipeline
>> + * @entity: The entity
>> + *
>> + * Return the total number of users of all video device nodes in the pipeline.
>> + */
>> +static int iss_pipeline_pm_use_count(struct media_entity *entity)
>> +{
>> +     struct media_entity_graph graph;
>> +     int use = 0;
>> +
>> +     media_entity_graph_walk_start(&graph, entity);
>> +
>> +     while ((entity = media_entity_graph_walk_next(&graph))) {
>> +             if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE)
>> +                     use += entity->use_count;
>> +     }
>> +
>> +     return use;
>> +}
>> +
>> +/*
>> + * iss_pipeline_pm_power_one - Apply power change to an entity
>> + * @entity: The entity
>> + * @change: Use count change
>> + *
>> + * Change the entity use count by @change. If the entity is a subdev update its
>> + * power state by calling the core::s_power operation when the use count goes
>> + * from 0 to != 0 or from != 0 to 0.
>> + *
>> + * Return 0 on success or a negative error code on failure.
>> + */
>> +static int iss_pipeline_pm_power_one(struct media_entity *entity, int change)
>> +{
>> +     struct v4l2_subdev *subdev;
>> +
>> +     subdev = media_entity_type(entity) == MEDIA_ENT_T_V4L2_SUBDEV
>> +            ? media_entity_to_v4l2_subdev(entity) : NULL;
>> +
>> +     if (entity->use_count == 0 && change > 0 && subdev != NULL) {
>> +             int ret;
>> +
>> +             ret = v4l2_subdev_call(subdev, core, s_power, 1);
>> +             if (ret < 0 && ret != -ENOIOCTLCMD)
>> +                     return ret;
>> +     }
>> +
>> +     entity->use_count += change;
>> +     WARN_ON(entity->use_count < 0);
>> +
>> +     if (entity->use_count == 0 && change < 0 && subdev != NULL)
>> +             v4l2_subdev_call(subdev, core, s_power, 0);
>> +
>> +     return 0;
>> +}
>> +
>> +/*
>> + * iss_pipeline_pm_power - Apply power change to all entities in a pipeline
>> + * @entity: The entity
>> + * @change: Use count change
>> + *
>> + * Walk the pipeline to update the use count and the power state of all non-node
>> + * entities.
>> + *
>> + * Return 0 on success or a negative error code on failure.
>> + */
>> +static int iss_pipeline_pm_power(struct media_entity *entity, int change)
>> +{
>> +     struct media_entity_graph graph;
>> +     struct media_entity *first = entity;
>> +     int ret = 0;
>> +
>> +     if (!change)
>> +             return 0;
>> +
>> +     media_entity_graph_walk_start(&graph, entity);
>> +
>> +     while (!ret && (entity = media_entity_graph_walk_next(&graph)))
>> +             if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE)
>> +                     ret = iss_pipeline_pm_power_one(entity, change);
>> +
>> +     if (!ret)
>> +             return 0;
>> +
>> +     media_entity_graph_walk_start(&graph, first);
>> +
>> +     while ((first = media_entity_graph_walk_next(&graph))
>> +            && first != entity)
>> +             if (media_entity_type(first) != MEDIA_ENT_T_DEVNODE)
>> +                     iss_pipeline_pm_power_one(first, -change);
>> +
>> +     return ret;
>> +}
>> +
>> +/*
>> + * omap4iss_pipeline_pm_use - Update the use count of an entity
>> + * @entity: The entity
>> + * @use: Use (1) or stop using (0) the entity
>> + *
>> + * Update the use count of all entities in the pipeline and power entities on or
>> + * off accordingly.
>> + *
>> + * Return 0 on success or a negative error code on failure. Powering entities
>> + * off is assumed to never fail. No failure can occur when the use parameter is
>> + * set to 0.
>> + */
>> +int omap4iss_pipeline_pm_use(struct media_entity *entity, int use)
>> +{
>> +     int change = use ? 1 : -1;
>> +     int ret;
>> +
>> +     mutex_lock(&entity->parent->graph_mutex);
>> +
>> +     /* Apply use count to node. */
>> +     entity->use_count += change;
>> +     WARN_ON(entity->use_count < 0);
>> +
>> +     /* Apply power change to connected non-nodes. */
>> +     ret = iss_pipeline_pm_power(entity, change);
>> +     if (ret < 0)
>> +             entity->use_count -= change;
>> +
>> +     mutex_unlock(&entity->parent->graph_mutex);
>> +
>> +     return ret;
>> +}
>> +
>> +/*
>> + * iss_pipeline_link_notify - Link management notification callback
>> + * @source: Pad at the start of the link
>> + * @sink: Pad at the end of the link
>> + * @flags: New link flags that will be applied
>> + *
>> + * React to link management on powered pipelines by updating the use count of
>> + * all entities in the source and sink sides of the link. Entities are powered
>> + * on or off accordingly.
>> + *
>> + * Return 0 on success or a negative error code on failure. Powering entities
>> + * off is assumed to never fail. This function will not fail for disconnection
>> + * events.
>> + */
>> +static int iss_pipeline_link_notify(struct media_pad *source,
>> +                                 struct media_pad *sink, u32 flags)
>> +{
>> +     int source_use = iss_pipeline_pm_use_count(source->entity);
>> +     int sink_use = iss_pipeline_pm_use_count(sink->entity);
>> +     int ret;
>> +
>> +     if (!(flags & MEDIA_LNK_FL_ENABLED)) {
>> +             /* Powering off entities is assumed to never fail. */
>> +             iss_pipeline_pm_power(source->entity, -sink_use);
>> +             iss_pipeline_pm_power(sink->entity, -source_use);
>> +             return 0;
>> +     }
>> +
>> +     ret = iss_pipeline_pm_power(source->entity, sink_use);
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     ret = iss_pipeline_pm_power(sink->entity, source_use);
>> +     if (ret < 0)
>> +             iss_pipeline_pm_power(source->entity, -sink_use);
>> +
>> +     return ret;
>> +}
>> +
>> +/* -----------------------------------------------------------------------------
>> + * Pipeline stream management
>> + */
>> +
>> +/*
>> + * iss_pipeline_enable - Enable streaming on a pipeline
>> + * @pipe: ISS pipeline
>> + * @mode: Stream mode (single shot or continuous)
>> + *
>> + * Walk the entities chain starting at the pipeline output video node and start
>> + * all modules in the chain in the given mode.
>> + *
>> + * Return 0 if successful, or the return value of the failed video::s_stream
>> + * operation otherwise.
>> + */
>> +static int iss_pipeline_enable(struct iss_pipeline *pipe,
>> +                            enum iss_pipeline_stream_state mode)
>> +{
>> +     struct media_entity *entity;
>> +     struct media_pad *pad;
>> +     struct v4l2_subdev *subdev;
>> +     unsigned long flags;
>> +     int ret = 0;
>> +
>> +     spin_lock_irqsave(&pipe->lock, flags);
>> +     pipe->state &= ~(ISS_PIPELINE_IDLE_INPUT | ISS_PIPELINE_IDLE_OUTPUT);
>> +     spin_unlock_irqrestore(&pipe->lock, flags);
>> +
>> +     pipe->do_propagation = false;
>> +
>> +     entity = &pipe->output->video.entity;
>> +     while (1) {
>> +             pad = &entity->pads[0];
>> +             if (!(pad->flags & MEDIA_PAD_FL_SINK))
>> +                     break;
>> +
>> +             pad = media_entity_remote_source(pad);
>> +             if (pad == NULL ||
>> +                 media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
>> +                     break;
>> +
>> +             entity = pad->entity;
>> +             subdev = media_entity_to_v4l2_subdev(entity);
>> +
>> +             ret = v4l2_subdev_call(subdev, video, s_stream, mode);
>> +             if (ret < 0 && ret != -ENOIOCTLCMD)
>> +                     break;
>> +     }
>> +
>> +     return ret;
>> +}
>> +
>> +/*
>> + * iss_pipeline_disable - Disable streaming on a pipeline
>> + * @pipe: ISS pipeline
>> + *
>> + * Walk the entities chain starting at the pipeline output video node and stop
>> + * all modules in the chain. Wait synchronously for the modules to be stopped if
>> + * necessary.
>> + */
>> +static int iss_pipeline_disable(struct iss_pipeline *pipe)
>> +{
>> +     struct media_entity *entity;
>> +     struct media_pad *pad;
>> +     struct v4l2_subdev *subdev;
>> +     int failure = 0;
>> +
>> +     entity = &pipe->output->video.entity;
>> +     while (1) {
>> +             pad = &entity->pads[0];
>> +             if (!(pad->flags & MEDIA_PAD_FL_SINK))
>> +                     break;
>> +
>> +             pad = media_entity_remote_source(pad);
>> +             if (pad == NULL ||
>> +                 media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
>> +                     break;
>> +
>> +             entity = pad->entity;
>> +             subdev = media_entity_to_v4l2_subdev(entity);
>> +
>> +             v4l2_subdev_call(subdev, video, s_stream, 0);
>> +     }
>> +
>> +     return failure;
>> +}
>> +
>> +/*
>> + * omap4iss_pipeline_set_stream - Enable/disable streaming on a pipeline
>> + * @pipe: ISS pipeline
>> + * @state: Stream state (stopped, single shot or continuous)
>> + *
>> + * Set the pipeline to the given stream state. Pipelines can be started in
>> + * single-shot or continuous mode.
>> + *
>> + * Return 0 if successful, or the return value of the failed video::s_stream
>> + * operation otherwise. The pipeline state is not updated when the operation
>> + * fails, except when stopping the pipeline.
>> + */
>> +int omap4iss_pipeline_set_stream(struct iss_pipeline *pipe,
>> +                              enum iss_pipeline_stream_state state)
>> +{
>> +     int ret;
>> +
>> +     if (state == ISS_PIPELINE_STREAM_STOPPED)
>> +             ret = iss_pipeline_disable(pipe);
>> +     else
>> +             ret = iss_pipeline_enable(pipe, state);
>> +
>> +     if (ret == 0 || state == ISS_PIPELINE_STREAM_STOPPED)
>> +             pipe->stream_state = state;
>> +
>> +     return ret;
>> +}
>> +
>> +/*
>> + * iss_pipeline_is_last - Verify if entity has an enabled link to the output
>> + *                     video node
>> + * @me: ISS module's media entity
>> + *
>> + * Returns 1 if the entity has an enabled link to the output video node or 0
>> + * otherwise. It's true only while pipeline can have no more than one output
>> + * node.
>> + */
>> +static int iss_pipeline_is_last(struct media_entity *me)
>> +{
>> +     struct iss_pipeline *pipe;
>> +     struct media_pad *pad;
>> +
>> +     if (!me->pipe)
>> +             return 0;
>> +     pipe = to_iss_pipeline(me);
>> +     if (pipe->stream_state == ISS_PIPELINE_STREAM_STOPPED)
>> +             return 0;
>> +     pad = media_entity_remote_source(&pipe->output->pad);
>> +     return pad->entity == me;
>> +}
>> +
>> +static int iss_reset(struct iss_device *iss)
>> +{
>> +     unsigned long timeout = 0;
>> +
>> +     writel(readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_SYSCONFIG) |
>> +             ISS_HL_SYSCONFIG_SOFTRESET,
>> +             iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_SYSCONFIG);
>> +
>> +     while (readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_SYSCONFIG) &
>> +                     ISS_HL_SYSCONFIG_SOFTRESET) {
>> +             if (timeout++ > 10000) {
>> +                     dev_alert(iss->dev, "cannot reset ISS\n");
>> +                     return -ETIMEDOUT;
>> +             }
>> +             udelay(1);
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +/*
>> + * iss_save_context - Saves the values of the ISS module registers.
>> + * @iss: OMAP4 ISS device
>> + * @reg_list: Structure containing pairs of register address and value to
>> + *            modify on OMAP.
>> + */
>> +static void
>> +iss_save_context(struct iss_device *iss, struct iss_reg *reg_list)
>> +{
>> +     struct iss_reg *next = reg_list;
>> +
>> +     for (; next->reg != ISS_TOK_TERM; next++)
>> +             next->val = readl(iss->regs[next->mmio_range] + next->reg);
>> +}
>> +
>> +/*
>> + * iss_restore_context - Restores the values of the ISS module registers.
>> + * @iss: OMAP4 ISS device
>> + * @reg_list: Structure containing pairs of register address and value to
>> + *            modify on OMAP.
>> + */
>> +static void
>> +iss_restore_context(struct iss_device *iss, struct iss_reg *reg_list)
>> +{
>> +     struct iss_reg *next = reg_list;
>> +
>> +     for (; next->reg != ISS_TOK_TERM; next++)
>> +             writel(next->val, iss->regs[next->mmio_range] + next->reg);
>> +}
>> +
>> +/*
>> + * iss_save_ctx - Saves ISS context.
>> + * @iss: OMAP4 ISS device
>> + *
>> + * Routine for saving the context of each module in the ISS.
>> + */
>> +static void iss_save_ctx(struct iss_device *iss)
>> +{
>> +     iss_save_context(iss, iss_reg_list);
>> +}
>> +
>> +/*
>> + * iss_restore_ctx - Restores ISS context.
>> + * @iss: OMAP4 ISS device
>> + *
>> + * Routine for restoring the context of each module in the ISS.
>> + */
>> +static void iss_restore_ctx(struct iss_device *iss)
>> +{
>> +     iss_restore_context(iss, iss_reg_list);
>> +}
>> +
>> +/*
>> + * iss_module_sync_idle - Helper to sync module with its idle state
>> + * @me: ISS submodule's media entity
>> + * @wait: ISS submodule's wait queue for streamoff/interrupt synchronization
>> + * @stopping: flag which tells module wants to stop
>> + *
>> + * This function checks if ISS submodule needs to wait for next interrupt. If
>> + * yes, makes the caller to sleep while waiting for such event.
>> + */
>> +int omap4iss_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait,
>> +                           atomic_t *stopping)
>> +{
>> +     struct iss_pipeline *pipe = to_iss_pipeline(me);
>> +
>> +     if (pipe->stream_state == ISS_PIPELINE_STREAM_STOPPED ||
>> +         (pipe->stream_state == ISS_PIPELINE_STREAM_SINGLESHOT &&
>> +          !iss_pipeline_ready(pipe)))
>> +             return 0;
>> +
>> +     /*
>> +      * atomic_set() doesn't include memory barrier on ARM platform for SMP
>> +      * scenario. We'll call it here to avoid race conditions.
>> +      */
>> +     atomic_set(stopping, 1);
>> +     smp_mb();
>
> It was new to me atomic_t requires memory barriers. But shouldn't this be
> wmb() instead?

Ok, I'll change this to smp_wmb() if that's fine with you.

>
>> +
>> +     /*
>> +      * If module is the last one, it's writing to memory. In this case,
>> +      * it's necessary to check if the module is already paused due to
>> +      * DMA queue underrun or if it has to wait for next interrupt to be
>> +      * idle.
>> +      * If it isn't the last one, the function won't sleep but *stopping
>> +      * will still be set to warn next submodule caller's interrupt the
>> +      * module wants to be idle.
>> +      */
>> +     if (iss_pipeline_is_last(me)) {
>
> You could return here if the function returns zero.

Agreed. Will do it.

Regards,
Sergio

>
>> +             struct iss_video *video = pipe->output;
>> +             unsigned long flags;
>> +
>> +             spin_lock_irqsave(&video->qlock, flags);
>> +             if (video->dmaqueue_flags & ISS_VIDEO_DMAQUEUE_UNDERRUN) {
>> +                     spin_unlock_irqrestore(&video->qlock, flags);
>> +                     atomic_set(stopping, 0);
>> +                     smp_mb();
>> +                     return 0;
>> +             }
>> +             spin_unlock_irqrestore(&video->qlock, flags);
>> +             if (!wait_event_timeout(*wait, !atomic_read(stopping),
>> +                                     msecs_to_jiffies(1000))) {
>> +                     atomic_set(stopping, 0);
>> +                     smp_mb();
>> +                     return -ETIMEDOUT;
>> +             }
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +/*
>> + * omap4iss_module_sync_is_stopped - Helper to verify if module was stopping
>> + * @wait: ISS submodule's wait queue for streamoff/interrupt synchronization
>> + * @stopping: flag which tells module wants to stop
>> + *
>> + * This function checks if ISS submodule was stopping. In case of yes, it
>> + * notices the caller by setting stopping to 0 and waking up the wait queue.
>> + * Returns 1 if it was stopping or 0 otherwise.
>> + */
>> +int omap4iss_module_sync_is_stopping(wait_queue_head_t *wait,
>> +                                  atomic_t *stopping)
>> +{
>> +     if (atomic_cmpxchg(stopping, 1, 0)) {
>> +             wake_up(wait);
>> +             return 1;
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +/* --------------------------------------------------------------------------
>> + * Clock management
>> + */
>> +
>> +#define ISS_CLKCTRL_MASK     (ISS_CLKCTRL_CSI2_A)
>> +
>> +static int __iss_subclk_update(struct iss_device *iss)
>> +{
>> +     u32 clk = 0;
>> +     int ret = 0, timeout = 1000;
>> +
>> +     if (iss->subclk_resources & OMAP4_ISS_SUBCLK_CSI2_A)
>> +             clk |= ISS_CLKCTRL_CSI2_A;
>> +
>> +     writel((readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_CLKCTRL) &
>> +             ~ISS_CLKCTRL_MASK) | clk,
>> +             iss->regs[OMAP4_ISS_MEM_TOP] + ISS_CLKCTRL);
>> +
>> +     /* Wait for HW assertion */
>> +     while (timeout-- > 0) {
>> +             udelay(1);
>> +             if ((readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_CLKSTAT) &
>> +                  ISS_CLKCTRL_MASK) == clk)
>> +                     break;
>> +     }
>> +
>> +     if (!timeout)
>> +             ret = -EBUSY;
>> +
>> +     return ret;
>> +}
>> +
>> +int omap4iss_subclk_enable(struct iss_device *iss,
>> +                         enum iss_subclk_resource res)
>> +{
>> +     iss->subclk_resources |= res;
>> +
>> +     return __iss_subclk_update(iss);
>> +}
>> +
>> +int omap4iss_subclk_disable(struct iss_device *iss,
>> +                          enum iss_subclk_resource res)
>> +{
>> +     iss->subclk_resources &= ~res;
>> +
>> +     return __iss_subclk_update(iss);
>> +}
>> +
>> +/*
>> + * iss_enable_clocks - Enable ISS clocks
>> + * @iss: OMAP4 ISS device
>> + *
>> + * Return 0 if successful, or clk_enable return value if any of tthem fails.
>> + */
>> +static int iss_enable_clocks(struct iss_device *iss)
>> +{
>> +     int r;
>> +
>> +     r = clk_enable(iss->iss_fck);
>> +     if (r) {
>> +             dev_err(iss->dev, "clk_enable iss_fck failed\n");
>> +             goto out_clk_enable_fck;
>> +     }
>> +
>> +     r = clk_enable(iss->iss_ctrlclk);
>> +     if (r) {
>> +             dev_err(iss->dev, "clk_enable iss_ctrlclk failed\n");
>> +             goto out_clk_enable_ctrlclk;
>> +     }
>> +     return 0;
>> +
>> +out_clk_enable_ctrlclk:
>> +     clk_disable(iss->iss_fck);
>> +out_clk_enable_fck:
>> +     return r;
>> +}
>> +
>> +/*
>> + * iss_disable_clocks - Disable ISS clocks
>> + * @iss: OMAP4 ISS device
>> + */
>> +static void iss_disable_clocks(struct iss_device *iss)
>> +{
>> +     clk_disable(iss->iss_ctrlclk);
>> +     clk_disable(iss->iss_fck);
>> +}
>> +
>> +static void iss_put_clocks(struct iss_device *iss)
>> +{
>> +     if (iss->iss_fck) {
>> +             clk_put(iss->iss_fck);
>> +             iss->iss_fck = NULL;
>> +     }
>> +
>> +     if (iss->iss_ctrlclk) {
>> +             clk_put(iss->iss_ctrlclk);
>> +             iss->iss_ctrlclk = NULL;
>> +     }
>> +}
>> +
>> +static int iss_get_clocks(struct iss_device *iss)
>> +{
>> +     iss->iss_fck = clk_get(iss->dev, "iss_fck");
>> +     if (IS_ERR(iss->iss_fck)) {
>> +             dev_err(iss->dev, "Unable to get iss_fck clock info\n");
>> +             iss_put_clocks(iss);
>> +             return PTR_ERR(iss->iss_fck);
>> +     }
>> +
>> +     iss->iss_ctrlclk = clk_get(iss->dev, "iss_ctrlclk");
>> +     if (IS_ERR(iss->iss_ctrlclk)) {
>> +             dev_err(iss->dev, "Unable to get iss_ctrlclk clock info\n");
>> +             iss_put_clocks(iss);
>> +             return PTR_ERR(iss->iss_fck);
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +/*
>> + * omap4iss_get - Acquire the ISS resource.
>> + *
>> + * Initializes the clocks for the first acquire.
>> + *
>> + * Increment the reference count on the ISS. If the first reference is taken,
>> + * enable clocks and power-up all submodules.
>> + *
>> + * Return a pointer to the ISS device structure, or NULL if an error occurred.
>> + */
>> +struct iss_device *omap4iss_get(struct iss_device *iss)
>> +{
>> +     struct iss_device *__iss = iss;
>> +
>> +     if (iss == NULL)
>> +             return NULL;
>> +
>> +     mutex_lock(&iss->iss_mutex);
>> +     if (iss->ref_count > 0)
>> +             goto out;
>> +
>> +     if (iss_enable_clocks(iss) < 0) {
>> +             __iss = NULL;
>> +             goto out;
>> +     }
>> +
>> +     /* We don't want to restore context before saving it! */
>> +     if (iss->has_context)
>> +             iss_restore_ctx(iss);
>> +     else
>> +             iss->has_context = 1;
>> +
>> +     iss_enable_interrupts(iss);
>> +
>> +out:
>> +     if (__iss != NULL)
>> +             iss->ref_count++;
>> +     mutex_unlock(&iss->iss_mutex);
>> +
>> +     return __iss;
>> +}
>> +
>> +/*
>> + * omap4iss_put - Release the ISS
>> + *
>> + * Decrement the reference count on the ISS. If the last reference is released,
>> + * power-down all submodules, disable clocks and free temporary buffers.
>> + */
>> +void omap4iss_put(struct iss_device *iss)
>> +{
>> +     if (iss == NULL)
>> +             return;
>> +
>> +     mutex_lock(&iss->iss_mutex);
>> +     BUG_ON(iss->ref_count == 0);
>> +     if (--iss->ref_count == 0) {
>> +             iss_disable_interrupts(iss);
>> +             iss_save_ctx(iss);
>> +             iss_disable_clocks(iss);
>> +     }
>> +     mutex_unlock(&iss->iss_mutex);
>> +}
>> +
>> +static int iss_map_mem_resource(struct platform_device *pdev,
>> +                             struct iss_device *iss,
>> +                             enum iss_mem_resources res)
>> +{
>> +     struct resource *mem;
>> +
>> +     /* request the mem region for the camera registers */
>> +
>> +     mem = platform_get_resource(pdev, IORESOURCE_MEM, res);
>> +     if (!mem) {
>> +             dev_err(iss->dev, "no mem resource?\n");
>> +             return -ENODEV;
>> +     }
>> +
>> +     if (!request_mem_region(mem->start, resource_size(mem), pdev->name)) {
>> +             dev_err(iss->dev,
>> +                     "cannot reserve camera register I/O region\n");
>> +             return -ENODEV;
>> +     }
>> +     iss->res[res] = mem;
>> +
>> +     /* map the region */
>> +     iss->regs[res] = ioremap_nocache(mem->start, resource_size(mem));
>> +     if (!iss->regs[res]) {
>> +             dev_err(iss->dev, "cannot map camera register I/O region\n");
>> +             return -ENODEV;
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +static void iss_unregister_entities(struct iss_device *iss)
>> +{
>> +     omap4iss_csi2_unregister_entities(&iss->csi2a);
>> +
>> +     v4l2_device_unregister(&iss->v4l2_dev);
>> +     media_device_unregister(&iss->media_dev);
>> +}
>> +
>> +/*
>> + * iss_register_subdev_group - Register a group of subdevices
>> + * @iss: OMAP4 ISS device
>> + * @board_info: I2C subdevs board information array
>> + *
>> + * Register all I2C subdevices in the board_info array. The array must be
>> + * terminated by a NULL entry, and the first entry must be the sensor.
>> + *
>> + * Return a pointer to the sensor media entity if it has been successfully
>> + * registered, or NULL otherwise.
>> + */
>> +static struct v4l2_subdev *
>> +iss_register_subdev_group(struct iss_device *iss,
>> +                  struct iss_subdev_i2c_board_info *board_info)
>> +{
>> +     struct v4l2_subdev *sensor = NULL;
>> +     unsigned int first;
>> +
>> +     if (board_info->board_info == NULL)
>> +             return NULL;
>> +
>> +     for (first = 1; board_info->board_info; ++board_info, first = 0) {
>> +             struct v4l2_subdev *subdev;
>> +             struct i2c_adapter *adapter;
>> +
>> +             adapter = i2c_get_adapter(board_info->i2c_adapter_id);
>> +             if (adapter == NULL) {
>> +                     printk(KERN_ERR "%s: Unable to get I2C adapter %d for "
>> +                             "device %s\n", __func__,
>> +                             board_info->i2c_adapter_id,
>> +                             board_info->board_info->type);
>> +                     continue;
>> +             }
>> +
>> +             subdev = v4l2_i2c_new_subdev_board(&iss->v4l2_dev, adapter,
>> +                             board_info->board_info, NULL);
>> +             if (subdev == NULL) {
>> +                     printk(KERN_ERR "%s: Unable to register subdev %s\n",
>> +                             __func__, board_info->board_info->type);
>> +                     continue;
>> +             }
>> +
>> +             if (first)
>> +                     sensor = subdev;
>> +     }
>> +
>> +     return sensor;
>> +}
>> +
>> +static int iss_register_entities(struct iss_device *iss)
>> +{
>> +     struct iss_platform_data *pdata = iss->pdata;
>> +     struct iss_v4l2_subdevs_group *subdevs;
>> +     int ret;
>> +
>> +     iss->media_dev.dev = iss->dev;
>> +     strlcpy(iss->media_dev.model, "TI OMAP4 ISS",
>> +             sizeof(iss->media_dev.model));
>> +     iss->media_dev.link_notify = iss_pipeline_link_notify;
>> +     ret = media_device_register(&iss->media_dev);
>> +     if (ret < 0) {
>> +             printk(KERN_ERR "%s: Media device registration failed (%d)\n",
>> +                     __func__, ret);
>> +             return ret;
>> +     }
>> +
>> +     iss->v4l2_dev.mdev = &iss->media_dev;
>> +     ret = v4l2_device_register(iss->dev, &iss->v4l2_dev);
>> +     if (ret < 0) {
>> +             printk(KERN_ERR "%s: V4L2 device registration failed (%d)\n",
>> +                     __func__, ret);
>> +             goto done;
>> +     }
>> +
>> +     /* Register internal entities */
>> +     ret = omap4iss_csi2_register_entities(&iss->csi2a, &iss->v4l2_dev);
>> +     if (ret < 0)
>> +             goto done;
>> +
>> +     /* Register external entities */
>> +     for (subdevs = pdata->subdevs; subdevs && subdevs->subdevs; ++subdevs) {
>> +             struct v4l2_subdev *sensor;
>> +             struct media_entity *input;
>> +             unsigned int flags;
>> +             unsigned int pad;
>> +
>> +             sensor = iss_register_subdev_group(iss, subdevs->subdevs);
>> +             if (sensor == NULL)
>> +                     continue;
>> +
>> +             sensor->host_priv = subdevs;
>> +
>> +             /* Connect the sensor to the correct interface module.
>> +              * CSI2a receiver through CSIPHY1.
>> +              */
>> +             switch (subdevs->interface) {
>> +             case ISS_INTERFACE_CSI2A_PHY1:
>> +                     input = &iss->csi2a.subdev.entity;
>> +                     pad = CSI2_PAD_SINK;
>> +                     flags = MEDIA_LNK_FL_IMMUTABLE
>> +                           | MEDIA_LNK_FL_ENABLED;
>> +                     break;
>> +
>> +             default:
>> +                     printk(KERN_ERR "%s: invalid interface type %u\n",
>> +                            __func__, subdevs->interface);
>> +                     ret = -EINVAL;
>> +                     goto done;
>> +             }
>> +
>> +             ret = media_entity_create_link(&sensor->entity, 0, input, pad,
>> +                                            flags);
>> +             if (ret < 0)
>> +                     goto done;
>> +     }
>> +
>> +     ret = v4l2_device_register_subdev_nodes(&iss->v4l2_dev);
>> +
>> +done:
>> +     if (ret < 0)
>> +             iss_unregister_entities(iss);
>> +
>> +     return ret;
>> +}
>> +
>> +static void iss_cleanup_modules(struct iss_device *iss)
>> +{
>> +     omap4iss_csi2_cleanup(iss);
>> +}
>> +
>> +static int iss_initialize_modules(struct iss_device *iss)
>> +{
>> +     int ret;
>> +
>> +     ret = omap4iss_csiphy_init(iss);
>> +     if (ret < 0) {
>> +             dev_err(iss->dev, "CSI PHY initialization failed\n");
>> +             goto error_csiphy;
>> +     }
>> +
>> +     ret = omap4iss_csi2_init(iss);
>> +     if (ret < 0) {
>> +             dev_err(iss->dev, "CSI2 initialization failed\n");
>> +             goto error_csi2;
>> +     }
>> +
>> +     return 0;
>> +
>> +error_csi2:
>> +error_csiphy:
>> +     return ret;
>> +}
>> +
>> +static int iss_probe(struct platform_device *pdev)
>> +{
>> +     struct iss_platform_data *pdata = pdev->dev.platform_data;
>> +     struct iss_device *iss;
>> +     int i, ret;
>> +
>> +     if (pdata == NULL)
>> +             return -EINVAL;
>> +
>> +     iss = kzalloc(sizeof(*iss), GFP_KERNEL);
>> +     if (!iss) {
>> +             dev_err(&pdev->dev, "Could not allocate memory\n");
>> +             return -ENOMEM;
>> +     }
>> +
>> +     mutex_init(&iss->iss_mutex);
>> +
>> +     iss->dev = &pdev->dev;
>> +     iss->pdata = pdata;
>> +     iss->ref_count = 0;
>> +
>> +     iss->raw_dmamask = DMA_BIT_MASK(32);
>> +     iss->dev->dma_mask = &iss->raw_dmamask;
>> +     iss->dev->coherent_dma_mask = DMA_BIT_MASK(32);
>> +
>> +     platform_set_drvdata(pdev, iss);
>> +
>> +     /* Clocks */
>> +     ret = iss_map_mem_resource(pdev, iss, OMAP4_ISS_MEM_TOP);
>> +     if (ret < 0)
>> +             goto error;
>> +
>> +     ret = iss_get_clocks(iss);
>> +     if (ret < 0)
>> +             goto error;
>> +
>> +     if (omap4iss_get(iss) == NULL)
>> +             goto error;
>> +
>> +     ret = iss_reset(iss);
>> +     if (ret < 0)
>> +             goto error_iss;
>> +
>> +     iss->revision = readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_REVISION);
>> +     dev_info(iss->dev, "Revision %08x found\n", iss->revision);
>> +
>> +     for (i = 1; i < OMAP4_ISS_MEM_LAST; i++) {
>> +             ret = iss_map_mem_resource(pdev, iss, i);
>> +             if (ret)
>> +                     goto error_iss;
>> +     }
>> +
>> +     /* Interrupt */
>> +     iss->irq_num = platform_get_irq(pdev, 0);
>> +     if (iss->irq_num <= 0) {
>> +             dev_err(iss->dev, "No IRQ resource\n");
>> +             ret = -ENODEV;
>> +             goto error_iss;
>> +     }
>> +
>> +     if (request_irq(iss->irq_num, iss_isr, IRQF_SHARED, "OMAP4 ISS", iss)) {
>> +             dev_err(iss->dev, "Unable to request IRQ\n");
>> +             ret = -EINVAL;
>> +             goto error_iss;
>> +     }
>> +
>> +     /* Entities */
>> +     ret = iss_initialize_modules(iss);
>> +     if (ret < 0)
>> +             goto error_irq;
>> +
>> +     ret = iss_register_entities(iss);
>> +     if (ret < 0)
>> +             goto error_modules;
>> +
>> +     omap4iss_put(iss);
>> +
>> +     return 0;
>> +
>> +error_modules:
>> +     iss_cleanup_modules(iss);
>> +error_irq:
>> +     free_irq(iss->irq_num, iss);
>> +error_iss:
>> +     omap4iss_put(iss);
>> +error:
>> +     iss_put_clocks(iss);
>> +
>> +     for (i = 0; i < OMAP4_ISS_MEM_LAST; i++) {
>> +             if (iss->regs[i]) {
>> +                     iounmap(iss->regs[i]);
>> +                     iss->regs[i] = NULL;
>> +             }
>> +
>> +             if (iss->res[i]) {
>> +                     release_mem_region(iss->res[i]->start,
>> +                                        resource_size(iss->res[i]));
>> +                     iss->res[i] = NULL;
>> +             }
>> +     }
>> +     platform_set_drvdata(pdev, NULL);
>> +     kfree(iss);
>> +
>> +     return ret;
>> +}
>> +
>> +static int iss_remove(struct platform_device *pdev)
>> +{
>> +     struct iss_device *iss = platform_get_drvdata(pdev);
>> +     int i;
>> +
>> +     iss_unregister_entities(iss);
>> +     iss_cleanup_modules(iss);
>> +
>> +     free_irq(iss->irq_num, iss);
>> +     iss_put_clocks(iss);
>> +
>> +     for (i = 0; i < OMAP4_ISS_MEM_LAST; i++) {
>> +             if (iss->regs[i]) {
>> +                     iounmap(iss->regs[i]);
>> +                     iss->regs[i] = NULL;
>> +             }
>> +
>> +             if (iss->res[i]) {
>> +                     release_mem_region(iss->res[i]->start,
>> +                                        resource_size(iss->res[i]));
>> +                     iss->res[i] = NULL;
>> +             }
>> +     }
>> +
>> +     kfree(iss);
>> +
>> +     return 0;
>> +}
>> +
>> +static struct platform_device_id omap4iss_id_table[] = {
>> +     { "omap4iss", 0 },
>> +     { },
>> +};
>> +MODULE_DEVICE_TABLE(platform, omap4iss_id_table);
>> +
>> +static struct platform_driver iss_driver = {
>> +     .probe          = iss_probe,
>> +     .remove         = iss_remove,
>> +     .id_table       = omap4iss_id_table,
>> +     .driver = {
>> +             .owner  = THIS_MODULE,
>> +             .name   = "omap4iss",
>> +     },
>> +};
>> +
>> +static int __init iss_init(void)
>> +{
>> +     return platform_driver_register(&iss_driver);
>> +}
>> +
>> +static void __exit iss_exit(void)
>> +{
>> +     platform_driver_unregister(&iss_driver);
>> +}
>> +
>> +/*
>> + * FIXME: Had to make it late_initcall. Strangely while being module_init,
>> + * The I2C communication was failing in the sensor, because no XCLK was
>> + * provided.
>> + */
>> +late_initcall(iss_init);
>> +module_exit(iss_exit);
>> +
>> +MODULE_DESCRIPTION("TI OMAP4 ISS driver");
>> +MODULE_AUTHOR("Sergio Aguirre <saaguirre@ti.com>");
>> +MODULE_LICENSE("GPL");
>> diff --git a/drivers/media/video/omap4iss/iss.h b/drivers/media/video/omap4iss/iss.h
>> new file mode 100644
>> index 0000000..8346c80
>> --- /dev/null
>> +++ b/drivers/media/video/omap4iss/iss.h
>> @@ -0,0 +1,133 @@
>> +/*
>> + * iss.h
>> + *
>> + * Copyright (C) 2011 Texas Instruments.
>> + *
>> + * Author: Sergio Aguirre <saaguirre@ti.com>
>> + */
>> +
>> +#ifndef _OMAP4_ISS_H_
>> +#define _OMAP4_ISS_H_
>> +
>> +#include <media/v4l2-device.h>
>> +#include <linux/device.h>
>> +#include <linux/io.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/wait.h>
>> +
>> +#include <plat/omap4-iss.h>
>> +
>> +#include "iss_regs.h"
>> +#include "iss_csiphy.h"
>> +#include "iss_csi2.h"
>> +
>> +#define ISS_TOK_TERM         0xFFFFFFFF      /*
>> +                                              * terminating token for ISS
>> +                                              * modules reg list
>> +                                              */
>> +#define to_iss_device(ptr_module)                            \
>> +     container_of(ptr_module, struct iss_device, ptr_module)
>> +#define to_device(ptr_module)                                                \
>> +     (to_iss_device(ptr_module)->dev)
>> +
>> +enum iss_mem_resources {
>> +     OMAP4_ISS_MEM_TOP,
>> +     OMAP4_ISS_MEM_CSI2_A_REGS1,
>> +     OMAP4_ISS_MEM_CAMERARX_CORE1,
>> +     OMAP4_ISS_MEM_LAST,
>> +};
>> +
>> +enum iss_subclk_resource {
>> +     OMAP4_ISS_SUBCLK_SIMCOP         = (1 << 0),
>> +     OMAP4_ISS_SUBCLK_ISP            = (1 << 1),
>> +     OMAP4_ISS_SUBCLK_CSI2_A         = (1 << 2),
>> +     OMAP4_ISS_SUBCLK_CSI2_B         = (1 << 3),
>> +     OMAP4_ISS_SUBCLK_CCP2           = (1 << 4),
>> +};
>> +
>> +/*
>> + * struct iss_reg - Structure for ISS register values.
>> + * @reg: 32-bit Register address.
>> + * @val: 32-bit Register value.
>> + */
>> +struct iss_reg {
>> +     enum iss_mem_resources mmio_range;
>> +     u32 reg;
>> +     u32 val;
>> +};
>> +
>> +struct iss_platform_callback {
>> +     int (*csiphy_config)(struct iss_csiphy *phy,
>> +                          struct iss_csiphy_dphy_cfg *dphy,
>> +                          struct iss_csiphy_lanes_cfg *lanes);
>> +};
>> +
>> +struct iss_device {
>> +     struct v4l2_device v4l2_dev;
>> +     struct media_device media_dev;
>> +     struct device *dev;
>> +     u32 revision;
>> +
>> +     /* platform HW resources */
>> +     struct iss_platform_data *pdata;
>> +     unsigned int irq_num;
>> +
>> +     struct resource *res[OMAP4_ISS_MEM_LAST];
>> +     void __iomem *regs[OMAP4_ISS_MEM_LAST];
>> +
>> +     u64 raw_dmamask;
>> +
>> +     struct mutex iss_mutex; /* For handling ref_count field */
>> +     int has_context;
>> +     int ref_count;
>> +
>> +     struct clk *iss_fck;
>> +     struct clk *iss_ctrlclk;
>> +
>> +     /* ISS modules */
>> +     struct iss_csi2_device csi2a;
>> +     struct iss_csiphy csiphy1;
>> +
>> +     unsigned int subclk_resources;
>> +
>> +     struct iss_platform_callback platform_cb;
>> +};
>> +
>> +#define v4l2_dev_to_iss_device(dev) \
>> +     container_of(dev, struct iss_device, v4l2_dev)
>> +
>> +int omap4iss_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait,
>> +                           atomic_t *stopping);
>> +
>> +int omap4iss_module_sync_is_stopping(wait_queue_head_t *wait,
>> +                                  atomic_t *stopping);
>> +
>> +int omap4iss_pipeline_set_stream(struct iss_pipeline *pipe,
>> +                              enum iss_pipeline_stream_state state);
>> +
>> +struct iss_device *omap4iss_get(struct iss_device *iss);
>> +void omap4iss_put(struct iss_device *iss);
>> +int omap4iss_subclk_enable(struct iss_device *iss,
>> +                        enum iss_subclk_resource res);
>> +int omap4iss_subclk_disable(struct iss_device *iss,
>> +                         enum iss_subclk_resource res);
>> +
>> +int omap4iss_pipeline_pm_use(struct media_entity *entity, int use);
>> +
>> +int omap4iss_register_entities(struct platform_device *pdev,
>> +                            struct v4l2_device *v4l2_dev);
>> +void omap4iss_unregister_entities(struct platform_device *pdev);
>> +
>> +static inline enum v4l2_buf_type
>> +iss_pad_buffer_type(const struct v4l2_subdev *subdev, int pad)
>> +{
>> +     if (pad >= subdev->entity.num_pads)
>> +             return 0;
>> +
>> +     if (subdev->entity.pads[pad].flags & MEDIA_PAD_FL_SINK)
>> +             return V4L2_BUF_TYPE_VIDEO_OUTPUT;
>> +     else
>> +             return V4L2_BUF_TYPE_VIDEO_CAPTURE;
>> +}
>> +
>> +#endif /* _OMAP4_ISS_H_ */
>> diff --git a/drivers/media/video/omap4iss/iss_csi2.c b/drivers/media/video/omap4iss/iss_csi2.c
>> new file mode 100644
>> index 0000000..916d5ef
>> --- /dev/null
>> +++ b/drivers/media/video/omap4iss/iss_csi2.c
>> @@ -0,0 +1,1324 @@
>> +/*
>> + * iss_csi2.c
>> + *
>> + * TI OMAP4 ISS - CSI PHY module
>> + *
>> + * Copyright (C) 2011 Texas Instruments, Inc.
>> + *
>> + * Contacts: Sergio Aguirre <saaguirre@ti.com>
>> + *
>> + * 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.
>> + *
>> + * This program is distributed in the hope that it will be useful, but
>> + * WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write to the Free Software
>> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
>> + * 02110-1301 USA
>> + */
>> +#include <linux/delay.h>
>> +#include <media/v4l2-common.h>
>> +#include <linux/v4l2-mediabus.h>
>> +#include <linux/mm.h>
>> +
>> +#include "iss.h"
>> +#include "iss_regs.h"
>> +#include "iss_csi2.h"
>> +
>> +/*
>> + * csi2_if_enable - Enable CSI2 Receiver interface.
>> + * @enable: enable flag
>> + *
>> + */
>> +static void csi2_if_enable(struct iss_csi2_device *csi2, u8 enable)
>> +{
>> +     struct iss_csi2_ctrl_cfg *currctrl = &csi2->ctrl;
>> +
>> +     writel((readl(csi2->regs1 + CSI2_CTRL) & ~CSI2_CTRL_IF_EN) |
>> +             (enable ? CSI2_CTRL_IF_EN : 0),
>> +             csi2->regs1 + CSI2_CTRL);
>> +
>> +     currctrl->if_enable = enable;
>> +}
>> +
>> +/*
>> + * csi2_recv_config - CSI2 receiver module configuration.
>> + * @currctrl: iss_csi2_ctrl_cfg structure
>> + *
>> + */
>> +static void csi2_recv_config(struct iss_csi2_device *csi2,
>> +                          struct iss_csi2_ctrl_cfg *currctrl)
>> +{
>> +     u32 reg;
>> +
>> +     reg = readl(csi2->regs1 + CSI2_CTRL);
>> +
>> +     if (currctrl->frame_mode)
>> +             reg |= CSI2_CTRL_FRAME;
>> +     else
>> +             reg &= ~CSI2_CTRL_FRAME;
>> +
>> +     if (currctrl->vp_clk_enable)
>> +             reg |= CSI2_CTRL_VP_CLK_EN;
>> +     else
>> +             reg &= ~CSI2_CTRL_VP_CLK_EN;
>> +
>> +     if (currctrl->vp_only_enable)
>> +             reg |= CSI2_CTRL_VP_ONLY_EN;
>> +     else
>> +             reg &= ~CSI2_CTRL_VP_ONLY_EN;
>> +
>> +     reg &= ~CSI2_CTRL_VP_OUT_CTRL_MASK;
>> +     reg |= currctrl->vp_out_ctrl << CSI2_CTRL_VP_OUT_CTRL_SHIFT;
>> +
>> +     if (currctrl->ecc_enable)
>> +             reg |= CSI2_CTRL_ECC_EN;
>> +     else
>> +             reg &= ~CSI2_CTRL_ECC_EN;
>> +
>> +     /*
>> +      * Set MFlag assertion boundaries to:
>> +      * Low: 4/8 of FIFO size
>> +      * High: 6/8 of FIFO size
>> +      */
>> +     reg &= ~(CSI2_CTRL_MFLAG_LEVH_MASK | CSI2_CTRL_MFLAG_LEVL_MASK);
>> +     reg |= (2 << CSI2_CTRL_MFLAG_LEVH_SHIFT) |
>> +            (4 << CSI2_CTRL_MFLAG_LEVL_SHIFT);
>> +
>> +     /* Generation of 16x64-bit bursts (Recommended) */
>> +     reg |= CSI2_CTRL_BURST_SIZE_EXPAND;
>> +
>> +     /* Do Non-Posted writes (Recommended) */
>> +     reg |= CSI2_CTRL_NON_POSTED_WRITE;
>> +
>> +     /*
>> +      * Enforce Little endian for all formats, including:
>> +      * YUV4:2:2 8-bit and YUV4:2:0 Legacy
>> +      */
>> +     reg |= CSI2_CTRL_ENDIANNESS;
>> +
>> +     writel(reg, csi2->regs1 + CSI2_CTRL);
>> +}
>> +
>> +static const unsigned int csi2_input_fmts[] = {
>> +     V4L2_MBUS_FMT_SGRBG10_1X10,
>> +     V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8,
>> +     V4L2_MBUS_FMT_SRGGB10_1X10,
>> +     V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8,
>> +     V4L2_MBUS_FMT_SBGGR10_1X10,
>> +     V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8,
>> +     V4L2_MBUS_FMT_SGBRG10_1X10,
>> +     V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8,
>> +     V4L2_MBUS_FMT_UYVY8_1X16,
>> +     V4L2_MBUS_FMT_YUYV8_1X16,
>> +};
>> +
>> +/* To set the format on the CSI2 requires a mapping function that takes
>> + * the following inputs:
>> + * - 3 different formats (at this time)
>> + * - 2 destinations (mem, vp+mem) (vp only handled separately)
>> + * - 2 decompression options (on, off)
>> + * Output should be CSI2 frame format code
>> + * Array indices as follows: [format][dest][decompr]
>> + * Not all combinations are valid. 0 means invalid.
>> + */
>> +static const u16 __csi2_fmt_map[][2][2] = {
>> +     /* RAW10 formats */
>> +     {
>> +             /* Output to memory */
>> +             {
>> +                     /* No DPCM decompression */
>> +                     CSI2_PIX_FMT_RAW10_EXP16,
>> +                     /* DPCM decompression */
>> +                     0,
>> +             },
>> +             /* Output to both */
>> +             {
>> +                     /* No DPCM decompression */
>> +                     CSI2_PIX_FMT_RAW10_EXP16_VP,
>> +                     /* DPCM decompression */
>> +                     0,
>> +             },
>> +     },
>> +     /* RAW10 DPCM8 formats */
>> +     {
>> +             /* Output to memory */
>> +             {
>> +                     /* No DPCM decompression */
>> +                     CSI2_USERDEF_8BIT_DATA1,
>> +                     /* DPCM decompression */
>> +                     CSI2_USERDEF_8BIT_DATA1_DPCM10,
>> +             },
>> +             /* Output to both */
>> +             {
>> +                     /* No DPCM decompression */
>> +                     CSI2_PIX_FMT_RAW8_VP,
>> +                     /* DPCM decompression */
>> +                     CSI2_USERDEF_8BIT_DATA1_DPCM10_VP,
>> +             },
>> +     },
>> +     /* YUV422 formats */
>> +     {
>> +             /* Output to memory */
>> +             {
>> +                     /* No DPCM decompression */
>> +                     CSI2_PIX_FMT_YUV422_8BIT,
>> +                     /* DPCM decompression */
>> +                     0,
>> +             },
>> +             /* Output to both */
>> +             {
>> +                     /* No DPCM decompression */
>> +                     CSI2_PIX_FMT_YUV422_8BIT_VP,
>> +                     /* DPCM decompression */
>> +                     0,
>> +             },
>> +     },
>> +};
>> +
>> +/*
>> + * csi2_ctx_map_format - Map CSI2 sink media bus format to CSI2 format ID
>> + * @csi2: ISS CSI2 device
>> + *
>> + * Returns CSI2 physical format id
>> + */
>> +static u16 csi2_ctx_map_format(struct iss_csi2_device *csi2)
>> +{
>> +     const struct v4l2_mbus_framefmt *fmt = &csi2->formats[CSI2_PAD_SINK];
>> +     int fmtidx, destidx;
>> +
>> +     switch (fmt->code) {
>> +     case V4L2_MBUS_FMT_SGRBG10_1X10:
>> +     case V4L2_MBUS_FMT_SRGGB10_1X10:
>> +     case V4L2_MBUS_FMT_SBGGR10_1X10:
>> +     case V4L2_MBUS_FMT_SGBRG10_1X10:
>> +             fmtidx = 0;
>> +             break;
>> +     case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8:
>> +     case V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8:
>> +     case V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8:
>> +     case V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8:
>> +             fmtidx = 1;
>> +             break;
>> +     case V4L2_MBUS_FMT_UYVY8_1X16:
>> +     case V4L2_MBUS_FMT_YUYV8_1X16:
>> +             fmtidx = 2;
>> +             break;
>> +     default:
>> +             WARN(1, KERN_ERR "CSI2: pixel format %08x unsupported!\n",
>> +                  fmt->code);
>> +             return 0;
>> +     }
>> +
>> +     if (!(csi2->output & CSI2_OUTPUT_IPIPEIF) &&
>> +         !(csi2->output & CSI2_OUTPUT_MEMORY)) {
>> +             /* Neither output enabled is a valid combination */
>> +             return CSI2_PIX_FMT_OTHERS;
>> +     }
>> +
>> +     /* If we need to skip frames at the beginning of the stream disable the
>> +      * video port to avoid sending the skipped frames to the IPIPEIF.
>> +      */
>> +     destidx = csi2->frame_skip ? 0 : !!(csi2->output & CSI2_OUTPUT_IPIPEIF);
>> +
>> +     return __csi2_fmt_map[fmtidx][destidx][csi2->dpcm_decompress];
>> +}
>> +
>> +/*
>> + * csi2_set_outaddr - Set memory address to save output image
>> + * @csi2: Pointer to ISS CSI2a device.
>> + * @addr: 32-bit memory address aligned on 32 byte boundary.
>> + *
>> + * Sets the memory address where the output will be saved.
>> + *
>> + * Returns 0 if successful, or -EINVAL if the address is not in the 32 byte
>> + * boundary.
>> + */
>> +static void csi2_set_outaddr(struct iss_csi2_device *csi2, u32 addr)
>> +{
>> +     struct iss_csi2_ctx_cfg *ctx = &csi2->contexts[0];
>> +
>> +     ctx->ping_addr = addr;
>> +     ctx->pong_addr = addr;
>> +     writel(ctx->ping_addr,
>> +            csi2->regs1 + CSI2_CTX_PING_ADDR(ctx->ctxnum));
>> +     writel(ctx->pong_addr,
>> +            csi2->regs1 + CSI2_CTX_PONG_ADDR(ctx->ctxnum));
>> +}
>> +
>> +/*
>> + * is_usr_def_mapping - Checks whether USER_DEF_MAPPING should
>> + *                   be enabled by CSI2.
>> + * @format_id: mapped format id
>> + *
>> + */
>> +static inline int is_usr_def_mapping(u32 format_id)
>> +{
>> +     return (format_id & 0x40) ? 1 : 0;
>> +}
>> +
>> +/*
>> + * csi2_ctx_enable - Enable specified CSI2 context
>> + * @ctxnum: Context number, valid between 0 and 7 values.
>> + * @enable: enable
>> + *
>> + */
>> +static void csi2_ctx_enable(struct iss_csi2_device *csi2, u8 ctxnum, u8 enable)
>> +{
>> +     struct iss_csi2_ctx_cfg *ctx = &csi2->contexts[ctxnum];
>> +     u32 reg;
>> +
>> +     reg = readl(csi2->regs1 + CSI2_CTX_CTRL1(ctxnum));
>> +
>> +     if (enable) {
>> +             unsigned int skip = 0;
>> +
>> +             if (csi2->frame_skip)
>> +                     skip = csi2->frame_skip;
>> +             else if (csi2->output & CSI2_OUTPUT_MEMORY)
>> +                     skip = 1;
>> +
>> +             reg &= ~CSI2_CTX_CTRL1_COUNT_MASK;
>> +             reg |= CSI2_CTX_CTRL1_COUNT_UNLOCK
>> +                 |  (skip << CSI2_CTX_CTRL1_COUNT_SHIFT)
>> +                 |  CSI2_CTX_CTRL1_CTX_EN;
>> +     } else {
>> +             reg &= ~CSI2_CTX_CTRL1_CTX_EN;
>> +     }
>> +
>> +     writel(reg, csi2->regs1 + CSI2_CTX_CTRL1(ctxnum));
>> +     ctx->enabled = enable;
>> +}
>> +
>> +/*
>> + * csi2_ctx_config - CSI2 context configuration.
>> + * @ctx: context configuration
>> + *
>> + */
>> +static void csi2_ctx_config(struct iss_csi2_device *csi2,
>> +                         struct iss_csi2_ctx_cfg *ctx)
>> +{
>> +     u32 reg;
>> +
>> +     /* Set up CSI2_CTx_CTRL1 */
>> +     reg = readl(csi2->regs1 + CSI2_CTX_CTRL1(ctx->ctxnum));
>> +
>> +     if (ctx->eof_enabled)
>> +             reg |= CSI2_CTX_CTRL1_EOF_EN;
>> +     else
>> +             reg &= ~CSI2_CTX_CTRL1_EOF_EN;
>> +
>> +     if (ctx->eol_enabled)
>> +             reg |= CSI2_CTX_CTRL1_EOL_EN;
>> +     else
>> +             reg &= ~CSI2_CTX_CTRL1_EOL_EN;
>> +
>> +     if (ctx->checksum_enabled)
>> +             reg |= CSI2_CTX_CTRL1_CS_EN;
>> +     else
>> +             reg &= ~CSI2_CTX_CTRL1_CS_EN;
>> +
>> +     writel(reg, csi2->regs1 + CSI2_CTX_CTRL1(ctx->ctxnum));
>> +
>> +     /* Set up CSI2_CTx_CTRL2 */
>> +     reg = readl(csi2->regs1 + CSI2_CTX_CTRL2(ctx->ctxnum));
>> +
>> +     reg &= ~(CSI2_CTX_CTRL2_VIRTUAL_ID_MASK);
>> +     reg |= ctx->virtual_id << CSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT;
>> +
>> +     reg &= ~(CSI2_CTX_CTRL2_FORMAT_MASK);
>> +     reg |= ctx->format_id << CSI2_CTX_CTRL2_FORMAT_SHIFT;
>> +
>> +     if (ctx->dpcm_decompress) {
>> +             if (ctx->dpcm_predictor)
>> +                     reg |= CSI2_CTX_CTRL2_DPCM_PRED;
>> +             else
>> +                     reg &= ~CSI2_CTX_CTRL2_DPCM_PRED;
>> +     }
>> +
>> +     if (is_usr_def_mapping(ctx->format_id)) {
>> +             reg &= ~CSI2_CTX_CTRL2_USER_DEF_MAP_MASK;
>> +             reg |= 2 << CSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT;
>> +     }
>> +
>> +     writel(reg, csi2->regs1 + CSI2_CTX_CTRL2(ctx->ctxnum));
>> +
>> +     /* Set up CSI2_CTx_CTRL3 */
>> +     reg = readl(csi2->regs1 + CSI2_CTX_CTRL3(ctx->ctxnum));
>> +     reg &= ~(CSI2_CTX_CTRL3_ALPHA_MASK);
>> +     reg |= (ctx->alpha << CSI2_CTX_CTRL3_ALPHA_SHIFT);
>> +
>> +     writel(reg, csi2->regs1 + CSI2_CTX_CTRL3(ctx->ctxnum));
>> +
>> +     /* Set up CSI2_CTx_DAT_OFST */
>> +     reg = readl(csi2->regs1 + CSI2_CTX_DAT_OFST(ctx->ctxnum));
>> +     reg &= ~CSI2_CTX_DAT_OFST_MASK;
>> +     reg |= ctx->data_offset;
>> +     writel(reg, csi2->regs1 + CSI2_CTX_DAT_OFST(ctx->ctxnum));
>> +
>> +     writel(ctx->ping_addr,
>> +                    csi2->regs1 + CSI2_CTX_PING_ADDR(ctx->ctxnum));
>> +
>> +     writel(ctx->pong_addr,
>> +                    csi2->regs1 + CSI2_CTX_PONG_ADDR(ctx->ctxnum));
>> +}
>> +
>> +/*
>> + * csi2_timing_config - CSI2 timing configuration.
>> + * @timing: csi2_timing_cfg structure
>> + */
>> +static void csi2_timing_config(struct iss_csi2_device *csi2,
>> +                            struct iss_csi2_timing_cfg *timing)
>> +{
>> +     u32 reg;
>> +
>> +     reg = readl(csi2->regs1 + CSI2_TIMING);
>> +
>> +     if (timing->force_rx_mode)
>> +             reg |= CSI2_TIMING_FORCE_RX_MODE_IO1;
>> +     else
>> +             reg &= ~CSI2_TIMING_FORCE_RX_MODE_IO1;
>> +
>> +     if (timing->stop_state_16x)
>> +             reg |= CSI2_TIMING_STOP_STATE_X16_IO1;
>> +     else
>> +             reg &= ~CSI2_TIMING_STOP_STATE_X16_IO1;
>> +
>> +     if (timing->stop_state_4x)
>> +             reg |= CSI2_TIMING_STOP_STATE_X4_IO1;
>> +     else
>> +             reg &= ~CSI2_TIMING_STOP_STATE_X4_IO1;
>> +
>> +     reg &= ~CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK;
>> +     reg |= timing->stop_state_counter <<
>> +            CSI2_TIMING_STOP_STATE_COUNTER_IO1_SHIFT;
>> +
>> +     writel(reg, csi2->regs1 + CSI2_TIMING);
>> +}
>> +
>> +/*
>> + * csi2_irq_ctx_set - Enables CSI2 Context IRQs.
>> + * @enable: Enable/disable CSI2 Context interrupts
>> + */
>> +static void csi2_irq_ctx_set(struct iss_csi2_device *csi2, int enable)
>> +{
>> +     u32 reg = CSI2_CTX_IRQ_FE;
>> +     int i;
>> +
>> +     if (csi2->use_fs_irq)
>> +             reg |= CSI2_CTX_IRQ_FS;
>> +
>> +     for (i = 0; i < 8; i++) {
>> +             writel(reg, csi2->regs1 + CSI2_CTX_IRQSTATUS(i));
>> +             if (enable)
>> +                     writel(readl(csi2->regs1 + CSI2_CTX_IRQENABLE(i)) | reg,
>> +                             csi2->regs1 + CSI2_CTX_IRQENABLE(i));
>> +             else
>> +                     writel(readl(csi2->regs1 + CSI2_CTX_IRQENABLE(i)) &
>> +                             ~reg,
>> +                             csi2->regs1 + CSI2_CTX_IRQENABLE(i));
>> +     }
>> +}
>> +
>> +/*
>> + * csi2_irq_complexio1_set - Enables CSI2 ComplexIO IRQs.
>> + * @enable: Enable/disable CSI2 ComplexIO #1 interrupts
>> + */
>> +static void csi2_irq_complexio1_set(struct iss_csi2_device *csi2, int enable)
>> +{
>> +     u32 reg;
>> +     reg = CSI2_COMPLEXIO_IRQ_STATEALLULPMEXIT |
>> +             CSI2_COMPLEXIO_IRQ_STATEALLULPMENTER |
>> +             CSI2_COMPLEXIO_IRQ_STATEULPM5 |
>> +             CSI2_COMPLEXIO_IRQ_ERRCONTROL5 |
>> +             CSI2_COMPLEXIO_IRQ_ERRESC5 |
>> +             CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS5 |
>> +             CSI2_COMPLEXIO_IRQ_ERRSOTHS5 |
>> +             CSI2_COMPLEXIO_IRQ_STATEULPM4 |
>> +             CSI2_COMPLEXIO_IRQ_ERRCONTROL4 |
>> +             CSI2_COMPLEXIO_IRQ_ERRESC4 |
>> +             CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS4 |
>> +             CSI2_COMPLEXIO_IRQ_ERRSOTHS4 |
>> +             CSI2_COMPLEXIO_IRQ_STATEULPM3 |
>> +             CSI2_COMPLEXIO_IRQ_ERRCONTROL3 |
>> +             CSI2_COMPLEXIO_IRQ_ERRESC3 |
>> +             CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS3 |
>> +             CSI2_COMPLEXIO_IRQ_ERRSOTHS3 |
>> +             CSI2_COMPLEXIO_IRQ_STATEULPM2 |
>> +             CSI2_COMPLEXIO_IRQ_ERRCONTROL2 |
>> +             CSI2_COMPLEXIO_IRQ_ERRESC2 |
>> +             CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS2 |
>> +             CSI2_COMPLEXIO_IRQ_ERRSOTHS2 |
>> +             CSI2_COMPLEXIO_IRQ_STATEULPM1 |
>> +             CSI2_COMPLEXIO_IRQ_ERRCONTROL1 |
>> +             CSI2_COMPLEXIO_IRQ_ERRESC1 |
>> +             CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS1 |
>> +             CSI2_COMPLEXIO_IRQ_ERRSOTHS1;
>> +     writel(reg, csi2->regs1 + CSI2_COMPLEXIO_IRQSTATUS);
>> +     if (enable)
>> +             reg |= readl(csi2->regs1 + CSI2_COMPLEXIO_IRQENABLE);
>> +     else
>> +             reg = 0;
>> +     writel(reg, csi2->regs1 + CSI2_COMPLEXIO_IRQENABLE);
>> +}
>> +
>> +/*
>> + * csi2_irq_status_set - Enables CSI2 Status IRQs.
>> + * @enable: Enable/disable CSI2 Status interrupts
>> + */
>> +static void csi2_irq_status_set(struct iss_csi2_device *csi2, int enable)
>> +{
>> +     u32 reg;
>> +     reg = CSI2_IRQ_OCP_ERR |
>> +             CSI2_IRQ_SHORT_PACKET |
>> +             CSI2_IRQ_ECC_CORRECTION |
>> +             CSI2_IRQ_ECC_NO_CORRECTION |
>> +             CSI2_IRQ_COMPLEXIO_ERR |
>> +             CSI2_IRQ_FIFO_OVF |
>> +             CSI2_IRQ_CONTEXT0;
>> +     writel(reg, csi2->regs1 + CSI2_IRQSTATUS);
>> +     if (enable)
>> +             reg |= readl(csi2->regs1 + CSI2_IRQENABLE);
>> +     else
>> +             reg = 0;
>> +
>> +     writel(reg, csi2->regs1 + CSI2_IRQENABLE);
>> +}
>> +
>> +/*
>> + * omap4iss_csi2_reset - Resets the CSI2 module.
>> + *
>> + * Must be called with the phy lock held.
>> + *
>> + * Returns 0 if successful, or -EBUSY if power command didn't respond.
>> + */
>> +int omap4iss_csi2_reset(struct iss_csi2_device *csi2)
>> +{
>> +     u8 soft_reset_retries = 0;
>> +     u32 reg;
>> +     int i;
>> +
>> +     if (!csi2->available)
>> +             return -ENODEV;
>> +
>> +     if (csi2->phy->phy_in_use)
>> +             return -EBUSY;
>> +
>> +     writel(readl(csi2->regs1 + CSI2_SYSCONFIG) |
>> +             CSI2_SYSCONFIG_SOFT_RESET,
>> +             csi2->regs1 + CSI2_SYSCONFIG);
>> +
>> +     do {
>> +             reg = readl(csi2->regs1 + CSI2_SYSSTATUS) &
>> +                                 CSI2_SYSSTATUS_RESET_DONE;
>> +             if (reg == CSI2_SYSSTATUS_RESET_DONE)
>> +                     break;
>> +             soft_reset_retries++;
>> +             if (soft_reset_retries < 5)
>> +                     udelay(100);
>> +     } while (soft_reset_retries < 5);
>> +
>> +     if (soft_reset_retries == 5) {
>> +             printk(KERN_ERR "CSI2: Soft reset try count exceeded!\n");
>> +             return -EBUSY;
>> +     }
>> +
>> +     writel(readl(csi2->regs1 + CSI2_COMPLEXIO_CFG) |
>> +             CSI2_COMPLEXIO_CFG_RESET_CTRL,
>> +             csi2->regs1 + CSI2_COMPLEXIO_CFG);
>> +
>> +     i = 100;
>> +     do {
>> +             reg = readl(csi2->phy->phy_regs + REGISTER1)
>> +                 & REGISTER1_RESET_DONE_CTRLCLK;
>> +             if (reg == REGISTER1_RESET_DONE_CTRLCLK)
>> +                     break;
>> +             udelay(100);
>> +     } while (--i > 0);
>> +
>> +     if (i == 0) {
>> +             printk(KERN_ERR
>> +                    "CSI2: Reset for CSI2_96M_FCLK domain Failed!\n");
>> +             return -EBUSY;
>> +     }
>> +
>> +     writel((readl(csi2->regs1 + CSI2_SYSCONFIG) &
>> +             ~(CSI2_SYSCONFIG_MSTANDBY_MODE_MASK |
>> +               CSI2_SYSCONFIG_AUTO_IDLE)) |
>> +             CSI2_SYSCONFIG_MSTANDBY_MODE_NO,
>> +             csi2->regs1 + CSI2_SYSCONFIG);
>> +
>> +     return 0;
>> +}
>> +
>> +static int csi2_configure(struct iss_csi2_device *csi2)
>> +{
>> +     const struct iss_v4l2_subdevs_group *pdata;
>> +     struct iss_csi2_timing_cfg *timing = &csi2->timing[0];
>> +     struct v4l2_subdev *sensor;
>> +     struct media_pad *pad;
>> +
>> +     /*
>> +      * CSI2 fields that can be updated while the context has
>> +      * been enabled or the interface has been enabled are not
>> +      * updated dynamically currently. So we do not allow to
>> +      * reconfigure if either has been enabled
>> +      */
>> +     if (csi2->contexts[0].enabled || csi2->ctrl.if_enable)
>> +             return -EBUSY;
>> +
>> +     pad = media_entity_remote_source(&csi2->pads[CSI2_PAD_SINK]);
>> +     sensor = media_entity_to_v4l2_subdev(pad->entity);
>> +     pdata = sensor->host_priv;
>> +
>> +     csi2->frame_skip = 0;
>> +     v4l2_subdev_call(sensor, sensor, g_skip_frames, &csi2->frame_skip);
>> +
>> +     csi2->ctrl.vp_out_ctrl = pdata->bus.csi2.vpclk_div;
>> +     csi2->ctrl.frame_mode = ISS_CSI2_FRAME_IMMEDIATE;
>> +     csi2->ctrl.ecc_enable = pdata->bus.csi2.crc;
>> +
>> +     timing->force_rx_mode = 1;
>> +     timing->stop_state_16x = 1;
>> +     timing->stop_state_4x = 1;
>> +     timing->stop_state_counter = 0x1FF;
>> +
>> +     /*
>> +      * The CSI2 receiver can't do any format conversion except DPCM
>> +      * decompression, so every set_format call configures both pads
>> +      * and enables DPCM decompression as a special case:
>> +      */
>> +     if (csi2->formats[CSI2_PAD_SINK].code !=
>> +         csi2->formats[CSI2_PAD_SOURCE].code)
>> +             csi2->dpcm_decompress = true;
>> +     else
>> +             csi2->dpcm_decompress = false;
>> +
>> +     csi2->contexts[0].format_id = csi2_ctx_map_format(csi2);
>> +
>> +     if (csi2->video_out.bpl_padding == 0)
>> +             csi2->contexts[0].data_offset = 0;
>> +     else
>> +             csi2->contexts[0].data_offset = csi2->video_out.bpl_value;
>> +
>> +     /*
>> +      * Enable end of frame and end of line signals generation for
>> +      * context 0. These signals are generated from CSI2 receiver to
>> +      * qualify the last pixel of a frame and the last pixel of a line.
>> +      * Without enabling the signals CSI2 receiver writes data to memory
>> +      * beyond buffer size and/or data line offset is not handled correctly.
>> +      */
>> +     csi2->contexts[0].eof_enabled = 1;
>> +     csi2->contexts[0].eol_enabled = 1;
>> +
>> +     csi2_irq_complexio1_set(csi2, 1);
>> +     csi2_irq_ctx_set(csi2, 1);
>> +     csi2_irq_status_set(csi2, 1);
>> +
>> +     /* Set configuration (timings, format and links) */
>> +     csi2_timing_config(csi2, timing);
>> +     csi2_recv_config(csi2, &csi2->ctrl);
>> +     csi2_ctx_config(csi2, &csi2->contexts[0]);
>> +
>> +     return 0;
>> +}
>> +
>> +/*
>> + * csi2_print_status - Prints CSI2 debug information.
>> + */
>> +#define CSI2_PRINT_REGISTER(iss, regs, name)\
>> +     dev_dbg(iss->dev, "###CSI2 " #name "=0x%08x\n", \
>> +             readl(regs + CSI2_##name))
>> +
>> +static void csi2_print_status(struct iss_csi2_device *csi2)
>> +{
>> +     struct iss_device *iss = csi2->iss;
>> +
>> +     if (!csi2->available)
>> +             return;
>> +
>> +     dev_dbg(iss->dev, "-------------CSI2 Register dump-------------\n");
>> +
>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, SYSCONFIG);
>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, SYSSTATUS);
>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, IRQENABLE);
>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, IRQSTATUS);
>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, CTRL);
>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, DBG_H);
>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, COMPLEXIO_CFG);
>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, COMPLEXIO_IRQSTATUS);
>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, SHORT_PACKET);
>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, COMPLEXIO_IRQENABLE);
>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, DBG_P);
>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, TIMING);
>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_CTRL1(0));
>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_CTRL2(0));
>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_DAT_OFST(0));
>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_PING_ADDR(0));
>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_PONG_ADDR(0));
>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_IRQENABLE(0));
>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_IRQSTATUS(0));
>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_CTRL3(0));
>> +
>> +     dev_dbg(iss->dev, "--------------------------------------------\n");
>> +}
>> +
>> +/* -----------------------------------------------------------------------------
>> + * Interrupt handling
>> + */
>> +
>> +/*
>> + * csi2_isr_buffer - Does buffer handling at end-of-frame
>> + * when writing to memory.
>> + */
>> +static void csi2_isr_buffer(struct iss_csi2_device *csi2)
>> +{
>> +     struct iss_buffer *buffer;
>> +
>> +     csi2_ctx_enable(csi2, 0, 0);
>> +
>> +     buffer = omap4iss_video_buffer_next(&csi2->video_out, 0);
>> +
>> +     /*
>> +      * Let video queue operation restart engine if there is an underrun
>> +      * condition.
>> +      */
>> +     if (buffer == NULL)
>> +             return;
>> +
>> +     csi2_set_outaddr(csi2, buffer->iss_addr);
>> +     csi2_ctx_enable(csi2, 0, 1);
>> +}
>> +
>> +static void csi2_isr_ctx(struct iss_csi2_device *csi2,
>> +                      struct iss_csi2_ctx_cfg *ctx)
>> +{
>> +     unsigned int n = ctx->ctxnum;
>> +     u32 status;
>> +
>> +     status = readl(csi2->regs1 + CSI2_CTX_IRQSTATUS(n));
>> +     writel(status, csi2->regs1 + CSI2_CTX_IRQSTATUS(n));
>> +
>> +     /* Propagate frame number */
>> +     if (status & CSI2_CTX_IRQ_FS) {
>> +             struct iss_pipeline *pipe =
>> +                                  to_iss_pipeline(&csi2->subdev.entity);
>> +             if (pipe->do_propagation)
>> +                     atomic_inc(&pipe->frame_number);
>> +     }
>> +
>> +     if (!(status & CSI2_CTX_IRQ_FE))
>> +             return;
>> +
>> +     /* Skip interrupts until we reach the frame skip count. The CSI2 will be
>> +      * automatically disabled, as the frame skip count has been programmed
>> +      * in the CSI2_CTx_CTRL1::COUNT field, so reenable it.
>> +      *
>> +      * It would have been nice to rely on the FRAME_NUMBER interrupt instead
>> +      * but it turned out that the interrupt is only generated when the CSI2
>> +      * writes to memory (the CSI2_CTx_CTRL1::COUNT field is decreased
>> +      * correctly and reaches 0 when data is forwarded to the video port only
>> +      * but no interrupt arrives). Maybe a CSI2 hardware bug.
>> +      */
>> +     if (csi2->frame_skip) {
>> +             csi2->frame_skip--;
>> +             if (csi2->frame_skip == 0) {
>> +                     ctx->format_id = csi2_ctx_map_format(csi2);
>> +                     csi2_ctx_config(csi2, ctx);
>> +                     csi2_ctx_enable(csi2, n, 1);
>> +             }
>> +             return;
>> +     }
>> +
>> +     if (csi2->output & CSI2_OUTPUT_MEMORY)
>> +             csi2_isr_buffer(csi2);
>> +}
>> +
>> +/*
>> + * omap4iss_csi2_isr - CSI2 interrupt handling.
>> + *
>> + * Return -EIO on Transmission error
>> + */
>> +int omap4iss_csi2_isr(struct iss_csi2_device *csi2)
>> +{
>> +     u32 csi2_irqstatus, cpxio1_irqstatus;
>> +     struct iss_device *iss = csi2->iss;
>> +     int retval = 0;
>> +
>> +     if (!csi2->available)
>> +             return -ENODEV;
>> +
>> +     csi2_irqstatus = readl(csi2->regs1 + CSI2_IRQSTATUS);
>> +     writel(csi2_irqstatus, csi2->regs1 + CSI2_IRQSTATUS);
>> +
>> +     /* Failure Cases */
>> +     if (csi2_irqstatus & CSI2_IRQ_COMPLEXIO_ERR) {
>> +             cpxio1_irqstatus = readl(csi2->regs1 +
>> +                                      CSI2_COMPLEXIO_IRQSTATUS);
>> +             writel(cpxio1_irqstatus,
>> +                     csi2->regs1 + CSI2_COMPLEXIO_IRQSTATUS);
>> +             dev_dbg(iss->dev, "CSI2: ComplexIO Error IRQ "
>> +                     "%x\n", cpxio1_irqstatus);
>> +             retval = -EIO;
>> +     }
>> +
>> +     if (csi2_irqstatus & (CSI2_IRQ_OCP_ERR |
>> +                           CSI2_IRQ_SHORT_PACKET |
>> +                           CSI2_IRQ_ECC_NO_CORRECTION |
>> +                           CSI2_IRQ_COMPLEXIO_ERR |
>> +                           CSI2_IRQ_FIFO_OVF)) {
>> +             dev_dbg(iss->dev, "CSI2 Err:"
>> +                     " OCP:%d,"
>> +                     " Short_pack:%d,"
>> +                     " ECC:%d,"
>> +                     " CPXIO:%d,"
>> +                     " FIFO_OVF:%d,"
>> +                     "\n",
>> +                     (csi2_irqstatus &
>> +                      CSI2_IRQ_OCP_ERR) ? 1 : 0,
>> +                     (csi2_irqstatus &
>> +                      CSI2_IRQ_SHORT_PACKET) ? 1 : 0,
>> +                     (csi2_irqstatus &
>> +                      CSI2_IRQ_ECC_NO_CORRECTION) ? 1 : 0,
>> +                     (csi2_irqstatus &
>> +                      CSI2_IRQ_COMPLEXIO_ERR) ? 1 : 0,
>> +                     (csi2_irqstatus &
>> +                      CSI2_IRQ_FIFO_OVF) ? 1 : 0);
>> +             retval = -EIO;
>> +     }
>> +
>> +     if (omap4iss_module_sync_is_stopping(&csi2->wait, &csi2->stopping))
>> +             return 0;
>> +
>> +     /* Successful cases */
>> +     if (csi2_irqstatus & CSI2_IRQ_CONTEXT0)
>> +             csi2_isr_ctx(csi2, &csi2->contexts[0]);
>> +
>> +     if (csi2_irqstatus & CSI2_IRQ_ECC_CORRECTION)
>> +             dev_dbg(iss->dev, "CSI2: ECC correction done\n");
>> +
>> +     return retval;
>> +}
>> +
>> +/* -----------------------------------------------------------------------------
>> + * ISS video operations
>> + */
>> +
>> +/*
>> + * csi2_queue - Queues the first buffer when using memory output
>> + * @video: The video node
>> + * @buffer: buffer to queue
>> + */
>> +static int csi2_queue(struct iss_video *video, struct iss_buffer *buffer)
>> +{
>> +     struct iss_device *iss = video->iss;
>> +     struct iss_csi2_device *csi2 = &iss->csi2a;
>> +
>> +     csi2_set_outaddr(csi2, buffer->iss_addr);
>> +
>> +     /*
>> +      * If streaming was enabled before there was a buffer queued
>> +      * or underrun happened in the ISR, the hardware was not enabled
>> +      * and DMA queue flag ISS_VIDEO_DMAQUEUE_UNDERRUN is still set.
>> +      * Enable it now.
>> +      */
>> +     if (csi2->video_out.dmaqueue_flags & ISS_VIDEO_DMAQUEUE_UNDERRUN) {
>> +             /* Enable / disable context 0 and IRQs */
>> +             csi2_if_enable(csi2, 1);
>> +             csi2_ctx_enable(csi2, 0, 1);
>> +             iss_video_dmaqueue_flags_clr(&csi2->video_out);
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +static const struct iss_video_operations csi2_issvideo_ops = {
>> +     .queue = csi2_queue,
>> +};
>> +
>> +/* -----------------------------------------------------------------------------
>> + * V4L2 subdev operations
>> + */
>> +
>> +static struct v4l2_mbus_framefmt *
>> +__csi2_get_format(struct iss_csi2_device *csi2, struct v4l2_subdev_fh *fh,
>> +               unsigned int pad, enum v4l2_subdev_format_whence which)
>> +{
>> +     if (which == V4L2_SUBDEV_FORMAT_TRY)
>> +             return v4l2_subdev_get_try_format(fh, pad);
>> +     else
>> +             return &csi2->formats[pad];
>> +}
>> +
>> +static void
>> +csi2_try_format(struct iss_csi2_device *csi2, struct v4l2_subdev_fh *fh,
>> +             unsigned int pad, struct v4l2_mbus_framefmt *fmt,
>> +             enum v4l2_subdev_format_whence which)
>> +{
>> +     enum v4l2_mbus_pixelcode pixelcode;
>> +     struct v4l2_mbus_framefmt *format;
>> +     const struct iss_format_info *info;
>> +     unsigned int i;
>> +
>> +     switch (pad) {
>> +     case CSI2_PAD_SINK:
>> +             /* Clamp the width and height to valid range (1-8191). */
>> +             for (i = 0; i < ARRAY_SIZE(csi2_input_fmts); i++) {
>> +                     if (fmt->code == csi2_input_fmts[i])
>> +                             break;
>> +             }
>> +
>> +             /* If not found, use SGRBG10 as default */
>> +             if (i >= ARRAY_SIZE(csi2_input_fmts))
>> +                     fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10;
>> +
>> +             fmt->width = clamp_t(u32, fmt->width, 1, 8191);
>> +             fmt->height = clamp_t(u32, fmt->height, 1, 8191);
>> +             break;
>> +
>> +     case CSI2_PAD_SOURCE:
>> +             /* Source format same as sink format, except for DPCM
>> +              * compression.
>> +              */
>> +             pixelcode = fmt->code;
>> +             format = __csi2_get_format(csi2, fh, CSI2_PAD_SINK, which);
>> +             memcpy(fmt, format, sizeof(*fmt));
>> +
>> +             /*
>> +              * Only Allow DPCM decompression, and check that the
>> +              * pattern is preserved
>> +              */
>> +             info = omap4iss_video_format_info(fmt->code);
>> +             if (info->uncompressed == pixelcode)
>> +                     fmt->code = pixelcode;
>> +             break;
>> +     }
>> +
>> +     /* RGB, non-interlaced */
>> +     fmt->colorspace = V4L2_COLORSPACE_SRGB;
>> +     fmt->field = V4L2_FIELD_NONE;
>> +}
>> +
>> +/*
>> + * csi2_enum_mbus_code - Handle pixel format enumeration
>> + * @sd     : pointer to v4l2 subdev structure
>> + * @fh     : V4L2 subdev file handle
>> + * @code   : pointer to v4l2_subdev_mbus_code_enum structure
>> + * return -EINVAL or zero on success
>> + */
>> +static int csi2_enum_mbus_code(struct v4l2_subdev *sd,
>> +                            struct v4l2_subdev_fh *fh,
>> +                            struct v4l2_subdev_mbus_code_enum *code)
>> +{
>> +     struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
>> +     struct v4l2_mbus_framefmt *format;
>> +     const struct iss_format_info *info;
>> +
>> +     if (code->pad == CSI2_PAD_SINK) {
>> +             if (code->index >= ARRAY_SIZE(csi2_input_fmts))
>> +                     return -EINVAL;
>> +
>> +             code->code = csi2_input_fmts[code->index];
>> +     } else {
>> +             format = __csi2_get_format(csi2, fh, CSI2_PAD_SINK,
>> +                                        V4L2_SUBDEV_FORMAT_TRY);
>> +             switch (code->index) {
>> +             case 0:
>> +                     /* Passthrough sink pad code */
>> +                     code->code = format->code;
>> +                     break;
>> +             case 1:
>> +                     /* Uncompressed code */
>> +                     info = omap4iss_video_format_info(format->code);
>> +                     if (info->uncompressed == format->code)
>> +                             return -EINVAL;
>> +
>> +                     code->code = info->uncompressed;
>> +                     break;
>> +             default:
>> +                     return -EINVAL;
>> +             }
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +static int csi2_enum_frame_size(struct v4l2_subdev *sd,
>> +                             struct v4l2_subdev_fh *fh,
>> +                             struct v4l2_subdev_frame_size_enum *fse)
>> +{
>> +     struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
>> +     struct v4l2_mbus_framefmt format;
>> +
>> +     if (fse->index != 0)
>> +             return -EINVAL;
>> +
>> +     format.code = fse->code;
>> +     format.width = 1;
>> +     format.height = 1;
>> +     csi2_try_format(csi2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
>> +     fse->min_width = format.width;
>> +     fse->min_height = format.height;
>> +
>> +     if (format.code != fse->code)
>> +             return -EINVAL;
>> +
>> +     format.code = fse->code;
>> +     format.width = -1;
>> +     format.height = -1;
>> +     csi2_try_format(csi2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
>> +     fse->max_width = format.width;
>> +     fse->max_height = format.height;
>> +
>> +     return 0;
>> +}
>> +
>> +/*
>> + * csi2_get_format - Handle get format by pads subdev method
>> + * @sd : pointer to v4l2 subdev structure
>> + * @fh : V4L2 subdev file handle
>> + * @fmt: pointer to v4l2 subdev format structure
>> + * return -EINVAL or zero on success
>> + */
>> +static int csi2_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
>> +                        struct v4l2_subdev_format *fmt)
>> +{
>> +     struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
>> +     struct v4l2_mbus_framefmt *format;
>> +
>> +     format = __csi2_get_format(csi2, fh, fmt->pad, fmt->which);
>> +     if (format == NULL)
>> +             return -EINVAL;
>> +
>> +     fmt->format = *format;
>> +     return 0;
>> +}
>> +
>> +/*
>> + * csi2_set_format - Handle set format by pads subdev method
>> + * @sd : pointer to v4l2 subdev structure
>> + * @fh : V4L2 subdev file handle
>> + * @fmt: pointer to v4l2 subdev format structure
>> + * return -EINVAL or zero on success
>> + */
>> +static int csi2_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
>> +                        struct v4l2_subdev_format *fmt)
>> +{
>> +     struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
>> +     struct v4l2_mbus_framefmt *format;
>> +
>> +     format = __csi2_get_format(csi2, fh, fmt->pad, fmt->which);
>> +     if (format == NULL)
>> +             return -EINVAL;
>> +
>> +     csi2_try_format(csi2, fh, fmt->pad, &fmt->format, fmt->which);
>> +     *format = fmt->format;
>> +
>> +     /* Propagate the format from sink to source */
>> +     if (fmt->pad == CSI2_PAD_SINK) {
>> +             format = __csi2_get_format(csi2, fh, CSI2_PAD_SOURCE,
>> +                                        fmt->which);
>> +             *format = fmt->format;
>> +             csi2_try_format(csi2, fh, CSI2_PAD_SOURCE, format, fmt->which);
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +/*
>> + * csi2_init_formats - Initialize formats on all pads
>> + * @sd: ISS CSI2 V4L2 subdevice
>> + * @fh: V4L2 subdev file handle
>> + *
>> + * Initialize all pad formats with default values. If fh is not NULL, try
>> + * formats are initialized on the file handle. Otherwise active formats are
>> + * initialized on the device.
>> + */
>> +static int csi2_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
>> +{
>> +     struct v4l2_subdev_format format;
>> +
>> +     memset(&format, 0, sizeof(format));
>> +     format.pad = CSI2_PAD_SINK;
>> +     format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
>> +     format.format.code = V4L2_MBUS_FMT_SGRBG10_1X10;
>> +     format.format.width = 4096;
>> +     format.format.height = 4096;
>> +     csi2_set_format(sd, fh, &format);
>> +
>> +     return 0;
>> +}
>> +
>> +/*
>> + * csi2_set_stream - Enable/Disable streaming on the CSI2 module
>> + * @sd: ISS CSI2 V4L2 subdevice
>> + * @enable: ISS pipeline stream state
>> + *
>> + * Return 0 on success or a negative error code otherwise.
>> + */
>> +static int csi2_set_stream(struct v4l2_subdev *sd, int enable)
>> +{
>> +     struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
>> +     struct iss_device *iss = csi2->iss;
>> +     struct iss_pipeline *pipe = to_iss_pipeline(&csi2->subdev.entity);
>> +     struct iss_video *video_out = &csi2->video_out;
>> +
>> +     if (csi2->state == ISS_PIPELINE_STREAM_STOPPED) {
>> +             if (enable == ISS_PIPELINE_STREAM_STOPPED)
>> +                     return 0;
>> +
>> +             omap4iss_subclk_enable(iss, OMAP4_ISS_SUBCLK_CSI2_A);
>> +     }
>> +
>> +     switch (enable) {
>> +     case ISS_PIPELINE_STREAM_CONTINUOUS:
>> +             if (omap4iss_csiphy_acquire(csi2->phy) < 0)
>> +                     return -ENODEV;
>> +             csi2->use_fs_irq = pipe->do_propagation;
>> +             csi2_configure(csi2);
>> +             csi2_print_status(csi2);
>> +
>> +             /*
>> +              * When outputting to memory with no buffer available, let the
>> +              * buffer queue handler start the hardware. A DMA queue flag
>> +              * ISS_VIDEO_DMAQUEUE_QUEUED will be set as soon as there is
>> +              * a buffer available.
>> +              */
>> +             if (csi2->output & CSI2_OUTPUT_MEMORY &&
>> +                 !(video_out->dmaqueue_flags & ISS_VIDEO_DMAQUEUE_QUEUED))
>> +                     break;
>> +             /* Enable context 0 and IRQs */
>> +             atomic_set(&csi2->stopping, 0);
>> +             csi2_ctx_enable(csi2, 0, 1);
>> +             csi2_if_enable(csi2, 1);
>> +             iss_video_dmaqueue_flags_clr(video_out);
>> +             break;
>> +
>> +     case ISS_PIPELINE_STREAM_STOPPED:
>> +             if (csi2->state == ISS_PIPELINE_STREAM_STOPPED)
>> +                     return 0;
>> +             if (omap4iss_module_sync_idle(&sd->entity, &csi2->wait,
>> +                                           &csi2->stopping))
>> +                     dev_dbg(iss->dev, "%s: module stop timeout.\n",
>> +                             sd->name);
>> +             csi2_ctx_enable(csi2, 0, 0);
>> +             csi2_if_enable(csi2, 0);
>> +             csi2_irq_ctx_set(csi2, 0);
>> +             omap4iss_csiphy_release(csi2->phy);
>> +             omap4iss_subclk_disable(iss, OMAP4_ISS_SUBCLK_CSI2_A);
>> +             iss_video_dmaqueue_flags_clr(video_out);
>> +             break;
>> +     }
>> +
>> +     csi2->state = enable;
>> +     return 0;
>> +}
>> +
>> +/* subdev video operations */
>> +static const struct v4l2_subdev_video_ops csi2_video_ops = {
>> +     .s_stream = csi2_set_stream,
>> +};
>> +
>> +/* subdev pad operations */
>> +static const struct v4l2_subdev_pad_ops csi2_pad_ops = {
>> +     .enum_mbus_code = csi2_enum_mbus_code,
>> +     .enum_frame_size = csi2_enum_frame_size,
>> +     .get_fmt = csi2_get_format,
>> +     .set_fmt = csi2_set_format,
>> +};
>> +
>> +/* subdev operations */
>> +static const struct v4l2_subdev_ops csi2_ops = {
>> +     .video = &csi2_video_ops,
>> +     .pad = &csi2_pad_ops,
>> +};
>> +
>> +/* subdev internal operations */
>> +static const struct v4l2_subdev_internal_ops csi2_internal_ops = {
>> +     .open = csi2_init_formats,
>> +};
>> +
>> +/* -----------------------------------------------------------------------------
>> + * Media entity operations
>> + */
>> +
>> +/*
>> + * csi2_link_setup - Setup CSI2 connections.
>> + * @entity : Pointer to media entity structure
>> + * @local  : Pointer to local pad array
>> + * @remote : Pointer to remote pad array
>> + * @flags  : Link flags
>> + * return -EINVAL or zero on success
>> + */
>> +static int csi2_link_setup(struct media_entity *entity,
>> +                        const struct media_pad *local,
>> +                        const struct media_pad *remote, u32 flags)
>> +{
>> +     struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
>> +     struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
>> +     struct iss_csi2_ctrl_cfg *ctrl = &csi2->ctrl;
>> +
>> +     /*
>> +      * The ISS core doesn't support pipelines with multiple video outputs.
>> +      * Revisit this when it will be implemented, and return -EBUSY for now.
>> +      */
>> +
>> +     switch (local->index | media_entity_type(remote->entity)) {
>> +     case CSI2_PAD_SOURCE | MEDIA_ENT_T_DEVNODE:
>> +             if (flags & MEDIA_LNK_FL_ENABLED) {
>> +                     if (csi2->output & ~CSI2_OUTPUT_MEMORY)
>> +                             return -EBUSY;
>> +                     csi2->output |= CSI2_OUTPUT_MEMORY;
>> +             } else {
>> +                     csi2->output &= ~CSI2_OUTPUT_MEMORY;
>> +             }
>> +             break;
>> +
>> +     case CSI2_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV:
>> +             if (flags & MEDIA_LNK_FL_ENABLED) {
>> +                     if (csi2->output & ~CSI2_OUTPUT_IPIPEIF)
>> +                             return -EBUSY;
>> +                     csi2->output |= CSI2_OUTPUT_IPIPEIF;
>> +             } else {
>> +                     csi2->output &= ~CSI2_OUTPUT_IPIPEIF;
>> +             }
>> +             break;
>> +
>> +     default:
>> +             /* Link from camera to CSI2 is fixed... */
>> +             return -EINVAL;
>> +     }
>> +
>> +     ctrl->vp_only_enable =
>> +             (csi2->output & CSI2_OUTPUT_MEMORY) ? false : true;
>> +     ctrl->vp_clk_enable = !!(csi2->output & CSI2_OUTPUT_IPIPEIF);
>> +
>> +     return 0;
>> +}
>> +
>> +/* media operations */
>> +static const struct media_entity_operations csi2_media_ops = {
>> +     .link_setup = csi2_link_setup,
>> +};
>> +
>> +/*
>> + * csi2_init_entities - Initialize subdev and media entity.
>> + * @csi2: Pointer to csi2 structure.
>> + * return -ENOMEM or zero on success
>> + */
>> +static int csi2_init_entities(struct iss_csi2_device *csi2)
>> +{
>> +     struct v4l2_subdev *sd = &csi2->subdev;
>> +     struct media_pad *pads = csi2->pads;
>> +     struct media_entity *me = &sd->entity;
>> +     int ret;
>> +
>> +     v4l2_subdev_init(sd, &csi2_ops);
>> +     sd->internal_ops = &csi2_internal_ops;
>> +     strlcpy(sd->name, "OMAP4 ISS CSI2a", sizeof(sd->name));
>> +
>> +     sd->grp_id = 1 << 16;   /* group ID for iss subdevs */
>> +     v4l2_set_subdevdata(sd, csi2);
>> +     sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
>> +
>> +     pads[CSI2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
>> +     pads[CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
>> +
>> +     me->ops = &csi2_media_ops;
>> +     ret = media_entity_init(me, CSI2_PADS_NUM, pads, 0);
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     csi2_init_formats(sd, NULL);
>> +
>> +     /* Video device node */
>> +     csi2->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>> +     csi2->video_out.ops = &csi2_issvideo_ops;
>> +     csi2->video_out.bpl_alignment = 32;
>> +     csi2->video_out.bpl_zero_padding = 1;
>> +     csi2->video_out.bpl_max = 0x1ffe0;
>> +     csi2->video_out.iss = csi2->iss;
>> +     csi2->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 3;
>> +
>> +     ret = omap4iss_video_init(&csi2->video_out, "CSI2a");
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     /* Connect the CSI2 subdev to the video node. */
>> +     ret = media_entity_create_link(&csi2->subdev.entity, CSI2_PAD_SOURCE,
>> +                                    &csi2->video_out.video.entity, 0, 0);
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     return 0;
>> +}
>> +
>> +void omap4iss_csi2_unregister_entities(struct iss_csi2_device *csi2)
>> +{
>> +     media_entity_cleanup(&csi2->subdev.entity);
>> +
>> +     v4l2_device_unregister_subdev(&csi2->subdev);
>> +     omap4iss_video_unregister(&csi2->video_out);
>> +}
>> +
>> +int omap4iss_csi2_register_entities(struct iss_csi2_device *csi2,
>> +                                 struct v4l2_device *vdev)
>> +{
>> +     int ret;
>> +
>> +     /* Register the subdev and video nodes. */
>> +     ret = v4l2_device_register_subdev(vdev, &csi2->subdev);
>> +     if (ret < 0)
>> +             goto error;
>> +
>> +     ret = omap4iss_video_register(&csi2->video_out, vdev);
>> +     if (ret < 0)
>> +             goto error;
>> +
>> +     return 0;
>> +
>> +error:
>> +     omap4iss_csi2_unregister_entities(csi2);
>> +     return ret;
>> +}
>> +
>> +/* -----------------------------------------------------------------------------
>> + * ISS CSI2 initialisation and cleanup
>> + */
>> +
>> +/*
>> + * omap4iss_csi2_cleanup - Routine for module driver cleanup
>> + */
>> +void omap4iss_csi2_cleanup(struct iss_device *iss)
>> +{
>> +}
>> +
>> +/*
>> + * omap4iss_csi2_init - Routine for module driver init
>> + */
>> +int omap4iss_csi2_init(struct iss_device *iss)
>> +{
>> +     struct iss_csi2_device *csi2a = &iss->csi2a;
>> +     int ret;
>> +
>> +     csi2a->iss = iss;
>> +     csi2a->available = 1;
>> +     csi2a->regs1 = iss->regs[OMAP4_ISS_MEM_CSI2_A_REGS1];
>> +     csi2a->phy = &iss->csiphy1;
>> +     csi2a->state = ISS_PIPELINE_STREAM_STOPPED;
>> +     init_waitqueue_head(&csi2a->wait);
>> +
>> +     ret = csi2_init_entities(csi2a);
>> +     if (ret < 0)
>> +             goto fail;
>> +
>> +     return 0;
>> +fail:
>> +     omap4iss_csi2_cleanup(iss);
>> +     return ret;
>> +}
>> diff --git a/drivers/media/video/omap4iss/iss_csi2.h b/drivers/media/video/omap4iss/iss_csi2.h
>> new file mode 100644
>> index 0000000..4fa94cf
>> --- /dev/null
>> +++ b/drivers/media/video/omap4iss/iss_csi2.h
>> @@ -0,0 +1,166 @@
>> +/*
>> + * iss_csi2.h
>> + *
>> + * TI OMAP4 ISS - CSI2 module
>> + *
>> + * Copyright (C) 2011 Texas Instruments, Inc.
>> + *
>> + * Contacts: Sergio Aguirre <saaguirre@ti.com>
>> + *
>> + * 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.
>> + *
>> + * This program is distributed in the hope that it will be useful, but
>> + * WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write to the Free Software
>> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
>> + * 02110-1301 USA
>> + */
>> +
>> +#ifndef OMAP4_ISS_CSI2_H
>> +#define OMAP4_ISS_CSI2_H
>> +
>> +#include <linux/types.h>
>> +#include <linux/videodev2.h>
>> +
>> +#include "iss_video.h"
>> +
>> +struct iss_csiphy;
>> +
>> +/* This is not an exhaustive list */
>> +enum iss_csi2_pix_formats {
>> +     CSI2_PIX_FMT_OTHERS = 0,
>> +     CSI2_PIX_FMT_YUV422_8BIT = 0x1e,
>> +     CSI2_PIX_FMT_YUV422_8BIT_VP = 0x9e,
>> +     CSI2_PIX_FMT_RAW10_EXP16 = 0xab,
>> +     CSI2_PIX_FMT_RAW10_EXP16_VP = 0x12f,
>> +     CSI2_PIX_FMT_RAW8 = 0x2a,
>> +     CSI2_PIX_FMT_RAW8_DPCM10_EXP16 = 0x2aa,
>> +     CSI2_PIX_FMT_RAW8_DPCM10_VP = 0x32a,
>> +     CSI2_PIX_FMT_RAW8_VP = 0x12a,
>> +     CSI2_USERDEF_8BIT_DATA1_DPCM10_VP = 0x340,
>> +     CSI2_USERDEF_8BIT_DATA1_DPCM10 = 0x2c0,
>> +     CSI2_USERDEF_8BIT_DATA1 = 0x40,
>> +};
>> +
>> +enum iss_csi2_irqevents {
>> +     OCP_ERR_IRQ = 0x4000,
>> +     SHORT_PACKET_IRQ = 0x2000,
>> +     ECC_CORRECTION_IRQ = 0x1000,
>> +     ECC_NO_CORRECTION_IRQ = 0x800,
>> +     COMPLEXIO2_ERR_IRQ = 0x400,
>> +     COMPLEXIO1_ERR_IRQ = 0x200,
>> +     FIFO_OVF_IRQ = 0x100,
>> +     CONTEXT7 = 0x80,
>> +     CONTEXT6 = 0x40,
>> +     CONTEXT5 = 0x20,
>> +     CONTEXT4 = 0x10,
>> +     CONTEXT3 = 0x8,
>> +     CONTEXT2 = 0x4,
>> +     CONTEXT1 = 0x2,
>> +     CONTEXT0 = 0x1,
>> +};
>> +
>> +enum iss_csi2_ctx_irqevents {
>> +     CTX_ECC_CORRECTION = 0x100,
>> +     CTX_LINE_NUMBER = 0x80,
>> +     CTX_FRAME_NUMBER = 0x40,
>> +     CTX_CS = 0x20,
>> +     CTX_LE = 0x8,
>> +     CTX_LS = 0x4,
>> +     CTX_FE = 0x2,
>> +     CTX_FS = 0x1,
>> +};
>> +
>> +enum iss_csi2_frame_mode {
>> +     ISS_CSI2_FRAME_IMMEDIATE,
>> +     ISS_CSI2_FRAME_AFTERFEC,
>> +};
>> +
>> +#define ISS_CSI2_MAX_CTX_NUM 7
>> +
>> +struct iss_csi2_ctx_cfg {
>> +     u8 ctxnum;              /* context number 0 - 7 */
>> +     u8 dpcm_decompress;
>> +
>> +     /* Fields in CSI2_CTx_CTRL2 - locked by CSI2_CTx_CTRL1.CTX_EN */
>> +     u8 virtual_id;
>> +     u16 format_id;          /* as in CSI2_CTx_CTRL2[9:0] */
>> +     u8 dpcm_predictor;      /* 1: simple, 0: advanced */
>> +
>> +     /* Fields in CSI2_CTx_CTRL1/3 - Shadowed */
>> +     u16 alpha;
>> +     u16 data_offset;
>> +     u32 ping_addr;
>> +     u32 pong_addr;
>> +     u8 eof_enabled;
>> +     u8 eol_enabled;
>> +     u8 checksum_enabled;
>> +     u8 enabled;
>> +};
>> +
>> +struct iss_csi2_timing_cfg {
>> +     u8 ionum;                       /* IO1 or IO2 as in CSI2_TIMING */
>> +     unsigned force_rx_mode:1;
>> +     unsigned stop_state_16x:1;
>> +     unsigned stop_state_4x:1;
>> +     u16 stop_state_counter;
>> +};
>> +
>> +struct iss_csi2_ctrl_cfg {
>> +     bool vp_clk_enable;
>> +     bool vp_only_enable;
>> +     u8 vp_out_ctrl;
>> +     enum iss_csi2_frame_mode frame_mode;
>> +     bool ecc_enable;
>> +     bool if_enable;
>> +};
>> +
>> +#define CSI2_PAD_SINK                0
>> +#define CSI2_PAD_SOURCE              1
>> +#define CSI2_PADS_NUM                2
>> +
>> +#define CSI2_OUTPUT_IPIPEIF  (1 << 0)
>> +#define CSI2_OUTPUT_MEMORY   (1 << 1)
>> +
>> +struct iss_csi2_device {
>> +     struct v4l2_subdev subdev;
>> +     struct media_pad pads[CSI2_PADS_NUM];
>> +     struct v4l2_mbus_framefmt formats[CSI2_PADS_NUM];
>> +
>> +     struct iss_video video_out;
>> +     struct iss_device *iss;
>> +
>> +     u8 available;           /* Is the IP present on the silicon? */
>> +
>> +     /* Pointer to register remaps into kernel space */
>> +     void __iomem *regs1;
>> +     void __iomem *regs2;
>> +
>> +     u32 output; /* output to IPIPEIF, memory or both? */
>> +     bool dpcm_decompress;
>> +     unsigned int frame_skip;
>> +     bool use_fs_irq;
>> +
>> +     struct iss_csiphy *phy;
>> +     struct iss_csi2_ctx_cfg contexts[ISS_CSI2_MAX_CTX_NUM + 1];
>> +     struct iss_csi2_timing_cfg timing[2];
>> +     struct iss_csi2_ctrl_cfg ctrl;
>> +     enum iss_pipeline_stream_state state;
>> +     wait_queue_head_t wait;
>> +     atomic_t stopping;
>> +};
>> +
>> +int omap4iss_csi2_isr(struct iss_csi2_device *csi2);
>> +int omap4iss_csi2_reset(struct iss_csi2_device *csi2);
>> +int omap4iss_csi2_init(struct iss_device *iss);
>> +void omap4iss_csi2_cleanup(struct iss_device *iss);
>> +void omap4iss_csi2_unregister_entities(struct iss_csi2_device *csi2);
>> +int omap4iss_csi2_register_entities(struct iss_csi2_device *csi2,
>> +                                 struct v4l2_device *vdev);
>> +#endif       /* OMAP4_ISS_CSI2_H */
>> diff --git a/drivers/media/video/omap4iss/iss_csiphy.c b/drivers/media/video/omap4iss/iss_csiphy.c
>> new file mode 100644
>> index 0000000..9545622
>> --- /dev/null
>> +++ b/drivers/media/video/omap4iss/iss_csiphy.c
>> @@ -0,0 +1,215 @@
>> +/*
>> + * iss_csiphy.c
>> + *
>> + * TI OMAP4 ISS - CSI PHY module
>> + *
>> + * Copyright (C) 2011 Texas Instruments, Inc.
>> + *
>> + * Contacts: Sergio Aguirre <saaguirre@ti.com>
>> + *
>> + * 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.
>> + *
>> + * This program is distributed in the hope that it will be useful, but
>> + * WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write to the Free Software
>> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
>> + * 02110-1301 USA
>> + */
>> +
>> +#include <linux/delay.h>
>> +#include <linux/device.h>
>> +
>> +#include "iss.h"
>> +#include "iss_regs.h"
>> +#include "iss_csiphy.h"
>> +
>> +/*
>> + * csiphy_lanes_config - Configuration of CSIPHY lanes.
>> + *
>> + * Updates HW configuration.
>> + * Called with phy->mutex taken.
>> + */
>> +static void csiphy_lanes_config(struct iss_csiphy *phy)
>> +{
>> +     unsigned int i;
>> +     u32 reg;
>> +
>> +     reg = readl(phy->cfg_regs + CSI2_COMPLEXIO_CFG);
>> +
>> +     for (i = 0; i < phy->num_data_lanes; i++) {
>> +             reg &= ~(CSI2_COMPLEXIO_CFG_DATA_POL(i + 1) |
>> +                      CSI2_COMPLEXIO_CFG_DATA_POSITION_MASK(i + 1));
>> +             reg |= (phy->lanes.data[i].pol ?
>> +                     CSI2_COMPLEXIO_CFG_DATA_POL(i + 1) : 0);
>> +             reg |= (phy->lanes.data[i].pos <<
>> +                     CSI2_COMPLEXIO_CFG_DATA_POSITION_SHIFT(i + 1));
>> +     }
>> +
>> +     reg &= ~(CSI2_COMPLEXIO_CFG_CLOCK_POL |
>> +              CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK);
>> +     reg |= phy->lanes.clk.pol ? CSI2_COMPLEXIO_CFG_CLOCK_POL : 0;
>> +     reg |= phy->lanes.clk.pos << CSI2_COMPLEXIO_CFG_CLOCK_POSITION_SHIFT;
>> +
>> +     writel(reg, phy->cfg_regs + CSI2_COMPLEXIO_CFG);
>> +}
>> +
>> +/*
>> + * csiphy_set_power
>> + * @power: Power state to be set.
>> + *
>> + * Returns 0 if successful, or -EBUSY if the retry count is exceeded.
>> + */
>> +static int csiphy_set_power(struct iss_csiphy *phy, u32 power)
>> +{
>> +     u32 reg;
>> +     u8 retry_count;
>> +
>> +     writel((readl(phy->cfg_regs + CSI2_COMPLEXIO_CFG) &
>> +             ~CSI2_COMPLEXIO_CFG_PWD_CMD_MASK) |
>> +             power,
>> +             phy->cfg_regs + CSI2_COMPLEXIO_CFG);
>> +
>> +     retry_count = 0;
>> +     do {
>> +             udelay(50);
>> +             reg = readl(phy->cfg_regs + CSI2_COMPLEXIO_CFG) &
>> +                             CSI2_COMPLEXIO_CFG_PWD_STATUS_MASK;
>> +
>> +             if (reg != power >> 2)
>> +                     retry_count++;
>> +
>> +     } while ((reg != power >> 2) && (retry_count < 100));
>> +
>> +     if (retry_count == 100) {
>> +             printk(KERN_ERR "CSI2 CIO set power failed!\n");
>> +             return -EBUSY;
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +/*
>> + * csiphy_dphy_config - Configure CSI2 D-PHY parameters.
>> + *
>> + * Called with phy->mutex taken.
>> + */
>> +static void csiphy_dphy_config(struct iss_csiphy *phy)
>> +{
>> +     u32 reg;
>> +
>> +     /* Set up REGISTER0 */
>> +     reg = readl(phy->phy_regs + REGISTER0);
>> +
>> +     reg &= ~(REGISTER0_THS_TERM_MASK |
>> +              REGISTER0_THS_SETTLE_MASK);
>> +     reg |= phy->dphy.ths_term << REGISTER0_THS_TERM_SHIFT;
>> +     reg |= phy->dphy.ths_settle << REGISTER0_THS_SETTLE_SHIFT;
>> +
>> +     writel(reg, phy->phy_regs + REGISTER0);
>> +
>> +     /* Set up REGISTER1 */
>> +     reg = readl(phy->phy_regs + REGISTER1);
>> +
>> +     reg &= ~(REGISTER1_TCLK_TERM_MASK |
>> +              REGISTER1_CTRLCLK_DIV_FACTOR_MASK |
>> +              REGISTER1_TCLK_SETTLE_MASK);
>> +     reg |= phy->dphy.tclk_term << REGISTER1_TCLK_TERM_SHIFT;
>> +     reg |= phy->dphy.tclk_miss << REGISTER1_CTRLCLK_DIV_FACTOR_SHIFT;
>> +     reg |= phy->dphy.tclk_settle << REGISTER1_TCLK_SETTLE_SHIFT;
>> +
>> +     writel(reg, phy->phy_regs + REGISTER1);
>> +}
>> +
>> +static int csiphy_config(struct iss_csiphy *phy,
>> +                      struct iss_csiphy_dphy_cfg *dphy,
>> +                      struct iss_csiphy_lanes_cfg *lanes)
>> +{
>> +     unsigned int used_lanes = 0;
>> +     unsigned int i;
>> +
>> +     /* Clock and data lanes verification */
>> +     for (i = 0; i < phy->num_data_lanes; i++) {
>> +             if (lanes->data[i].pos == 0)
>> +                     continue;
>> +
>> +             if (lanes->data[i].pol > 1 || lanes->data[i].pos > 5)
>> +                     return -EINVAL;
>> +
>> +             if (used_lanes & (1 << lanes->data[i].pos))
>> +                     return -EINVAL;
>> +
>> +             used_lanes |= 1 << lanes->data[i].pos;
>> +     }
>> +
>> +     if (lanes->clk.pol > 1 || lanes->clk.pos > 4)
>> +             return -EINVAL;
>> +
>> +     if (lanes->clk.pos == 0 || used_lanes & (1 << lanes->clk.pos))
>> +             return -EINVAL;
>> +
>> +     mutex_lock(&phy->mutex);
>> +     phy->dphy = *dphy;
>> +     phy->lanes = *lanes;
>> +     mutex_unlock(&phy->mutex);
>> +
>> +     return 0;
>> +}
>> +
>> +int omap4iss_csiphy_acquire(struct iss_csiphy *phy)
>> +{
>> +     int rval;
>> +
>> +     mutex_lock(&phy->mutex);
>> +
>> +     rval = omap4iss_csi2_reset(phy->csi2);
>> +     if (rval)
>> +             goto done;
>> +
>> +     csiphy_dphy_config(phy);
>> +     csiphy_lanes_config(phy);
>> +
>> +     rval = csiphy_set_power(phy, CSI2_COMPLEXIO_CFG_PWD_CMD_ON);
>> +     if (rval)
>> +             goto done;
>> +
>> +     phy->phy_in_use = 1;
>> +
>> +done:
>> +     mutex_unlock(&phy->mutex);
>> +     return rval;
>> +}
>> +
>> +void omap4iss_csiphy_release(struct iss_csiphy *phy)
>> +{
>> +     mutex_lock(&phy->mutex);
>> +     if (phy->phy_in_use) {
>> +             csiphy_set_power(phy, CSI2_COMPLEXIO_CFG_PWD_CMD_OFF);
>> +             phy->phy_in_use = 0;
>> +     }
>> +     mutex_unlock(&phy->mutex);
>> +}
>> +
>> +/*
>> + * omap4iss_csiphy_init - Initialize the CSI PHY frontends
>> + */
>> +int omap4iss_csiphy_init(struct iss_device *iss)
>> +{
>> +     struct iss_csiphy *phy1 = &iss->csiphy1;
>> +
>> +     iss->platform_cb.csiphy_config = csiphy_config;
>> +
>> +     phy1->iss = iss;
>> +     phy1->csi2 = &iss->csi2a;
>> +     phy1->num_data_lanes = ISS_CSIPHY1_NUM_DATA_LANES;
>> +     phy1->cfg_regs = iss->regs[OMAP4_ISS_MEM_CSI2_A_REGS1];
>> +     phy1->phy_regs = iss->regs[OMAP4_ISS_MEM_CAMERARX_CORE1];
>> +     mutex_init(&phy1->mutex);
>> +
>> +     return 0;
>> +}
>> diff --git a/drivers/media/video/omap4iss/iss_csiphy.h b/drivers/media/video/omap4iss/iss_csiphy.h
>> new file mode 100644
>> index 0000000..c513ba8
>> --- /dev/null
>> +++ b/drivers/media/video/omap4iss/iss_csiphy.h
>> @@ -0,0 +1,69 @@
>> +/*
>> + * iss_csiphy.h
>> + *
>> + * TI OMAP4 ISS - CSI PHY module
>> + *
>> + * Copyright (C) 2011 Texas Instruments, Inc.
>> + *
>> + * Contacts: Sergio Aguirre <saaguirre@ti.com>
>> + *
>> + * 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.
>> + *
>> + * This program is distributed in the hope that it will be useful, but
>> + * WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write to the Free Software
>> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
>> + * 02110-1301 USA
>> + */
>> +
>> +#ifndef OMAP4_ISS_CSI_PHY_H
>> +#define OMAP4_ISS_CSI_PHY_H
>> +
>> +struct iss_csi2_device;
>> +
>> +struct csiphy_lane {
>> +     u8 pos;
>> +     u8 pol;
>> +};
>> +
>> +#define ISS_CSIPHY1_NUM_DATA_LANES   4
>> +
>> +struct iss_csiphy_lanes_cfg {
>> +     struct csiphy_lane data[ISS_CSIPHY1_NUM_DATA_LANES];
>> +     struct csiphy_lane clk;
>> +};
>> +
>> +struct iss_csiphy_dphy_cfg {
>> +     u8 ths_term;
>> +     u8 ths_settle;
>> +     u8 tclk_term;
>> +     unsigned tclk_miss:1;
>> +     u8 tclk_settle;
>> +};
>> +
>> +struct iss_csiphy {
>> +     struct iss_device *iss;
>> +     struct mutex mutex;     /* serialize csiphy configuration */
>> +     u8 phy_in_use;
>> +     struct iss_csi2_device *csi2;
>> +
>> +     /* Pointer to register remaps into kernel space */
>> +     void __iomem *cfg_regs;
>> +     void __iomem *phy_regs;
>> +
>> +     u8 num_data_lanes;      /* number of CSI2 Data Lanes supported */
>> +     struct iss_csiphy_lanes_cfg lanes;
>> +     struct iss_csiphy_dphy_cfg dphy;
>> +};
>> +
>> +int omap4iss_csiphy_acquire(struct iss_csiphy *phy);
>> +void omap4iss_csiphy_release(struct iss_csiphy *phy);
>> +int omap4iss_csiphy_init(struct iss_device *iss);
>> +
>> +#endif       /* OMAP4_ISS_CSI_PHY_H */
>> diff --git a/drivers/media/video/omap4iss/iss_regs.h b/drivers/media/video/omap4iss/iss_regs.h
>> new file mode 100644
>> index 0000000..0bd70ac
>> --- /dev/null
>> +++ b/drivers/media/video/omap4iss/iss_regs.h
>> @@ -0,0 +1,238 @@
>> +/*
>> + * iss_regs.h
>> + *
>> + * Copyright (C) 2011 Texas Instruments.
>> + *
>> + * Author: Sergio Aguirre <saaguirre@ti.com>
>> + */
>> +
>> +#ifndef _OMAP4_ISS_REGS_H_
>> +#define _OMAP4_ISS_REGS_H_
>> +
>> +/* ISS */
>> +#define ISS_HL_REVISION                                      (0x0)
>> +
>> +#define ISS_HL_SYSCONFIG                             (0x10)
>> +#define ISS_HL_SYSCONFIG_IDLEMODE_SHIFT                      2
>> +#define ISS_HL_SYSCONFIG_IDLEMODE_FORCEIDLE          0x0
>> +#define ISS_HL_SYSCONFIG_IDLEMODE_NOIDLE             0x1
>> +#define ISS_HL_SYSCONFIG_IDLEMODE_SMARTIDLE          0x2
>> +#define ISS_HL_SYSCONFIG_SOFTRESET                   (1 << 0)
>> +
>> +#define ISS_HL_IRQSTATUS_5                           (0x24 + (0x10 * 5))
>> +#define ISS_HL_IRQENABLE_5_SET                               (0x28 + (0x10 * 5))
>> +#define ISS_HL_IRQENABLE_5_CLR                               (0x2C + (0x10 * 5))
>> +
>> +#define ISS_HL_IRQ_BTE                                       (1 << 11)
>> +#define ISS_HL_IRQ_CBUFF                             (1 << 10)
>> +#define ISS_HL_IRQ_CSIA                                      (1 << 4)
>> +
>> +#define ISS_CTRL                                     (0x80)
>> +
>> +#define ISS_CLKCTRL                                  (0x84)
>> +#define ISS_CLKCTRL_VPORT2_CLK                               (1 << 30)
>> +#define ISS_CLKCTRL_VPORT1_CLK                               (1 << 29)
>> +#define ISS_CLKCTRL_VPORT0_CLK                               (1 << 28)
>> +#define ISS_CLKCTRL_CCP2                             (1 << 4)
>> +#define ISS_CLKCTRL_CSI2_B                           (1 << 3)
>> +#define ISS_CLKCTRL_CSI2_A                           (1 << 2)
>> +#define ISS_CLKCTRL_ISP                                      (1 << 1)
>> +#define ISS_CLKCTRL_SIMCOP                           (1 << 0)
>> +
>> +#define ISS_CLKSTAT                                  (0x88)
>> +#define ISS_CLKSTAT_VPORT2_CLK                               (1 << 30)
>> +#define ISS_CLKSTAT_VPORT1_CLK                               (1 << 29)
>> +#define ISS_CLKSTAT_VPORT0_CLK                               (1 << 28)
>> +#define ISS_CLKSTAT_CCP2                             (1 << 4)
>> +#define ISS_CLKSTAT_CSI2_B                           (1 << 3)
>> +#define ISS_CLKSTAT_CSI2_A                           (1 << 2)
>> +#define ISS_CLKSTAT_ISP                                      (1 << 1)
>> +#define ISS_CLKSTAT_SIMCOP                           (1 << 0)
>> +
>> +#define ISS_PM_STATUS                                        (0x8C)
>> +#define ISS_PM_STATUS_CBUFF_PM_MASK                  (3 << 12)
>> +#define ISS_PM_STATUS_BTE_PM_MASK                    (3 << 10)
>> +#define ISS_PM_STATUS_SIMCOP_PM_MASK                 (3 << 8)
>> +#define ISS_PM_STATUS_ISP_PM_MASK                    (3 << 6)
>> +#define ISS_PM_STATUS_CCP2_PM_MASK                   (3 << 4)
>> +#define ISS_PM_STATUS_CSI2_B_PM_MASK                 (3 << 2)
>> +#define ISS_PM_STATUS_CSI2_A_PM_MASK                 (3 << 0)
>> +
>> +#define REGISTER0                                    (0x0)
>> +#define REGISTER0_HSCLOCKCONFIG                              (1 << 24)
>> +#define REGISTER0_THS_TERM_MASK                              (0xFF << 8)
>> +#define REGISTER0_THS_TERM_SHIFT                     8
>> +#define REGISTER0_THS_SETTLE_MASK                    (0xFF << 0)
>> +#define REGISTER0_THS_SETTLE_SHIFT                   0
>> +
>> +#define REGISTER1                                    (0x4)
>> +#define REGISTER1_RESET_DONE_CTRLCLK                 (1 << 29)
>> +#define REGISTER1_CLOCK_MISS_DETECTOR_STATUS         (1 << 25)
>> +#define REGISTER1_TCLK_TERM_MASK                     (0x3F << 18)
>> +#define REGISTER1_TCLK_TERM_SHIFT                    18
>> +#define REGISTER1_DPHY_HS_SYNC_PATTERN_MASK          (0xFF << 10)
>> +#define REGISTER1_CTRLCLK_DIV_FACTOR_MASK            (0x3 << 8)
>> +#define REGISTER1_CTRLCLK_DIV_FACTOR_SHIFT           8
>> +#define REGISTER1_TCLK_SETTLE_MASK                   (0xFF << 0)
>> +#define REGISTER1_TCLK_SETTLE_SHIFT                  0
>> +
>> +#define REGISTER2                                    (0x8)
>> +
>> +#define CSI2_SYSCONFIG                                       (0x10)
>> +#define CSI2_SYSCONFIG_MSTANDBY_MODE_MASK            (3 << 12)
>> +#define CSI2_SYSCONFIG_MSTANDBY_MODE_FORCE           (0 << 12)
>> +#define CSI2_SYSCONFIG_MSTANDBY_MODE_NO                      (1 << 12)
>> +#define CSI2_SYSCONFIG_MSTANDBY_MODE_SMART           (2 << 12)
>> +#define CSI2_SYSCONFIG_SOFT_RESET                    (1 << 1)
>> +#define CSI2_SYSCONFIG_AUTO_IDLE                     (1 << 0)
>> +
>> +#define CSI2_SYSSTATUS                                       (0x14)
>> +#define CSI2_SYSSTATUS_RESET_DONE                    (1 << 0)
>> +
>> +#define CSI2_IRQSTATUS                                       (0x18)
>> +#define CSI2_IRQENABLE                                       (0x1C)
>> +
>> +/* Shared bits across CSI2_IRQENABLE and IRQSTATUS */
>> +
>> +#define CSI2_IRQ_OCP_ERR                             (1 << 14)
>> +#define CSI2_IRQ_SHORT_PACKET                                (1 << 13)
>> +#define CSI2_IRQ_ECC_CORRECTION                              (1 << 12)
>> +#define CSI2_IRQ_ECC_NO_CORRECTION                   (1 << 11)
>> +#define CSI2_IRQ_COMPLEXIO_ERR                               (1 << 9)
>> +#define CSI2_IRQ_FIFO_OVF                            (1 << 8)
>> +#define CSI2_IRQ_CONTEXT0                            (1 << 0)
>> +
>> +#define CSI2_CTRL                                    (0x40)
>> +#define CSI2_CTRL_MFLAG_LEVH_MASK                    (7 << 20)
>> +#define CSI2_CTRL_MFLAG_LEVH_SHIFT                   20
>> +#define CSI2_CTRL_MFLAG_LEVL_MASK                    (7 << 17)
>> +#define CSI2_CTRL_MFLAG_LEVL_SHIFT                   17
>> +#define CSI2_CTRL_BURST_SIZE_EXPAND                  (1 << 16)
>> +#define CSI2_CTRL_VP_CLK_EN                          (1 << 15)
>> +#define CSI2_CTRL_NON_POSTED_WRITE                   (1 << 13)
>> +#define CSI2_CTRL_VP_ONLY_EN                         (1 << 11)
>> +#define CSI2_CTRL_VP_OUT_CTRL_MASK                   (3 << 8)
>> +#define CSI2_CTRL_VP_OUT_CTRL_SHIFT                  8
>> +#define CSI2_CTRL_DBG_EN                             (1 << 7)
>> +#define CSI2_CTRL_BURST_SIZE_MASK                    (3 << 5)
>> +#define CSI2_CTRL_ENDIANNESS                         (1 << 4)
>> +#define CSI2_CTRL_FRAME                                      (1 << 3)
>> +#define CSI2_CTRL_ECC_EN                             (1 << 2)
>> +#define CSI2_CTRL_IF_EN                                      (1 << 0)
>> +
>> +#define CSI2_DBG_H                                   (0x44)
>> +
>> +#define CSI2_COMPLEXIO_CFG                           (0x50)
>> +#define CSI2_COMPLEXIO_CFG_RESET_CTRL                        (1 << 30)
>> +#define CSI2_COMPLEXIO_CFG_RESET_DONE                        (1 << 29)
>> +#define CSI2_COMPLEXIO_CFG_PWD_CMD_MASK                      (3 << 27)
>> +#define CSI2_COMPLEXIO_CFG_PWD_CMD_OFF                       (0 << 27)
>> +#define CSI2_COMPLEXIO_CFG_PWD_CMD_ON                        (1 << 27)
>> +#define CSI2_COMPLEXIO_CFG_PWD_CMD_ULP                       (2 << 27)
>> +#define CSI2_COMPLEXIO_CFG_PWD_STATUS_MASK           (3 << 25)
>> +#define CSI2_COMPLEXIO_CFG_PWD_STATUS_OFF            (0 << 25)
>> +#define CSI2_COMPLEXIO_CFG_PWD_STATUS_ON             (1 << 25)
>> +#define CSI2_COMPLEXIO_CFG_PWD_STATUS_ULP            (2 << 25)
>> +#define CSI2_COMPLEXIO_CFG_PWR_AUTO                  (1 << 24)
>> +#define CSI2_COMPLEXIO_CFG_DATA_POL(i)                       (1 << (((i) * 4) + 3))
>> +#define CSI2_COMPLEXIO_CFG_DATA_POSITION_MASK(i)     (7 << ((i) * 4))
>> +#define CSI2_COMPLEXIO_CFG_DATA_POSITION_SHIFT(i)    ((i) * 4)
>> +#define CSI2_COMPLEXIO_CFG_CLOCK_POL                 (1 << 3)
>> +#define CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK               (7 << 0)
>> +#define CSI2_COMPLEXIO_CFG_CLOCK_POSITION_SHIFT              0
>> +
>> +#define CSI2_COMPLEXIO_IRQSTATUS                     (0x54)
>> +
>> +#define CSI2_SHORT_PACKET                            (0x5C)
>> +
>> +#define CSI2_COMPLEXIO_IRQENABLE                     (0x60)
>> +
>> +/* Shared bits across CSI2_COMPLEXIO_IRQENABLE and IRQSTATUS */
>> +#define CSI2_COMPLEXIO_IRQ_STATEALLULPMEXIT          (1 << 26)
>> +#define CSI2_COMPLEXIO_IRQ_STATEALLULPMENTER         (1 << 25)
>> +#define CSI2_COMPLEXIO_IRQ_STATEULPM5                        (1 << 24)
>> +#define CSI2_COMPLEXIO_IRQ_STATEULPM4                        (1 << 23)
>> +#define CSI2_COMPLEXIO_IRQ_STATEULPM3                        (1 << 22)
>> +#define CSI2_COMPLEXIO_IRQ_STATEULPM2                        (1 << 21)
>> +#define CSI2_COMPLEXIO_IRQ_STATEULPM1                        (1 << 20)
>> +#define CSI2_COMPLEXIO_IRQ_ERRCONTROL5                       (1 << 19)
>> +#define CSI2_COMPLEXIO_IRQ_ERRCONTROL4                       (1 << 18)
>> +#define CSI2_COMPLEXIO_IRQ_ERRCONTROL3                       (1 << 17)
>> +#define CSI2_COMPLEXIO_IRQ_ERRCONTROL2                       (1 << 16)
>> +#define CSI2_COMPLEXIO_IRQ_ERRCONTROL1                       (1 << 15)
>> +#define CSI2_COMPLEXIO_IRQ_ERRESC5                   (1 << 14)
>> +#define CSI2_COMPLEXIO_IRQ_ERRESC4                   (1 << 13)
>> +#define CSI2_COMPLEXIO_IRQ_ERRESC3                   (1 << 12)
>> +#define CSI2_COMPLEXIO_IRQ_ERRESC2                   (1 << 11)
>> +#define CSI2_COMPLEXIO_IRQ_ERRESC1                   (1 << 10)
>> +#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS5             (1 << 9)
>> +#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS4             (1 << 8)
>> +#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS3             (1 << 7)
>> +#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS2             (1 << 6)
>> +#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS1             (1 << 5)
>> +#define CSI2_COMPLEXIO_IRQ_ERRSOTHS5                 (1 << 4)
>> +#define CSI2_COMPLEXIO_IRQ_ERRSOTHS4                 (1 << 3)
>> +#define CSI2_COMPLEXIO_IRQ_ERRSOTHS3                 (1 << 2)
>> +#define CSI2_COMPLEXIO_IRQ_ERRSOTHS2                 (1 << 1)
>> +#define CSI2_COMPLEXIO_IRQ_ERRSOTHS1                 (1 << 0)
>> +
>> +#define CSI2_DBG_P                                   (0x68)
>> +
>> +#define CSI2_TIMING                                  (0x6C)
>> +#define CSI2_TIMING_FORCE_RX_MODE_IO1                        (1 << 15)
>> +#define CSI2_TIMING_STOP_STATE_X16_IO1                       (1 << 14)
>> +#define CSI2_TIMING_STOP_STATE_X4_IO1                        (1 << 13)
>> +#define CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK              (0x1FFF << 0)
>> +#define CSI2_TIMING_STOP_STATE_COUNTER_IO1_SHIFT     0
>> +
>> +#define CSI2_CTX_CTRL1(i)                            (0x70 + (0x20 * i))
>> +#define CSI2_CTX_CTRL1_GENERIC                               (1 << 30)
>> +#define CSI2_CTX_CTRL1_TRANSCODE                     (0xF << 24)
>> +#define CSI2_CTX_CTRL1_FEC_NUMBER_MASK                       (0xFF << 16)
>> +#define CSI2_CTX_CTRL1_COUNT_MASK                    (0xFF << 8)
>> +#define CSI2_CTX_CTRL1_COUNT_SHIFT                   8
>> +#define CSI2_CTX_CTRL1_EOF_EN                                (1 << 7)
>> +#define CSI2_CTX_CTRL1_EOL_EN                                (1 << 6)
>> +#define CSI2_CTX_CTRL1_CS_EN                         (1 << 5)
>> +#define CSI2_CTX_CTRL1_COUNT_UNLOCK                  (1 << 4)
>> +#define CSI2_CTX_CTRL1_PING_PONG                     (1 << 3)
>> +#define CSI2_CTX_CTRL1_CTX_EN                                (1 << 0)
>> +
>> +#define CSI2_CTX_CTRL2(i)                            (0x74 + (0x20 * i))
>> +#define CSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT            13
>> +#define CSI2_CTX_CTRL2_USER_DEF_MAP_MASK             \
>> +             (0x3 << CSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT)
>> +#define CSI2_CTX_CTRL2_VIRTUAL_ID_MASK                       (3 << 11)
>> +#define CSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT                      11
>> +#define CSI2_CTX_CTRL2_DPCM_PRED                     (1 << 10)
>> +#define CSI2_CTX_CTRL2_FORMAT_MASK                   (0x3FF << 0)
>> +#define CSI2_CTX_CTRL2_FORMAT_SHIFT                  0
>> +
>> +#define CSI2_CTX_DAT_OFST(i)                         (0x78 + (0x20 * i))
>> +#define CSI2_CTX_DAT_OFST_MASK                               (0xFFF << 5)
>> +
>> +#define CSI2_CTX_PING_ADDR(i)                                (0x7C + (0x20 * i))
>> +#define CSI2_CTX_PING_ADDR_MASK                              0xFFFFFFE0
>> +
>> +#define CSI2_CTX_PONG_ADDR(i)                                (0x80 + (0x20 * i))
>> +#define CSI2_CTX_PONG_ADDR_MASK                              CSI2_CTX_PING_ADDR_MASK
>> +
>> +#define CSI2_CTX_IRQENABLE(i)                                (0x84 + (0x20 * i))
>> +#define CSI2_CTX_IRQSTATUS(i)                                (0x88 + (0x20 * i))
>> +
>> +#define CSI2_CTX_CTRL3(i)                            (0x8C + (0x20 * i))
>> +#define CSI2_CTX_CTRL3_ALPHA_SHIFT                   5
>> +#define CSI2_CTX_CTRL3_ALPHA_MASK                    \
>> +             (0x3fff << CSI2_CTX_CTRL3_ALPHA_SHIFT)
>> +
>> +/* Shared bits across CSI2_CTX_IRQENABLE and IRQSTATUS */
>> +#define CSI2_CTX_IRQ_ECC_CORRECTION                  (1 << 8)
>> +#define CSI2_CTX_IRQ_LINE_NUMBER                     (1 << 7)
>> +#define CSI2_CTX_IRQ_FRAME_NUMBER                    (1 << 6)
>> +#define CSI2_CTX_IRQ_CS                                      (1 << 5)
>> +#define CSI2_CTX_IRQ_LE                                      (1 << 3)
>> +#define CSI2_CTX_IRQ_LS                                      (1 << 2)
>> +#define CSI2_CTX_IRQ_FE                                      (1 << 1)
>> +#define CSI2_CTX_IRQ_FS                                      (1 << 0)
>> +
>> +#endif /* _OMAP4_CAMERA_REGS_H_ */
>> diff --git a/drivers/media/video/omap4iss/iss_video.c b/drivers/media/video/omap4iss/iss_video.c
>> new file mode 100644
>> index 0000000..3248711
>> --- /dev/null
>> +++ b/drivers/media/video/omap4iss/iss_video.c
>> @@ -0,0 +1,1192 @@
>> +/*
>> + * iss_video.c
>> + *
>> + * TI OMAP4 ISS - Generic video node
>> + *
>> + * Copyright (C) 2011 Texas Instruments, Inc.
>> + *
>> + * Contacts: Sergio Aguirre <saaguirre@ti.com>
>> + *
>> + * 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.
>> + *
>> + * This program is distributed in the hope that it will be useful, but
>> + * WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write to the Free Software
>> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
>> + * 02110-1301 USA
>> + */
>> +
>> +#include <asm/cacheflush.h>
>> +#include <linux/clk.h>
>> +#include <linux/mm.h>
>> +#include <linux/pagemap.h>
>> +#include <linux/sched.h>
>> +#include <linux/slab.h>
>> +#include <linux/vmalloc.h>
>> +#include <linux/module.h>
>> +#include <media/v4l2-dev.h>
>> +#include <media/v4l2-ioctl.h>
>> +#include <plat/omap-pm.h>
>> +
>> +#include "iss_video.h"
>> +#include "iss.h"
>> +
>> +
>> +/* -----------------------------------------------------------------------------
>> + * Helper functions
>> + */
>> +
>> +static struct iss_format_info formats[] = {
>> +     { V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8,
>> +       V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8,
>> +       V4L2_PIX_FMT_GREY, 8, },
>> +     { V4L2_MBUS_FMT_Y10_1X10, V4L2_MBUS_FMT_Y10_1X10,
>> +       V4L2_MBUS_FMT_Y10_1X10, V4L2_MBUS_FMT_Y8_1X8,
>> +       V4L2_PIX_FMT_Y10, 10, },
>> +     { V4L2_MBUS_FMT_Y12_1X12, V4L2_MBUS_FMT_Y10_1X10,
>> +       V4L2_MBUS_FMT_Y12_1X12, V4L2_MBUS_FMT_Y8_1X8,
>> +       V4L2_PIX_FMT_Y12, 12, },
>> +     { V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_MBUS_FMT_SBGGR8_1X8,
>> +       V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_MBUS_FMT_SBGGR8_1X8,
>> +       V4L2_PIX_FMT_SBGGR8, 8, },
>> +     { V4L2_MBUS_FMT_SGBRG8_1X8, V4L2_MBUS_FMT_SGBRG8_1X8,
>> +       V4L2_MBUS_FMT_SGBRG8_1X8, V4L2_MBUS_FMT_SGBRG8_1X8,
>> +       V4L2_PIX_FMT_SGBRG8, 8, },
>> +     { V4L2_MBUS_FMT_SGRBG8_1X8, V4L2_MBUS_FMT_SGRBG8_1X8,
>> +       V4L2_MBUS_FMT_SGRBG8_1X8, V4L2_MBUS_FMT_SGRBG8_1X8,
>> +       V4L2_PIX_FMT_SGRBG8, 8, },
>> +     { V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8,
>> +       V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8,
>> +       V4L2_PIX_FMT_SRGGB8, 8, },
>> +     { V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8,
>> +       V4L2_MBUS_FMT_SGRBG10_1X10, 0,
>> +       V4L2_PIX_FMT_SGRBG10DPCM8, 8, },
>> +     { V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR10_1X10,
>> +       V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR8_1X8,
>> +       V4L2_PIX_FMT_SBGGR10, 10, },
>> +     { V4L2_MBUS_FMT_SGBRG10_1X10, V4L2_MBUS_FMT_SGBRG10_1X10,
>> +       V4L2_MBUS_FMT_SGBRG10_1X10, V4L2_MBUS_FMT_SGBRG8_1X8,
>> +       V4L2_PIX_FMT_SGBRG10, 10, },
>> +     { V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_MBUS_FMT_SGRBG10_1X10,
>> +       V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_MBUS_FMT_SGRBG8_1X8,
>> +       V4L2_PIX_FMT_SGRBG10, 10, },
>> +     { V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_MBUS_FMT_SRGGB10_1X10,
>> +       V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_MBUS_FMT_SRGGB8_1X8,
>> +       V4L2_PIX_FMT_SRGGB10, 10, },
>> +     { V4L2_MBUS_FMT_SBGGR12_1X12, V4L2_MBUS_FMT_SBGGR10_1X10,
>> +       V4L2_MBUS_FMT_SBGGR12_1X12, V4L2_MBUS_FMT_SBGGR8_1X8,
>> +       V4L2_PIX_FMT_SBGGR12, 12, },
>> +     { V4L2_MBUS_FMT_SGBRG12_1X12, V4L2_MBUS_FMT_SGBRG10_1X10,
>> +       V4L2_MBUS_FMT_SGBRG12_1X12, V4L2_MBUS_FMT_SGBRG8_1X8,
>> +       V4L2_PIX_FMT_SGBRG12, 12, },
>> +     { V4L2_MBUS_FMT_SGRBG12_1X12, V4L2_MBUS_FMT_SGRBG10_1X10,
>> +       V4L2_MBUS_FMT_SGRBG12_1X12, V4L2_MBUS_FMT_SGRBG8_1X8,
>> +       V4L2_PIX_FMT_SGRBG12, 12, },
>> +     { V4L2_MBUS_FMT_SRGGB12_1X12, V4L2_MBUS_FMT_SRGGB10_1X10,
>> +       V4L2_MBUS_FMT_SRGGB12_1X12, V4L2_MBUS_FMT_SRGGB8_1X8,
>> +       V4L2_PIX_FMT_SRGGB12, 12, },
>> +     { V4L2_MBUS_FMT_UYVY8_1X16, V4L2_MBUS_FMT_UYVY8_1X16,
>> +       V4L2_MBUS_FMT_UYVY8_1X16, 0,
>> +       V4L2_PIX_FMT_UYVY, 16, },
>> +     { V4L2_MBUS_FMT_YUYV8_1X16, V4L2_MBUS_FMT_YUYV8_1X16,
>> +       V4L2_MBUS_FMT_YUYV8_1X16, 0,
>> +       V4L2_PIX_FMT_YUYV, 16, },
>> +};
>> +
>> +const struct iss_format_info *
>> +omap4iss_video_format_info(enum v4l2_mbus_pixelcode code)
>> +{
>> +     unsigned int i;
>> +
>> +     for (i = 0; i < ARRAY_SIZE(formats); ++i) {
>> +             if (formats[i].code == code)
>> +                     return &formats[i];
>> +     }
>> +
>> +     return NULL;
>> +}
>> +
>> +/*
>> + * iss_video_mbus_to_pix - Convert v4l2_mbus_framefmt to v4l2_pix_format
>> + * @video: ISS video instance
>> + * @mbus: v4l2_mbus_framefmt format (input)
>> + * @pix: v4l2_pix_format format (output)
>> + *
>> + * Fill the output pix structure with information from the input mbus format.
>> + * The bytesperline and sizeimage fields are computed from the requested bytes
>> + * per line value in the pix format and information from the video instance.
>> + *
>> + * Return the number of padding bytes at end of line.
>> + */
>> +static unsigned int iss_video_mbus_to_pix(const struct iss_video *video,
>> +                                       const struct v4l2_mbus_framefmt *mbus,
>> +                                       struct v4l2_pix_format *pix)
>> +{
>> +     unsigned int bpl = pix->bytesperline;
>> +     unsigned int min_bpl;
>> +     unsigned int i;
>> +
>> +     memset(pix, 0, sizeof(*pix));
>> +     pix->width = mbus->width;
>> +     pix->height = mbus->height;
>> +
>> +     for (i = 0; i < ARRAY_SIZE(formats); ++i) {
>> +             if (formats[i].code == mbus->code)
>> +                     break;
>> +     }
>> +
>> +     if (WARN_ON(i == ARRAY_SIZE(formats)))
>> +             return 0;
>> +
>> +     min_bpl = pix->width * ALIGN(formats[i].bpp, 8) / 8;
>> +
>> +     /* Clamp the requested bytes per line value. If the maximum bytes per
>> +      * line value is zero, the module doesn't support user configurable line
>> +      * sizes. Override the requested value with the minimum in that case.
>> +      */
>> +     if (video->bpl_max)
>> +             bpl = clamp(bpl, min_bpl, video->bpl_max);
>> +     else
>> +             bpl = min_bpl;
>> +
>> +     if (!video->bpl_zero_padding || bpl != min_bpl)
>> +             bpl = ALIGN(bpl, video->bpl_alignment);
>> +
>> +     pix->pixelformat = formats[i].pixelformat;
>> +     pix->bytesperline = bpl;
>> +     pix->sizeimage = pix->bytesperline * pix->height;
>> +     pix->colorspace = mbus->colorspace;
>> +     pix->field = mbus->field;
>> +
>> +     return bpl - min_bpl;
>> +}
>> +
>> +static void iss_video_pix_to_mbus(const struct v4l2_pix_format *pix,
>> +                               struct v4l2_mbus_framefmt *mbus)
>> +{
>> +     unsigned int i;
>> +
>> +     memset(mbus, 0, sizeof(*mbus));
>> +     mbus->width = pix->width;
>> +     mbus->height = pix->height;
>> +
>> +     for (i = 0; i < ARRAY_SIZE(formats); ++i) {
>> +             if (formats[i].pixelformat == pix->pixelformat)
>> +                     break;
>> +     }
>> +
>> +     if (WARN_ON(i == ARRAY_SIZE(formats)))
>> +             return;
>> +
>> +     mbus->code = formats[i].code;
>> +     mbus->colorspace = pix->colorspace;
>> +     mbus->field = pix->field;
>> +}
>> +
>> +static struct v4l2_subdev *
>> +iss_video_remote_subdev(struct iss_video *video, u32 *pad)
>> +{
>> +     struct media_pad *remote;
>> +
>> +     remote = media_entity_remote_source(&video->pad);
>> +
>> +     if (remote == NULL ||
>> +         media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
>> +             return NULL;
>> +
>> +     if (pad)
>> +             *pad = remote->index;
>> +
>> +     return media_entity_to_v4l2_subdev(remote->entity);
>> +}
>> +
>> +/* Return a pointer to the ISS video instance at the far end of the pipeline. */
>> +static struct iss_video *
>> +iss_video_far_end(struct iss_video *video)
>> +{
>> +     struct media_entity_graph graph;
>> +     struct media_entity *entity = &video->video.entity;
>> +     struct media_device *mdev = entity->parent;
>> +     struct iss_video *far_end = NULL;
>> +
>> +     mutex_lock(&mdev->graph_mutex);
>> +     media_entity_graph_walk_start(&graph, entity);
>> +
>> +     while ((entity = media_entity_graph_walk_next(&graph))) {
>> +             if (entity == &video->video.entity)
>> +                     continue;
>> +
>> +             if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE)
>> +                     continue;
>> +
>> +             far_end = to_iss_video(media_entity_to_video_device(entity));
>> +             if (far_end->type != video->type)
>> +                     break;
>> +
>> +             far_end = NULL;
>> +     }
>> +
>> +     mutex_unlock(&mdev->graph_mutex);
>> +     return far_end;
>> +}
>> +
>> +/*
>> + * Validate a pipeline by checking both ends of all links for format
>> + * discrepancies.
>> + *
>> + * Compute the minimum time per frame value as the maximum of time per frame
>> + * limits reported by every block in the pipeline.
>> + *
>> + * Return 0 if all formats match, or -EPIPE if at least one link is found with
>> + * different formats on its two ends.
>> + */
>> +static int iss_video_validate_pipeline(struct iss_pipeline *pipe)
>> +{
>> +     struct v4l2_subdev_format fmt_source;
>> +     struct v4l2_subdev_format fmt_sink;
>> +     struct media_pad *pad;
>> +     struct v4l2_subdev *subdev;
>> +     int ret;
>> +
>> +     subdev = iss_video_remote_subdev(pipe->output, NULL);
>> +     if (subdev == NULL)
>> +             return -EPIPE;
>> +
>> +     while (1) {
>> +             /* Retrieve the sink format */
>> +             pad = &subdev->entity.pads[0];
>> +             if (!(pad->flags & MEDIA_PAD_FL_SINK))
>> +                     break;
>> +
>> +             fmt_sink.pad = pad->index;
>> +             fmt_sink.which = V4L2_SUBDEV_FORMAT_ACTIVE;
>> +             ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt_sink);
>> +             if (ret < 0 && ret != -ENOIOCTLCMD)
>> +                     return -EPIPE;
>> +
>> +             /* Retrieve the source format */
>> +             pad = media_entity_remote_source(pad);
>> +             if (pad == NULL ||
>> +                 media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
>> +                     break;
>> +
>> +             subdev = media_entity_to_v4l2_subdev(pad->entity);
>> +
>> +             fmt_source.pad = pad->index;
>> +             fmt_source.which = V4L2_SUBDEV_FORMAT_ACTIVE;
>> +             ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt_source);
>> +             if (ret < 0 && ret != -ENOIOCTLCMD)
>> +                     return -EPIPE;
>> +
>> +             /* Check if the two ends match */
>> +             if (fmt_source.format.width != fmt_sink.format.width ||
>> +                 fmt_source.format.height != fmt_sink.format.height)
>> +                     return -EPIPE;
>> +
>> +             if (fmt_source.format.code != fmt_sink.format.code)
>> +                     return -EPIPE;
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +static int
>> +__iss_video_get_format(struct iss_video *video, struct v4l2_format *format)
>> +{
>> +     struct v4l2_subdev_format fmt;
>> +     struct v4l2_subdev *subdev;
>> +     u32 pad;
>> +     int ret;
>> +
>> +     subdev = iss_video_remote_subdev(video, &pad);
>> +     if (subdev == NULL)
>> +             return -EINVAL;
>> +
>> +     mutex_lock(&video->mutex);
>> +
>> +     fmt.pad = pad;
>> +     fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
>> +     ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt);
>> +     if (ret == -ENOIOCTLCMD)
>> +             ret = -EINVAL;
>> +
>> +     mutex_unlock(&video->mutex);
>> +
>> +     if (ret)
>> +             return ret;
>> +
>> +     format->type = video->type;
>> +     return iss_video_mbus_to_pix(video, &fmt.format, &format->fmt.pix);
>> +}
>> +
>> +static int
>> +iss_video_check_format(struct iss_video *video, struct iss_video_fh *vfh)
>> +{
>> +     struct v4l2_format format;
>> +     int ret;
>> +
>> +     memcpy(&format, &vfh->format, sizeof(format));
>> +     ret = __iss_video_get_format(video, &format);
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     if (vfh->format.fmt.pix.pixelformat != format.fmt.pix.pixelformat ||
>> +         vfh->format.fmt.pix.height != format.fmt.pix.height ||
>> +         vfh->format.fmt.pix.width != format.fmt.pix.width ||
>> +         vfh->format.fmt.pix.bytesperline != format.fmt.pix.bytesperline ||
>> +         vfh->format.fmt.pix.sizeimage != format.fmt.pix.sizeimage)
>> +             return -EINVAL;
>> +
>> +     return ret;
>> +}
>> +
>> +/* -----------------------------------------------------------------------------
>> + * Video queue operations
>> + */
>> +
>> +static int iss_video_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
>> +                              unsigned int *count, unsigned int *num_planes,
>> +                              unsigned int sizes[], void *alloc_ctxs[])
>> +{
>> +     struct iss_video_fh *vfh = vb2_get_drv_priv(vq);
>> +     struct iss_video *video = vfh->video;
>> +
>> +     /* Revisit multi-planar support for NV12 */
>> +     *num_planes = 1;
>> +
>> +     sizes[0] = vfh->format.fmt.pix.sizeimage;
>> +     if (sizes[0] == 0)
>> +             return -EINVAL;
>> +
>> +     alloc_ctxs[0] = video->alloc_ctx;
>> +
>> +     *count = min(*count, (unsigned int)(video->capture_mem / PAGE_ALIGN(sizes[0])));
>> +
>> +     return 0;
>> +}
>> +
>> +static void iss_video_buf_cleanup(struct vb2_buffer *vb)
>> +{
>> +     struct iss_buffer *buffer = container_of(vb, struct iss_buffer, vb);
>> +
>> +     if (buffer->iss_addr)
>> +             buffer->iss_addr = 0;
>> +}
>> +
>> +static int iss_video_buf_prepare(struct vb2_buffer *vb)
>> +{
>> +     struct iss_video_fh *vfh = vb2_get_drv_priv(vb->vb2_queue);
>> +     struct iss_buffer *buffer = container_of(vb, struct iss_buffer, vb);
>> +     struct iss_video *video = vfh->video;
>> +     unsigned long size = vfh->format.fmt.pix.sizeimage;
>> +     dma_addr_t addr;
>> +
>> +     if (vb2_plane_size(vb, 0) < size)
>> +             return -ENOBUFS;
>> +
>> +     addr = vb2_dma_contig_plane_dma_addr(vb, 0);
>> +     if (!IS_ALIGNED(addr, 32)) {
>> +             dev_dbg(video->iss->dev, "Buffer address must be "
>> +                     "aligned to 32 bytes boundary.\n");
>> +             return -EINVAL;
>> +     }
>> +
>> +     vb2_set_plane_payload(vb, 0, size);
>> +     buffer->iss_addr = addr;
>> +     return 0;
>> +}
>> +
>> +static void iss_video_buf_queue(struct vb2_buffer *vb)
>> +{
>> +     struct iss_video_fh *vfh = vb2_get_drv_priv(vb->vb2_queue);
>> +     struct iss_video *video = vfh->video;
>> +     struct iss_buffer *buffer = container_of(vb, struct iss_buffer, vb);
>> +     struct iss_pipeline *pipe = to_iss_pipeline(&video->video.entity);
>> +     unsigned int empty;
>> +     unsigned long flags;
>> +
>> +     spin_lock_irqsave(&video->qlock, flags);
>> +     empty = list_empty(&video->dmaqueue);
>> +     list_add_tail(&buffer->list, &video->dmaqueue);
>> +     spin_unlock_irqrestore(&video->qlock, flags);
>> +
>> +     if (empty) {
>> +             enum iss_pipeline_state state;
>> +             unsigned int start;
>> +
>> +             if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
>> +                     state = ISS_PIPELINE_QUEUE_OUTPUT;
>> +             else
>> +                     state = ISS_PIPELINE_QUEUE_INPUT;
>> +
>> +             spin_lock_irqsave(&pipe->lock, flags);
>> +             pipe->state |= state;
>> +             video->ops->queue(video, buffer);
>> +             video->dmaqueue_flags |= ISS_VIDEO_DMAQUEUE_QUEUED;
>> +
>> +             start = iss_pipeline_ready(pipe);
>> +             if (start)
>> +                     pipe->state |= ISS_PIPELINE_STREAM;
>> +             spin_unlock_irqrestore(&pipe->lock, flags);
>> +
>> +             if (start)
>> +                     omap4iss_pipeline_set_stream(pipe,
>> +                                             ISS_PIPELINE_STREAM_SINGLESHOT);
>> +     }
>> +}
>> +
>> +static struct vb2_ops iss_video_vb2ops = {
>> +     .queue_setup    = iss_video_queue_setup,
>> +     .buf_prepare    = iss_video_buf_prepare,
>> +     .buf_queue      = iss_video_buf_queue,
>> +     .buf_cleanup    = iss_video_buf_cleanup,
>> +};
>> +
>> +/*
>> + * omap4iss_video_buffer_next - Complete the current buffer and return the next
>> + * @video: ISS video object
>> + * @error: Whether an error occurred during capture
>> + *
>> + * Remove the current video buffer from the DMA queue and fill its timestamp,
>> + * field count and state fields before waking up its completion handler.
>> + *
>> + * The buffer state is set to VIDEOBUF_DONE if no error occurred (@error is 0)
>> + * or VIDEOBUF_ERROR otherwise (@error is non-zero).
>> + *
>> + * The DMA queue is expected to contain at least one buffer.
>> + *
>> + * Return a pointer to the next buffer in the DMA queue, or NULL if the queue is
>> + * empty.
>> + */
>> +struct iss_buffer *omap4iss_video_buffer_next(struct iss_video *video,
>> +                                           unsigned int error)
>> +{
>> +     struct iss_pipeline *pipe = to_iss_pipeline(&video->video.entity);
>> +     enum iss_pipeline_state state;
>> +     struct iss_buffer *buf;
>> +     unsigned long flags;
>> +     struct timespec ts;
>> +
>> +     spin_lock_irqsave(&video->qlock, flags);
>> +     if (WARN_ON(list_empty(&video->dmaqueue))) {
>> +             spin_unlock_irqrestore(&video->qlock, flags);
>> +             return NULL;
>> +     }
>> +
>> +     buf = list_first_entry(&video->dmaqueue, struct iss_buffer,
>> +                            list);
>> +     list_del(&buf->list);
>> +     spin_unlock_irqrestore(&video->qlock, flags);
>> +
>> +     ktime_get_ts(&ts);
>> +     buf->vb.v4l2_buf.timestamp.tv_sec = ts.tv_sec;
>> +     buf->vb.v4l2_buf.timestamp.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
>> +
>> +     /* Do frame number propagation only if this is the output video node.
>> +      * Frame number either comes from the CSI receivers or it gets
>> +      * incremented here if H3A is not active.
>> +      * Note: There is no guarantee that the output buffer will finish
>> +      * first, so the input number might lag behind by 1 in some cases.
>> +      */
>> +     if (video == pipe->output && !pipe->do_propagation)
>> +             buf->vb.v4l2_buf.sequence = atomic_inc_return(&pipe->frame_number);
>> +     else
>> +             buf->vb.v4l2_buf.sequence = atomic_read(&pipe->frame_number);
>> +
>> +     vb2_buffer_done(&buf->vb, error < 0 ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
>> +
>> +     spin_lock_irqsave(&video->qlock, flags);
>> +     if (list_empty(&video->dmaqueue)) {
>> +             spin_unlock_irqrestore(&video->qlock, flags);
>> +             if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
>> +                     state = ISS_PIPELINE_QUEUE_OUTPUT
>> +                           | ISS_PIPELINE_STREAM;
>> +             else
>> +                     state = ISS_PIPELINE_QUEUE_INPUT
>> +                           | ISS_PIPELINE_STREAM;
>> +
>> +             spin_lock_irqsave(&pipe->lock, flags);
>> +             pipe->state &= ~state;
>> +             if (video->pipe.stream_state == ISS_PIPELINE_STREAM_CONTINUOUS)
>> +                     video->dmaqueue_flags |= ISS_VIDEO_DMAQUEUE_UNDERRUN;
>> +             spin_unlock_irqrestore(&pipe->lock, flags);
>> +             return NULL;
>> +     }
>> +
>> +     if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->input != NULL) {
>> +             spin_lock_irqsave(&pipe->lock, flags);
>> +             pipe->state &= ~ISS_PIPELINE_STREAM;
>> +             spin_unlock_irqrestore(&pipe->lock, flags);
>> +     }
>> +
>> +     buf = list_first_entry(&video->dmaqueue, struct iss_buffer,
>> +                            list);
>> +     spin_unlock_irqrestore(&video->qlock, flags);
>> +     buf->vb.state = VB2_BUF_STATE_ACTIVE;
>> +     return buf;
>> +}
>> +
>> +/* -----------------------------------------------------------------------------
>> + * V4L2 ioctls
>> + */
>> +
>> +static int
>> +iss_video_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
>> +{
>> +     struct iss_video *video = video_drvdata(file);
>> +
>> +     strlcpy(cap->driver, ISS_VIDEO_DRIVER_NAME, sizeof(cap->driver));
>> +     strlcpy(cap->card, video->video.name, sizeof(cap->card));
>> +     strlcpy(cap->bus_info, "media", sizeof(cap->bus_info));
>> +     cap->version = ISS_VIDEO_DRIVER_VERSION;
>> +
>> +     if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
>> +             cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
>> +     else
>> +             cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
>> +
>> +     return 0;
>> +}
>> +
>> +static int
>> +iss_video_get_format(struct file *file, void *fh, struct v4l2_format *format)
>> +{
>> +     struct iss_video_fh *vfh = to_iss_video_fh(fh);
>> +     struct iss_video *video = video_drvdata(file);
>> +
>> +     if (format->type != video->type)
>> +             return -EINVAL;
>> +
>> +     mutex_lock(&video->mutex);
>> +     *format = vfh->format;
>> +     mutex_unlock(&video->mutex);
>> +
>> +     return 0;
>> +}
>> +
>> +static int
>> +iss_video_set_format(struct file *file, void *fh, struct v4l2_format *format)
>> +{
>> +     struct iss_video_fh *vfh = to_iss_video_fh(fh);
>> +     struct iss_video *video = video_drvdata(file);
>> +     struct v4l2_mbus_framefmt fmt;
>> +
>> +     if (format->type != video->type)
>> +             return -EINVAL;
>> +
>> +     mutex_lock(&video->mutex);
>> +
>> +     /* Fill the bytesperline and sizeimage fields by converting to media bus
>> +      * format and back to pixel format.
>> +      */
>> +     iss_video_pix_to_mbus(&format->fmt.pix, &fmt);
>> +     iss_video_mbus_to_pix(video, &fmt, &format->fmt.pix);
>> +
>> +     vfh->format = *format;
>> +
>> +     mutex_unlock(&video->mutex);
>> +     return 0;
>> +}
>> +
>> +static int
>> +iss_video_try_format(struct file *file, void *fh, struct v4l2_format *format)
>> +{
>> +     struct iss_video *video = video_drvdata(file);
>> +     struct v4l2_subdev_format fmt;
>> +     struct v4l2_subdev *subdev;
>> +     u32 pad;
>> +     int ret;
>> +
>> +     if (format->type != video->type)
>> +             return -EINVAL;
>> +
>> +     subdev = iss_video_remote_subdev(video, &pad);
>> +     if (subdev == NULL)
>> +             return -EINVAL;
>> +
>> +     iss_video_pix_to_mbus(&format->fmt.pix, &fmt.format);
>> +
>> +     fmt.pad = pad;
>> +     fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
>> +     ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt);
>> +     if (ret)
>> +             return ret == -ENOIOCTLCMD ? -EINVAL : ret;
>> +
>> +     iss_video_mbus_to_pix(video, &fmt.format, &format->fmt.pix);
>> +     return 0;
>> +}
>> +
>> +static int
>> +iss_video_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cropcap)
>> +{
>> +     struct iss_video *video = video_drvdata(file);
>> +     struct v4l2_subdev *subdev;
>> +     int ret;
>> +
>> +     subdev = iss_video_remote_subdev(video, NULL);
>> +     if (subdev == NULL)
>> +             return -EINVAL;
>> +
>> +     mutex_lock(&video->mutex);
>> +     ret = v4l2_subdev_call(subdev, video, cropcap, cropcap);
>> +     mutex_unlock(&video->mutex);
>> +
>> +     return ret == -ENOIOCTLCMD ? -EINVAL : ret;
>> +}
>> +
>> +static int
>> +iss_video_get_crop(struct file *file, void *fh, struct v4l2_crop *crop)
>> +{
>> +     struct iss_video *video = video_drvdata(file);
>> +     struct v4l2_subdev_format format;
>> +     struct v4l2_subdev *subdev;
>> +     u32 pad;
>> +     int ret;
>> +
>> +     subdev = iss_video_remote_subdev(video, &pad);
>> +     if (subdev == NULL)
>> +             return -EINVAL;
>> +
>> +     /* Try the get crop operation first and fallback to get format if not
>> +      * implemented.
>> +      */
>> +     ret = v4l2_subdev_call(subdev, video, g_crop, crop);
>> +     if (ret != -ENOIOCTLCMD)
>> +             return ret;
>> +
>> +     format.pad = pad;
>> +     format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
>> +     ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &format);
>> +     if (ret < 0)
>> +             return ret == -ENOIOCTLCMD ? -EINVAL : ret;
>> +
>> +     crop->c.left = 0;
>> +     crop->c.top = 0;
>> +     crop->c.width = format.format.width;
>> +     crop->c.height = format.format.height;
>> +
>> +     return 0;
>> +}
>> +
>> +static int
>> +iss_video_set_crop(struct file *file, void *fh, struct v4l2_crop *crop)
>> +{
>> +     struct iss_video *video = video_drvdata(file);
>> +     struct v4l2_subdev *subdev;
>> +     int ret;
>> +
>> +     subdev = iss_video_remote_subdev(video, NULL);
>> +     if (subdev == NULL)
>> +             return -EINVAL;
>> +
>> +     mutex_lock(&video->mutex);
>> +     ret = v4l2_subdev_call(subdev, video, s_crop, crop);
>> +     mutex_unlock(&video->mutex);
>> +
>> +     return ret == -ENOIOCTLCMD ? -EINVAL : ret;
>> +}
>> +
>> +static int
>> +iss_video_get_param(struct file *file, void *fh, struct v4l2_streamparm *a)
>> +{
>> +     struct iss_video_fh *vfh = to_iss_video_fh(fh);
>> +     struct iss_video *video = video_drvdata(file);
>> +
>> +     if (video->type != V4L2_BUF_TYPE_VIDEO_OUTPUT ||
>> +         video->type != a->type)
>> +             return -EINVAL;
>> +
>> +     memset(a, 0, sizeof(*a));
>> +     a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
>> +     a->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
>> +     a->parm.output.timeperframe = vfh->timeperframe;
>> +
>> +     return 0;
>> +}
>> +
>> +static int
>> +iss_video_set_param(struct file *file, void *fh, struct v4l2_streamparm *a)
>> +{
>> +     struct iss_video_fh *vfh = to_iss_video_fh(fh);
>> +     struct iss_video *video = video_drvdata(file);
>> +
>> +     if (video->type != V4L2_BUF_TYPE_VIDEO_OUTPUT ||
>> +         video->type != a->type)
>> +             return -EINVAL;
>> +
>> +     if (a->parm.output.timeperframe.denominator == 0)
>> +             a->parm.output.timeperframe.denominator = 1;
>> +
>> +     vfh->timeperframe = a->parm.output.timeperframe;
>> +
>> +     return 0;
>> +}
>> +
>> +static int
>> +iss_video_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *rb)
>> +{
>> +     struct iss_video_fh *vfh = to_iss_video_fh(fh);
>> +
>> +     return vb2_reqbufs(&vfh->queue, rb);
>> +}
>> +
>> +static int
>> +iss_video_querybuf(struct file *file, void *fh, struct v4l2_buffer *b)
>> +{
>> +     struct iss_video_fh *vfh = to_iss_video_fh(fh);
>> +
>> +     return vb2_querybuf(&vfh->queue, b);
>> +}
>> +
>> +static int
>> +iss_video_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
>> +{
>> +     struct iss_video_fh *vfh = to_iss_video_fh(fh);
>> +
>> +     return vb2_qbuf(&vfh->queue, b);
>> +}
>> +
>> +static int
>> +iss_video_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
>> +{
>> +     struct iss_video_fh *vfh = to_iss_video_fh(fh);
>> +
>> +     return vb2_dqbuf(&vfh->queue, b, file->f_flags & O_NONBLOCK);
>> +}
>> +
>> +/*
>> + * Stream management
>> + *
>> + * Every ISS pipeline has a single input and a single output. The input can be
>> + * either a sensor or a video node. The output is always a video node.
>> + *
>> + * As every pipeline has an output video node, the ISS video objects at the
>> + * pipeline output stores the pipeline state. It tracks the streaming state of
>> + * both the input and output, as well as the availability of buffers.
>> + *
>> + * In sensor-to-memory mode, frames are always available at the pipeline input.
>> + * Starting the sensor usually requires I2C transfers and must be done in
>> + * interruptible context. The pipeline is started and stopped synchronously
>> + * to the stream on/off commands. All modules in the pipeline will get their
>> + * subdev set stream handler called. The module at the end of the pipeline must
>> + * delay starting the hardware until buffers are available at its output.
>> + *
>> + * In memory-to-memory mode, starting/stopping the stream requires
>> + * synchronization between the input and output. ISS modules can't be stopped
>> + * in the middle of a frame, and at least some of the modules seem to become
>> + * busy as soon as they're started, even if they don't receive a frame start
>> + * event. For that reason frames need to be processed in single-shot mode. The
>> + * driver needs to wait until a frame is completely processed and written to
>> + * memory before restarting the pipeline for the next frame. Pipelined
>> + * processing might be possible but requires more testing.
>> + *
>> + * Stream start must be delayed until buffers are available at both the input
>> + * and output. The pipeline must be started in the videobuf queue callback with
>> + * the buffers queue spinlock held. The modules subdev set stream operation must
>> + * not sleep.
>> + */
>> +static int
>> +iss_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
>> +{
>> +     struct iss_video_fh *vfh = to_iss_video_fh(fh);
>> +     struct iss_video *video = video_drvdata(file);
>> +     enum iss_pipeline_state state;
>> +     struct iss_pipeline *pipe;
>> +     struct iss_video *far_end;
>> +     unsigned long flags;
>> +     int ret;
>> +
>> +     if (type != video->type)
>> +             return -EINVAL;
>> +
>> +     mutex_lock(&video->stream_lock);
>> +
>> +     if (video->streaming) {
>> +             mutex_unlock(&video->stream_lock);
>> +             return -EBUSY;
>> +     }
>> +
>> +     /* Start streaming on the pipeline. No link touching an entity in the
>> +      * pipeline can be activated or deactivated once streaming is started.
>> +      */
>> +     pipe = video->video.entity.pipe
>> +          ? to_iss_pipeline(&video->video.entity) : &video->pipe;
>> +     media_entity_pipeline_start(&video->video.entity, &pipe->pipe);
>> +
>> +     /* Verify that the currently configured format matches the output of
>> +      * the connected subdev.
>> +      */
>> +     ret = iss_video_check_format(video, vfh);
>> +     if (ret < 0)
>> +             goto error;
>> +
>> +     video->bpl_padding = ret;
>> +     video->bpl_value = vfh->format.fmt.pix.bytesperline;
>> +
>> +     /* Find the ISS video node connected at the far end of the pipeline and
>> +      * update the pipeline.
>> +      */
>> +     far_end = iss_video_far_end(video);
>> +
>> +     if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
>> +             state = ISS_PIPELINE_STREAM_OUTPUT | ISS_PIPELINE_IDLE_OUTPUT;
>> +             pipe->input = far_end;
>> +             pipe->output = video;
>> +     } else {
>> +             if (far_end == NULL) {
>> +                     ret = -EPIPE;
>> +                     goto error;
>> +             }
>> +
>> +             state = ISS_PIPELINE_STREAM_INPUT | ISS_PIPELINE_IDLE_INPUT;
>> +             pipe->input = video;
>> +             pipe->output = far_end;
>> +     }
>> +
>> +     if (video->iss->pdata->set_constraints)
>> +             video->iss->pdata->set_constraints(video->iss, true);
>> +
>> +     /* Validate the pipeline and update its state. */
>> +     ret = iss_video_validate_pipeline(pipe);
>> +     if (ret < 0)
>> +             goto error;
>> +
>> +     spin_lock_irqsave(&pipe->lock, flags);
>> +     pipe->state &= ~ISS_PIPELINE_STREAM;
>> +     pipe->state |= state;
>> +     spin_unlock_irqrestore(&pipe->lock, flags);
>> +
>> +     /* Set the maximum time per frame as the value requested by userspace.
>> +      * This is a soft limit that can be overridden if the hardware doesn't
>> +      * support the request limit.
>> +      */
>> +     if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
>> +             pipe->max_timeperframe = vfh->timeperframe;
>> +
>> +     video->queue = &vfh->queue;
>> +     INIT_LIST_HEAD(&video->dmaqueue);
>> +     spin_lock_init(&video->qlock);
>> +     atomic_set(&pipe->frame_number, -1);
>> +
>> +     ret = vb2_streamon(&vfh->queue, type);
>> +     if (ret < 0)
>> +             goto error;
>> +
>> +     /* In sensor-to-memory mode, the stream can be started synchronously
>> +      * to the stream on command. In memory-to-memory mode, it will be
>> +      * started when buffers are queued on both the input and output.
>> +      */
>> +     if (pipe->input == NULL) {
>> +             unsigned long flags;
>> +             ret = omap4iss_pipeline_set_stream(pipe,
>> +                                           ISS_PIPELINE_STREAM_CONTINUOUS);
>> +             if (ret < 0)
>> +                     goto error;
>> +             spin_lock_irqsave(&video->qlock, flags);
>> +             if (list_empty(&video->dmaqueue))
>> +                     video->dmaqueue_flags |= ISS_VIDEO_DMAQUEUE_UNDERRUN;
>> +             spin_unlock_irqrestore(&video->qlock, flags);
>> +     }
>> +
>> +error:
>> +     if (ret < 0) {
>> +             vb2_streamoff(&vfh->queue, type);
>> +             if (video->iss->pdata->set_constraints)
>> +                     video->iss->pdata->set_constraints(video->iss, false);
>> +             media_entity_pipeline_stop(&video->video.entity);
>> +             video->queue = NULL;
>> +     }
>> +
>> +     if (!ret)
>> +             video->streaming = 1;
>> +
>> +     mutex_unlock(&video->stream_lock);
>> +     return ret;
>> +}
>> +
>> +static int
>> +iss_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type)
>> +{
>> +     struct iss_video_fh *vfh = to_iss_video_fh(fh);
>> +     struct iss_video *video = video_drvdata(file);
>> +     struct iss_pipeline *pipe = to_iss_pipeline(&video->video.entity);
>> +     enum iss_pipeline_state state;
>> +     unsigned long flags;
>> +
>> +     if (type != video->type)
>> +             return -EINVAL;
>> +
>> +     mutex_lock(&video->stream_lock);
>> +
>> +     if (!vb2_is_streaming(&vfh->queue))
>> +             goto done;
>> +
>> +     /* Update the pipeline state. */
>> +     if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
>> +             state = ISS_PIPELINE_STREAM_OUTPUT
>> +                   | ISS_PIPELINE_QUEUE_OUTPUT;
>> +     else
>> +             state = ISS_PIPELINE_STREAM_INPUT
>> +                   | ISS_PIPELINE_QUEUE_INPUT;
>> +
>> +     spin_lock_irqsave(&pipe->lock, flags);
>> +     pipe->state &= ~state;
>> +     spin_unlock_irqrestore(&pipe->lock, flags);
>> +
>> +     /* Stop the stream. */
>> +     omap4iss_pipeline_set_stream(pipe, ISS_PIPELINE_STREAM_STOPPED);
>> +     vb2_streamoff(&vfh->queue, type);
>> +     video->queue = NULL;
>> +     video->streaming = 0;
>> +
>> +     if (video->iss->pdata->set_constraints)
>> +             video->iss->pdata->set_constraints(video->iss, false);
>> +     media_entity_pipeline_stop(&video->video.entity);
>> +
>> +done:
>> +     mutex_unlock(&video->stream_lock);
>> +     return 0;
>> +}
>> +
>> +static int
>> +iss_video_enum_input(struct file *file, void *fh, struct v4l2_input *input)
>> +{
>> +     if (input->index > 0)
>> +             return -EINVAL;
>> +
>> +     strlcpy(input->name, "camera", sizeof(input->name));
>> +     input->type = V4L2_INPUT_TYPE_CAMERA;
>> +
>> +     return 0;
>> +}
>> +
>> +static int
>> +iss_video_g_input(struct file *file, void *fh, unsigned int *input)
>> +{
>> +     *input = 0;
>> +
>> +     return 0;
>> +}
>> +
>> +static int
>> +iss_video_s_input(struct file *file, void *fh, unsigned int input)
>> +{
>> +     return input == 0 ? 0 : -EINVAL;
>> +}
>> +
>> +static const struct v4l2_ioctl_ops iss_video_ioctl_ops = {
>> +     .vidioc_querycap                = iss_video_querycap,
>> +     .vidioc_g_fmt_vid_cap           = iss_video_get_format,
>> +     .vidioc_s_fmt_vid_cap           = iss_video_set_format,
>> +     .vidioc_try_fmt_vid_cap         = iss_video_try_format,
>> +     .vidioc_g_fmt_vid_out           = iss_video_get_format,
>> +     .vidioc_s_fmt_vid_out           = iss_video_set_format,
>> +     .vidioc_try_fmt_vid_out         = iss_video_try_format,
>> +     .vidioc_cropcap                 = iss_video_cropcap,
>> +     .vidioc_g_crop                  = iss_video_get_crop,
>> +     .vidioc_s_crop                  = iss_video_set_crop,
>> +     .vidioc_g_parm                  = iss_video_get_param,
>> +     .vidioc_s_parm                  = iss_video_set_param,
>> +     .vidioc_reqbufs                 = iss_video_reqbufs,
>> +     .vidioc_querybuf                = iss_video_querybuf,
>> +     .vidioc_qbuf                    = iss_video_qbuf,
>> +     .vidioc_dqbuf                   = iss_video_dqbuf,
>> +     .vidioc_streamon                = iss_video_streamon,
>> +     .vidioc_streamoff               = iss_video_streamoff,
>> +     .vidioc_enum_input              = iss_video_enum_input,
>> +     .vidioc_g_input                 = iss_video_g_input,
>> +     .vidioc_s_input                 = iss_video_s_input,
>> +};
>> +
>> +/* -----------------------------------------------------------------------------
>> + * V4L2 file operations
>> + */
>> +
>> +static int iss_video_open(struct file *file)
>> +{
>> +     struct iss_video *video = video_drvdata(file);
>> +     struct iss_video_fh *handle;
>> +     struct vb2_queue *q;
>> +     int ret = 0;
>> +
>> +     handle = kzalloc(sizeof(*handle), GFP_KERNEL);
>> +     if (handle == NULL)
>> +             return -ENOMEM;
>> +
>> +     v4l2_fh_init(&handle->vfh, &video->video);
>> +     v4l2_fh_add(&handle->vfh);
>> +
>> +     /* If this is the first user, initialise the pipeline. */
>> +     if (omap4iss_get(video->iss) == NULL) {
>> +             ret = -EBUSY;
>> +             goto done;
>> +     }
>> +
>> +     ret = omap4iss_pipeline_pm_use(&video->video.entity, 1);
>> +     if (ret < 0) {
>> +             omap4iss_put(video->iss);
>> +             goto done;
>> +     }
>> +
>> +     video->alloc_ctx = vb2_dma_contig_init_ctx(video->iss->dev);
>> +     if (IS_ERR(video->alloc_ctx)) {
>> +             ret = PTR_ERR(video->alloc_ctx);
>> +             omap4iss_put(video->iss);
>> +             goto done;
>> +     }
>> +
>> +     q = &handle->queue;
>> +
>> +     q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>> +     q->io_modes = VB2_MMAP;
>> +     q->drv_priv = handle;
>> +     q->ops = &iss_video_vb2ops;
>> +     q->mem_ops = &vb2_dma_contig_memops;
>> +     q->buf_struct_size = sizeof(struct iss_buffer);
>> +
>> +     ret = vb2_queue_init(q);
>> +     if (ret) {
>> +             omap4iss_put(video->iss);
>> +             goto done;
>> +     }
>> +
>> +     memset(&handle->format, 0, sizeof(handle->format));
>> +     handle->format.type = video->type;
>> +     handle->timeperframe.denominator = 1;
>> +
>> +     handle->video = video;
>> +     file->private_data = &handle->vfh;
>> +
>> +done:
>> +     if (ret < 0) {
>> +             v4l2_fh_del(&handle->vfh);
>> +             kfree(handle);
>> +     }
>> +
>> +     return ret;
>> +}
>> +
>> +static int iss_video_release(struct file *file)
>> +{
>> +     struct iss_video *video = video_drvdata(file);
>> +     struct v4l2_fh *vfh = file->private_data;
>> +     struct iss_video_fh *handle = to_iss_video_fh(vfh);
>> +
>> +     /* Disable streaming and free the buffers queue resources. */
>> +     iss_video_streamoff(file, vfh, video->type);
>> +
>> +     omap4iss_pipeline_pm_use(&video->video.entity, 0);
>> +
>> +     /* Release the file handle. */
>> +     v4l2_fh_del(vfh);
>> +     kfree(handle);
>> +     file->private_data = NULL;
>> +
>> +     omap4iss_put(video->iss);
>> +
>> +     return 0;
>> +}
>> +
>> +static unsigned int iss_video_poll(struct file *file, poll_table *wait)
>> +{
>> +     struct iss_video_fh *vfh = to_iss_video_fh(file->private_data);
>> +
>> +     return vb2_poll(&vfh->queue, file, wait);
>> +}
>> +
>> +static int iss_video_mmap(struct file *file, struct vm_area_struct *vma)
>> +{
>> +     struct iss_video_fh *vfh = to_iss_video_fh(file->private_data);
>> +
>> +     return vb2_mmap(&vfh->queue, vma);;
>> +}
>> +
>> +static struct v4l2_file_operations iss_video_fops = {
>> +     .owner = THIS_MODULE,
>> +     .unlocked_ioctl = video_ioctl2,
>> +     .open = iss_video_open,
>> +     .release = iss_video_release,
>> +     .poll = iss_video_poll,
>> +     .mmap = iss_video_mmap,
>> +};
>> +
>> +/* -----------------------------------------------------------------------------
>> + * ISS video core
>> + */
>> +
>> +static const struct iss_video_operations iss_video_dummy_ops = {
>> +};
>> +
>> +int omap4iss_video_init(struct iss_video *video, const char *name)
>> +{
>> +     const char *direction;
>> +     int ret;
>> +
>> +     switch (video->type) {
>> +     case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>> +             direction = "output";
>> +             video->pad.flags = MEDIA_PAD_FL_SINK;
>> +             break;
>> +     case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>> +             direction = "input";
>> +             video->pad.flags = MEDIA_PAD_FL_SOURCE;
>> +             break;
>> +
>> +     default:
>> +             return -EINVAL;
>> +     }
>> +
>> +     ret = media_entity_init(&video->video.entity, 1, &video->pad, 0);
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     mutex_init(&video->mutex);
>> +     atomic_set(&video->active, 0);
>> +
>> +     spin_lock_init(&video->pipe.lock);
>> +     mutex_init(&video->stream_lock);
>> +
>> +     /* Initialize the video device. */
>> +     if (video->ops == NULL)
>> +             video->ops = &iss_video_dummy_ops;
>> +
>> +     video->video.fops = &iss_video_fops;
>> +     snprintf(video->video.name, sizeof(video->video.name),
>> +              "OMAP4 ISS %s %s", name, direction);
>> +     video->video.vfl_type = VFL_TYPE_GRABBER;
>> +     video->video.release = video_device_release_empty;
>> +     video->video.ioctl_ops = &iss_video_ioctl_ops;
>> +     video->pipe.stream_state = ISS_PIPELINE_STREAM_STOPPED;
>> +
>> +     video_set_drvdata(&video->video, video);
>> +
>> +     return 0;
>> +}
>> +
>> +int omap4iss_video_register(struct iss_video *video, struct v4l2_device *vdev)
>> +{
>> +     int ret;
>> +
>> +     video->video.v4l2_dev = vdev;
>> +
>> +     ret = video_register_device(&video->video, VFL_TYPE_GRABBER, -1);
>> +     if (ret < 0)
>> +             printk(KERN_ERR "%s: could not register video device (%d)\n",
>> +                     __func__, ret);
>> +
>> +     return ret;
>> +}
>> +
>> +void omap4iss_video_unregister(struct iss_video *video)
>> +{
>> +     if (video_is_registered(&video->video)) {
>> +             media_entity_cleanup(&video->video.entity);
>> +             video_unregister_device(&video->video);
>> +     }
>> +}
>> diff --git a/drivers/media/video/omap4iss/iss_video.h b/drivers/media/video/omap4iss/iss_video.h
>> new file mode 100644
>> index 0000000..fc123b0
>> --- /dev/null
>> +++ b/drivers/media/video/omap4iss/iss_video.h
>> @@ -0,0 +1,205 @@
>> +/*
>> + * iss_video.h
>> + *
>> + * TI OMAP4 ISS - Generic video node
>> + *
>> + * Copyright (C) 2011 Texas Instruments, Inc.
>> + *
>> + * Contacts: Sergio Aguirre <saaguirre@ti.com>
>> + *
>> + * 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.
>> + *
>> + * This program is distributed in the hope that it will be useful, but
>> + * WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write to the Free Software
>> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
>> + * 02110-1301 USA
>> + */
>> +
>> +#ifndef OMAP4_ISS_VIDEO_H
>> +#define OMAP4_ISS_VIDEO_H
>> +
>> +#include <linux/v4l2-mediabus.h>
>> +#include <linux/version.h>
>> +#include <media/media-entity.h>
>> +#include <media/v4l2-dev.h>
>> +#include <media/v4l2-fh.h>
>> +#include <media/videobuf2-core.h>
>> +#include <media/videobuf2-dma-contig.h>
>> +
>> +#define ISS_VIDEO_DRIVER_NAME                "issvideo"
>> +#define ISS_VIDEO_DRIVER_VERSION     KERNEL_VERSION(0, 0, 1)
>> +
>> +struct iss_device;
>> +struct iss_video;
>> +struct v4l2_mbus_framefmt;
>> +struct v4l2_pix_format;
>> +
>> +/*
>> + * struct iss_format_info - ISS media bus format information
>> + * @code: V4L2 media bus format code
>> + * @truncated: V4L2 media bus format code for the same format truncated to 10
>> + *   bits. Identical to @code if the format is 10 bits wide or less.
>> + * @uncompressed: V4L2 media bus format code for the corresponding uncompressed
>> + *   format. Identical to @code if the format is not DPCM compressed.
>> + * @flavor: V4L2 media bus format code for the same pixel layout but
>> + *   shifted to be 8 bits per pixel. =0 if format is not shiftable.
>> + * @pixelformat: V4L2 pixel format FCC identifier
>> + * @bpp: Bits per pixel
>> + */
>> +struct iss_format_info {
>> +     enum v4l2_mbus_pixelcode code;
>> +     enum v4l2_mbus_pixelcode truncated;
>> +     enum v4l2_mbus_pixelcode uncompressed;
>> +     enum v4l2_mbus_pixelcode flavor;
>> +     u32 pixelformat;
>> +     unsigned int bpp;
>> +};
>> +
>> +enum iss_pipeline_stream_state {
>> +     ISS_PIPELINE_STREAM_STOPPED = 0,
>> +     ISS_PIPELINE_STREAM_CONTINUOUS = 1,
>> +     ISS_PIPELINE_STREAM_SINGLESHOT = 2,
>> +};
>> +
>> +enum iss_pipeline_state {
>> +     /* The stream has been started on the input video node. */
>> +     ISS_PIPELINE_STREAM_INPUT = 1,
>> +     /* The stream has been started on the output video node. */
>> +     ISS_PIPELINE_STREAM_OUTPUT = 2,
>> +     /* At least one buffer is queued on the input video node. */
>> +     ISS_PIPELINE_QUEUE_INPUT = 4,
>> +     /* At least one buffer is queued on the output video node. */
>> +     ISS_PIPELINE_QUEUE_OUTPUT = 8,
>> +     /* The input entity is idle, ready to be started. */
>> +     ISS_PIPELINE_IDLE_INPUT = 16,
>> +     /* The output entity is idle, ready to be started. */
>> +     ISS_PIPELINE_IDLE_OUTPUT = 32,
>> +     /* The pipeline is currently streaming. */
>> +     ISS_PIPELINE_STREAM = 64,
>> +};
>> +
>> +struct iss_pipeline {
>> +     struct media_pipeline pipe;
>> +     spinlock_t lock;                /* Pipeline state and queue flags */
>> +     unsigned int state;
>> +     enum iss_pipeline_stream_state stream_state;
>> +     struct iss_video *input;
>> +     struct iss_video *output;
>> +     atomic_t frame_number;
>> +     bool do_propagation; /* of frame number */
>> +     struct v4l2_fract max_timeperframe;
>> +};
>> +
>> +#define to_iss_pipeline(__e) \
>> +     container_of((__e)->pipe, struct iss_pipeline, pipe)
>> +
>> +static inline int iss_pipeline_ready(struct iss_pipeline *pipe)
>> +{
>> +     return pipe->state == (ISS_PIPELINE_STREAM_INPUT |
>> +                            ISS_PIPELINE_STREAM_OUTPUT |
>> +                            ISS_PIPELINE_QUEUE_INPUT |
>> +                            ISS_PIPELINE_QUEUE_OUTPUT |
>> +                            ISS_PIPELINE_IDLE_INPUT |
>> +                            ISS_PIPELINE_IDLE_OUTPUT);
>> +}
>> +
>> +/*
>> + * struct iss_buffer - ISS buffer
>> + * @buffer: ISS video buffer
>> + * @iss_addr: Physical address of the buffer.
>> + */
>> +struct iss_buffer {
>> +     /* common v4l buffer stuff -- must be first */
>> +     struct vb2_buffer       vb;
>> +     struct list_head        list;
>> +     dma_addr_t iss_addr;
>> +};
>> +
>> +#define to_iss_buffer(buf)   container_of(buf, struct iss_buffer, buffer)
>> +
>> +enum iss_video_dmaqueue_flags {
>> +     /* Set if DMA queue becomes empty when ISS_PIPELINE_STREAM_CONTINUOUS */
>> +     ISS_VIDEO_DMAQUEUE_UNDERRUN = (1 << 0),
>> +     /* Set when queuing buffer to an empty DMA queue */
>> +     ISS_VIDEO_DMAQUEUE_QUEUED = (1 << 1),
>> +};
>> +
>> +#define iss_video_dmaqueue_flags_clr(video)  \
>> +                     ({ (video)->dmaqueue_flags = 0; })
>> +
>> +/*
>> + * struct iss_video_operations - ISS video operations
>> + * @queue:   Resume streaming when a buffer is queued. Called on VIDIOC_QBUF
>> + *           if there was no buffer previously queued.
>> + */
>> +struct iss_video_operations {
>> +     int(*queue)(struct iss_video *video, struct iss_buffer *buffer);
>> +};
>> +
>> +struct iss_video {
>> +     struct video_device video;
>> +     enum v4l2_buf_type type;
>> +     struct media_pad pad;
>> +
>> +     struct mutex mutex;             /* format and crop settings */
>> +     atomic_t active;
>> +
>> +     struct iss_device *iss;
>> +
>> +     unsigned int capture_mem;
>> +     unsigned int bpl_alignment;     /* alignment value */
>> +     unsigned int bpl_zero_padding;  /* whether the alignment is optional */
>> +     unsigned int bpl_max;           /* maximum bytes per line value */
>> +     unsigned int bpl_value;         /* bytes per line value */
>> +     unsigned int bpl_padding;       /* padding at end of line */
>> +
>> +     /* Entity video node streaming */
>> +     unsigned int streaming:1;
>> +
>> +     /* Pipeline state */
>> +     struct iss_pipeline pipe;
>> +     struct mutex stream_lock;       /* pipeline and stream states */
>> +
>> +     /* Video buffers queue */
>> +     struct vb2_queue *queue;
>> +     spinlock_t qlock;       /* Spinlock for dmaqueue */
>> +     struct list_head dmaqueue;
>> +     enum iss_video_dmaqueue_flags dmaqueue_flags;
>> +     struct vb2_alloc_ctx *alloc_ctx;
>> +
>> +     const struct iss_video_operations *ops;
>> +};
>> +
>> +#define to_iss_video(vdev)   container_of(vdev, struct iss_video, video)
>> +
>> +struct iss_video_fh {
>> +     struct v4l2_fh vfh;
>> +     struct iss_video *video;
>> +     struct vb2_queue queue;
>> +     struct v4l2_format format;
>> +     struct v4l2_fract timeperframe;
>> +};
>> +
>> +#define to_iss_video_fh(fh)  container_of(fh, struct iss_video_fh, vfh)
>> +#define iss_video_queue_to_iss_video_fh(q) \
>> +                             container_of(q, struct iss_video_fh, queue)
>> +
>> +int omap4iss_video_init(struct iss_video *video, const char *name);
>> +int omap4iss_video_register(struct iss_video *video,
>> +                         struct v4l2_device *vdev);
>> +void omap4iss_video_unregister(struct iss_video *video);
>> +struct iss_buffer *omap4iss_video_buffer_next(struct iss_video *video,
>> +                                           unsigned int error);
>> +struct media_pad *omap4iss_video_remote_pad(struct iss_video *video);
>> +
>> +const struct iss_format_info *
>> +omap4iss_video_format_info(enum v4l2_mbus_pixelcode code);
>> +
>> +#endif /* OMAP4_ISS_VIDEO_H */
>> --
>> 1.7.7.4
>>
>
> --
> Sakari Ailus
> e-mail: sakari.ailus@iki.fi     jabber/XMPP/Gmail: sailus@retiisi.org.uk

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

* Re: [PATCH v2 06/11] v4l: Add support for omap4iss driver
  2012-01-14 17:51   ` Sakari Ailus
@ 2012-01-25  8:58     ` Aguirre, Sergio
  2012-01-26 21:05       ` Sakari Ailus
  0 siblings, 1 reply; 65+ messages in thread
From: Aguirre, Sergio @ 2012-01-25  8:58 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: linux-media, linux-omap, laurent.pinchart

Hi Sakari,

On Sat, Jan 14, 2012 at 11:51 AM, Sakari Ailus <sakari.ailus@iki.fi> wrote:
> Hi Sergio,
>
> Thanks again for the patch! My previous review escaped me too fast. The rest
> is here.

No problem. Thanks for reviewing such a long patch :)

>
> On Wed, Nov 30, 2011 at 06:14:55PM -0600, Sergio Aguirre wrote:
>> This adds a very simplistic driver to utilize the
>> CSI2A interface inside the ISS subsystem in OMAP4,
>> and dump the data to memory.
>>
>> Tested so far on omap4430sdp w/ ES2.1 GP & w/ ES2.2 EMU.
>>
>> Check newly added Documentation/video4linux/omap4_camera.txt
>> for details.
>>
>> Signed-off-by: Sergio Aguirre <saaguirre@ti.com>
>> ---
>>  Documentation/video4linux/omap4_camera.txt  |   60 ++
>>  arch/arm/plat-omap/include/plat/omap4-iss.h |   42 +
>>  drivers/media/video/Kconfig                 |   13 +
>>  drivers/media/video/Makefile                |    1 +
>>  drivers/media/video/omap4iss/Makefile       |    6 +
>>  drivers/media/video/omap4iss/iss.c          | 1179 ++++++++++++++++++++++++
>>  drivers/media/video/omap4iss/iss.h          |  133 +++
>>  drivers/media/video/omap4iss/iss_csi2.c     | 1324 +++++++++++++++++++++++++++
>>  drivers/media/video/omap4iss/iss_csi2.h     |  166 ++++
>>  drivers/media/video/omap4iss/iss_csiphy.c   |  215 +++++
>>  drivers/media/video/omap4iss/iss_csiphy.h   |   69 ++
>>  drivers/media/video/omap4iss/iss_regs.h     |  238 +++++
>>  drivers/media/video/omap4iss/iss_video.c    | 1192 ++++++++++++++++++++++++
>>  drivers/media/video/omap4iss/iss_video.h    |  205 +++++
>>  14 files changed, 4843 insertions(+), 0 deletions(-)
>>  create mode 100644 Documentation/video4linux/omap4_camera.txt
>>  create mode 100644 arch/arm/plat-omap/include/plat/omap4-iss.h
>>  create mode 100644 drivers/media/video/omap4iss/Makefile
>>  create mode 100644 drivers/media/video/omap4iss/iss.c
>>  create mode 100644 drivers/media/video/omap4iss/iss.h
>>  create mode 100644 drivers/media/video/omap4iss/iss_csi2.c
>>  create mode 100644 drivers/media/video/omap4iss/iss_csi2.h
>>  create mode 100644 drivers/media/video/omap4iss/iss_csiphy.c
>>  create mode 100644 drivers/media/video/omap4iss/iss_csiphy.h
>>  create mode 100644 drivers/media/video/omap4iss/iss_regs.h
>>  create mode 100644 drivers/media/video/omap4iss/iss_video.c
>>  create mode 100644 drivers/media/video/omap4iss/iss_video.h
>>
>> diff --git a/Documentation/video4linux/omap4_camera.txt b/Documentation/video4linux/omap4_camera.txt
>> new file mode 100644
>> index 0000000..a60c80f
>> --- /dev/null
>> +++ b/Documentation/video4linux/omap4_camera.txt
>> @@ -0,0 +1,60 @@
>> +                              OMAP4 ISS Driver
>> +                              ================
>> +
>> +Introduction
>> +------------
>> +
>> +The OMAP44XX family of chips contains the Imaging SubSystem (a.k.a. ISS),
>> +Which contains several components that can be categorized in 3 big groups:
>> +
>> +- Interfaces (2 Interfaces: CSI2-A & CSI2-B/CCP2)
>> +- ISP (Image Signal Processor)
>> +- SIMCOP (Still Image Coprocessor)
>> +
>> +For more information, please look in [1] for latest version of:
>> +     "OMAP4430 Multimedia Device Silicon Revision 2.x"
>> +
>> +As of Revision L, the ISS is described in detail in section 8.
>> +
>> +This driver is supporting _only_ the CSI2-A interface for now.
>> +
>> +It makes use of the Media Controller framework [2], and inherited most of the
>> +code from OMAP3 ISP driver (found under drivers/media/video/omap3isp/*), except
>> +that it doesn't need an IOMMU now for ISS buffers memory mapping.
>> +
>> +Supports usage of MMAP buffers only (for now).
>> +
>> +IMPORTANT: It is recommended to have this patchset:
>> +  Contiguous Memory Allocator (v15) [3].
>> +
>> +Tested platforms
>> +----------------
>> +
>> +- OMAP4430SDP, w/ ES2.1 GP & SEVM4430-CAM-V1-0 (Contains IMX060 & OV5640, in
>> +  which only the last one is supported, outputting YUV422 frames).
>> +
>> +- PandaBoard, Rev. A2, w/ OMAP4430 ES2.1 GP & OV adapter board, tested with
>> +  following sensors:
>> +  * OV5640
>> +  * OV5650
>> +
>> +- Tested on mainline kernel:
>> +
>> +     http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=summary
>> +
>> +  Tag: v3.0 (commit 02f8c6aee8df3cdc935e9bdd4f2d020306035dbe)
>> +
>> +File list
>> +---------
>> +drivers/media/video/omap4iss/
>> +
>> +References
>> +----------
>> +
>> +[1] http://focus.ti.com/general/docs/wtbu/wtbudocumentcenter.tsp?navigationId=12037&templateId=6123#62
>> +[2] http://lwn.net/Articles/420485/
>> +[3] http://lwn.net/Articles/455801/
>> +--
>> +Author: Sergio Aguirre <saaguirre@ti.com>
>> +Copyright (C) 2011, Texas Instruments
>> +
>> diff --git a/arch/arm/plat-omap/include/plat/omap4-iss.h b/arch/arm/plat-omap/include/plat/omap4-iss.h
>> new file mode 100644
>> index 0000000..3a1c6b6
>> --- /dev/null
>> +++ b/arch/arm/plat-omap/include/plat/omap4-iss.h
>> @@ -0,0 +1,42 @@
>> +#ifndef ARCH_ARM_PLAT_OMAP4_ISS_H
>> +#define ARCH_ARM_PLAT_OMAP4_ISS_H
>> +
>> +#include <linux/i2c.h>
>> +
>> +struct iss_device;
>> +
>> +enum iss_interface_type {
>> +     ISS_INTERFACE_CSI2A_PHY1,
>> +};
>> +
>> +/**
>> + * struct iss_csi2_platform_data - CSI2 interface platform data
>> + * @crc: Enable the cyclic redundancy check
>> + * @vpclk_div: Video port output clock control
>> + */
>> +struct iss_csi2_platform_data {
>> +     unsigned crc:1;
>> +     unsigned vpclk_div:2;
>> +};
>> +
>> +struct iss_subdev_i2c_board_info {
>> +     struct i2c_board_info *board_info;
>> +     int i2c_adapter_id;
>> +};
>> +
>> +struct iss_v4l2_subdevs_group {
>> +     struct iss_subdev_i2c_board_info *subdevs;
>> +     enum iss_interface_type interface;
>> +     union {
>> +             struct iss_csi2_platform_data csi2;
>> +     } bus; /* gcc < 4.6.0 chokes on anonymous union initializers */
>> +};
>> +
>> +struct iss_platform_data {
>> +     struct iss_v4l2_subdevs_group *subdevs;
>> +     void (*set_constraints)(struct iss_device *iss, bool enable);
>> +};
>> +
>> +extern int omap4_init_camera(struct iss_platform_data *pdata,
>> +                          struct omap_board_data *bdata);
>> +#endif
>> diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
>> index b303a3f..ae2a99d 100644
>> --- a/drivers/media/video/Kconfig
>> +++ b/drivers/media/video/Kconfig
>> @@ -796,6 +796,19 @@ config VIDEO_OMAP3_DEBUG
>>       ---help---
>>         Enable debug messages on OMAP 3 camera controller driver.
>>
>> +config VIDEO_OMAP4
>> +     tristate "OMAP 4 Camera support (EXPERIMENTAL)"
>> +     depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP4 && EXPERIMENTAL
>> +     select VIDEOBUF2_DMA_CONTIG
>> +     ---help---
>> +       Driver for an OMAP 4 ISS controller.
>> +
>> +config VIDEO_OMAP4_DEBUG
>> +     bool "OMAP 4 Camera debug messages"
>> +     depends on VIDEO_OMAP4
>> +     ---help---
>> +       Enable debug messages on OMAP 4 ISS controller driver.
>> +
>>  config SOC_CAMERA
>>       tristate "SoC camera support"
>>       depends on VIDEO_V4L2 && HAS_DMA && I2C
>> diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
>> index 117f9c4..f02a4c4 100644
>> --- a/drivers/media/video/Makefile
>> +++ b/drivers/media/video/Makefile
>> @@ -140,6 +140,7 @@ obj-$(CONFIG_VIDEO_MMP_CAMERA) += marvell-ccic/
>>  obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o
>>
>>  obj-$(CONFIG_VIDEO_OMAP3)    += omap3isp/
>> +obj-$(CONFIG_VIDEO_OMAP4)    += omap4iss/
>>
>>  obj-$(CONFIG_USB_ZR364XX)       += zr364xx.o
>>  obj-$(CONFIG_USB_STKWEBCAM)     += stkwebcam.o
>> diff --git a/drivers/media/video/omap4iss/Makefile b/drivers/media/video/omap4iss/Makefile
>> new file mode 100644
>> index 0000000..1d3b0a7
>> --- /dev/null
>> +++ b/drivers/media/video/omap4iss/Makefile
>> @@ -0,0 +1,6 @@
>> +# Makefile for OMAP4 ISS driver
>> +
>> +omap4-iss-objs += \
>> +     iss.o iss_csi2.o iss_csiphy.o iss_video.o
>> +
>> +obj-$(CONFIG_VIDEO_OMAP4) += omap4-iss.o
>> diff --git a/drivers/media/video/omap4iss/iss.c b/drivers/media/video/omap4iss/iss.c
>> new file mode 100644
>> index 0000000..255738b
>> --- /dev/null
>> +++ b/drivers/media/video/omap4iss/iss.c
>> @@ -0,0 +1,1179 @@
>> +/*
>> + * V4L2 Driver for OMAP4 ISS
>> + *
>> + * Copyright (C) 2011, Texas Instruments
>> + *
>> + * Author: Sergio Aguirre <saaguirre@ti.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/delay.h>
>> +#include <linux/device.h>
>> +#include <linux/dma-mapping.h>
>> +#include <linux/i2c.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/module.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/slab.h>
>> +#include <linux/sched.h>
>> +#include <linux/vmalloc.h>
>> +
>> +#include <media/v4l2-common.h>
>> +#include <media/v4l2-device.h>
>> +
>> +#include "iss.h"
>> +#include "iss_regs.h"
>> +
>> +static void iss_save_ctx(struct iss_device *iss);
>> +
>> +static void iss_restore_ctx(struct iss_device *iss);
>> +
>> +/* Structure for saving/restoring ISS module registers */
>> +static struct iss_reg iss_reg_list[] = {
>> +     {OMAP4_ISS_MEM_TOP, ISS_HL_SYSCONFIG, 0},
>> +     {OMAP4_ISS_MEM_TOP, ISS_CTRL, 0},
>> +     {OMAP4_ISS_MEM_TOP, ISS_CLKCTRL, 0},
>> +     {0, ISS_TOK_TERM, 0}
>> +};
>> +
>> +/*
>> + * omap4iss_flush - Post pending L3 bus writes by doing a register readback
>> + * @iss: OMAP4 ISS device
>> + *
>> + * In order to force posting of pending writes, we need to write and
>> + * readback the same register, in this case the revision register.
>> + *
>> + * See this link for reference:
>> + *   http://www.mail-archive.com/linux-omap@vger.kernel.org/msg08149.html
>> + */
>> +void omap4iss_flush(struct iss_device *iss)
>> +{
>> +     writel(0, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_REVISION);
>> +     readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_REVISION);
>> +}
>> +
>> +/*
>> + * iss_enable_interrupts - Enable ISS interrupts.
>> + * @iss: OMAP4 ISS device
>> + */
>> +static void iss_enable_interrupts(struct iss_device *iss)
>> +{
>> +     static const u32 irq = ISS_HL_IRQ_CSIA;
>> +
>> +     /* Enable HL interrupts */
>> +     writel(irq, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQSTATUS_5);
>> +     writel(irq, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQENABLE_5_SET);
>> +}
>> +
>> +/*
>> + * iss_disable_interrupts - Disable ISS interrupts.
>> + * @iss: OMAP4 ISS device
>> + */
>> +static void iss_disable_interrupts(struct iss_device *iss)
>> +{
>> +     writel(-1, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQENABLE_5_CLR);
>> +}
>> +
>> +static inline void iss_isr_dbg(struct iss_device *iss, u32 irqstatus)
>> +{
>> +     static const char *name[] = {
>> +             "ISP_IRQ0",
>> +             "ISP_IRQ1",
>> +             "ISP_IRQ2",
>> +             "ISP_IRQ3",
>> +             "CSIA_IRQ",
>> +             "CSIB_IRQ",
>> +             "CCP2_IRQ0",
>> +             "CCP2_IRQ1",
>> +             "CCP2_IRQ2",
>> +             "CCP2_IRQ3",
>> +             "CBUFF_IRQ",
>> +             "BTE_IRQ",
>> +             "SIMCOP_IRQ0",
>> +             "SIMCOP_IRQ1",
>> +             "SIMCOP_IRQ2",
>> +             "SIMCOP_IRQ3",
>> +             "CCP2_IRQ8",
>> +             "HS_VS_IRQ",
>> +             "res18",
>> +             "res19",
>> +             "res20",
>> +             "res21",
>> +             "res22",
>> +             "res23",
>> +             "res24",
>> +             "res25",
>> +             "res26",
>> +             "res27",
>> +             "res28",
>> +             "res29",
>> +             "res30",
>> +             "res31",
>> +     };
>> +     int i;
>> +
>> +     dev_dbg(iss->dev, "ISS IRQ: ");
>> +
>> +     for (i = 0; i < ARRAY_SIZE(name); i++) {
>> +             if ((1 << i) & irqstatus)
>> +                     printk(KERN_CONT "%s ", name[i]);
>> +     }
>> +     printk(KERN_CONT "\n");
>> +}
>> +
>> +/*
>> + * iss_isr - Interrupt Service Routine for ISS module.
>> + * @irq: Not used currently.
>> + * @_iss: Pointer to the OMAP4 ISS device
>> + *
>> + * Handles the corresponding callback if plugged in.
>> + *
>> + * Returns IRQ_HANDLED when IRQ was correctly handled, or IRQ_NONE when the
>> + * IRQ wasn't handled.
>> + */
>> +static irqreturn_t iss_isr(int irq, void *_iss)
>> +{
>> +     struct iss_device *iss = _iss;
>> +     u32 irqstatus;
>> +
>> +     irqstatus = readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQSTATUS_5);
>> +     writel(irqstatus, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQSTATUS_5);
>> +
>> +     if (irqstatus & ISS_HL_IRQ_CSIA)
>> +             omap4iss_csi2_isr(&iss->csi2a);
>> +
>> +     omap4iss_flush(iss);
>> +
>> +#if defined(DEBUG) && defined(ISS_ISR_DEBUG)
>> +     iss_isr_dbg(iss, irqstatus);
>> +#endif
>> +
>> +     return IRQ_HANDLED;
>> +}
>> +
>> +/* -----------------------------------------------------------------------------
>> + * Pipeline power management
>> + *
>> + * Entities must be powered up when part of a pipeline that contains at least
>> + * one open video device node.
>> + *
>> + * To achieve this use the entity use_count field to track the number of users.
>> + * For entities corresponding to video device nodes the use_count field stores
>> + * the users count of the node. For entities corresponding to subdevs the
>> + * use_count field stores the total number of users of all video device nodes
>> + * in the pipeline.
>> + *
>> + * The omap4iss_pipeline_pm_use() function must be called in the open() and
>> + * close() handlers of video device nodes. It increments or decrements the use
>> + * count of all subdev entities in the pipeline.
>> + *
>> + * To react to link management on powered pipelines, the link setup notification
>> + * callback updates the use count of all entities in the source and sink sides
>> + * of the link.
>> + */
>> +
>> +/*
>> + * iss_pipeline_pm_use_count - Count the number of users of a pipeline
>> + * @entity: The entity
>> + *
>> + * Return the total number of users of all video device nodes in the pipeline.
>> + */
>> +static int iss_pipeline_pm_use_count(struct media_entity *entity)
>> +{
>> +     struct media_entity_graph graph;
>> +     int use = 0;
>> +
>> +     media_entity_graph_walk_start(&graph, entity);
>> +
>> +     while ((entity = media_entity_graph_walk_next(&graph))) {
>> +             if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE)
>> +                     use += entity->use_count;
>> +     }
>> +
>> +     return use;
>> +}
>> +
>> +/*
>> + * iss_pipeline_pm_power_one - Apply power change to an entity
>> + * @entity: The entity
>> + * @change: Use count change
>> + *
>> + * Change the entity use count by @change. If the entity is a subdev update its
>> + * power state by calling the core::s_power operation when the use count goes
>> + * from 0 to != 0 or from != 0 to 0.
>> + *
>> + * Return 0 on success or a negative error code on failure.
>> + */
>> +static int iss_pipeline_pm_power_one(struct media_entity *entity, int change)
>> +{
>> +     struct v4l2_subdev *subdev;
>> +
>> +     subdev = media_entity_type(entity) == MEDIA_ENT_T_V4L2_SUBDEV
>> +            ? media_entity_to_v4l2_subdev(entity) : NULL;
>> +
>> +     if (entity->use_count == 0 && change > 0 && subdev != NULL) {
>> +             int ret;
>> +
>> +             ret = v4l2_subdev_call(subdev, core, s_power, 1);
>> +             if (ret < 0 && ret != -ENOIOCTLCMD)
>> +                     return ret;
>> +     }
>> +
>> +     entity->use_count += change;
>> +     WARN_ON(entity->use_count < 0);
>> +
>> +     if (entity->use_count == 0 && change < 0 && subdev != NULL)
>> +             v4l2_subdev_call(subdev, core, s_power, 0);
>> +
>> +     return 0;
>> +}
>> +
>> +/*
>> + * iss_pipeline_pm_power - Apply power change to all entities in a pipeline
>> + * @entity: The entity
>> + * @change: Use count change
>> + *
>> + * Walk the pipeline to update the use count and the power state of all non-node
>> + * entities.
>> + *
>> + * Return 0 on success or a negative error code on failure.
>> + */
>> +static int iss_pipeline_pm_power(struct media_entity *entity, int change)
>> +{
>> +     struct media_entity_graph graph;
>> +     struct media_entity *first = entity;
>> +     int ret = 0;
>> +
>> +     if (!change)
>> +             return 0;
>> +
>> +     media_entity_graph_walk_start(&graph, entity);
>> +
>> +     while (!ret && (entity = media_entity_graph_walk_next(&graph)))
>> +             if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE)
>> +                     ret = iss_pipeline_pm_power_one(entity, change);
>> +
>> +     if (!ret)
>> +             return 0;
>> +
>> +     media_entity_graph_walk_start(&graph, first);
>> +
>> +     while ((first = media_entity_graph_walk_next(&graph))
>> +            && first != entity)
>> +             if (media_entity_type(first) != MEDIA_ENT_T_DEVNODE)
>> +                     iss_pipeline_pm_power_one(first, -change);
>> +
>> +     return ret;
>> +}
>> +
>> +/*
>> + * omap4iss_pipeline_pm_use - Update the use count of an entity
>> + * @entity: The entity
>> + * @use: Use (1) or stop using (0) the entity
>> + *
>> + * Update the use count of all entities in the pipeline and power entities on or
>> + * off accordingly.
>> + *
>> + * Return 0 on success or a negative error code on failure. Powering entities
>> + * off is assumed to never fail. No failure can occur when the use parameter is
>> + * set to 0.
>> + */
>> +int omap4iss_pipeline_pm_use(struct media_entity *entity, int use)
>> +{
>> +     int change = use ? 1 : -1;
>> +     int ret;
>> +
>> +     mutex_lock(&entity->parent->graph_mutex);
>> +
>> +     /* Apply use count to node. */
>> +     entity->use_count += change;
>> +     WARN_ON(entity->use_count < 0);
>> +
>> +     /* Apply power change to connected non-nodes. */
>> +     ret = iss_pipeline_pm_power(entity, change);
>> +     if (ret < 0)
>> +             entity->use_count -= change;
>> +
>> +     mutex_unlock(&entity->parent->graph_mutex);
>> +
>> +     return ret;
>> +}
>> +
>> +/*
>> + * iss_pipeline_link_notify - Link management notification callback
>> + * @source: Pad at the start of the link
>> + * @sink: Pad at the end of the link
>> + * @flags: New link flags that will be applied
>> + *
>> + * React to link management on powered pipelines by updating the use count of
>> + * all entities in the source and sink sides of the link. Entities are powered
>> + * on or off accordingly.
>> + *
>> + * Return 0 on success or a negative error code on failure. Powering entities
>> + * off is assumed to never fail. This function will not fail for disconnection
>> + * events.
>> + */
>> +static int iss_pipeline_link_notify(struct media_pad *source,
>> +                                 struct media_pad *sink, u32 flags)
>> +{
>> +     int source_use = iss_pipeline_pm_use_count(source->entity);
>> +     int sink_use = iss_pipeline_pm_use_count(sink->entity);
>> +     int ret;
>> +
>> +     if (!(flags & MEDIA_LNK_FL_ENABLED)) {
>> +             /* Powering off entities is assumed to never fail. */
>> +             iss_pipeline_pm_power(source->entity, -sink_use);
>> +             iss_pipeline_pm_power(sink->entity, -source_use);
>> +             return 0;
>> +     }
>> +
>> +     ret = iss_pipeline_pm_power(source->entity, sink_use);
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     ret = iss_pipeline_pm_power(sink->entity, source_use);
>> +     if (ret < 0)
>> +             iss_pipeline_pm_power(source->entity, -sink_use);
>> +
>> +     return ret;
>> +}
>> +
>> +/* -----------------------------------------------------------------------------
>> + * Pipeline stream management
>> + */
>> +
>> +/*
>> + * iss_pipeline_enable - Enable streaming on a pipeline
>> + * @pipe: ISS pipeline
>> + * @mode: Stream mode (single shot or continuous)
>> + *
>> + * Walk the entities chain starting at the pipeline output video node and start
>> + * all modules in the chain in the given mode.
>> + *
>> + * Return 0 if successful, or the return value of the failed video::s_stream
>> + * operation otherwise.
>> + */
>> +static int iss_pipeline_enable(struct iss_pipeline *pipe,
>> +                            enum iss_pipeline_stream_state mode)
>> +{
>> +     struct media_entity *entity;
>> +     struct media_pad *pad;
>> +     struct v4l2_subdev *subdev;
>> +     unsigned long flags;
>> +     int ret = 0;
>> +
>> +     spin_lock_irqsave(&pipe->lock, flags);
>> +     pipe->state &= ~(ISS_PIPELINE_IDLE_INPUT | ISS_PIPELINE_IDLE_OUTPUT);
>> +     spin_unlock_irqrestore(&pipe->lock, flags);
>> +
>> +     pipe->do_propagation = false;
>> +
>> +     entity = &pipe->output->video.entity;
>> +     while (1) {
>> +             pad = &entity->pads[0];
>> +             if (!(pad->flags & MEDIA_PAD_FL_SINK))
>> +                     break;
>> +
>> +             pad = media_entity_remote_source(pad);
>> +             if (pad == NULL ||
>> +                 media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
>> +                     break;
>> +
>> +             entity = pad->entity;
>> +             subdev = media_entity_to_v4l2_subdev(entity);
>> +
>> +             ret = v4l2_subdev_call(subdev, video, s_stream, mode);
>> +             if (ret < 0 && ret != -ENOIOCTLCMD)
>> +                     break;
>> +     }
>> +
>> +     return ret;
>> +}
>> +
>> +/*
>> + * iss_pipeline_disable - Disable streaming on a pipeline
>> + * @pipe: ISS pipeline
>> + *
>> + * Walk the entities chain starting at the pipeline output video node and stop
>> + * all modules in the chain. Wait synchronously for the modules to be stopped if
>> + * necessary.
>> + */
>> +static int iss_pipeline_disable(struct iss_pipeline *pipe)
>> +{
>> +     struct media_entity *entity;
>> +     struct media_pad *pad;
>> +     struct v4l2_subdev *subdev;
>> +     int failure = 0;
>> +
>> +     entity = &pipe->output->video.entity;
>> +     while (1) {
>> +             pad = &entity->pads[0];
>> +             if (!(pad->flags & MEDIA_PAD_FL_SINK))
>> +                     break;
>> +
>> +             pad = media_entity_remote_source(pad);
>> +             if (pad == NULL ||
>> +                 media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
>> +                     break;
>> +
>> +             entity = pad->entity;
>> +             subdev = media_entity_to_v4l2_subdev(entity);
>> +
>> +             v4l2_subdev_call(subdev, video, s_stream, 0);
>> +     }
>> +
>> +     return failure;
>> +}
>> +
>> +/*
>> + * omap4iss_pipeline_set_stream - Enable/disable streaming on a pipeline
>> + * @pipe: ISS pipeline
>> + * @state: Stream state (stopped, single shot or continuous)
>> + *
>> + * Set the pipeline to the given stream state. Pipelines can be started in
>> + * single-shot or continuous mode.
>> + *
>> + * Return 0 if successful, or the return value of the failed video::s_stream
>> + * operation otherwise. The pipeline state is not updated when the operation
>> + * fails, except when stopping the pipeline.
>> + */
>> +int omap4iss_pipeline_set_stream(struct iss_pipeline *pipe,
>> +                              enum iss_pipeline_stream_state state)
>> +{
>> +     int ret;
>> +
>> +     if (state == ISS_PIPELINE_STREAM_STOPPED)
>> +             ret = iss_pipeline_disable(pipe);
>> +     else
>> +             ret = iss_pipeline_enable(pipe, state);
>> +
>> +     if (ret == 0 || state == ISS_PIPELINE_STREAM_STOPPED)
>> +             pipe->stream_state = state;
>> +
>> +     return ret;
>> +}
>> +
>> +/*
>> + * iss_pipeline_is_last - Verify if entity has an enabled link to the output
>> + *                     video node
>> + * @me: ISS module's media entity
>> + *
>> + * Returns 1 if the entity has an enabled link to the output video node or 0
>> + * otherwise. It's true only while pipeline can have no more than one output
>> + * node.
>> + */
>> +static int iss_pipeline_is_last(struct media_entity *me)
>> +{
>> +     struct iss_pipeline *pipe;
>> +     struct media_pad *pad;
>> +
>> +     if (!me->pipe)
>> +             return 0;
>> +     pipe = to_iss_pipeline(me);
>> +     if (pipe->stream_state == ISS_PIPELINE_STREAM_STOPPED)
>> +             return 0;
>> +     pad = media_entity_remote_source(&pipe->output->pad);
>> +     return pad->entity == me;
>> +}
>> +
>> +static int iss_reset(struct iss_device *iss)
>> +{
>> +     unsigned long timeout = 0;
>> +
>> +     writel(readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_SYSCONFIG) |
>> +             ISS_HL_SYSCONFIG_SOFTRESET,
>> +             iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_SYSCONFIG);
>> +
>> +     while (readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_SYSCONFIG) &
>> +                     ISS_HL_SYSCONFIG_SOFTRESET) {
>> +             if (timeout++ > 10000) {
>> +                     dev_alert(iss->dev, "cannot reset ISS\n");
>> +                     return -ETIMEDOUT;
>> +             }
>> +             udelay(1);
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +/*
>> + * iss_save_context - Saves the values of the ISS module registers.
>> + * @iss: OMAP4 ISS device
>> + * @reg_list: Structure containing pairs of register address and value to
>> + *            modify on OMAP.
>> + */
>> +static void
>> +iss_save_context(struct iss_device *iss, struct iss_reg *reg_list)
>> +{
>> +     struct iss_reg *next = reg_list;
>> +
>> +     for (; next->reg != ISS_TOK_TERM; next++)
>> +             next->val = readl(iss->regs[next->mmio_range] + next->reg);
>> +}
>> +
>> +/*
>> + * iss_restore_context - Restores the values of the ISS module registers.
>> + * @iss: OMAP4 ISS device
>> + * @reg_list: Structure containing pairs of register address and value to
>> + *            modify on OMAP.
>> + */
>> +static void
>> +iss_restore_context(struct iss_device *iss, struct iss_reg *reg_list)
>> +{
>> +     struct iss_reg *next = reg_list;
>> +
>> +     for (; next->reg != ISS_TOK_TERM; next++)
>> +             writel(next->val, iss->regs[next->mmio_range] + next->reg);
>> +}
>> +
>> +/*
>> + * iss_save_ctx - Saves ISS context.
>> + * @iss: OMAP4 ISS device
>> + *
>> + * Routine for saving the context of each module in the ISS.
>> + */
>> +static void iss_save_ctx(struct iss_device *iss)
>> +{
>> +     iss_save_context(iss, iss_reg_list);
>> +}
>
> Do you really, really need to save context related data? Couldn't you
> initialise the device the usual way when e.g. returning from suspended
> state?

I'll actually have to revisit this more carefuly. I haven't really
tested Runtime PM on this.

I think I'll remove this for now, until i come up with something better.

>
>> +/*
>> + * iss_restore_ctx - Restores ISS context.
>> + * @iss: OMAP4 ISS device
>> + *
>> + * Routine for restoring the context of each module in the ISS.
>> + */
>> +static void iss_restore_ctx(struct iss_device *iss)
>> +{
>> +     iss_restore_context(iss, iss_reg_list);
>> +}
>> +
>> +/*
>> + * iss_module_sync_idle - Helper to sync module with its idle state
>> + * @me: ISS submodule's media entity
>> + * @wait: ISS submodule's wait queue for streamoff/interrupt synchronization
>> + * @stopping: flag which tells module wants to stop
>> + *
>> + * This function checks if ISS submodule needs to wait for next interrupt. If
>> + * yes, makes the caller to sleep while waiting for such event.
>> + */
>> +int omap4iss_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait,
>> +                           atomic_t *stopping)
>> +{
>> +     struct iss_pipeline *pipe = to_iss_pipeline(me);
>> +
>> +     if (pipe->stream_state == ISS_PIPELINE_STREAM_STOPPED ||
>> +         (pipe->stream_state == ISS_PIPELINE_STREAM_SINGLESHOT &&
>> +          !iss_pipeline_ready(pipe)))
>> +             return 0;
>> +
>> +     /*
>> +      * atomic_set() doesn't include memory barrier on ARM platform for SMP
>> +      * scenario. We'll call it here to avoid race conditions.
>> +      */
>> +     atomic_set(stopping, 1);
>> +     smp_mb();
>> +
>> +     /*
>> +      * If module is the last one, it's writing to memory. In this case,
>> +      * it's necessary to check if the module is already paused due to
>> +      * DMA queue underrun or if it has to wait for next interrupt to be
>> +      * idle.
>> +      * If it isn't the last one, the function won't sleep but *stopping
>> +      * will still be set to warn next submodule caller's interrupt the
>> +      * module wants to be idle.
>> +      */
>> +     if (iss_pipeline_is_last(me)) {
>> +             struct iss_video *video = pipe->output;
>> +             unsigned long flags;
>> +
>> +             spin_lock_irqsave(&video->qlock, flags);
>> +             if (video->dmaqueue_flags & ISS_VIDEO_DMAQUEUE_UNDERRUN) {
>> +                     spin_unlock_irqrestore(&video->qlock, flags);
>> +                     atomic_set(stopping, 0);
>> +                     smp_mb();
>> +                     return 0;
>> +             }
>> +             spin_unlock_irqrestore(&video->qlock, flags);
>> +             if (!wait_event_timeout(*wait, !atomic_read(stopping),
>> +                                     msecs_to_jiffies(1000))) {
>> +                     atomic_set(stopping, 0);
>> +                     smp_mb();
>> +                     return -ETIMEDOUT;
>> +             }
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +/*
>> + * omap4iss_module_sync_is_stopped - Helper to verify if module was stopping
>> + * @wait: ISS submodule's wait queue for streamoff/interrupt synchronization
>> + * @stopping: flag which tells module wants to stop
>> + *
>> + * This function checks if ISS submodule was stopping. In case of yes, it
>> + * notices the caller by setting stopping to 0 and waking up the wait queue.
>> + * Returns 1 if it was stopping or 0 otherwise.
>> + */
>> +int omap4iss_module_sync_is_stopping(wait_queue_head_t *wait,
>> +                                  atomic_t *stopping)
>> +{
>> +     if (atomic_cmpxchg(stopping, 1, 0)) {
>> +             wake_up(wait);
>> +             return 1;
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +/* --------------------------------------------------------------------------
>> + * Clock management
>> + */
>> +
>> +#define ISS_CLKCTRL_MASK     (ISS_CLKCTRL_CSI2_A)
>> +
>> +static int __iss_subclk_update(struct iss_device *iss)
>> +{
>> +     u32 clk = 0;
>> +     int ret = 0, timeout = 1000;
>> +
>> +     if (iss->subclk_resources & OMAP4_ISS_SUBCLK_CSI2_A)
>> +             clk |= ISS_CLKCTRL_CSI2_A;
>> +
>> +     writel((readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_CLKCTRL) &
>> +             ~ISS_CLKCTRL_MASK) | clk,
>> +             iss->regs[OMAP4_ISS_MEM_TOP] + ISS_CLKCTRL);
>> +
>> +     /* Wait for HW assertion */
>> +     while (timeout-- > 0) {
>
> The value must be decremented before the comparison. Otherwise the result
> could be negative, and the check a few lines below will fail even if the
> timeout happened.

Oops yes. Missed that. Will fix.

>
> I guess it's not a crime to use a for loop either in situations like this.

Ok.

>
>> +             udelay(1);
>> +             if ((readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_CLKSTAT) &
>> +                  ISS_CLKCTRL_MASK) == clk)
>> +                     break;
>> +     }
>> +
>> +     if (!timeout)
>> +             ret = -EBUSY;
>> +
>> +     return ret;
>> +}
>> +
>> +int omap4iss_subclk_enable(struct iss_device *iss,
>> +                         enum iss_subclk_resource res)
>> +{
>> +     iss->subclk_resources |= res;
>> +
>> +     return __iss_subclk_update(iss);
>> +}
>> +
>> +int omap4iss_subclk_disable(struct iss_device *iss,
>> +                          enum iss_subclk_resource res)
>> +{
>> +     iss->subclk_resources &= ~res;
>> +
>> +     return __iss_subclk_update(iss);
>> +}
>> +
>> +/*
>> + * iss_enable_clocks - Enable ISS clocks
>> + * @iss: OMAP4 ISS device
>> + *
>> + * Return 0 if successful, or clk_enable return value if any of tthem fails.
>> + */
>> +static int iss_enable_clocks(struct iss_device *iss)
>> +{
>> +     int r;
>> +
>> +     r = clk_enable(iss->iss_fck);
>> +     if (r) {
>> +             dev_err(iss->dev, "clk_enable iss_fck failed\n");
>> +             goto out_clk_enable_fck;
>
> You can return the error here and your error handling below becomes more
> simple.

Right. Will fix.

>
>> +     }
>> +
>> +     r = clk_enable(iss->iss_ctrlclk);
>> +     if (r) {
>> +             dev_err(iss->dev, "clk_enable iss_ctrlclk failed\n");
>> +             goto out_clk_enable_ctrlclk;
>> +     }
>> +     return 0;
>> +
>> +out_clk_enable_ctrlclk:
>> +     clk_disable(iss->iss_fck);
>> +out_clk_enable_fck:
>> +     return r;
>> +}
>> +
>> +/*
>> + * iss_disable_clocks - Disable ISS clocks
>> + * @iss: OMAP4 ISS device
>> + */
>> +static void iss_disable_clocks(struct iss_device *iss)
>> +{
>> +     clk_disable(iss->iss_ctrlclk);
>> +     clk_disable(iss->iss_fck);
>> +}
>> +
>> +static void iss_put_clocks(struct iss_device *iss)
>> +{
>> +     if (iss->iss_fck) {
>> +             clk_put(iss->iss_fck);
>> +             iss->iss_fck = NULL;
>> +     }
>> +
>> +     if (iss->iss_ctrlclk) {
>> +             clk_put(iss->iss_ctrlclk);
>> +             iss->iss_ctrlclk = NULL;
>> +     }
>> +}
>> +
>> +static int iss_get_clocks(struct iss_device *iss)
>> +{
>> +     iss->iss_fck = clk_get(iss->dev, "iss_fck");
>> +     if (IS_ERR(iss->iss_fck)) {
>> +             dev_err(iss->dev, "Unable to get iss_fck clock info\n");
>> +             iss_put_clocks(iss);
>> +             return PTR_ERR(iss->iss_fck);
>> +     }
>> +
>> +     iss->iss_ctrlclk = clk_get(iss->dev, "iss_ctrlclk");
>> +     if (IS_ERR(iss->iss_ctrlclk)) {
>> +             dev_err(iss->dev, "Unable to get iss_ctrlclk clock info\n");
>> +             iss_put_clocks(iss);
>> +             return PTR_ERR(iss->iss_fck);
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +/*
>> + * omap4iss_get - Acquire the ISS resource.
>> + *
>> + * Initializes the clocks for the first acquire.
>> + *
>> + * Increment the reference count on the ISS. If the first reference is taken,
>> + * enable clocks and power-up all submodules.
>> + *
>> + * Return a pointer to the ISS device structure, or NULL if an error occurred.
>> + */
>> +struct iss_device *omap4iss_get(struct iss_device *iss)
>> +{
>> +     struct iss_device *__iss = iss;
>> +
>> +     if (iss == NULL)
>> +             return NULL;
>> +
>> +     mutex_lock(&iss->iss_mutex);
>> +     if (iss->ref_count > 0)
>> +             goto out;
>> +
>> +     if (iss_enable_clocks(iss) < 0) {
>> +             __iss = NULL;
>> +             goto out;
>> +     }
>> +
>> +     /* We don't want to restore context before saving it! */
>> +     if (iss->has_context)
>> +             iss_restore_ctx(iss);
>> +     else
>> +             iss->has_context = 1;
>> +
>> +     iss_enable_interrupts(iss);
>> +
>> +out:
>> +     if (__iss != NULL)
>> +             iss->ref_count++;
>> +     mutex_unlock(&iss->iss_mutex);
>> +
>> +     return __iss;
>> +}
>> +
>> +/*
>> + * omap4iss_put - Release the ISS
>> + *
>> + * Decrement the reference count on the ISS. If the last reference is released,
>> + * power-down all submodules, disable clocks and free temporary buffers.
>> + */
>> +void omap4iss_put(struct iss_device *iss)
>> +{
>> +     if (iss == NULL)
>> +             return;
>> +
>> +     mutex_lock(&iss->iss_mutex);
>> +     BUG_ON(iss->ref_count == 0);
>> +     if (--iss->ref_count == 0) {
>> +             iss_disable_interrupts(iss);
>> +             iss_save_ctx(iss);
>> +             iss_disable_clocks(iss);
>> +     }
>> +     mutex_unlock(&iss->iss_mutex);
>> +}
>> +
>> +static int iss_map_mem_resource(struct platform_device *pdev,
>> +                             struct iss_device *iss,
>> +                             enum iss_mem_resources res)
>> +{
>> +     struct resource *mem;
>> +
>> +     /* request the mem region for the camera registers */
>> +
>> +     mem = platform_get_resource(pdev, IORESOURCE_MEM, res);
>> +     if (!mem) {
>> +             dev_err(iss->dev, "no mem resource?\n");
>> +             return -ENODEV;
>> +     }
>> +
>> +     if (!request_mem_region(mem->start, resource_size(mem), pdev->name)) {
>> +             dev_err(iss->dev,
>> +                     "cannot reserve camera register I/O region\n");
>> +             return -ENODEV;
>> +     }
>> +     iss->res[res] = mem;
>> +
>> +     /* map the region */
>> +     iss->regs[res] = ioremap_nocache(mem->start, resource_size(mem));
>> +     if (!iss->regs[res]) {
>> +             dev_err(iss->dev, "cannot map camera register I/O region\n");
>> +             return -ENODEV;
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +static void iss_unregister_entities(struct iss_device *iss)
>> +{
>> +     omap4iss_csi2_unregister_entities(&iss->csi2a);
>> +
>> +     v4l2_device_unregister(&iss->v4l2_dev);
>> +     media_device_unregister(&iss->media_dev);
>> +}
>> +
>> +/*
>> + * iss_register_subdev_group - Register a group of subdevices
>> + * @iss: OMAP4 ISS device
>> + * @board_info: I2C subdevs board information array
>> + *
>> + * Register all I2C subdevices in the board_info array. The array must be
>> + * terminated by a NULL entry, and the first entry must be the sensor.
>> + *
>> + * Return a pointer to the sensor media entity if it has been successfully
>> + * registered, or NULL otherwise.
>> + */
>> +static struct v4l2_subdev *
>> +iss_register_subdev_group(struct iss_device *iss,
>> +                  struct iss_subdev_i2c_board_info *board_info)
>> +{
>> +     struct v4l2_subdev *sensor = NULL;
>> +     unsigned int first;
>> +
>> +     if (board_info->board_info == NULL)
>> +             return NULL;
>> +
>> +     for (first = 1; board_info->board_info; ++board_info, first = 0) {
>> +             struct v4l2_subdev *subdev;
>> +             struct i2c_adapter *adapter;
>> +
>> +             adapter = i2c_get_adapter(board_info->i2c_adapter_id);
>> +             if (adapter == NULL) {
>> +                     printk(KERN_ERR "%s: Unable to get I2C adapter %d for "
>> +                             "device %s\n", __func__,
>> +                             board_info->i2c_adapter_id,
>> +                             board_info->board_info->type);
>
> Why printk? I think you already have access to struct device.

You're right. Will fix.

>
>> +                     continue;
>> +             }
>> +
>> +             subdev = v4l2_i2c_new_subdev_board(&iss->v4l2_dev, adapter,
>> +                             board_info->board_info, NULL);
>> +             if (subdev == NULL) {
>> +                     printk(KERN_ERR "%s: Unable to register subdev %s\n",
>> +                             __func__, board_info->board_info->type);
>> +                     continue;
>> +             }
>> +
>> +             if (first)
>> +                     sensor = subdev;
>> +     }
>> +
>> +     return sensor;
>> +}
>> +
>> +static int iss_register_entities(struct iss_device *iss)
>> +{
>> +     struct iss_platform_data *pdata = iss->pdata;
>> +     struct iss_v4l2_subdevs_group *subdevs;
>> +     int ret;
>> +
>> +     iss->media_dev.dev = iss->dev;
>> +     strlcpy(iss->media_dev.model, "TI OMAP4 ISS",
>> +             sizeof(iss->media_dev.model));
>> +     iss->media_dev.link_notify = iss_pipeline_link_notify;
>> +     ret = media_device_register(&iss->media_dev);
>> +     if (ret < 0) {
>> +             printk(KERN_ERR "%s: Media device registration failed (%d)\n",
>> +                     __func__, ret);
>> +             return ret;
>> +     }
>> +
>> +     iss->v4l2_dev.mdev = &iss->media_dev;
>> +     ret = v4l2_device_register(iss->dev, &iss->v4l2_dev);
>> +     if (ret < 0) {
>> +             printk(KERN_ERR "%s: V4L2 device registration failed (%d)\n",
>> +                     __func__, ret);
>> +             goto done;
>> +     }
>> +
>> +     /* Register internal entities */
>> +     ret = omap4iss_csi2_register_entities(&iss->csi2a, &iss->v4l2_dev);
>> +     if (ret < 0)
>> +             goto done;
>> +
>> +     /* Register external entities */
>> +     for (subdevs = pdata->subdevs; subdevs && subdevs->subdevs; ++subdevs) {
>> +             struct v4l2_subdev *sensor;
>> +             struct media_entity *input;
>> +             unsigned int flags;
>> +             unsigned int pad;
>> +
>> +             sensor = iss_register_subdev_group(iss, subdevs->subdevs);
>> +             if (sensor == NULL)
>> +                     continue;
>> +
>> +             sensor->host_priv = subdevs;
>> +
>> +             /* Connect the sensor to the correct interface module.
>> +              * CSI2a receiver through CSIPHY1.
>> +              */
>> +             switch (subdevs->interface) {
>> +             case ISS_INTERFACE_CSI2A_PHY1:
>> +                     input = &iss->csi2a.subdev.entity;
>> +                     pad = CSI2_PAD_SINK;
>> +                     flags = MEDIA_LNK_FL_IMMUTABLE
>> +                           | MEDIA_LNK_FL_ENABLED;
>> +                     break;
>> +
>> +             default:
>> +                     printk(KERN_ERR "%s: invalid interface type %u\n",
>> +                            __func__, subdevs->interface);
>> +                     ret = -EINVAL;
>> +                     goto done;
>> +             }
>> +
>> +             ret = media_entity_create_link(&sensor->entity, 0, input, pad,
>> +                                            flags);
>> +             if (ret < 0)
>> +                     goto done;
>> +     }
>> +
>> +     ret = v4l2_device_register_subdev_nodes(&iss->v4l2_dev);
>> +
>> +done:
>> +     if (ret < 0)
>> +             iss_unregister_entities(iss);
>> +
>> +     return ret;
>> +}
>> +
>> +static void iss_cleanup_modules(struct iss_device *iss)
>> +{
>> +     omap4iss_csi2_cleanup(iss);
>> +}
>> +
>> +static int iss_initialize_modules(struct iss_device *iss)
>> +{
>> +     int ret;
>> +
>> +     ret = omap4iss_csiphy_init(iss);
>> +     if (ret < 0) {
>> +             dev_err(iss->dev, "CSI PHY initialization failed\n");
>> +             goto error_csiphy;
>> +     }
>> +
>> +     ret = omap4iss_csi2_init(iss);
>> +     if (ret < 0) {
>> +             dev_err(iss->dev, "CSI2 initialization failed\n");
>> +             goto error_csi2;
>> +     }
>> +
>> +     return 0;
>> +
>> +error_csi2:
>> +error_csiphy:
>> +     return ret;
>
> If there's nothing special you need to do here, I suggest to return the
> error where it happened.

Agreed. Will fix.

>
>> +}
>> +
>> +static int iss_probe(struct platform_device *pdev)
>> +{
>> +     struct iss_platform_data *pdata = pdev->dev.platform_data;
>> +     struct iss_device *iss;
>> +     int i, ret;
>> +
>> +     if (pdata == NULL)
>> +             return -EINVAL;
>> +
>> +     iss = kzalloc(sizeof(*iss), GFP_KERNEL);
>> +     if (!iss) {
>> +             dev_err(&pdev->dev, "Could not allocate memory\n");
>> +             return -ENOMEM;
>> +     }
>> +
>> +     mutex_init(&iss->iss_mutex);
>> +
>> +     iss->dev = &pdev->dev;
>> +     iss->pdata = pdata;
>> +     iss->ref_count = 0;
>> +
>> +     iss->raw_dmamask = DMA_BIT_MASK(32);
>> +     iss->dev->dma_mask = &iss->raw_dmamask;
>> +     iss->dev->coherent_dma_mask = DMA_BIT_MASK(32);
>> +
>> +     platform_set_drvdata(pdev, iss);
>> +
>> +     /* Clocks */
>> +     ret = iss_map_mem_resource(pdev, iss, OMAP4_ISS_MEM_TOP);
>> +     if (ret < 0)
>> +             goto error;
>> +
>> +     ret = iss_get_clocks(iss);
>> +     if (ret < 0)
>> +             goto error;
>> +
>> +     if (omap4iss_get(iss) == NULL)
>> +             goto error;
>> +
>> +     ret = iss_reset(iss);
>> +     if (ret < 0)
>> +             goto error_iss;
>> +
>> +     iss->revision = readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_REVISION);
>> +     dev_info(iss->dev, "Revision %08x found\n", iss->revision);
>> +
>> +     for (i = 1; i < OMAP4_ISS_MEM_LAST; i++) {
>> +             ret = iss_map_mem_resource(pdev, iss, i);
>> +             if (ret)
>> +                     goto error_iss;
>> +     }
>> +
>> +     /* Interrupt */
>> +     iss->irq_num = platform_get_irq(pdev, 0);
>> +     if (iss->irq_num <= 0) {
>> +             dev_err(iss->dev, "No IRQ resource\n");
>> +             ret = -ENODEV;
>> +             goto error_iss;
>> +     }
>> +
>> +     if (request_irq(iss->irq_num, iss_isr, IRQF_SHARED, "OMAP4 ISS", iss)) {
>> +             dev_err(iss->dev, "Unable to request IRQ\n");
>> +             ret = -EINVAL;
>> +             goto error_iss;
>> +     }
>> +
>> +     /* Entities */
>> +     ret = iss_initialize_modules(iss);
>> +     if (ret < 0)
>> +             goto error_irq;
>> +
>> +     ret = iss_register_entities(iss);
>> +     if (ret < 0)
>> +             goto error_modules;
>> +
>> +     omap4iss_put(iss);
>> +
>> +     return 0;
>> +
>> +error_modules:
>> +     iss_cleanup_modules(iss);
>> +error_irq:
>> +     free_irq(iss->irq_num, iss);
>> +error_iss:
>> +     omap4iss_put(iss);
>> +error:
>> +     iss_put_clocks(iss);
>> +
>> +     for (i = 0; i < OMAP4_ISS_MEM_LAST; i++) {
>> +             if (iss->regs[i]) {
>> +                     iounmap(iss->regs[i]);
>> +                     iss->regs[i] = NULL;
>> +             }
>> +
>> +             if (iss->res[i]) {
>> +                     release_mem_region(iss->res[i]->start,
>> +                                        resource_size(iss->res[i]));
>> +                     iss->res[i] = NULL;
>> +             }
>> +     }
>> +     platform_set_drvdata(pdev, NULL);
>> +     kfree(iss);
>> +
>> +     return ret;
>> +}
>> +
>> +static int iss_remove(struct platform_device *pdev)
>> +{
>> +     struct iss_device *iss = platform_get_drvdata(pdev);
>> +     int i;
>> +
>> +     iss_unregister_entities(iss);
>> +     iss_cleanup_modules(iss);
>> +
>> +     free_irq(iss->irq_num, iss);
>> +     iss_put_clocks(iss);
>> +
>> +     for (i = 0; i < OMAP4_ISS_MEM_LAST; i++) {
>> +             if (iss->regs[i]) {
>> +                     iounmap(iss->regs[i]);
>> +                     iss->regs[i] = NULL;
>> +             }
>> +
>> +             if (iss->res[i]) {
>> +                     release_mem_region(iss->res[i]->start,
>> +                                        resource_size(iss->res[i]));
>> +                     iss->res[i] = NULL;
>> +             }
>> +     }
>> +
>> +     kfree(iss);
>> +
>> +     return 0;
>> +}
>> +
>> +static struct platform_device_id omap4iss_id_table[] = {
>> +     { "omap4iss", 0 },
>> +     { },
>> +};
>> +MODULE_DEVICE_TABLE(platform, omap4iss_id_table);
>> +
>> +static struct platform_driver iss_driver = {
>> +     .probe          = iss_probe,
>> +     .remove         = iss_remove,
>> +     .id_table       = omap4iss_id_table,
>> +     .driver = {
>> +             .owner  = THIS_MODULE,
>> +             .name   = "omap4iss",
>> +     },
>> +};
>> +
>> +static int __init iss_init(void)
>> +{
>> +     return platform_driver_register(&iss_driver);
>> +}
>> +
>> +static void __exit iss_exit(void)
>> +{
>> +     platform_driver_unregister(&iss_driver);
>> +}
>> +
>> +/*
>> + * FIXME: Had to make it late_initcall. Strangely while being module_init,
>> + * The I2C communication was failing in the sensor, because no XCLK was
>> + * provided.
>> + */
>> +late_initcall(iss_init);
>> +module_exit(iss_exit);
>> +
>> +MODULE_DESCRIPTION("TI OMAP4 ISS driver");
>> +MODULE_AUTHOR("Sergio Aguirre <saaguirre@ti.com>");
>> +MODULE_LICENSE("GPL");
>> diff --git a/drivers/media/video/omap4iss/iss.h b/drivers/media/video/omap4iss/iss.h
>> new file mode 100644
>> index 0000000..8346c80
>> --- /dev/null
>> +++ b/drivers/media/video/omap4iss/iss.h
>> @@ -0,0 +1,133 @@
>> +/*
>> + * iss.h
>> + *
>> + * Copyright (C) 2011 Texas Instruments.
>> + *
>> + * Author: Sergio Aguirre <saaguirre@ti.com>
>> + */
>
> Have you intentionally left out the more verbose notice? It's also 2012 now.
> :)

:) Not intentional... Will fix.

>
>> +#ifndef _OMAP4_ISS_H_
>> +#define _OMAP4_ISS_H_
>> +
>> +#include <media/v4l2-device.h>
>> +#include <linux/device.h>
>> +#include <linux/io.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/wait.h>
>> +
>> +#include <plat/omap4-iss.h>
>> +
>> +#include "iss_regs.h"
>> +#include "iss_csiphy.h"
>> +#include "iss_csi2.h"
>> +
>> +#define ISS_TOK_TERM         0xFFFFFFFF      /*
>
> Minor thing, but lowercase hex are preferred.

I'll actually get rid of this, along with the context save-restore for now.

>
>> +                                              * terminating token for ISS
>> +                                              * modules reg list
>> +                                              */
>> +#define to_iss_device(ptr_module)                            \
>> +     container_of(ptr_module, struct iss_device, ptr_module)
>> +#define to_device(ptr_module)                                                \
>> +     (to_iss_device(ptr_module)->dev)
>> +
>> +enum iss_mem_resources {
>> +     OMAP4_ISS_MEM_TOP,
>> +     OMAP4_ISS_MEM_CSI2_A_REGS1,
>> +     OMAP4_ISS_MEM_CAMERARX_CORE1,
>> +     OMAP4_ISS_MEM_LAST,
>> +};
>> +
>> +enum iss_subclk_resource {
>> +     OMAP4_ISS_SUBCLK_SIMCOP         = (1 << 0),
>> +     OMAP4_ISS_SUBCLK_ISP            = (1 << 1),
>> +     OMAP4_ISS_SUBCLK_CSI2_A         = (1 << 2),
>> +     OMAP4_ISS_SUBCLK_CSI2_B         = (1 << 3),
>> +     OMAP4_ISS_SUBCLK_CCP2           = (1 << 4),
>> +};
>> +
>> +/*
>> + * struct iss_reg - Structure for ISS register values.
>> + * @reg: 32-bit Register address.
>> + * @val: 32-bit Register value.
>> + */
>> +struct iss_reg {
>> +     enum iss_mem_resources mmio_range;
>> +     u32 reg;
>> +     u32 val;
>> +};
>> +
>> +struct iss_platform_callback {
>> +     int (*csiphy_config)(struct iss_csiphy *phy,
>> +                          struct iss_csiphy_dphy_cfg *dphy,
>> +                          struct iss_csiphy_lanes_cfg *lanes);
>> +};
>
> You might want to look my recent patchset (a few days ago). You can probably
> get rid of this callback easily.

Ok. I'll pay attention to it.

>
>> +struct iss_device {
>> +     struct v4l2_device v4l2_dev;
>> +     struct media_device media_dev;
>> +     struct device *dev;
>> +     u32 revision;
>> +
>> +     /* platform HW resources */
>> +     struct iss_platform_data *pdata;
>> +     unsigned int irq_num;
>> +
>> +     struct resource *res[OMAP4_ISS_MEM_LAST];
>> +     void __iomem *regs[OMAP4_ISS_MEM_LAST];
>> +
>> +     u64 raw_dmamask;
>> +
>> +     struct mutex iss_mutex; /* For handling ref_count field */
>> +     int has_context;
>> +     int ref_count;
>> +
>> +     struct clk *iss_fck;
>> +     struct clk *iss_ctrlclk;
>> +
>> +     /* ISS modules */
>> +     struct iss_csi2_device csi2a;
>> +     struct iss_csiphy csiphy1;
>> +
>> +     unsigned int subclk_resources;
>> +
>> +     struct iss_platform_callback platform_cb;
>> +};
>> +
>> +#define v4l2_dev_to_iss_device(dev) \
>> +     container_of(dev, struct iss_device, v4l2_dev)
>> +
>> +int omap4iss_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait,
>> +                           atomic_t *stopping);
>> +
>> +int omap4iss_module_sync_is_stopping(wait_queue_head_t *wait,
>> +                                  atomic_t *stopping);
>> +
>> +int omap4iss_pipeline_set_stream(struct iss_pipeline *pipe,
>> +                              enum iss_pipeline_stream_state state);
>> +
>> +struct iss_device *omap4iss_get(struct iss_device *iss);
>> +void omap4iss_put(struct iss_device *iss);
>> +int omap4iss_subclk_enable(struct iss_device *iss,
>> +                        enum iss_subclk_resource res);
>> +int omap4iss_subclk_disable(struct iss_device *iss,
>> +                         enum iss_subclk_resource res);
>> +
>> +int omap4iss_pipeline_pm_use(struct media_entity *entity, int use);
>> +
>> +int omap4iss_register_entities(struct platform_device *pdev,
>> +                            struct v4l2_device *v4l2_dev);
>> +void omap4iss_unregister_entities(struct platform_device *pdev);
>> +
>> +static inline enum v4l2_buf_type
>> +iss_pad_buffer_type(const struct v4l2_subdev *subdev, int pad)
>> +{
>> +     if (pad >= subdev->entity.num_pads)
>> +             return 0;
>
> Hmm. Is it just me or is this function unused?

Oops, yes. Will fix.

>
>> +     if (subdev->entity.pads[pad].flags & MEDIA_PAD_FL_SINK)
>> +             return V4L2_BUF_TYPE_VIDEO_OUTPUT;
>> +     else
>> +             return V4L2_BUF_TYPE_VIDEO_CAPTURE;
>> +}
>> +
>> +#endif /* _OMAP4_ISS_H_ */
>> diff --git a/drivers/media/video/omap4iss/iss_csi2.c b/drivers/media/video/omap4iss/iss_csi2.c
>> new file mode 100644
>> index 0000000..916d5ef
>> --- /dev/null
>> +++ b/drivers/media/video/omap4iss/iss_csi2.c
>> @@ -0,0 +1,1324 @@
>> +/*
>> + * iss_csi2.c
>> + *
>> + * TI OMAP4 ISS - CSI PHY module
>> + *
>> + * Copyright (C) 2011 Texas Instruments, Inc.
>> + *
>> + * Contacts: Sergio Aguirre <saaguirre@ti.com>
>> + *
>> + * 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.
>> + *
>> + * This program is distributed in the hope that it will be useful, but
>> + * WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write to the Free Software
>> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
>> + * 02110-1301 USA
>> + */
>> +#include <linux/delay.h>
>> +#include <media/v4l2-common.h>
>> +#include <linux/v4l2-mediabus.h>
>> +#include <linux/mm.h>
>> +
>> +#include "iss.h"
>> +#include "iss_regs.h"
>> +#include "iss_csi2.h"
>> +
>> +/*
>> + * csi2_if_enable - Enable CSI2 Receiver interface.
>> + * @enable: enable flag
>> + *
>> + */
>> +static void csi2_if_enable(struct iss_csi2_device *csi2, u8 enable)
>> +{
>> +     struct iss_csi2_ctrl_cfg *currctrl = &csi2->ctrl;
>> +
>> +     writel((readl(csi2->regs1 + CSI2_CTRL) & ~CSI2_CTRL_IF_EN) |
>> +             (enable ? CSI2_CTRL_IF_EN : 0),
>> +             csi2->regs1 + CSI2_CTRL);
>> +
>> +     currctrl->if_enable = enable;
>> +}
>> +
>> +/*
>> + * csi2_recv_config - CSI2 receiver module configuration.
>> + * @currctrl: iss_csi2_ctrl_cfg structure
>> + *
>> + */
>> +static void csi2_recv_config(struct iss_csi2_device *csi2,
>> +                          struct iss_csi2_ctrl_cfg *currctrl)
>> +{
>> +     u32 reg;
>> +
>> +     reg = readl(csi2->regs1 + CSI2_CTRL);
>> +
>> +     if (currctrl->frame_mode)
>> +             reg |= CSI2_CTRL_FRAME;
>> +     else
>> +             reg &= ~CSI2_CTRL_FRAME;
>> +
>> +     if (currctrl->vp_clk_enable)
>> +             reg |= CSI2_CTRL_VP_CLK_EN;
>> +     else
>> +             reg &= ~CSI2_CTRL_VP_CLK_EN;
>> +
>> +     if (currctrl->vp_only_enable)
>> +             reg |= CSI2_CTRL_VP_ONLY_EN;
>> +     else
>> +             reg &= ~CSI2_CTRL_VP_ONLY_EN;
>> +
>> +     reg &= ~CSI2_CTRL_VP_OUT_CTRL_MASK;
>> +     reg |= currctrl->vp_out_ctrl << CSI2_CTRL_VP_OUT_CTRL_SHIFT;
>> +
>> +     if (currctrl->ecc_enable)
>> +             reg |= CSI2_CTRL_ECC_EN;
>> +     else
>> +             reg &= ~CSI2_CTRL_ECC_EN;
>> +
>> +     /*
>> +      * Set MFlag assertion boundaries to:
>> +      * Low: 4/8 of FIFO size
>> +      * High: 6/8 of FIFO size
>> +      */
>> +     reg &= ~(CSI2_CTRL_MFLAG_LEVH_MASK | CSI2_CTRL_MFLAG_LEVL_MASK);
>> +     reg |= (2 << CSI2_CTRL_MFLAG_LEVH_SHIFT) |
>> +            (4 << CSI2_CTRL_MFLAG_LEVL_SHIFT);
>> +
>> +     /* Generation of 16x64-bit bursts (Recommended) */
>> +     reg |= CSI2_CTRL_BURST_SIZE_EXPAND;
>> +
>> +     /* Do Non-Posted writes (Recommended) */
>> +     reg |= CSI2_CTRL_NON_POSTED_WRITE;
>> +
>> +     /*
>> +      * Enforce Little endian for all formats, including:
>> +      * YUV4:2:2 8-bit and YUV4:2:0 Legacy
>> +      */
>> +     reg |= CSI2_CTRL_ENDIANNESS;
>> +
>> +     writel(reg, csi2->regs1 + CSI2_CTRL);
>
> Is this register accessed elsewhere? If not, you could skip reading it and
> just write the value there. This was very common in the original OMAP 3 ISP
> driver. :)

Right. Will fix.

>
>> +}
>> +
>> +static const unsigned int csi2_input_fmts[] = {
>> +     V4L2_MBUS_FMT_SGRBG10_1X10,
>> +     V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8,
>> +     V4L2_MBUS_FMT_SRGGB10_1X10,
>> +     V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8,
>> +     V4L2_MBUS_FMT_SBGGR10_1X10,
>> +     V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8,
>> +     V4L2_MBUS_FMT_SGBRG10_1X10,
>> +     V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8,
>> +     V4L2_MBUS_FMT_UYVY8_1X16,
>> +     V4L2_MBUS_FMT_YUYV8_1X16,
>> +};
>> +
>> +/* To set the format on the CSI2 requires a mapping function that takes
>> + * the following inputs:
>> + * - 3 different formats (at this time)
>> + * - 2 destinations (mem, vp+mem) (vp only handled separately)
>> + * - 2 decompression options (on, off)
>> + * Output should be CSI2 frame format code
>> + * Array indices as follows: [format][dest][decompr]
>> + * Not all combinations are valid. 0 means invalid.
>> + */
>> +static const u16 __csi2_fmt_map[][2][2] = {
>> +     /* RAW10 formats */
>> +     {
>> +             /* Output to memory */
>> +             {
>> +                     /* No DPCM decompression */
>> +                     CSI2_PIX_FMT_RAW10_EXP16,
>> +                     /* DPCM decompression */
>> +                     0,
>> +             },
>> +             /* Output to both */
>> +             {
>> +                     /* No DPCM decompression */
>> +                     CSI2_PIX_FMT_RAW10_EXP16_VP,
>> +                     /* DPCM decompression */
>> +                     0,
>> +             },
>> +     },
>> +     /* RAW10 DPCM8 formats */
>> +     {
>> +             /* Output to memory */
>> +             {
>> +                     /* No DPCM decompression */
>> +                     CSI2_USERDEF_8BIT_DATA1,
>> +                     /* DPCM decompression */
>> +                     CSI2_USERDEF_8BIT_DATA1_DPCM10,
>> +             },
>> +             /* Output to both */
>> +             {
>> +                     /* No DPCM decompression */
>> +                     CSI2_PIX_FMT_RAW8_VP,
>> +                     /* DPCM decompression */
>> +                     CSI2_USERDEF_8BIT_DATA1_DPCM10_VP,
>> +             },
>> +     },
>> +     /* YUV422 formats */
>> +     {
>> +             /* Output to memory */
>> +             {
>> +                     /* No DPCM decompression */
>> +                     CSI2_PIX_FMT_YUV422_8BIT,
>> +                     /* DPCM decompression */
>> +                     0,
>> +             },
>> +             /* Output to both */
>> +             {
>> +                     /* No DPCM decompression */
>> +                     CSI2_PIX_FMT_YUV422_8BIT_VP,
>> +                     /* DPCM decompression */
>> +                     0,
>> +             },
>> +     },
>> +};
>> +
>> +/*
>> + * csi2_ctx_map_format - Map CSI2 sink media bus format to CSI2 format ID
>> + * @csi2: ISS CSI2 device
>> + *
>> + * Returns CSI2 physical format id
>> + */
>> +static u16 csi2_ctx_map_format(struct iss_csi2_device *csi2)
>> +{
>> +     const struct v4l2_mbus_framefmt *fmt = &csi2->formats[CSI2_PAD_SINK];
>> +     int fmtidx, destidx;
>> +
>> +     switch (fmt->code) {
>> +     case V4L2_MBUS_FMT_SGRBG10_1X10:
>> +     case V4L2_MBUS_FMT_SRGGB10_1X10:
>> +     case V4L2_MBUS_FMT_SBGGR10_1X10:
>> +     case V4L2_MBUS_FMT_SGBRG10_1X10:
>> +             fmtidx = 0;
>> +             break;
>> +     case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8:
>> +     case V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8:
>> +     case V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8:
>> +     case V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8:
>> +             fmtidx = 1;
>> +             break;
>> +     case V4L2_MBUS_FMT_UYVY8_1X16:
>> +     case V4L2_MBUS_FMT_YUYV8_1X16:
>> +             fmtidx = 2;
>> +             break;
>> +     default:
>> +             WARN(1, KERN_ERR "CSI2: pixel format %08x unsupported!\n",
>> +                  fmt->code);
>> +             return 0;
>> +     }
>> +
>> +     if (!(csi2->output & CSI2_OUTPUT_IPIPEIF) &&
>> +         !(csi2->output & CSI2_OUTPUT_MEMORY)) {
>> +             /* Neither output enabled is a valid combination */
>> +             return CSI2_PIX_FMT_OTHERS;
>> +     }
>> +
>> +     /* If we need to skip frames at the beginning of the stream disable the
>> +      * video port to avoid sending the skipped frames to the IPIPEIF.
>> +      */
>> +     destidx = csi2->frame_skip ? 0 : !!(csi2->output & CSI2_OUTPUT_IPIPEIF);
>> +
>> +     return __csi2_fmt_map[fmtidx][destidx][csi2->dpcm_decompress];
>> +}
>> +
>> +/*
>> + * csi2_set_outaddr - Set memory address to save output image
>> + * @csi2: Pointer to ISS CSI2a device.
>> + * @addr: 32-bit memory address aligned on 32 byte boundary.
>> + *
>> + * Sets the memory address where the output will be saved.
>> + *
>> + * Returns 0 if successful, or -EINVAL if the address is not in the 32 byte
>> + * boundary.
>> + */
>> +static void csi2_set_outaddr(struct iss_csi2_device *csi2, u32 addr)
>> +{
>> +     struct iss_csi2_ctx_cfg *ctx = &csi2->contexts[0];
>> +
>> +     ctx->ping_addr = addr;
>> +     ctx->pong_addr = addr;
>> +     writel(ctx->ping_addr,
>> +            csi2->regs1 + CSI2_CTX_PING_ADDR(ctx->ctxnum));
>> +     writel(ctx->pong_addr,
>> +            csi2->regs1 + CSI2_CTX_PONG_ADDR(ctx->ctxnum));
>> +}
>> +
>> +/*
>> + * is_usr_def_mapping - Checks whether USER_DEF_MAPPING should
>> + *                   be enabled by CSI2.
>> + * @format_id: mapped format id
>> + *
>> + */
>> +static inline int is_usr_def_mapping(u32 format_id)
>> +{
>> +     return (format_id & 0x40) ? 1 : 0;
>> +}
>> +
>> +/*
>> + * csi2_ctx_enable - Enable specified CSI2 context
>> + * @ctxnum: Context number, valid between 0 and 7 values.
>> + * @enable: enable
>> + *
>> + */
>> +static void csi2_ctx_enable(struct iss_csi2_device *csi2, u8 ctxnum, u8 enable)
>> +{
>> +     struct iss_csi2_ctx_cfg *ctx = &csi2->contexts[ctxnum];
>> +     u32 reg;
>> +
>> +     reg = readl(csi2->regs1 + CSI2_CTX_CTRL1(ctxnum));
>> +
>> +     if (enable) {
>> +             unsigned int skip = 0;
>> +
>> +             if (csi2->frame_skip)
>> +                     skip = csi2->frame_skip;
>> +             else if (csi2->output & CSI2_OUTPUT_MEMORY)
>> +                     skip = 1;
>> +
>> +             reg &= ~CSI2_CTX_CTRL1_COUNT_MASK;
>> +             reg |= CSI2_CTX_CTRL1_COUNT_UNLOCK
>> +                 |  (skip << CSI2_CTX_CTRL1_COUNT_SHIFT)
>> +                 |  CSI2_CTX_CTRL1_CTX_EN;
>> +     } else {
>> +             reg &= ~CSI2_CTX_CTRL1_CTX_EN;
>> +     }
>> +
>> +     writel(reg, csi2->regs1 + CSI2_CTX_CTRL1(ctxnum));
>> +     ctx->enabled = enable;
>> +}
>> +
>> +/*
>> + * csi2_ctx_config - CSI2 context configuration.
>> + * @ctx: context configuration
>> + *
>> + */
>> +static void csi2_ctx_config(struct iss_csi2_device *csi2,
>> +                         struct iss_csi2_ctx_cfg *ctx)
>> +{
>> +     u32 reg;
>> +
>> +     /* Set up CSI2_CTx_CTRL1 */
>> +     reg = readl(csi2->regs1 + CSI2_CTX_CTRL1(ctx->ctxnum));
>>
>> +     if (ctx->eof_enabled)
>> +             reg |= CSI2_CTX_CTRL1_EOF_EN;
>> +     else
>> +             reg &= ~CSI2_CTX_CTRL1_EOF_EN;
>> +
>> +     if (ctx->eol_enabled)
>> +             reg |= CSI2_CTX_CTRL1_EOL_EN;
>> +     else
>> +             reg &= ~CSI2_CTX_CTRL1_EOL_EN;
>> +
>> +     if (ctx->checksum_enabled)
>> +             reg |= CSI2_CTX_CTRL1_CS_EN;
>> +     else
>> +             reg &= ~CSI2_CTX_CTRL1_CS_EN;
>> +
>> +     writel(reg, csi2->regs1 + CSI2_CTX_CTRL1(ctx->ctxnum));
>> +
>> +     /* Set up CSI2_CTx_CTRL2 */
>> +     reg = readl(csi2->regs1 + CSI2_CTX_CTRL2(ctx->ctxnum));
>
> CTX_CTRL2 is only used in this function. I don't think you'd need to read it
> back. How about other CTX_CTRL regs?

Right. Will fix.

>
>> +     reg &= ~(CSI2_CTX_CTRL2_VIRTUAL_ID_MASK);
>> +     reg |= ctx->virtual_id << CSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT;
>> +
>> +     reg &= ~(CSI2_CTX_CTRL2_FORMAT_MASK);
>> +     reg |= ctx->format_id << CSI2_CTX_CTRL2_FORMAT_SHIFT;
>> +
>> +     if (ctx->dpcm_decompress) {
>> +             if (ctx->dpcm_predictor)
>> +                     reg |= CSI2_CTX_CTRL2_DPCM_PRED;
>> +             else
>> +                     reg &= ~CSI2_CTX_CTRL2_DPCM_PRED;
>> +     }
>> +
>> +     if (is_usr_def_mapping(ctx->format_id)) {
>> +             reg &= ~CSI2_CTX_CTRL2_USER_DEF_MAP_MASK;
>> +             reg |= 2 << CSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT;
>> +     }
>> +
>> +     writel(reg, csi2->regs1 + CSI2_CTX_CTRL2(ctx->ctxnum));
>> +
>> +     /* Set up CSI2_CTx_CTRL3 */
>> +     reg = readl(csi2->regs1 + CSI2_CTX_CTRL3(ctx->ctxnum));
>> +     reg &= ~(CSI2_CTX_CTRL3_ALPHA_MASK);
>> +     reg |= (ctx->alpha << CSI2_CTX_CTRL3_ALPHA_SHIFT);
>> +
>> +     writel(reg, csi2->regs1 + CSI2_CTX_CTRL3(ctx->ctxnum));
>> +
>> +     /* Set up CSI2_CTx_DAT_OFST */
>> +     reg = readl(csi2->regs1 + CSI2_CTX_DAT_OFST(ctx->ctxnum));
>> +     reg &= ~CSI2_CTX_DAT_OFST_MASK;
>> +     reg |= ctx->data_offset;
>> +     writel(reg, csi2->regs1 + CSI2_CTX_DAT_OFST(ctx->ctxnum));
>> +
>> +     writel(ctx->ping_addr,
>> +                    csi2->regs1 + CSI2_CTX_PING_ADDR(ctx->ctxnum));
>> +
>> +     writel(ctx->pong_addr,
>> +                    csi2->regs1 + CSI2_CTX_PONG_ADDR(ctx->ctxnum));
>> +}
>> +
>> +/*
>> + * csi2_timing_config - CSI2 timing configuration.
>> + * @timing: csi2_timing_cfg structure
>> + */
>> +static void csi2_timing_config(struct iss_csi2_device *csi2,
>> +                            struct iss_csi2_timing_cfg *timing)
>> +{
>> +     u32 reg;
>> +
>> +     reg = readl(csi2->regs1 + CSI2_TIMING);
>> +
>> +     if (timing->force_rx_mode)
>> +             reg |= CSI2_TIMING_FORCE_RX_MODE_IO1;
>> +     else
>> +             reg &= ~CSI2_TIMING_FORCE_RX_MODE_IO1;
>> +
>> +     if (timing->stop_state_16x)
>> +             reg |= CSI2_TIMING_STOP_STATE_X16_IO1;
>> +     else
>> +             reg &= ~CSI2_TIMING_STOP_STATE_X16_IO1;
>> +
>> +     if (timing->stop_state_4x)
>> +             reg |= CSI2_TIMING_STOP_STATE_X4_IO1;
>> +     else
>> +             reg &= ~CSI2_TIMING_STOP_STATE_X4_IO1;
>> +
>> +     reg &= ~CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK;
>> +     reg |= timing->stop_state_counter <<
>> +            CSI2_TIMING_STOP_STATE_COUNTER_IO1_SHIFT;
>> +
>> +     writel(reg, csi2->regs1 + CSI2_TIMING);
>> +}
>> +
>> +/*
>> + * csi2_irq_ctx_set - Enables CSI2 Context IRQs.
>> + * @enable: Enable/disable CSI2 Context interrupts
>> + */
>> +static void csi2_irq_ctx_set(struct iss_csi2_device *csi2, int enable)
>> +{
>> +     u32 reg = CSI2_CTX_IRQ_FE;
>> +     int i;
>> +
>> +     if (csi2->use_fs_irq)
>> +             reg |= CSI2_CTX_IRQ_FS;
>> +
>> +     for (i = 0; i < 8; i++) {
>
> 8 == number of contexts?

Yes. This loops from 0 to 7.

Are you implying that I need to add a define to this?

>
>> +             writel(reg, csi2->regs1 + CSI2_CTX_IRQSTATUS(i));
>> +             if (enable)
>> +                     writel(readl(csi2->regs1 + CSI2_CTX_IRQENABLE(i)) | reg,
>> +                             csi2->regs1 + CSI2_CTX_IRQENABLE(i));
>> +             else
>> +                     writel(readl(csi2->regs1 + CSI2_CTX_IRQENABLE(i)) &
>> +                             ~reg,
>> +                             csi2->regs1 + CSI2_CTX_IRQENABLE(i));
>> +     }
>> +}
>> +
>> +/*
>> + * csi2_irq_complexio1_set - Enables CSI2 ComplexIO IRQs.
>> + * @enable: Enable/disable CSI2 ComplexIO #1 interrupts
>> + */
>> +static void csi2_irq_complexio1_set(struct iss_csi2_device *csi2, int enable)
>> +{
>> +     u32 reg;
>> +     reg = CSI2_COMPLEXIO_IRQ_STATEALLULPMEXIT |
>> +             CSI2_COMPLEXIO_IRQ_STATEALLULPMENTER |
>> +             CSI2_COMPLEXIO_IRQ_STATEULPM5 |
>> +             CSI2_COMPLEXIO_IRQ_ERRCONTROL5 |
>> +             CSI2_COMPLEXIO_IRQ_ERRESC5 |
>> +             CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS5 |
>> +             CSI2_COMPLEXIO_IRQ_ERRSOTHS5 |
>> +             CSI2_COMPLEXIO_IRQ_STATEULPM4 |
>> +             CSI2_COMPLEXIO_IRQ_ERRCONTROL4 |
>> +             CSI2_COMPLEXIO_IRQ_ERRESC4 |
>> +             CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS4 |
>> +             CSI2_COMPLEXIO_IRQ_ERRSOTHS4 |
>> +             CSI2_COMPLEXIO_IRQ_STATEULPM3 |
>> +             CSI2_COMPLEXIO_IRQ_ERRCONTROL3 |
>> +             CSI2_COMPLEXIO_IRQ_ERRESC3 |
>> +             CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS3 |
>> +             CSI2_COMPLEXIO_IRQ_ERRSOTHS3 |
>> +             CSI2_COMPLEXIO_IRQ_STATEULPM2 |
>> +             CSI2_COMPLEXIO_IRQ_ERRCONTROL2 |
>> +             CSI2_COMPLEXIO_IRQ_ERRESC2 |
>> +             CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS2 |
>> +             CSI2_COMPLEXIO_IRQ_ERRSOTHS2 |
>> +             CSI2_COMPLEXIO_IRQ_STATEULPM1 |
>> +             CSI2_COMPLEXIO_IRQ_ERRCONTROL1 |
>> +             CSI2_COMPLEXIO_IRQ_ERRESC1 |
>> +             CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS1 |
>> +             CSI2_COMPLEXIO_IRQ_ERRSOTHS1;
>> +     writel(reg, csi2->regs1 + CSI2_COMPLEXIO_IRQSTATUS);
>> +     if (enable)
>> +             reg |= readl(csi2->regs1 + CSI2_COMPLEXIO_IRQENABLE);
>> +     else
>> +             reg = 0;
>> +     writel(reg, csi2->regs1 + CSI2_COMPLEXIO_IRQENABLE);
>> +}
>> +
>> +/*
>> + * csi2_irq_status_set - Enables CSI2 Status IRQs.
>> + * @enable: Enable/disable CSI2 Status interrupts
>> + */
>> +static void csi2_irq_status_set(struct iss_csi2_device *csi2, int enable)
>> +{
>> +     u32 reg;
>> +     reg = CSI2_IRQ_OCP_ERR |
>> +             CSI2_IRQ_SHORT_PACKET |
>> +             CSI2_IRQ_ECC_CORRECTION |
>> +             CSI2_IRQ_ECC_NO_CORRECTION |
>> +             CSI2_IRQ_COMPLEXIO_ERR |
>> +             CSI2_IRQ_FIFO_OVF |
>> +             CSI2_IRQ_CONTEXT0;
>> +     writel(reg, csi2->regs1 + CSI2_IRQSTATUS);
>> +     if (enable)
>> +             reg |= readl(csi2->regs1 + CSI2_IRQENABLE);
>> +     else
>> +             reg = 0;
>> +
>> +     writel(reg, csi2->regs1 + CSI2_IRQENABLE);
>> +}
>> +
>> +/*
>> + * omap4iss_csi2_reset - Resets the CSI2 module.
>> + *
>> + * Must be called with the phy lock held.
>> + *
>> + * Returns 0 if successful, or -EBUSY if power command didn't respond.
>> + */
>> +int omap4iss_csi2_reset(struct iss_csi2_device *csi2)
>> +{
>> +     u8 soft_reset_retries = 0;
>> +     u32 reg;
>> +     int i;
>> +
>> +     if (!csi2->available)
>> +             return -ENODEV;
>> +
>> +     if (csi2->phy->phy_in_use)
>> +             return -EBUSY;
>> +
>> +     writel(readl(csi2->regs1 + CSI2_SYSCONFIG) |
>> +             CSI2_SYSCONFIG_SOFT_RESET,
>> +             csi2->regs1 + CSI2_SYSCONFIG);
>> +
>> +     do {
>> +             reg = readl(csi2->regs1 + CSI2_SYSSTATUS) &
>> +                                 CSI2_SYSSTATUS_RESET_DONE;
>> +             if (reg == CSI2_SYSSTATUS_RESET_DONE)
>> +                     break;
>> +             soft_reset_retries++;
>> +             if (soft_reset_retries < 5)
>> +                     udelay(100);
>
> How about usleep_range() here? Or omit such a long busyloop. Hardware
> typically resets quite fast.

I guess i'll try to fine-tune this then.

>
>> +     } while (soft_reset_retries < 5);
>> +
>> +     if (soft_reset_retries == 5) {
>> +             printk(KERN_ERR "CSI2: Soft reset try count exceeded!\n");
>> +             return -EBUSY;
>> +     }
>> +
>> +     writel(readl(csi2->regs1 + CSI2_COMPLEXIO_CFG) |
>> +             CSI2_COMPLEXIO_CFG_RESET_CTRL,
>> +             csi2->regs1 + CSI2_COMPLEXIO_CFG);
>> +
>> +     i = 100;
>> +     do {
>> +             reg = readl(csi2->phy->phy_regs + REGISTER1)
>> +                 & REGISTER1_RESET_DONE_CTRLCLK;
>> +             if (reg == REGISTER1_RESET_DONE_CTRLCLK)
>> +                     break;
>> +             udelay(100);
>
> Same here.

Ok. Will do too.

>
>> +     } while (--i > 0);
>> +
>> +     if (i == 0) {
>> +             printk(KERN_ERR
>> +                    "CSI2: Reset for CSI2_96M_FCLK domain Failed!\n");
>> +             return -EBUSY;
>> +     }
>> +
>> +     writel((readl(csi2->regs1 + CSI2_SYSCONFIG) &
>> +             ~(CSI2_SYSCONFIG_MSTANDBY_MODE_MASK |
>> +               CSI2_SYSCONFIG_AUTO_IDLE)) |
>> +             CSI2_SYSCONFIG_MSTANDBY_MODE_NO,
>> +             csi2->regs1 + CSI2_SYSCONFIG);
>> +
>> +     return 0;
>> +}
>> +
>> +static int csi2_configure(struct iss_csi2_device *csi2)
>> +{
>> +     const struct iss_v4l2_subdevs_group *pdata;
>> +     struct iss_csi2_timing_cfg *timing = &csi2->timing[0];
>> +     struct v4l2_subdev *sensor;
>> +     struct media_pad *pad;
>> +
>> +     /*
>> +      * CSI2 fields that can be updated while the context has
>> +      * been enabled or the interface has been enabled are not
>> +      * updated dynamically currently. So we do not allow to
>> +      * reconfigure if either has been enabled
>> +      */
>> +     if (csi2->contexts[0].enabled || csi2->ctrl.if_enable)
>> +             return -EBUSY;
>> +
>> +     pad = media_entity_remote_source(&csi2->pads[CSI2_PAD_SINK]);
>> +     sensor = media_entity_to_v4l2_subdev(pad->entity);
>> +     pdata = sensor->host_priv;
>> +
>> +     csi2->frame_skip = 0;
>> +     v4l2_subdev_call(sensor, sensor, g_skip_frames, &csi2->frame_skip);
>> +
>> +     csi2->ctrl.vp_out_ctrl = pdata->bus.csi2.vpclk_div;
>> +     csi2->ctrl.frame_mode = ISS_CSI2_FRAME_IMMEDIATE;
>> +     csi2->ctrl.ecc_enable = pdata->bus.csi2.crc;
>> +
>> +     timing->force_rx_mode = 1;
>> +     timing->stop_state_16x = 1;
>> +     timing->stop_state_4x = 1;
>> +     timing->stop_state_counter = 0x1FF;
>> +
>> +     /*
>> +      * The CSI2 receiver can't do any format conversion except DPCM
>> +      * decompression, so every set_format call configures both pads
>> +      * and enables DPCM decompression as a special case:
>> +      */
>> +     if (csi2->formats[CSI2_PAD_SINK].code !=
>> +         csi2->formats[CSI2_PAD_SOURCE].code)
>> +             csi2->dpcm_decompress = true;
>> +     else
>> +             csi2->dpcm_decompress = false;
>> +
>> +     csi2->contexts[0].format_id = csi2_ctx_map_format(csi2);
>> +
>> +     if (csi2->video_out.bpl_padding == 0)
>> +             csi2->contexts[0].data_offset = 0;
>> +     else
>> +             csi2->contexts[0].data_offset = csi2->video_out.bpl_value;
>> +
>> +     /*
>> +      * Enable end of frame and end of line signals generation for
>> +      * context 0. These signals are generated from CSI2 receiver to
>> +      * qualify the last pixel of a frame and the last pixel of a line.
>> +      * Without enabling the signals CSI2 receiver writes data to memory
>> +      * beyond buffer size and/or data line offset is not handled correctly.
>> +      */
>> +     csi2->contexts[0].eof_enabled = 1;
>> +     csi2->contexts[0].eol_enabled = 1;
>> +
>> +     csi2_irq_complexio1_set(csi2, 1);
>> +     csi2_irq_ctx_set(csi2, 1);
>> +     csi2_irq_status_set(csi2, 1);
>> +
>> +     /* Set configuration (timings, format and links) */
>> +     csi2_timing_config(csi2, timing);
>> +     csi2_recv_config(csi2, &csi2->ctrl);
>> +     csi2_ctx_config(csi2, &csi2->contexts[0]);
>> +
>> +     return 0;
>> +}
>> +
>> +/*
>> + * csi2_print_status - Prints CSI2 debug information.
>> + */
>> +#define CSI2_PRINT_REGISTER(iss, regs, name)\
>> +     dev_dbg(iss->dev, "###CSI2 " #name "=0x%08x\n", \
>> +             readl(regs + CSI2_##name))
>> +
>> +static void csi2_print_status(struct iss_csi2_device *csi2)
>> +{
>> +     struct iss_device *iss = csi2->iss;
>> +
>> +     if (!csi2->available)
>> +             return;
>> +
>> +     dev_dbg(iss->dev, "-------------CSI2 Register dump-------------\n");
>> +
>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, SYSCONFIG);
>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, SYSSTATUS);
>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, IRQENABLE);
>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, IRQSTATUS);
>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, CTRL);
>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, DBG_H);
>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, COMPLEXIO_CFG);
>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, COMPLEXIO_IRQSTATUS);
>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, SHORT_PACKET);
>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, COMPLEXIO_IRQENABLE);
>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, DBG_P);
>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, TIMING);
>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_CTRL1(0));
>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_CTRL2(0));
>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_DAT_OFST(0));
>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_PING_ADDR(0));
>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_PONG_ADDR(0));
>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_IRQENABLE(0));
>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_IRQSTATUS(0));
>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_CTRL3(0));
>> +
>> +     dev_dbg(iss->dev, "--------------------------------------------\n");
>
> _If_ this is user-triggered, you might want to consider using debugfs for
> the same. Just FYI.

Ok. I'll see what can I do.

>
>> +}
>> +
>> +/* -----------------------------------------------------------------------------
>> + * Interrupt handling
>> + */
>> +
>> +/*
>> + * csi2_isr_buffer - Does buffer handling at end-of-frame
>> + * when writing to memory.
>> + */
>> +static void csi2_isr_buffer(struct iss_csi2_device *csi2)
>> +{
>> +     struct iss_buffer *buffer;
>> +
>> +     csi2_ctx_enable(csi2, 0, 0);
>> +
>> +     buffer = omap4iss_video_buffer_next(&csi2->video_out, 0);
>> +
>> +     /*
>> +      * Let video queue operation restart engine if there is an underrun
>> +      * condition.
>> +      */
>> +     if (buffer == NULL)
>> +             return;
>> +
>> +     csi2_set_outaddr(csi2, buffer->iss_addr);
>> +     csi2_ctx_enable(csi2, 0, 1);
>> +}
>> +
>> +static void csi2_isr_ctx(struct iss_csi2_device *csi2,
>> +                      struct iss_csi2_ctx_cfg *ctx)
>> +{
>> +     unsigned int n = ctx->ctxnum;
>> +     u32 status;
>> +
>> +     status = readl(csi2->regs1 + CSI2_CTX_IRQSTATUS(n));
>> +     writel(status, csi2->regs1 + CSI2_CTX_IRQSTATUS(n));
>> +
>> +     /* Propagate frame number */
>> +     if (status & CSI2_CTX_IRQ_FS) {
>> +             struct iss_pipeline *pipe =
>> +                                  to_iss_pipeline(&csi2->subdev.entity);
>> +             if (pipe->do_propagation)
>> +                     atomic_inc(&pipe->frame_number);
>> +     }
>> +
>> +     if (!(status & CSI2_CTX_IRQ_FE))
>> +             return;
>> +
>> +     /* Skip interrupts until we reach the frame skip count. The CSI2 will be
>> +      * automatically disabled, as the frame skip count has been programmed
>> +      * in the CSI2_CTx_CTRL1::COUNT field, so reenable it.
>> +      *
>> +      * It would have been nice to rely on the FRAME_NUMBER interrupt instead
>> +      * but it turned out that the interrupt is only generated when the CSI2
>> +      * writes to memory (the CSI2_CTx_CTRL1::COUNT field is decreased
>> +      * correctly and reaches 0 when data is forwarded to the video port only
>> +      * but no interrupt arrives). Maybe a CSI2 hardware bug.
>> +      */
>> +     if (csi2->frame_skip) {
>> +             csi2->frame_skip--;
>> +             if (csi2->frame_skip == 0) {
>> +                     ctx->format_id = csi2_ctx_map_format(csi2);
>> +                     csi2_ctx_config(csi2, ctx);
>
> Is configuration of the context needed elsewhere than in streamon? What
> changes while streaming?

Nothing I think...

Same thing is done for OMAP3, so I tried to keep the driver as similar
as possible.

I'll try removing this then.

>
>> +                     csi2_ctx_enable(csi2, n, 1);
>> +             }
>> +             return;
>> +     }
>> +
>> +     if (csi2->output & CSI2_OUTPUT_MEMORY)
>> +             csi2_isr_buffer(csi2);
>> +}
>> +
>> +/*
>> + * omap4iss_csi2_isr - CSI2 interrupt handling.
>> + *
>> + * Return -EIO on Transmission error
>> + */
>> +int omap4iss_csi2_isr(struct iss_csi2_device *csi2)
>> +{
>> +     u32 csi2_irqstatus, cpxio1_irqstatus;
>> +     struct iss_device *iss = csi2->iss;
>> +     int retval = 0;
>> +
>> +     if (!csi2->available)
>> +             return -ENODEV;
>> +
>> +     csi2_irqstatus = readl(csi2->regs1 + CSI2_IRQSTATUS);
>> +     writel(csi2_irqstatus, csi2->regs1 + CSI2_IRQSTATUS);
>> +
>> +     /* Failure Cases */
>> +     if (csi2_irqstatus & CSI2_IRQ_COMPLEXIO_ERR) {
>> +             cpxio1_irqstatus = readl(csi2->regs1 +
>> +                                      CSI2_COMPLEXIO_IRQSTATUS);
>> +             writel(cpxio1_irqstatus,
>> +                     csi2->regs1 + CSI2_COMPLEXIO_IRQSTATUS);
>> +             dev_dbg(iss->dev, "CSI2: ComplexIO Error IRQ "
>> +                     "%x\n", cpxio1_irqstatus);
>> +             retval = -EIO;
>> +     }
>> +
>> +     if (csi2_irqstatus & (CSI2_IRQ_OCP_ERR |
>> +                           CSI2_IRQ_SHORT_PACKET |
>> +                           CSI2_IRQ_ECC_NO_CORRECTION |
>> +                           CSI2_IRQ_COMPLEXIO_ERR |
>> +                           CSI2_IRQ_FIFO_OVF)) {
>> +             dev_dbg(iss->dev, "CSI2 Err:"
>> +                     " OCP:%d,"
>> +                     " Short_pack:%d,"
>> +                     " ECC:%d,"
>> +                     " CPXIO:%d,"
>> +                     " FIFO_OVF:%d,"
>> +                     "\n",
>> +                     (csi2_irqstatus &
>> +                      CSI2_IRQ_OCP_ERR) ? 1 : 0,
>> +                     (csi2_irqstatus &
>> +                      CSI2_IRQ_SHORT_PACKET) ? 1 : 0,
>> +                     (csi2_irqstatus &
>> +                      CSI2_IRQ_ECC_NO_CORRECTION) ? 1 : 0,
>> +                     (csi2_irqstatus &
>> +                      CSI2_IRQ_COMPLEXIO_ERR) ? 1 : 0,
>> +                     (csi2_irqstatus &
>> +                      CSI2_IRQ_FIFO_OVF) ? 1 : 0);
>> +             retval = -EIO;
>> +     }
>> +
>> +     if (omap4iss_module_sync_is_stopping(&csi2->wait, &csi2->stopping))
>> +             return 0;
>> +
>> +     /* Successful cases */
>> +     if (csi2_irqstatus & CSI2_IRQ_CONTEXT0)
>> +             csi2_isr_ctx(csi2, &csi2->contexts[0]);
>> +
>> +     if (csi2_irqstatus & CSI2_IRQ_ECC_CORRECTION)
>> +             dev_dbg(iss->dev, "CSI2: ECC correction done\n");
>> +
>> +     return retval;
>> +}
>>
>> +/* -----------------------------------------------------------------------------
>> + * ISS video operations
>> + */
>> +
>> +/*
>> + * csi2_queue - Queues the first buffer when using memory output
>> + * @video: The video node
>> + * @buffer: buffer to queue
>> + */
>> +static int csi2_queue(struct iss_video *video, struct iss_buffer *buffer)
>> +{
>> +     struct iss_device *iss = video->iss;
>> +     struct iss_csi2_device *csi2 = &iss->csi2a;
>> +
>> +     csi2_set_outaddr(csi2, buffer->iss_addr);
>> +
>> +     /*
>> +      * If streaming was enabled before there was a buffer queued
>> +      * or underrun happened in the ISR, the hardware was not enabled
>> +      * and DMA queue flag ISS_VIDEO_DMAQUEUE_UNDERRUN is still set.
>> +      * Enable it now.
>> +      */
>> +     if (csi2->video_out.dmaqueue_flags & ISS_VIDEO_DMAQUEUE_UNDERRUN) {
>> +             /* Enable / disable context 0 and IRQs */
>> +             csi2_if_enable(csi2, 1);
>> +             csi2_ctx_enable(csi2, 0, 1);
>> +             iss_video_dmaqueue_flags_clr(&csi2->video_out);
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +static const struct iss_video_operations csi2_issvideo_ops = {
>> +     .queue = csi2_queue,
>> +};
>> +
>> +/* -----------------------------------------------------------------------------
>> + * V4L2 subdev operations
>> + */
>> +
>> +static struct v4l2_mbus_framefmt *
>> +__csi2_get_format(struct iss_csi2_device *csi2, struct v4l2_subdev_fh *fh,
>> +               unsigned int pad, enum v4l2_subdev_format_whence which)
>> +{
>> +     if (which == V4L2_SUBDEV_FORMAT_TRY)
>> +             return v4l2_subdev_get_try_format(fh, pad);
>> +     else
>> +             return &csi2->formats[pad];
>> +}
>> +
>> +static void
>> +csi2_try_format(struct iss_csi2_device *csi2, struct v4l2_subdev_fh *fh,
>> +             unsigned int pad, struct v4l2_mbus_framefmt *fmt,
>> +             enum v4l2_subdev_format_whence which)
>> +{
>> +     enum v4l2_mbus_pixelcode pixelcode;
>> +     struct v4l2_mbus_framefmt *format;
>> +     const struct iss_format_info *info;
>> +     unsigned int i;
>> +
>> +     switch (pad) {
>> +     case CSI2_PAD_SINK:
>> +             /* Clamp the width and height to valid range (1-8191). */
>> +             for (i = 0; i < ARRAY_SIZE(csi2_input_fmts); i++) {
>> +                     if (fmt->code == csi2_input_fmts[i])
>> +                             break;
>> +             }
>> +
>> +             /* If not found, use SGRBG10 as default */
>> +             if (i >= ARRAY_SIZE(csi2_input_fmts))
>> +                     fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10;
>> +
>> +             fmt->width = clamp_t(u32, fmt->width, 1, 8191);
>> +             fmt->height = clamp_t(u32, fmt->height, 1, 8191);
>> +             break;
>> +
>> +     case CSI2_PAD_SOURCE:
>> +             /* Source format same as sink format, except for DPCM
>> +              * compression.
>> +              */
>> +             pixelcode = fmt->code;
>> +             format = __csi2_get_format(csi2, fh, CSI2_PAD_SINK, which);
>> +             memcpy(fmt, format, sizeof(*fmt));
>> +
>> +             /*
>> +              * Only Allow DPCM decompression, and check that the
>> +              * pattern is preserved
>> +              */
>> +             info = omap4iss_video_format_info(fmt->code);
>> +             if (info->uncompressed == pixelcode)
>> +                     fmt->code = pixelcode;
>> +             break;
>> +     }
>> +
>> +     /* RGB, non-interlaced */
>> +     fmt->colorspace = V4L2_COLORSPACE_SRGB;
>> +     fmt->field = V4L2_FIELD_NONE;
>> +}
>> +
>> +/*
>> + * csi2_enum_mbus_code - Handle pixel format enumeration
>> + * @sd     : pointer to v4l2 subdev structure
>> + * @fh     : V4L2 subdev file handle
>> + * @code   : pointer to v4l2_subdev_mbus_code_enum structure
>> + * return -EINVAL or zero on success
>> + */
>> +static int csi2_enum_mbus_code(struct v4l2_subdev *sd,
>> +                            struct v4l2_subdev_fh *fh,
>> +                            struct v4l2_subdev_mbus_code_enum *code)
>> +{
>> +     struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
>> +     struct v4l2_mbus_framefmt *format;
>> +     const struct iss_format_info *info;
>> +
>> +     if (code->pad == CSI2_PAD_SINK) {
>> +             if (code->index >= ARRAY_SIZE(csi2_input_fmts))
>> +                     return -EINVAL;
>> +
>> +             code->code = csi2_input_fmts[code->index];
>> +     } else {
>> +             format = __csi2_get_format(csi2, fh, CSI2_PAD_SINK,
>> +                                        V4L2_SUBDEV_FORMAT_TRY);
>> +             switch (code->index) {
>> +             case 0:
>> +                     /* Passthrough sink pad code */
>> +                     code->code = format->code;
>> +                     break;
>> +             case 1:
>> +                     /* Uncompressed code */
>> +                     info = omap4iss_video_format_info(format->code);
>> +                     if (info->uncompressed == format->code)
>> +                             return -EINVAL;
>> +
>> +                     code->code = info->uncompressed;
>> +                     break;
>> +             default:
>> +                     return -EINVAL;
>> +             }
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +static int csi2_enum_frame_size(struct v4l2_subdev *sd,
>> +                             struct v4l2_subdev_fh *fh,
>> +                             struct v4l2_subdev_frame_size_enum *fse)
>> +{
>> +     struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
>> +     struct v4l2_mbus_framefmt format;
>> +
>> +     if (fse->index != 0)
>> +             return -EINVAL;
>> +
>> +     format.code = fse->code;
>> +     format.width = 1;
>> +     format.height = 1;
>> +     csi2_try_format(csi2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
>> +     fse->min_width = format.width;
>> +     fse->min_height = format.height;
>> +
>> +     if (format.code != fse->code)
>> +             return -EINVAL;
>> +
>> +     format.code = fse->code;
>> +     format.width = -1;
>> +     format.height = -1;
>> +     csi2_try_format(csi2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
>> +     fse->max_width = format.width;
>> +     fse->max_height = format.height;
>> +
>> +     return 0;
>> +}
>> +
>> +/*
>> + * csi2_get_format - Handle get format by pads subdev method
>> + * @sd : pointer to v4l2 subdev structure
>> + * @fh : V4L2 subdev file handle
>> + * @fmt: pointer to v4l2 subdev format structure
>> + * return -EINVAL or zero on success
>> + */
>> +static int csi2_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
>> +                        struct v4l2_subdev_format *fmt)
>> +{
>> +     struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
>> +     struct v4l2_mbus_framefmt *format;
>> +
>> +     format = __csi2_get_format(csi2, fh, fmt->pad, fmt->which);
>> +     if (format == NULL)
>> +             return -EINVAL;
>> +
>> +     fmt->format = *format;
>> +     return 0;
>> +}
>> +
>> +/*
>> + * csi2_set_format - Handle set format by pads subdev method
>> + * @sd : pointer to v4l2 subdev structure
>> + * @fh : V4L2 subdev file handle
>> + * @fmt: pointer to v4l2 subdev format structure
>> + * return -EINVAL or zero on success
>> + */
>> +static int csi2_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
>> +                        struct v4l2_subdev_format *fmt)
>> +{
>> +     struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
>> +     struct v4l2_mbus_framefmt *format;
>> +
>> +     format = __csi2_get_format(csi2, fh, fmt->pad, fmt->which);
>> +     if (format == NULL)
>> +             return -EINVAL;
>> +
>> +     csi2_try_format(csi2, fh, fmt->pad, &fmt->format, fmt->which);
>> +     *format = fmt->format;
>> +
>> +     /* Propagate the format from sink to source */
>> +     if (fmt->pad == CSI2_PAD_SINK) {
>> +             format = __csi2_get_format(csi2, fh, CSI2_PAD_SOURCE,
>> +                                        fmt->which);
>> +             *format = fmt->format;
>> +             csi2_try_format(csi2, fh, CSI2_PAD_SOURCE, format, fmt->which);
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +/*
>> + * csi2_init_formats - Initialize formats on all pads
>> + * @sd: ISS CSI2 V4L2 subdevice
>> + * @fh: V4L2 subdev file handle
>> + *
>> + * Initialize all pad formats with default values. If fh is not NULL, try
>> + * formats are initialized on the file handle. Otherwise active formats are
>> + * initialized on the device.
>> + */
>> +static int csi2_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
>> +{
>> +     struct v4l2_subdev_format format;
>> +
>> +     memset(&format, 0, sizeof(format));
>> +     format.pad = CSI2_PAD_SINK;
>> +     format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
>> +     format.format.code = V4L2_MBUS_FMT_SGRBG10_1X10;
>> +     format.format.width = 4096;
>> +     format.format.height = 4096;
>> +     csi2_set_format(sd, fh, &format);
>> +
>> +     return 0;
>> +}
>> +
>> +/*
>> + * csi2_set_stream - Enable/Disable streaming on the CSI2 module
>> + * @sd: ISS CSI2 V4L2 subdevice
>> + * @enable: ISS pipeline stream state
>> + *
>> + * Return 0 on success or a negative error code otherwise.
>> + */
>> +static int csi2_set_stream(struct v4l2_subdev *sd, int enable)
>> +{
>> +     struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
>> +     struct iss_device *iss = csi2->iss;
>> +     struct iss_pipeline *pipe = to_iss_pipeline(&csi2->subdev.entity);
>> +     struct iss_video *video_out = &csi2->video_out;
>> +
>> +     if (csi2->state == ISS_PIPELINE_STREAM_STOPPED) {
>> +             if (enable == ISS_PIPELINE_STREAM_STOPPED)
>> +                     return 0;
>> +
>> +             omap4iss_subclk_enable(iss, OMAP4_ISS_SUBCLK_CSI2_A);
>> +     }
>> +
>> +     switch (enable) {
>> +     case ISS_PIPELINE_STREAM_CONTINUOUS:
>> +             if (omap4iss_csiphy_acquire(csi2->phy) < 0)
>> +                     return -ENODEV;
>> +             csi2->use_fs_irq = pipe->do_propagation;
>> +             csi2_configure(csi2);
>> +             csi2_print_status(csi2);
>> +
>> +             /*
>> +              * When outputting to memory with no buffer available, let the
>> +              * buffer queue handler start the hardware. A DMA queue flag
>> +              * ISS_VIDEO_DMAQUEUE_QUEUED will be set as soon as there is
>> +              * a buffer available.
>> +              */
>> +             if (csi2->output & CSI2_OUTPUT_MEMORY &&
>> +                 !(video_out->dmaqueue_flags & ISS_VIDEO_DMAQUEUE_QUEUED))
>> +                     break;
>> +             /* Enable context 0 and IRQs */
>> +             atomic_set(&csi2->stopping, 0);
>> +             csi2_ctx_enable(csi2, 0, 1);
>> +             csi2_if_enable(csi2, 1);
>> +             iss_video_dmaqueue_flags_clr(video_out);
>> +             break;
>> +
>> +     case ISS_PIPELINE_STREAM_STOPPED:
>> +             if (csi2->state == ISS_PIPELINE_STREAM_STOPPED)
>> +                     return 0;
>> +             if (omap4iss_module_sync_idle(&sd->entity, &csi2->wait,
>> +                                           &csi2->stopping))
>> +                     dev_dbg(iss->dev, "%s: module stop timeout.\n",
>> +                             sd->name);
>> +             csi2_ctx_enable(csi2, 0, 0);
>> +             csi2_if_enable(csi2, 0);
>> +             csi2_irq_ctx_set(csi2, 0);
>> +             omap4iss_csiphy_release(csi2->phy);
>> +             omap4iss_subclk_disable(iss, OMAP4_ISS_SUBCLK_CSI2_A);
>> +             iss_video_dmaqueue_flags_clr(video_out);
>> +             break;
>> +     }
>> +
>> +     csi2->state = enable;
>> +     return 0;
>> +}
>> +
>> +/* subdev video operations */
>> +static const struct v4l2_subdev_video_ops csi2_video_ops = {
>> +     .s_stream = csi2_set_stream,
>> +};
>> +
>> +/* subdev pad operations */
>> +static const struct v4l2_subdev_pad_ops csi2_pad_ops = {
>> +     .enum_mbus_code = csi2_enum_mbus_code,
>> +     .enum_frame_size = csi2_enum_frame_size,
>> +     .get_fmt = csi2_get_format,
>> +     .set_fmt = csi2_set_format,
>> +};
>> +
>> +/* subdev operations */
>> +static const struct v4l2_subdev_ops csi2_ops = {
>> +     .video = &csi2_video_ops,
>> +     .pad = &csi2_pad_ops,
>> +};
>> +
>> +/* subdev internal operations */
>> +static const struct v4l2_subdev_internal_ops csi2_internal_ops = {
>> +     .open = csi2_init_formats,
>> +};
>> +
>> +/* -----------------------------------------------------------------------------
>> + * Media entity operations
>> + */
>> +
>> +/*
>> + * csi2_link_setup - Setup CSI2 connections.
>> + * @entity : Pointer to media entity structure
>> + * @local  : Pointer to local pad array
>> + * @remote : Pointer to remote pad array
>> + * @flags  : Link flags
>> + * return -EINVAL or zero on success
>> + */
>> +static int csi2_link_setup(struct media_entity *entity,
>> +                        const struct media_pad *local,
>> +                        const struct media_pad *remote, u32 flags)
>> +{
>> +     struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
>> +     struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
>> +     struct iss_csi2_ctrl_cfg *ctrl = &csi2->ctrl;
>> +
>> +     /*
>> +      * The ISS core doesn't support pipelines with multiple video outputs.
>> +      * Revisit this when it will be implemented, and return -EBUSY for now.
>> +      */
>> +
>> +     switch (local->index | media_entity_type(remote->entity)) {
>> +     case CSI2_PAD_SOURCE | MEDIA_ENT_T_DEVNODE:
>> +             if (flags & MEDIA_LNK_FL_ENABLED) {
>> +                     if (csi2->output & ~CSI2_OUTPUT_MEMORY)
>> +                             return -EBUSY;
>> +                     csi2->output |= CSI2_OUTPUT_MEMORY;
>> +             } else {
>> +                     csi2->output &= ~CSI2_OUTPUT_MEMORY;
>> +             }
>> +             break;
>> +
>> +     case CSI2_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV:
>> +             if (flags & MEDIA_LNK_FL_ENABLED) {
>> +                     if (csi2->output & ~CSI2_OUTPUT_IPIPEIF)
>> +                             return -EBUSY;
>> +                     csi2->output |= CSI2_OUTPUT_IPIPEIF;
>> +             } else {
>> +                     csi2->output &= ~CSI2_OUTPUT_IPIPEIF;
>> +             }
>> +             break;
>> +
>> +     default:
>> +             /* Link from camera to CSI2 is fixed... */
>> +             return -EINVAL;
>> +     }
>> +
>> +     ctrl->vp_only_enable =
>> +             (csi2->output & CSI2_OUTPUT_MEMORY) ? false : true;
>> +     ctrl->vp_clk_enable = !!(csi2->output & CSI2_OUTPUT_IPIPEIF);
>> +
>> +     return 0;
>> +}
>> +
>> +/* media operations */
>> +static const struct media_entity_operations csi2_media_ops = {
>> +     .link_setup = csi2_link_setup,
>> +};
>> +
>> +/*
>> + * csi2_init_entities - Initialize subdev and media entity.
>> + * @csi2: Pointer to csi2 structure.
>> + * return -ENOMEM or zero on success
>> + */
>> +static int csi2_init_entities(struct iss_csi2_device *csi2)
>> +{
>> +     struct v4l2_subdev *sd = &csi2->subdev;
>> +     struct media_pad *pads = csi2->pads;
>> +     struct media_entity *me = &sd->entity;
>> +     int ret;
>> +
>> +     v4l2_subdev_init(sd, &csi2_ops);
>> +     sd->internal_ops = &csi2_internal_ops;
>> +     strlcpy(sd->name, "OMAP4 ISS CSI2a", sizeof(sd->name));
>> +
>> +     sd->grp_id = 1 << 16;   /* group ID for iss subdevs */
>> +     v4l2_set_subdevdata(sd, csi2);
>> +     sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
>> +
>> +     pads[CSI2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
>> +     pads[CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
>> +
>> +     me->ops = &csi2_media_ops;
>> +     ret = media_entity_init(me, CSI2_PADS_NUM, pads, 0);
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     csi2_init_formats(sd, NULL);
>> +
>> +     /* Video device node */
>> +     csi2->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>> +     csi2->video_out.ops = &csi2_issvideo_ops;
>> +     csi2->video_out.bpl_alignment = 32;
>> +     csi2->video_out.bpl_zero_padding = 1;
>> +     csi2->video_out.bpl_max = 0x1ffe0;
>> +     csi2->video_out.iss = csi2->iss;
>> +     csi2->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 3;
>> +
>> +     ret = omap4iss_video_init(&csi2->video_out, "CSI2a");
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     /* Connect the CSI2 subdev to the video node. */
>> +     ret = media_entity_create_link(&csi2->subdev.entity, CSI2_PAD_SOURCE,
>> +                                    &csi2->video_out.video.entity, 0, 0);
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     return 0;
>> +}
>> +
>> +void omap4iss_csi2_unregister_entities(struct iss_csi2_device *csi2)
>> +{
>> +     media_entity_cleanup(&csi2->subdev.entity);
>> +
>> +     v4l2_device_unregister_subdev(&csi2->subdev);
>> +     omap4iss_video_unregister(&csi2->video_out);
>> +}
>> +
>> +int omap4iss_csi2_register_entities(struct iss_csi2_device *csi2,
>> +                                 struct v4l2_device *vdev)
>> +{
>> +     int ret;
>> +
>> +     /* Register the subdev and video nodes. */
>> +     ret = v4l2_device_register_subdev(vdev, &csi2->subdev);
>> +     if (ret < 0)
>> +             goto error;
>> +
>> +     ret = omap4iss_video_register(&csi2->video_out, vdev);
>> +     if (ret < 0)
>> +             goto error;
>> +
>> +     return 0;
>> +
>> +error:
>> +     omap4iss_csi2_unregister_entities(csi2);
>> +     return ret;
>> +}
>> +
>> +/* -----------------------------------------------------------------------------
>> + * ISS CSI2 initialisation and cleanup
>> + */
>> +
>> +/*
>> + * omap4iss_csi2_cleanup - Routine for module driver cleanup
>> + */
>> +void omap4iss_csi2_cleanup(struct iss_device *iss)
>> +{
>> +}
>> +
>> +/*
>> + * omap4iss_csi2_init - Routine for module driver init
>> + */
>> +int omap4iss_csi2_init(struct iss_device *iss)
>> +{
>> +     struct iss_csi2_device *csi2a = &iss->csi2a;
>> +     int ret;
>> +
>> +     csi2a->iss = iss;
>> +     csi2a->available = 1;
>> +     csi2a->regs1 = iss->regs[OMAP4_ISS_MEM_CSI2_A_REGS1];
>> +     csi2a->phy = &iss->csiphy1;
>> +     csi2a->state = ISS_PIPELINE_STREAM_STOPPED;
>> +     init_waitqueue_head(&csi2a->wait);
>> +
>> +     ret = csi2_init_entities(csi2a);
>> +     if (ret < 0)
>> +             goto fail;
>> +
>> +     return 0;
>> +fail:
>> +     omap4iss_csi2_cleanup(iss);
>> +     return ret;
>> +}
>> diff --git a/drivers/media/video/omap4iss/iss_csi2.h b/drivers/media/video/omap4iss/iss_csi2.h
>> new file mode 100644
>> index 0000000..4fa94cf
>> --- /dev/null
>> +++ b/drivers/media/video/omap4iss/iss_csi2.h
>> @@ -0,0 +1,166 @@
>> +/*
>> + * iss_csi2.h
>> + *
>> + * TI OMAP4 ISS - CSI2 module
>> + *
>> + * Copyright (C) 2011 Texas Instruments, Inc.
>> + *
>> + * Contacts: Sergio Aguirre <saaguirre@ti.com>
>> + *
>> + * 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.
>> + *
>> + * This program is distributed in the hope that it will be useful, but
>> + * WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write to the Free Software
>> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
>> + * 02110-1301 USA
>> + */
>> +
>> +#ifndef OMAP4_ISS_CSI2_H
>> +#define OMAP4_ISS_CSI2_H
>> +
>> +#include <linux/types.h>
>> +#include <linux/videodev2.h>
>> +
>> +#include "iss_video.h"
>> +
>> +struct iss_csiphy;
>> +
>> +/* This is not an exhaustive list */
>> +enum iss_csi2_pix_formats {
>> +     CSI2_PIX_FMT_OTHERS = 0,
>> +     CSI2_PIX_FMT_YUV422_8BIT = 0x1e,
>> +     CSI2_PIX_FMT_YUV422_8BIT_VP = 0x9e,
>> +     CSI2_PIX_FMT_RAW10_EXP16 = 0xab,
>> +     CSI2_PIX_FMT_RAW10_EXP16_VP = 0x12f,
>> +     CSI2_PIX_FMT_RAW8 = 0x2a,
>> +     CSI2_PIX_FMT_RAW8_DPCM10_EXP16 = 0x2aa,
>> +     CSI2_PIX_FMT_RAW8_DPCM10_VP = 0x32a,
>> +     CSI2_PIX_FMT_RAW8_VP = 0x12a,
>> +     CSI2_USERDEF_8BIT_DATA1_DPCM10_VP = 0x340,
>> +     CSI2_USERDEF_8BIT_DATA1_DPCM10 = 0x2c0,
>> +     CSI2_USERDEF_8BIT_DATA1 = 0x40,
>
> What are the USERDEF formats?

Again, inherited and adapted these from OMAP3 driver.

Should I delete them?

>
>> +};
>> +
>> +enum iss_csi2_irqevents {
>> +     OCP_ERR_IRQ = 0x4000,
>> +     SHORT_PACKET_IRQ = 0x2000,
>> +     ECC_CORRECTION_IRQ = 0x1000,
>> +     ECC_NO_CORRECTION_IRQ = 0x800,
>> +     COMPLEXIO2_ERR_IRQ = 0x400,
>> +     COMPLEXIO1_ERR_IRQ = 0x200,
>> +     FIFO_OVF_IRQ = 0x100,
>> +     CONTEXT7 = 0x80,
>> +     CONTEXT6 = 0x40,
>> +     CONTEXT5 = 0x20,
>> +     CONTEXT4 = 0x10,
>> +     CONTEXT3 = 0x8,
>> +     CONTEXT2 = 0x4,
>> +     CONTEXT1 = 0x2,
>> +     CONTEXT0 = 0x1,
>> +};
>> +
>> +enum iss_csi2_ctx_irqevents {
>> +     CTX_ECC_CORRECTION = 0x100,
>> +     CTX_LINE_NUMBER = 0x80,
>> +     CTX_FRAME_NUMBER = 0x40,
>> +     CTX_CS = 0x20,
>> +     CTX_LE = 0x8,
>> +     CTX_LS = 0x4,
>> +     CTX_FE = 0x2,
>> +     CTX_FS = 0x1,
>> +};
>> +
>> +enum iss_csi2_frame_mode {
>> +     ISS_CSI2_FRAME_IMMEDIATE,
>> +     ISS_CSI2_FRAME_AFTERFEC,
>> +};
>> +
>> +#define ISS_CSI2_MAX_CTX_NUM 7
>> +
>> +struct iss_csi2_ctx_cfg {
>> +     u8 ctxnum;              /* context number 0 - 7 */
>> +     u8 dpcm_decompress;
>> +
>> +     /* Fields in CSI2_CTx_CTRL2 - locked by CSI2_CTx_CTRL1.CTX_EN */
>> +     u8 virtual_id;
>> +     u16 format_id;          /* as in CSI2_CTx_CTRL2[9:0] */
>> +     u8 dpcm_predictor;      /* 1: simple, 0: advanced */
>> +
>> +     /* Fields in CSI2_CTx_CTRL1/3 - Shadowed */
>> +     u16 alpha;
>> +     u16 data_offset;
>> +     u32 ping_addr;
>> +     u32 pong_addr;
>> +     u8 eof_enabled;
>> +     u8 eol_enabled;
>> +     u8 checksum_enabled;
>> +     u8 enabled;
>> +};
>> +
>> +struct iss_csi2_timing_cfg {
>> +     u8 ionum;                       /* IO1 or IO2 as in CSI2_TIMING */
>> +     unsigned force_rx_mode:1;
>> +     unsigned stop_state_16x:1;
>> +     unsigned stop_state_4x:1;
>> +     u16 stop_state_counter;
>> +};
>> +
>> +struct iss_csi2_ctrl_cfg {
>> +     bool vp_clk_enable;
>> +     bool vp_only_enable;
>> +     u8 vp_out_ctrl;
>> +     enum iss_csi2_frame_mode frame_mode;
>> +     bool ecc_enable;
>> +     bool if_enable;
>> +};
>> +
>> +#define CSI2_PAD_SINK                0
>> +#define CSI2_PAD_SOURCE              1
>> +#define CSI2_PADS_NUM                2
>> +
>> +#define CSI2_OUTPUT_IPIPEIF  (1 << 0)
>> +#define CSI2_OUTPUT_MEMORY   (1 << 1)
>> +
>> +struct iss_csi2_device {
>> +     struct v4l2_subdev subdev;
>> +     struct media_pad pads[CSI2_PADS_NUM];
>> +     struct v4l2_mbus_framefmt formats[CSI2_PADS_NUM];
>> +
>> +     struct iss_video video_out;
>> +     struct iss_device *iss;
>> +
>> +     u8 available;           /* Is the IP present on the silicon? */
>> +
>> +     /* Pointer to register remaps into kernel space */
>> +     void __iomem *regs1;
>> +     void __iomem *regs2;
>> +
>> +     u32 output; /* output to IPIPEIF, memory or both? */
>> +     bool dpcm_decompress;
>> +     unsigned int frame_skip;
>> +     bool use_fs_irq;
>> +
>> +     struct iss_csiphy *phy;
>> +     struct iss_csi2_ctx_cfg contexts[ISS_CSI2_MAX_CTX_NUM + 1];
>> +     struct iss_csi2_timing_cfg timing[2];
>> +     struct iss_csi2_ctrl_cfg ctrl;
>> +     enum iss_pipeline_stream_state state;
>> +     wait_queue_head_t wait;
>> +     atomic_t stopping;
>> +};
>> +
>> +int omap4iss_csi2_isr(struct iss_csi2_device *csi2);
>> +int omap4iss_csi2_reset(struct iss_csi2_device *csi2);
>> +int omap4iss_csi2_init(struct iss_device *iss);
>> +void omap4iss_csi2_cleanup(struct iss_device *iss);
>> +void omap4iss_csi2_unregister_entities(struct iss_csi2_device *csi2);
>> +int omap4iss_csi2_register_entities(struct iss_csi2_device *csi2,
>> +                                 struct v4l2_device *vdev);
>> +#endif       /* OMAP4_ISS_CSI2_H */
>> diff --git a/drivers/media/video/omap4iss/iss_csiphy.c b/drivers/media/video/omap4iss/iss_csiphy.c
>> new file mode 100644
>> index 0000000..9545622
>> --- /dev/null
>> +++ b/drivers/media/video/omap4iss/iss_csiphy.c
>> @@ -0,0 +1,215 @@
>> +/*
>> + * iss_csiphy.c
>> + *
>> + * TI OMAP4 ISS - CSI PHY module
>> + *
>> + * Copyright (C) 2011 Texas Instruments, Inc.
>> + *
>> + * Contacts: Sergio Aguirre <saaguirre@ti.com>
>> + *
>> + * 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.
>> + *
>> + * This program is distributed in the hope that it will be useful, but
>> + * WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write to the Free Software
>> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
>> + * 02110-1301 USA
>> + */
>> +
>> +#include <linux/delay.h>
>> +#include <linux/device.h>
>> +
>> +#include "iss.h"
>> +#include "iss_regs.h"
>> +#include "iss_csiphy.h"
>> +
>> +/*
>> + * csiphy_lanes_config - Configuration of CSIPHY lanes.
>> + *
>> + * Updates HW configuration.
>> + * Called with phy->mutex taken.
>> + */
>> +static void csiphy_lanes_config(struct iss_csiphy *phy)
>> +{
>> +     unsigned int i;
>> +     u32 reg;
>> +
>> +     reg = readl(phy->cfg_regs + CSI2_COMPLEXIO_CFG);
>> +
>> +     for (i = 0; i < phy->num_data_lanes; i++) {
>> +             reg &= ~(CSI2_COMPLEXIO_CFG_DATA_POL(i + 1) |
>> +                      CSI2_COMPLEXIO_CFG_DATA_POSITION_MASK(i + 1));
>> +             reg |= (phy->lanes.data[i].pol ?
>> +                     CSI2_COMPLEXIO_CFG_DATA_POL(i + 1) : 0);
>> +             reg |= (phy->lanes.data[i].pos <<
>> +                     CSI2_COMPLEXIO_CFG_DATA_POSITION_SHIFT(i + 1));
>> +     }
>> +
>> +     reg &= ~(CSI2_COMPLEXIO_CFG_CLOCK_POL |
>> +              CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK);
>> +     reg |= phy->lanes.clk.pol ? CSI2_COMPLEXIO_CFG_CLOCK_POL : 0;
>> +     reg |= phy->lanes.clk.pos << CSI2_COMPLEXIO_CFG_CLOCK_POSITION_SHIFT;
>> +
>> +     writel(reg, phy->cfg_regs + CSI2_COMPLEXIO_CFG);
>> +}
>> +
>> +/*
>> + * csiphy_set_power
>> + * @power: Power state to be set.
>> + *
>> + * Returns 0 if successful, or -EBUSY if the retry count is exceeded.
>> + */
>> +static int csiphy_set_power(struct iss_csiphy *phy, u32 power)
>> +{
>> +     u32 reg;
>> +     u8 retry_count;
>> +
>> +     writel((readl(phy->cfg_regs + CSI2_COMPLEXIO_CFG) &
>> +             ~CSI2_COMPLEXIO_CFG_PWD_CMD_MASK) |
>> +             power,
>> +             phy->cfg_regs + CSI2_COMPLEXIO_CFG);
>> +
>> +     retry_count = 0;
>> +     do {
>> +             udelay(50);
>
> I guess udelay(1) should do. There's no reason to busyloop longer, if at
> all. How much is a typical value for retry_count?

Ok. Will check this, not sure what's the count.

>
>> +             reg = readl(phy->cfg_regs + CSI2_COMPLEXIO_CFG) &
>> +                             CSI2_COMPLEXIO_CFG_PWD_STATUS_MASK;
>> +
>> +             if (reg != power >> 2)
>> +                     retry_count++;
>> +
>> +     } while ((reg != power >> 2) && (retry_count < 100));
>> +
>> +     if (retry_count == 100) {
>> +             printk(KERN_ERR "CSI2 CIO set power failed!\n");
>> +             return -EBUSY;
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +/*
>> + * csiphy_dphy_config - Configure CSI2 D-PHY parameters.
>> + *
>> + * Called with phy->mutex taken.
>> + */
>> +static void csiphy_dphy_config(struct iss_csiphy *phy)
>> +{
>> +     u32 reg;
>> +
>> +     /* Set up REGISTER0 */
>> +     reg = readl(phy->phy_regs + REGISTER0);
>> +
>> +     reg &= ~(REGISTER0_THS_TERM_MASK |
>> +              REGISTER0_THS_SETTLE_MASK);
>> +     reg |= phy->dphy.ths_term << REGISTER0_THS_TERM_SHIFT;
>> +     reg |= phy->dphy.ths_settle << REGISTER0_THS_SETTLE_SHIFT;
>> +
>> +     writel(reg, phy->phy_regs + REGISTER0);
>> +
>> +     /* Set up REGISTER1 */
>> +     reg = readl(phy->phy_regs + REGISTER1);
>> +
>> +     reg &= ~(REGISTER1_TCLK_TERM_MASK |
>> +              REGISTER1_CTRLCLK_DIV_FACTOR_MASK |
>> +              REGISTER1_TCLK_SETTLE_MASK);
>> +     reg |= phy->dphy.tclk_term << REGISTER1_TCLK_TERM_SHIFT;
>> +     reg |= phy->dphy.tclk_miss << REGISTER1_CTRLCLK_DIV_FACTOR_SHIFT;
>> +     reg |= phy->dphy.tclk_settle << REGISTER1_TCLK_SETTLE_SHIFT;
>> +
>> +     writel(reg, phy->phy_regs + REGISTER1);
>
> REGISTER0 or 1 aren't really used elsewhere. You could omit reading them
> back.

Right. Will fix.

>
>> +}
>> +
>> +static int csiphy_config(struct iss_csiphy *phy,
>> +                      struct iss_csiphy_dphy_cfg *dphy,
>> +                      struct iss_csiphy_lanes_cfg *lanes)
>> +{
>> +     unsigned int used_lanes = 0;
>> +     unsigned int i;
>> +
>> +     /* Clock and data lanes verification */
>> +     for (i = 0; i < phy->num_data_lanes; i++) {
>> +             if (lanes->data[i].pos == 0)
>> +                     continue;
>> +
>> +             if (lanes->data[i].pol > 1 || lanes->data[i].pos > 5)
>> +                     return -EINVAL;
>> +
>> +             if (used_lanes & (1 << lanes->data[i].pos))
>> +                     return -EINVAL;
>> +
>> +             used_lanes |= 1 << lanes->data[i].pos;
>> +     }
>> +
>> +     if (lanes->clk.pol > 1 || lanes->clk.pos > 4)
>> +             return -EINVAL;
>> +
>> +     if (lanes->clk.pos == 0 || used_lanes & (1 << lanes->clk.pos))
>> +             return -EINVAL;
>> +
>> +     mutex_lock(&phy->mutex);
>> +     phy->dphy = *dphy;
>> +     phy->lanes = *lanes;
>> +     mutex_unlock(&phy->mutex);
>> +
>> +     return 0;
>> +}
>> +
>> +int omap4iss_csiphy_acquire(struct iss_csiphy *phy)
>> +{
>> +     int rval;
>> +
>> +     mutex_lock(&phy->mutex);
>> +
>> +     rval = omap4iss_csi2_reset(phy->csi2);
>> +     if (rval)
>> +             goto done;
>> +
>> +     csiphy_dphy_config(phy);
>> +     csiphy_lanes_config(phy);
>> +
>> +     rval = csiphy_set_power(phy, CSI2_COMPLEXIO_CFG_PWD_CMD_ON);
>> +     if (rval)
>> +             goto done;
>> +
>> +     phy->phy_in_use = 1;
>> +
>> +done:
>> +     mutex_unlock(&phy->mutex);
>> +     return rval;
>> +}
>> +
>> +void omap4iss_csiphy_release(struct iss_csiphy *phy)
>> +{
>> +     mutex_lock(&phy->mutex);
>> +     if (phy->phy_in_use) {
>> +             csiphy_set_power(phy, CSI2_COMPLEXIO_CFG_PWD_CMD_OFF);
>> +             phy->phy_in_use = 0;
>> +     }
>> +     mutex_unlock(&phy->mutex);
>> +}
>> +
>> +/*
>> + * omap4iss_csiphy_init - Initialize the CSI PHY frontends
>> + */
>> +int omap4iss_csiphy_init(struct iss_device *iss)
>> +{
>> +     struct iss_csiphy *phy1 = &iss->csiphy1;
>> +
>> +     iss->platform_cb.csiphy_config = csiphy_config;
>> +
>> +     phy1->iss = iss;
>> +     phy1->csi2 = &iss->csi2a;
>> +     phy1->num_data_lanes = ISS_CSIPHY1_NUM_DATA_LANES;
>> +     phy1->cfg_regs = iss->regs[OMAP4_ISS_MEM_CSI2_A_REGS1];
>> +     phy1->phy_regs = iss->regs[OMAP4_ISS_MEM_CAMERARX_CORE1];
>> +     mutex_init(&phy1->mutex);
>> +
>> +     return 0;
>> +}
>> diff --git a/drivers/media/video/omap4iss/iss_csiphy.h b/drivers/media/video/omap4iss/iss_csiphy.h
>> new file mode 100644
>> index 0000000..c513ba8
>> --- /dev/null
>> +++ b/drivers/media/video/omap4iss/iss_csiphy.h
>> @@ -0,0 +1,69 @@
>> +/*
>> + * iss_csiphy.h
>> + *
>> + * TI OMAP4 ISS - CSI PHY module
>> + *
>> + * Copyright (C) 2011 Texas Instruments, Inc.
>> + *
>> + * Contacts: Sergio Aguirre <saaguirre@ti.com>
>> + *
>> + * 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.
>> + *
>> + * This program is distributed in the hope that it will be useful, but
>> + * WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write to the Free Software
>> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
>> + * 02110-1301 USA
>> + */
>> +
>> +#ifndef OMAP4_ISS_CSI_PHY_H
>> +#define OMAP4_ISS_CSI_PHY_H
>> +
>> +struct iss_csi2_device;
>> +
>> +struct csiphy_lane {
>> +     u8 pos;
>> +     u8 pol;
>> +};
>> +
>> +#define ISS_CSIPHY1_NUM_DATA_LANES   4
>> +
>> +struct iss_csiphy_lanes_cfg {
>> +     struct csiphy_lane data[ISS_CSIPHY1_NUM_DATA_LANES];
>> +     struct csiphy_lane clk;
>> +};
>> +
>> +struct iss_csiphy_dphy_cfg {
>> +     u8 ths_term;
>> +     u8 ths_settle;
>> +     u8 tclk_term;
>> +     unsigned tclk_miss:1;
>> +     u8 tclk_settle;
>> +};
>> +
>> +struct iss_csiphy {
>> +     struct iss_device *iss;
>> +     struct mutex mutex;     /* serialize csiphy configuration */
>> +     u8 phy_in_use;
>> +     struct iss_csi2_device *csi2;
>> +
>> +     /* Pointer to register remaps into kernel space */
>> +     void __iomem *cfg_regs;
>> +     void __iomem *phy_regs;
>> +
>> +     u8 num_data_lanes;      /* number of CSI2 Data Lanes supported */
>> +     struct iss_csiphy_lanes_cfg lanes;
>> +     struct iss_csiphy_dphy_cfg dphy;
>> +};
>> +
>> +int omap4iss_csiphy_acquire(struct iss_csiphy *phy);
>> +void omap4iss_csiphy_release(struct iss_csiphy *phy);
>> +int omap4iss_csiphy_init(struct iss_device *iss);
>> +
>> +#endif       /* OMAP4_ISS_CSI_PHY_H */
>> diff --git a/drivers/media/video/omap4iss/iss_regs.h b/drivers/media/video/omap4iss/iss_regs.h
>> new file mode 100644
>> index 0000000..0bd70ac
>> --- /dev/null
>> +++ b/drivers/media/video/omap4iss/iss_regs.h
>> @@ -0,0 +1,238 @@
>> +/*
>> + * iss_regs.h
>> + *
>> + * Copyright (C) 2011 Texas Instruments.
>> + *
>> + * Author: Sergio Aguirre <saaguirre@ti.com>
>> + */
>> +
>> +#ifndef _OMAP4_ISS_REGS_H_
>> +#define _OMAP4_ISS_REGS_H_
>> +
>> +/* ISS */
>> +#define ISS_HL_REVISION                                      (0x0)
>
> No need for parenthesis for hexadecimals.

Ok. Will fix.

>
>> +#define ISS_HL_SYSCONFIG                             (0x10)
>> +#define ISS_HL_SYSCONFIG_IDLEMODE_SHIFT                      2
>> +#define ISS_HL_SYSCONFIG_IDLEMODE_FORCEIDLE          0x0
>> +#define ISS_HL_SYSCONFIG_IDLEMODE_NOIDLE             0x1
>> +#define ISS_HL_SYSCONFIG_IDLEMODE_SMARTIDLE          0x2
>> +#define ISS_HL_SYSCONFIG_SOFTRESET                   (1 << 0)
>> +
>> +#define ISS_HL_IRQSTATUS_5                           (0x24 + (0x10 * 5))
>> +#define ISS_HL_IRQENABLE_5_SET                               (0x28 + (0x10 * 5))
>> +#define ISS_HL_IRQENABLE_5_CLR                               (0x2C + (0x10 * 5))
>> +
>> +#define ISS_HL_IRQ_BTE                                       (1 << 11)
>> +#define ISS_HL_IRQ_CBUFF                             (1 << 10)
>> +#define ISS_HL_IRQ_CSIA                                      (1 << 4)
>> +
>> +#define ISS_CTRL                                     (0x80)
>> +
>> +#define ISS_CLKCTRL                                  (0x84)
>> +#define ISS_CLKCTRL_VPORT2_CLK                               (1 << 30)
>> +#define ISS_CLKCTRL_VPORT1_CLK                               (1 << 29)
>> +#define ISS_CLKCTRL_VPORT0_CLK                               (1 << 28)
>> +#define ISS_CLKCTRL_CCP2                             (1 << 4)
>> +#define ISS_CLKCTRL_CSI2_B                           (1 << 3)
>> +#define ISS_CLKCTRL_CSI2_A                           (1 << 2)
>> +#define ISS_CLKCTRL_ISP                                      (1 << 1)
>> +#define ISS_CLKCTRL_SIMCOP                           (1 << 0)
>> +
>> +#define ISS_CLKSTAT                                  (0x88)
>> +#define ISS_CLKSTAT_VPORT2_CLK                               (1 << 30)
>> +#define ISS_CLKSTAT_VPORT1_CLK                               (1 << 29)
>> +#define ISS_CLKSTAT_VPORT0_CLK                               (1 << 28)
>> +#define ISS_CLKSTAT_CCP2                             (1 << 4)
>> +#define ISS_CLKSTAT_CSI2_B                           (1 << 3)
>> +#define ISS_CLKSTAT_CSI2_A                           (1 << 2)
>> +#define ISS_CLKSTAT_ISP                                      (1 << 1)
>> +#define ISS_CLKSTAT_SIMCOP                           (1 << 0)
>> +
>> +#define ISS_PM_STATUS                                        (0x8C)
>> +#define ISS_PM_STATUS_CBUFF_PM_MASK                  (3 << 12)
>> +#define ISS_PM_STATUS_BTE_PM_MASK                    (3 << 10)
>> +#define ISS_PM_STATUS_SIMCOP_PM_MASK                 (3 << 8)
>> +#define ISS_PM_STATUS_ISP_PM_MASK                    (3 << 6)
>> +#define ISS_PM_STATUS_CCP2_PM_MASK                   (3 << 4)
>> +#define ISS_PM_STATUS_CSI2_B_PM_MASK                 (3 << 2)
>> +#define ISS_PM_STATUS_CSI2_A_PM_MASK                 (3 << 0)
>> +
>> +#define REGISTER0                                    (0x0)
>> +#define REGISTER0_HSCLOCKCONFIG                              (1 << 24)
>> +#define REGISTER0_THS_TERM_MASK                              (0xFF << 8)
>> +#define REGISTER0_THS_TERM_SHIFT                     8
>> +#define REGISTER0_THS_SETTLE_MASK                    (0xFF << 0)
>> +#define REGISTER0_THS_SETTLE_SHIFT                   0
>> +
>> +#define REGISTER1                                    (0x4)
>> +#define REGISTER1_RESET_DONE_CTRLCLK                 (1 << 29)
>> +#define REGISTER1_CLOCK_MISS_DETECTOR_STATUS         (1 << 25)
>> +#define REGISTER1_TCLK_TERM_MASK                     (0x3F << 18)
>> +#define REGISTER1_TCLK_TERM_SHIFT                    18
>> +#define REGISTER1_DPHY_HS_SYNC_PATTERN_MASK          (0xFF << 10)
>> +#define REGISTER1_CTRLCLK_DIV_FACTOR_MASK            (0x3 << 8)
>> +#define REGISTER1_CTRLCLK_DIV_FACTOR_SHIFT           8
>> +#define REGISTER1_TCLK_SETTLE_MASK                   (0xFF << 0)
>> +#define REGISTER1_TCLK_SETTLE_SHIFT                  0
>> +
>> +#define REGISTER2                                    (0x8)
>> +
>> +#define CSI2_SYSCONFIG                                       (0x10)
>> +#define CSI2_SYSCONFIG_MSTANDBY_MODE_MASK            (3 << 12)
>> +#define CSI2_SYSCONFIG_MSTANDBY_MODE_FORCE           (0 << 12)
>> +#define CSI2_SYSCONFIG_MSTANDBY_MODE_NO                      (1 << 12)
>> +#define CSI2_SYSCONFIG_MSTANDBY_MODE_SMART           (2 << 12)
>> +#define CSI2_SYSCONFIG_SOFT_RESET                    (1 << 1)
>> +#define CSI2_SYSCONFIG_AUTO_IDLE                     (1 << 0)
>> +
>> +#define CSI2_SYSSTATUS                                       (0x14)
>> +#define CSI2_SYSSTATUS_RESET_DONE                    (1 << 0)
>> +
>> +#define CSI2_IRQSTATUS                                       (0x18)
>> +#define CSI2_IRQENABLE                                       (0x1C)
>> +
>> +/* Shared bits across CSI2_IRQENABLE and IRQSTATUS */
>> +
>> +#define CSI2_IRQ_OCP_ERR                             (1 << 14)
>> +#define CSI2_IRQ_SHORT_PACKET                                (1 << 13)
>> +#define CSI2_IRQ_ECC_CORRECTION                              (1 << 12)
>> +#define CSI2_IRQ_ECC_NO_CORRECTION                   (1 << 11)
>> +#define CSI2_IRQ_COMPLEXIO_ERR                               (1 << 9)
>> +#define CSI2_IRQ_FIFO_OVF                            (1 << 8)
>> +#define CSI2_IRQ_CONTEXT0                            (1 << 0)
>> +
>> +#define CSI2_CTRL                                    (0x40)
>> +#define CSI2_CTRL_MFLAG_LEVH_MASK                    (7 << 20)
>> +#define CSI2_CTRL_MFLAG_LEVH_SHIFT                   20
>> +#define CSI2_CTRL_MFLAG_LEVL_MASK                    (7 << 17)
>> +#define CSI2_CTRL_MFLAG_LEVL_SHIFT                   17
>> +#define CSI2_CTRL_BURST_SIZE_EXPAND                  (1 << 16)
>> +#define CSI2_CTRL_VP_CLK_EN                          (1 << 15)
>> +#define CSI2_CTRL_NON_POSTED_WRITE                   (1 << 13)
>> +#define CSI2_CTRL_VP_ONLY_EN                         (1 << 11)
>> +#define CSI2_CTRL_VP_OUT_CTRL_MASK                   (3 << 8)
>> +#define CSI2_CTRL_VP_OUT_CTRL_SHIFT                  8
>> +#define CSI2_CTRL_DBG_EN                             (1 << 7)
>> +#define CSI2_CTRL_BURST_SIZE_MASK                    (3 << 5)
>> +#define CSI2_CTRL_ENDIANNESS                         (1 << 4)
>> +#define CSI2_CTRL_FRAME                                      (1 << 3)
>> +#define CSI2_CTRL_ECC_EN                             (1 << 2)
>> +#define CSI2_CTRL_IF_EN                                      (1 << 0)
>> +
>> +#define CSI2_DBG_H                                   (0x44)
>> +
>> +#define CSI2_COMPLEXIO_CFG                           (0x50)
>> +#define CSI2_COMPLEXIO_CFG_RESET_CTRL                        (1 << 30)
>> +#define CSI2_COMPLEXIO_CFG_RESET_DONE                        (1 << 29)
>> +#define CSI2_COMPLEXIO_CFG_PWD_CMD_MASK                      (3 << 27)
>> +#define CSI2_COMPLEXIO_CFG_PWD_CMD_OFF                       (0 << 27)
>> +#define CSI2_COMPLEXIO_CFG_PWD_CMD_ON                        (1 << 27)
>> +#define CSI2_COMPLEXIO_CFG_PWD_CMD_ULP                       (2 << 27)
>> +#define CSI2_COMPLEXIO_CFG_PWD_STATUS_MASK           (3 << 25)
>> +#define CSI2_COMPLEXIO_CFG_PWD_STATUS_OFF            (0 << 25)
>> +#define CSI2_COMPLEXIO_CFG_PWD_STATUS_ON             (1 << 25)
>> +#define CSI2_COMPLEXIO_CFG_PWD_STATUS_ULP            (2 << 25)
>> +#define CSI2_COMPLEXIO_CFG_PWR_AUTO                  (1 << 24)
>> +#define CSI2_COMPLEXIO_CFG_DATA_POL(i)                       (1 << (((i) * 4) + 3))
>> +#define CSI2_COMPLEXIO_CFG_DATA_POSITION_MASK(i)     (7 << ((i) * 4))
>> +#define CSI2_COMPLEXIO_CFG_DATA_POSITION_SHIFT(i)    ((i) * 4)
>> +#define CSI2_COMPLEXIO_CFG_CLOCK_POL                 (1 << 3)
>> +#define CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK               (7 << 0)
>> +#define CSI2_COMPLEXIO_CFG_CLOCK_POSITION_SHIFT              0
>> +
>> +#define CSI2_COMPLEXIO_IRQSTATUS                     (0x54)
>> +
>> +#define CSI2_SHORT_PACKET                            (0x5C)
>> +
>> +#define CSI2_COMPLEXIO_IRQENABLE                     (0x60)
>> +
>> +/* Shared bits across CSI2_COMPLEXIO_IRQENABLE and IRQSTATUS */
>> +#define CSI2_COMPLEXIO_IRQ_STATEALLULPMEXIT          (1 << 26)
>> +#define CSI2_COMPLEXIO_IRQ_STATEALLULPMENTER         (1 << 25)
>> +#define CSI2_COMPLEXIO_IRQ_STATEULPM5                        (1 << 24)
>> +#define CSI2_COMPLEXIO_IRQ_STATEULPM4                        (1 << 23)
>> +#define CSI2_COMPLEXIO_IRQ_STATEULPM3                        (1 << 22)
>> +#define CSI2_COMPLEXIO_IRQ_STATEULPM2                        (1 << 21)
>> +#define CSI2_COMPLEXIO_IRQ_STATEULPM1                        (1 << 20)
>> +#define CSI2_COMPLEXIO_IRQ_ERRCONTROL5                       (1 << 19)
>> +#define CSI2_COMPLEXIO_IRQ_ERRCONTROL4                       (1 << 18)
>> +#define CSI2_COMPLEXIO_IRQ_ERRCONTROL3                       (1 << 17)
>> +#define CSI2_COMPLEXIO_IRQ_ERRCONTROL2                       (1 << 16)
>> +#define CSI2_COMPLEXIO_IRQ_ERRCONTROL1                       (1 << 15)
>> +#define CSI2_COMPLEXIO_IRQ_ERRESC5                   (1 << 14)
>> +#define CSI2_COMPLEXIO_IRQ_ERRESC4                   (1 << 13)
>> +#define CSI2_COMPLEXIO_IRQ_ERRESC3                   (1 << 12)
>> +#define CSI2_COMPLEXIO_IRQ_ERRESC2                   (1 << 11)
>> +#define CSI2_COMPLEXIO_IRQ_ERRESC1                   (1 << 10)
>> +#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS5             (1 << 9)
>> +#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS4             (1 << 8)
>> +#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS3             (1 << 7)
>> +#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS2             (1 << 6)
>> +#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS1             (1 << 5)
>> +#define CSI2_COMPLEXIO_IRQ_ERRSOTHS5                 (1 << 4)
>> +#define CSI2_COMPLEXIO_IRQ_ERRSOTHS4                 (1 << 3)
>> +#define CSI2_COMPLEXIO_IRQ_ERRSOTHS3                 (1 << 2)
>> +#define CSI2_COMPLEXIO_IRQ_ERRSOTHS2                 (1 << 1)
>> +#define CSI2_COMPLEXIO_IRQ_ERRSOTHS1                 (1 << 0)
>> +
>> +#define CSI2_DBG_P                                   (0x68)
>> +
>> +#define CSI2_TIMING                                  (0x6C)
>> +#define CSI2_TIMING_FORCE_RX_MODE_IO1                        (1 << 15)
>> +#define CSI2_TIMING_STOP_STATE_X16_IO1                       (1 << 14)
>> +#define CSI2_TIMING_STOP_STATE_X4_IO1                        (1 << 13)
>> +#define CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK              (0x1FFF << 0)
>> +#define CSI2_TIMING_STOP_STATE_COUNTER_IO1_SHIFT     0
>> +
>> +#define CSI2_CTX_CTRL1(i)                            (0x70 + (0x20 * i))
>> +#define CSI2_CTX_CTRL1_GENERIC                               (1 << 30)
>> +#define CSI2_CTX_CTRL1_TRANSCODE                     (0xF << 24)
>> +#define CSI2_CTX_CTRL1_FEC_NUMBER_MASK                       (0xFF << 16)
>> +#define CSI2_CTX_CTRL1_COUNT_MASK                    (0xFF << 8)
>> +#define CSI2_CTX_CTRL1_COUNT_SHIFT                   8
>> +#define CSI2_CTX_CTRL1_EOF_EN                                (1 << 7)
>> +#define CSI2_CTX_CTRL1_EOL_EN                                (1 << 6)
>> +#define CSI2_CTX_CTRL1_CS_EN                         (1 << 5)
>> +#define CSI2_CTX_CTRL1_COUNT_UNLOCK                  (1 << 4)
>> +#define CSI2_CTX_CTRL1_PING_PONG                     (1 << 3)
>> +#define CSI2_CTX_CTRL1_CTX_EN                                (1 << 0)
>> +
>> +#define CSI2_CTX_CTRL2(i)                            (0x74 + (0x20 * i))
>> +#define CSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT            13
>> +#define CSI2_CTX_CTRL2_USER_DEF_MAP_MASK             \
>> +             (0x3 << CSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT)
>> +#define CSI2_CTX_CTRL2_VIRTUAL_ID_MASK                       (3 << 11)
>> +#define CSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT                      11
>> +#define CSI2_CTX_CTRL2_DPCM_PRED                     (1 << 10)
>> +#define CSI2_CTX_CTRL2_FORMAT_MASK                   (0x3FF << 0)
>> +#define CSI2_CTX_CTRL2_FORMAT_SHIFT                  0
>> +
>> +#define CSI2_CTX_DAT_OFST(i)                         (0x78 + (0x20 * i))
>> +#define CSI2_CTX_DAT_OFST_MASK                               (0xFFF << 5)
>> +
>> +#define CSI2_CTX_PING_ADDR(i)                                (0x7C + (0x20 * i))
>> +#define CSI2_CTX_PING_ADDR_MASK                              0xFFFFFFE0
>> +
>> +#define CSI2_CTX_PONG_ADDR(i)                                (0x80 + (0x20 * i))
>> +#define CSI2_CTX_PONG_ADDR_MASK                              CSI2_CTX_PING_ADDR_MASK
>> +
>> +#define CSI2_CTX_IRQENABLE(i)                                (0x84 + (0x20 * i))
>> +#define CSI2_CTX_IRQSTATUS(i)                                (0x88 + (0x20 * i))
>> +
>> +#define CSI2_CTX_CTRL3(i)                            (0x8C + (0x20 * i))
>> +#define CSI2_CTX_CTRL3_ALPHA_SHIFT                   5
>> +#define CSI2_CTX_CTRL3_ALPHA_MASK                    \
>> +             (0x3fff << CSI2_CTX_CTRL3_ALPHA_SHIFT)
>> +
>> +/* Shared bits across CSI2_CTX_IRQENABLE and IRQSTATUS */
>> +#define CSI2_CTX_IRQ_ECC_CORRECTION                  (1 << 8)
>> +#define CSI2_CTX_IRQ_LINE_NUMBER                     (1 << 7)
>> +#define CSI2_CTX_IRQ_FRAME_NUMBER                    (1 << 6)
>> +#define CSI2_CTX_IRQ_CS                                      (1 << 5)
>> +#define CSI2_CTX_IRQ_LE                                      (1 << 3)
>> +#define CSI2_CTX_IRQ_LS                                      (1 << 2)
>> +#define CSI2_CTX_IRQ_FE                                      (1 << 1)
>> +#define CSI2_CTX_IRQ_FS                                      (1 << 0)
>> +
>> +#endif /* _OMAP4_CAMERA_REGS_H_ */
>> diff --git a/drivers/media/video/omap4iss/iss_video.c b/drivers/media/video/omap4iss/iss_video.c
>> new file mode 100644
>> index 0000000..3248711
>> --- /dev/null
>> +++ b/drivers/media/video/omap4iss/iss_video.c
>> @@ -0,0 +1,1192 @@
>> +/*
>> + * iss_video.c
>> + *
>> + * TI OMAP4 ISS - Generic video node
>> + *
>> + * Copyright (C) 2011 Texas Instruments, Inc.
>> + *
>> + * Contacts: Sergio Aguirre <saaguirre@ti.com>
>> + *
>> + * 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.
>> + *
>> + * This program is distributed in the hope that it will be useful, but
>> + * WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write to the Free Software
>> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
>> + * 02110-1301 USA
>> + */
>> +
>> +#include <asm/cacheflush.h>
>> +#include <linux/clk.h>
>> +#include <linux/mm.h>
>> +#include <linux/pagemap.h>
>> +#include <linux/sched.h>
>> +#include <linux/slab.h>
>> +#include <linux/vmalloc.h>
>> +#include <linux/module.h>
>> +#include <media/v4l2-dev.h>
>> +#include <media/v4l2-ioctl.h>
>> +#include <plat/omap-pm.h>
>> +
>> +#include "iss_video.h"
>> +#include "iss.h"
>> +
>> +
>> +/* -----------------------------------------------------------------------------
>> + * Helper functions
>> + */
>> +
>> +static struct iss_format_info formats[] = {
>> +     { V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8,
>> +       V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8,
>> +       V4L2_PIX_FMT_GREY, 8, },
>> +     { V4L2_MBUS_FMT_Y10_1X10, V4L2_MBUS_FMT_Y10_1X10,
>> +       V4L2_MBUS_FMT_Y10_1X10, V4L2_MBUS_FMT_Y8_1X8,
>> +       V4L2_PIX_FMT_Y10, 10, },
>> +     { V4L2_MBUS_FMT_Y12_1X12, V4L2_MBUS_FMT_Y10_1X10,
>> +       V4L2_MBUS_FMT_Y12_1X12, V4L2_MBUS_FMT_Y8_1X8,
>> +       V4L2_PIX_FMT_Y12, 12, },
>> +     { V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_MBUS_FMT_SBGGR8_1X8,
>> +       V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_MBUS_FMT_SBGGR8_1X8,
>> +       V4L2_PIX_FMT_SBGGR8, 8, },
>> +     { V4L2_MBUS_FMT_SGBRG8_1X8, V4L2_MBUS_FMT_SGBRG8_1X8,
>> +       V4L2_MBUS_FMT_SGBRG8_1X8, V4L2_MBUS_FMT_SGBRG8_1X8,
>> +       V4L2_PIX_FMT_SGBRG8, 8, },
>> +     { V4L2_MBUS_FMT_SGRBG8_1X8, V4L2_MBUS_FMT_SGRBG8_1X8,
>> +       V4L2_MBUS_FMT_SGRBG8_1X8, V4L2_MBUS_FMT_SGRBG8_1X8,
>> +       V4L2_PIX_FMT_SGRBG8, 8, },
>> +     { V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8,
>> +       V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8,
>> +       V4L2_PIX_FMT_SRGGB8, 8, },
>> +     { V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8,
>> +       V4L2_MBUS_FMT_SGRBG10_1X10, 0,
>> +       V4L2_PIX_FMT_SGRBG10DPCM8, 8, },
>
> I had a few patches (not acked yet) to add more supported media bus formats.
> It might make sense to add support for those for this driver as well once
> we've discussed 4cc's with Hans.

Ok. Will wait for that then.

>
>> +     { V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR10_1X10,
>> +       V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR8_1X8,
>> +       V4L2_PIX_FMT_SBGGR10, 10, },
>> +     { V4L2_MBUS_FMT_SGBRG10_1X10, V4L2_MBUS_FMT_SGBRG10_1X10,
>> +       V4L2_MBUS_FMT_SGBRG10_1X10, V4L2_MBUS_FMT_SGBRG8_1X8,
>> +       V4L2_PIX_FMT_SGBRG10, 10, },
>> +     { V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_MBUS_FMT_SGRBG10_1X10,
>> +       V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_MBUS_FMT_SGRBG8_1X8,
>> +       V4L2_PIX_FMT_SGRBG10, 10, },
>> +     { V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_MBUS_FMT_SRGGB10_1X10,
>> +       V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_MBUS_FMT_SRGGB8_1X8,
>> +       V4L2_PIX_FMT_SRGGB10, 10, },
>> +     { V4L2_MBUS_FMT_SBGGR12_1X12, V4L2_MBUS_FMT_SBGGR10_1X10,
>> +       V4L2_MBUS_FMT_SBGGR12_1X12, V4L2_MBUS_FMT_SBGGR8_1X8,
>> +       V4L2_PIX_FMT_SBGGR12, 12, },
>> +     { V4L2_MBUS_FMT_SGBRG12_1X12, V4L2_MBUS_FMT_SGBRG10_1X10,
>> +       V4L2_MBUS_FMT_SGBRG12_1X12, V4L2_MBUS_FMT_SGBRG8_1X8,
>> +       V4L2_PIX_FMT_SGBRG12, 12, },
>> +     { V4L2_MBUS_FMT_SGRBG12_1X12, V4L2_MBUS_FMT_SGRBG10_1X10,
>> +       V4L2_MBUS_FMT_SGRBG12_1X12, V4L2_MBUS_FMT_SGRBG8_1X8,
>> +       V4L2_PIX_FMT_SGRBG12, 12, },
>> +     { V4L2_MBUS_FMT_SRGGB12_1X12, V4L2_MBUS_FMT_SRGGB10_1X10,
>> +       V4L2_MBUS_FMT_SRGGB12_1X12, V4L2_MBUS_FMT_SRGGB8_1X8,
>> +       V4L2_PIX_FMT_SRGGB12, 12, },
>> +     { V4L2_MBUS_FMT_UYVY8_1X16, V4L2_MBUS_FMT_UYVY8_1X16,
>> +       V4L2_MBUS_FMT_UYVY8_1X16, 0,
>> +       V4L2_PIX_FMT_UYVY, 16, },
>> +     { V4L2_MBUS_FMT_YUYV8_1X16, V4L2_MBUS_FMT_YUYV8_1X16,
>> +       V4L2_MBUS_FMT_YUYV8_1X16, 0,
>> +       V4L2_PIX_FMT_YUYV, 16, },
>> +};
>> +
>> +const struct iss_format_info *
>> +omap4iss_video_format_info(enum v4l2_mbus_pixelcode code)
>> +{
>> +     unsigned int i;
>> +
>> +     for (i = 0; i < ARRAY_SIZE(formats); ++i) {
>> +             if (formats[i].code == code)
>> +                     return &formats[i];
>> +     }
>> +
>> +     return NULL;
>> +}
>> +
>> +/*
>> + * iss_video_mbus_to_pix - Convert v4l2_mbus_framefmt to v4l2_pix_format
>> + * @video: ISS video instance
>> + * @mbus: v4l2_mbus_framefmt format (input)
>> + * @pix: v4l2_pix_format format (output)
>> + *
>> + * Fill the output pix structure with information from the input mbus format.
>> + * The bytesperline and sizeimage fields are computed from the requested bytes
>> + * per line value in the pix format and information from the video instance.
>> + *
>> + * Return the number of padding bytes at end of line.
>> + */
>> +static unsigned int iss_video_mbus_to_pix(const struct iss_video *video,
>> +                                       const struct v4l2_mbus_framefmt *mbus,
>> +                                       struct v4l2_pix_format *pix)
>> +{
>> +     unsigned int bpl = pix->bytesperline;
>> +     unsigned int min_bpl;
>> +     unsigned int i;
>> +
>> +     memset(pix, 0, sizeof(*pix));
>> +     pix->width = mbus->width;
>> +     pix->height = mbus->height;
>> +
>> +     for (i = 0; i < ARRAY_SIZE(formats); ++i) {
>> +             if (formats[i].code == mbus->code)
>> +                     break;
>> +     }
>> +
>> +     if (WARN_ON(i == ARRAY_SIZE(formats)))
>> +             return 0;
>> +
>> +     min_bpl = pix->width * ALIGN(formats[i].bpp, 8) / 8;
>> +
>> +     /* Clamp the requested bytes per line value. If the maximum bytes per
>> +      * line value is zero, the module doesn't support user configurable line
>> +      * sizes. Override the requested value with the minimum in that case.
>> +      */
>> +     if (video->bpl_max)
>> +             bpl = clamp(bpl, min_bpl, video->bpl_max);
>> +     else
>> +             bpl = min_bpl;
>> +
>> +     if (!video->bpl_zero_padding || bpl != min_bpl)
>> +             bpl = ALIGN(bpl, video->bpl_alignment);
>> +
>> +     pix->pixelformat = formats[i].pixelformat;
>> +     pix->bytesperline = bpl;
>> +     pix->sizeimage = pix->bytesperline * pix->height;
>> +     pix->colorspace = mbus->colorspace;
>> +     pix->field = mbus->field;
>> +
>> +     return bpl - min_bpl;
>> +}
>> +
>> +static void iss_video_pix_to_mbus(const struct v4l2_pix_format *pix,
>> +                               struct v4l2_mbus_framefmt *mbus)
>> +{
>> +     unsigned int i;
>> +
>> +     memset(mbus, 0, sizeof(*mbus));
>> +     mbus->width = pix->width;
>> +     mbus->height = pix->height;
>> +
>> +     for (i = 0; i < ARRAY_SIZE(formats); ++i) {
>> +             if (formats[i].pixelformat == pix->pixelformat)
>> +                     break;
>> +     }
>> +
>> +     if (WARN_ON(i == ARRAY_SIZE(formats)))
>> +             return;
>> +
>> +     mbus->code = formats[i].code;
>> +     mbus->colorspace = pix->colorspace;
>> +     mbus->field = pix->field;
>> +}
>> +
>> +static struct v4l2_subdev *
>> +iss_video_remote_subdev(struct iss_video *video, u32 *pad)
>> +{
>> +     struct media_pad *remote;
>> +
>> +     remote = media_entity_remote_source(&video->pad);
>> +
>> +     if (remote == NULL ||
>> +         media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
>> +             return NULL;
>> +
>> +     if (pad)
>> +             *pad = remote->index;
>> +
>> +     return media_entity_to_v4l2_subdev(remote->entity);
>> +}
>> +
>> +/* Return a pointer to the ISS video instance at the far end of the pipeline. */
>> +static struct iss_video *
>> +iss_video_far_end(struct iss_video *video)
>> +{
>> +     struct media_entity_graph graph;
>> +     struct media_entity *entity = &video->video.entity;
>> +     struct media_device *mdev = entity->parent;
>> +     struct iss_video *far_end = NULL;
>> +
>> +     mutex_lock(&mdev->graph_mutex);
>> +     media_entity_graph_walk_start(&graph, entity);
>> +
>> +     while ((entity = media_entity_graph_walk_next(&graph))) {
>> +             if (entity == &video->video.entity)
>> +                     continue;
>> +
>> +             if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE)
>> +                     continue;
>> +
>> +             far_end = to_iss_video(media_entity_to_video_device(entity));
>> +             if (far_end->type != video->type)
>> +                     break;
>> +
>> +             far_end = NULL;
>> +     }
>> +
>> +     mutex_unlock(&mdev->graph_mutex);
>> +     return far_end;
>> +}
>> +
>> +/*
>> + * Validate a pipeline by checking both ends of all links for format
>> + * discrepancies.
>> + *
>> + * Compute the minimum time per frame value as the maximum of time per frame
>> + * limits reported by every block in the pipeline.
>> + *
>> + * Return 0 if all formats match, or -EPIPE if at least one link is found with
>> + * different formats on its two ends.
>> + */
>> +static int iss_video_validate_pipeline(struct iss_pipeline *pipe)
>> +{
>> +     struct v4l2_subdev_format fmt_source;
>> +     struct v4l2_subdev_format fmt_sink;
>> +     struct media_pad *pad;
>> +     struct v4l2_subdev *subdev;
>> +     int ret;
>> +
>> +     subdev = iss_video_remote_subdev(pipe->output, NULL);
>> +     if (subdev == NULL)
>> +             return -EPIPE;
>> +
>> +     while (1) {
>> +             /* Retrieve the sink format */
>> +             pad = &subdev->entity.pads[0];
>> +             if (!(pad->flags & MEDIA_PAD_FL_SINK))
>> +                     break;
>> +
>> +             fmt_sink.pad = pad->index;
>> +             fmt_sink.which = V4L2_SUBDEV_FORMAT_ACTIVE;
>> +             ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt_sink);
>> +             if (ret < 0 && ret != -ENOIOCTLCMD)
>> +                     return -EPIPE;
>> +
>> +             /* Retrieve the source format */
>> +             pad = media_entity_remote_source(pad);
>> +             if (pad == NULL ||
>> +                 media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
>> +                     break;
>> +
>> +             subdev = media_entity_to_v4l2_subdev(pad->entity);
>> +
>> +             fmt_source.pad = pad->index;
>> +             fmt_source.which = V4L2_SUBDEV_FORMAT_ACTIVE;
>> +             ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt_source);
>> +             if (ret < 0 && ret != -ENOIOCTLCMD)
>> +                     return -EPIPE;
>> +
>> +             /* Check if the two ends match */
>> +             if (fmt_source.format.width != fmt_sink.format.width ||
>> +                 fmt_source.format.height != fmt_sink.format.height)
>> +                     return -EPIPE;
>> +
>> +             if (fmt_source.format.code != fmt_sink.format.code)
>> +                     return -EPIPE;
>> +     }
>> +
>> +     return 0;
>> +}
>
> You won't need pipeline validation function anymore. This will be moved to
> media controller. The patches for the OMAP 3 ISP are one the list:
>
> <URL:http://www.spinics.net/lists/linux-media/msg42991.html>

Ok. Will wait for this to be accepted then.

>
>> +static int
>> +__iss_video_get_format(struct iss_video *video, struct v4l2_format *format)
>> +{
>> +     struct v4l2_subdev_format fmt;
>> +     struct v4l2_subdev *subdev;
>> +     u32 pad;
>> +     int ret;
>> +
>> +     subdev = iss_video_remote_subdev(video, &pad);
>> +     if (subdev == NULL)
>> +             return -EINVAL;
>> +
>> +     mutex_lock(&video->mutex);
>> +
>> +     fmt.pad = pad;
>> +     fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
>> +     ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt);
>> +     if (ret == -ENOIOCTLCMD)
>> +             ret = -EINVAL;
>> +
>> +     mutex_unlock(&video->mutex);
>> +
>> +     if (ret)
>> +             return ret;
>> +
>> +     format->type = video->type;
>> +     return iss_video_mbus_to_pix(video, &fmt.format, &format->fmt.pix);
>> +}
>> +
>> +static int
>> +iss_video_check_format(struct iss_video *video, struct iss_video_fh *vfh)
>> +{
>> +     struct v4l2_format format;
>> +     int ret;
>> +
>> +     memcpy(&format, &vfh->format, sizeof(format));
>> +     ret = __iss_video_get_format(video, &format);
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     if (vfh->format.fmt.pix.pixelformat != format.fmt.pix.pixelformat ||
>> +         vfh->format.fmt.pix.height != format.fmt.pix.height ||
>> +         vfh->format.fmt.pix.width != format.fmt.pix.width ||
>> +         vfh->format.fmt.pix.bytesperline != format.fmt.pix.bytesperline ||
>> +         vfh->format.fmt.pix.sizeimage != format.fmt.pix.sizeimage)
>> +             return -EINVAL;
>> +
>> +     return ret;
>> +}
>> +
>> +/* -----------------------------------------------------------------------------
>> + * Video queue operations
>> + */
>> +
>> +static int iss_video_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
>> +                              unsigned int *count, unsigned int *num_planes,
>> +                              unsigned int sizes[], void *alloc_ctxs[])
>> +{
>> +     struct iss_video_fh *vfh = vb2_get_drv_priv(vq);
>> +     struct iss_video *video = vfh->video;
>> +
>> +     /* Revisit multi-planar support for NV12 */
>> +     *num_planes = 1;
>> +
>> +     sizes[0] = vfh->format.fmt.pix.sizeimage;
>> +     if (sizes[0] == 0)
>> +             return -EINVAL;
>> +
>> +     alloc_ctxs[0] = video->alloc_ctx;
>> +
>> +     *count = min(*count, (unsigned int)(video->capture_mem / PAGE_ALIGN(sizes[0])));
>> +
>> +     return 0;
>> +}
>> +
>> +static void iss_video_buf_cleanup(struct vb2_buffer *vb)
>> +{
>> +     struct iss_buffer *buffer = container_of(vb, struct iss_buffer, vb);
>> +
>> +     if (buffer->iss_addr)
>> +             buffer->iss_addr = 0;
>> +}
>> +
>> +static int iss_video_buf_prepare(struct vb2_buffer *vb)
>> +{
>> +     struct iss_video_fh *vfh = vb2_get_drv_priv(vb->vb2_queue);
>> +     struct iss_buffer *buffer = container_of(vb, struct iss_buffer, vb);
>> +     struct iss_video *video = vfh->video;
>> +     unsigned long size = vfh->format.fmt.pix.sizeimage;
>> +     dma_addr_t addr;
>> +
>> +     if (vb2_plane_size(vb, 0) < size)
>> +             return -ENOBUFS;
>> +
>> +     addr = vb2_dma_contig_plane_dma_addr(vb, 0);
>> +     if (!IS_ALIGNED(addr, 32)) {
>> +             dev_dbg(video->iss->dev, "Buffer address must be "
>> +                     "aligned to 32 bytes boundary.\n");
>> +             return -EINVAL;
>> +     }
>> +
>> +     vb2_set_plane_payload(vb, 0, size);
>> +     buffer->iss_addr = addr;
>> +     return 0;
>> +}
>> +
>> +static void iss_video_buf_queue(struct vb2_buffer *vb)
>> +{
>> +     struct iss_video_fh *vfh = vb2_get_drv_priv(vb->vb2_queue);
>> +     struct iss_video *video = vfh->video;
>> +     struct iss_buffer *buffer = container_of(vb, struct iss_buffer, vb);
>> +     struct iss_pipeline *pipe = to_iss_pipeline(&video->video.entity);
>> +     unsigned int empty;
>> +     unsigned long flags;
>> +
>> +     spin_lock_irqsave(&video->qlock, flags);
>> +     empty = list_empty(&video->dmaqueue);
>> +     list_add_tail(&buffer->list, &video->dmaqueue);
>> +     spin_unlock_irqrestore(&video->qlock, flags);
>> +
>> +     if (empty) {
>> +             enum iss_pipeline_state state;
>> +             unsigned int start;
>> +
>> +             if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
>> +                     state = ISS_PIPELINE_QUEUE_OUTPUT;
>> +             else
>> +                     state = ISS_PIPELINE_QUEUE_INPUT;
>> +
>> +             spin_lock_irqsave(&pipe->lock, flags);
>> +             pipe->state |= state;
>> +             video->ops->queue(video, buffer);
>> +             video->dmaqueue_flags |= ISS_VIDEO_DMAQUEUE_QUEUED;
>> +
>> +             start = iss_pipeline_ready(pipe);
>> +             if (start)
>> +                     pipe->state |= ISS_PIPELINE_STREAM;
>> +             spin_unlock_irqrestore(&pipe->lock, flags);
>> +
>> +             if (start)
>> +                     omap4iss_pipeline_set_stream(pipe,
>> +                                             ISS_PIPELINE_STREAM_SINGLESHOT);
>> +     }
>> +}
>> +
>> +static struct vb2_ops iss_video_vb2ops = {
>> +     .queue_setup    = iss_video_queue_setup,
>> +     .buf_prepare    = iss_video_buf_prepare,
>> +     .buf_queue      = iss_video_buf_queue,
>> +     .buf_cleanup    = iss_video_buf_cleanup,
>> +};
>> +
>> +/*
>> + * omap4iss_video_buffer_next - Complete the current buffer and return the next
>> + * @video: ISS video object
>> + * @error: Whether an error occurred during capture
>> + *
>> + * Remove the current video buffer from the DMA queue and fill its timestamp,
>> + * field count and state fields before waking up its completion handler.
>> + *
>> + * The buffer state is set to VIDEOBUF_DONE if no error occurred (@error is 0)
>> + * or VIDEOBUF_ERROR otherwise (@error is non-zero).
>> + *
>> + * The DMA queue is expected to contain at least one buffer.
>> + *
>> + * Return a pointer to the next buffer in the DMA queue, or NULL if the queue is
>> + * empty.
>> + */
>> +struct iss_buffer *omap4iss_video_buffer_next(struct iss_video *video,
>> +                                           unsigned int error)
>> +{
>> +     struct iss_pipeline *pipe = to_iss_pipeline(&video->video.entity);
>> +     enum iss_pipeline_state state;
>> +     struct iss_buffer *buf;
>> +     unsigned long flags;
>> +     struct timespec ts;
>> +
>> +     spin_lock_irqsave(&video->qlock, flags);
>> +     if (WARN_ON(list_empty(&video->dmaqueue))) {
>> +             spin_unlock_irqrestore(&video->qlock, flags);
>> +             return NULL;
>> +     }
>> +
>> +     buf = list_first_entry(&video->dmaqueue, struct iss_buffer,
>> +                            list);
>> +     list_del(&buf->list);
>> +     spin_unlock_irqrestore(&video->qlock, flags);
>> +
>> +     ktime_get_ts(&ts);
>> +     buf->vb.v4l2_buf.timestamp.tv_sec = ts.tv_sec;
>> +     buf->vb.v4l2_buf.timestamp.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
>> +
>> +     /* Do frame number propagation only if this is the output video node.
>> +      * Frame number either comes from the CSI receivers or it gets
>> +      * incremented here if H3A is not active.
>> +      * Note: There is no guarantee that the output buffer will finish
>> +      * first, so the input number might lag behind by 1 in some cases.
>> +      */
>> +     if (video == pipe->output && !pipe->do_propagation)
>> +             buf->vb.v4l2_buf.sequence = atomic_inc_return(&pipe->frame_number);
>> +     else
>> +             buf->vb.v4l2_buf.sequence = atomic_read(&pipe->frame_number);
>> +
>> +     vb2_buffer_done(&buf->vb, error < 0 ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
>> +
>> +     spin_lock_irqsave(&video->qlock, flags);
>> +     if (list_empty(&video->dmaqueue)) {
>> +             spin_unlock_irqrestore(&video->qlock, flags);
>> +             if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
>> +                     state = ISS_PIPELINE_QUEUE_OUTPUT
>> +                           | ISS_PIPELINE_STREAM;
>> +             else
>> +                     state = ISS_PIPELINE_QUEUE_INPUT
>> +                           | ISS_PIPELINE_STREAM;
>> +
>> +             spin_lock_irqsave(&pipe->lock, flags);
>> +             pipe->state &= ~state;
>> +             if (video->pipe.stream_state == ISS_PIPELINE_STREAM_CONTINUOUS)
>> +                     video->dmaqueue_flags |= ISS_VIDEO_DMAQUEUE_UNDERRUN;
>> +             spin_unlock_irqrestore(&pipe->lock, flags);
>> +             return NULL;
>> +     }
>> +
>> +     if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->input != NULL) {
>> +             spin_lock_irqsave(&pipe->lock, flags);
>> +             pipe->state &= ~ISS_PIPELINE_STREAM;
>> +             spin_unlock_irqrestore(&pipe->lock, flags);
>> +     }
>> +
>> +     buf = list_first_entry(&video->dmaqueue, struct iss_buffer,
>> +                            list);
>> +     spin_unlock_irqrestore(&video->qlock, flags);
>> +     buf->vb.state = VB2_BUF_STATE_ACTIVE;
>> +     return buf;
>> +}
>> +
>> +/* -----------------------------------------------------------------------------
>> + * V4L2 ioctls
>> + */
>> +
>> +static int
>> +iss_video_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
>> +{
>> +     struct iss_video *video = video_drvdata(file);
>> +
>> +     strlcpy(cap->driver, ISS_VIDEO_DRIVER_NAME, sizeof(cap->driver));
>> +     strlcpy(cap->card, video->video.name, sizeof(cap->card));
>> +     strlcpy(cap->bus_info, "media", sizeof(cap->bus_info));
>> +     cap->version = ISS_VIDEO_DRIVER_VERSION;
>> +
>> +     if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
>> +             cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
>> +     else
>> +             cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
>> +
>> +     return 0;
>> +}
>> +
>> +static int
>> +iss_video_get_format(struct file *file, void *fh, struct v4l2_format *format)
>> +{
>> +     struct iss_video_fh *vfh = to_iss_video_fh(fh);
>> +     struct iss_video *video = video_drvdata(file);
>> +
>> +     if (format->type != video->type)
>> +             return -EINVAL;
>> +
>> +     mutex_lock(&video->mutex);
>> +     *format = vfh->format;
>> +     mutex_unlock(&video->mutex);
>> +
>> +     return 0;
>> +}
>> +
>> +static int
>> +iss_video_set_format(struct file *file, void *fh, struct v4l2_format *format)
>> +{
>> +     struct iss_video_fh *vfh = to_iss_video_fh(fh);
>> +     struct iss_video *video = video_drvdata(file);
>> +     struct v4l2_mbus_framefmt fmt;
>> +
>> +     if (format->type != video->type)
>> +             return -EINVAL;
>> +
>> +     mutex_lock(&video->mutex);
>> +
>> +     /* Fill the bytesperline and sizeimage fields by converting to media bus
>> +      * format and back to pixel format.
>> +      */
>> +     iss_video_pix_to_mbus(&format->fmt.pix, &fmt);
>> +     iss_video_mbus_to_pix(video, &fmt, &format->fmt.pix);
>> +
>> +     vfh->format = *format;
>> +
>> +     mutex_unlock(&video->mutex);
>> +     return 0;
>> +}
>> +
>> +static int
>> +iss_video_try_format(struct file *file, void *fh, struct v4l2_format *format)
>> +{
>> +     struct iss_video *video = video_drvdata(file);
>> +     struct v4l2_subdev_format fmt;
>> +     struct v4l2_subdev *subdev;
>> +     u32 pad;
>> +     int ret;
>> +
>> +     if (format->type != video->type)
>> +             return -EINVAL;
>> +
>> +     subdev = iss_video_remote_subdev(video, &pad);
>> +     if (subdev == NULL)
>> +             return -EINVAL;
>> +
>> +     iss_video_pix_to_mbus(&format->fmt.pix, &fmt.format);
>> +
>> +     fmt.pad = pad;
>> +     fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
>> +     ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt);
>> +     if (ret)
>> +             return ret == -ENOIOCTLCMD ? -EINVAL : ret;
>> +
>> +     iss_video_mbus_to_pix(video, &fmt.format, &format->fmt.pix);
>> +     return 0;
>> +}
>> +
>> +static int
>> +iss_video_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cropcap)
>> +{
>> +     struct iss_video *video = video_drvdata(file);
>> +     struct v4l2_subdev *subdev;
>> +     int ret;
>> +
>> +     subdev = iss_video_remote_subdev(video, NULL);
>> +     if (subdev == NULL)
>> +             return -EINVAL;
>> +
>> +     mutex_lock(&video->mutex);
>> +     ret = v4l2_subdev_call(subdev, video, cropcap, cropcap);
>> +     mutex_unlock(&video->mutex);
>> +
>> +     return ret == -ENOIOCTLCMD ? -EINVAL : ret;
>> +}
>> +
>> +static int
>> +iss_video_get_crop(struct file *file, void *fh, struct v4l2_crop *crop)
>> +{
>> +     struct iss_video *video = video_drvdata(file);
>> +     struct v4l2_subdev_format format;
>> +     struct v4l2_subdev *subdev;
>> +     u32 pad;
>> +     int ret;
>> +
>> +     subdev = iss_video_remote_subdev(video, &pad);
>> +     if (subdev == NULL)
>> +             return -EINVAL;
>> +
>> +     /* Try the get crop operation first and fallback to get format if not
>> +      * implemented.
>> +      */
>> +     ret = v4l2_subdev_call(subdev, video, g_crop, crop);
>> +     if (ret != -ENOIOCTLCMD)
>> +             return ret;
>> +
>> +     format.pad = pad;
>> +     format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
>> +     ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &format);
>> +     if (ret < 0)
>> +             return ret == -ENOIOCTLCMD ? -EINVAL : ret;
>> +
>> +     crop->c.left = 0;
>> +     crop->c.top = 0;
>> +     crop->c.width = format.format.width;
>> +     crop->c.height = format.format.height;
>> +
>> +     return 0;
>> +}
>> +
>> +static int
>> +iss_video_set_crop(struct file *file, void *fh, struct v4l2_crop *crop)
>> +{
>> +     struct iss_video *video = video_drvdata(file);
>> +     struct v4l2_subdev *subdev;
>> +     int ret;
>> +
>> +     subdev = iss_video_remote_subdev(video, NULL);
>> +     if (subdev == NULL)
>> +             return -EINVAL;
>> +
>> +     mutex_lock(&video->mutex);
>> +     ret = v4l2_subdev_call(subdev, video, s_crop, crop);
>> +     mutex_unlock(&video->mutex);
>> +
>> +     return ret == -ENOIOCTLCMD ? -EINVAL : ret;
>> +}
>> +
>> +static int
>> +iss_video_get_param(struct file *file, void *fh, struct v4l2_streamparm *a)
>> +{
>> +     struct iss_video_fh *vfh = to_iss_video_fh(fh);
>> +     struct iss_video *video = video_drvdata(file);
>> +
>> +     if (video->type != V4L2_BUF_TYPE_VIDEO_OUTPUT ||
>> +         video->type != a->type)
>> +             return -EINVAL;
>> +
>> +     memset(a, 0, sizeof(*a));
>> +     a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
>> +     a->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
>> +     a->parm.output.timeperframe = vfh->timeperframe;
>> +
>> +     return 0;
>> +}
>> +
>> +static int
>> +iss_video_set_param(struct file *file, void *fh, struct v4l2_streamparm *a)
>> +{
>> +     struct iss_video_fh *vfh = to_iss_video_fh(fh);
>> +     struct iss_video *video = video_drvdata(file);
>> +
>> +     if (video->type != V4L2_BUF_TYPE_VIDEO_OUTPUT ||
>> +         video->type != a->type)
>> +             return -EINVAL;
>> +
>> +     if (a->parm.output.timeperframe.denominator == 0)
>> +             a->parm.output.timeperframe.denominator = 1;
>> +
>> +     vfh->timeperframe = a->parm.output.timeperframe;
>> +
>> +     return 0;
>> +}
>> +
>> +static int
>> +iss_video_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *rb)
>> +{
>> +     struct iss_video_fh *vfh = to_iss_video_fh(fh);
>> +
>> +     return vb2_reqbufs(&vfh->queue, rb);
>> +}
>> +
>> +static int
>> +iss_video_querybuf(struct file *file, void *fh, struct v4l2_buffer *b)
>> +{
>> +     struct iss_video_fh *vfh = to_iss_video_fh(fh);
>> +
>> +     return vb2_querybuf(&vfh->queue, b);
>> +}
>> +
>> +static int
>> +iss_video_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
>> +{
>> +     struct iss_video_fh *vfh = to_iss_video_fh(fh);
>> +
>> +     return vb2_qbuf(&vfh->queue, b);
>> +}
>> +
>> +static int
>> +iss_video_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
>> +{
>> +     struct iss_video_fh *vfh = to_iss_video_fh(fh);
>> +
>> +     return vb2_dqbuf(&vfh->queue, b, file->f_flags & O_NONBLOCK);
>> +}
>> +
>> +/*
>> + * Stream management
>> + *
>> + * Every ISS pipeline has a single input and a single output. The input can be
>> + * either a sensor or a video node. The output is always a video node.
>> + *
>> + * As every pipeline has an output video node, the ISS video objects at the
>> + * pipeline output stores the pipeline state. It tracks the streaming state of
>> + * both the input and output, as well as the availability of buffers.
>> + *
>> + * In sensor-to-memory mode, frames are always available at the pipeline input.
>> + * Starting the sensor usually requires I2C transfers and must be done in
>> + * interruptible context. The pipeline is started and stopped synchronously
>> + * to the stream on/off commands. All modules in the pipeline will get their
>> + * subdev set stream handler called. The module at the end of the pipeline must
>> + * delay starting the hardware until buffers are available at its output.
>> + *
>> + * In memory-to-memory mode, starting/stopping the stream requires
>> + * synchronization between the input and output. ISS modules can't be stopped
>> + * in the middle of a frame, and at least some of the modules seem to become
>> + * busy as soon as they're started, even if they don't receive a frame start
>> + * event. For that reason frames need to be processed in single-shot mode. The
>> + * driver needs to wait until a frame is completely processed and written to
>> + * memory before restarting the pipeline for the next frame. Pipelined
>> + * processing might be possible but requires more testing.
>> + *
>> + * Stream start must be delayed until buffers are available at both the input
>> + * and output. The pipeline must be started in the videobuf queue callback with
>> + * the buffers queue spinlock held. The modules subdev set stream operation must
>> + * not sleep.
>> + */
>> +static int
>> +iss_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
>> +{
>> +     struct iss_video_fh *vfh = to_iss_video_fh(fh);
>> +     struct iss_video *video = video_drvdata(file);
>> +     enum iss_pipeline_state state;
>> +     struct iss_pipeline *pipe;
>> +     struct iss_video *far_end;
>> +     unsigned long flags;
>> +     int ret;
>> +
>> +     if (type != video->type)
>> +             return -EINVAL;
>> +
>> +     mutex_lock(&video->stream_lock);
>> +
>> +     if (video->streaming) {
>> +             mutex_unlock(&video->stream_lock);
>> +             return -EBUSY;
>> +     }
>> +
>> +     /* Start streaming on the pipeline. No link touching an entity in the
>> +      * pipeline can be activated or deactivated once streaming is started.
>> +      */
>> +     pipe = video->video.entity.pipe
>> +          ? to_iss_pipeline(&video->video.entity) : &video->pipe;
>> +     media_entity_pipeline_start(&video->video.entity, &pipe->pipe);
>> +
>> +     /* Verify that the currently configured format matches the output of
>> +      * the connected subdev.
>> +      */
>> +     ret = iss_video_check_format(video, vfh);
>> +     if (ret < 0)
>> +             goto error;
>> +
>> +     video->bpl_padding = ret;
>> +     video->bpl_value = vfh->format.fmt.pix.bytesperline;
>> +
>> +     /* Find the ISS video node connected at the far end of the pipeline and
>> +      * update the pipeline.
>> +      */
>> +     far_end = iss_video_far_end(video);
>> +
>> +     if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
>> +             state = ISS_PIPELINE_STREAM_OUTPUT | ISS_PIPELINE_IDLE_OUTPUT;
>> +             pipe->input = far_end;
>> +             pipe->output = video;
>> +     } else {
>> +             if (far_end == NULL) {
>> +                     ret = -EPIPE;
>> +                     goto error;
>> +             }
>> +
>> +             state = ISS_PIPELINE_STREAM_INPUT | ISS_PIPELINE_IDLE_INPUT;
>> +             pipe->input = video;
>> +             pipe->output = far_end;
>> +     }
>> +
>> +     if (video->iss->pdata->set_constraints)
>> +             video->iss->pdata->set_constraints(video->iss, true);
>> +
>> +     /* Validate the pipeline and update its state. */
>> +     ret = iss_video_validate_pipeline(pipe);
>> +     if (ret < 0)
>> +             goto error;
>> +
>> +     spin_lock_irqsave(&pipe->lock, flags);
>> +     pipe->state &= ~ISS_PIPELINE_STREAM;
>> +     pipe->state |= state;
>> +     spin_unlock_irqrestore(&pipe->lock, flags);
>> +
>> +     /* Set the maximum time per frame as the value requested by userspace.
>> +      * This is a soft limit that can be overridden if the hardware doesn't
>> +      * support the request limit.
>> +      */
>> +     if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
>> +             pipe->max_timeperframe = vfh->timeperframe;
>> +
>> +     video->queue = &vfh->queue;
>> +     INIT_LIST_HEAD(&video->dmaqueue);
>> +     spin_lock_init(&video->qlock);
>> +     atomic_set(&pipe->frame_number, -1);
>> +
>> +     ret = vb2_streamon(&vfh->queue, type);
>> +     if (ret < 0)
>> +             goto error;
>> +
>> +     /* In sensor-to-memory mode, the stream can be started synchronously
>> +      * to the stream on command. In memory-to-memory mode, it will be
>> +      * started when buffers are queued on both the input and output.
>> +      */
>> +     if (pipe->input == NULL) {
>> +             unsigned long flags;
>> +             ret = omap4iss_pipeline_set_stream(pipe,
>> +                                           ISS_PIPELINE_STREAM_CONTINUOUS);
>> +             if (ret < 0)
>> +                     goto error;
>> +             spin_lock_irqsave(&video->qlock, flags);
>> +             if (list_empty(&video->dmaqueue))
>> +                     video->dmaqueue_flags |= ISS_VIDEO_DMAQUEUE_UNDERRUN;
>> +             spin_unlock_irqrestore(&video->qlock, flags);
>> +     }
>> +
>> +error:
>> +     if (ret < 0) {
>> +             vb2_streamoff(&vfh->queue, type);
>> +             if (video->iss->pdata->set_constraints)
>> +                     video->iss->pdata->set_constraints(video->iss, false);
>> +             media_entity_pipeline_stop(&video->video.entity);
>> +             video->queue = NULL;
>> +     }
>> +
>> +     if (!ret)
>> +             video->streaming = 1;
>> +
>> +     mutex_unlock(&video->stream_lock);
>> +     return ret;
>> +}
>> +
>> +static int
>> +iss_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type)
>> +{
>> +     struct iss_video_fh *vfh = to_iss_video_fh(fh);
>> +     struct iss_video *video = video_drvdata(file);
>> +     struct iss_pipeline *pipe = to_iss_pipeline(&video->video.entity);
>> +     enum iss_pipeline_state state;
>> +     unsigned long flags;
>> +
>> +     if (type != video->type)
>> +             return -EINVAL;
>> +
>> +     mutex_lock(&video->stream_lock);
>> +
>> +     if (!vb2_is_streaming(&vfh->queue))
>> +             goto done;
>> +
>> +     /* Update the pipeline state. */
>> +     if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
>> +             state = ISS_PIPELINE_STREAM_OUTPUT
>> +                   | ISS_PIPELINE_QUEUE_OUTPUT;
>> +     else
>> +             state = ISS_PIPELINE_STREAM_INPUT
>> +                   | ISS_PIPELINE_QUEUE_INPUT;
>> +
>> +     spin_lock_irqsave(&pipe->lock, flags);
>> +     pipe->state &= ~state;
>> +     spin_unlock_irqrestore(&pipe->lock, flags);
>> +
>> +     /* Stop the stream. */
>> +     omap4iss_pipeline_set_stream(pipe, ISS_PIPELINE_STREAM_STOPPED);
>> +     vb2_streamoff(&vfh->queue, type);
>> +     video->queue = NULL;
>> +     video->streaming = 0;
>> +
>> +     if (video->iss->pdata->set_constraints)
>> +             video->iss->pdata->set_constraints(video->iss, false);
>> +     media_entity_pipeline_stop(&video->video.entity);
>> +
>> +done:
>> +     mutex_unlock(&video->stream_lock);
>> +     return 0;
>> +}
>> +
>> +static int
>> +iss_video_enum_input(struct file *file, void *fh, struct v4l2_input *input)
>> +{
>> +     if (input->index > 0)
>> +             return -EINVAL;
>> +
>> +     strlcpy(input->name, "camera", sizeof(input->name));
>> +     input->type = V4L2_INPUT_TYPE_CAMERA;
>> +
>> +     return 0;
>> +}
>> +
>> +static int
>> +iss_video_g_input(struct file *file, void *fh, unsigned int *input)
>> +{
>> +     *input = 0;
>> +
>> +     return 0;
>> +}
>> +
>> +static int
>> +iss_video_s_input(struct file *file, void *fh, unsigned int input)
>> +{
>> +     return input == 0 ? 0 : -EINVAL;
>> +}
>
> While it's possible to support s_input support it provides no useful
> functionality for the user. The OMAP 3 ISP driver also supports it. I'd just
> remove it.

Ok. Will fix.

>
> The same will be properly provided to apps by the libv4l2pile(auto) in the
> future.
>
>> +static const struct v4l2_ioctl_ops iss_video_ioctl_ops = {
>> +     .vidioc_querycap                = iss_video_querycap,
>> +     .vidioc_g_fmt_vid_cap           = iss_video_get_format,
>> +     .vidioc_s_fmt_vid_cap           = iss_video_set_format,
>> +     .vidioc_try_fmt_vid_cap         = iss_video_try_format,
>> +     .vidioc_g_fmt_vid_out           = iss_video_get_format,
>> +     .vidioc_s_fmt_vid_out           = iss_video_set_format,
>> +     .vidioc_try_fmt_vid_out         = iss_video_try_format,
>> +     .vidioc_cropcap                 = iss_video_cropcap,
>> +     .vidioc_g_crop                  = iss_video_get_crop,
>> +     .vidioc_s_crop                  = iss_video_set_crop,
>> +     .vidioc_g_parm                  = iss_video_get_param,
>> +     .vidioc_s_parm                  = iss_video_set_param,
>> +     .vidioc_reqbufs                 = iss_video_reqbufs,
>> +     .vidioc_querybuf                = iss_video_querybuf,
>> +     .vidioc_qbuf                    = iss_video_qbuf,
>> +     .vidioc_dqbuf                   = iss_video_dqbuf,
>> +     .vidioc_streamon                = iss_video_streamon,
>> +     .vidioc_streamoff               = iss_video_streamoff,
>> +     .vidioc_enum_input              = iss_video_enum_input,
>> +     .vidioc_g_input                 = iss_video_g_input,
>> +     .vidioc_s_input                 = iss_video_s_input,
>> +};
>> +
>> +/* -----------------------------------------------------------------------------
>> + * V4L2 file operations
>> + */
>> +
>> +static int iss_video_open(struct file *file)
>> +{
>> +     struct iss_video *video = video_drvdata(file);
>> +     struct iss_video_fh *handle;
>> +     struct vb2_queue *q;
>> +     int ret = 0;
>> +
>> +     handle = kzalloc(sizeof(*handle), GFP_KERNEL);
>> +     if (handle == NULL)
>> +             return -ENOMEM;
>> +
>> +     v4l2_fh_init(&handle->vfh, &video->video);
>> +     v4l2_fh_add(&handle->vfh);
>> +
>> +     /* If this is the first user, initialise the pipeline. */
>> +     if (omap4iss_get(video->iss) == NULL) {
>> +             ret = -EBUSY;
>> +             goto done;
>> +     }
>> +
>> +     ret = omap4iss_pipeline_pm_use(&video->video.entity, 1);
>> +     if (ret < 0) {
>> +             omap4iss_put(video->iss);
>> +             goto done;
>> +     }
>> +
>> +     video->alloc_ctx = vb2_dma_contig_init_ctx(video->iss->dev);
>> +     if (IS_ERR(video->alloc_ctx)) {
>> +             ret = PTR_ERR(video->alloc_ctx);
>> +             omap4iss_put(video->iss);
>> +             goto done;
>> +     }
>> +
>> +     q = &handle->queue;
>> +
>> +     q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>> +     q->io_modes = VB2_MMAP;
>> +     q->drv_priv = handle;
>> +     q->ops = &iss_video_vb2ops;
>> +     q->mem_ops = &vb2_dma_contig_memops;
>> +     q->buf_struct_size = sizeof(struct iss_buffer);
>> +
>> +     ret = vb2_queue_init(q);
>> +     if (ret) {
>> +             omap4iss_put(video->iss);
>> +             goto done;
>> +     }
>> +
>> +     memset(&handle->format, 0, sizeof(handle->format));
>> +     handle->format.type = video->type;
>> +     handle->timeperframe.denominator = 1;
>> +
>> +     handle->video = video;
>> +     file->private_data = &handle->vfh;
>> +
>> +done:
>> +     if (ret < 0) {
>> +             v4l2_fh_del(&handle->vfh);
>> +             kfree(handle);
>> +     }
>> +
>> +     return ret;
>> +}
>> +
>> +static int iss_video_release(struct file *file)
>> +{
>> +     struct iss_video *video = video_drvdata(file);
>> +     struct v4l2_fh *vfh = file->private_data;
>> +     struct iss_video_fh *handle = to_iss_video_fh(vfh);
>> +
>> +     /* Disable streaming and free the buffers queue resources. */
>> +     iss_video_streamoff(file, vfh, video->type);
>> +
>> +     omap4iss_pipeline_pm_use(&video->video.entity, 0);
>> +
>> +     /* Release the file handle. */
>> +     v4l2_fh_del(vfh);
>> +     kfree(handle);
>> +     file->private_data = NULL;
>> +
>> +     omap4iss_put(video->iss);
>> +
>> +     return 0;
>> +}
>> +
>> +static unsigned int iss_video_poll(struct file *file, poll_table *wait)
>> +{
>> +     struct iss_video_fh *vfh = to_iss_video_fh(file->private_data);
>> +
>> +     return vb2_poll(&vfh->queue, file, wait);
>> +}
>> +
>> +static int iss_video_mmap(struct file *file, struct vm_area_struct *vma)
>> +{
>> +     struct iss_video_fh *vfh = to_iss_video_fh(file->private_data);
>> +
>> +     return vb2_mmap(&vfh->queue, vma);;
>> +}
>> +
>> +static struct v4l2_file_operations iss_video_fops = {
>> +     .owner = THIS_MODULE,
>> +     .unlocked_ioctl = video_ioctl2,
>> +     .open = iss_video_open,
>> +     .release = iss_video_release,
>> +     .poll = iss_video_poll,
>> +     .mmap = iss_video_mmap,
>> +};
>> +
>> +/* -----------------------------------------------------------------------------
>> + * ISS video core
>> + */
>> +
>> +static const struct iss_video_operations iss_video_dummy_ops = {
>> +};
>> +
>> +int omap4iss_video_init(struct iss_video *video, const char *name)
>> +{
>> +     const char *direction;
>> +     int ret;
>> +
>> +     switch (video->type) {
>> +     case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>> +             direction = "output";
>> +             video->pad.flags = MEDIA_PAD_FL_SINK;
>> +             break;
>> +     case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>> +             direction = "input";
>> +             video->pad.flags = MEDIA_PAD_FL_SOURCE;
>> +             break;
>> +
>> +     default:
>> +             return -EINVAL;
>> +     }
>> +
>> +     ret = media_entity_init(&video->video.entity, 1, &video->pad, 0);
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     mutex_init(&video->mutex);
>> +     atomic_set(&video->active, 0);
>> +
>> +     spin_lock_init(&video->pipe.lock);
>> +     mutex_init(&video->stream_lock);
>> +
>> +     /* Initialize the video device. */
>> +     if (video->ops == NULL)
>> +             video->ops = &iss_video_dummy_ops;
>> +
>> +     video->video.fops = &iss_video_fops;
>> +     snprintf(video->video.name, sizeof(video->video.name),
>> +              "OMAP4 ISS %s %s", name, direction);
>> +     video->video.vfl_type = VFL_TYPE_GRABBER;
>> +     video->video.release = video_device_release_empty;
>> +     video->video.ioctl_ops = &iss_video_ioctl_ops;
>> +     video->pipe.stream_state = ISS_PIPELINE_STREAM_STOPPED;
>> +
>> +     video_set_drvdata(&video->video, video);
>> +
>> +     return 0;
>> +}
>> +
>> +int omap4iss_video_register(struct iss_video *video, struct v4l2_device *vdev)
>> +{
>> +     int ret;
>> +
>> +     video->video.v4l2_dev = vdev;
>> +
>> +     ret = video_register_device(&video->video, VFL_TYPE_GRABBER, -1);
>> +     if (ret < 0)
>> +             printk(KERN_ERR "%s: could not register video device (%d)\n",
>> +                     __func__, ret);
>> +
>> +     return ret;
>> +}
>> +
>> +void omap4iss_video_unregister(struct iss_video *video)
>> +{
>> +     if (video_is_registered(&video->video)) {
>> +             media_entity_cleanup(&video->video.entity);
>> +             video_unregister_device(&video->video);
>> +     }
>> +}
>> diff --git a/drivers/media/video/omap4iss/iss_video.h b/drivers/media/video/omap4iss/iss_video.h
>> new file mode 100644
>> index 0000000..fc123b0
>> --- /dev/null
>> +++ b/drivers/media/video/omap4iss/iss_video.h
>> @@ -0,0 +1,205 @@
>> +/*
>> + * iss_video.h
>> + *
>> + * TI OMAP4 ISS - Generic video node
>> + *
>> + * Copyright (C) 2011 Texas Instruments, Inc.
>> + *
>> + * Contacts: Sergio Aguirre <saaguirre@ti.com>
>> + *
>> + * 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.
>> + *
>> + * This program is distributed in the hope that it will be useful, but
>> + * WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write to the Free Software
>> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
>> + * 02110-1301 USA
>> + */
>> +
>> +#ifndef OMAP4_ISS_VIDEO_H
>> +#define OMAP4_ISS_VIDEO_H
>> +
>> +#include <linux/v4l2-mediabus.h>
>> +#include <linux/version.h>
>> +#include <media/media-entity.h>
>> +#include <media/v4l2-dev.h>
>> +#include <media/v4l2-fh.h>
>> +#include <media/videobuf2-core.h>
>> +#include <media/videobuf2-dma-contig.h>
>> +
>> +#define ISS_VIDEO_DRIVER_NAME                "issvideo"
>> +#define ISS_VIDEO_DRIVER_VERSION     KERNEL_VERSION(0, 0, 1)
>> +
>> +struct iss_device;
>> +struct iss_video;
>> +struct v4l2_mbus_framefmt;
>> +struct v4l2_pix_format;
>> +
>> +/*
>> + * struct iss_format_info - ISS media bus format information
>> + * @code: V4L2 media bus format code
>> + * @truncated: V4L2 media bus format code for the same format truncated to 10
>> + *   bits. Identical to @code if the format is 10 bits wide or less.
>> + * @uncompressed: V4L2 media bus format code for the corresponding uncompressed
>> + *   format. Identical to @code if the format is not DPCM compressed.
>> + * @flavor: V4L2 media bus format code for the same pixel layout but
>> + *   shifted to be 8 bits per pixel. =0 if format is not shiftable.
>> + * @pixelformat: V4L2 pixel format FCC identifier
>> + * @bpp: Bits per pixel
>> + */
>> +struct iss_format_info {
>> +     enum v4l2_mbus_pixelcode code;
>> +     enum v4l2_mbus_pixelcode truncated;
>> +     enum v4l2_mbus_pixelcode uncompressed;
>> +     enum v4l2_mbus_pixelcode flavor;
>> +     u32 pixelformat;
>> +     unsigned int bpp;
>> +};
>> +
>> +enum iss_pipeline_stream_state {
>> +     ISS_PIPELINE_STREAM_STOPPED = 0,
>> +     ISS_PIPELINE_STREAM_CONTINUOUS = 1,
>> +     ISS_PIPELINE_STREAM_SINGLESHOT = 2,
>> +};
>> +
>> +enum iss_pipeline_state {
>> +     /* The stream has been started on the input video node. */
>> +     ISS_PIPELINE_STREAM_INPUT = 1,
>> +     /* The stream has been started on the output video node. */
>> +     ISS_PIPELINE_STREAM_OUTPUT = 2,
>> +     /* At least one buffer is queued on the input video node. */
>> +     ISS_PIPELINE_QUEUE_INPUT = 4,
>> +     /* At least one buffer is queued on the output video node. */
>> +     ISS_PIPELINE_QUEUE_OUTPUT = 8,
>> +     /* The input entity is idle, ready to be started. */
>> +     ISS_PIPELINE_IDLE_INPUT = 16,
>> +     /* The output entity is idle, ready to be started. */
>> +     ISS_PIPELINE_IDLE_OUTPUT = 32,
>> +     /* The pipeline is currently streaming. */
>> +     ISS_PIPELINE_STREAM = 64,
>
> I'd suggest using 1 << n to define flags instead.

Agreed. Will fix.

>
>> +};
>> +
>> +struct iss_pipeline {
>> +     struct media_pipeline pipe;
>> +     spinlock_t lock;                /* Pipeline state and queue flags */
>> +     unsigned int state;
>> +     enum iss_pipeline_stream_state stream_state;
>> +     struct iss_video *input;
>> +     struct iss_video *output;
>> +     atomic_t frame_number;
>> +     bool do_propagation; /* of frame number */
>> +     struct v4l2_fract max_timeperframe;
>> +};
>> +
>> +#define to_iss_pipeline(__e) \
>> +     container_of((__e)->pipe, struct iss_pipeline, pipe)
>> +
>> +static inline int iss_pipeline_ready(struct iss_pipeline *pipe)
>> +{
>> +     return pipe->state == (ISS_PIPELINE_STREAM_INPUT |
>> +                            ISS_PIPELINE_STREAM_OUTPUT |
>> +                            ISS_PIPELINE_QUEUE_INPUT |
>> +                            ISS_PIPELINE_QUEUE_OUTPUT |
>> +                            ISS_PIPELINE_IDLE_INPUT |
>> +                            ISS_PIPELINE_IDLE_OUTPUT);
>> +}
>> +
>> +/*
>> + * struct iss_buffer - ISS buffer
>> + * @buffer: ISS video buffer
>> + * @iss_addr: Physical address of the buffer.
>> + */
>> +struct iss_buffer {
>> +     /* common v4l buffer stuff -- must be first */
>> +     struct vb2_buffer       vb;
>> +     struct list_head        list;
>> +     dma_addr_t iss_addr;
>> +};
>> +
>> +#define to_iss_buffer(buf)   container_of(buf, struct iss_buffer, buffer)
>> +
>> +enum iss_video_dmaqueue_flags {
>> +     /* Set if DMA queue becomes empty when ISS_PIPELINE_STREAM_CONTINUOUS */
>> +     ISS_VIDEO_DMAQUEUE_UNDERRUN = (1 << 0),
>> +     /* Set when queuing buffer to an empty DMA queue */
>> +     ISS_VIDEO_DMAQUEUE_QUEUED = (1 << 1),
>> +};
>> +
>> +#define iss_video_dmaqueue_flags_clr(video)  \
>> +                     ({ (video)->dmaqueue_flags = 0; })
>> +
>> +/*
>> + * struct iss_video_operations - ISS video operations
>> + * @queue:   Resume streaming when a buffer is queued. Called on VIDIOC_QBUF
>> + *           if there was no buffer previously queued.
>> + */
>> +struct iss_video_operations {
>> +     int(*queue)(struct iss_video *video, struct iss_buffer *buffer);
>> +};
>> +
>> +struct iss_video {
>> +     struct video_device video;
>> +     enum v4l2_buf_type type;
>> +     struct media_pad pad;
>> +
>> +     struct mutex mutex;             /* format and crop settings */
>> +     atomic_t active;
>> +
>> +     struct iss_device *iss;
>> +
>> +     unsigned int capture_mem;
>> +     unsigned int bpl_alignment;     /* alignment value */
>> +     unsigned int bpl_zero_padding;  /* whether the alignment is optional */
>> +     unsigned int bpl_max;           /* maximum bytes per line value */
>> +     unsigned int bpl_value;         /* bytes per line value */
>> +     unsigned int bpl_padding;       /* padding at end of line */
>> +
>> +     /* Entity video node streaming */
>> +     unsigned int streaming:1;
>> +
>> +     /* Pipeline state */
>> +     struct iss_pipeline pipe;
>> +     struct mutex stream_lock;       /* pipeline and stream states */
>> +
>> +     /* Video buffers queue */
>> +     struct vb2_queue *queue;
>> +     spinlock_t qlock;       /* Spinlock for dmaqueue */
>> +     struct list_head dmaqueue;
>> +     enum iss_video_dmaqueue_flags dmaqueue_flags;
>> +     struct vb2_alloc_ctx *alloc_ctx;
>> +
>> +     const struct iss_video_operations *ops;
>> +};
>> +
>> +#define to_iss_video(vdev)   container_of(vdev, struct iss_video, video)
>> +
>> +struct iss_video_fh {
>> +     struct v4l2_fh vfh;
>> +     struct iss_video *video;
>> +     struct vb2_queue queue;
>> +     struct v4l2_format format;
>
> Format shouldn't be part of the file handle anymore. There was a reason for
> it in the past before PREPARE_BUF --- which is also supported by videobuf2.

Ok. So should I just remove it completely?

Sorry if i'm not understanding this PREPARE_BUF thing... What should I
have there?


Regards,
Sergio

>
>> +     struct v4l2_fract timeperframe;
>> +};
>> +
>> +#define to_iss_video_fh(fh)  container_of(fh, struct iss_video_fh, vfh)
>> +#define iss_video_queue_to_iss_video_fh(q) \
>> +                             container_of(q, struct iss_video_fh, queue)
>> +
>> +int omap4iss_video_init(struct iss_video *video, const char *name);
>> +int omap4iss_video_register(struct iss_video *video,
>> +                         struct v4l2_device *vdev);
>> +void omap4iss_video_unregister(struct iss_video *video);
>> +struct iss_buffer *omap4iss_video_buffer_next(struct iss_video *video,
>> +                                           unsigned int error);
>> +struct media_pad *omap4iss_video_remote_pad(struct iss_video *video);
>> +
>> +const struct iss_format_info *
>> +omap4iss_video_format_info(enum v4l2_mbus_pixelcode code);
>> +
>> +#endif /* OMAP4_ISS_VIDEO_H */
>
> Cheers,
>
> --
> Sakari Ailus
> e-mail: sakari.ailus@iki.fi     jabber/XMPP/Gmail: sailus@retiisi.org.uk

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

* Re: [PATCH v2 06/11] v4l: Add support for omap4iss driver
  2012-01-25  8:58     ` Aguirre, Sergio
@ 2012-01-26 21:05       ` Sakari Ailus
  2012-03-09  3:14           ` Aguirre, Sergio
  0 siblings, 1 reply; 65+ messages in thread
From: Sakari Ailus @ 2012-01-26 21:05 UTC (permalink / raw)
  To: Aguirre, Sergio; +Cc: linux-media, linux-omap, laurent.pinchart

Hi Sergio,

Aguirre, Sergio wrote:
>> On Wed, Nov 30, 2011 at 06:14:55PM -0600, Sergio Aguirre wrote:
...
>>> +/*
>>> + * iss_save_ctx - Saves ISS context.
>>> + * @iss: OMAP4 ISS device
>>> + *
>>> + * Routine for saving the context of each module in the ISS.
>>> + */
>>> +static void iss_save_ctx(struct iss_device *iss)
>>> +{
>>> +     iss_save_context(iss, iss_reg_list);
>>> +}
>>
>> Do you really, really need to save context related data? Couldn't you
>> initialise the device the usual way when e.g. returning from suspended
>> state?
>
> I'll actually have to revisit this more carefuly. I haven't really
> tested Runtime PM on this.
>
> I think I'll remove this for now, until i come up with something better.

Usually it's best to recreate the configuration the same way the driver did
it in the first place. Some registers have side effects so that restoring
them requires extra care.

>>> +/*
>>> + * iss_restore_ctx - Restores ISS context.
>>> + * @iss: OMAP4 ISS device
>>> + *
>>> + * Routine for restoring the context of each module in the ISS.
>>> + */
>>> +static void iss_restore_ctx(struct iss_device *iss)
>>> +{
>>> +     iss_restore_context(iss, iss_reg_list);
>>> +}
>>> +

...

>>> +/*
>>> + * csi2_irq_ctx_set - Enables CSI2 Context IRQs.
>>> + * @enable: Enable/disable CSI2 Context interrupts
>>> + */
>>> +static void csi2_irq_ctx_set(struct iss_csi2_device *csi2, int enable)
>>> +{
>>> +     u32 reg = CSI2_CTX_IRQ_FE;
>>> +     int i;
>>> +
>>> +     if (csi2->use_fs_irq)
>>> +             reg |= CSI2_CTX_IRQ_FS;
>>> +
>>> +     for (i = 0; i<  8; i++) {
>>
>> 8 == number of contexts?
>
> Yes. This loops from 0 to 7.
>
> Are you implying that I need to add a define to this?

I think something that tells it is the number of contexts would be nicer.

...

>>> +     writel(readl(csi2->regs1 + CSI2_SYSCONFIG) |
>>> +             CSI2_SYSCONFIG_SOFT_RESET,
>>> +             csi2->regs1 + CSI2_SYSCONFIG);
>>> +
>>> +     do {
>>> +             reg = readl(csi2->regs1 + CSI2_SYSSTATUS)&
>>> +                                 CSI2_SYSSTATUS_RESET_DONE;
>>> +             if (reg == CSI2_SYSSTATUS_RESET_DONE)
>>> +                     break;
>>> +             soft_reset_retries++;
>>> +             if (soft_reset_retries<  5)
>>> +                     udelay(100);
>>
>> How about usleep_range() here? Or omit such a long busyloop. Hardware
>> typically resets quite fast.
>
> I guess i'll try to fine-tune this then.

I think it's fine to replace it with usleep_range() for now. Fine
optimisations can be left for later if really needed.

...

>>> +static void csi2_print_status(struct iss_csi2_device *csi2)
>>> +{
>>> +     struct iss_device *iss = csi2->iss;
>>> +
>>> +     if (!csi2->available)
>>> +             return;
>>> +
>>> +     dev_dbg(iss->dev, "-------------CSI2 Register dump-------------\n");
>>> +
>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, SYSCONFIG);
>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, SYSSTATUS);
>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, IRQENABLE);
>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, IRQSTATUS);
>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, CTRL);
>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, DBG_H);
>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, COMPLEXIO_CFG);
>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, COMPLEXIO_IRQSTATUS);
>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, SHORT_PACKET);
>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, COMPLEXIO_IRQENABLE);
>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, DBG_P);
>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, TIMING);
>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_CTRL1(0));
>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_CTRL2(0));
>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_DAT_OFST(0));
>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_PING_ADDR(0));
>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_PONG_ADDR(0));
>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_IRQENABLE(0));
>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_IRQSTATUS(0));
>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_CTRL3(0));
>>> +
>>> +     dev_dbg(iss->dev, "--------------------------------------------\n");
>>
>> _If_ this is user-triggered, you might want to consider using debugfs for
>> the same. Just FYI.
>
> Ok. I'll see what can I do.

Just FYI. :-) Thinking about this agian, debugfs might not go well with this
since the prints may be triggered by the driver.

...

>>> +     /* Skip interrupts until we reach the frame skip count. The CSI2 will be
>>> +      * automatically disabled, as the frame skip count has been programmed
>>> +      * in the CSI2_CTx_CTRL1::COUNT field, so reenable it.
>>> +      *
>>> +      * It would have been nice to rely on the FRAME_NUMBER interrupt instead
>>> +      * but it turned out that the interrupt is only generated when the CSI2
>>> +      * writes to memory (the CSI2_CTx_CTRL1::COUNT field is decreased
>>> +      * correctly and reaches 0 when data is forwarded to the video port only
>>> +      * but no interrupt arrives). Maybe a CSI2 hardware bug.
>>> +      */
>>> +     if (csi2->frame_skip) {
>>> +             csi2->frame_skip--;
>>> +             if (csi2->frame_skip == 0) {
>>> +                     ctx->format_id = csi2_ctx_map_format(csi2);
>>> +                     csi2_ctx_config(csi2, ctx);
>>
>> Is configuration of the context needed elsewhere than in streamon? What
>> changes while streaming?
>
> Nothing I think...
>
> Same thing is done for OMAP3, so I tried to keep the driver as similar
> as possible.

Ok. Perhaps this could be fixed for OMAP 3, too. :-)

> I'll try removing this then.
>

...

>>> +/* This is not an exhaustive list */
>>> +enum iss_csi2_pix_formats {
>>> +     CSI2_PIX_FMT_OTHERS = 0,
>>> +     CSI2_PIX_FMT_YUV422_8BIT = 0x1e,
>>> +     CSI2_PIX_FMT_YUV422_8BIT_VP = 0x9e,
>>> +     CSI2_PIX_FMT_RAW10_EXP16 = 0xab,
>>> +     CSI2_PIX_FMT_RAW10_EXP16_VP = 0x12f,
>>> +     CSI2_PIX_FMT_RAW8 = 0x2a,
>>> +     CSI2_PIX_FMT_RAW8_DPCM10_EXP16 = 0x2aa,
>>> +     CSI2_PIX_FMT_RAW8_DPCM10_VP = 0x32a,
>>> +     CSI2_PIX_FMT_RAW8_VP = 0x12a,
>>> +     CSI2_USERDEF_8BIT_DATA1_DPCM10_VP = 0x340,
>>> +     CSI2_USERDEF_8BIT_DATA1_DPCM10 = 0x2c0,
>>> +     CSI2_USERDEF_8BIT_DATA1 = 0x40,
>>
>> What are the USERDEF formats?
>
> Again, inherited and adapted these from OMAP3 driver.
>
> Should I delete them?

It's fine to keep them for now I guess --- Laurent must know what they
actually are. :-) Looks like CSI-2 pixel format codes (plus VP control, I
guess).

...

>>> +struct iss_video {
>>> +     struct video_device video;
>>> +     enum v4l2_buf_type type;
>>> +     struct media_pad pad;
>>> +
>>> +     struct mutex mutex;             /* format and crop settings */
>>> +     atomic_t active;
>>> +
>>> +     struct iss_device *iss;
>>> +
>>> +     unsigned int capture_mem;
>>> +     unsigned int bpl_alignment;     /* alignment value */
>>> +     unsigned int bpl_zero_padding;  /* whether the alignment is optional */
>>> +     unsigned int bpl_max;           /* maximum bytes per line value */
>>> +     unsigned int bpl_value;         /* bytes per line value */
>>> +     unsigned int bpl_padding;       /* padding at end of line */
>>> +
>>> +     /* Entity video node streaming */
>>> +     unsigned int streaming:1;
>>> +
>>> +     /* Pipeline state */
>>> +     struct iss_pipeline pipe;
>>> +     struct mutex stream_lock;       /* pipeline and stream states */
>>> +
>>> +     /* Video buffers queue */
>>> +     struct vb2_queue *queue;
>>> +     spinlock_t qlock;       /* Spinlock for dmaqueue */
>>> +     struct list_head dmaqueue;
>>> +     enum iss_video_dmaqueue_flags dmaqueue_flags;
>>> +     struct vb2_alloc_ctx *alloc_ctx;
>>> +
>>> +     const struct iss_video_operations *ops;
>>> +};
>>> +
>>> +#define to_iss_video(vdev)   container_of(vdev, struct iss_video, video)
>>> +
>>> +struct iss_video_fh {
>>> +     struct v4l2_fh vfh;
>>> +     struct iss_video *video;
>>> +     struct vb2_queue queue;
>>> +     struct v4l2_format format;
>>
>> Format shouldn't be part of the file handle anymore. There was a reason for
>> it in the past before PREPARE_BUF --- which is also supported by videobuf2.
>
> Ok. So should I just remove it completely?
>
> Sorry if i'm not understanding this PREPARE_BUF thing... What should I
> have there?

PREPARE_BUF prepares the buffer for the use in the device's DMA engine. This
is exactly what QBUF does, except that PREPARE_BUF doesn't put the buffer to
the driver's queue (calling QBUF will then do that, and only that).

Together with CREATE_BUFS this allows having different kinds of buffers in
the same queue. This didn't use to be possible.

Think of sets of buffers for still capture and viewfinder. They can now
co-exist in the same queue.

Kind regards,

-- 
Sakari Ailus
sakari.ailus@iki.fi

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

* Re: [PATCH v2 06/11] v4l: Add support for omap4iss driver
  2012-01-26 21:05       ` Sakari Ailus
@ 2012-03-09  3:14           ` Aguirre, Sergio
  0 siblings, 0 replies; 65+ messages in thread
From: Aguirre, Sergio @ 2012-03-09  3:14 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: linux-media, linux-omap, laurent.pinchart

Hi Sakari,

On Thu, Jan 26, 2012 at 3:05 PM, Sakari Ailus <sakari.ailus@iki.fi> wrote:
> Hi Sergio,
>
>
> Aguirre, Sergio wrote:
>>>
>>> On Wed, Nov 30, 2011 at 06:14:55PM -0600, Sergio Aguirre wrote:
>
> ...
>
>>>> +/*
>>>> + * iss_save_ctx - Saves ISS context.
>>>> + * @iss: OMAP4 ISS device
>>>> + *
>>>> + * Routine for saving the context of each module in the ISS.
>>>> + */
>>>> +static void iss_save_ctx(struct iss_device *iss)
>>>> +{
>>>> +     iss_save_context(iss, iss_reg_list);
>>>> +}
>>>
>>>
>>> Do you really, really need to save context related data? Couldn't you
>>> initialise the device the usual way when e.g. returning from suspended
>>> state?
>>
>>
>> I'll actually have to revisit this more carefuly. I haven't really
>> tested Runtime PM on this.
>>
>> I think I'll remove this for now, until i come up with something better.
>
>
> Usually it's best to recreate the configuration the same way the driver did
> it in the first place. Some registers have side effects so that restoring
> them requires extra care.
>
>
>>>> +/*
>>>> + * iss_restore_ctx - Restores ISS context.
>>>> + * @iss: OMAP4 ISS device
>>>> + *
>>>> + * Routine for restoring the context of each module in the ISS.
>>>> + */
>>>> +static void iss_restore_ctx(struct iss_device *iss)
>>>> +{
>>>> +     iss_restore_context(iss, iss_reg_list);
>>>> +}
>>>> +
>
>
> ...
>
>
>>>> +/*
>>>> + * csi2_irq_ctx_set - Enables CSI2 Context IRQs.
>>>> + * @enable: Enable/disable CSI2 Context interrupts
>>>> + */
>>>> +static void csi2_irq_ctx_set(struct iss_csi2_device *csi2, int enable)
>>>> +{
>>>> +     u32 reg = CSI2_CTX_IRQ_FE;
>>>> +     int i;
>>>> +
>>>> +     if (csi2->use_fs_irq)
>>>> +             reg |= CSI2_CTX_IRQ_FS;
>>>> +
>>>> +     for (i = 0; i<  8; i++) {
>>>
>>>
>>> 8 == number of contexts?
>>
>>
>> Yes. This loops from 0 to 7.
>>
>> Are you implying that I need to add a define to this?
>
>
> I think something that tells it is the number of contexts would be nicer.
>
> ...
>
>
>>>> +     writel(readl(csi2->regs1 + CSI2_SYSCONFIG) |
>>>> +             CSI2_SYSCONFIG_SOFT_RESET,
>>>> +             csi2->regs1 + CSI2_SYSCONFIG);
>>>> +
>>>> +     do {
>>>> +             reg = readl(csi2->regs1 + CSI2_SYSSTATUS)&
>>>> +                                 CSI2_SYSSTATUS_RESET_DONE;
>>>> +             if (reg == CSI2_SYSSTATUS_RESET_DONE)
>>>> +                     break;
>>>> +             soft_reset_retries++;
>>>> +             if (soft_reset_retries<  5)
>>>> +                     udelay(100);
>>>
>>>
>>> How about usleep_range() here? Or omit such a long busyloop. Hardware
>>> typically resets quite fast.
>>
>>
>> I guess i'll try to fine-tune this then.
>
>
> I think it's fine to replace it with usleep_range() for now. Fine
> optimisations can be left for later if really needed.
>
> ...
>
>
>>>> +static void csi2_print_status(struct iss_csi2_device *csi2)
>>>> +{
>>>> +     struct iss_device *iss = csi2->iss;
>>>> +
>>>> +     if (!csi2->available)
>>>> +             return;
>>>> +
>>>> +     dev_dbg(iss->dev, "-------------CSI2 Register
>>>> dump-------------\n");
>>>> +
>>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, SYSCONFIG);
>>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, SYSSTATUS);
>>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, IRQENABLE);
>>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, IRQSTATUS);
>>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, CTRL);
>>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, DBG_H);
>>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, COMPLEXIO_CFG);
>>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, COMPLEXIO_IRQSTATUS);
>>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, SHORT_PACKET);
>>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, COMPLEXIO_IRQENABLE);
>>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, DBG_P);
>>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, TIMING);
>>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_CTRL1(0));
>>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_CTRL2(0));
>>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_DAT_OFST(0));
>>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_PING_ADDR(0));
>>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_PONG_ADDR(0));
>>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_IRQENABLE(0));
>>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_IRQSTATUS(0));
>>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_CTRL3(0));
>>>> +
>>>> +     dev_dbg(iss->dev,
>>>> "--------------------------------------------\n");
>>>
>>>
>>> _If_ this is user-triggered, you might want to consider using debugfs for
>>> the same. Just FYI.
>>
>>
>> Ok. I'll see what can I do.
>
>
> Just FYI. :-) Thinking about this agian, debugfs might not go well with this
> since the prints may be triggered by the driver.
>
> ...
>
>
>>>> +     /* Skip interrupts until we reach the frame skip count. The CSI2
>>>> will be
>>>> +      * automatically disabled, as the frame skip count has been
>>>> programmed
>>>> +      * in the CSI2_CTx_CTRL1::COUNT field, so reenable it.
>>>> +      *
>>>> +      * It would have been nice to rely on the FRAME_NUMBER interrupt
>>>> instead
>>>> +      * but it turned out that the interrupt is only generated when the
>>>> CSI2
>>>> +      * writes to memory (the CSI2_CTx_CTRL1::COUNT field is decreased
>>>> +      * correctly and reaches 0 when data is forwarded to the video
>>>> port only
>>>> +      * but no interrupt arrives). Maybe a CSI2 hardware bug.
>>>> +      */
>>>> +     if (csi2->frame_skip) {
>>>> +             csi2->frame_skip--;
>>>> +             if (csi2->frame_skip == 0) {
>>>> +                     ctx->format_id = csi2_ctx_map_format(csi2);
>>>> +                     csi2_ctx_config(csi2, ctx);
>>>
>>>
>>> Is configuration of the context needed elsewhere than in streamon? What
>>> changes while streaming?
>>
>>
>> Nothing I think...
>>
>> Same thing is done for OMAP3, so I tried to keep the driver as similar
>> as possible.
>
>
> Ok. Perhaps this could be fixed for OMAP 3, too. :-)

Actually, I just realized something while enabling ISP support for OMAP4.

If you have a sensor that returns something bigger than zero for
"g_skip_frames",
removing the csi2_ctx_config() call breaks VP output.

So, CSI2 is first enabled with VP only enabled, but the context format
doesn't have
a "+ VP", with a framecount = g_skip_frames... then when this is done,
and you have
a videoport, the next FE IRQ, ti hsould reconfigure the right VP
enabled context format.

This made me scratch my head several times when i wasn't receiving any
ISP interrupts :P

So please, don't try removing this from OMAP3 CSI2 driver! :)

Regards,
Sergio

>
>
>> I'll try removing this then.
>>
>
> ...
>
>>>> +/* This is not an exhaustive list */
>>>> +enum iss_csi2_pix_formats {
>>>> +     CSI2_PIX_FMT_OTHERS = 0,
>>>> +     CSI2_PIX_FMT_YUV422_8BIT = 0x1e,
>>>> +     CSI2_PIX_FMT_YUV422_8BIT_VP = 0x9e,
>>>> +     CSI2_PIX_FMT_RAW10_EXP16 = 0xab,
>>>> +     CSI2_PIX_FMT_RAW10_EXP16_VP = 0x12f,
>>>> +     CSI2_PIX_FMT_RAW8 = 0x2a,
>>>> +     CSI2_PIX_FMT_RAW8_DPCM10_EXP16 = 0x2aa,
>>>> +     CSI2_PIX_FMT_RAW8_DPCM10_VP = 0x32a,
>>>> +     CSI2_PIX_FMT_RAW8_VP = 0x12a,
>>>> +     CSI2_USERDEF_8BIT_DATA1_DPCM10_VP = 0x340,
>>>> +     CSI2_USERDEF_8BIT_DATA1_DPCM10 = 0x2c0,
>>>> +     CSI2_USERDEF_8BIT_DATA1 = 0x40,
>>>
>>>
>>> What are the USERDEF formats?
>>
>>
>> Again, inherited and adapted these from OMAP3 driver.
>>
>> Should I delete them?
>
>
> It's fine to keep them for now I guess --- Laurent must know what they
> actually are. :-) Looks like CSI-2 pixel format codes (plus VP control, I
> guess).
>
> ...
>
>>>> +struct iss_video {
>>>> +     struct video_device video;
>>>> +     enum v4l2_buf_type type;
>>>> +     struct media_pad pad;
>>>> +
>>>> +     struct mutex mutex;             /* format and crop settings */
>>>> +     atomic_t active;
>>>> +
>>>> +     struct iss_device *iss;
>>>> +
>>>> +     unsigned int capture_mem;
>>>> +     unsigned int bpl_alignment;     /* alignment value */
>>>> +     unsigned int bpl_zero_padding;  /* whether the alignment is
>>>> optional */
>>>> +     unsigned int bpl_max;           /* maximum bytes per line value */
>>>> +     unsigned int bpl_value;         /* bytes per line value */
>>>> +     unsigned int bpl_padding;       /* padding at end of line */
>>>> +
>>>> +     /* Entity video node streaming */
>>>> +     unsigned int streaming:1;
>>>> +
>>>> +     /* Pipeline state */
>>>> +     struct iss_pipeline pipe;
>>>> +     struct mutex stream_lock;       /* pipeline and stream states */
>>>> +
>>>> +     /* Video buffers queue */
>>>> +     struct vb2_queue *queue;
>>>> +     spinlock_t qlock;       /* Spinlock for dmaqueue */
>>>> +     struct list_head dmaqueue;
>>>> +     enum iss_video_dmaqueue_flags dmaqueue_flags;
>>>> +     struct vb2_alloc_ctx *alloc_ctx;
>>>> +
>>>> +     const struct iss_video_operations *ops;
>>>> +};
>>>> +
>>>> +#define to_iss_video(vdev)   container_of(vdev, struct iss_video,
>>>> video)
>>>> +
>>>> +struct iss_video_fh {
>>>> +     struct v4l2_fh vfh;
>>>> +     struct iss_video *video;
>>>> +     struct vb2_queue queue;
>>>> +     struct v4l2_format format;
>>>
>>>
>>> Format shouldn't be part of the file handle anymore. There was a reason
>>> for
>>> it in the past before PREPARE_BUF --- which is also supported by
>>> videobuf2.
>>
>>
>> Ok. So should I just remove it completely?
>>
>> Sorry if i'm not understanding this PREPARE_BUF thing... What should I
>> have there?
>
>
> PREPARE_BUF prepares the buffer for the use in the device's DMA engine. This
> is exactly what QBUF does, except that PREPARE_BUF doesn't put the buffer to
> the driver's queue (calling QBUF will then do that, and only that).
>
> Together with CREATE_BUFS this allows having different kinds of buffers in
> the same queue. This didn't use to be possible.
>
> Think of sets of buffers for still capture and viewfinder. They can now
> co-exist in the same queue.
>
> Kind regards,
>
> --
> Sakari Ailus
> sakari.ailus@iki.fi

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

* Re: [PATCH v2 06/11] v4l: Add support for omap4iss driver
@ 2012-03-09  3:14           ` Aguirre, Sergio
  0 siblings, 0 replies; 65+ messages in thread
From: Aguirre, Sergio @ 2012-03-09  3:14 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: linux-media, linux-omap, laurent.pinchart

Hi Sakari,

On Thu, Jan 26, 2012 at 3:05 PM, Sakari Ailus <sakari.ailus@iki.fi> wrote:
> Hi Sergio,
>
>
> Aguirre, Sergio wrote:
>>>
>>> On Wed, Nov 30, 2011 at 06:14:55PM -0600, Sergio Aguirre wrote:
>
> ...
>
>>>> +/*
>>>> + * iss_save_ctx - Saves ISS context.
>>>> + * @iss: OMAP4 ISS device
>>>> + *
>>>> + * Routine for saving the context of each module in the ISS.
>>>> + */
>>>> +static void iss_save_ctx(struct iss_device *iss)
>>>> +{
>>>> +     iss_save_context(iss, iss_reg_list);
>>>> +}
>>>
>>>
>>> Do you really, really need to save context related data? Couldn't you
>>> initialise the device the usual way when e.g. returning from suspended
>>> state?
>>
>>
>> I'll actually have to revisit this more carefuly. I haven't really
>> tested Runtime PM on this.
>>
>> I think I'll remove this for now, until i come up with something better.
>
>
> Usually it's best to recreate the configuration the same way the driver did
> it in the first place. Some registers have side effects so that restoring
> them requires extra care.
>
>
>>>> +/*
>>>> + * iss_restore_ctx - Restores ISS context.
>>>> + * @iss: OMAP4 ISS device
>>>> + *
>>>> + * Routine for restoring the context of each module in the ISS.
>>>> + */
>>>> +static void iss_restore_ctx(struct iss_device *iss)
>>>> +{
>>>> +     iss_restore_context(iss, iss_reg_list);
>>>> +}
>>>> +
>
>
> ...
>
>
>>>> +/*
>>>> + * csi2_irq_ctx_set - Enables CSI2 Context IRQs.
>>>> + * @enable: Enable/disable CSI2 Context interrupts
>>>> + */
>>>> +static void csi2_irq_ctx_set(struct iss_csi2_device *csi2, int enable)
>>>> +{
>>>> +     u32 reg = CSI2_CTX_IRQ_FE;
>>>> +     int i;
>>>> +
>>>> +     if (csi2->use_fs_irq)
>>>> +             reg |= CSI2_CTX_IRQ_FS;
>>>> +
>>>> +     for (i = 0; i<  8; i++) {
>>>
>>>
>>> 8 == number of contexts?
>>
>>
>> Yes. This loops from 0 to 7.
>>
>> Are you implying that I need to add a define to this?
>
>
> I think something that tells it is the number of contexts would be nicer.
>
> ...
>
>
>>>> +     writel(readl(csi2->regs1 + CSI2_SYSCONFIG) |
>>>> +             CSI2_SYSCONFIG_SOFT_RESET,
>>>> +             csi2->regs1 + CSI2_SYSCONFIG);
>>>> +
>>>> +     do {
>>>> +             reg = readl(csi2->regs1 + CSI2_SYSSTATUS)&
>>>> +                                 CSI2_SYSSTATUS_RESET_DONE;
>>>> +             if (reg == CSI2_SYSSTATUS_RESET_DONE)
>>>> +                     break;
>>>> +             soft_reset_retries++;
>>>> +             if (soft_reset_retries<  5)
>>>> +                     udelay(100);
>>>
>>>
>>> How about usleep_range() here? Or omit such a long busyloop. Hardware
>>> typically resets quite fast.
>>
>>
>> I guess i'll try to fine-tune this then.
>
>
> I think it's fine to replace it with usleep_range() for now. Fine
> optimisations can be left for later if really needed.
>
> ...
>
>
>>>> +static void csi2_print_status(struct iss_csi2_device *csi2)
>>>> +{
>>>> +     struct iss_device *iss = csi2->iss;
>>>> +
>>>> +     if (!csi2->available)
>>>> +             return;
>>>> +
>>>> +     dev_dbg(iss->dev, "-------------CSI2 Register
>>>> dump-------------\n");
>>>> +
>>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, SYSCONFIG);
>>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, SYSSTATUS);
>>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, IRQENABLE);
>>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, IRQSTATUS);
>>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, CTRL);
>>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, DBG_H);
>>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, COMPLEXIO_CFG);
>>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, COMPLEXIO_IRQSTATUS);
>>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, SHORT_PACKET);
>>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, COMPLEXIO_IRQENABLE);
>>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, DBG_P);
>>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, TIMING);
>>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_CTRL1(0));
>>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_CTRL2(0));
>>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_DAT_OFST(0));
>>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_PING_ADDR(0));
>>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_PONG_ADDR(0));
>>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_IRQENABLE(0));
>>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_IRQSTATUS(0));
>>>> +     CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_CTRL3(0));
>>>> +
>>>> +     dev_dbg(iss->dev,
>>>> "--------------------------------------------\n");
>>>
>>>
>>> _If_ this is user-triggered, you might want to consider using debugfs for
>>> the same. Just FYI.
>>
>>
>> Ok. I'll see what can I do.
>
>
> Just FYI. :-) Thinking about this agian, debugfs might not go well with this
> since the prints may be triggered by the driver.
>
> ...
>
>
>>>> +     /* Skip interrupts until we reach the frame skip count. The CSI2
>>>> will be
>>>> +      * automatically disabled, as the frame skip count has been
>>>> programmed
>>>> +      * in the CSI2_CTx_CTRL1::COUNT field, so reenable it.
>>>> +      *
>>>> +      * It would have been nice to rely on the FRAME_NUMBER interrupt
>>>> instead
>>>> +      * but it turned out that the interrupt is only generated when the
>>>> CSI2
>>>> +      * writes to memory (the CSI2_CTx_CTRL1::COUNT field is decreased
>>>> +      * correctly and reaches 0 when data is forwarded to the video
>>>> port only
>>>> +      * but no interrupt arrives). Maybe a CSI2 hardware bug.
>>>> +      */
>>>> +     if (csi2->frame_skip) {
>>>> +             csi2->frame_skip--;
>>>> +             if (csi2->frame_skip == 0) {
>>>> +                     ctx->format_id = csi2_ctx_map_format(csi2);
>>>> +                     csi2_ctx_config(csi2, ctx);
>>>
>>>
>>> Is configuration of the context needed elsewhere than in streamon? What
>>> changes while streaming?
>>
>>
>> Nothing I think...
>>
>> Same thing is done for OMAP3, so I tried to keep the driver as similar
>> as possible.
>
>
> Ok. Perhaps this could be fixed for OMAP 3, too. :-)

Actually, I just realized something while enabling ISP support for OMAP4.

If you have a sensor that returns something bigger than zero for
"g_skip_frames",
removing the csi2_ctx_config() call breaks VP output.

So, CSI2 is first enabled with VP only enabled, but the context format
doesn't have
a "+ VP", with a framecount = g_skip_frames... then when this is done,
and you have
a videoport, the next FE IRQ, ti hsould reconfigure the right VP
enabled context format.

This made me scratch my head several times when i wasn't receiving any
ISP interrupts :P

So please, don't try removing this from OMAP3 CSI2 driver! :)

Regards,
Sergio

>
>
>> I'll try removing this then.
>>
>
> ...
>
>>>> +/* This is not an exhaustive list */
>>>> +enum iss_csi2_pix_formats {
>>>> +     CSI2_PIX_FMT_OTHERS = 0,
>>>> +     CSI2_PIX_FMT_YUV422_8BIT = 0x1e,
>>>> +     CSI2_PIX_FMT_YUV422_8BIT_VP = 0x9e,
>>>> +     CSI2_PIX_FMT_RAW10_EXP16 = 0xab,
>>>> +     CSI2_PIX_FMT_RAW10_EXP16_VP = 0x12f,
>>>> +     CSI2_PIX_FMT_RAW8 = 0x2a,
>>>> +     CSI2_PIX_FMT_RAW8_DPCM10_EXP16 = 0x2aa,
>>>> +     CSI2_PIX_FMT_RAW8_DPCM10_VP = 0x32a,
>>>> +     CSI2_PIX_FMT_RAW8_VP = 0x12a,
>>>> +     CSI2_USERDEF_8BIT_DATA1_DPCM10_VP = 0x340,
>>>> +     CSI2_USERDEF_8BIT_DATA1_DPCM10 = 0x2c0,
>>>> +     CSI2_USERDEF_8BIT_DATA1 = 0x40,
>>>
>>>
>>> What are the USERDEF formats?
>>
>>
>> Again, inherited and adapted these from OMAP3 driver.
>>
>> Should I delete them?
>
>
> It's fine to keep them for now I guess --- Laurent must know what they
> actually are. :-) Looks like CSI-2 pixel format codes (plus VP control, I
> guess).
>
> ...
>
>>>> +struct iss_video {
>>>> +     struct video_device video;
>>>> +     enum v4l2_buf_type type;
>>>> +     struct media_pad pad;
>>>> +
>>>> +     struct mutex mutex;             /* format and crop settings */
>>>> +     atomic_t active;
>>>> +
>>>> +     struct iss_device *iss;
>>>> +
>>>> +     unsigned int capture_mem;
>>>> +     unsigned int bpl_alignment;     /* alignment value */
>>>> +     unsigned int bpl_zero_padding;  /* whether the alignment is
>>>> optional */
>>>> +     unsigned int bpl_max;           /* maximum bytes per line value */
>>>> +     unsigned int bpl_value;         /* bytes per line value */
>>>> +     unsigned int bpl_padding;       /* padding at end of line */
>>>> +
>>>> +     /* Entity video node streaming */
>>>> +     unsigned int streaming:1;
>>>> +
>>>> +     /* Pipeline state */
>>>> +     struct iss_pipeline pipe;
>>>> +     struct mutex stream_lock;       /* pipeline and stream states */
>>>> +
>>>> +     /* Video buffers queue */
>>>> +     struct vb2_queue *queue;
>>>> +     spinlock_t qlock;       /* Spinlock for dmaqueue */
>>>> +     struct list_head dmaqueue;
>>>> +     enum iss_video_dmaqueue_flags dmaqueue_flags;
>>>> +     struct vb2_alloc_ctx *alloc_ctx;
>>>> +
>>>> +     const struct iss_video_operations *ops;
>>>> +};
>>>> +
>>>> +#define to_iss_video(vdev)   container_of(vdev, struct iss_video,
>>>> video)
>>>> +
>>>> +struct iss_video_fh {
>>>> +     struct v4l2_fh vfh;
>>>> +     struct iss_video *video;
>>>> +     struct vb2_queue queue;
>>>> +     struct v4l2_format format;
>>>
>>>
>>> Format shouldn't be part of the file handle anymore. There was a reason
>>> for
>>> it in the past before PREPARE_BUF --- which is also supported by
>>> videobuf2.
>>
>>
>> Ok. So should I just remove it completely?
>>
>> Sorry if i'm not understanding this PREPARE_BUF thing... What should I
>> have there?
>
>
> PREPARE_BUF prepares the buffer for the use in the device's DMA engine. This
> is exactly what QBUF does, except that PREPARE_BUF doesn't put the buffer to
> the driver's queue (calling QBUF will then do that, and only that).
>
> Together with CREATE_BUFS this allows having different kinds of buffers in
> the same queue. This didn't use to be possible.
>
> Think of sets of buffers for still capture and viewfinder. They can now
> co-exist in the same queue.
>
> Kind regards,
>
> --
> Sakari Ailus
> sakari.ailus@iki.fi
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

end of thread, other threads:[~2012-03-09  3:21 UTC | newest]

Thread overview: 65+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-12-01  0:14 [PATCH v2 00/11] v4l2: OMAP4 ISS driver + Sensor + Board support Sergio Aguirre
2011-12-01  0:14 ` Sergio Aguirre
2011-12-01  0:14 ` [PATCH v2 01/11] TWL6030: Add mapping for auxiliary regs Sergio Aguirre
2011-12-01  0:14   ` Sergio Aguirre
2011-12-01 15:58   ` T Krishnamoorthy, Balaji
2011-12-01 17:47     ` Aguirre, Sergio
2011-12-01 17:47       ` Aguirre, Sergio
2011-12-01  0:14 ` [PATCH v2 02/11] mfd: twl6040: Fix wrong TWL6040_GPO3 bitfield value Sergio Aguirre
2011-12-01  0:14   ` Sergio Aguirre
2011-12-01 17:24   ` Laurent Pinchart
2011-12-01 17:35     ` Aguirre, Sergio
2011-12-02  1:08       ` Laurent Pinchart
2011-12-01  0:14 ` [PATCH v2 03/11] v4l: Introduce sensor operation for getting interface configuration Sergio Aguirre
2011-12-01  0:14   ` Sergio Aguirre
2011-12-02 13:32   ` Stanimir Varbanov
2011-12-02 16:04     ` Aguirre, Sergio
2011-12-01  0:14 ` [PATCH v2 04/11] OMAP4: hwmod: Include CSI2A and CSIPHY1 memory sections Sergio Aguirre
2011-12-01  0:14   ` Sergio Aguirre
2011-12-01  6:34   ` Hiremath, Vaibhav
2011-12-01 13:17     ` Aguirre, Sergio
2011-12-01 13:17       ` Aguirre, Sergio
2011-12-02 22:49       ` Kevin Hilman
2011-12-02 22:49         ` Kevin Hilman
2011-12-02 22:59         ` Aguirre, Sergio
2011-12-02 23:18           ` Kevin Hilman
2011-12-02 23:18             ` Kevin Hilman
2011-12-01  0:14 ` [PATCH v2 05/11] OMAP4: Add base addresses for ISS Sergio Aguirre
2011-12-01  0:14   ` Sergio Aguirre
2011-12-01 17:24   ` Laurent Pinchart
2011-12-01 17:30     ` Aguirre, Sergio
2011-12-02 22:45   ` Kevin Hilman
2011-12-02 22:45     ` Kevin Hilman
2011-12-02 23:01     ` Aguirre, Sergio
2011-12-02 23:01       ` Aguirre, Sergio
2011-12-01  0:14 ` [PATCH v2 06/11] v4l: Add support for omap4iss driver Sergio Aguirre
2011-12-01  0:14   ` Sergio Aguirre
2011-12-11  9:11   ` Sakari Ailus
2012-01-23  9:10     ` Aguirre, Sergio
2012-01-14 17:51   ` Sakari Ailus
2012-01-25  8:58     ` Aguirre, Sergio
2012-01-26 21:05       ` Sakari Ailus
2012-03-09  3:14         ` Aguirre, Sergio
2012-03-09  3:14           ` Aguirre, Sergio
2011-12-01  0:14 ` [PATCH v2 07/11] v4l: Add support for ov5640 sensor Sergio Aguirre
2011-12-01  0:14   ` Sergio Aguirre
2011-12-01  0:14 ` [PATCH v2 08/11] v4l: Add support for ov5650 sensor Sergio Aguirre
2011-12-01  0:14   ` Sergio Aguirre
2011-12-01  0:14 ` [PATCH v2 09/11] arm: omap4430sdp: Add support for omap4iss camera Sergio Aguirre
2011-12-01  0:14   ` Sergio Aguirre
2011-12-11  8:05   ` Sakari Ailus
2011-12-01  0:14 ` [PATCH v2 10/11] arm: omap4panda: " Sergio Aguirre
2011-12-01  0:14   ` Sergio Aguirre
2011-12-01  6:24   ` Hiremath, Vaibhav
2011-12-01 13:34     ` Aguirre, Sergio
2011-12-01 13:34       ` Aguirre, Sergio
2011-12-01  0:15 ` [PATCH v2 11/11] arm: Add support for CMA for omap4iss driver Sergio Aguirre
2011-12-01  0:15   ` Sergio Aguirre
2011-12-01  4:05 ` [PATCH v2 00/11] v4l2: OMAP4 ISS driver + Sensor + Board support Aguirre, Sergio
2011-12-01  4:05   ` Aguirre, Sergio
2011-12-01 16:14 ` Sakari Ailus
2011-12-01 17:41   ` Aguirre, Sergio
2011-12-01 17:26 ` Laurent Pinchart
2011-12-01 17:43   ` Aguirre, Sergio
2011-12-01 17:43     ` Aguirre, Sergio
2011-12-07 21:50 ` Tony Lindgren

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.