[media] exynos4-is: Add the FIMC-IS ISP capture DMA driver
[deliverable/linux.git] / drivers / media / platform / exynos4-is / media-dev.c
index c1bce170df6fbce44d519a498bf6d19694389306..9bec34c4ac94a2bfe7c879582d8d1718562a50a0 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>
@@ -24,6 +26,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>
@@ -218,6 +221,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;
@@ -378,77 +382,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,
@@ -457,7 +402,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;
@@ -485,6 +429,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",
@@ -524,10 +470,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 */
@@ -731,8 +684,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,
@@ -883,11 +844,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)
@@ -1004,16 +967,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);
@@ -1021,7 +985,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);
 }
 
 /**
@@ -1222,6 +1194,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;
 
@@ -1276,6 +1256,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);
 }
 
@@ -1437,6 +1425,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;
@@ -1469,63 +1604,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;
 }
@@ -1537,12 +1700,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;
 }
 
This page took 0.037614 seconds and 5 git commands to generate.