Re: [PATCH v2 1/2] omap3isp: Use the common clock framework

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Quoting Laurent Pinchart (2013-04-04 04:51:40)
> Expose the two ISP external clocks XCLKA and XCLKB as common clocks for
> subdev drivers.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart@xxxxxxxxxxxxxxxx>

Acked-by: Mike Turquette <mturquette@xxxxxxxxxx>

Regards,
Mike

> ---
>  drivers/media/platform/omap3isp/isp.c | 270 ++++++++++++++++++++++++----------
>  drivers/media/platform/omap3isp/isp.h |  22 ++-
>  include/media/omap3isp.h              |  10 +-
>  3 files changed, 218 insertions(+), 84 deletions(-)
> 
> diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c
> index 6e5ad8e..694470d 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,194 @@ 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 int isp_xclk_init(struct isp_device *isp)
> +{
> +       struct isp_platform_data *pdata = isp->pdata;
> +       unsigned int i;
> +
> +       for (i = 0; i < ARRAY_SIZE(isp->xclks); ++i) {
> +               struct isp_xclk *xclk = &isp->xclks[i];
> +               struct clk_init_data init;
> +               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 +369,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 +2084,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 +2158,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 +2208,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 +2281,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.8.1.5
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux