Browse Source

Merge branch 'topic/exynos' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media

Pull exynos media updates from Mauro Carvalho Chehab:
 "These are the remaining patches I have for the merge windows.  It
  basically adds a new sensor and adds the needed DT bits for it to
  work"

* 'topic/exynos' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media:
  [media] s5p-fimc: Remove reference to outdated macro
  [media] s5p-jpeg: Fix broken indentation in jpeg-regs.h
  [media] exynos4-is: Add the FIMC-IS ISP capture DMA driver
  [media] exynos4-is: Add support for asynchronous subdevices registration
  [media] exynos4-is: Add clock provider for the SCLK_CAM clock outputs
  [media] exynos4-is: Use external s5k6a3 sensor driver
  [media] V4L: s5c73m3: Add device tree support
  [media] V4L: Add driver for s5k6a3 image sensor
  [media] Documentation: devicetree: Update Samsung FIMC DT binding
  [media] Documentation: dt: Add binding documentation for S5C73M3 camera
  [media] Documentation: dt: Add binding documentation for S5K6A3 image sensor
Linus Torvalds 11 years ago
parent
commit
463b21fb27
27 changed files with 1886 additions and 565 deletions
  1. 29 15
      Documentation/devicetree/bindings/media/samsung-fimc.txt
  2. 97 0
      Documentation/devicetree/bindings/media/samsung-s5c73m3.txt
  3. 33 0
      Documentation/devicetree/bindings/media/samsung-s5k6a3.txt
  4. 2 3
      Documentation/video4linux/fimc.txt
  5. 8 0
      drivers/media/i2c/Kconfig
  6. 1 0
      drivers/media/i2c/Makefile
  7. 157 50
      drivers/media/i2c/s5c73m3/s5c73m3-core.c
  8. 6 0
      drivers/media/i2c/s5c73m3/s5c73m3-spi.c
  9. 4 0
      drivers/media/i2c/s5c73m3/s5c73m3.h
  10. 389 0
      drivers/media/i2c/s5k6a3.c
  11. 9 0
      drivers/media/platform/exynos4-is/Kconfig
  12. 4 0
      drivers/media/platform/exynos4-is/Makefile
  13. 1 1
      drivers/media/platform/exynos4-is/fimc-is-param.c
  14. 5 0
      drivers/media/platform/exynos4-is/fimc-is-param.h
  15. 15 1
      drivers/media/platform/exynos4-is/fimc-is-regs.c
  16. 1 0
      drivers/media/platform/exynos4-is/fimc-is-regs.h
  17. 7 278
      drivers/media/platform/exynos4-is/fimc-is-sensor.c
  18. 8 41
      drivers/media/platform/exynos4-is/fimc-is-sensor.h
  19. 41 57
      drivers/media/platform/exynos4-is/fimc-is.c
  20. 7 2
      drivers/media/platform/exynos4-is/fimc-is.h
  21. 660 0
      drivers/media/platform/exynos4-is/fimc-isp-video.c
  22. 44 0
      drivers/media/platform/exynos4-is/fimc-isp-video.h
  23. 28 1
      drivers/media/platform/exynos4-is/fimc-isp.c
  24. 23 4
      drivers/media/platform/exynos4-is/fimc-isp.h
  25. 265 98
      drivers/media/platform/exynos4-is/media-dev.c
  26. 30 2
      drivers/media/platform/exynos4-is/media-dev.h
  27. 12 12
      drivers/media/platform/s5p-jpeg/jpeg-regs.h

+ 29 - 15
Documentation/devicetree/bindings/media/samsung-fimc.txt

@@ -15,11 +15,21 @@ Common 'camera' node
 
 
 Required properties:
 Required properties:
 
 
-- compatible	: must be "samsung,fimc", "simple-bus"
-- clocks	: list of clock specifiers, corresponding to entries in
-		  the clock-names property;
-- clock-names	: must contain "sclk_cam0", "sclk_cam1", "pxl_async0",
-		  "pxl_async1" entries, matching entries in the clocks property.
+- compatible: must be "samsung,fimc", "simple-bus"
+- clocks: list of clock specifiers, corresponding to entries in
+  the clock-names property;
+- clock-names : must contain "sclk_cam0", "sclk_cam1", "pxl_async0",
+  "pxl_async1" entries, matching entries in the clocks property.
+
+- #clock-cells: from the common clock bindings (../clock/clock-bindings.txt),
+  must be 1. A clock provider is associated with the 'camera' node and it should
+  be referenced by external sensors that use clocks provided by the SoC on
+  CAM_*_CLKOUT pins. The clock specifier cell stores an index of a clock.
+  The indices are 0, 1 for CAM_A_CLKOUT, CAM_B_CLKOUT clocks respectively.
+
+- clock-output-names: from the common clock bindings, should contain names of
+  clocks registered by the camera subsystem corresponding to CAM_A_CLKOUT,
+  CAM_B_CLKOUT output clocks respectively.
 
 
 The pinctrl bindings defined in ../pinctrl/pinctrl-bindings.txt must be used
 The pinctrl bindings defined in ../pinctrl/pinctrl-bindings.txt must be used
 to define a required pinctrl state named "default" and optional pinctrl states:
 to define a required pinctrl state named "default" and optional pinctrl states:
@@ -32,6 +42,7 @@ way around.
 
 
 The 'camera' node must include at least one 'fimc' child node.
 The 'camera' node must include at least one 'fimc' child node.
 
 
+
 'fimc' device nodes
 'fimc' device nodes
 -------------------
 -------------------
 
 
@@ -88,8 +99,8 @@ port nodes specifies data input - 0, 1 indicates input A, B respectively.
 
 
 Optional properties
 Optional properties
 
 
-- samsung,camclk-out : specifies clock output for remote sensor,
-		       0 - CAM_A_CLKOUT, 1 - CAM_B_CLKOUT;
+- samsung,camclk-out (deprecated) : specifies clock output for remote sensor,
+  0 - CAM_A_CLKOUT, 1 - CAM_B_CLKOUT;
 
 
 Image sensor nodes
 Image sensor nodes
 ------------------
 ------------------
@@ -97,8 +108,6 @@ Image sensor nodes
 The sensor device nodes should be added to their control bus controller (e.g.
 The sensor device nodes should be added to their control bus controller (e.g.
 I2C0) nodes and linked to a port node in the csis or the parallel-ports node,
 I2C0) nodes and linked to a port node in the csis or the parallel-ports node,
 using the common video interfaces bindings, defined in video-interfaces.txt.
 using the common video interfaces bindings, defined in video-interfaces.txt.
-The implementation of this bindings requires clock-frequency property to be
-present in the sensor device nodes.
 
 
 Example:
 Example:
 
 
@@ -114,7 +123,7 @@ Example:
 			vddio-supply = <...>;
 			vddio-supply = <...>;
 
 
 			clock-frequency = <24000000>;
 			clock-frequency = <24000000>;
-			clocks = <...>;
+			clocks = <&camera 1>;
 			clock-names = "mclk";
 			clock-names = "mclk";
 
 
 			port {
 			port {
@@ -135,7 +144,7 @@ Example:
 			vddio-supply = <...>;
 			vddio-supply = <...>;
 
 
 			clock-frequency = <24000000>;
 			clock-frequency = <24000000>;
-			clocks = <...>;
+			clocks = <&camera 0>;
 			clock-names = "mclk";
 			clock-names = "mclk";
 
 
 			port {
 			port {
@@ -149,12 +158,17 @@ Example:
 
 
 	camera {
 	camera {
 		compatible = "samsung,fimc", "simple-bus";
 		compatible = "samsung,fimc", "simple-bus";
-		#address-cells = <1>;
-		#size-cells = <1>;
-		status = "okay";
-
+		clocks = <&clock 132>, <&clock 133>, <&clock 351>,
+			 <&clock 352>;
+		clock-names = "sclk_cam0", "sclk_cam1", "pxl_async0",
+			      "pxl_async1";
+		#clock-cells = <1>;
+		clock-output-names = "cam_a_clkout", "cam_b_clkout";
 		pinctrl-names = "default";
 		pinctrl-names = "default";
 		pinctrl-0 = <&cam_port_a_clk_active>;
 		pinctrl-0 = <&cam_port_a_clk_active>;
+		status = "okay";
+		#address-cells = <1>;
+		#size-cells = <1>;
 
 
 		/* parallel camera ports */
 		/* parallel camera ports */
 		parallel-ports {
 		parallel-ports {

+ 97 - 0
Documentation/devicetree/bindings/media/samsung-s5c73m3.txt

@@ -0,0 +1,97 @@
+Samsung S5C73M3 8Mp camera ISP
+------------------------------
+
+The S5C73M3 camera ISP supports MIPI CSI-2 and parallel (ITU-R BT.656) video
+data busses. The I2C bus is the main control bus and additionally the SPI bus
+is used, mostly for transferring the firmware to and from the device. Two
+slave device nodes corresponding to these control bus interfaces are required
+and should be placed under respective bus controller nodes.
+
+I2C slave device node
+---------------------
+
+Required properties:
+
+- compatible	    : "samsung,s5c73m3";
+- reg		    : I2C slave address of the sensor;
+- vdd-int-supply    : digital power supply (1.2V);
+- vdda-supply	    : analog power supply (1.2V);
+- vdd-reg-supply    : regulator input power supply (2.8V);
+- vddio-host-supply : host I/O power supply (1.8V to 2.8V);
+- vddio-cis-supply  : CIS I/O power supply (1.2V to 1.8V);
+- vdd-af-supply     : lens power supply (2.8V);
+- xshutdown-gpios   : specifier of GPIO connected to the XSHUTDOWN pin;
+- standby-gpios     : specifier of GPIO connected to the STANDBY pin;
+- clocks	    : should contain list of phandle and clock specifier pairs
+		      according to common clock bindings for the clocks described
+		      in the clock-names property;
+- clock-names	    : should contain "cis_extclk" entry for the CIS_EXTCLK clock;
+
+Optional properties:
+
+- clock-frequency   : the frequency at which the "cis_extclk" clock should be
+		      configured to operate, in Hz; if this property is not
+		      specified default 24 MHz value will be used.
+
+The common video interfaces bindings (see video-interfaces.txt) should be used
+to specify link from the S5C73M3 to an external image data receiver. The S5C73M3
+device node should contain one 'port' child node with an 'endpoint' subnode for
+this purpose. The data link from a raw image sensor to the S5C73M3 can be
+similarly specified, but it is optional since the S5C73M3 ISP and a raw image
+sensor are usually inseparable and form a hybrid module.
+
+Following properties are valid for the endpoint node(s):
+
+endpoint subnode
+----------------
+
+- data-lanes : (optional) specifies MIPI CSI-2 data lanes as covered in
+  video-interfaces.txt. This sensor doesn't support data lane remapping
+  and physical lane indexes in subsequent elements of the array should
+  be only consecutive ascending values.
+
+SPI device node
+---------------
+
+Required properties:
+
+- compatible	    : "samsung,s5c73m3";
+
+For more details see description of the SPI busses bindings
+(../spi/spi-bus.txt) and bindings of a specific bus controller.
+
+Example:
+
+i2c@138A000000 {
+	...
+	s5c73m3@3c {
+		compatible = "samsung,s5c73m3";
+		reg = <0x3c>;
+		vdd-int-supply = <&buck9_reg>;
+		vdda-supply = <&ldo17_reg>;
+		vdd-reg-supply = <&cam_io_reg>;
+		vddio-host-supply = <&ldo18_reg>;
+		vddio-cis-supply = <&ldo9_reg>;
+		vdd-af-supply = <&cam_af_reg>;
+		clock-frequency = <24000000>;
+		clocks = <&clk 0>;
+		clock-names = "cis_extclk";
+		reset-gpios = <&gpf1 3 1>;
+		standby-gpios = <&gpm0 1 1>;
+		port {
+			s5c73m3_ep: endpoint {
+				remote-endpoint = <&csis0_ep>;
+				data-lanes = <1 2 3 4>;
+			};
+		};
+	};
+};
+
+spi@1392000 {
+	...
+	s5c73m3_spi: s5c73m3@0 {
+		compatible = "samsung,s5c73m3";
+		reg = <0>;
+		...
+	};
+};

+ 33 - 0
Documentation/devicetree/bindings/media/samsung-s5k6a3.txt

@@ -0,0 +1,33 @@
+Samsung S5K6A3(YX) raw image sensor
+---------------------------------
+
+S5K6A3(YX) is a raw image sensor with MIPI CSI-2 and CCP2 image data interfaces
+and CCI (I2C compatible) control bus.
+
+Required properties:
+
+- compatible	: "samsung,s5k6a3";
+- reg		: I2C slave address of the sensor;
+- svdda-supply	: core voltage supply;
+- svddio-supply	: I/O voltage supply;
+- afvdd-supply	: AF (actuator) voltage supply;
+- gpios		: specifier of a GPIO connected to the RESET pin;
+- clocks	: should contain list of phandle and clock specifier pairs
+		  according to common clock bindings for the clocks described
+		  in the clock-names property;
+- clock-names	: should contain "extclk" entry for the sensor's EXTCLK clock;
+
+Optional properties:
+
+- clock-frequency : the frequency at which the "extclk" clock should be
+		    configured to operate, in Hz; if this property is not
+		    specified default 24 MHz value will be used.
+
+The common video interfaces bindings (see video-interfaces.txt) should be
+used to specify link to the image data receiver. The S5K6A3(YX) device
+node should contain one 'port' child node with an 'endpoint' subnode.
+
+Following properties are valid for the endpoint node:
+
+- data-lanes : (optional) specifies MIPI CSI-2 data lanes as covered in
+  video-interfaces.txt.  The sensor supports only one data lane.

+ 2 - 3
Documentation/video4linux/fimc.txt

@@ -151,9 +151,8 @@ CONFIG_S5P_DEV_FIMC1  \
 CONFIG_S5P_DEV_FIMC2  |    optional
 CONFIG_S5P_DEV_FIMC2  |    optional
 CONFIG_S5P_DEV_FIMC3  |
 CONFIG_S5P_DEV_FIMC3  |
 CONFIG_S5P_SETUP_FIMC /
 CONFIG_S5P_SETUP_FIMC /
-CONFIG_S5P_SETUP_MIPIPHY \
-CONFIG_S5P_DEV_CSIS0     | optional for MIPI-CSI interface
-CONFIG_S5P_DEV_CSIS1     /
+CONFIG_S5P_DEV_CSIS0  \    optional for MIPI-CSI interface
+CONFIG_S5P_DEV_CSIS1  /
 
 
 Except that, relevant s5p_device_fimc? should be registered in the machine code
 Except that, relevant s5p_device_fimc? should be registered in the machine code
 in addition to a "s5p-fimc-md" platform device to which the media device driver
 in addition to a "s5p-fimc-md" platform device to which the media device driver

+ 8 - 0
drivers/media/i2c/Kconfig

@@ -579,6 +579,14 @@ config VIDEO_S5K6AA
 	  This is a V4L2 sensor-level driver for Samsung S5K6AA(FX) 1.3M
 	  This is a V4L2 sensor-level driver for Samsung S5K6AA(FX) 1.3M
 	  camera sensor with an embedded SoC image signal processor.
 	  camera sensor with an embedded SoC image signal processor.
 
 
+config VIDEO_S5K6A3
+	tristate "Samsung S5K6A3 sensor support"
+	depends on MEDIA_CAMERA_SUPPORT
+	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+	---help---
+	  This is a V4L2 sensor-level driver for Samsung S5K6A3 raw
+	  camera sensor.
+
 config VIDEO_S5K4ECGX
 config VIDEO_S5K4ECGX
         tristate "Samsung S5K4ECGX sensor support"
         tristate "Samsung S5K4ECGX sensor support"
         depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
         depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API

+ 1 - 0
drivers/media/i2c/Makefile

@@ -66,6 +66,7 @@ obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o
 obj-$(CONFIG_VIDEO_SR030PC30)	+= sr030pc30.o
 obj-$(CONFIG_VIDEO_SR030PC30)	+= sr030pc30.o
 obj-$(CONFIG_VIDEO_NOON010PC30)	+= noon010pc30.o
 obj-$(CONFIG_VIDEO_NOON010PC30)	+= noon010pc30.o
 obj-$(CONFIG_VIDEO_S5K6AA)	+= s5k6aa.o
 obj-$(CONFIG_VIDEO_S5K6AA)	+= s5k6aa.o
+obj-$(CONFIG_VIDEO_S5K6A3)	+= s5k6a3.o
 obj-$(CONFIG_VIDEO_S5K4ECGX)	+= s5k4ecgx.o
 obj-$(CONFIG_VIDEO_S5K4ECGX)	+= s5k4ecgx.o
 obj-$(CONFIG_VIDEO_S5K5BAF)	+= s5k5baf.o
 obj-$(CONFIG_VIDEO_S5K5BAF)	+= s5k5baf.o
 obj-$(CONFIG_VIDEO_S5C73M3)	+= s5c73m3/
 obj-$(CONFIG_VIDEO_S5C73M3)	+= s5c73m3/

+ 157 - 50
drivers/media/i2c/s5c73m3/s5c73m3-core.c

@@ -15,7 +15,7 @@
  * GNU General Public License for more details.
  * GNU General Public License for more details.
  */
  */
 
 
-#include <linux/sizes.h>
+#include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/delay.h>
 #include <linux/firmware.h>
 #include <linux/firmware.h>
 #include <linux/gpio.h>
 #include <linux/gpio.h>
@@ -23,7 +23,9 @@
 #include <linux/init.h>
 #include <linux/init.h>
 #include <linux/media.h>
 #include <linux/media.h>
 #include <linux/module.h>
 #include <linux/module.h>
+#include <linux/of_gpio.h>
 #include <linux/regulator/consumer.h>
 #include <linux/regulator/consumer.h>
+#include <linux/sizes.h>
 #include <linux/slab.h>
 #include <linux/slab.h>
 #include <linux/spi/spi.h>
 #include <linux/spi/spi.h>
 #include <linux/videodev2.h>
 #include <linux/videodev2.h>
@@ -33,6 +35,7 @@
 #include <media/v4l2-subdev.h>
 #include <media/v4l2-subdev.h>
 #include <media/v4l2-mediabus.h>
 #include <media/v4l2-mediabus.h>
 #include <media/s5c73m3.h>
 #include <media/s5c73m3.h>
+#include <media/v4l2-of.h>
 
 
 #include "s5c73m3.h"
 #include "s5c73m3.h"
 
 
@@ -46,6 +49,8 @@ static int update_fw;
 module_param(update_fw, int, 0644);
 module_param(update_fw, int, 0644);
 
 
 #define S5C73M3_EMBEDDED_DATA_MAXLEN	SZ_4K
 #define S5C73M3_EMBEDDED_DATA_MAXLEN	SZ_4K
+#define S5C73M3_MIPI_DATA_LANES		4
+#define S5C73M3_CLK_NAME		"cis_extclk"
 
 
 static const char * const s5c73m3_supply_names[S5C73M3_MAX_SUPPLIES] = {
 static const char * const s5c73m3_supply_names[S5C73M3_MAX_SUPPLIES] = {
 	"vdd-int",	/* Digital Core supply (1.2V), CAM_ISP_CORE_1.2V */
 	"vdd-int",	/* Digital Core supply (1.2V), CAM_ISP_CORE_1.2V */
@@ -1355,9 +1360,20 @@ static int __s5c73m3_power_on(struct s5c73m3 *state)
 	for (i = 0; i < S5C73M3_MAX_SUPPLIES; i++) {
 	for (i = 0; i < S5C73M3_MAX_SUPPLIES; i++) {
 		ret = regulator_enable(state->supplies[i].consumer);
 		ret = regulator_enable(state->supplies[i].consumer);
 		if (ret)
 		if (ret)
-			goto err;
+			goto err_reg_dis;
 	}
 	}
 
 
+	ret = clk_set_rate(state->clock, state->mclk_frequency);
+	if (ret < 0)
+		goto err_reg_dis;
+
+	ret = clk_prepare_enable(state->clock);
+	if (ret < 0)
+		goto err_reg_dis;
+
+	v4l2_dbg(1, s5c73m3_dbg, &state->oif_sd, "clock frequency: %ld\n",
+					clk_get_rate(state->clock));
+
 	s5c73m3_gpio_deassert(state, STBY);
 	s5c73m3_gpio_deassert(state, STBY);
 	usleep_range(100, 200);
 	usleep_range(100, 200);
 
 
@@ -1365,7 +1381,8 @@ static int __s5c73m3_power_on(struct s5c73m3 *state)
 	usleep_range(50, 100);
 	usleep_range(50, 100);
 
 
 	return 0;
 	return 0;
-err:
+
+err_reg_dis:
 	for (--i; i >= 0; i--)
 	for (--i; i >= 0; i--)
 		regulator_disable(state->supplies[i].consumer);
 		regulator_disable(state->supplies[i].consumer);
 	return ret;
 	return ret;
@@ -1380,6 +1397,9 @@ static int __s5c73m3_power_off(struct s5c73m3 *state)
 
 
 	if (s5c73m3_gpio_assert(state, STBY))
 	if (s5c73m3_gpio_assert(state, STBY))
 		usleep_range(100, 200);
 		usleep_range(100, 200);
+
+	clk_disable_unprepare(state->clock);
+
 	state->streaming = 0;
 	state->streaming = 0;
 	state->isp_ready = 0;
 	state->isp_ready = 0;
 
 
@@ -1388,6 +1408,7 @@ static int __s5c73m3_power_off(struct s5c73m3 *state)
 		if (ret)
 		if (ret)
 			goto err;
 			goto err;
 	}
 	}
+
 	return 0;
 	return 0;
 err:
 err:
 	for (++i; i < S5C73M3_MAX_SUPPLIES; i++) {
 	for (++i; i < S5C73M3_MAX_SUPPLIES; i++) {
@@ -1396,6 +1417,8 @@ err:
 			v4l2_err(&state->oif_sd, "Failed to reenable %s: %d\n",
 			v4l2_err(&state->oif_sd, "Failed to reenable %s: %d\n",
 				 state->supplies[i].supply, r);
 				 state->supplies[i].supply, r);
 	}
 	}
+
+	clk_prepare_enable(state->clock);
 	return ret;
 	return ret;
 }
 }
 
 
@@ -1451,17 +1474,6 @@ static int s5c73m3_oif_registered(struct v4l2_subdev *sd)
 			S5C73M3_JPEG_PAD, &state->oif_sd.entity, OIF_JPEG_PAD,
 			S5C73M3_JPEG_PAD, &state->oif_sd.entity, OIF_JPEG_PAD,
 			MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
 			MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
 
 
-	mutex_lock(&state->lock);
-	ret = __s5c73m3_power_on(state);
-	if (ret == 0)
-		s5c73m3_get_fw_version(state);
-
-	__s5c73m3_power_off(state);
-	mutex_unlock(&state->lock);
-
-	v4l2_dbg(1, s5c73m3_dbg, sd, "%s: Booting %s (%d)\n",
-		 __func__, ret ? "failed" : "succeeded", ret);
-
 	return ret;
 	return ret;
 }
 }
 
 
@@ -1519,41 +1531,112 @@ static const struct v4l2_subdev_ops oif_subdev_ops = {
 	.video	= &s5c73m3_oif_video_ops,
 	.video	= &s5c73m3_oif_video_ops,
 };
 };
 
 
-static int s5c73m3_configure_gpios(struct s5c73m3 *state,
-				   const struct s5c73m3_platform_data *pdata)
+static int s5c73m3_configure_gpios(struct s5c73m3 *state)
+{
+	static const char * const gpio_names[] = {
+		"S5C73M3_STBY", "S5C73M3_RST"
+	};
+	struct i2c_client *c = state->i2c_client;
+	struct s5c73m3_gpio *g = state->gpio;
+	int ret, i;
+
+	for (i = 0; i < GPIO_NUM; ++i) {
+		unsigned int flags = GPIOF_DIR_OUT;
+		if (g[i].level)
+			flags |= GPIOF_INIT_HIGH;
+		ret = devm_gpio_request_one(&c->dev, g[i].gpio, flags,
+					    gpio_names[i]);
+		if (ret) {
+			v4l2_err(c, "failed to request gpio %s\n",
+				 gpio_names[i]);
+			return ret;
+		}
+	}
+	return 0;
+}
+
+static int s5c73m3_parse_gpios(struct s5c73m3 *state)
+{
+	static const char * const prop_names[] = {
+		"standby-gpios", "xshutdown-gpios",
+	};
+	struct device *dev = &state->i2c_client->dev;
+	struct device_node *node = dev->of_node;
+	int ret, i;
+
+	for (i = 0; i < GPIO_NUM; ++i) {
+		enum of_gpio_flags of_flags;
+
+		ret = of_get_named_gpio_flags(node, prop_names[i],
+					      0, &of_flags);
+		if (ret < 0) {
+			dev_err(dev, "failed to parse %s DT property\n",
+				prop_names[i]);
+			return -EINVAL;
+		}
+		state->gpio[i].gpio = ret;
+		state->gpio[i].level = !(of_flags & OF_GPIO_ACTIVE_LOW);
+	}
+	return 0;
+}
+
+static int s5c73m3_get_platform_data(struct s5c73m3 *state)
 {
 {
 	struct device *dev = &state->i2c_client->dev;
 	struct device *dev = &state->i2c_client->dev;
-	const struct s5c73m3_gpio *gpio;
-	unsigned long flags;
+	const struct s5c73m3_platform_data *pdata = dev->platform_data;
+	struct device_node *node = dev->of_node;
+	struct device_node *node_ep;
+	struct v4l2_of_endpoint ep;
 	int ret;
 	int ret;
 
 
-	state->gpio[STBY].gpio = -EINVAL;
-	state->gpio[RST].gpio  = -EINVAL;
+	if (!node) {
+		if (!pdata) {
+			dev_err(dev, "Platform data not specified\n");
+			return -EINVAL;
+		}
 
 
-	gpio = &pdata->gpio_stby;
-	if (gpio_is_valid(gpio->gpio)) {
-		flags = (gpio->level ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW)
-		      | GPIOF_EXPORT;
-		ret = devm_gpio_request_one(dev, gpio->gpio, flags,
-					    "S5C73M3_STBY");
-		if (ret < 0)
-			return ret;
+		state->mclk_frequency = pdata->mclk_frequency;
+		state->gpio[STBY] = pdata->gpio_stby;
+		state->gpio[RST] = pdata->gpio_reset;
+		return 0;
+	}
+
+	state->clock = devm_clk_get(dev, S5C73M3_CLK_NAME);
+	if (IS_ERR(state->clock))
+		return PTR_ERR(state->clock);
 
 
-		state->gpio[STBY] = *gpio;
+	if (of_property_read_u32(node, "clock-frequency",
+				 &state->mclk_frequency)) {
+		state->mclk_frequency = S5C73M3_DEFAULT_MCLK_FREQ;
+		dev_info(dev, "using default %u Hz clock frequency\n",
+					state->mclk_frequency);
 	}
 	}
 
 
-	gpio = &pdata->gpio_reset;
-	if (gpio_is_valid(gpio->gpio)) {
-		flags = (gpio->level ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW)
-		      | GPIOF_EXPORT;
-		ret = devm_gpio_request_one(dev, gpio->gpio, flags,
-					    "S5C73M3_RST");
-		if (ret < 0)
-			return ret;
+	ret = s5c73m3_parse_gpios(state);
+	if (ret < 0)
+		return -EINVAL;
 
 
-		state->gpio[RST] = *gpio;
+	node_ep = v4l2_of_get_next_endpoint(node, NULL);
+	if (!node_ep) {
+		dev_warn(dev, "no endpoint defined for node: %s\n",
+						node->full_name);
+		return 0;
 	}
 	}
 
 
+	v4l2_of_parse_endpoint(node_ep, &ep);
+	of_node_put(node_ep);
+
+	if (ep.bus_type != V4L2_MBUS_CSI2) {
+		dev_err(dev, "unsupported bus type\n");
+		return -EINVAL;
+	}
+	/*
+	 * Number of MIPI CSI-2 data lanes is currently not configurable,
+	 * always a default value of 4 lanes is used.
+	 */
+	if (ep.bus.mipi_csi2.num_data_lanes != S5C73M3_MIPI_DATA_LANES)
+		dev_info(dev, "falling back to 4 MIPI CSI-2 data lanes\n");
+
 	return 0;
 	return 0;
 }
 }
 
 
@@ -1561,21 +1644,20 @@ static int s5c73m3_probe(struct i2c_client *client,
 				const struct i2c_device_id *id)
 				const struct i2c_device_id *id)
 {
 {
 	struct device *dev = &client->dev;
 	struct device *dev = &client->dev;
-	const struct s5c73m3_platform_data *pdata = client->dev.platform_data;
 	struct v4l2_subdev *sd;
 	struct v4l2_subdev *sd;
 	struct v4l2_subdev *oif_sd;
 	struct v4l2_subdev *oif_sd;
 	struct s5c73m3 *state;
 	struct s5c73m3 *state;
 	int ret, i;
 	int ret, i;
 
 
-	if (pdata == NULL) {
-		dev_err(&client->dev, "Platform data not specified\n");
-		return -EINVAL;
-	}
-
 	state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
 	state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
 	if (!state)
 	if (!state)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
+	state->i2c_client = client;
+	ret = s5c73m3_get_platform_data(state);
+	if (ret < 0)
+		return ret;
+
 	mutex_init(&state->lock);
 	mutex_init(&state->lock);
 	sd = &state->sensor_sd;
 	sd = &state->sensor_sd;
 	oif_sd = &state->oif_sd;
 	oif_sd = &state->oif_sd;
@@ -1613,11 +1695,7 @@ static int s5c73m3_probe(struct i2c_client *client,
 	if (ret < 0)
 	if (ret < 0)
 		return ret;
 		return ret;
 
 
-	state->mclk_frequency = pdata->mclk_frequency;
-	state->bus_type = pdata->bus_type;
-	state->i2c_client = client;
-
-	ret = s5c73m3_configure_gpios(state, pdata);
+	ret = s5c73m3_configure_gpios(state);
 	if (ret)
 	if (ret)
 		goto out_err;
 		goto out_err;
 
 
@@ -1651,9 +1729,29 @@ static int s5c73m3_probe(struct i2c_client *client,
 	if (ret < 0)
 	if (ret < 0)
 		goto out_err;
 		goto out_err;
 
 
+	oif_sd->dev = dev;
+
+	ret = __s5c73m3_power_on(state);
+	if (ret < 0)
+		goto out_err1;
+
+	ret = s5c73m3_get_fw_version(state);
+	__s5c73m3_power_off(state);
+
+	if (ret < 0) {
+		dev_err(dev, "Device detection failed: %d\n", ret);
+		goto out_err1;
+	}
+
+	ret = v4l2_async_register_subdev(oif_sd);
+	if (ret < 0)
+		goto out_err1;
+
 	v4l2_info(sd, "%s: completed successfully\n", __func__);
 	v4l2_info(sd, "%s: completed successfully\n", __func__);
 	return 0;
 	return 0;
 
 
+out_err1:
+	s5c73m3_unregister_spi_driver(state);
 out_err:
 out_err:
 	media_entity_cleanup(&sd->entity);
 	media_entity_cleanup(&sd->entity);
 	return ret;
 	return ret;
@@ -1665,7 +1763,7 @@ static int s5c73m3_remove(struct i2c_client *client)
 	struct s5c73m3 *state = oif_sd_to_s5c73m3(oif_sd);
 	struct s5c73m3 *state = oif_sd_to_s5c73m3(oif_sd);
 	struct v4l2_subdev *sensor_sd = &state->sensor_sd;
 	struct v4l2_subdev *sensor_sd = &state->sensor_sd;
 
 
-	v4l2_device_unregister_subdev(oif_sd);
+	v4l2_async_unregister_subdev(oif_sd);
 
 
 	v4l2_ctrl_handler_free(oif_sd->ctrl_handler);
 	v4l2_ctrl_handler_free(oif_sd->ctrl_handler);
 	media_entity_cleanup(&oif_sd->entity);
 	media_entity_cleanup(&oif_sd->entity);
@@ -1684,8 +1782,17 @@ static const struct i2c_device_id s5c73m3_id[] = {
 };
 };
 MODULE_DEVICE_TABLE(i2c, s5c73m3_id);
 MODULE_DEVICE_TABLE(i2c, s5c73m3_id);
 
 
+#ifdef CONFIG_OF
+static const struct of_device_id s5c73m3_of_match[] = {
+	{ .compatible = "samsung,s5c73m3" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, s5c73m3_of_match);
+#endif
+
 static struct i2c_driver s5c73m3_i2c_driver = {
 static struct i2c_driver s5c73m3_i2c_driver = {
 	.driver = {
 	.driver = {
+		.of_match_table = of_match_ptr(s5c73m3_of_match),
 		.name	= DRIVER_NAME,
 		.name	= DRIVER_NAME,
 	},
 	},
 	.probe		= s5c73m3_probe,
 	.probe		= s5c73m3_probe,

+ 6 - 0
drivers/media/i2c/s5c73m3/s5c73m3-spi.c

@@ -27,6 +27,11 @@
 
 
 #define S5C73M3_SPI_DRV_NAME "S5C73M3-SPI"
 #define S5C73M3_SPI_DRV_NAME "S5C73M3-SPI"
 
 
+static const struct of_device_id s5c73m3_spi_ids[] = {
+	{ .compatible = "samsung,s5c73m3" },
+	{ }
+};
+
 enum spi_direction {
 enum spi_direction {
 	SPI_DIR_RX,
 	SPI_DIR_RX,
 	SPI_DIR_TX
 	SPI_DIR_TX
@@ -146,6 +151,7 @@ int s5c73m3_register_spi_driver(struct s5c73m3 *state)
 	spidrv->driver.name = S5C73M3_SPI_DRV_NAME;
 	spidrv->driver.name = S5C73M3_SPI_DRV_NAME;
 	spidrv->driver.bus = &spi_bus_type;
 	spidrv->driver.bus = &spi_bus_type;
 	spidrv->driver.owner = THIS_MODULE;
 	spidrv->driver.owner = THIS_MODULE;
+	spidrv->driver.of_match_table = s5c73m3_spi_ids;
 
 
 	return spi_register_driver(spidrv);
 	return spi_register_driver(spidrv);
 }
 }

+ 4 - 0
drivers/media/i2c/s5c73m3/s5c73m3.h

@@ -17,6 +17,7 @@
 #ifndef S5C73M3_H_
 #ifndef S5C73M3_H_
 #define S5C73M3_H_
 #define S5C73M3_H_
 
 
+#include <linux/clk.h>
 #include <linux/kernel.h>
 #include <linux/kernel.h>
 #include <linux/regulator/consumer.h>
 #include <linux/regulator/consumer.h>
 #include <media/v4l2-common.h>
 #include <media/v4l2-common.h>
@@ -321,6 +322,7 @@ enum s5c73m3_oif_pads {
 
 
 
 
 #define S5C73M3_MAX_SUPPLIES			6
 #define S5C73M3_MAX_SUPPLIES			6
+#define S5C73M3_DEFAULT_MCLK_FREQ		24000000U
 
 
 struct s5c73m3_ctrls {
 struct s5c73m3_ctrls {
 	struct v4l2_ctrl_handler handler;
 	struct v4l2_ctrl_handler handler;
@@ -391,6 +393,8 @@ struct s5c73m3 {
 	struct regulator_bulk_data supplies[S5C73M3_MAX_SUPPLIES];
 	struct regulator_bulk_data supplies[S5C73M3_MAX_SUPPLIES];
 	struct s5c73m3_gpio gpio[GPIO_NUM];
 	struct s5c73m3_gpio gpio[GPIO_NUM];
 
 
+	struct clk *clock;
+
 	/* External master clock frequency */
 	/* External master clock frequency */
 	u32 mclk_frequency;
 	u32 mclk_frequency;
 	/* Video bus type - MIPI-CSI2/parallel */
 	/* Video bus type - MIPI-CSI2/parallel */

+ 389 - 0
drivers/media/i2c/s5k6a3.c

@@ -0,0 +1,389 @@
+/*
+ * Samsung S5K6A3 image sensor driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Author: Sylwester Nawrocki <s.nawrocki@samsung.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.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-subdev.h>
+
+#define S5K6A3_SENSOR_MAX_WIDTH		1412
+#define S5K6A3_SENSOR_MAX_HEIGHT	1412
+#define S5K6A3_SENSOR_MIN_WIDTH		32
+#define S5K6A3_SENSOR_MIN_HEIGHT	32
+
+#define S5K6A3_DEFAULT_WIDTH		1296
+#define S5K6A3_DEFAULT_HEIGHT		732
+
+#define S5K6A3_DRV_NAME			"S5K6A3"
+#define S5K6A3_CLK_NAME			"extclk"
+#define S5K6A3_DEFAULT_CLK_FREQ		24000000U
+
+enum {
+	S5K6A3_SUPP_VDDA,
+	S5K6A3_SUPP_VDDIO,
+	S5K6A3_SUPP_AFVDD,
+	S5K6A3_NUM_SUPPLIES,
+};
+
+/**
+ * struct s5k6a3 - fimc-is sensor data structure
+ * @dev: pointer to this I2C client device structure
+ * @subdev: the image sensor's v4l2 subdev
+ * @pad: subdev media source pad
+ * @supplies: image sensor's voltage regulator supplies
+ * @gpio_reset: GPIO connected to the sensor's reset pin
+ * @lock: mutex protecting the structure's members below
+ * @format: media bus format at the sensor's source pad
+ */
+struct s5k6a3 {
+	struct device *dev;
+	struct v4l2_subdev subdev;
+	struct media_pad pad;
+	struct regulator_bulk_data supplies[S5K6A3_NUM_SUPPLIES];
+	int gpio_reset;
+	struct mutex lock;
+	struct v4l2_mbus_framefmt format;
+	struct clk *clock;
+	u32 clock_frequency;
+	int power_count;
+};
+
+static const char * const s5k6a3_supply_names[] = {
+	[S5K6A3_SUPP_VDDA]	= "svdda",
+	[S5K6A3_SUPP_VDDIO]	= "svddio",
+	[S5K6A3_SUPP_AFVDD]	= "afvdd",
+};
+
+static inline struct s5k6a3 *sd_to_s5k6a3(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct s5k6a3, subdev);
+}
+
+static const struct v4l2_mbus_framefmt s5k6a3_formats[] = {
+	{
+		.code = V4L2_MBUS_FMT_SGRBG10_1X10,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.field = V4L2_FIELD_NONE,
+	}
+};
+
+static const struct v4l2_mbus_framefmt *find_sensor_format(
+	struct v4l2_mbus_framefmt *mf)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(s5k6a3_formats); i++)
+		if (mf->code == s5k6a3_formats[i].code)
+			return &s5k6a3_formats[i];
+
+	return &s5k6a3_formats[0];
+}
+
+static int s5k6a3_enum_mbus_code(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_fh *fh,
+				  struct v4l2_subdev_mbus_code_enum *code)
+{
+	if (code->index >= ARRAY_SIZE(s5k6a3_formats))
+		return -EINVAL;
+
+	code->code = s5k6a3_formats[code->index].code;
+	return 0;
+}
+
+static void s5k6a3_try_format(struct v4l2_mbus_framefmt *mf)
+{
+	const struct v4l2_mbus_framefmt *fmt;
+
+	fmt = find_sensor_format(mf);
+	mf->code = fmt->code;
+	v4l_bound_align_image(&mf->width, S5K6A3_SENSOR_MIN_WIDTH,
+			      S5K6A3_SENSOR_MAX_WIDTH, 0,
+			      &mf->height, S5K6A3_SENSOR_MIN_HEIGHT,
+			      S5K6A3_SENSOR_MAX_HEIGHT, 0, 0);
+}
+
+static struct v4l2_mbus_framefmt *__s5k6a3_get_format(
+		struct s5k6a3 *sensor, struct v4l2_subdev_fh *fh,
+		u32 pad, enum v4l2_subdev_format_whence which)
+{
+	if (which == V4L2_SUBDEV_FORMAT_TRY)
+		return fh ? v4l2_subdev_get_try_format(fh, pad) : NULL;
+
+	return &sensor->format;
+}
+
+static int s5k6a3_set_fmt(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_fh *fh,
+				  struct v4l2_subdev_format *fmt)
+{
+	struct s5k6a3 *sensor = sd_to_s5k6a3(sd);
+	struct v4l2_mbus_framefmt *mf;
+
+	s5k6a3_try_format(&fmt->format);
+
+	mf = __s5k6a3_get_format(sensor, fh, fmt->pad, fmt->which);
+	if (mf) {
+		mutex_lock(&sensor->lock);
+		if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+			*mf = fmt->format;
+		mutex_unlock(&sensor->lock);
+	}
+	return 0;
+}
+
+static int s5k6a3_get_fmt(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_fh *fh,
+				  struct v4l2_subdev_format *fmt)
+{
+	struct s5k6a3 *sensor = sd_to_s5k6a3(sd);
+	struct v4l2_mbus_framefmt *mf;
+
+	mf = __s5k6a3_get_format(sensor, fh, fmt->pad, fmt->which);
+
+	mutex_lock(&sensor->lock);
+	fmt->format = *mf;
+	mutex_unlock(&sensor->lock);
+	return 0;
+}
+
+static struct v4l2_subdev_pad_ops s5k6a3_pad_ops = {
+	.enum_mbus_code	= s5k6a3_enum_mbus_code,
+	.get_fmt	= s5k6a3_get_fmt,
+	.set_fmt	= s5k6a3_set_fmt,
+};
+
+static int s5k6a3_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0);
+
+	*format		= s5k6a3_formats[0];
+	format->width	= S5K6A3_DEFAULT_WIDTH;
+	format->height	= S5K6A3_DEFAULT_HEIGHT;
+
+	return 0;
+}
+
+static const struct v4l2_subdev_internal_ops s5k6a3_sd_internal_ops = {
+	.open = s5k6a3_open,
+};
+
+static int __s5k6a3_power_on(struct s5k6a3 *sensor)
+{
+	int i = S5K6A3_SUPP_VDDA;
+	int ret;
+
+	ret = clk_set_rate(sensor->clock, sensor->clock_frequency);
+	if (ret < 0)
+		return ret;
+
+	ret = pm_runtime_get(sensor->dev);
+	if (ret < 0)
+		return ret;
+
+	ret = regulator_enable(sensor->supplies[i].consumer);
+	if (ret < 0)
+		goto error_rpm_put;
+
+	ret = clk_prepare_enable(sensor->clock);
+	if (ret < 0)
+		goto error_reg_dis;
+
+	for (i++; i < S5K6A3_NUM_SUPPLIES; i++) {
+		ret = regulator_enable(sensor->supplies[i].consumer);
+		if (ret < 0)
+			goto error_reg_dis;
+	}
+
+	gpio_set_value(sensor->gpio_reset, 1);
+	usleep_range(600, 800);
+	gpio_set_value(sensor->gpio_reset, 0);
+	usleep_range(600, 800);
+	gpio_set_value(sensor->gpio_reset, 1);
+
+	/* Delay needed for the sensor initialization */
+	msleep(20);
+	return 0;
+
+error_reg_dis:
+	for (--i; i >= 0; --i)
+		regulator_disable(sensor->supplies[i].consumer);
+error_rpm_put:
+	pm_runtime_put(sensor->dev);
+	return ret;
+}
+
+static int __s5k6a3_power_off(struct s5k6a3 *sensor)
+{
+	int i;
+
+	gpio_set_value(sensor->gpio_reset, 0);
+
+	for (i = S5K6A3_NUM_SUPPLIES - 1; i >= 0; i--)
+		regulator_disable(sensor->supplies[i].consumer);
+
+	clk_disable_unprepare(sensor->clock);
+	pm_runtime_put(sensor->dev);
+	return 0;
+}
+
+static int s5k6a3_s_power(struct v4l2_subdev *sd, int on)
+{
+	struct s5k6a3 *sensor = sd_to_s5k6a3(sd);
+	int ret = 0;
+
+	mutex_lock(&sensor->lock);
+
+	if (sensor->power_count == !on) {
+		if (on)
+			ret = __s5k6a3_power_on(sensor);
+		else
+			ret = __s5k6a3_power_off(sensor);
+
+		if (ret == 0)
+			sensor->power_count += on ? 1 : -1;
+	}
+
+	mutex_unlock(&sensor->lock);
+	return ret;
+}
+
+static struct v4l2_subdev_core_ops s5k6a3_core_ops = {
+	.s_power = s5k6a3_s_power,
+};
+
+static struct v4l2_subdev_ops s5k6a3_subdev_ops = {
+	.core = &s5k6a3_core_ops,
+	.pad = &s5k6a3_pad_ops,
+};
+
+static int s5k6a3_probe(struct i2c_client *client,
+				const struct i2c_device_id *id)
+{
+	struct device *dev = &client->dev;
+	struct s5k6a3 *sensor;
+	struct v4l2_subdev *sd;
+	int gpio, i, ret;
+
+	sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
+	if (!sensor)
+		return -ENOMEM;
+
+	mutex_init(&sensor->lock);
+	sensor->gpio_reset = -EINVAL;
+	sensor->clock = ERR_PTR(-EINVAL);
+	sensor->dev = dev;
+
+	sensor->clock = devm_clk_get(sensor->dev, S5K6A3_CLK_NAME);
+	if (IS_ERR(sensor->clock))
+		return PTR_ERR(sensor->clock);
+
+	gpio = of_get_gpio_flags(dev->of_node, 0, NULL);
+	if (!gpio_is_valid(gpio))
+		return gpio;
+
+	ret = devm_gpio_request_one(dev, gpio, GPIOF_OUT_INIT_LOW,
+						S5K6A3_DRV_NAME);
+	if (ret < 0)
+		return ret;
+
+	sensor->gpio_reset = gpio;
+
+	if (of_property_read_u32(dev->of_node, "clock-frequency",
+				 &sensor->clock_frequency)) {
+		sensor->clock_frequency = S5K6A3_DEFAULT_CLK_FREQ;
+		dev_info(dev, "using default %u Hz clock frequency\n",
+					sensor->clock_frequency);
+	}
+
+	for (i = 0; i < S5K6A3_NUM_SUPPLIES; i++)
+		sensor->supplies[i].supply = s5k6a3_supply_names[i];
+
+	ret = devm_regulator_bulk_get(&client->dev, S5K6A3_NUM_SUPPLIES,
+				      sensor->supplies);
+	if (ret < 0)
+		return ret;
+
+	sd = &sensor->subdev;
+	v4l2_i2c_subdev_init(sd, client, &s5k6a3_subdev_ops);
+	sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	sd->internal_ops = &s5k6a3_sd_internal_ops;
+
+	sensor->format.code = s5k6a3_formats[0].code;
+	sensor->format.width = S5K6A3_DEFAULT_WIDTH;
+	sensor->format.height = S5K6A3_DEFAULT_HEIGHT;
+
+	sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
+	ret = media_entity_init(&sd->entity, 1, &sensor->pad, 0);
+	if (ret < 0)
+		return ret;
+
+	pm_runtime_no_callbacks(dev);
+	pm_runtime_enable(dev);
+
+	ret = v4l2_async_register_subdev(sd);
+
+	if (ret < 0) {
+		pm_runtime_disable(&client->dev);
+		media_entity_cleanup(&sd->entity);
+	}
+
+	return ret;
+}
+
+static int s5k6a3_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+	pm_runtime_disable(&client->dev);
+	v4l2_async_unregister_subdev(sd);
+	media_entity_cleanup(&sd->entity);
+	return 0;
+}
+
+static const struct i2c_device_id s5k6a3_ids[] = {
+	{ }
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id s5k6a3_of_match[] = {
+	{ .compatible = "samsung,s5k6a3" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, s5k6a3_of_match);
+#endif
+
+static struct i2c_driver s5k6a3_driver = {
+	.driver = {
+		.of_match_table	= of_match_ptr(s5k6a3_of_match),
+		.name		= S5K6A3_DRV_NAME,
+		.owner		= THIS_MODULE,
+	},
+	.probe		= s5k6a3_probe,
+	.remove		= s5k6a3_remove,
+	.id_table	= s5k6a3_ids,
+};
+
+module_i2c_driver(s5k6a3_driver);
+
+MODULE_DESCRIPTION("S5K6A3 image sensor subdev driver");
+MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
+MODULE_LICENSE("GPL v2");

+ 9 - 0
drivers/media/platform/exynos4-is/Kconfig

@@ -64,4 +64,13 @@ config VIDEO_EXYNOS4_FIMC_IS
 	  To compile this driver as a module, choose M here: the
 	  To compile this driver as a module, choose M here: the
 	  module will be called exynos4-fimc-is.
 	  module will be called exynos4-fimc-is.
 
 
+config VIDEO_EXYNOS4_ISP_DMA_CAPTURE
+	bool "EXYNOS4x12 FIMC-IS ISP Direct DMA capture support"
+	depends on VIDEO_EXYNOS4_FIMC_IS
+	select VIDEO_EXYNOS4_IS_COMMON
+	default y
+	  help
+	  This option enables an additional video device node exposing a V4L2
+	  video capture interface for the FIMC-IS ISP raw (Bayer) capture DMA.
+
 endif # VIDEO_SAMSUNG_EXYNOS4_IS
 endif # VIDEO_SAMSUNG_EXYNOS4_IS

+ 4 - 0
drivers/media/platform/exynos4-is/Makefile

@@ -6,6 +6,10 @@ exynos4-is-common-objs := common.o
 exynos-fimc-is-objs := fimc-is.o fimc-isp.o fimc-is-sensor.o fimc-is-regs.o
 exynos-fimc-is-objs := fimc-is.o fimc-isp.o fimc-is-sensor.o fimc-is-regs.o
 exynos-fimc-is-objs += fimc-is-param.o fimc-is-errno.o fimc-is-i2c.o
 exynos-fimc-is-objs += fimc-is-param.o fimc-is-errno.o fimc-is-i2c.o
 
 
+ifeq ($(CONFIG_VIDEO_EXYNOS4_ISP_DMA_CAPTURE),y)
+exynos-fimc-is-objs += fimc-isp-video.o
+endif
+
 obj-$(CONFIG_VIDEO_S5P_MIPI_CSIS)	+= s5p-csis.o
 obj-$(CONFIG_VIDEO_S5P_MIPI_CSIS)	+= s5p-csis.o
 obj-$(CONFIG_VIDEO_EXYNOS_FIMC_LITE)	+= exynos-fimc-lite.o
 obj-$(CONFIG_VIDEO_EXYNOS_FIMC_LITE)	+= exynos-fimc-lite.o
 obj-$(CONFIG_VIDEO_EXYNOS4_FIMC_IS)	+= exynos-fimc-is.o
 obj-$(CONFIG_VIDEO_EXYNOS4_FIMC_IS)	+= exynos-fimc-is.o

+ 1 - 1
drivers/media/platform/exynos4-is/fimc-is-param.c

@@ -56,7 +56,7 @@ static void __fimc_is_hw_update_param_sensor_framerate(struct fimc_is *is)
 	__hw_param_copy(dst, src);
 	__hw_param_copy(dst, src);
 }
 }
 
 
-static int __fimc_is_hw_update_param(struct fimc_is *is, u32 offset)
+int __fimc_is_hw_update_param(struct fimc_is *is, u32 offset)
 {
 {
 	struct is_param_region *par = &is->is_p_region->parameter;
 	struct is_param_region *par = &is->is_p_region->parameter;
 	struct chain_config *cfg = &is->config[is->config_index];
 	struct chain_config *cfg = &is->config[is->config_index];

+ 5 - 0
drivers/media/platform/exynos4-is/fimc-is-param.h

@@ -911,6 +911,10 @@ struct is_region {
 	u32 shared[MAX_SHARED_COUNT];
 	u32 shared[MAX_SHARED_COUNT];
 } __packed;
 } __packed;
 
 
+/* Offset to the ISP DMA2 output buffer address array. */
+#define DMA2_OUTPUT_ADDR_ARRAY_OFFS \
+	(offsetof(struct is_region, shared) + 32 * sizeof(u32))
+
 struct is_debug_frame_descriptor {
 struct is_debug_frame_descriptor {
 	u32 sensor_frame_time;
 	u32 sensor_frame_time;
 	u32 sensor_exposure_time;
 	u32 sensor_exposure_time;
@@ -988,6 +992,7 @@ struct sensor_open_extended {
 struct fimc_is;
 struct fimc_is;
 
 
 int fimc_is_hw_get_sensor_max_framerate(struct fimc_is *is);
 int fimc_is_hw_get_sensor_max_framerate(struct fimc_is *is);
+int __fimc_is_hw_update_param(struct fimc_is *is, u32 offset);
 void fimc_is_set_initial_params(struct fimc_is *is);
 void fimc_is_set_initial_params(struct fimc_is *is);
 unsigned int __get_pending_param_count(struct fimc_is *is);
 unsigned int __get_pending_param_count(struct fimc_is *is);
 
 

+ 15 - 1
drivers/media/platform/exynos4-is/fimc-is-regs.c

@@ -105,6 +105,20 @@ int fimc_is_hw_get_params(struct fimc_is *is, unsigned int num_args)
 	return 0;
 	return 0;
 }
 }
 
 
+void fimc_is_hw_set_isp_buf_mask(struct fimc_is *is, unsigned int mask)
+{
+	if (hweight32(mask) == 1) {
+		dev_err(&is->pdev->dev, "%s(): not enough buffers (mask %#x)\n",
+							__func__, mask);
+		return;
+	}
+
+	if (mcuctl_read(is, MCUCTL_REG_ISSR(23)) != 0)
+		dev_dbg(&is->pdev->dev, "non-zero DMA buffer mask\n");
+
+	mcuctl_write(mask, is, MCUCTL_REG_ISSR(23));
+}
+
 void fimc_is_hw_set_sensor_num(struct fimc_is *is)
 void fimc_is_hw_set_sensor_num(struct fimc_is *is)
 {
 {
 	pr_debug("setting sensor index to: %d\n", is->sensor_index);
 	pr_debug("setting sensor index to: %d\n", is->sensor_index);
@@ -112,7 +126,7 @@ void fimc_is_hw_set_sensor_num(struct fimc_is *is)
 	mcuctl_write(IH_REPLY_DONE, is, MCUCTL_REG_ISSR(0));
 	mcuctl_write(IH_REPLY_DONE, is, MCUCTL_REG_ISSR(0));
 	mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1));
 	mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1));
 	mcuctl_write(IHC_GET_SENSOR_NUM, is, MCUCTL_REG_ISSR(2));
 	mcuctl_write(IHC_GET_SENSOR_NUM, is, MCUCTL_REG_ISSR(2));
-	mcuctl_write(FIMC_IS_SENSOR_NUM, is, MCUCTL_REG_ISSR(3));
+	mcuctl_write(FIMC_IS_SENSORS_NUM, is, MCUCTL_REG_ISSR(3));
 }
 }
 
 
 void fimc_is_hw_close_sensor(struct fimc_is *is, unsigned int index)
 void fimc_is_hw_close_sensor(struct fimc_is *is, unsigned int index)

+ 1 - 0
drivers/media/platform/exynos4-is/fimc-is-regs.h

@@ -147,6 +147,7 @@ int fimc_is_hw_get_params(struct fimc_is *is, unsigned int num);
 void fimc_is_hw_set_intgr0_gd0(struct fimc_is *is);
 void fimc_is_hw_set_intgr0_gd0(struct fimc_is *is);
 int fimc_is_hw_wait_intmsr0_intmsd0(struct fimc_is *is);
 int fimc_is_hw_wait_intmsr0_intmsd0(struct fimc_is *is);
 void fimc_is_hw_set_sensor_num(struct fimc_is *is);
 void fimc_is_hw_set_sensor_num(struct fimc_is *is);
+void fimc_is_hw_set_isp_buf_mask(struct fimc_is *is, unsigned int mask);
 void fimc_is_hw_stream_on(struct fimc_is *is);
 void fimc_is_hw_stream_on(struct fimc_is *is);
 void fimc_is_hw_stream_off(struct fimc_is *is);
 void fimc_is_hw_stream_off(struct fimc_is *is);
 int fimc_is_hw_set_param(struct fimc_is *is);
 int fimc_is_hw_set_param(struct fimc_is *is);

+ 7 - 278
drivers/media/platform/exynos4-is/fimc-is-sensor.c

@@ -2,276 +2,21 @@
  * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver
  * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver
  *
  *
  * Copyright (C) 2013 Samsung Electronics Co., Ltd.
  * Copyright (C) 2013 Samsung Electronics Co., Ltd.
- *
  * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
  * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * 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
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  * published by the Free Software Foundation.
  */
  */
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/errno.h>
-#include <linux/gpio.h>
-#include <linux/i2c.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/of_gpio.h>
-#include <linux/pm_runtime.h>
-#include <linux/regulator/consumer.h>
-#include <linux/slab.h>
-#include <media/v4l2-subdev.h>
 
 
-#include "fimc-is.h"
 #include "fimc-is-sensor.h"
 #include "fimc-is-sensor.h"
 
 
-#define DRIVER_NAME "FIMC-IS-SENSOR"
-
-static const char * const sensor_supply_names[] = {
-	"svdda",
-	"svddio",
-};
-
-static const struct v4l2_mbus_framefmt fimc_is_sensor_formats[] = {
-	{
-		.code = V4L2_MBUS_FMT_SGRBG10_1X10,
-		.colorspace = V4L2_COLORSPACE_SRGB,
-		.field = V4L2_FIELD_NONE,
-	}
-};
-
-static const struct v4l2_mbus_framefmt *find_sensor_format(
-	struct v4l2_mbus_framefmt *mf)
-{
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(fimc_is_sensor_formats); i++)
-		if (mf->code == fimc_is_sensor_formats[i].code)
-			return &fimc_is_sensor_formats[i];
-
-	return &fimc_is_sensor_formats[0];
-}
-
-static int fimc_is_sensor_enum_mbus_code(struct v4l2_subdev *sd,
-				  struct v4l2_subdev_fh *fh,
-				  struct v4l2_subdev_mbus_code_enum *code)
-{
-	if (code->index >= ARRAY_SIZE(fimc_is_sensor_formats))
-		return -EINVAL;
-
-	code->code = fimc_is_sensor_formats[code->index].code;
-	return 0;
-}
-
-static void fimc_is_sensor_try_format(struct fimc_is_sensor *sensor,
-				      struct v4l2_mbus_framefmt *mf)
-{
-	const struct sensor_drv_data *dd = sensor->drvdata;
-	const struct v4l2_mbus_framefmt *fmt;
-
-	fmt = find_sensor_format(mf);
-	mf->code = fmt->code;
-	v4l_bound_align_image(&mf->width, 16 + 8, dd->width, 0,
-			      &mf->height, 12 + 8, dd->height, 0, 0);
-}
-
-static struct v4l2_mbus_framefmt *__fimc_is_sensor_get_format(
-		struct fimc_is_sensor *sensor, struct v4l2_subdev_fh *fh,
-		u32 pad, enum v4l2_subdev_format_whence which)
-{
-	if (which == V4L2_SUBDEV_FORMAT_TRY)
-		return fh ? v4l2_subdev_get_try_format(fh, pad) : NULL;
-
-	return &sensor->format;
-}
-
-static int fimc_is_sensor_set_fmt(struct v4l2_subdev *sd,
-				  struct v4l2_subdev_fh *fh,
-				  struct v4l2_subdev_format *fmt)
-{
-	struct fimc_is_sensor *sensor = sd_to_fimc_is_sensor(sd);
-	struct v4l2_mbus_framefmt *mf;
-
-	fimc_is_sensor_try_format(sensor, &fmt->format);
-
-	mf = __fimc_is_sensor_get_format(sensor, fh, fmt->pad, fmt->which);
-	if (mf) {
-		mutex_lock(&sensor->lock);
-		if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
-			*mf = fmt->format;
-		mutex_unlock(&sensor->lock);
-	}
-	return 0;
-}
-
-static int fimc_is_sensor_get_fmt(struct v4l2_subdev *sd,
-				  struct v4l2_subdev_fh *fh,
-				  struct v4l2_subdev_format *fmt)
-{
-	struct fimc_is_sensor *sensor = sd_to_fimc_is_sensor(sd);
-	struct v4l2_mbus_framefmt *mf;
-
-	mf = __fimc_is_sensor_get_format(sensor, fh, fmt->pad, fmt->which);
-
-	mutex_lock(&sensor->lock);
-	fmt->format = *mf;
-	mutex_unlock(&sensor->lock);
-	return 0;
-}
-
-static struct v4l2_subdev_pad_ops fimc_is_sensor_pad_ops = {
-	.enum_mbus_code	= fimc_is_sensor_enum_mbus_code,
-	.get_fmt	= fimc_is_sensor_get_fmt,
-	.set_fmt	= fimc_is_sensor_set_fmt,
-};
-
-static int fimc_is_sensor_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
-{
-	struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0);
-
-	*format		= fimc_is_sensor_formats[0];
-	format->width	= FIMC_IS_SENSOR_DEF_PIX_WIDTH;
-	format->height	= FIMC_IS_SENSOR_DEF_PIX_HEIGHT;
-
-	return 0;
-}
-
-static const struct v4l2_subdev_internal_ops fimc_is_sensor_sd_internal_ops = {
-	.open = fimc_is_sensor_open,
-};
-
-static int fimc_is_sensor_s_power(struct v4l2_subdev *sd, int on)
-{
-	struct fimc_is_sensor *sensor = sd_to_fimc_is_sensor(sd);
-	int gpio = sensor->gpio_reset;
-	int ret;
-
-	if (on) {
-		ret = pm_runtime_get(sensor->dev);
-		if (ret < 0)
-			return ret;
-
-		ret = regulator_bulk_enable(SENSOR_NUM_SUPPLIES,
-					    sensor->supplies);
-		if (ret < 0) {
-			pm_runtime_put(sensor->dev);
-			return ret;
-		}
-		if (gpio_is_valid(gpio)) {
-			gpio_set_value(gpio, 1);
-			usleep_range(600, 800);
-			gpio_set_value(gpio, 0);
-			usleep_range(10000, 11000);
-			gpio_set_value(gpio, 1);
-		}
-
-		/* A delay needed for the sensor initialization. */
-		msleep(20);
-	} else {
-		if (gpio_is_valid(gpio))
-			gpio_set_value(gpio, 0);
-
-		ret = regulator_bulk_disable(SENSOR_NUM_SUPPLIES,
-					     sensor->supplies);
-		if (!ret)
-			pm_runtime_put(sensor->dev);
-	}
-
-	pr_info("%s:%d: on: %d, ret: %d\n", __func__, __LINE__, on, ret);
-
-	return ret;
-}
-
-static struct v4l2_subdev_core_ops fimc_is_sensor_core_ops = {
-	.s_power = fimc_is_sensor_s_power,
-};
-
-static struct v4l2_subdev_ops fimc_is_sensor_subdev_ops = {
-	.core = &fimc_is_sensor_core_ops,
-	.pad = &fimc_is_sensor_pad_ops,
-};
-
-static const struct of_device_id fimc_is_sensor_of_match[];
-
-static int fimc_is_sensor_probe(struct i2c_client *client,
-				const struct i2c_device_id *id)
-{
-	struct device *dev = &client->dev;
-	struct fimc_is_sensor *sensor;
-	const struct of_device_id *of_id;
-	struct v4l2_subdev *sd;
-	int gpio, i, ret;
-
-	sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
-	if (!sensor)
-		return -ENOMEM;
-
-	mutex_init(&sensor->lock);
-	sensor->gpio_reset = -EINVAL;
-
-	gpio = of_get_gpio_flags(dev->of_node, 0, NULL);
-	if (gpio_is_valid(gpio)) {
-		ret = devm_gpio_request_one(dev, gpio, GPIOF_OUT_INIT_LOW,
-							DRIVER_NAME);
-		if (ret < 0)
-			return ret;
-	}
-	sensor->gpio_reset = gpio;
-
-	for (i = 0; i < SENSOR_NUM_SUPPLIES; i++)
-		sensor->supplies[i].supply = sensor_supply_names[i];
-
-	ret = devm_regulator_bulk_get(&client->dev, SENSOR_NUM_SUPPLIES,
-				      sensor->supplies);
-	if (ret < 0)
-		return ret;
-
-	of_id = of_match_node(fimc_is_sensor_of_match, dev->of_node);
-	if (!of_id)
-		return -ENODEV;
-
-	sensor->drvdata = of_id->data;
-	sensor->dev = dev;
-
-	sd = &sensor->subdev;
-	v4l2_i2c_subdev_init(sd, client, &fimc_is_sensor_subdev_ops);
-	snprintf(sd->name, sizeof(sd->name), sensor->drvdata->subdev_name);
-	sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
-
-	sensor->format.code = fimc_is_sensor_formats[0].code;
-	sensor->format.width = FIMC_IS_SENSOR_DEF_PIX_WIDTH;
-	sensor->format.height = FIMC_IS_SENSOR_DEF_PIX_HEIGHT;
-
-	sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
-	ret = media_entity_init(&sd->entity, 1, &sensor->pad, 0);
-	if (ret < 0)
-		return ret;
-
-	pm_runtime_no_callbacks(dev);
-	pm_runtime_enable(dev);
-
-	return ret;
-}
-
-static int fimc_is_sensor_remove(struct i2c_client *client)
-{
-	struct v4l2_subdev *sd = i2c_get_clientdata(client);
-	media_entity_cleanup(&sd->entity);
-	return 0;
-}
-
-static const struct i2c_device_id fimc_is_sensor_ids[] = {
-	{ }
-};
-
 static const struct sensor_drv_data s5k6a3_drvdata = {
 static const struct sensor_drv_data s5k6a3_drvdata = {
 	.id		= FIMC_IS_SENSOR_ID_S5K6A3,
 	.id		= FIMC_IS_SENSOR_ID_S5K6A3,
-	.subdev_name	= "S5K6A3",
-	.width		= S5K6A3_SENSOR_WIDTH,
-	.height		= S5K6A3_SENSOR_HEIGHT,
+	.open_timeout	= S5K6A3_OPEN_TIMEOUT,
 };
 };
 
 
-static const struct of_device_id fimc_is_sensor_of_match[] = {
+static const struct of_device_id fimc_is_sensor_of_ids[] = {
 	{
 	{
 		.compatible	= "samsung,s5k6a3",
 		.compatible	= "samsung,s5k6a3",
 		.data		= &s5k6a3_drvdata,
 		.data		= &s5k6a3_drvdata,
@@ -279,27 +24,11 @@ static const struct of_device_id fimc_is_sensor_of_match[] = {
 	{  }
 	{  }
 };
 };
 
 
-static struct i2c_driver fimc_is_sensor_driver = {
-	.driver = {
-		.of_match_table	= fimc_is_sensor_of_match,
-		.name		= DRIVER_NAME,
-		.owner		= THIS_MODULE,
-	},
-	.probe		= fimc_is_sensor_probe,
-	.remove		= fimc_is_sensor_remove,
-	.id_table	= fimc_is_sensor_ids,
-};
-
-int fimc_is_register_sensor_driver(void)
+const struct sensor_drv_data *fimc_is_sensor_get_drvdata(
+			struct device_node *node)
 {
 {
-	return i2c_add_driver(&fimc_is_sensor_driver);
-}
+	const struct of_device_id *of_id;
 
 
-void fimc_is_unregister_sensor_driver(void)
-{
-	i2c_del_driver(&fimc_is_sensor_driver);
+	of_id = of_match_node(fimc_is_sensor_of_ids, node);
+	return of_id ? of_id->data : NULL;
 }
 }
-
-MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
-MODULE_DESCRIPTION("Exynos4x12 FIMC-IS image sensor subdev driver");
-MODULE_LICENSE("GPL");

+ 8 - 41
drivers/media/platform/exynos4-is/fimc-is-sensor.h

@@ -13,24 +13,13 @@
 #ifndef FIMC_IS_SENSOR_H_
 #ifndef FIMC_IS_SENSOR_H_
 #define FIMC_IS_SENSOR_H_
 #define FIMC_IS_SENSOR_H_
 
 
-#include <linux/clk.h>
-#include <linux/device.h>
-#include <linux/kernel.h>
-#include <linux/platform_device.h>
-#include <linux/regulator/consumer.h>
-#include <linux/videodev2.h>
-#include <media/v4l2-subdev.h>
-
-#define FIMC_IS_SENSOR_OPEN_TIMEOUT	2000 /* ms */
-
-#define FIMC_IS_SENSOR_DEF_PIX_WIDTH	1296
-#define FIMC_IS_SENSOR_DEF_PIX_HEIGHT	732
+#include <linux/of.h>
+#include <linux/types.h>
 
 
+#define S5K6A3_OPEN_TIMEOUT		2000 /* ms */
 #define S5K6A3_SENSOR_WIDTH		1392
 #define S5K6A3_SENSOR_WIDTH		1392
 #define S5K6A3_SENSOR_HEIGHT		1392
 #define S5K6A3_SENSOR_HEIGHT		1392
 
 
-#define SENSOR_NUM_SUPPLIES		2
-
 enum fimc_is_sensor_id {
 enum fimc_is_sensor_id {
 	FIMC_IS_SENSOR_ID_S5K3H2 = 1,
 	FIMC_IS_SENSOR_ID_S5K3H2 = 1,
 	FIMC_IS_SENSOR_ID_S5K6A3,
 	FIMC_IS_SENSOR_ID_S5K6A3,
@@ -45,45 +34,23 @@ enum fimc_is_sensor_id {
 
 
 struct sensor_drv_data {
 struct sensor_drv_data {
 	enum fimc_is_sensor_id id;
 	enum fimc_is_sensor_id id;
-	const char * const subdev_name;
-	unsigned int width;
-	unsigned int height;
+	/* sensor open timeout in ms */
+	unsigned short open_timeout;
 };
 };
 
 
 /**
 /**
  * struct fimc_is_sensor - fimc-is sensor data structure
  * struct fimc_is_sensor - fimc-is sensor data structure
- * @dev: pointer to this I2C client device structure
- * @subdev: the image sensor's v4l2 subdev
- * @pad: subdev media source pad
- * @supplies: image sensor's voltage regulator supplies
- * @gpio_reset: GPIO connected to the sensor's reset pin
  * @drvdata: a pointer to the sensor's parameters data structure
  * @drvdata: a pointer to the sensor's parameters data structure
  * @i2c_bus: ISP I2C bus index (0...1)
  * @i2c_bus: ISP I2C bus index (0...1)
  * @test_pattern: true to enable video test pattern
  * @test_pattern: true to enable video test pattern
- * @lock: mutex protecting the structure's members below
- * @format: media bus format at the sensor's source pad
  */
  */
 struct fimc_is_sensor {
 struct fimc_is_sensor {
-	struct device *dev;
-	struct v4l2_subdev subdev;
-	struct media_pad pad;
-	struct regulator_bulk_data supplies[SENSOR_NUM_SUPPLIES];
-	int gpio_reset;
 	const struct sensor_drv_data *drvdata;
 	const struct sensor_drv_data *drvdata;
 	unsigned int i2c_bus;
 	unsigned int i2c_bus;
-	bool test_pattern;
-
-	struct mutex lock;
-	struct v4l2_mbus_framefmt format;
+	u8 test_pattern;
 };
 };
 
 
-static inline
-struct fimc_is_sensor *sd_to_fimc_is_sensor(struct v4l2_subdev *sd)
-{
-	return container_of(sd, struct fimc_is_sensor, subdev);
-}
-
-int fimc_is_register_sensor_driver(void);
-void fimc_is_unregister_sensor_driver(void);
+const struct sensor_drv_data *fimc_is_sensor_get_drvdata(
+				struct device_node *node);
 
 
 #endif /* FIMC_IS_SENSOR_H_ */
 #endif /* FIMC_IS_SENSOR_H_ */

+ 41 - 57
drivers/media/platform/exynos4-is/fimc-is.c

@@ -161,78 +161,69 @@ static void fimc_is_disable_clocks(struct fimc_is *is)
 	}
 	}
 }
 }
 
 
-static int fimc_is_parse_sensor_config(struct fimc_is_sensor *sensor,
-				       struct device_node *np)
+static int fimc_is_parse_sensor_config(struct fimc_is *is, unsigned int index,
+						struct device_node *node)
 {
 {
+	struct fimc_is_sensor *sensor = &is->sensor[index];
 	u32 tmp = 0;
 	u32 tmp = 0;
 	int ret;
 	int ret;
 
 
-	np = of_graph_get_next_endpoint(np, NULL);
-	if (!np)
+	sensor->drvdata = fimc_is_sensor_get_drvdata(node);
+	if (!sensor->drvdata) {
+		dev_err(&is->pdev->dev, "no driver data found for: %s\n",
+							 node->full_name);
+		return -EINVAL;
+	}
+
+	node = of_graph_get_next_endpoint(node, NULL);
+	if (!node)
 		return -ENXIO;
 		return -ENXIO;
-	np = of_graph_get_remote_port(np);
-	if (!np)
+
+	node = of_graph_get_remote_port(node);
+	if (!node)
 		return -ENXIO;
 		return -ENXIO;
 
 
 	/* Use MIPI-CSIS channel id to determine the ISP I2C bus index. */
 	/* Use MIPI-CSIS channel id to determine the ISP I2C bus index. */
-	ret = of_property_read_u32(np, "reg", &tmp);
-	sensor->i2c_bus = tmp - FIMC_INPUT_MIPI_CSI2_0;
+	ret = of_property_read_u32(node, "reg", &tmp);
+	if (ret < 0) {
+		dev_err(&is->pdev->dev, "reg property not found at: %s\n",
+							 node->full_name);
+		return ret;
+	}
 
 
-	return ret;
+	sensor->i2c_bus = tmp - FIMC_INPUT_MIPI_CSI2_0;
+	return 0;
 }
 }
 
 
 static int fimc_is_register_subdevs(struct fimc_is *is)
 static int fimc_is_register_subdevs(struct fimc_is *is)
 {
 {
-	struct device_node *adapter, *child;
-	int ret;
+	struct device_node *i2c_bus, *child;
+	int ret, index = 0;
 
 
 	ret = fimc_isp_subdev_create(&is->isp);
 	ret = fimc_isp_subdev_create(&is->isp);
 	if (ret < 0)
 	if (ret < 0)
 		return ret;
 		return ret;
 
 
-	for_each_compatible_node(adapter, NULL, FIMC_IS_I2C_COMPATIBLE) {
-		if (!of_find_device_by_node(adapter)) {
-			of_node_put(adapter);
-			return -EPROBE_DEFER;
-		}
-
-		for_each_available_child_of_node(adapter, child) {
-			struct i2c_client *client;
-			struct v4l2_subdev *sd;
-
-			client = of_find_i2c_device_by_node(child);
-			if (!client)
-				goto e_retry;
-
-			sd = i2c_get_clientdata(client);
-			if (!sd)
-				goto e_retry;
+	/* Initialize memory allocator context for the ISP DMA. */
+	is->isp.alloc_ctx = is->alloc_ctx;
 
 
-			/* FIXME: Add support for multiple sensors. */
-			if (WARN_ON(is->sensor))
-				continue;
+	for_each_compatible_node(i2c_bus, NULL, FIMC_IS_I2C_COMPATIBLE) {
+		for_each_available_child_of_node(i2c_bus, child) {
+			ret = fimc_is_parse_sensor_config(is, index, child);
 
 
-			is->sensor = sd_to_fimc_is_sensor(sd);
-
-			if (fimc_is_parse_sensor_config(is->sensor, child)) {
-				dev_warn(&is->pdev->dev, "DT parse error: %s\n",
-							 child->full_name);
+			if (ret < 0 || index >= FIMC_IS_SENSORS_NUM) {
+				of_node_put(child);
+				return ret;
 			}
 			}
-			pr_debug("%s(): registered subdev: %p\n",
-				 __func__, sd->name);
+			index++;
 		}
 		}
 	}
 	}
 	return 0;
 	return 0;
-
-e_retry:
-	of_node_put(child);
-	return -EPROBE_DEFER;
 }
 }
 
 
 static int fimc_is_unregister_subdevs(struct fimc_is *is)
 static int fimc_is_unregister_subdevs(struct fimc_is *is)
 {
 {
 	fimc_isp_subdev_destroy(&is->isp);
 	fimc_isp_subdev_destroy(&is->isp);
-	is->sensor = NULL;
 	return 0;
 	return 0;
 }
 }
 
 
@@ -647,7 +638,7 @@ static int fimc_is_hw_open_sensor(struct fimc_is *is,
 	fimc_is_hw_set_intgr0_gd0(is);
 	fimc_is_hw_set_intgr0_gd0(is);
 
 
 	return fimc_is_wait_event(is, IS_ST_OPEN_SENSOR, 1,
 	return fimc_is_wait_event(is, IS_ST_OPEN_SENSOR, 1,
-				  FIMC_IS_SENSOR_OPEN_TIMEOUT);
+				  sensor->drvdata->open_timeout);
 }
 }
 
 
 
 
@@ -661,8 +652,8 @@ int fimc_is_hw_initialize(struct fimc_is *is)
 	u32 prev_id;
 	u32 prev_id;
 	int i, ret;
 	int i, ret;
 
 
-	/* Sensor initialization. */
-	ret = fimc_is_hw_open_sensor(is, is->sensor);
+	/* Sensor initialization. Only one sensor is currently supported. */
+	ret = fimc_is_hw_open_sensor(is, &is->sensor[0]);
 	if (ret < 0)
 	if (ret < 0)
 		return ret;
 		return ret;
 
 
@@ -977,27 +968,20 @@ static int fimc_is_module_init(void)
 {
 {
 	int ret;
 	int ret;
 
 
-	ret = fimc_is_register_sensor_driver();
-	if (ret < 0)
-		return ret;
-
 	ret = fimc_is_register_i2c_driver();
 	ret = fimc_is_register_i2c_driver();
 	if (ret < 0)
 	if (ret < 0)
-		goto err_sens;
+		return ret;
 
 
 	ret = platform_driver_register(&fimc_is_driver);
 	ret = platform_driver_register(&fimc_is_driver);
-	if (!ret)
-		return ret;
 
 
-	fimc_is_unregister_i2c_driver();
-err_sens:
-	fimc_is_unregister_sensor_driver();
+	if (ret < 0)
+		fimc_is_unregister_i2c_driver();
+
 	return ret;
 	return ret;
 }
 }
 
 
 static void fimc_is_module_exit(void)
 static void fimc_is_module_exit(void)
 {
 {
-	fimc_is_unregister_sensor_driver();
 	fimc_is_unregister_i2c_driver();
 	fimc_is_unregister_i2c_driver();
 	platform_driver_unregister(&fimc_is_driver);
 	platform_driver_unregister(&fimc_is_driver);
 }
 }

+ 7 - 2
drivers/media/platform/exynos4-is/fimc-is.h

@@ -39,7 +39,7 @@
 #define FIMC_IS_FW_LOAD_TIMEOUT		1000 /* ms */
 #define FIMC_IS_FW_LOAD_TIMEOUT		1000 /* ms */
 #define FIMC_IS_POWER_ON_TIMEOUT	1000 /* us */
 #define FIMC_IS_POWER_ON_TIMEOUT	1000 /* us */
 
 
-#define FIMC_IS_SENSOR_NUM		2
+#define FIMC_IS_SENSORS_NUM		2
 
 
 /* Memory definitions */
 /* Memory definitions */
 #define FIMC_IS_CPU_MEM_SIZE		(0xa00000)
 #define FIMC_IS_CPU_MEM_SIZE		(0xa00000)
@@ -253,7 +253,7 @@ struct fimc_is {
 	struct firmware			*f_w;
 	struct firmware			*f_w;
 
 
 	struct fimc_isp			isp;
 	struct fimc_isp			isp;
-	struct fimc_is_sensor		*sensor;
+	struct fimc_is_sensor		sensor[FIMC_IS_SENSORS_NUM];
 	struct fimc_is_setfile		setfile;
 	struct fimc_is_setfile		setfile;
 
 
 	struct vb2_alloc_ctx		*alloc_ctx;
 	struct vb2_alloc_ctx		*alloc_ctx;
@@ -292,6 +292,11 @@ static inline struct fimc_is *fimc_isp_to_is(struct fimc_isp *isp)
 	return container_of(isp, struct fimc_is, isp);
 	return container_of(isp, struct fimc_is, isp);
 }
 }
 
 
+static inline struct chain_config *__get_curr_is_config(struct fimc_is *is)
+{
+	return &is->config[is->config_index];
+}
+
 static inline void fimc_is_mem_barrier(void)
 static inline void fimc_is_mem_barrier(void)
 {
 {
 	mb();
 	mb();

+ 660 - 0
drivers/media/platform/exynos4-is/fimc-isp-video.c

@@ -0,0 +1,660 @@
+/*
+ * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver
+ *
+ * FIMC-IS ISP video input and video output DMA interface driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
+ *
+ * The hardware handling code derived from a driver written by
+ * Younghwan Joo <yhwan.joo@samsung.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.
+ */
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/printk.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/s5p_fimc.h>
+
+#include "common.h"
+#include "media-dev.h"
+#include "fimc-is.h"
+#include "fimc-isp-video.h"
+#include "fimc-is-param.h"
+
+static int isp_video_capture_queue_setup(struct vb2_queue *vq,
+			const struct v4l2_format *pfmt,
+			unsigned int *num_buffers, unsigned int *num_planes,
+			unsigned int sizes[], void *allocators[])
+{
+	struct fimc_isp *isp = vb2_get_drv_priv(vq);
+	struct v4l2_pix_format_mplane *vid_fmt = &isp->video_capture.pixfmt;
+	const struct v4l2_pix_format_mplane *pixm = NULL;
+	const struct fimc_fmt *fmt;
+	unsigned int wh, i;
+
+	if (pfmt) {
+		pixm = &pfmt->fmt.pix_mp;
+		fmt = fimc_isp_find_format(&pixm->pixelformat, NULL, -1);
+		wh = pixm->width * pixm->height;
+	} else {
+		fmt = isp->video_capture.format;
+		wh = vid_fmt->width * vid_fmt->height;
+	}
+
+	if (fmt == NULL)
+		return -EINVAL;
+
+	*num_buffers = clamp_t(u32, *num_buffers, FIMC_ISP_REQ_BUFS_MIN,
+						FIMC_ISP_REQ_BUFS_MAX);
+	*num_planes = fmt->memplanes;
+
+	for (i = 0; i < fmt->memplanes; i++) {
+		unsigned int size = (wh * fmt->depth[i]) / 8;
+		if (pixm)
+			sizes[i] = max(size, pixm->plane_fmt[i].sizeimage);
+		else
+			sizes[i] = size;
+		allocators[i] = isp->alloc_ctx;
+	}
+
+	return 0;
+}
+
+static inline struct param_dma_output *__get_isp_dma2(struct fimc_is *is)
+{
+	return &__get_curr_is_config(is)->isp.dma2_output;
+}
+
+static int isp_video_capture_start_streaming(struct vb2_queue *q,
+						unsigned int count)
+{
+	struct fimc_isp *isp = vb2_get_drv_priv(q);
+	struct fimc_is *is = fimc_isp_to_is(isp);
+	struct param_dma_output *dma = __get_isp_dma2(is);
+	struct fimc_is_video *video = &isp->video_capture;
+	int ret;
+
+	if (!test_bit(ST_ISP_VID_CAP_BUF_PREP, &isp->state) ||
+	    test_bit(ST_ISP_VID_CAP_STREAMING, &isp->state))
+		return 0;
+
+
+	dma->cmd = DMA_OUTPUT_COMMAND_ENABLE;
+	dma->notify_dma_done = DMA_OUTPUT_NOTIFY_DMA_DONE_ENABLE;
+	dma->buffer_address = is->is_dma_p_region +
+				DMA2_OUTPUT_ADDR_ARRAY_OFFS;
+	dma->buffer_number = video->reqbufs_count;
+	dma->dma_out_mask = video->buf_mask;
+
+	isp_dbg(2, &video->ve.vdev,
+		"buf_count: %d, planes: %d, dma addr table: %#x\n",
+		video->buf_count, video->format->memplanes,
+		dma->buffer_address);
+
+	fimc_is_mem_barrier();
+
+	fimc_is_set_param_bit(is, PARAM_ISP_DMA2_OUTPUT);
+	__fimc_is_hw_update_param(is, PARAM_ISP_DMA2_OUTPUT);
+
+	ret = fimc_is_itf_s_param(is, false);
+	if (ret < 0)
+		return ret;
+
+	ret = fimc_pipeline_call(&video->ve, set_stream, 1);
+	if (ret < 0)
+		return ret;
+
+	set_bit(ST_ISP_VID_CAP_STREAMING, &isp->state);
+	return ret;
+}
+
+static int isp_video_capture_stop_streaming(struct vb2_queue *q)
+{
+	struct fimc_isp *isp = vb2_get_drv_priv(q);
+	struct fimc_is *is = fimc_isp_to_is(isp);
+	struct param_dma_output *dma = __get_isp_dma2(is);
+	int ret;
+
+	ret = fimc_pipeline_call(&isp->video_capture.ve, set_stream, 0);
+	if (ret < 0)
+		return ret;
+
+	dma->cmd = DMA_OUTPUT_COMMAND_DISABLE;
+	dma->notify_dma_done = DMA_OUTPUT_NOTIFY_DMA_DONE_DISABLE;
+	dma->buffer_number = 0;
+	dma->buffer_address = 0;
+	dma->dma_out_mask = 0;
+
+	fimc_is_set_param_bit(is, PARAM_ISP_DMA2_OUTPUT);
+	__fimc_is_hw_update_param(is, PARAM_ISP_DMA2_OUTPUT);
+
+	ret = fimc_is_itf_s_param(is, false);
+	if (ret < 0)
+		dev_warn(&is->pdev->dev, "%s: DMA stop failed\n", __func__);
+
+	fimc_is_hw_set_isp_buf_mask(is, 0);
+
+	clear_bit(ST_ISP_VID_CAP_BUF_PREP, &isp->state);
+	clear_bit(ST_ISP_VID_CAP_STREAMING, &isp->state);
+
+	isp->video_capture.buf_count = 0;
+	return 0;
+}
+
+static int isp_video_capture_buffer_prepare(struct vb2_buffer *vb)
+{
+	struct fimc_isp *isp = vb2_get_drv_priv(vb->vb2_queue);
+	struct fimc_is_video *video = &isp->video_capture;
+	int i;
+
+	if (video->format == NULL)
+		return -EINVAL;
+
+	for (i = 0; i < video->format->memplanes; i++) {
+		unsigned long size = video->pixfmt.plane_fmt[i].sizeimage;
+
+		if (vb2_plane_size(vb, i) < size) {
+			v4l2_err(&video->ve.vdev,
+				 "User buffer too small (%ld < %ld)\n",
+				 vb2_plane_size(vb, i), size);
+			return -EINVAL;
+		}
+		vb2_set_plane_payload(vb, i, size);
+	}
+
+	/* Check if we get one of the already known buffers. */
+	if (test_bit(ST_ISP_VID_CAP_BUF_PREP, &isp->state)) {
+		dma_addr_t dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0);
+		int i;
+
+		for (i = 0; i < video->buf_count; i++)
+			if (video->buffers[i]->dma_addr[0] == dma_addr)
+				return 0;
+		return -ENXIO;
+	}
+
+	return 0;
+}
+
+static void isp_video_capture_buffer_queue(struct vb2_buffer *vb)
+{
+	struct fimc_isp *isp = vb2_get_drv_priv(vb->vb2_queue);
+	struct fimc_is_video *video = &isp->video_capture;
+	struct fimc_is *is = fimc_isp_to_is(isp);
+	struct isp_video_buf *ivb = to_isp_video_buf(vb);
+	unsigned long flags;
+	unsigned int i;
+
+	if (test_bit(ST_ISP_VID_CAP_BUF_PREP, &isp->state)) {
+		spin_lock_irqsave(&is->slock, flags);
+		video->buf_mask |= BIT(ivb->index);
+		spin_unlock_irqrestore(&is->slock, flags);
+	} else {
+		unsigned int num_planes = video->format->memplanes;
+
+		ivb->index = video->buf_count;
+		video->buffers[ivb->index] = ivb;
+
+		for (i = 0; i < num_planes; i++) {
+			int buf_index = ivb->index * num_planes + i;
+
+			ivb->dma_addr[i] = vb2_dma_contig_plane_dma_addr(vb, i);
+			is->is_p_region->shared[32 + buf_index] =
+							ivb->dma_addr[i];
+
+			isp_dbg(2, &video->ve.vdev,
+				"dma_buf %d (%d/%d/%d) addr: %#x\n",
+				buf_index, ivb->index, i, vb->v4l2_buf.index,
+				ivb->dma_addr[i]);
+		}
+
+		if (++video->buf_count < video->reqbufs_count)
+			return;
+
+		video->buf_mask = (1UL << video->buf_count) - 1;
+		set_bit(ST_ISP_VID_CAP_BUF_PREP, &isp->state);
+	}
+
+	if (!test_bit(ST_ISP_VID_CAP_STREAMING, &isp->state))
+		isp_video_capture_start_streaming(vb->vb2_queue, 0);
+}
+
+/*
+ * FIMC-IS ISP input and output DMA interface interrupt handler.
+ * Locking: called with is->slock spinlock held.
+ */
+void fimc_isp_video_irq_handler(struct fimc_is *is)
+{
+	struct fimc_is_video *video = &is->isp.video_capture;
+	struct vb2_buffer *vb;
+	int buf_index;
+
+	/* TODO: Ensure the DMA is really stopped in stop_streaming callback */
+	if (!test_bit(ST_ISP_VID_CAP_STREAMING, &is->isp.state))
+		return;
+
+	buf_index = (is->i2h_cmd.args[1] - 1) % video->buf_count;
+	vb = &video->buffers[buf_index]->vb;
+
+	v4l2_get_timestamp(&vb->v4l2_buf.timestamp);
+	vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+
+	video->buf_mask &= ~BIT(buf_index);
+	fimc_is_hw_set_isp_buf_mask(is, video->buf_mask);
+}
+
+static const struct vb2_ops isp_video_capture_qops = {
+	.queue_setup	 = isp_video_capture_queue_setup,
+	.buf_prepare	 = isp_video_capture_buffer_prepare,
+	.buf_queue	 = isp_video_capture_buffer_queue,
+	.wait_prepare	 = vb2_ops_wait_prepare,
+	.wait_finish	 = vb2_ops_wait_finish,
+	.start_streaming = isp_video_capture_start_streaming,
+	.stop_streaming	 = isp_video_capture_stop_streaming,
+};
+
+static int isp_video_open(struct file *file)
+{
+	struct fimc_isp *isp = video_drvdata(file);
+	struct exynos_video_entity *ve = &isp->video_capture.ve;
+	struct media_entity *me = &ve->vdev.entity;
+	int ret;
+
+	if (mutex_lock_interruptible(&isp->video_lock))
+		return -ERESTARTSYS;
+
+	ret = v4l2_fh_open(file);
+	if (ret < 0)
+		goto unlock;
+
+	ret = pm_runtime_get_sync(&isp->pdev->dev);
+	if (ret < 0)
+		goto rel_fh;
+
+	if (v4l2_fh_is_singular_file(file)) {
+		mutex_lock(&me->parent->graph_mutex);
+
+		ret = fimc_pipeline_call(ve, open, me, true);
+
+		/* Mark the video pipeline as in use. */
+		if (ret == 0)
+			me->use_count++;
+
+		mutex_unlock(&me->parent->graph_mutex);
+	}
+	if (!ret)
+		goto unlock;
+rel_fh:
+	v4l2_fh_release(file);
+unlock:
+	mutex_unlock(&isp->video_lock);
+	return ret;
+}
+
+static int isp_video_release(struct file *file)
+{
+	struct fimc_isp *isp = video_drvdata(file);
+	struct fimc_is_video *ivc = &isp->video_capture;
+	struct media_entity *entity = &ivc->ve.vdev.entity;
+	struct media_device *mdev = entity->parent;
+	int ret = 0;
+
+	mutex_lock(&isp->video_lock);
+
+	if (v4l2_fh_is_singular_file(file) && ivc->streaming) {
+		media_entity_pipeline_stop(entity);
+		ivc->streaming = 0;
+	}
+
+	vb2_fop_release(file);
+
+	if (v4l2_fh_is_singular_file(file)) {
+		fimc_pipeline_call(&ivc->ve, close);
+
+		mutex_lock(&mdev->graph_mutex);
+		entity->use_count--;
+		mutex_unlock(&mdev->graph_mutex);
+	}
+
+	pm_runtime_put(&isp->pdev->dev);
+	mutex_unlock(&isp->video_lock);
+
+	return ret;
+}
+
+static const struct v4l2_file_operations isp_video_fops = {
+	.owner		= THIS_MODULE,
+	.open		= isp_video_open,
+	.release	= isp_video_release,
+	.poll		= vb2_fop_poll,
+	.unlocked_ioctl	= video_ioctl2,
+	.mmap		= vb2_fop_mmap,
+};
+
+/*
+ * Video node ioctl operations
+ */
+static int isp_video_querycap(struct file *file, void *priv,
+					struct v4l2_capability *cap)
+{
+	struct fimc_isp *isp = video_drvdata(file);
+
+	__fimc_vidioc_querycap(&isp->pdev->dev, cap, V4L2_CAP_STREAMING);
+	return 0;
+}
+
+static int isp_video_enum_fmt_mplane(struct file *file, void *priv,
+					struct v4l2_fmtdesc *f)
+{
+	const struct fimc_fmt *fmt;
+
+	if (f->index >= FIMC_ISP_NUM_FORMATS)
+		return -EINVAL;
+
+	fmt = fimc_isp_find_format(NULL, NULL, f->index);
+	if (WARN_ON(fmt == NULL))
+		return -EINVAL;
+
+	strlcpy(f->description, fmt->name, sizeof(f->description));
+	f->pixelformat = fmt->fourcc;
+
+	return 0;
+}
+
+static int isp_video_g_fmt_mplane(struct file *file, void *fh,
+					struct v4l2_format *f)
+{
+	struct fimc_isp *isp = video_drvdata(file);
+
+	f->fmt.pix_mp = isp->video_capture.pixfmt;
+	return 0;
+}
+
+static void __isp_video_try_fmt(struct fimc_isp *isp,
+				struct v4l2_pix_format_mplane *pixm,
+				const struct fimc_fmt **fmt)
+{
+	*fmt = fimc_isp_find_format(&pixm->pixelformat, NULL, 2);
+
+	pixm->colorspace = V4L2_COLORSPACE_SRGB;
+	pixm->field = V4L2_FIELD_NONE;
+	pixm->num_planes = (*fmt)->memplanes;
+	pixm->pixelformat = (*fmt)->fourcc;
+	/*
+	 * TODO: double check with the docmentation these width/height
+	 * constraints are correct.
+	 */
+	v4l_bound_align_image(&pixm->width, FIMC_ISP_SOURCE_WIDTH_MIN,
+			      FIMC_ISP_SOURCE_WIDTH_MAX, 3,
+			      &pixm->height, FIMC_ISP_SOURCE_HEIGHT_MIN,
+			      FIMC_ISP_SOURCE_HEIGHT_MAX, 0, 0);
+}
+
+static int isp_video_try_fmt_mplane(struct file *file, void *fh,
+					struct v4l2_format *f)
+{
+	struct fimc_isp *isp = video_drvdata(file);
+
+	__isp_video_try_fmt(isp, &f->fmt.pix_mp, NULL);
+	return 0;
+}
+
+static int isp_video_s_fmt_mplane(struct file *file, void *priv,
+					struct v4l2_format *f)
+{
+	struct fimc_isp *isp = video_drvdata(file);
+	struct fimc_is *is = fimc_isp_to_is(isp);
+	struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp;
+	const struct fimc_fmt *ifmt = NULL;
+	struct param_dma_output *dma = __get_isp_dma2(is);
+
+	__isp_video_try_fmt(isp, pixm, &ifmt);
+
+	if (WARN_ON(ifmt == NULL))
+		return -EINVAL;
+
+	dma->format = DMA_OUTPUT_FORMAT_BAYER;
+	dma->order = DMA_OUTPUT_ORDER_GB_BG;
+	dma->plane = ifmt->memplanes;
+	dma->bitwidth = ifmt->depth[0];
+	dma->width = pixm->width;
+	dma->height = pixm->height;
+
+	fimc_is_mem_barrier();
+
+	isp->video_capture.format = ifmt;
+	isp->video_capture.pixfmt = *pixm;
+
+	return 0;
+}
+
+/*
+ * Check for source/sink format differences at each link.
+ * Return 0 if the formats match or -EPIPE otherwise.
+ */
+static int isp_video_pipeline_validate(struct fimc_isp *isp)
+{
+	struct v4l2_subdev *sd = &isp->subdev;
+	struct v4l2_subdev_format sink_fmt, src_fmt;
+	struct media_pad *pad;
+	int ret;
+
+	while (1) {
+		/* Retrieve format at the sink pad */
+		pad = &sd->entity.pads[0];
+		if (!(pad->flags & MEDIA_PAD_FL_SINK))
+			break;
+		sink_fmt.pad = pad->index;
+		sink_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+		ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sink_fmt);
+		if (ret < 0 && ret != -ENOIOCTLCMD)
+			return -EPIPE;
+
+		/* Retrieve format at the source pad */
+		pad = media_entity_remote_pad(pad);
+		if (pad == NULL ||
+		    media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+			break;
+
+		sd = media_entity_to_v4l2_subdev(pad->entity);
+		src_fmt.pad = pad->index;
+		src_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+		ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &src_fmt);
+		if (ret < 0 && ret != -ENOIOCTLCMD)
+			return -EPIPE;
+
+		if (src_fmt.format.width != sink_fmt.format.width ||
+		    src_fmt.format.height != sink_fmt.format.height ||
+		    src_fmt.format.code != sink_fmt.format.code)
+			return -EPIPE;
+	}
+
+	return 0;
+}
+
+static int isp_video_streamon(struct file *file, void *priv,
+				      enum v4l2_buf_type type)
+{
+	struct fimc_isp *isp = video_drvdata(file);
+	struct exynos_video_entity *ve = &isp->video_capture.ve;
+	struct media_entity *me = &ve->vdev.entity;
+	int ret;
+
+	ret = media_entity_pipeline_start(me, &ve->pipe->mp);
+	if (ret < 0)
+		return ret;
+
+	ret = isp_video_pipeline_validate(isp);
+	if (ret < 0)
+		goto p_stop;
+
+	ret = vb2_ioctl_streamon(file, priv, type);
+	if (ret < 0)
+		goto p_stop;
+
+	isp->video_capture.streaming = 1;
+	return 0;
+p_stop:
+	media_entity_pipeline_stop(me);
+	return ret;
+}
+
+static int isp_video_streamoff(struct file *file, void *priv,
+					enum v4l2_buf_type type)
+{
+	struct fimc_isp *isp = video_drvdata(file);
+	struct fimc_is_video *video = &isp->video_capture;
+	int ret;
+
+	ret = vb2_ioctl_streamoff(file, priv, type);
+	if (ret < 0)
+		return ret;
+
+	media_entity_pipeline_stop(&video->ve.vdev.entity);
+	video->streaming = 0;
+	return 0;
+}
+
+static int isp_video_reqbufs(struct file *file, void *priv,
+				struct v4l2_requestbuffers *rb)
+{
+	struct fimc_isp *isp = video_drvdata(file);
+	int ret;
+
+	ret = vb2_ioctl_reqbufs(file, priv, rb);
+	if (ret < 0)
+		return ret;
+
+	if (rb->count && rb->count < FIMC_ISP_REQ_BUFS_MIN) {
+		rb->count = 0;
+		vb2_ioctl_reqbufs(file, priv, rb);
+		ret = -ENOMEM;
+	}
+
+	isp->video_capture.reqbufs_count = rb->count;
+	return ret;
+}
+
+static const struct v4l2_ioctl_ops isp_video_ioctl_ops = {
+	.vidioc_querycap		= isp_video_querycap,
+	.vidioc_enum_fmt_vid_cap_mplane	= isp_video_enum_fmt_mplane,
+	.vidioc_try_fmt_vid_cap_mplane	= isp_video_try_fmt_mplane,
+	.vidioc_s_fmt_vid_cap_mplane	= isp_video_s_fmt_mplane,
+	.vidioc_g_fmt_vid_cap_mplane	= isp_video_g_fmt_mplane,
+	.vidioc_reqbufs			= isp_video_reqbufs,
+	.vidioc_querybuf		= vb2_ioctl_querybuf,
+	.vidioc_prepare_buf		= vb2_ioctl_prepare_buf,
+	.vidioc_create_bufs		= vb2_ioctl_create_bufs,
+	.vidioc_qbuf			= vb2_ioctl_qbuf,
+	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
+	.vidioc_streamon		= isp_video_streamon,
+	.vidioc_streamoff		= isp_video_streamoff,
+};
+
+int fimc_isp_video_device_register(struct fimc_isp *isp,
+				   struct v4l2_device *v4l2_dev,
+				   enum v4l2_buf_type type)
+{
+	struct vb2_queue *q = &isp->video_capture.vb_queue;
+	struct fimc_is_video *iv;
+	struct video_device *vdev;
+	int ret;
+
+	if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+		iv = &isp->video_capture;
+	else
+		return -ENOSYS;
+
+	mutex_init(&isp->video_lock);
+	INIT_LIST_HEAD(&iv->pending_buf_q);
+	INIT_LIST_HEAD(&iv->active_buf_q);
+	iv->format = fimc_isp_find_format(NULL, NULL, 0);
+	iv->pixfmt.width = IS_DEFAULT_WIDTH;
+	iv->pixfmt.height = IS_DEFAULT_HEIGHT;
+	iv->pixfmt.pixelformat = iv->format->fourcc;
+	iv->pixfmt.colorspace = V4L2_COLORSPACE_SRGB;
+	iv->reqbufs_count = 0;
+
+	memset(q, 0, sizeof(*q));
+	q->type = type;
+	q->io_modes = VB2_MMAP | VB2_USERPTR;
+	q->ops = &isp_video_capture_qops;
+	q->mem_ops = &vb2_dma_contig_memops;
+	q->buf_struct_size = sizeof(struct isp_video_buf);
+	q->drv_priv = isp;
+	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+	q->lock = &isp->video_lock;
+
+	ret = vb2_queue_init(q);
+	if (ret < 0)
+		return ret;
+
+	vdev = &iv->ve.vdev;
+	memset(vdev, 0, sizeof(*vdev));
+	snprintf(vdev->name, sizeof(vdev->name), "fimc-is-isp.%s",
+			type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ?
+			"capture" : "output");
+	vdev->queue = q;
+	vdev->fops = &isp_video_fops;
+	vdev->ioctl_ops = &isp_video_ioctl_ops;
+	vdev->v4l2_dev = v4l2_dev;
+	vdev->minor = -1;
+	vdev->release = video_device_release_empty;
+	vdev->lock = &isp->video_lock;
+
+	iv->pad.flags = MEDIA_PAD_FL_SINK;
+	ret = media_entity_init(&vdev->entity, 1, &iv->pad, 0);
+	if (ret < 0)
+		return ret;
+
+	video_set_drvdata(vdev, isp);
+
+	ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+	if (ret < 0) {
+		media_entity_cleanup(&vdev->entity);
+		return ret;
+	}
+
+	v4l2_info(v4l2_dev, "Registered %s as /dev/%s\n",
+		  vdev->name, video_device_node_name(vdev));
+
+	return 0;
+}
+
+void fimc_isp_video_device_unregister(struct fimc_isp *isp,
+				      enum v4l2_buf_type type)
+{
+	struct exynos_video_entity *ve;
+
+	if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+		ve = &isp->video_capture.ve;
+	else
+		return;
+
+	mutex_lock(&isp->video_lock);
+
+	if (video_is_registered(&ve->vdev)) {
+		video_unregister_device(&ve->vdev);
+		media_entity_cleanup(&ve->vdev.entity);
+		ve->pipe = NULL;
+	}
+
+	mutex_unlock(&isp->video_lock);
+}

+ 44 - 0
drivers/media/platform/exynos4-is/fimc-isp-video.h

@@ -0,0 +1,44 @@
+/*
+ * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Sylwester Nawrocki <s.nawrocki@samsung.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.
+ */
+#ifndef FIMC_ISP_VIDEO__
+#define FIMC_ISP_VIDEO__
+
+#include <media/videobuf2-core.h>
+#include "fimc-isp.h"
+
+#ifdef CONFIG_VIDEO_EXYNOS4_ISP_DMA_CAPTURE
+int fimc_isp_video_device_register(struct fimc_isp *isp,
+				struct v4l2_device *v4l2_dev,
+				enum v4l2_buf_type type);
+
+void fimc_isp_video_device_unregister(struct fimc_isp *isp,
+				enum v4l2_buf_type type);
+
+void fimc_isp_video_irq_handler(struct fimc_is *is);
+#else
+static inline void fimc_isp_video_irq_handler(struct fimc_is *is)
+{
+}
+
+static inline int fimc_isp_video_device_register(struct fimc_isp *isp,
+						struct v4l2_device *v4l2_dev,
+						enum v4l2_buf_type type)
+{
+	return 0;
+}
+
+void fimc_isp_video_device_unregister(struct fimc_isp *isp,
+				enum v4l2_buf_type type)
+{
+}
+#endif /* !CONFIG_VIDEO_EXYNOS4_ISP_DMA_CAPTURE */
+
+#endif /* FIMC_ISP_VIDEO__ */

+ 28 - 1
drivers/media/platform/exynos4-is/fimc-isp.c

@@ -25,6 +25,7 @@
 #include <media/v4l2-device.h>
 #include <media/v4l2-device.h>
 
 
 #include "media-dev.h"
 #include "media-dev.h"
+#include "fimc-isp-video.h"
 #include "fimc-is-command.h"
 #include "fimc-is-command.h"
 #include "fimc-is-param.h"
 #include "fimc-is-param.h"
 #include "fimc-is-regs.h"
 #include "fimc-is-regs.h"
@@ -93,8 +94,8 @@ void fimc_isp_irq_handler(struct fimc_is *is)
 	is->i2h_cmd.args[1] = mcuctl_read(is, MCUCTL_REG_ISSR(21));
 	is->i2h_cmd.args[1] = mcuctl_read(is, MCUCTL_REG_ISSR(21));
 
 
 	fimc_is_fw_clear_irq1(is, FIMC_IS_INT_FRAME_DONE_ISP);
 	fimc_is_fw_clear_irq1(is, FIMC_IS_INT_FRAME_DONE_ISP);
+	fimc_isp_video_irq_handler(is);
 
 
-	/* TODO: Complete ISP DMA interrupt handler */
 	wake_up(&is->irq_queue);
 	wake_up(&is->irq_queue);
 }
 }
 
 
@@ -388,7 +389,33 @@ static int fimc_isp_subdev_open(struct v4l2_subdev *sd,
 	return 0;
 	return 0;
 }
 }
 
 
+static int fimc_isp_subdev_registered(struct v4l2_subdev *sd)
+{
+	struct fimc_isp *isp = v4l2_get_subdevdata(sd);
+	int ret;
+
+	/* Use pipeline object allocated by the media device. */
+	isp->video_capture.ve.pipe = v4l2_get_subdev_hostdata(sd);
+
+	ret = fimc_isp_video_device_register(isp, sd->v4l2_dev,
+			V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+	if (ret < 0)
+		isp->video_capture.ve.pipe = NULL;
+
+	return ret;
+}
+
+static void fimc_isp_subdev_unregistered(struct v4l2_subdev *sd)
+{
+	struct fimc_isp *isp = v4l2_get_subdevdata(sd);
+
+	fimc_isp_video_device_unregister(isp,
+			V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+}
+
 static const struct v4l2_subdev_internal_ops fimc_is_subdev_internal_ops = {
 static const struct v4l2_subdev_internal_ops fimc_is_subdev_internal_ops = {
+	.registered = fimc_isp_subdev_registered,
+	.unregistered = fimc_isp_subdev_unregistered,
 	.open = fimc_isp_subdev_open,
 	.open = fimc_isp_subdev_open,
 };
 };
 
 

+ 23 - 4
drivers/media/platform/exynos4-is/fimc-isp.h

@@ -35,17 +35,18 @@ extern int fimc_isp_debug;
 #define FIMC_ISP_SINK_WIDTH_MIN		(16 + 8)
 #define FIMC_ISP_SINK_WIDTH_MIN		(16 + 8)
 #define FIMC_ISP_SINK_HEIGHT_MIN	(12 + 8)
 #define FIMC_ISP_SINK_HEIGHT_MIN	(12 + 8)
 #define FIMC_ISP_SOURCE_WIDTH_MIN	8
 #define FIMC_ISP_SOURCE_WIDTH_MIN	8
-#define FIMC_ISP_SOURC_HEIGHT_MIN	8
+#define FIMC_ISP_SOURCE_HEIGHT_MIN	8
 #define FIMC_ISP_CAC_MARGIN_WIDTH	16
 #define FIMC_ISP_CAC_MARGIN_WIDTH	16
 #define FIMC_ISP_CAC_MARGIN_HEIGHT	12
 #define FIMC_ISP_CAC_MARGIN_HEIGHT	12
 
 
 #define FIMC_ISP_SINK_WIDTH_MAX		(4000 - 16)
 #define FIMC_ISP_SINK_WIDTH_MAX		(4000 - 16)
 #define FIMC_ISP_SINK_HEIGHT_MAX	(4000 + 12)
 #define FIMC_ISP_SINK_HEIGHT_MAX	(4000 + 12)
 #define FIMC_ISP_SOURCE_WIDTH_MAX	4000
 #define FIMC_ISP_SOURCE_WIDTH_MAX	4000
-#define FIMC_ISP_SOURC_HEIGHT_MAX	4000
+#define FIMC_ISP_SOURCE_HEIGHT_MAX	4000
 
 
 #define FIMC_ISP_NUM_FORMATS		3
 #define FIMC_ISP_NUM_FORMATS		3
 #define FIMC_ISP_REQ_BUFS_MIN		2
 #define FIMC_ISP_REQ_BUFS_MIN		2
+#define FIMC_ISP_REQ_BUFS_MAX		32
 
 
 #define FIMC_ISP_SD_PAD_SINK		0
 #define FIMC_ISP_SD_PAD_SINK		0
 #define FIMC_ISP_SD_PAD_SRC_FIFO	1
 #define FIMC_ISP_SD_PAD_SRC_FIFO	1
@@ -100,6 +101,16 @@ struct fimc_isp_ctrls {
 	struct v4l2_ctrl *colorfx;
 	struct v4l2_ctrl *colorfx;
 };
 };
 
 
+struct isp_video_buf {
+	struct vb2_buffer vb;
+	dma_addr_t dma_addr[FIMC_ISP_MAX_PLANES];
+	unsigned int index;
+};
+
+#define to_isp_video_buf(_b) container_of(_b, struct isp_video_buf, vb)
+
+#define FIMC_ISP_MAX_BUFS	4
+
 /**
 /**
  * struct fimc_is_video - fimc-is video device structure
  * struct fimc_is_video - fimc-is video device structure
  * @vdev: video_device structure
  * @vdev: video_device structure
@@ -114,18 +125,26 @@ struct fimc_isp_ctrls {
  * @format: current pixel format
  * @format: current pixel format
  */
  */
 struct fimc_is_video {
 struct fimc_is_video {
-	struct video_device	vdev;
+	struct exynos_video_entity ve;
 	enum v4l2_buf_type	type;
 	enum v4l2_buf_type	type;
 	struct media_pad	pad;
 	struct media_pad	pad;
 	struct list_head	pending_buf_q;
 	struct list_head	pending_buf_q;
 	struct list_head	active_buf_q;
 	struct list_head	active_buf_q;
 	struct vb2_queue	vb_queue;
 	struct vb2_queue	vb_queue;
-	unsigned int		frame_count;
 	unsigned int		reqbufs_count;
 	unsigned int		reqbufs_count;
+	unsigned int		buf_count;
+	unsigned int		buf_mask;
+	unsigned int		frame_count;
 	int			streaming;
 	int			streaming;
+	struct isp_video_buf	*buffers[FIMC_ISP_MAX_BUFS];
 	const struct fimc_fmt	*format;
 	const struct fimc_fmt	*format;
+	struct v4l2_pix_format_mplane pixfmt;
 };
 };
 
 
+/* struct fimc_isp:state bit definitions */
+#define ST_ISP_VID_CAP_BUF_PREP		0
+#define ST_ISP_VID_CAP_STREAMING	1
+
 /**
 /**
  * struct fimc_isp - FIMC-IS ISP data structure
  * struct fimc_isp - FIMC-IS ISP data structure
  * @pdev: pointer to FIMC-IS platform device
  * @pdev: pointer to FIMC-IS platform device

+ 265 - 98
drivers/media/platform/exynos4-is/media-dev.c

@@ -11,6 +11,8 @@
  */
  */
 
 
 #include <linux/bug.h>
 #include <linux/bug.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
 #include <linux/device.h>
 #include <linux/device.h>
 #include <linux/errno.h>
 #include <linux/errno.h>
 #include <linux/i2c.h>
 #include <linux/i2c.h>
@@ -25,6 +27,7 @@
 #include <linux/pm_runtime.h>
 #include <linux/pm_runtime.h>
 #include <linux/types.h>
 #include <linux/types.h>
 #include <linux/slab.h>
 #include <linux/slab.h>
+#include <media/v4l2-async.h>
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-of.h>
 #include <media/v4l2-of.h>
 #include <media/media-device.h>
 #include <media/media-device.h>
@@ -219,6 +222,7 @@ static int __fimc_pipeline_open(struct exynos_media_pipeline *ep,
 		if (ret < 0)
 		if (ret < 0)
 			return ret;
 			return ret;
 	}
 	}
+
 	ret = fimc_md_set_camclk(sd, true);
 	ret = fimc_md_set_camclk(sd, true);
 	if (ret < 0)
 	if (ret < 0)
 		goto err_wbclk;
 		goto err_wbclk;
@@ -379,77 +383,18 @@ static void fimc_md_unregister_sensor(struct v4l2_subdev *sd)
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct i2c_adapter *adapter;
 	struct i2c_adapter *adapter;
 
 
-	if (!client)
+	if (!client || client->dev.of_node)
 		return;
 		return;
 
 
 	v4l2_device_unregister_subdev(sd);
 	v4l2_device_unregister_subdev(sd);
 
 
-	if (!client->dev.of_node) {
-		adapter = client->adapter;
-		i2c_unregister_device(client);
-		if (adapter)
-			i2c_put_adapter(adapter);
-	}
+	adapter = client->adapter;
+	i2c_unregister_device(client);
+	if (adapter)
+		i2c_put_adapter(adapter);
 }
 }
 
 
 #ifdef CONFIG_OF
 #ifdef CONFIG_OF
-/* Register I2C client subdev associated with @node. */
-static int fimc_md_of_add_sensor(struct fimc_md *fmd,
-				 struct device_node *node, int index)
-{
-	struct fimc_sensor_info *si;
-	struct i2c_client *client;
-	struct v4l2_subdev *sd;
-	int ret;
-
-	if (WARN_ON(index >= ARRAY_SIZE(fmd->sensor)))
-		return -EINVAL;
-	si = &fmd->sensor[index];
-
-	client = of_find_i2c_device_by_node(node);
-	if (!client)
-		return -EPROBE_DEFER;
-
-	device_lock(&client->dev);
-
-	if (!client->dev.driver ||
-	    !try_module_get(client->dev.driver->owner)) {
-		ret = -EPROBE_DEFER;
-		v4l2_info(&fmd->v4l2_dev, "No driver found for %s\n",
-						node->full_name);
-		goto dev_put;
-	}
-
-	/* Enable sensor's master clock */
-	ret = __fimc_md_set_camclk(fmd, &si->pdata, true);
-	if (ret < 0)
-		goto mod_put;
-	sd = i2c_get_clientdata(client);
-
-	ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
-	__fimc_md_set_camclk(fmd, &si->pdata, false);
-	if (ret < 0)
-		goto mod_put;
-
-	v4l2_set_subdev_hostdata(sd, &si->pdata);
-	if (si->pdata.fimc_bus_type == FIMC_BUS_TYPE_ISP_WRITEBACK)
-		sd->grp_id = GRP_ID_FIMC_IS_SENSOR;
-	else
-		sd->grp_id = GRP_ID_SENSOR;
-
-	si->subdev = sd;
-	v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice: %s (%d)\n",
-		  sd->name, fmd->num_sensors);
-	fmd->num_sensors++;
-
-mod_put:
-	module_put(client->dev.driver->owner);
-dev_put:
-	device_unlock(&client->dev);
-	put_device(&client->dev);
-	return ret;
-}
-
 /* Parse port node and register as a sub-device any sensor specified there. */
 /* Parse port node and register as a sub-device any sensor specified there. */
 static int fimc_md_parse_port_node(struct fimc_md *fmd,
 static int fimc_md_parse_port_node(struct fimc_md *fmd,
 				   struct device_node *port,
 				   struct device_node *port,
@@ -458,7 +403,6 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd,
 	struct device_node *rem, *ep, *np;
 	struct device_node *rem, *ep, *np;
 	struct fimc_source_info *pd;
 	struct fimc_source_info *pd;
 	struct v4l2_of_endpoint endpoint;
 	struct v4l2_of_endpoint endpoint;
-	int ret;
 	u32 val;
 	u32 val;
 
 
 	pd = &fmd->sensor[index].pdata;
 	pd = &fmd->sensor[index].pdata;
@@ -486,6 +430,8 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd,
 
 
 	if (!of_property_read_u32(rem, "clock-frequency", &val))
 	if (!of_property_read_u32(rem, "clock-frequency", &val))
 		pd->clk_frequency = val;
 		pd->clk_frequency = val;
+	else
+		pd->clk_frequency = DEFAULT_SENSOR_CLK_FREQ;
 
 
 	if (pd->clk_frequency == 0) {
 	if (pd->clk_frequency == 0) {
 		v4l2_err(&fmd->v4l2_dev, "Wrong clock frequency at node %s\n",
 		v4l2_err(&fmd->v4l2_dev, "Wrong clock frequency at node %s\n",
@@ -525,10 +471,17 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd,
 	else
 	else
 		pd->fimc_bus_type = pd->sensor_bus_type;
 		pd->fimc_bus_type = pd->sensor_bus_type;
 
 
-	ret = fimc_md_of_add_sensor(fmd, rem, index);
-	of_node_put(rem);
+	if (WARN_ON(index >= ARRAY_SIZE(fmd->sensor)))
+		return -EINVAL;
 
 
-	return ret;
+	fmd->sensor[index].asd.match_type = V4L2_ASYNC_MATCH_OF;
+	fmd->sensor[index].asd.match.of.node = rem;
+	fmd->async_subdevs[index] = &fmd->sensor[index].asd;
+
+	fmd->num_sensors++;
+
+	of_node_put(rem);
+	return 0;
 }
 }
 
 
 /* Register all SoC external sub-devices */
 /* Register all SoC external sub-devices */
@@ -732,8 +685,16 @@ static int register_csis_entity(struct fimc_md *fmd,
 static int register_fimc_is_entity(struct fimc_md *fmd, struct fimc_is *is)
 static int register_fimc_is_entity(struct fimc_md *fmd, struct fimc_is *is)
 {
 {
 	struct v4l2_subdev *sd = &is->isp.subdev;
 	struct v4l2_subdev *sd = &is->isp.subdev;
+	struct exynos_media_pipeline *ep;
 	int ret;
 	int ret;
 
 
+	/* Allocate pipeline object for the ISP capture video node. */
+	ep = fimc_md_pipeline_create(fmd);
+	if (!ep)
+		return -ENOMEM;
+
+	v4l2_set_subdev_hostdata(sd, ep);
+
 	ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
 	ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
 	if (ret) {
 	if (ret) {
 		v4l2_err(&fmd->v4l2_dev,
 		v4l2_err(&fmd->v4l2_dev,
@@ -884,11 +845,13 @@ static void fimc_md_unregister_entities(struct fimc_md *fmd)
 		v4l2_device_unregister_subdev(fmd->csis[i].sd);
 		v4l2_device_unregister_subdev(fmd->csis[i].sd);
 		fmd->csis[i].sd = NULL;
 		fmd->csis[i].sd = NULL;
 	}
 	}
-	for (i = 0; i < fmd->num_sensors; i++) {
-		if (fmd->sensor[i].subdev == NULL)
-			continue;
-		fimc_md_unregister_sensor(fmd->sensor[i].subdev);
-		fmd->sensor[i].subdev = NULL;
+	if (fmd->pdev->dev.of_node == NULL) {
+		for (i = 0; i < fmd->num_sensors; i++) {
+			if (fmd->sensor[i].subdev == NULL)
+				continue;
+			fimc_md_unregister_sensor(fmd->sensor[i].subdev);
+			fmd->sensor[i].subdev = NULL;
+		}
 	}
 	}
 
 
 	if (fmd->fimc_is)
 	if (fmd->fimc_is)
@@ -1005,16 +968,17 @@ static int __fimc_md_create_flite_source_links(struct fimc_md *fmd)
 /* Create FIMC-IS links */
 /* Create FIMC-IS links */
 static int __fimc_md_create_fimc_is_links(struct fimc_md *fmd)
 static int __fimc_md_create_fimc_is_links(struct fimc_md *fmd)
 {
 {
+	struct fimc_isp *isp = &fmd->fimc_is->isp;
 	struct media_entity *source, *sink;
 	struct media_entity *source, *sink;
 	int i, ret;
 	int i, ret;
 
 
-	source = &fmd->fimc_is->isp.subdev.entity;
+	source = &isp->subdev.entity;
 
 
 	for (i = 0; i < FIMC_MAX_DEVS; i++) {
 	for (i = 0; i < FIMC_MAX_DEVS; i++) {
 		if (fmd->fimc[i] == NULL)
 		if (fmd->fimc[i] == NULL)
 			continue;
 			continue;
 
 
-		/* Link from IS-ISP subdev to FIMC */
+		/* Link from FIMC-IS-ISP subdev to FIMC */
 		sink = &fmd->fimc[i]->vid_cap.subdev.entity;
 		sink = &fmd->fimc[i]->vid_cap.subdev.entity;
 		ret = media_entity_create_link(source, FIMC_ISP_SD_PAD_SRC_FIFO,
 		ret = media_entity_create_link(source, FIMC_ISP_SD_PAD_SRC_FIFO,
 					       sink, FIMC_SD_PAD_SINK_FIFO, 0);
 					       sink, FIMC_SD_PAD_SINK_FIFO, 0);
@@ -1022,7 +986,15 @@ static int __fimc_md_create_fimc_is_links(struct fimc_md *fmd)
 			return ret;
 			return ret;
 	}
 	}
 
 
-	return ret;
+	/* Link from FIMC-IS-ISP subdev to fimc-is-isp.capture video node */
+	sink = &isp->video_capture.ve.vdev.entity;
+
+	/* Skip this link if the fimc-is-isp video node driver isn't built-in */
+	if (sink->num_pads == 0)
+		return 0;
+
+	return media_entity_create_link(source, FIMC_ISP_SD_PAD_SRC_DMA,
+					sink, 0, 0);
 }
 }
 
 
 /**
 /**
@@ -1223,6 +1195,14 @@ static int __fimc_md_set_camclk(struct fimc_md *fmd,
 	struct fimc_camclk_info *camclk;
 	struct fimc_camclk_info *camclk;
 	int ret = 0;
 	int ret = 0;
 
 
+	/*
+	 * When device tree is used the sensor drivers are supposed to
+	 * control the clock themselves. This whole function will be
+	 * removed once S5PV210 platform is converted to the device tree.
+	 */
+	if (fmd->pdev->dev.of_node)
+		return 0;
+
 	if (WARN_ON(si->clk_id >= FIMC_MAX_CAMCLKS) || !fmd || !fmd->pmf)
 	if (WARN_ON(si->clk_id >= FIMC_MAX_CAMCLKS) || !fmd || !fmd->pmf)
 		return -EINVAL;
 		return -EINVAL;
 
 
@@ -1277,6 +1257,14 @@ int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on)
 	struct fimc_source_info *si = v4l2_get_subdev_hostdata(sd);
 	struct fimc_source_info *si = v4l2_get_subdev_hostdata(sd);
 	struct fimc_md *fmd = entity_to_fimc_mdev(&sd->entity);
 	struct fimc_md *fmd = entity_to_fimc_mdev(&sd->entity);
 
 
+	/*
+	 * If there is a clock provider registered the sensors will
+	 * handle their clock themselves, no need to control it on
+	 * the host interface side.
+	 */
+	if (fmd->clk_provider.num_clocks > 0)
+		return 0;
+
 	return __fimc_md_set_camclk(fmd, si, on);
 	return __fimc_md_set_camclk(fmd, si, on);
 }
 }
 
 
@@ -1438,6 +1426,153 @@ static int fimc_md_get_pinctrl(struct fimc_md *fmd)
 	return 0;
 	return 0;
 }
 }
 
 
+#ifdef CONFIG_OF
+static int cam_clk_prepare(struct clk_hw *hw)
+{
+	struct cam_clk *camclk = to_cam_clk(hw);
+	int ret;
+
+	if (camclk->fmd->pmf == NULL)
+		return -ENODEV;
+
+	ret = pm_runtime_get_sync(camclk->fmd->pmf);
+	return ret < 0 ? ret : 0;
+}
+
+static void cam_clk_unprepare(struct clk_hw *hw)
+{
+	struct cam_clk *camclk = to_cam_clk(hw);
+
+	if (camclk->fmd->pmf == NULL)
+		return;
+
+	pm_runtime_put_sync(camclk->fmd->pmf);
+}
+
+static const struct clk_ops cam_clk_ops = {
+	.prepare = cam_clk_prepare,
+	.unprepare = cam_clk_unprepare,
+};
+
+static void fimc_md_unregister_clk_provider(struct fimc_md *fmd)
+{
+	struct cam_clk_provider *cp = &fmd->clk_provider;
+	unsigned int i;
+
+	if (cp->of_node)
+		of_clk_del_provider(cp->of_node);
+
+	for (i = 0; i < cp->num_clocks; i++)
+		clk_unregister(cp->clks[i]);
+}
+
+static int fimc_md_register_clk_provider(struct fimc_md *fmd)
+{
+	struct cam_clk_provider *cp = &fmd->clk_provider;
+	struct device *dev = &fmd->pdev->dev;
+	int i, ret;
+
+	for (i = 0; i < FIMC_MAX_CAMCLKS; i++) {
+		struct cam_clk *camclk = &cp->camclk[i];
+		struct clk_init_data init;
+		const char *p_name;
+
+		ret = of_property_read_string_index(dev->of_node,
+					"clock-output-names", i, &init.name);
+		if (ret < 0)
+			break;
+
+		p_name = __clk_get_name(fmd->camclk[i].clock);
+
+		/* It's safe since clk_register() will duplicate the string. */
+		init.parent_names = &p_name;
+		init.num_parents = 1;
+		init.ops = &cam_clk_ops;
+		init.flags = CLK_SET_RATE_PARENT;
+		camclk->hw.init = &init;
+		camclk->fmd = fmd;
+
+		cp->clks[i] = clk_register(NULL, &camclk->hw);
+		if (IS_ERR(cp->clks[i])) {
+			dev_err(dev, "failed to register clock: %s (%ld)\n",
+					init.name, PTR_ERR(cp->clks[i]));
+			ret = PTR_ERR(cp->clks[i]);
+			goto err;
+		}
+		cp->num_clocks++;
+	}
+
+	if (cp->num_clocks == 0) {
+		dev_warn(dev, "clk provider not registered\n");
+		return 0;
+	}
+
+	cp->clk_data.clks = cp->clks;
+	cp->clk_data.clk_num = cp->num_clocks;
+	cp->of_node = dev->of_node;
+	ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get,
+				  &cp->clk_data);
+	if (ret == 0)
+		return 0;
+err:
+	fimc_md_unregister_clk_provider(fmd);
+	return ret;
+}
+#else
+#define fimc_md_register_clk_provider(fmd) (0)
+#define fimc_md_unregister_clk_provider(fmd) (0)
+#endif
+
+static int subdev_notifier_bound(struct v4l2_async_notifier *notifier,
+				 struct v4l2_subdev *subdev,
+				 struct v4l2_async_subdev *asd)
+{
+	struct fimc_md *fmd = notifier_to_fimc_md(notifier);
+	struct fimc_sensor_info *si = NULL;
+	int i;
+
+	/* Find platform data for this sensor subdev */
+	for (i = 0; i < ARRAY_SIZE(fmd->sensor); i++)
+		if (fmd->sensor[i].asd.match.of.node == subdev->dev->of_node)
+			si = &fmd->sensor[i];
+
+	if (si == NULL)
+		return -EINVAL;
+
+	v4l2_set_subdev_hostdata(subdev, &si->pdata);
+
+	if (si->pdata.fimc_bus_type == FIMC_BUS_TYPE_ISP_WRITEBACK)
+		subdev->grp_id = GRP_ID_FIMC_IS_SENSOR;
+	else
+		subdev->grp_id = GRP_ID_SENSOR;
+
+	si->subdev = subdev;
+
+	v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice: %s (%d)\n",
+		  subdev->name, fmd->num_sensors);
+
+	fmd->num_sensors++;
+
+	return 0;
+}
+
+static int subdev_notifier_complete(struct v4l2_async_notifier *notifier)
+{
+	struct fimc_md *fmd = notifier_to_fimc_md(notifier);
+	int ret;
+
+	mutex_lock(&fmd->media_dev.graph_mutex);
+
+	ret = fimc_md_create_links(fmd);
+	if (ret < 0)
+		goto unlock;
+
+	ret = v4l2_device_register_subdev_nodes(&fmd->v4l2_dev);
+unlock:
+	mutex_unlock(&fmd->media_dev.graph_mutex);
+	return ret;
+}
+
 static int fimc_md_probe(struct platform_device *pdev)
 static int fimc_md_probe(struct platform_device *pdev)
 {
 {
 	struct device *dev = &pdev->dev;
 	struct device *dev = &pdev->dev;
@@ -1470,63 +1605,91 @@ static int fimc_md_probe(struct platform_device *pdev)
 		v4l2_err(v4l2_dev, "Failed to register v4l2_device: %d\n", ret);
 		v4l2_err(v4l2_dev, "Failed to register v4l2_device: %d\n", ret);
 		return ret;
 		return ret;
 	}
 	}
+
 	ret = media_device_register(&fmd->media_dev);
 	ret = media_device_register(&fmd->media_dev);
 	if (ret < 0) {
 	if (ret < 0) {
 		v4l2_err(v4l2_dev, "Failed to register media device: %d\n", ret);
 		v4l2_err(v4l2_dev, "Failed to register media device: %d\n", ret);
-		goto err_md;
+		goto err_v4l2_dev;
 	}
 	}
+
 	ret = fimc_md_get_clocks(fmd);
 	ret = fimc_md_get_clocks(fmd);
 	if (ret)
 	if (ret)
-		goto err_clk;
+		goto err_md;
 
 
 	fmd->user_subdev_api = (dev->of_node != NULL);
 	fmd->user_subdev_api = (dev->of_node != NULL);
 
 
-	/* Protect the media graph while we're registering entities */
-	mutex_lock(&fmd->media_dev.graph_mutex);
-
 	ret = fimc_md_get_pinctrl(fmd);
 	ret = fimc_md_get_pinctrl(fmd);
 	if (ret < 0) {
 	if (ret < 0) {
 		if (ret != EPROBE_DEFER)
 		if (ret != EPROBE_DEFER)
 			dev_err(dev, "Failed to get pinctrl: %d\n", ret);
 			dev_err(dev, "Failed to get pinctrl: %d\n", ret);
-		goto err_unlock;
+		goto err_clk;
 	}
 	}
 
 
+	platform_set_drvdata(pdev, fmd);
+
+	/* Protect the media graph while we're registering entities */
+	mutex_lock(&fmd->media_dev.graph_mutex);
+
 	if (dev->of_node)
 	if (dev->of_node)
 		ret = fimc_md_register_of_platform_entities(fmd, dev->of_node);
 		ret = fimc_md_register_of_platform_entities(fmd, dev->of_node);
 	else
 	else
 		ret = bus_for_each_dev(&platform_bus_type, NULL, fmd,
 		ret = bus_for_each_dev(&platform_bus_type, NULL, fmd,
 						fimc_md_pdev_match);
 						fimc_md_pdev_match);
-	if (ret)
-		goto err_unlock;
+	if (ret) {
+		mutex_unlock(&fmd->media_dev.graph_mutex);
+		goto err_clk;
+	}
 
 
 	if (dev->platform_data || dev->of_node) {
 	if (dev->platform_data || dev->of_node) {
 		ret = fimc_md_register_sensor_entities(fmd);
 		ret = fimc_md_register_sensor_entities(fmd);
-		if (ret)
-			goto err_unlock;
+		if (ret) {
+			mutex_unlock(&fmd->media_dev.graph_mutex);
+			goto err_m_ent;
+		}
 	}
 	}
 
 
-	ret = fimc_md_create_links(fmd);
-	if (ret)
-		goto err_unlock;
-	ret = v4l2_device_register_subdev_nodes(&fmd->v4l2_dev);
-	if (ret)
-		goto err_unlock;
+	mutex_unlock(&fmd->media_dev.graph_mutex);
 
 
 	ret = device_create_file(&pdev->dev, &dev_attr_subdev_conf_mode);
 	ret = device_create_file(&pdev->dev, &dev_attr_subdev_conf_mode);
 	if (ret)
 	if (ret)
-		goto err_unlock;
+		goto err_m_ent;
+	/*
+	 * FIMC platform devices need to be registered before the sclk_cam
+	 * clocks provider, as one of these devices needs to be activated
+	 * to enable the clock.
+	 */
+	ret = fimc_md_register_clk_provider(fmd);
+	if (ret < 0) {
+		v4l2_err(v4l2_dev, "clock provider registration failed\n");
+		goto err_attr;
+	}
+
+	if (fmd->num_sensors > 0) {
+		fmd->subdev_notifier.subdevs = fmd->async_subdevs;
+		fmd->subdev_notifier.num_subdevs = fmd->num_sensors;
+		fmd->subdev_notifier.bound = subdev_notifier_bound;
+		fmd->subdev_notifier.complete = subdev_notifier_complete;
+		fmd->num_sensors = 0;
+
+		ret = v4l2_async_notifier_register(&fmd->v4l2_dev,
+						&fmd->subdev_notifier);
+		if (ret)
+			goto err_clk_p;
+	}
 
 
-	platform_set_drvdata(pdev, fmd);
-	mutex_unlock(&fmd->media_dev.graph_mutex);
 	return 0;
 	return 0;
 
 
-err_unlock:
-	mutex_unlock(&fmd->media_dev.graph_mutex);
+err_clk_p:
+	fimc_md_unregister_clk_provider(fmd);
+err_attr:
+	device_remove_file(&pdev->dev, &dev_attr_subdev_conf_mode);
 err_clk:
 err_clk:
 	fimc_md_put_clocks(fmd);
 	fimc_md_put_clocks(fmd);
+err_m_ent:
 	fimc_md_unregister_entities(fmd);
 	fimc_md_unregister_entities(fmd);
-	media_device_unregister(&fmd->media_dev);
 err_md:
 err_md:
+	media_device_unregister(&fmd->media_dev);
+err_v4l2_dev:
 	v4l2_device_unregister(&fmd->v4l2_dev);
 	v4l2_device_unregister(&fmd->v4l2_dev);
 	return ret;
 	return ret;
 }
 }
@@ -1538,12 +1701,16 @@ static int fimc_md_remove(struct platform_device *pdev)
 	if (!fmd)
 	if (!fmd)
 		return 0;
 		return 0;
 
 
+	fimc_md_unregister_clk_provider(fmd);
+	v4l2_async_notifier_unregister(&fmd->subdev_notifier);
+
 	v4l2_device_unregister(&fmd->v4l2_dev);
 	v4l2_device_unregister(&fmd->v4l2_dev);
 	device_remove_file(&pdev->dev, &dev_attr_subdev_conf_mode);
 	device_remove_file(&pdev->dev, &dev_attr_subdev_conf_mode);
 	fimc_md_unregister_entities(fmd);
 	fimc_md_unregister_entities(fmd);
 	fimc_md_pipelines_free(fmd);
 	fimc_md_pipelines_free(fmd);
 	media_device_unregister(&fmd->media_dev);
 	media_device_unregister(&fmd->media_dev);
 	fimc_md_put_clocks(fmd);
 	fimc_md_put_clocks(fmd);
+
 	return 0;
 	return 0;
 }
 }
 
 

+ 30 - 2
drivers/media/platform/exynos4-is/media-dev.h

@@ -10,6 +10,7 @@
 #define FIMC_MDEVICE_H_
 #define FIMC_MDEVICE_H_
 
 
 #include <linux/clk.h>
 #include <linux/clk.h>
