+From 369b60c157f67a71a6f302ab9843ae2de1805a2a Mon Sep 17 00:00:00 2001
+From: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+Date: Mon, 22 Oct 2012 15:43:00 +0200
+Subject: [PATCH 1/6] omap3isp: Use the common clock framework
+
+Expose the two ISP external clocks XCLKA and XCLKB as common clocks for
+subdev drivers.
+
+Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+Acked-by: Mike Turquette <mturquette@linaro.org>
+---
+ drivers/media/platform/omap3isp/isp.c | 277 ++++++++++++++++++++++++---------
+ drivers/media/platform/omap3isp/isp.h | 22 ++-
+ include/media/omap3isp.h | 10 +-
+ 3 files changed, 225 insertions(+), 84 deletions(-)
+
+diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c
+index 6e5ad8e..1d7dbd5 100644
+--- a/drivers/media/platform/omap3isp/isp.c
++++ b/drivers/media/platform/omap3isp/isp.c
+@@ -55,6 +55,7 @@
+ #include <asm/cacheflush.h>
+
+ #include <linux/clk.h>
++#include <linux/clkdev.h>
+ #include <linux/delay.h>
+ #include <linux/device.h>
+ #include <linux/dma-mapping.h>
+@@ -148,6 +149,201 @@ void omap3isp_flush(struct isp_device *isp)
+ isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION);
+ }
+
++/* -----------------------------------------------------------------------------
++ * XCLK
++ */
++
++#define to_isp_xclk(_hw) container_of(_hw, struct isp_xclk, hw)
++
++static void isp_xclk_update(struct isp_xclk *xclk, u32 divider)
++{
++ switch (xclk->id) {
++ case ISP_XCLK_A:
++ isp_reg_clr_set(xclk->isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL,
++ ISPTCTRL_CTRL_DIVA_MASK,
++ divider << ISPTCTRL_CTRL_DIVA_SHIFT);
++ break;
++ case ISP_XCLK_B:
++ isp_reg_clr_set(xclk->isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL,
++ ISPTCTRL_CTRL_DIVB_MASK,
++ divider << ISPTCTRL_CTRL_DIVB_SHIFT);
++ break;
++ }
++}
++
++static int isp_xclk_prepare(struct clk_hw *hw)
++{
++ struct isp_xclk *xclk = to_isp_xclk(hw);
++
++ omap3isp_get(xclk->isp);
++
++ return 0;
++}
++
++static void isp_xclk_unprepare(struct clk_hw *hw)
++{
++ struct isp_xclk *xclk = to_isp_xclk(hw);
++
++ omap3isp_put(xclk->isp);
++}
++
++static int isp_xclk_enable(struct clk_hw *hw)
++{
++ struct isp_xclk *xclk = to_isp_xclk(hw);
++ unsigned long flags;
++
++ spin_lock_irqsave(&xclk->lock, flags);
++ isp_xclk_update(xclk, xclk->divider);
++ xclk->enabled = true;
++ spin_unlock_irqrestore(&xclk->lock, flags);
++
++ return 0;
++}
++
++static void isp_xclk_disable(struct clk_hw *hw)
++{
++ struct isp_xclk *xclk = to_isp_xclk(hw);
++ unsigned long flags;
++
++ spin_lock_irqsave(&xclk->lock, flags);
++ isp_xclk_update(xclk, 0);
++ xclk->enabled = false;
++ spin_unlock_irqrestore(&xclk->lock, flags);
++}
++
++static unsigned long isp_xclk_recalc_rate(struct clk_hw *hw,
++ unsigned long parent_rate)
++{
++ struct isp_xclk *xclk = to_isp_xclk(hw);
++
++ return parent_rate / xclk->divider;
++}
++
++static u32 isp_xclk_calc_divider(unsigned long *rate, unsigned long parent_rate)
++{
++ u32 divider;
++
++ if (*rate >= parent_rate) {
++ *rate = parent_rate;
++ return ISPTCTRL_CTRL_DIV_BYPASS;
++ }
++
++ divider = DIV_ROUND_CLOSEST(parent_rate, *rate);
++ if (divider >= ISPTCTRL_CTRL_DIV_BYPASS)
++ divider = ISPTCTRL_CTRL_DIV_BYPASS - 1;
++
++ *rate = parent_rate / divider;
++ return divider;
++}
++
++static long isp_xclk_round_rate(struct clk_hw *hw, unsigned long rate,
++ unsigned long *parent_rate)
++{
++ isp_xclk_calc_divider(&rate, *parent_rate);
++ return rate;
++}
++
++static int isp_xclk_set_rate(struct clk_hw *hw, unsigned long rate,
++ unsigned long parent_rate)
++{
++ struct isp_xclk *xclk = to_isp_xclk(hw);
++ unsigned long flags;
++ u32 divider;
++
++ divider = isp_xclk_calc_divider(&rate, parent_rate);
++
++ spin_lock_irqsave(&xclk->lock, flags);
++
++ xclk->divider = divider;
++ if (xclk->enabled)
++ isp_xclk_update(xclk, divider);
++
++ spin_unlock_irqrestore(&xclk->lock, flags);
++
++ dev_dbg(xclk->isp->dev, "%s: cam_xclk%c set to %lu Hz (div %u)\n",
++ __func__, xclk->id == ISP_XCLK_A ? 'a' : 'b', rate, divider);
++ return 0;
++}
++
++static const struct clk_ops isp_xclk_ops = {
++ .prepare = isp_xclk_prepare,
++ .unprepare = isp_xclk_unprepare,
++ .enable = isp_xclk_enable,
++ .disable = isp_xclk_disable,
++ .recalc_rate = isp_xclk_recalc_rate,
++ .round_rate = isp_xclk_round_rate,
++ .set_rate = isp_xclk_set_rate,
++};
++
++static const char *isp_xclk_parent_name = "cam_mclk";
++
++static const struct clk_init_data isp_xclk_init_data = {
++ .name = "cam_xclk",
++ .ops = &isp_xclk_ops,
++ .parent_names = &isp_xclk_parent_name,
++ .num_parents = 1,
++};
++
++static int isp_xclk_init(struct isp_device *isp)
++{
++ struct isp_platform_data *pdata = isp->pdata;
++ struct clk_init_data init;
++ unsigned int i;
++
++ for (i = 0; i < ARRAY_SIZE(isp->xclks); ++i) {
++ struct isp_xclk *xclk = &isp->xclks[i];
++ struct clk *clk;
++
++ xclk->isp = isp;
++ xclk->id = i == 0 ? ISP_XCLK_A : ISP_XCLK_B;
++ xclk->divider = 1;
++ spin_lock_init(&xclk->lock);
++
++ init.name = i == 0 ? "cam_xclka" : "cam_xclkb";
++ init.ops = &isp_xclk_ops;
++ init.parent_names = &isp_xclk_parent_name;
++ init.num_parents = 1;
++
++ xclk->hw.init = &init;
++
++ clk = devm_clk_register(isp->dev, &xclk->hw);
++ if (IS_ERR(clk))
++ return PTR_ERR(clk);
++
++ if (pdata->xclks[i].con_id == NULL &&
++ pdata->xclks[i].dev_id == NULL)
++ continue;
++
++ xclk->lookup = kzalloc(sizeof(*xclk->lookup), GFP_KERNEL);
++ if (xclk->lookup == NULL)
++ return -ENOMEM;
++
++ xclk->lookup->con_id = pdata->xclks[i].con_id;
++ xclk->lookup->dev_id = pdata->xclks[i].dev_id;
++ xclk->lookup->clk = clk;
++
++ clkdev_add(xclk->lookup);
++ }
++
++ return 0;
++}
++
++static void isp_xclk_cleanup(struct isp_device *isp)
++{
++ unsigned int i;
++
++ for (i = 0; i < ARRAY_SIZE(isp->xclks); ++i) {
++ struct isp_xclk *xclk = &isp->xclks[i];
++
++ if (xclk->lookup)
++ clkdev_drop(xclk->lookup);
++ }
++}
++
++/* -----------------------------------------------------------------------------
++ * Interrupts
++ */
++
+ /*
+ * isp_enable_interrupts - Enable ISP interrupts.
+ * @isp: OMAP3 ISP device
+@@ -180,80 +376,6 @@ static void isp_disable_interrupts(struct isp_device *isp)
+ isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE);
+ }
+
+-/**
+- * isp_set_xclk - Configures the specified cam_xclk to the desired frequency.
+- * @isp: OMAP3 ISP device
+- * @xclk: Desired frequency of the clock in Hz. 0 = stable low, 1 is stable high
+- * @xclksel: XCLK to configure (0 = A, 1 = B).
+- *
+- * Configures the specified MCLK divisor in the ISP timing control register
+- * (TCTRL_CTRL) to generate the desired xclk clock value.
+- *
+- * Divisor = cam_mclk_hz / xclk
+- *
+- * Returns the final frequency that is actually being generated
+- **/
+-static u32 isp_set_xclk(struct isp_device *isp, u32 xclk, u8 xclksel)
+-{
+- u32 divisor;
+- u32 currentxclk;
+- unsigned long mclk_hz;
+-
+- if (!omap3isp_get(isp))
+- return 0;
+-
+- mclk_hz = clk_get_rate(isp->clock[ISP_CLK_CAM_MCLK]);
+-
+- if (xclk >= mclk_hz) {
+- divisor = ISPTCTRL_CTRL_DIV_BYPASS;
+- currentxclk = mclk_hz;
+- } else if (xclk >= 2) {
+- divisor = mclk_hz / xclk;
+- if (divisor >= ISPTCTRL_CTRL_DIV_BYPASS)
+- divisor = ISPTCTRL_CTRL_DIV_BYPASS - 1;
+- currentxclk = mclk_hz / divisor;
+- } else {
+- divisor = xclk;
+- currentxclk = 0;
+- }
+-
+- switch (xclksel) {
+- case ISP_XCLK_A:
+- isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL,
+- ISPTCTRL_CTRL_DIVA_MASK,
+- divisor << ISPTCTRL_CTRL_DIVA_SHIFT);
+- dev_dbg(isp->dev, "isp_set_xclk(): cam_xclka set to %d Hz\n",
+- currentxclk);
+- break;
+- case ISP_XCLK_B:
+- isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL,
+- ISPTCTRL_CTRL_DIVB_MASK,
+- divisor << ISPTCTRL_CTRL_DIVB_SHIFT);
+- dev_dbg(isp->dev, "isp_set_xclk(): cam_xclkb set to %d Hz\n",
+- currentxclk);
+- break;
+- case ISP_XCLK_NONE:
+- default:
+- omap3isp_put(isp);
+- dev_dbg(isp->dev, "ISP_ERR: isp_set_xclk(): Invalid requested "
+- "xclk. Must be 0 (A) or 1 (B).\n");
+- return -EINVAL;
+- }
+-
+- /* Do we go from stable whatever to clock? */
+- if (divisor >= 2 && isp->xclk_divisor[xclksel - 1] < 2)
+- omap3isp_get(isp);
+- /* Stopping the clock. */
+- else if (divisor < 2 && isp->xclk_divisor[xclksel - 1] >= 2)
+- omap3isp_put(isp);
+-
+- isp->xclk_divisor[xclksel - 1] = divisor;
+-
+- omap3isp_put(isp);
+-
+- return currentxclk;
+-}
+-
+ /*
+ * isp_core_init - ISP core settings
+ * @isp: OMAP3 ISP device
+@@ -1969,6 +2091,7 @@ static int isp_remove(struct platform_device *pdev)
+
+ isp_unregister_entities(isp);
+ isp_cleanup_modules(isp);
++ isp_xclk_cleanup(isp);
+
+ __omap3isp_get(isp, false);
+ iommu_detach_device(isp->domain, &pdev->dev);
+@@ -2042,7 +2165,6 @@ static int isp_probe(struct platform_device *pdev)
+ }
+
+ isp->autoidle = autoidle;
+- isp->platform_cb.set_xclk = isp_set_xclk;
+
+ mutex_init(&isp->isp_mutex);
+ spin_lock_init(&isp->stat_lock);
+@@ -2093,6 +2215,10 @@ static int isp_probe(struct platform_device *pdev)
+ if (ret < 0)
+ goto error_isp;
+
++ ret = isp_xclk_init(isp);
++ if (ret < 0)
++ goto error_isp;
++
+ /* Memory resources */
+ for (m = 0; m < ARRAY_SIZE(isp_res_maps); m++)
+ if (isp->revision == isp_res_maps[m].isp_rev)
+@@ -2162,6 +2288,7 @@ detach_dev:
+ free_domain:
+ iommu_domain_free(isp->domain);
+ error_isp:
++ isp_xclk_cleanup(isp);
+ omap3isp_put(isp);
+ error:
+ platform_set_drvdata(pdev, NULL);
+diff --git a/drivers/media/platform/omap3isp/isp.h b/drivers/media/platform/omap3isp/isp.h
+index c77e1f2..cd3eff4 100644
+--- a/drivers/media/platform/omap3isp/isp.h
++++ b/drivers/media/platform/omap3isp/isp.h
+@@ -29,6 +29,7 @@
+
+ #include <media/omap3isp.h>
+ #include <media/v4l2-device.h>
++#include <linux/clk-provider.h>
+ #include <linux/device.h>
+ #include <linux/io.h>
+ #include <linux/iommu.h>
+@@ -125,8 +126,20 @@ struct isp_reg {
+ u32 val;
+ };
+
+-struct isp_platform_callback {
+- u32 (*set_xclk)(struct isp_device *isp, u32 xclk, u8 xclksel);
++enum isp_xclk_id {
++ ISP_XCLK_A,
++ ISP_XCLK_B,
++};
++
++struct isp_xclk {
++ struct isp_device *isp;
++ struct clk_hw hw;
++ struct clk_lookup *lookup;
++ enum isp_xclk_id id;
++
++ spinlock_t lock; /* Protects enabled and divider */
++ bool enabled;
++ unsigned int divider;
+ };
+
+ /*
+@@ -149,6 +162,7 @@ struct isp_platform_callback {
+ * @cam_mclk: Pointer to camera functional clock structure.
+ * @csi2_fck: Pointer to camera CSI2 complexIO clock structure.
+ * @l3_ick: Pointer to OMAP3 L3 bus interface clock.
++ * @xclks: External clocks provided by the ISP
+ * @irq: Currently attached ISP ISR callbacks information structure.
+ * @isp_af: Pointer to current settings for ISP AutoFocus SCM.
+ * @isp_hist: Pointer to current settings for ISP Histogram SCM.
+@@ -185,12 +199,12 @@ struct isp_device {
+ int has_context;
+ int ref_count;
+ unsigned int autoidle;
+- u32 xclk_divisor[2]; /* Two clocks, a and b. */
+ #define ISP_CLK_CAM_ICK 0
+ #define ISP_CLK_CAM_MCLK 1
+ #define ISP_CLK_CSI2_FCK 2
+ #define ISP_CLK_L3_ICK 3
+ struct clk *clock[4];
++ struct isp_xclk xclks[2];
+
+ /* ISP modules */
+ struct ispstat isp_af;
+@@ -209,8 +223,6 @@ struct isp_device {
+ unsigned int subclk_resources;
+
+ struct iommu_domain *domain;
+-
+- struct isp_platform_callback platform_cb;
+ };
+
+ #define v4l2_dev_to_isp_device(dev) \
+diff --git a/include/media/omap3isp.h b/include/media/omap3isp.h
+index 9584269..c9d06d9 100644
+--- a/include/media/omap3isp.h
++++ b/include/media/omap3isp.h
+@@ -29,10 +29,6 @@
+ struct i2c_board_info;
+ struct isp_device;
+
+-#define ISP_XCLK_NONE 0
+-#define ISP_XCLK_A 1
+-#define ISP_XCLK_B 2
+-
+ enum isp_interface_type {
+ ISP_INTERFACE_PARALLEL,
+ ISP_INTERFACE_CSI2A_PHY2,
+@@ -153,7 +149,13 @@ struct isp_v4l2_subdevs_group {
+ } bus; /* gcc < 4.6.0 chokes on anonymous union initializers */
+ };
+
++struct isp_platform_xclk {
++ const char *dev_id;
++ const char *con_id;
++};
++
+ struct isp_platform_data {
++ struct isp_platform_xclk xclks[2];
+ struct isp_v4l2_subdevs_group *subdevs;
+ void (*set_constraints)(struct isp_device *isp, bool enable);
+ };
+--
+1.7.10.4
+