[media] exynos4-is: Add clock provider for the SCLK_CAM clock outputs
authorSylwester Nawrocki <s.nawrocki@samsung.com>
Fri, 20 Dec 2013 21:53:53 +0000 (18:53 -0300)
committerMauro Carvalho Chehab <m.chehab@samsung.com>
Fri, 14 Mar 2014 13:34:44 +0000 (10:34 -0300)
This patch adds clock provider so the the SCLK_CAM0/1 output clocks
can be accessed by image sensor devices through the clk API.

Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
drivers/media/platform/exynos4-is/media-dev.c
drivers/media/platform/exynos4-is/media-dev.h

index c1bce170df6fbce44d519a498bf6d19694389306..f047a9f1043c2607e268ca8c1e81c2631dbdf16e 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>
@@ -1276,6 +1278,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 +1447,103 @@ 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 fimc_md_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
@@ -1464,16 +1571,24 @@ static int fimc_md_probe(struct platform_device *pdev)
 
        fmd->use_isp = fimc_md_is_isp_available(dev->of_node);
 
+       ret = fimc_md_register_clk_provider(fmd);
+       if (ret < 0) {
+               v4l2_err(v4l2_dev, "clock provider registration failed\n");
+               return ret;
+       }
+
        ret = v4l2_device_register(dev, &fmd->v4l2_dev);
        if (ret < 0) {
                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;
        }
+
        ret = fimc_md_get_clocks(fmd);
        if (ret)
                goto err_clk;
@@ -1507,6 +1622,7 @@ static int fimc_md_probe(struct platform_device *pdev)
        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;
@@ -1527,6 +1643,7 @@ err_clk:
        media_device_unregister(&fmd->media_dev);
 err_md:
        v4l2_device_unregister(&fmd->v4l2_dev);
+       fimc_md_unregister_clk_provider(fmd);
        return ret;
 }
 
@@ -1537,6 +1654,7 @@ static int fimc_md_remove(struct platform_device *pdev)
        if (!fmd)
                return 0;
 
+       fimc_md_unregister_clk_provider(fmd);
        v4l2_device_unregister(&fmd->v4l2_dev);
        device_remove_file(&pdev->dev, &dev_attr_subdev_conf_mode);
        fimc_md_unregister_entities(fmd);
index 62599fd7756f5ce05c4a4ba23d9beb3f70abea2b..a88cee59fd2fcdafd212178c57ef634aa949bca3 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>
@@ -89,6 +90,12 @@ struct fimc_sensor_info {
        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 +112,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 +130,22 @@ 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;
+
+       bool user_subdev_api;
        spinlock_t slock;
        struct list_head pipelines;
 };