+#include <linux/clk-provider.h>
 #include <linux/platform_device.h>
 #include <linux/platform_device.h>
 #include <linux/mutex.h>
 #include <linux/mutex.h>
 #include <linux/of.h>
 #include <linux/of.h>
@@ -31,8 +32,9 @@
 
 
 #define PINCTRL_STATE_IDLE	"idle"
 #define PINCTRL_STATE_IDLE	"idle"
 
 
-#define FIMC_MAX_SENSORS	8
+#define FIMC_MAX_SENSORS	4
 #define FIMC_MAX_CAMCLKS	2
 #define FIMC_MAX_CAMCLKS	2
+#define DEFAULT_SENSOR_CLK_FREQ	24000000U
 
 
 /* LCD/ISP Writeback clocks (PIXELASYNCMx) */
 /* LCD/ISP Writeback clocks (PIXELASYNCMx) */
 enum {
 enum {
@@ -78,6 +80,7 @@ struct fimc_camclk_info {
 /**
 /**
  * struct fimc_sensor_info - image data source subdev information
  * struct fimc_sensor_info - image data source subdev information
  * @pdata: sensor's atrributes passed as media device's platform data
  * @pdata: sensor's atrributes passed as media device's platform data
+ * @asd: asynchronous subdev registration data structure
  * @subdev: image sensor v4l2 subdev
  * @subdev: image sensor v4l2 subdev
  * @host: fimc device the sensor is currently linked to
  * @host: fimc device the sensor is currently linked to
  *
  *
@@ -85,10 +88,17 @@ struct fimc_camclk_info {
  */
  */
 struct fimc_sensor_info {
 struct fimc_sensor_info {
 	struct fimc_source_info pdata;
 	struct fimc_source_info pdata;
+	struct v4l2_async_subdev asd;
 	struct v4l2_subdev *subdev;
 	struct v4l2_subdev *subdev;
 	struct fimc_dev *host;
 	struct fimc_dev *host;
 };
 };
 
 
+struct cam_clk {
+	struct clk_hw hw;
+	struct fimc_md *fmd;
+};
+#define to_cam_clk(_hw) container_of(_hw, struct cam_clk, hw)
+
 /**
 /**
  * struct fimc_md - fimc media device information
  * struct fimc_md - fimc media device information
  * @csis: MIPI CSIS subdevs data
  * @csis: MIPI CSIS subdevs data
@@ -105,6 +115,7 @@ struct fimc_sensor_info {
  * @pinctrl: camera port pinctrl handle
  * @pinctrl: camera port pinctrl handle
  * @state_default: pinctrl default state handle
  * @state_default: pinctrl default state handle
  * @state_idle: pinctrl idle state handle
  * @state_idle: pinctrl idle state handle
+ * @cam_clk_provider: CAMCLK clock provider structure
  * @user_subdev_api: true if subdevs are not configured by the host driver
  * @user_subdev_api: true if subdevs are not configured by the host driver
  * @slock: spinlock protecting @sensor array
  * @slock: spinlock protecting @sensor array
  */
  */
@@ -122,13 +133,25 @@ struct fimc_md {
 	struct media_device media_dev;
 	struct media_device media_dev;
 	struct v4l2_device v4l2_dev;
 	struct v4l2_device v4l2_dev;
 	struct platform_device *pdev;
 	struct platform_device *pdev;
+
 	struct fimc_pinctrl {
 	struct fimc_pinctrl {
 		struct pinctrl *pinctrl;
 		struct pinctrl *pinctrl;
 		struct pinctrl_state *state_default;
 		struct pinctrl_state *state_default;
 		struct pinctrl_state *state_idle;
 		struct pinctrl_state *state_idle;
 	} pinctl;
 	} pinctl;
-	bool user_subdev_api;
 
 
+	struct cam_clk_provider {
+		struct clk *clks[FIMC_MAX_CAMCLKS];
+		struct clk_onecell_data clk_data;
+		struct device_node *of_node;
+		struct cam_clk camclk[FIMC_MAX_CAMCLKS];
+		int num_clocks;
+	} clk_provider;
+
+	struct v4l2_async_notifier subdev_notifier;
+	struct v4l2_async_subdev *async_subdevs[FIMC_MAX_SENSORS];
+
+	bool user_subdev_api;
 	spinlock_t slock;
 	spinlock_t slock;
 	struct list_head pipelines;
 	struct list_head pipelines;
 };
 };
@@ -145,6 +168,11 @@ static inline struct fimc_md *entity_to_fimc_mdev(struct media_entity *me)
 		container_of(me->parent, struct fimc_md, media_dev);
 		container_of(me->parent, struct fimc_md, media_dev);
 }
 }
 
 
