Merge branch 'topic/exynos' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab...
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 5 Apr 2014 20:10:00 +0000 (13:10 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 5 Apr 2014 20:10:00 +0000 (13:10 -0700)
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

27 files changed:
Documentation/devicetree/bindings/media/samsung-fimc.txt
Documentation/devicetree/bindings/media/samsung-s5c73m3.txt [new file with mode: 0644]
Documentation/devicetree/bindings/media/samsung-s5k6a3.txt [new file with mode: 0644]
Documentation/video4linux/fimc.txt
drivers/media/i2c/Kconfig
drivers/media/i2c/Makefile
drivers/media/i2c/s5c73m3/s5c73m3-core.c
drivers/media/i2c/s5c73m3/s5c73m3-spi.c
drivers/media/i2c/s5c73m3/s5c73m3.h
drivers/media/i2c/s5k6a3.c [new file with mode: 0644]
drivers/media/platform/exynos4-is/Kconfig
drivers/media/platform/exynos4-is/Makefile
drivers/media/platform/exynos4-is/fimc-is-param.c
drivers/media/platform/exynos4-is/fimc-is-param.h
drivers/media/platform/exynos4-is/fimc-is-regs.c
drivers/media/platform/exynos4-is/fimc-is-regs.h
drivers/media/platform/exynos4-is/fimc-is-sensor.c
drivers/media/platform/exynos4-is/fimc-is-sensor.h
drivers/media/platform/exynos4-is/fimc-is.c
drivers/media/platform/exynos4-is/fimc-is.h
drivers/media/platform/exynos4-is/fimc-isp-video.c [new file with mode: 0644]
drivers/media/platform/exynos4-is/fimc-isp-video.h [new file with mode: 0644]
drivers/media/platform/exynos4-is/fimc-isp.c
drivers/media/platform/exynos4-is/fimc-isp.h
drivers/media/platform/exynos4-is/media-dev.c
drivers/media/platform/exynos4-is/media-dev.h
drivers/media/platform/s5p-jpeg/jpeg-regs.h

index 96312f6..922d6f8 100644 (file)
@@ -15,11 +15,21 @@ Common 'camera' node
 
 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
 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.
 
+
 'fimc' device nodes
 -------------------
 
@@ -88,8 +99,8 @@ port nodes specifies data input - 0, 1 indicates input A, B respectively.
 
 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
 ------------------
@@ -97,8 +108,6 @@ Image sensor nodes
 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,
 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:
 
@@ -114,7 +123,7 @@ Example:
                        vddio-supply = <...>;
 
                        clock-frequency = <24000000>;
-                       clocks = <...>;
+                       clocks = <&camera 1>;
                        clock-names = "mclk";
 
                        port {
@@ -135,7 +144,7 @@ Example:
                        vddio-supply = <...>;
 
                        clock-frequency = <24000000>;
-                       clocks = <...>;
+                       clocks = <&camera 0>;
                        clock-names = "mclk";
 
                        port {
@@ -149,12 +158,17 @@ Example:
 
        camera {
                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-0 = <&cam_port_a_clk_active>;
+               status = "okay";
+               #address-cells = <1>;
+               #size-cells = <1>;
 
                /* parallel camera ports */
                parallel-ports {
diff --git a/Documentation/devicetree/bindings/media/samsung-s5c73m3.txt b/Documentation/devicetree/bindings/media/samsung-s5c73m3.txt
new file mode 100644 (file)
index 0000000..2c85c45
--- /dev/null
@@ -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>;
+               ...
+       };
+};
diff --git a/Documentation/devicetree/bindings/media/samsung-s5k6a3.txt b/Documentation/devicetree/bindings/media/samsung-s5k6a3.txt
new file mode 100644 (file)
index 0000000..cce01e8
--- /dev/null
@@ -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.
index e51f1b5..7d6e160 100644 (file)
@@ -151,9 +151,8 @@ CONFIG_S5P_DEV_FIMC1  \
 CONFIG_S5P_DEV_FIMC2  |    optional
 CONFIG_S5P_DEV_FIMC3  |
 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
 in addition to a "s5p-fimc-md" platform device to which the media device driver
index c930be3..441053b 100644 (file)
@@ -579,6 +579,14 @@ config VIDEO_S5K6AA
          This is a V4L2 sensor-level driver for Samsung S5K6AA(FX) 1.3M
          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
         tristate "Samsung S5K4ECGX sensor support"
         depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
index 01b6bfc..01ae932 100644 (file)
@@ -66,6 +66,7 @@ obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o
 obj-$(CONFIG_VIDEO_SR030PC30)  += sr030pc30.o
 obj-$(CONFIG_VIDEO_NOON010PC30)        += noon010pc30.o
 obj-$(CONFIG_VIDEO_S5K6AA)     += s5k6aa.o
+obj-$(CONFIG_VIDEO_S5K6A3)     += s5k6a3.o
 obj-$(CONFIG_VIDEO_S5K4ECGX)   += s5k4ecgx.o
 obj-$(CONFIG_VIDEO_S5K5BAF)    += s5k5baf.o
 obj-$(CONFIG_VIDEO_S5C73M3)    += s5c73m3/
index e7f555c..a445930 100644 (file)
@@ -15,7 +15,7 @@
  * GNU General Public License for more details.
  */
 
-#include <linux/sizes.h>
+#include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/firmware.h>
 #include <linux/gpio.h>
@@ -23,7 +23,9 @@
 #include <linux/init.h>
 #include <linux/media.h>
 #include <linux/module.h>
+#include <linux/of_gpio.h>
 #include <linux/regulator/consumer.h>
+#include <linux/sizes.h>
 #include <linux/slab.h>
 #include <linux/spi/spi.h>
 #include <linux/videodev2.h>
@@ -33,6 +35,7 @@
 #include <media/v4l2-subdev.h>
 #include <media/v4l2-mediabus.h>
 #include <media/s5c73m3.h>
+#include <media/v4l2-of.h>
 
 #include "s5c73m3.h"
 
@@ -46,6 +49,8 @@ static int update_fw;
 module_param(update_fw, int, 0644);
 
 #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] = {
        "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++) {
                ret = regulator_enable(state->supplies[i].consumer);
                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);
        usleep_range(100, 200);
 
@@ -1365,7 +1381,8 @@ static int __s5c73m3_power_on(struct s5c73m3 *state)
        usleep_range(50, 100);
 
        return 0;
-err:
+
+err_reg_dis:
        for (--i; i >= 0; i--)
                regulator_disable(state->supplies[i].consumer);
        return ret;
@@ -1380,6 +1397,9 @@ static int __s5c73m3_power_off(struct s5c73m3 *state)
 
        if (s5c73m3_gpio_assert(state, STBY))
                usleep_range(100, 200);
+
+       clk_disable_unprepare(state->clock);
+
        state->streaming = 0;
        state->isp_ready = 0;
 
@@ -1388,6 +1408,7 @@ static int __s5c73m3_power_off(struct s5c73m3 *state)
                if (ret)
                        goto err;
        }
+
        return 0;
 err:
        for (++i; i < S5C73M3_MAX_SUPPLIES; i++) {
@@ -1396,6 +1417,8 @@ err:
                        v4l2_err(&state->oif_sd, "Failed to reenable %s: %d\n",
                                 state->supplies[i].supply, r);
        }
+
+       clk_prepare_enable(state->clock);
        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,
                        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;
 }
 
@@ -1519,41 +1531,112 @@ static const struct v4l2_subdev_ops oif_subdev_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;
-       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;
 
-       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;
 }
 
@@ -1561,21 +1644,20 @@ static int s5c73m3_probe(struct i2c_client *client,
                                const struct i2c_device_id *id)
 {
        struct device *dev = &client->dev;
-       const struct s5c73m3_platform_data *pdata = client->dev.platform_data;
        struct v4l2_subdev *sd;
        struct v4l2_subdev *oif_sd;
        struct s5c73m3 *state;
        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);
        if (!state)
                return -ENOMEM;
 
+       state->i2c_client = client;
+       ret = s5c73m3_get_platform_data(state);
+       if (ret < 0)
+               return ret;
+
        mutex_init(&state->lock);
        sd = &state->sensor_sd;
        oif_sd = &state->oif_sd;
@@ -1613,11 +1695,7 @@ static int s5c73m3_probe(struct i2c_client *client,
        if (ret < 0)
                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)
                goto out_err;
 
@@ -1651,9 +1729,29 @@ static int s5c73m3_probe(struct i2c_client *client,
        if (ret < 0)
                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__);
        return 0;
 
+out_err1:
+       s5c73m3_unregister_spi_driver(state);
 out_err:
        media_entity_cleanup(&sd->entity);
        return ret;
@@ -1665,7 +1763,7 @@ static int s5c73m3_remove(struct i2c_client *client)
        struct s5c73m3 *state = oif_sd_to_s5c73m3(oif_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);
        media_entity_cleanup(&oif_sd->entity);
@@ -1684,8 +1782,17 @@ static const struct i2c_device_id 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 = {
        .driver = {
+               .of_match_table = of_match_ptr(s5c73m3_of_match),
                .name   = DRIVER_NAME,
        },
        .probe          = s5c73m3_probe,
index 8079e26..f60b265 100644 (file)
 
 #define S5C73M3_SPI_DRV_NAME "S5C73M3-SPI"
 
+static const struct of_device_id s5c73m3_spi_ids[] = {
+       { .compatible = "samsung,s5c73m3" },
+       { }
+};
+
 enum spi_direction {
        SPI_DIR_RX,
        SPI_DIR_TX
@@ -146,6 +151,7 @@ int s5c73m3_register_spi_driver(struct s5c73m3 *state)
        spidrv->driver.name = S5C73M3_SPI_DRV_NAME;
        spidrv->driver.bus = &spi_bus_type;
        spidrv->driver.owner = THIS_MODULE;
+       spidrv->driver.of_match_table = s5c73m3_spi_ids;
 
        return spi_register_driver(spidrv);
 }
index 9dfa516..9656b67 100644 (file)
@@ -17,6 +17,7 @@
 #ifndef S5C73M3_H_
 #define S5C73M3_H_
 
+#include <linux/clk.h>
 #include <linux/kernel.h>
 #include <linux/regulator/consumer.h>
 #include <media/v4l2-common.h>
@@ -321,6 +322,7 @@ enum s5c73m3_oif_pads {
 
 
 #define S5C73M3_MAX_SUPPLIES                   6
+#define S5C73M3_DEFAULT_MCLK_FREQ              24000000U
 
 struct s5c73m3_ctrls {
        struct v4l2_ctrl_handler handler;
@@ -391,6 +393,8 @@ struct s5c73m3 {
        struct regulator_bulk_data supplies[S5C73M3_MAX_SUPPLIES];
        struct s5c73m3_gpio gpio[GPIO_NUM];
 
+       struct clk *clock;
+
        /* External master clock frequency */
        u32 mclk_frequency;
        /* Video bus type - MIPI-CSI2/parallel */
diff --git a/drivers/media/i2c/s5k6a3.c b/drivers/media/i2c/s5k6a3.c
new file mode 100644 (file)
index 0000000..7bc2271
--- /dev/null
@@ -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");
index 01ed1ec..e1b2ceb 100644 (file)
@@ -64,4 +64,13 @@ config VIDEO_EXYNOS4_FIMC_IS
          To compile this driver as a module, choose M here: the
          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
index c2ff29b..eed1b18 100644 (file)
@@ -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-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_EXYNOS_FIMC_LITE)   += exynos-fimc-lite.o
 obj-$(CONFIG_VIDEO_EXYNOS4_FIMC_IS)    += exynos-fimc-is.o
index 9bf3ddd..bf1465d 100644 (file)
@@ -56,7 +56,7 @@ static void __fimc_is_hw_update_param_sensor_framerate(struct fimc_is *is)
        __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 chain_config *cfg = &is->config[is->config_index];
index f9358c2..8e31f76 100644 (file)
@@ -911,6 +911,10 @@ struct is_region {
        u32 shared[MAX_SHARED_COUNT];
 } __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 {
        u32 sensor_frame_time;
        u32 sensor_exposure_time;
@@ -988,6 +992,7 @@ struct sensor_open_extended {
 struct fimc_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);
 unsigned int __get_pending_param_count(struct fimc_is *is);
 
index 2628733..cfe4406 100644 (file)
@@ -105,6 +105,20 @@ int fimc_is_hw_get_params(struct fimc_is *is, unsigned int num_args)
        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)
 {
        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(is->sensor_index, is, MCUCTL_REG_ISSR(1));
        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)
index 1d9d4ff..141e5dd 100644 (file)
@@ -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);
 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_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_off(struct fimc_is *is);
 int fimc_is_hw_set_param(struct fimc_is *is);
index 6647421..10e82e2 100644 (file)
  * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) 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/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"
 
-#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 = {
        .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",
                .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");
index 6036d49..173ccff 100644 (file)
 #ifndef 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_HEIGHT           1392
 
-#define SENSOR_NUM_SUPPLIES            2
-
 enum fimc_is_sensor_id {
        FIMC_IS_SENSOR_ID_S5K3H2 = 1,
        FIMC_IS_SENSOR_ID_S5K6A3,
@@ -45,45 +34,23 @@ enum fimc_is_sensor_id {
 
 struct sensor_drv_data {
        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
- * @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
  * @i2c_bus: ISP I2C bus index (0...1)
  * @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 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;
        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_ */
index 9bdfa45..128b73b 100644 (file)
@@ -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;
        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;
-       np = of_graph_get_remote_port(np);
-       if (!np)
+
+       node = of_graph_get_remote_port(node);
+       if (!node)
                return -ENXIO;
 
        /* 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)
 {
-       struct device_node *adapter, *child;
-       int ret;
+       struct device_node *i2c_bus, *child;
+       int ret, index = 0;
 
        ret = fimc_isp_subdev_create(&is->isp);
        if (ret < 0)
                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;
-
-e_retry:
-       of_node_put(child);
-       return -EPROBE_DEFER;
 }
 
 static int fimc_is_unregister_subdevs(struct fimc_is *is)
 {
        fimc_isp_subdev_destroy(&is->isp);
-       is->sensor = NULL;
        return 0;
 }
 
@@ -647,7 +638,7 @@ static int fimc_is_hw_open_sensor(struct fimc_is *is,
        fimc_is_hw_set_intgr0_gd0(is);
 
        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;
        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)
                return ret;
 
@@ -977,27 +968,20 @@ static int fimc_is_module_init(void)
 {
        int ret;
 
-       ret = fimc_is_register_sensor_driver();
-       if (ret < 0)
-               return ret;
-
        ret = fimc_is_register_i2c_driver();
        if (ret < 0)
-               goto err_sens;
+               return ret;
 
        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;
 }
 
 static void fimc_is_module_exit(void)
 {
-       fimc_is_unregister_sensor_driver();
        fimc_is_unregister_i2c_driver();
        platform_driver_unregister(&fimc_is_driver);
 }
index 61bb012..e0be691 100644 (file)
@@ -39,7 +39,7 @@
 #define FIMC_IS_FW_LOAD_TIMEOUT                1000 /* ms */
 #define FIMC_IS_POWER_ON_TIMEOUT       1000 /* us */
 
-#define FIMC_IS_SENSOR_NUM             2
+#define FIMC_IS_SENSORS_NUM            2
 
 /* Memory definitions */
 #define FIMC_IS_CPU_MEM_SIZE           (0xa00000)
@@ -253,7 +253,7 @@ struct fimc_is {
        struct firmware                 *f_w;
 
        struct fimc_isp                 isp;
-       struct fimc_is_sensor           *sensor;
+       struct fimc_is_sensor           sensor[FIMC_IS_SENSORS_NUM];
        struct fimc_is_setfile          setfile;
 
        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);
 }
 
+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)
 {
        mb();
diff --git a/drivers/media/platform/exynos4-is/fimc-isp-video.c b/drivers/media/platform/exynos4-is/fimc-isp-video.c
new file mode 100644 (file)
index 0000000..e92b4e1
--- /dev/null
@@ -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);
+}
diff --git a/drivers/media/platform/exynos4-is/fimc-isp-video.h b/drivers/media/platform/exynos4-is/fimc-isp-video.h
new file mode 100644 (file)
index 0000000..98c6626
--- /dev/null
@@ -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__ */
index f3c6136..be62d6b 100644 (file)
@@ -25,6 +25,7 @@
 #include <media/v4l2-device.h>
 
 #include "media-dev.h"
+#include "fimc-isp-video.h"
 #include "fimc-is-command.h"
 #include "fimc-is-param.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));
 
        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);
 }
 
