imx-drm: ipu-v3: more clocking fixes
authorRussell King <rmk+kernel@arm.linux.org.uk>
Sun, 20 Oct 2013 14:36:35 +0000 (15:36 +0100)
committerRussell King <rmk+kernel@arm.linux.org.uk>
Mon, 24 Feb 2014 12:03:54 +0000 (12:03 +0000)
There's no point in using the clk API for this; we end up having to
violate the layering this provides.

Acked-by: Philipp Zabel <p.zabel@pengutronix.de>
Acked-by: Shawn Guo <shawn.guo@linaro.org>
Reviewed-by: Fabio Estevam <fabio.estevam@freescale.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
drivers/staging/imx-drm/ipu-v3/ipu-di.c

index d766e18bfca0c451d55b4dfe94704c1ae85f8fdd..82a9ebad697c76b13181d809fa5c53c6d5200f62 100644 (file)
@@ -19,9 +19,6 @@
 #include <linux/io.h>
 #include <linux/err.h>
 #include <linux/platform_device.h>
-#include <linux/clk.h>
-#include <linux/clk-provider.h>
-#include <linux/clkdev.h>
 
 #include "imx-ipu-v3.h"
 #include "ipu-prv.h"
@@ -33,10 +30,7 @@ struct ipu_di {
        struct clk *clk_di;     /* display input clock */
        struct clk *clk_ipu;    /* IPU bus clock */
        struct clk *clk_di_pixel; /* resulting pixel clock */
-       struct clk_hw clk_hw_out;
-       char *clk_name;
        bool inuse;
-       unsigned long clkflags;
        struct ipu_soc *ipu;
 };
 
@@ -141,130 +135,6 @@ static inline void ipu_di_write(struct ipu_di *di, u32 value, unsigned offset)
        writel(value, di->base + offset);
 }
 
-static int ipu_di_clk_calc_div(unsigned long inrate, unsigned long outrate)
-{
-       u64 tmp = inrate;
-       int div;
-
-       tmp *= 16;
-
-       do_div(tmp, outrate);
-
-       div = tmp;
-
-       if (div < 0x10)
-               div = 0x10;
-
-#ifdef WTF_IS_THIS
-       /*
-        * Freescale has this in their Kernel. It is neither clear what
-        * it does nor why it does it
-        */
-       if (div & 0x10)
-               div &= ~0x7;
-       else {
-               /* Round up divider if it gets us closer to desired pix clk */
-               if ((div & 0xC) == 0xC) {
-                       div += 0x10;
-                       div &= ~0xF;
-               }
-       }
-#endif
-       return div;
-}
-
-static unsigned long clk_di_recalc_rate(struct clk_hw *hw,
-               unsigned long parent_rate)
-{
-       struct ipu_di *di = container_of(hw, struct ipu_di, clk_hw_out);
-       unsigned long outrate;
-       u32 div = ipu_di_read(di, DI_BS_CLKGEN0);
-
-       if (div < 0x10)
-               div = 0x10;
-
-       outrate = (parent_rate / div) * 16;
-
-       return outrate;
-}
-
-static long clk_di_round_rate(struct clk_hw *hw, unsigned long rate,
-                               unsigned long *prate)
-{
-       struct ipu_di *di = container_of(hw, struct ipu_di, clk_hw_out);
-       unsigned long outrate;
-       int div;
-       u32 val;
-
-       div = ipu_di_clk_calc_div(*prate, rate);
-
-       outrate = (*prate / div) * 16;
-
-       val = ipu_di_read(di, DI_GENERAL);
-
-       if (!(val & DI_GEN_DI_CLK_EXT) && outrate > *prate / 2)
-               outrate = *prate / 2;
-
-       dev_dbg(di->ipu->dev,
-               "%s: inrate: %ld div: 0x%08x outrate: %ld wanted: %ld\n",
-                       __func__, *prate, div, outrate, rate);
-
-       return outrate;
-}
-
-static int clk_di_set_rate(struct clk_hw *hw, unsigned long rate,
-                               unsigned long parent_rate)
-{
-       struct ipu_di *di = container_of(hw, struct ipu_di, clk_hw_out);
-       int div;
-       u32 clkgen0;
-
-       clkgen0 = ipu_di_read(di, DI_BS_CLKGEN0) & ~0xfff;
-
-       div = ipu_di_clk_calc_div(parent_rate, rate);
-
-       ipu_di_write(di, clkgen0 | div, DI_BS_CLKGEN0);
-
-       dev_dbg(di->ipu->dev, "%s: inrate: %ld desired: %ld div: 0x%08x\n",
-                       __func__, parent_rate, rate, div);
-       return 0;
-}
-
-static u8 clk_di_get_parent(struct clk_hw *hw)
-{
-       struct ipu_di *di = container_of(hw, struct ipu_di, clk_hw_out);
-       u32 val;
-
-       val = ipu_di_read(di, DI_GENERAL);
-
-       return val & DI_GEN_DI_CLK_EXT ? 1 : 0;
-}
-
-static int clk_di_set_parent(struct clk_hw *hw, u8 index)
-{
-       struct ipu_di *di = container_of(hw, struct ipu_di, clk_hw_out);
-       u32 val;
-
-       val = ipu_di_read(di, DI_GENERAL);
-
-       if (index)
-               val |= DI_GEN_DI_CLK_EXT;
-       else
-               val &= ~DI_GEN_DI_CLK_EXT;
-
-       ipu_di_write(di, val, DI_GENERAL);
-
-       return 0;
-}
-
-static struct clk_ops clk_di_ops = {
-       .round_rate = clk_di_round_rate,
-       .set_rate = clk_di_set_rate,
-       .recalc_rate = clk_di_recalc_rate,
-       .set_parent = clk_di_set_parent,
-       .get_parent = clk_di_get_parent,
-};
-
 static void ipu_di_data_wave_config(struct ipu_di *di,
                                     int wave_gen,
                                     int access_size, int component_size)