+static inline struct fimc_md *notifier_to_fimc_md(struct v4l2_async_notifier *n)
+{
+	return container_of(n, struct fimc_md, subdev_notifier);
+}
+
 static inline void fimc_md_graph_lock(struct exynos_video_entity *ve)
 static inline void fimc_md_graph_lock(struct exynos_video_entity *ve)
 {
 {
 	mutex_lock(&ve->vdev.entity.parent->graph_mutex);
 	mutex_lock(&ve->vdev.entity.parent->graph_mutex);

+ 12 - 12
drivers/media/platform/s5p-jpeg/jpeg-regs.h

@@ -210,19 +210,19 @@
 
 
 /* JPEG CNTL Register bit */
 /* JPEG CNTL Register bit */
 #define EXYNOS4_ENC_DEC_MODE_MASK	(0xfffffffc << 0)
 #define EXYNOS4_ENC_DEC_MODE_MASK	(0xfffffffc << 0)
-#define EXYNOS4_DEC_MODE			(1 << 0)
-#define EXYNOS4_ENC_MODE			(1 << 1)
+#define EXYNOS4_DEC_MODE		(1 << 0)
+#define EXYNOS4_ENC_MODE		(1 << 1)
 #define EXYNOS4_AUTO_RST_MARKER		(1 << 2)
 #define EXYNOS4_AUTO_RST_MARKER		(1 << 2)
 #define EXYNOS4_RST_INTERVAL_SHIFT	3
 #define EXYNOS4_RST_INTERVAL_SHIFT	3
 #define EXYNOS4_RST_INTERVAL(x)		(((x) & 0xffff) \
 #define EXYNOS4_RST_INTERVAL(x)		(((x) & 0xffff) \
 						<< EXYNOS4_RST_INTERVAL_SHIFT)
 						<< EXYNOS4_RST_INTERVAL_SHIFT)
 #define EXYNOS4_HUF_TBL_EN		(1 << 19)
 #define EXYNOS4_HUF_TBL_EN		(1 << 19)
 #define EXYNOS4_HOR_SCALING_SHIFT	20
 #define EXYNOS4_HOR_SCALING_SHIFT	20