@@ -388,7 +389,33 @@ static int fimc_isp_subdev_open(struct v4l2_subdev *sd,
        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 = {
+       .registered = fimc_isp_subdev_registered,
+       .unregistered = fimc_isp_subdev_unregistered,
        .open = fimc_isp_subdev_open,
 };
 
index 03bf95a..4dc55a1 100644 (file)
@@ -35,17 +35,18 @@ extern int fimc_isp_debug;
 #define FIMC_ISP_SINK_WIDTH_MIN                (16 + 8)
 #define FIMC_ISP_SINK_HEIGHT_MIN       (12 + 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_HEIGHT     12
 
 #define FIMC_ISP_SINK_WIDTH_MAX                (4000 - 16)
 #define FIMC_ISP_SINK_HEIGHT_MAX       (4000 + 12)
 #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_REQ_BUFS_MIN          2
+#define FIMC_ISP_REQ_BUFS_MAX          32
 
 #define FIMC_ISP_SD_PAD_SINK           0
 #define FIMC_ISP_SD_PAD_SRC_FIFO       1
@@ -100,6 +101,16 @@ struct fimc_isp_ctrls {
        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
  * @vdev: video_device structure
@@ -114,18 +125,26 @@ struct fimc_isp_ctrls {
  * @format: current pixel format
  */
 struct fimc_is_video {
-       struct video_device     vdev;
+       struct exynos_video_entity ve;
        enum v4l2_buf_type      type;
        struct media_pad        pad;
        struct list_head        pending_buf_q;
        struct list_head        active_buf_q;
        struct vb2_queue        vb_queue;
-       unsigned int            frame_count;
        unsigned int            reqbufs_count;
+       unsigned int            buf_count;
+       unsigned int            buf_mask;
+       unsigned int            frame_count;
        int                     streaming;
+       struct isp_video_buf    *buffers[FIMC_ISP_MAX_BUFS];
        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
  * @pdev: pointer to FIMC-IS platform device
index 04d6ecd..e62211a 100644 (file)
@@ -11,6 +11,8 @@
  */
 
 #include <linux/bug.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
 #include <linux/device.h>
 #include <linux/errno.h>
 #include <linux/i2c.h>
@@ -25,6 +27,7 @@
 #include <linux/pm_runtime.h>
 #include <linux/types.h>
 #include <linux/slab.h>
+#include <media/v4l2-async.h>
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-of.h>
 #include <media/media-device.h>
@@ -219,6 +222,7 @@ static int __fimc_pipeline_open(struct exynos_media_pipeline *ep,
                if (ret < 0)
                        return ret;
        }
+
        ret = fimc_md_set_camclk(sd, true);
        if (ret < 0)
                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_adapter *adapter;
 
-       if (!client)
+       if (!client || client->dev.of_node)
                return;
 
        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
-/* 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. */
 static int fimc_md_parse_port_node(struct fimc_md *fmd,
                                   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 fimc_source_info *pd;
        struct v4l2_of_endpoint endpoint;
-       int ret;
        u32 val;
 
        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))
                pd->clk_frequency = val;
+       else
+               pd->clk_frequency = DEFAULT_SENSOR_CLK_FREQ;
 
        if (pd->clk_frequency == 0) {
                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
                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 */
@@ -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)
 {
        struct v4l2_subdev *sd = &is->isp.subdev;
+       struct exynos_media_pipeline *ep;
        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);
        if (ret) {
                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);
                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)
@@ -1005,16 +968,17 @@ static int __fimc_md_create_flite_source_links(struct fimc_md *fmd)
 /* Create FIMC-IS links */
 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;
        int i, ret;
 
-       source = &fmd->fimc_is->isp.subdev.entity;
+       source = &isp->subdev.entity;
 
        for (i = 0; i < FIMC_MAX_DEVS; i++) {
                if (fmd->fimc[i] == NULL)
                        continue;
 
-               /* Link from IS-ISP subdev to FIMC */
+               /* Link from FIMC-IS-ISP subdev to FIMC */
                sink = &fmd->fimc[i]->vid_cap.subdev.entity;
                ret = media_entity_create_link(source, FIMC_ISP_SD_PAD_SRC_FIFO,
                                               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;
+       /* 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;
        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)
                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_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);
 }
 
@@ -1438,6 +1426,153 @@ static int fimc_md_get_pinctrl(struct fimc_md *fmd)
        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)
 {
        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);
                return ret;
        }
+
        ret = media_device_register(&fmd->media_dev);
        if (ret < 0) {
                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);
        if (ret)
-               goto err_clk;
+               goto err_md;
 
        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);
        if (ret < 0) {
                if (ret != EPROBE_DEFER)
                        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)
                ret = fimc_md_register_of_platform_entities(fmd, dev->of_node);
        else
                ret = bus_for_each_dev(&platform_bus_type, NULL, fmd,
                                                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) {
                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);
        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;
 
-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:
        fimc_md_put_clocks(fmd);
+err_m_ent:
        fimc_md_unregister_entities(fmd);
-       media_device_unregister(&fmd->media_dev);
 err_md:
+       media_device_unregister(&fmd->media_dev);
+err_v4l2_dev:
        v4l2_device_unregister(&fmd->v4l2_dev);
        return ret;
 }
@@ -1538,12 +1701,16 @@ static int fimc_md_remove(struct platform_device *pdev)
        if (!fmd)
                return 0;
 
+       fimc_md_unregister_clk_provider(fmd);
+       v4l2_async_notifier_unregister(&fmd->subdev_notifier);
+
        v4l2_device_unregister(&fmd->v4l2_dev);
        device_remove_file(&pdev->dev, &dev_attr_subdev_conf_mode);
        fimc_md_unregister_entities(fmd);
        fimc_md_pipelines_free(fmd);
        media_device_unregister(&fmd->media_dev);
        fimc_md_put_clocks(fmd);
+
        return 0;
 }
 
index 62599fd..ee1e251 100644 (file)
@@ -10,6 +10,7 @@
 #define FIMC_MDEVICE_H_
 
 #include <linux/clk.h>
+#include <linux/clk-provider.h>
 #include <linux/platform_device.h>
 #include <linux/mutex.h>
 #include <linux/of.h>
@@ -31,8 +32,9 @@
 
 #define PINCTRL_STATE_IDLE     "idle"
 
-#define FIMC_MAX_SENSORS       8
+#define FIMC_MAX_SENSORS       4
 #define FIMC_MAX_CAMCLKS       2
+#define DEFAULT_SENSOR_CLK_FREQ        24000000U
 
 /* LCD/ISP Writeback clocks (PIXELASYNCMx) */
 enum {
@@ -78,6 +80,7 @@ struct fimc_camclk_info {
 /**
  * struct fimc_sensor_info - image data source subdev information
  * @pdata: sensor's atrributes passed as media device's platform data
+ * @asd: asynchronous subdev registration data structure
  * @subdev: image sensor v4l2 subdev
  * @host: fimc device the sensor is currently linked to
  *
@@ -85,10 +88,17 @@ struct fimc_camclk_info {
  */
 struct fimc_sensor_info {
        struct fimc_source_info pdata;
+       struct v4l2_async_subdev asd;
        struct v4l2_subdev *subdev;
        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
  * @csis: MIPI CSIS subdevs data
@@ -105,6 +115,7 @@ struct fimc_sensor_info {
  * @pinctrl: camera port pinctrl handle
  * @state_default: pinctrl default 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
  * @slock: spinlock protecting @sensor array
  */
@@ -122,13 +133,25 @@ struct fimc_md {
        struct media_device media_dev;
        struct v4l2_device v4l2_dev;
        struct platform_device *pdev;
+
        struct fimc_pinctrl {
                struct pinctrl *pinctrl;
                struct pinctrl_state *state_default;
                struct pinctrl_state *state_idle;
        } 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;
        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);
 }
 
+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)
 {
        mutex_lock(&ve->vdev.entity.parent->graph_mutex);
index 33f2c73..57fb05b 100644 (file)
 
 /* JPEG CNTL Register bit */
 #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_RST_INTERVAL_SHIFT     3
 #define EXYNOS4_RST_INTERVAL(x)                (((x) & 0xffff) \
                                                << EXYNOS4_RST_INTERVAL_SHIFT)
 #define EXYNOS4_HUF_TBL_EN             (1 << 19)
 #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) \
                                                << EXYNOS4_HOR_SCALING_SHIFT)
 #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) \
                                                << EXYNOS4_VER_SCALING_SHIFT)
 #define EXYNOS4_PADDING                        (1 << 27)
 #define EXYNOS4_FRAME_ERR_EN           (1 << 4)
 #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_422  (1 << 0)
 #define EXYNOS4_DEC_YUV_420_IMG                (4 << 0)
 
 #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_RGB_IP_SHIFT           6
 #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_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_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_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_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_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_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_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 */
 #define EXYNOS4_HUFF_COUNT_MASK                        0xffff