]> git.ipfire.org Git - thirdparty/u-boot.git/commitdiff
video: imx: ipuv3: use clock framework
authorBrian Ruley <brian.ruley@gehealthcare.com>
Fri, 13 Feb 2026 08:49:55 +0000 (10:49 +0200)
committerFabio Estevam <festevam@gmail.com>
Sat, 28 Feb 2026 18:31:49 +0000 (15:31 -0300)
Clocks are now configurable via the common clock framework, however,
users have the option use the legacy clocks if desired. The intent is to
keep the changes minimal for this old SoC.

Signed-off-by: Brian Ruley <brian.ruley@gehealthcare.com>
drivers/video/imx/ipu_common.c
drivers/video/imx/ipu_disp.c
drivers/video/imx/mxc_ipuv3_fb.c

index d994053394f2fe067df95125b70077d2231d5769..8630374a055fdec7c610e128a6e8f8c3d6eaaa2a 100644 (file)
@@ -101,7 +101,18 @@ struct ipu_ch_param {
  */
 static int ipu_clk_init(struct ipu_ctx *ctx)
 {
+#if CONFIG_IS_ENABLED(IPU_CLK_LEGACY)
        return ipu_clk_init_legacy(ctx);
+#else
+       struct clk *clk;
+
+       clk = devm_clk_get(ctx->dev, "bus");
+       if (IS_ERR(clk))
+               return PTR_ERR(clk);
+
+       ctx->ipu_clk = clk;
+       return 0;
+#endif
 }
 
 /*
@@ -113,7 +124,13 @@ static int ipu_clk_init(struct ipu_ctx *ctx)
  */
 static int ipu_ldb_clk_init(struct ipu_ctx *ctx)
 {
+#if CONFIG_IS_ENABLED(IPU_CLK_LEGACY)
        return ipu_ldb_clk_init_legacy(ctx);
+#else
+       /* Set this in the FB driver where we know the display id */
+       ctx->ldb_clk = NULL;
+       return 0;
+#endif
 }
 
 /* Static functions */
@@ -150,6 +167,29 @@ static inline void ipu_ch_param_set_buffer(u32 ch, int buf_num,
 #define idma_mask(ch) (idma_is_valid(ch) ? (1UL << (ch & 0x1F)) : 0)
 #define idma_is_set(reg, dma) (__raw_readl(reg(dma)) & idma_mask(dma))
 
+/*
+ * Function to initialize the display clocks
+ *
+ * @param   ctx            The ipu context for which the function is called
+ *
+ * Return:  Returns 0 on success or negative error code on error
+ */
+static int ipu_di_clk_init(struct ipu_ctx *ctx, int id)
+{
+#if CONFIG_IS_ENABLED(IPU_CLK_LEGACY)
+       ctx->di_clk[id] = NULL;
+       return 0;
+#else
+       struct clk *clk;
+
+       clk = devm_clk_get(ctx->dev, id ? "di1" : "di0");
+       if (IS_ERR(clk))
+               return PTR_ERR(clk);
+
+       ctx->di_clk[id] = clk;
+       return 0;
+#endif
+}
 /*
  * Function to initialize the pixel clock
  *
@@ -159,7 +199,12 @@ static inline void ipu_ch_param_set_buffer(u32 ch, int buf_num,
  */
 static int ipu_pixel_clk_init(struct ipu_ctx *ctx, int id)
 {
+#if CONFIG_IS_ENABLED(IPU_CLK_LEGACY)
        return ipu_pixel_clk_init_legacy(ctx, id);
+#else
+       ctx->pixel_clk[id] = ctx->ipu_clk;
+       return 0;
+#endif
 }
 
 /*
@@ -230,33 +275,39 @@ struct ipu_ctx *ipu_probe(struct udevice *dev)
        ipu_cpmem_base = (u32 *)(ipu_base + IPU_CPMEM_REG_BASE);
        ipu_dc_tmpl_reg = (u32 *)(ipu_base + IPU_DC_TMPL_REG_BASE);
 
-       ret = ipu_pixel_clk_init(ctx, 0);
-       if (ret)
-               goto err;
-
-       ret = ipu_pixel_clk_init(ctx, 1);
-       if (ret)
-               goto err;
+       for (int i = 0; i <= 1; i++) {
+               ret = ipu_pixel_clk_init(ctx, i);
+               if (ret)
+                       goto err;
+       }
 
        ret = ipu_clk_init(ctx);
        if (ret)
                goto err;
 
-       debug("ipu_clk = %u\n", clk_get_rate(ctx->ipu_clk));
+       debug("ipu_clk = %lu\n", (ulong)clk_get_rate(ctx->ipu_clk));
 
        ret = ipu_ldb_clk_init(ctx);
        if (ret)
                goto err;
 
-       debug("ldb_clk = %u\n", clk_get_rate(ctx->ldb_clk));
+       if (ctx->ldb_clk)
+               debug("ldb_clk = %lu\n", (ulong)clk_get_rate(ctx->ldb_clk));
+
        ipu_reset();
 
+#if CONFIG_IS_ENABLED(IPU_CLK_LEGACY)
        clk_set_parent(ctx->pixel_clk[0], ctx->ipu_clk);
        clk_set_parent(ctx->pixel_clk[1], ctx->ipu_clk);
+
        clk_enable(ctx->ipu_clk);
+#endif
 
-       ctx->di_clk[0] = NULL;
-       ctx->di_clk[1] = NULL;
+       for (int i = 0; i <= 1; i++) {
+               ret = ipu_di_clk_init(ctx, i);
+               if (ret)
+                       goto err;
+       }
 
        __raw_writel(0x807FFFFF, IPU_MEM_RST);
        while (__raw_readl(IPU_MEM_RST) & 0x80000000)
@@ -278,7 +329,9 @@ struct ipu_ctx *ipu_probe(struct udevice *dev)
        /* Set MCU_T to divide MCU access window into 2 */
        __raw_writel(0x00400000L | (IPU_MCU_T_DEFAULT << 18), IPU_DISP_GEN);
 
+#if CONFIG_IS_ENABLED(IPU_CLK_LEGACY)
        clk_disable(ctx->ipu_clk);
+#endif
 
        return ctx;
 err:
index 6a337b13af602cdd0f0b49999e005656d50e918b..5e78574da9b472d5e7d3309f6434b7ae5ec4b510 100644 (file)
@@ -612,6 +612,11 @@ void ipu_dp_dc_enable(struct ipu_ctx *ctx, ipu_channel_t channel)
        __raw_writel(reg, DC_WR_CH_CONF(dc_chan));
 
        clk_enable(ctx->pixel_clk[di]);
+#if !CONFIG_IS_ENABLED(IPU_CLK_LEGACY)
+       reg = __raw_readl(IPU_DISP_GEN);
+       reg |= di ? DI1_COUNTER_RELEASE : DI0_COUNTER_RELEASE;
+       __raw_writel(reg, IPU_DISP_GEN);
+#endif
 }
 
 static unsigned char dc_swap;
@@ -702,6 +707,12 @@ void ipu_dp_dc_disable(struct ipu_ctx *ctx, ipu_channel_t channel,
 
                /* Clock is already off because it must be done quickly, but
                   we need to fix the ref count */
+#if !CONFIG_IS_ENABLED(IPU_CLK_LEGACY)
+               reg = __raw_readl(IPU_DISP_GEN);
+               reg &= ctx->dc_di_assignment[dc_chan] ? ~DI1_COUNTER_RELEASE :
+                                                       ~DI0_COUNTER_RELEASE;
+               __raw_writel(reg, IPU_DISP_GEN);
+#endif
                clk_disable(ctx->pixel_clk[ctx->dc_di_assignment[dc_chan]]);
        }
 }