-#define EXYNOS4_HOR_SCALING_MASK		(3 << EXYNOS4_HOR_SCALING_SHIFT)
+#define EXYNOS4_HOR_SCALING_MASK	(3 << EXYNOS4_HOR_SCALING_SHIFT)
 #define EXYNOS4_HOR_SCALING(x)		(((x) & 0x3) \
 #define EXYNOS4_HOR_SCALING(x)		(((x) & 0x3) \
 						<< EXYNOS4_HOR_SCALING_SHIFT)
 						<< EXYNOS4_HOR_SCALING_SHIFT)
 #define EXYNOS4_VER_SCALING_SHIFT	22
 #define EXYNOS4_VER_SCALING_SHIFT	22
-#define EXYNOS4_VER_SCALING_MASK		(3 << EXYNOS4_VER_SCALING_SHIFT)
+#define EXYNOS4_VER_SCALING_MASK	(3 << EXYNOS4_VER_SCALING_SHIFT)
 #define EXYNOS4_VER_SCALING(x)		(((x) & 0x3) \
 #define EXYNOS4_VER_SCALING(x)		(((x) & 0x3) \
 						<< EXYNOS4_VER_SCALING_SHIFT)
 						<< EXYNOS4_VER_SCALING_SHIFT)
 #define EXYNOS4_PADDING			(1 << 27)
 #define EXYNOS4_PADDING			(1 << 27)
@@ -238,8 +238,8 @@
 #define EXYNOS4_FRAME_ERR_EN		(1 << 4)
 #define EXYNOS4_FRAME_ERR_EN		(1 << 4)
 #define EXYNOS4_INT_EN_ALL		(0x1f << 0)
 #define EXYNOS4_INT_EN_ALL		(0x1f << 0)
 
 
-#define EXYNOS4_MOD_REG_PROC_ENC		(0 << 3)
-#define EXYNOS4_MOD_REG_PROC_DEC		(1 << 3)
+#define EXYNOS4_MOD_REG_PROC_ENC	(0 << 3)
+#define EXYNOS4_MOD_REG_PROC_DEC	(1 << 3)
 
 
 #define EXYNOS4_MOD_REG_SUBSAMPLE_444	(0 << 0)
 #define EXYNOS4_MOD_REG_SUBSAMPLE_444	(0 << 0)
 #define EXYNOS4_MOD_REG_SUBSAMPLE_422	(1 << 0)
 #define EXYNOS4_MOD_REG_SUBSAMPLE_422	(1 << 0)
@@ -270,7 +270,7 @@
 #define EXYNOS4_DEC_YUV_420_IMG		(4 << 0)
 #define EXYNOS4_DEC_YUV_420_IMG		(4 << 0)
 
 
 #define EXYNOS4_GRAY_IMG_IP_SHIFT	3
 #define EXYNOS4_GRAY_IMG_IP_SHIFT	3
-#define EXYNOS4_GRAY_IMG_IP_MASK		(7 << EXYNOS4_GRAY_IMG_IP_SHIFT)
+#define EXYNOS4_GRAY_IMG_IP_MASK	(7 << EXYNOS4_GRAY_IMG_IP_SHIFT)
 #define EXYNOS4_GRAY_IMG_IP		(4 << EXYNOS4_GRAY_IMG_IP_SHIFT)
 #define EXYNOS4_GRAY_IMG_IP		(4 << EXYNOS4_GRAY_IMG_IP_SHIFT)
 
 
 #define EXYNOS4_RGB_IP_SHIFT		6
 #define EXYNOS4_RGB_IP_SHIFT		6
@@ -278,18 +278,18 @@
 #define EXYNOS4_RGB_IP_RGB_16BIT_IMG	(4 << EXYNOS4_RGB_IP_SHIFT)
 #define EXYNOS4_RGB_IP_RGB_16BIT_IMG	(4 << EXYNOS4_RGB_IP_SHIFT)
 #define EXYNOS4_RGB_IP_RGB_32BIT_IMG	(5 << EXYNOS4_RGB_IP_SHIFT)
 #define EXYNOS4_RGB_IP_RGB_32BIT_IMG	(5 << EXYNOS4_RGB_IP_SHIFT)
 
 
-#define EXYNOS4_YUV_444_IP_SHIFT			9
+#define EXYNOS4_YUV_444_IP_SHIFT		9
 #define EXYNOS4_YUV_444_IP_MASK			(7 << EXYNOS4_YUV_444_IP_SHIFT)
 #define EXYNOS4_YUV_444_IP_MASK			(7 << EXYNOS4_YUV_444_IP_SHIFT)
 #define EXYNOS4_YUV_444_IP_YUV_444_2P_IMG	(4 << EXYNOS4_YUV_444_IP_SHIFT)
 #define EXYNOS4_YUV_444_IP_YUV_444_2P_IMG	(4 << EXYNOS4_YUV_444_IP_SHIFT)
 #define EXYNOS4_YUV_444_IP_YUV_444_3P_IMG	(5 << EXYNOS4_YUV_444_IP_SHIFT)
 #define EXYNOS4_YUV_444_IP_YUV_444_3P_IMG	(5 << EXYNOS4_YUV_444_IP_SHIFT)
 
 
-#define EXYNOS4_YUV_422_IP_SHIFT			12
+#define EXYNOS4_YUV_422_IP_SHIFT		12
 #define EXYNOS4_YUV_422_IP_MASK			(7 << EXYNOS4_YUV_422_IP_SHIFT)
 #define EXYNOS4_YUV_422_IP_MASK			(7 << EXYNOS4_YUV_422_IP_SHIFT)
 #define EXYNOS4_YUV_422_IP_YUV_422_1P_IMG	(4 << EXYNOS4_YUV_422_IP_SHIFT)
 #define EXYNOS4_YUV_422_IP_YUV_422_1P_IMG	(4 << EXYNOS4_YUV_422_IP_SHIFT)
 #define EXYNOS4_YUV_422_IP_YUV_422_2P_IMG	(5 << EXYNOS4_YUV_422_IP_SHIFT)
 #define EXYNOS4_YUV_422_IP_YUV_422_2P_IMG	(5 << EXYNOS4_YUV_422_IP_SHIFT)
 #define EXYNOS4_YUV_422_IP_YUV_422_3P_IMG	(6 << EXYNOS4_YUV_422_IP_SHIFT)
 #define EXYNOS4_YUV_422_IP_YUV_422_3P_IMG	(6 << EXYNOS4_YUV_422_IP_SHIFT)
 
 
-#define EXYNOS4_YUV_420_IP_SHIFT			15
+#define EXYNOS4_YUV_420_IP_SHIFT		15
 #define EXYNOS4_YUV_420_IP_MASK			(7 << EXYNOS4_YUV_420_IP_SHIFT)
 #define EXYNOS4_YUV_420_IP_MASK			(7 << EXYNOS4_YUV_420_IP_SHIFT)
 #define EXYNOS4_YUV_420_IP_YUV_420_2P_IMG	(4 << EXYNOS4_YUV_420_IP_SHIFT)
 #define EXYNOS4_YUV_420_IP_YUV_420_2P_IMG	(4 << EXYNOS4_YUV_420_IP_SHIFT)
 #define EXYNOS4_YUV_420_IP_YUV_420_3P_IMG	(5 << EXYNOS4_YUV_420_IP_SHIFT)
 #define EXYNOS4_YUV_420_IP_YUV_420_3P_IMG	(5 << EXYNOS4_YUV_420_IP_SHIFT)
@@ -303,8 +303,8 @@
 
 
 #define EXYNOS4_JPEG_DECODED_IMG_FMT_MASK	0x03
 #define EXYNOS4_JPEG_DECODED_IMG_FMT_MASK	0x03
 
 
-#define EXYNOS4_SWAP_CHROMA_CRCB			(1 << 26)
-#define EXYNOS4_SWAP_CHROMA_CBCR			(0 << 26)
+#define EXYNOS4_SWAP_CHROMA_CRCB		(1 << 26)
+#define EXYNOS4_SWAP_CHROMA_CBCR		(0 << 26)
 
 
 /* JPEG HUFF count Register bit */
 /* JPEG HUFF count Register bit */
 #define EXYNOS4_HUFF_COUNT_MASK			0xffff
 #define EXYNOS4_HUFF_COUNT_MASK			0xffff