@@ -528,42 +398,58 @@ static void ipu_di_sync_config_noninterlaced(struct ipu_di *di,
                ipu_di_sync_config(di, cfg_vga, 0, ARRAY_SIZE(cfg_vga));
 }
 
-int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig)
+static void ipu_di_config_clock(struct ipu_di *di,
+       const struct ipu_di_signal_cfg *sig)
 {
-       u32 reg;
-       u32 di_gen, vsync_cnt;
-       u32 div;
-       u32 h_total, v_total;
-       int ret;
-       unsigned long round;
-       struct clk *parent;
+       struct clk *clk;
+       unsigned clkgen0;
+       uint32_t val;
 
-       dev_dbg(di->ipu->dev, "disp %d: panel size = %d x %d\n",
-               di->id, sig->width, sig->height);
+       if (sig->clkflags & IPU_DI_CLKMODE_EXT) {
+               /*
+                * CLKMODE_EXT means we must use the DI clock: this is
+                * needed for things like LVDS which needs to feed the
+                * DI and LDB with the same pixel clock.
+                */
+               clk = di->clk_di;
+
+               if (sig->clkflags & IPU_DI_CLKMODE_SYNC) {
+                       /*
+                        * CLKMODE_SYNC means that we want the DI to be
+                        * clocked at the same rate as the parent clock.
+                        * This is needed (eg) for LDB which needs to be
+                        * fed with the same pixel clock.  We assume that
+                        * the LDB clock has already been set correctly.
+                        */
+                       clkgen0 = 1 << 4;
+               } else {
+                       /*
+                        * We can use the divider.  We should really have
+                        * a flag here indicating whether the bridge can
+                        * cope with a fractional divider or not.  For the
+                        * time being, let's go for simplicitly and
+                        * reliability.
+                        */
+                       unsigned long in_rate;
+                       unsigned div;
 
-       if ((sig->v_sync_width == 0) || (sig->h_sync_width == 0))
-               return -EINVAL;
+                       clk_set_rate(clk, sig->pixelclock);
 
-       dev_dbg(di->ipu->dev, "Clocks: IPU %luHz DI %luHz Needed %luHz\n",
-               clk_get_rate(di->clk_ipu),
-               clk_get_rate(di->clk_di),
-               sig->pixelclock);
+                       in_rate = clk_get_rate(clk);
+                       div = (in_rate + sig->pixelclock / 2) / sig->pixelclock;
+                       if (div == 0)
+                               div = 1;
 
-       /*
-        * CLKMODE_EXT means we must use the DI clock: this is needed
-        * for things like LVDS which needs to feed the DI and LDB with
-        * the same pixel clock.
-        *
-        * For other interfaces, we can arbitarily select between the DI
-        * specific clock and the internal IPU clock.  See DI_GENERAL
-        * bit 20.  We select the IPU clock if it can give us a clock
-        * rate within 1% of the requested frequency, otherwise we use
-        * the DI clock.
-        */
-       round = sig->pixelclock;
-       if (sig->clkflags & IPU_DI_CLKMODE_EXT) {
-               parent = di->clk_di;
+                       clkgen0 = div << 4;
+               }
        } else {
+               /*
+                * For other interfaces, we can arbitarily select between
+                * the DI specific clock and the internal IPU clock.  See
+                * DI_GENERAL bit 20.  We select the IPU clock if it can
+                * give us a clock rate within 1% of the requested frequency,
+                * otherwise we use the DI clock.
+                */
                unsigned long rate, clkrate;
                unsigned div, error;
 
@@ -578,54 +464,80 @@ int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig)
 
                /* Allow a 1% error */
                if (error < 1010 && error >= 990) {
-                       parent = di->clk_ipu;
+                       clk = di->clk_ipu;
+
+                       clkgen0 = div << 4;
                } else {
-                       parent = di->clk_di;
+                       unsigned long in_rate;
+                       unsigned div;
+
+                       clk = di->clk_di;
 
-                       ret = clk_set_rate(parent, sig->pixelclock);
-                       if (ret)
-                               dev_err(di->ipu->dev, "Setting of DI clock failed: %d\n", ret);
+                       clk_set_rate(clk, sig->pixelclock);
 
-                       /* Use the integer divisor rate - avoid fractional dividers */
-                       round = rate;
+                       in_rate = clk_get_rate(clk);
+                       div = (in_rate + sig->pixelclock / 2) / sig->pixelclock;
+                       if (div == 0)
+                               div = 1;
+
+                       clkgen0 = div << 4;
                }
        }
 
-       ret = clk_set_parent(di->clk_di_pixel, parent);
-       if (ret) {
-               dev_err(di->ipu->dev,
-                       "setting pixel clock to parent %s failed with %d\n",
-                               __clk_get_name(parent), ret);
-               return ret;
-       }
+       di->clk_di_pixel = clk;
+
+       /* Set the divider */
+       ipu_di_write(di, clkgen0, DI_BS_CLKGEN0);
 
        /*
-        * CLKMODE_SYNC means that we want the DI to be clocked at the
-        * same rate as the parent clock.  This is needed (eg) for LDB
-        * which needs to be fed with the same pixel clock.
-        *
-        * Note: clk_set_rate(clk, clk_round_rate(clk, rate)) is the
-        * same as clk_set_rate(clk, rate);
+        * Set the high/low periods.  Bits 24:16 give us the falling edge,
+        * and bits 8:0 give the rising edge.  LSB is fraction, and is
+        * based on the divider above.  We want a 50% duty cycle, so set
+        * the falling edge to be half the divider.
         */
-       if (sig->clkflags & IPU_DI_CLKMODE_SYNC)
-               round = clk_get_rate(parent);
+       ipu_di_write(di, (clkgen0 >> 4) << 16, DI_BS_CLKGEN1);
 
-       ret = clk_set_rate(di->clk_di_pixel, round);
+       /* Finally select the input clock */
+       val = ipu_di_read(di, DI_GENERAL) & ~DI_GEN_DI_CLK_EXT;
+       if (clk == di->clk_di)
+               val |= DI_GEN_DI_CLK_EXT;
+       ipu_di_write(di, val, DI_GENERAL);
 
-       dev_dbg(di->ipu->dev, "Want %luHz IPU %luHz DI %luHz using %s, got %luHz\n",
+       dev_dbg(di->ipu->dev, "Want %luHz IPU %luHz DI %luHz using %s, %luHz\n",
                sig->pixelclock,
                clk_get_rate(di->clk_ipu),
                clk_get_rate(di->clk_di),
-               parent == di->clk_di ? "DI" : "IPU",
-               clk_get_rate(di->clk_di_pixel));
+               clk == di->clk_di ? "DI" : "IPU",
+               clk_get_rate(di->clk_di_pixel) / (clkgen0 >> 4));
+}
+
+int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig)
+{
+       u32 reg;
+       u32 di_gen, vsync_cnt;
+       u32 div;
+       u32 h_total, v_total;
+
+       dev_dbg(di->ipu->dev, "disp %d: panel size = %d x %d\n",
+               di->id, sig->width, sig->height);
+
+       if ((sig->v_sync_width == 0) || (sig->h_sync_width == 0))
+               return -EINVAL;
 
        h_total = sig->width + sig->h_sync_width + sig->h_start_width +
                sig->h_end_width;
        v_total = sig->height + sig->v_sync_width + sig->v_start_width +
                sig->v_end_width;
 
+       dev_dbg(di->ipu->dev, "Clocks: IPU %luHz DI %luHz Needed %luHz\n",
+               clk_get_rate(di->clk_ipu),
+               clk_get_rate(di->clk_di),
+               sig->pixelclock);
+
        mutex_lock(&di_mutex);
 
+       ipu_di_config_clock(di, sig);
+
        div = ipu_di_read(di, DI_BS_CLKGEN0) & 0xfff;
        div = div / 16;         /* Now divider is integer portion */
 
@@ -709,7 +621,11 @@ EXPORT_SYMBOL_GPL(ipu_di_init_sync_panel);
 
 int ipu_di_enable(struct ipu_di *di)
 {
-       int ret = clk_prepare_enable(di->clk_di_pixel);
+       int ret;
+
+       WARN_ON(IS_ERR(di->clk_di_pixel));
+
+       ret = clk_prepare_enable(di->clk_di_pixel);
        if (ret)
                return ret;
 
@@ -721,6 +637,8 @@ EXPORT_SYMBOL_GPL(ipu_di_enable);
 
 int ipu_di_disable(struct ipu_di *di)
 {
+       WARN_ON(IS_ERR(di->clk_di_pixel));
+
        ipu_module_disable(di->ipu, di->module);
 
        clk_disable_unprepare(di->clk_di_pixel);
@@ -776,13 +694,6 @@ int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id,
                u32 module, struct clk *clk_ipu)
 {
        struct ipu_di *di;
-       int ret;
-       const char *di_parent[2];
-       struct clk_init_data init = {
-               .ops = &clk_di_ops,
-               .num_parents = 2,
-               .flags = 0,
-       };
 
        if (id > 1)
                return -ENODEV;
@@ -804,45 +715,16 @@ int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id,
        if (!di->base)
                return -ENOMEM;
 
-       di_parent[0] = __clk_get_name(di->clk_ipu);
-       di_parent[1] = __clk_get_name(di->clk_di);
-
        ipu_di_write(di, 0x10, DI_BS_CLKGEN0);
 
-       init.parent_names = (const char **)&di_parent;
-       di->clk_name = kasprintf(GFP_KERNEL, "%s_di%d_pixel",
-                       dev_name(dev), id);
-       if (!di->clk_name)
-               return -ENOMEM;
-
-       init.name = di->clk_name;
-
-       di->clk_hw_out.init = &init;
-       di->clk_di_pixel = clk_register(dev, &di->clk_hw_out);
-
-       if (IS_ERR(di->clk_di_pixel)) {
-               ret = PTR_ERR(di->clk_di_pixel);
-               goto failed_clk_register;
-       }
-
        dev_dbg(dev, "DI%d base: 0x%08lx remapped to %p\n",
                        id, base, di->base);
        di->inuse = false;
        di->ipu = ipu;
 
        return 0;
-
-failed_clk_register:
-
-       kfree(di->clk_name);
-
-       return ret;
 }
 
 void ipu_di_exit(struct ipu_soc *ipu, int id)
 {
-       struct ipu_di *di = ipu->di_priv[id];
-
-       clk_unregister(di->clk_di_pixel);
-       kfree(di->clk_name);
 }