@@ -765,40 +776,21 @@ static int ipu_pixfmt_to_map(u32 fmt)
  *
  * @param      sig     Bitfield of signal polarities for LCD interface.
  *
- * Return:     This function returns 0 on success or negative error code on
- *             fail.
+ * Return:     The integer portion of the divider set for the pixel clock.
  */
-
-int32_t ipu_init_sync_panel(struct ipu_di_config *di, ipu_di_signal_cfg_t sig)
+static u32 ipu_di_clk_config(struct ipu_di_config *di, ipu_di_signal_cfg_t sig)
 {
        struct ipu_ctx *ctx = di->ctx;
        int disp = di->disp;
-       u32 reg;
-       u32 di_gen, vsync_cnt;
-       u32 div, rounded_pixel_clk;
-       u32 h_total, v_total;
-       int map;
-       struct clk *di_parent;
-
-       debug("panel size = %d x %d\n", di->width, di->height);
-
-       if ((di->v_sync_width == 0) || (di->h_sync_width == 0))
-               return -EINVAL;
-
-       /* adapt panel to ipu restricitions */
-       if (di->v_end_width < 2) {
-               di->v_end_width = 2;
-               puts("WARNING: v_end_width (lower_margin) must be >= 2, adjusted\n");
-       }
-
-       h_total = di->width + di->h_sync_width + di->h_start_width +
-                 di->h_end_width;
-       v_total = di->height + di->v_sync_width + di->v_start_width +
-                 di->v_end_width;
+       u32 div;
 
        /* Init clocking */
        debug("pixel clk = %dHz\n", di->pixel_clk_rate);
 
+#if CONFIG_IS_ENABLED(IPU_CLK_LEGACY)
+       u32 rounded_pixel_clk;
+       struct clk *di_parent;
+
        if (sig.ext_clk) {
                if (!(g_di1_tvout && (disp == 1))) { /*not round div for tvout*/
                        /*
@@ -830,13 +822,109 @@ int32_t ipu_init_sync_panel(struct ipu_di_config *di, ipu_di_signal_cfg_t sig)
                if (clk_get_usecount(ctx->pixel_clk[disp]) != 0)
                        clk_set_parent(ctx->pixel_clk[disp], ctx->ipu_clk);
        }
+
        rounded_pixel_clk =
                clk_round_rate(ctx->pixel_clk[disp], di->pixel_clk_rate);
        clk_set_rate(ctx->pixel_clk[disp], rounded_pixel_clk);
-       udelay(5000);
+
        /* Get integer portion of divider */
        div = clk_get_rate(clk_get_parent(ctx->pixel_clk[disp])) /
              rounded_pixel_clk;
+#else
+       struct clk *clk;
+       u32 clkgen0, di_gen;
+       ulong id;
+
+       if (sig.ext_clk) {
+               /*
+                * Bypass the divider, assuming synchronous mode
+                */
+               clk = ctx->di_clk[disp];
+               div = 1;
+       } else {
+
+               ulong clk_rate = clk_get_rate(ctx->ipu_clk);
+               u32 error;
+
+               div = DIV_ROUND_CLOSEST(clk_rate, di->pixel_clk_rate);
+               div = clamp(div, 1U, 255U);
+
+               error = (clk_rate / div) / (di->pixel_clk_rate / 1000);
+
+               /*
+                 * Select IPU if the rate is within 1% of requested pixel
+                 * clock, otherwise, use the DI clock
+                 */
+               if (990 <= error && error < 1010) {
+                       clk = ctx->ipu_clk;
+               } else {
+                       clk = ctx->di_clk[disp];
+
+                       clk_set_rate(clk, di->pixel_clk_rate);
+                       div = DIV_ROUND_CLOSEST(clk_get_rate(clk),
+                                               di->pixel_clk_rate);
+                       div = clamp(div, 1U, 255U);
+               }
+       }
+
+       clkgen0 = div << 4;
+
+       ctx->pixel_clk[disp] = clk;
+       debug("new pixel rate: %lu Hz\n", clk_get_rate(clk));
+
+       id = clk_get_id(clk);
+       __raw_writel(clkgen0, DI_BS_CLKGEN0(id));
+       __raw_writel((clkgen0 & 0xFFF0) << 12, DI_BS_CLKGEN1(id));
+
+       di_gen = __raw_readl(DI_GENERAL(id)) & ~DI_GEN_DI_CLK_EXT;
+       if (clk == ctx->di_clk[disp])
+               di_gen |= DI_GEN_DI_CLK_EXT;
+
+       __raw_writel(di_gen, DI_GENERAL(id));
+#endif
+
+       udelay(5000);
+       return div;
+}
+
+/*
+ * This function is called to initialize a synchronous LCD panel.
+ *
+ * @param      di      Pointer to display data.
+ *
+ * @param      sig     Bitfield of signal polarities for LCD interface.
+ *
+ * Return:     This function returns 0 on success or negative error code on
+ *             fail.
+ */
+int32_t ipu_init_sync_panel(struct ipu_di_config *di, ipu_di_signal_cfg_t sig)
+{
+       int disp = di->disp;
+       u32 reg;
+       u32 di_gen, vsync_cnt;
+       u32 div;
+       u32 h_total, v_total;
+       int map;
+
+       debug("panel size = %d x %d\n", di->width, di->height);
+
+       if ((di->v_sync_width == 0) || (di->h_sync_width == 0))
+               return -EINVAL;
+
+       /* adapt panel to ipu restricitions */
+       if (di->v_end_width < 2) {
+               di->v_end_width = 2;
+               puts("WARNING: v_end_width (lower_margin) must be >= 2, adjusted\n");
+       }
+
+       h_total = di->width + di->h_sync_width + di->h_start_width +
+                 di->h_end_width;
+       v_total = di->height + di->v_sync_width + di->v_start_width +
+                 di->v_end_width;
+
+       div = ipu_di_clk_config(di, sig);
+       if (div < 0)
+               return div;
 
        ipu_di_data_wave_config(disp, SYNC_WAVE, div - 1, div - 1);
        ipu_di_data_pin_config(disp, SYNC_WAVE, DI_PIN15, 3, 0, div * 2);
index ef5d4faf3b38e0540d5d7f67d4c9c2227cb486f9..3a327b9e97d0244ad7fa9a276ee11cd3c01b6103 100644 (file)
@@ -35,6 +35,7 @@
 #include <dm.h>
 #include <dm/devres.h>
 #include <video.h>
+#include <dt-bindings/clock/imx6qdl-clock.h>
 
 static int mxcfb_map_video_memory(struct fb_info *fbi);
 static int mxcfb_unmap_video_memory(struct fb_info *fbi);
@@ -599,6 +600,22 @@ static int ipuv3_video_probe(struct udevice *dev)
        if (ret < 0)
                return ret;
 
+#if !CONFIG_IS_ENABLED(IPU_CLK_LEGACY)
+       if (of_machine_is_compatible("fsl,imx6qp"))
+               ret = clk_get_by_id(gdisp ? IMX6QDL_CLK_LDB_DI1_PODF :
+                                           IMX6QDL_CLK_LDB_DI0_PODF,
+                                   &ctx->ldb_clk);
+       else
+               ret = clk_get_by_id(gdisp ? IMX6QDL_CLK_LDB_DI1 :
+                                           IMX6QDL_CLK_LDB_DI0,
+                                   &ctx->ldb_clk);
+
+       if (ret < 0)
+               return ret;
+
+       debug("ldb_clk = %lu\n", clk_get_rate(ctx->ldb_clk));
+#endif
+
        ret = mxcfb_probe(dev, gpixfmt, gdisp, gmode);
        if (ret < 0)
                return ret;