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
next prev parent 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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox