All of lore.kernel.org
 help / color / mirror / Atom feed
From: Mike Turquette <mturquette@linaro.org>
To: Laurent Pinchart <laurent.pinchart@ideasonboard.com>,
	linux-media@vger.kernel.org
Cc: sakari.ailus@iki.fi, Mauro Carvalho Chehab <mchehab@redhat.com>
Subject: Re: [PATCH v2 1/2] omap3isp: Use the common clock framework
Date: Mon, 08 Apr 2013 21:21:24 -0700	[thread overview]
Message-ID: <20130409042124.14359.77204@quantum> (raw)
In-Reply-To: <1365076301-6542-2-git-send-email-laurent.pinchart@ideasonboard.com>

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@ideasonboard.com>

Acked-by: Mike Turquette <mturquette@linaro.org>

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

  reply	other threads:[~2013-04-09  4:21 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-04-04 11:51 [PATCH v2 0/2] OMAP3 ISP common clock framework support Laurent Pinchart
2013-04-04 11:51 ` [PATCH v2 1/2] omap3isp: Use the common clock framework Laurent Pinchart
2013-04-09  4:21   ` Mike Turquette [this message]
2013-04-04 11:51 ` [PATCH v2 2/2] mt9p031: " Laurent Pinchart
2013-04-04 13:10 ` [PATCH v2 0/2] OMAP3 ISP common clock framework support Sakari Ailus

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20130409042124.14359.77204@quantum \
    --to=mturquette@linaro.org \
    --cc=laurent.pinchart@ideasonboard.com \
    --cc=linux-media@vger.kernel.org \
    --cc=mchehab@redhat.com \
    --cc=sakari.ailus@iki.fi \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.