summaryrefslogtreecommitdiff
path: root/drivers/video/imx
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video/imx')
-rw-r--r--drivers/video/imx/Kconfig7
-rw-r--r--drivers/video/imx/Makefile1
-rw-r--r--drivers/video/imx/ipu.h60
-rw-r--r--drivers/video/imx/ipu_clk_legacy.c310
-rw-r--r--drivers/video/imx/ipu_common.c371
-rw-r--r--drivers/video/imx/ipu_disp.c142
-rw-r--r--drivers/video/imx/mxc_ipuv3_fb.c17
7 files changed, 559 insertions, 349 deletions
diff --git a/drivers/video/imx/Kconfig b/drivers/video/imx/Kconfig
index b35ba965efc..c25f209629e 100644
--- a/drivers/video/imx/Kconfig
+++ b/drivers/video/imx/Kconfig
@@ -15,6 +15,13 @@ config IMX_HDMI
bool "Enable HDMI support in IPUv3"
depends on VIDEO_IPUV3
+config IPU_CLK_LEGACY
+ bool "Use legacy clock management for IPU"
+ depends on VIDEO_IPUV3 && !CLK
+ default y
+ help
+ Use legacy clock management instead of Common Clock Framework.
+
config IMX_LDB
bool "Freescale i.MX8MP LDB bridge"
depends on VIDEO_BRIDGE
diff --git a/drivers/video/imx/Makefile b/drivers/video/imx/Makefile
index 1edf5a6bdf0..0e7f71a9f93 100644
--- a/drivers/video/imx/Makefile
+++ b/drivers/video/imx/Makefile
@@ -4,5 +4,6 @@
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
obj-$(CONFIG_VIDEO_IPUV3) += mxc_ipuv3_fb.o ipu_common.o ipu_disp.o
+obj-$(CONFIG_IPU_CLK_LEGACY) += ipu_clk_legacy.o
obj-$(CONFIG_IMX_LDB) += ldb.o
obj-$(CONFIG_IMX_LCDIF) += lcdif.o
diff --git a/drivers/video/imx/ipu.h b/drivers/video/imx/ipu.h
index 62827dc480d..ae40e20bc28 100644
--- a/drivers/video/imx/ipu.h
+++ b/drivers/video/imx/ipu.h
@@ -18,14 +18,23 @@
#ifndef __ASM_ARCH_IPU_H__
#define __ASM_ARCH_IPU_H__
+#if !CONFIG_IS_ENABLED(IPU_CLK_LEGACY)
+#include <clk.h>
+#endif
#include <ipu_pixfmt.h>
#include <linux/types.h>
+#define IPUV3_CLK_MX51 133000000
+#define IPUV3_CLK_MX53 200000000
+#define IPUV3_CLK_MX6Q 264000000
+#define IPUV3_CLK_MX6DL 198000000
+
#define IDMA_CHAN_INVALID 0xFF
#define HIGH_RESOLUTION_WIDTH 1024
struct ipu_ctx;
-struct ipu_di_config;
+
+#if CONFIG_IS_ENABLED(IPU_CLK_LEGACY)
struct clk {
const char *name;
@@ -75,6 +84,46 @@ struct clk {
int (*set_parent)(struct clk *clk, struct clk *parent);
};
+/* Legacy clock API functions */
+void clk_enable(struct clk *clk);
+void clk_disable(struct clk *clk);
+int clk_get_usecount(struct clk *clk);
+u32 clk_get_rate(struct clk *clk);
+struct clk *clk_get_parent(struct clk *clk);
+int clk_set_rate(struct clk *clk, unsigned long rate);
+long clk_round_rate(struct clk *clk, unsigned long rate);
+int clk_set_parent(struct clk *clk, struct clk *parent);
+
+/* IPU clock initialization */
+int ipu_clk_init_legacy(struct ipu_ctx *ctx);
+int ipu_ldb_clk_init_legacy(struct ipu_ctx *ctx);
+int ipu_pixel_clk_init_legacy(struct ipu_ctx *ctx, int id);
+
+#else
+
+static inline int clk_get_usecount(struct clk *clk)
+{
+ return clk->enable_count;
+}
+
+/* Stub functions for non-legacy builds */
+static inline int ipu_clk_init_legacy(struct ipu_ctx *ctx)
+{
+ return -ENOSYS;
+}
+
+static inline int ipu_ldb_clk_init_legacy(struct ipu_ctx *ctx)
+{
+ return -ENOSYS;
+}
+
+static inline int ipu_pixel_clk_init_legacy(struct ipu_ctx *ctx, int id)
+{
+ return -ENOSYS;
+}
+
+#endif /* CONFIG_IS_ENABLED(IPU_CLK_LEGACY) */
+
struct udevice;
/*
@@ -298,15 +347,6 @@ int32_t ipu_disp_set_color_key(ipu_channel_t channel, unsigned char enable,
u32 bytes_per_pixel(u32 fmt);
-void clk_enable(struct clk *clk);
-void clk_disable(struct clk *clk);
-u32 clk_get_rate(struct clk *clk);
-int clk_set_rate(struct clk *clk, unsigned long rate);
-long clk_round_rate(struct clk *clk, unsigned long rate);
-int clk_set_parent(struct clk *clk, struct clk *parent);
-int clk_get_usecount(struct clk *clk);
-struct clk *clk_get_parent(struct clk *clk);
-
void ipu_dump_registers(void);
struct ipu_ctx *ipu_probe(struct udevice *dev);
bool ipu_clk_enabled(struct ipu_ctx *ctx);
diff --git a/drivers/video/imx/ipu_clk_legacy.c b/drivers/video/imx/ipu_clk_legacy.c
new file mode 100644
index 00000000000..8aaafa2a080
--- /dev/null
+++ b/drivers/video/imx/ipu_clk_legacy.c
@@ -0,0 +1,310 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Legacy IPU clock management for i.MX5/6 without Common Clock Framework
+ *
+ * (C) Copyright 2026
+ * Brian Ruley, GE HealthCare, brian.ruley@gehealthcare.com
+ */
+
+#include "ipu.h"
+#include "ipu_regs.h"
+#include <asm/arch/crm_regs.h>
+#include <asm/arch/sys_proto.h>
+#include <asm/io.h>
+#include <div64.h>
+#include <dm/devres.h>
+#include <linux/err.h>
+#include <log.h>
+
+extern struct mxc_ccm_reg *mxc_ccm;
+
+void clk_enable(struct clk *clk)
+{
+ if (clk) {
+ if (clk->usecount++ == 0)
+ clk->enable(clk);
+ }
+}
+
+void clk_disable(struct clk *clk)
+{
+ if (clk) {
+ if (!(--clk->usecount)) {
+ if (clk->disable)
+ clk->disable(clk);
+ }
+ }
+}
+
+int clk_get_usecount(struct clk *clk)
+{
+ if (clk == NULL)
+ return 0;
+
+ return clk->usecount;
+}
+
+u32 clk_get_rate(struct clk *clk)
+{
+ if (!clk)
+ return 0;
+
+ return clk->rate;
+}
+
+struct clk *clk_get_parent(struct clk *clk)
+{
+ if (!clk)
+ return 0;
+
+ return clk->parent;
+}
+
+int clk_set_rate(struct clk *clk, unsigned long rate)
+{
+ if (!clk)
+ return 0;
+
+ if (clk->set_rate)
+ clk->set_rate(clk, rate);
+
+ return clk->rate;
+}
+
+long clk_round_rate(struct clk *clk, unsigned long rate)
+{
+ if (clk == NULL || !clk->round_rate)
+ return 0;
+
+ return clk->round_rate(clk, rate);
+}
+
+int clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ clk->parent = parent;
+ if (clk->set_parent)
+ return clk->set_parent(clk, parent);
+ return 0;
+}
+
+static int clk_ipu_enable(struct clk *clk)
+{
+ u32 reg;
+
+ reg = __raw_readl(clk->enable_reg);
+ reg |= MXC_CCM_CCGR_CG_MASK << clk->enable_shift;
+ __raw_writel(reg, clk->enable_reg);
+
+#if CONFIG_IS_ENABLED(MX51) || CONFIG_IS_ENABLED(MX53)
+ reg = __raw_readl(&mxc_ccm->ccdr);
+ reg &= ~MXC_CCM_CCDR_IPU_HS_MASK;
+ __raw_writel(reg, &mxc_ccm->ccdr);
+
+ reg = __raw_readl(&mxc_ccm->clpcr);
+ reg &= ~MXC_CCM_CLPCR_BYPASS_IPU_LPM_HS;
+ __raw_writel(reg, &mxc_ccm->clpcr);
+#endif
+ return 0;
+}
+
+static void clk_ipu_disable(struct clk *clk)
+{
+ u32 reg;
+
+ reg = __raw_readl(clk->enable_reg);
+ reg &= ~(MXC_CCM_CCGR_CG_MASK << clk->enable_shift);
+ __raw_writel(reg, clk->enable_reg);
+
+#if CONFIG_IS_ENABLED(MX51) || CONFIG_IS_ENABLED(MX53)
+ reg = __raw_readl(&mxc_ccm->ccdr);
+ reg |= MXC_CCM_CCDR_IPU_HS_MASK;
+ __raw_writel(reg, &mxc_ccm->ccdr);
+
+ reg = __raw_readl(&mxc_ccm->clpcr);
+ reg |= MXC_CCM_CLPCR_BYPASS_IPU_LPM_HS;
+ __raw_writel(reg, &mxc_ccm->clpcr);
+#endif
+}
+
+static void ipu_pixel_clk_recalc(struct clk *clk)
+{
+ u32 div;
+ u64 final_rate = (unsigned long long)clk->parent->rate * 16;
+
+ div = __raw_readl(DI_BS_CLKGEN0(clk->id));
+ debug("read BS_CLKGEN0 div:%d, final_rate:%lld, prate:%ld\n", div,
+ final_rate, clk->parent->rate);
+
+ clk->rate = 0;
+ if (div != 0) {
+ do_div(final_rate, div);
+ clk->rate = final_rate;
+ }
+}
+
+static unsigned long ipu_pixel_clk_round_rate(struct clk *clk,
+ unsigned long rate)
+{
+ u64 div, final_rate;
+ u32 remainder;
+ u64 parent_rate = (unsigned long long)clk->parent->rate * 16;
+
+ div = parent_rate;
+ remainder = do_div(div, rate);
+ if (remainder > (rate / 2))
+ div++;
+ if (div < 0x10)
+ div = 0x10;
+ if (div & ~0xFEF)
+ div &= 0xFF8;
+ else {
+ if ((div & 0xC) == 0xC) {
+ div += 0x10;
+ div &= ~0xF;
+ }
+ }
+ final_rate = parent_rate;
+ do_div(final_rate, div);
+
+ return final_rate;
+}
+
+static int ipu_pixel_clk_set_rate(struct clk *clk, unsigned long rate)
+{
+ u64 div, parent_rate;
+ u32 remainder;
+
+ parent_rate = (unsigned long long)clk->parent->rate * 16;
+ div = parent_rate;
+ remainder = do_div(div, rate);
+ if (remainder > (rate / 2))
+ div++;
+
+ if ((div & 0xC) == 0xC) {
+ div += 0x10;
+ div &= ~0xF;
+ }
+ if (div > 0x1000)
+ debug("Overflow, DI_BS_CLKGEN0 div:0x%x\n", (u32)div);
+
+ __raw_writel(div, DI_BS_CLKGEN0(clk->id));
+ __raw_writel((div / 16) << 16, DI_BS_CLKGEN1(clk->id));
+
+ do_div(parent_rate, div);
+ clk->rate = parent_rate;
+
+ return 0;
+}
+
+static int ipu_pixel_clk_enable(struct clk *clk)
+{
+ u32 disp_gen = __raw_readl(IPU_DISP_GEN);
+ disp_gen |= clk->id ? DI1_COUNTER_RELEASE : DI0_COUNTER_RELEASE;
+ __raw_writel(disp_gen, IPU_DISP_GEN);
+
+ return 0;
+}
+
+static void ipu_pixel_clk_disable(struct clk *clk)
+{
+ u32 disp_gen = __raw_readl(IPU_DISP_GEN);
+ disp_gen &= clk->id ? ~DI1_COUNTER_RELEASE : ~DI0_COUNTER_RELEASE;
+ __raw_writel(disp_gen, IPU_DISP_GEN);
+}
+
+static int ipu_pixel_clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 di_gen = __raw_readl(DI_GENERAL(clk->id));
+ struct ipu_ctx *ctx = clk->ctx;
+
+ if (parent == ctx->ipu_clk)
+ di_gen &= ~DI_GEN_DI_CLK_EXT;
+ else if (!IS_ERR(ctx->di_clk[clk->id]) && parent == ctx->ldb_clk)
+ di_gen |= DI_GEN_DI_CLK_EXT;
+ else
+ return -EINVAL;
+
+ __raw_writel(di_gen, DI_GENERAL(clk->id));
+ ipu_pixel_clk_recalc(clk);
+ return 0;
+}
+
+int ipu_pixel_clk_init_legacy(struct ipu_ctx *ctx, int id)
+{
+ struct clk *pixel_clk;
+
+ pixel_clk = devm_kzalloc(ctx->dev, sizeof(*pixel_clk), GFP_KERNEL);
+ if (!pixel_clk)
+ return -ENOMEM;
+
+ pixel_clk->name = "pixel_clk";
+ pixel_clk->id = id;
+ pixel_clk->ctx = ctx;
+ pixel_clk->recalc = ipu_pixel_clk_recalc;
+ pixel_clk->set_rate = ipu_pixel_clk_set_rate;
+ pixel_clk->round_rate = ipu_pixel_clk_round_rate;
+ pixel_clk->set_parent = ipu_pixel_clk_set_parent;
+ pixel_clk->enable = ipu_pixel_clk_enable;
+ pixel_clk->disable = ipu_pixel_clk_disable;
+ pixel_clk->usecount = 0;
+
+ ctx->pixel_clk[id] = pixel_clk;
+ return 0;
+}
+
+int ipu_clk_init_legacy(struct ipu_ctx *ctx)
+{
+ struct clk *ipu_clk;
+
+ ipu_clk = devm_kzalloc(ctx->dev, sizeof(*ipu_clk), GFP_KERNEL);
+ if (!ipu_clk)
+ return -ENOMEM;
+
+ ipu_clk->name = "ipu_clk";
+ ipu_clk->ctx = ctx;
+#if CONFIG_IS_ENABLED(MX51) || CONFIG_IS_ENABLED(MX53)
+ ipu_clk->enable_reg =
+ (u32 *)(CCM_BASE_ADDR + offsetof(struct mxc_ccm_reg, CCGR5));
+ ipu_clk->enable_shift = MXC_CCM_CCGR5_IPU_OFFSET;
+#else
+ ipu_clk->enable_reg =
+ (u32 *)(CCM_BASE_ADDR + offsetof(struct mxc_ccm_reg, CCGR3));
+ ipu_clk->enable_shift = MXC_CCM_CCGR3_IPU1_IPU_DI0_OFFSET;
+#endif
+
+ ipu_clk->enable = clk_ipu_enable;
+ ipu_clk->disable = clk_ipu_disable;
+ ipu_clk->usecount = 0;
+
+#if CONFIG_IS_ENABLED(MX51)
+ ipu_clk->rate = IPUV3_CLK_MX51;
+#elif CONFIG_IS_ENABLED(MX53)
+ ipu_clk->rate = IPUV3_CLK_MX53;
+#else
+ ipu_clk->rate = is_mx6sdl() ? IPUV3_CLK_MX6DL : IPUV3_CLK_MX6Q;
+#endif
+
+ ctx->ipu_clk = ipu_clk;
+ return 0;
+}
+
+#if !defined CFG_SYS_LDB_CLOCK
+#define CFG_SYS_LDB_CLOCK 65000000
+#endif
+
+int ipu_ldb_clk_init_legacy(struct ipu_ctx *ctx)
+{
+ struct clk *ldb_clk;
+
+ ldb_clk = devm_kzalloc(ctx->dev, sizeof(*ldb_clk), GFP_KERNEL);
+ if (!ldb_clk)
+ return -ENOMEM;
+
+ ldb_clk->name = "ldb_clk";
+ ldb_clk->ctx = ctx;
+ ldb_clk->rate = CFG_SYS_LDB_CLOCK;
+ ldb_clk->usecount = 0;
+
+ ctx->ldb_clk = ldb_clk;
+ return 0;
+}
diff --git a/drivers/video/imx/ipu_common.c b/drivers/video/imx/ipu_common.c
index e9897ee79d2..8630374a055 100644
--- a/drivers/video/imx/ipu_common.c
+++ b/drivers/video/imx/ipu_common.c
@@ -31,8 +31,8 @@
#include <linux/types.h>
#include <log.h>
-extern struct mxc_ccm_reg *mxc_ccm;
-extern u32 *ipu_cpmem_base;
+u32 *ipu_cpmem_base;
+u32 *ipu_dc_tmpl_reg;
struct ipu_ch_param_word {
u32 data[5];
@@ -92,126 +92,6 @@ struct ipu_ch_param {
#define IPU_SW_RST_TOUT_USEC (10000)
-#define IPUV3_CLK_MX51 133000000
-#define IPUV3_CLK_MX53 200000000
-#define IPUV3_CLK_MX6Q 264000000
-#define IPUV3_CLK_MX6DL 198000000
-
-void clk_enable(struct clk *clk)
-{
- if (clk) {
- if (clk->usecount++ == 0)
- clk->enable(clk);
- }
-}
-
-void clk_disable(struct clk *clk)
-{
- if (clk) {
- if (!(--clk->usecount)) {
- if (clk->disable)
- clk->disable(clk);
- }
- }
-}
-
-int clk_get_usecount(struct clk *clk)
-{
- if (clk == NULL)
- return 0;
-
- return clk->usecount;
-}
-
-u32 clk_get_rate(struct clk *clk)
-{
- if (!clk)
- return 0;
-
- return clk->rate;
-}
-
-struct clk *clk_get_parent(struct clk *clk)
-{
- if (!clk)
- return 0;
-
- return clk->parent;
-}
-
-int clk_set_rate(struct clk *clk, unsigned long rate)
-{
- if (!clk)
- return 0;
-
- if (clk->set_rate)
- clk->set_rate(clk, rate);
-
- return clk->rate;
-}
-
-long clk_round_rate(struct clk *clk, unsigned long rate)
-{
- if (clk == NULL || !clk->round_rate)
- return 0;
-
- return clk->round_rate(clk, rate);
-}
-
-int clk_set_parent(struct clk *clk, struct clk *parent)
-{
- clk->parent = parent;
- if (clk->set_parent)
- return clk->set_parent(clk, parent);
- return 0;
-}
-
-static int clk_ipu_enable(struct clk *clk)
-{
- u32 reg;
-
- reg = __raw_readl(clk->enable_reg);
- reg |= MXC_CCM_CCGR_CG_MASK << clk->enable_shift;
- __raw_writel(reg, clk->enable_reg);
-
-#if CONFIG_IS_ENABLED(MX51) || CONFIG_IS_ENABLED(MX53)
- /* Handshake with IPU when certain clock rates are changed. */
- reg = __raw_readl(&mxc_ccm->ccdr);
- reg &= ~MXC_CCM_CCDR_IPU_HS_MASK;
- __raw_writel(reg, &mxc_ccm->ccdr);
-
- /* Handshake with IPU when LPM is entered as its enabled. */
- reg = __raw_readl(&mxc_ccm->clpcr);
- reg &= ~MXC_CCM_CLPCR_BYPASS_IPU_LPM_HS;
- __raw_writel(reg, &mxc_ccm->clpcr);
-#endif
- return 0;
-}
-
-static void clk_ipu_disable(struct clk *clk)
-{
- u32 reg;
-
- reg = __raw_readl(clk->enable_reg);
- reg &= ~(MXC_CCM_CCGR_CG_MASK << clk->enable_shift);
- __raw_writel(reg, clk->enable_reg);
-
-#if CONFIG_IS_ENABLED(MX51) || CONFIG_IS_ENABLED(MX53)
- /*
- * No handshake with IPU whe dividers are changed
- * as its not enabled.
- */
- reg = __raw_readl(&mxc_ccm->ccdr);
- reg |= MXC_CCM_CCDR_IPU_HS_MASK;
- __raw_writel(reg, &mxc_ccm->ccdr);
-
- /* No handshake with IPU when LPM is entered as its not enabled. */
- reg = __raw_readl(&mxc_ccm->clpcr);
- reg |= MXC_CCM_CLPCR_BYPASS_IPU_LPM_HS;
- __raw_writel(reg, &mxc_ccm->clpcr);
-#endif
-}
-
/*
* Function to initialize the ipu clock
*
@@ -221,43 +101,19 @@ static void clk_ipu_disable(struct clk *clk)
*/
static int ipu_clk_init(struct ipu_ctx *ctx)
{
- struct clk *ipu_clk;
-
- ipu_clk = devm_kzalloc(ctx->dev, sizeof(*ipu_clk), GFP_KERNEL);
- if (!ipu_clk)
- return -ENOMEM;
-
- ipu_clk->name = "ipu_clk";
- ipu_clk->ctx = ctx;
-#if CONFIG_IS_ENABLED(MX51) || CONFIG_IS_ENABLED(MX53)
- ipu_clk->enable_reg =
- (u32 *)(CCM_BASE_ADDR + offsetof(struct mxc_ccm_reg, CCGR5));
- ipu_clk->enable_shift = MXC_CCM_CCGR5_IPU_OFFSET;
+#if CONFIG_IS_ENABLED(IPU_CLK_LEGACY)
+ return ipu_clk_init_legacy(ctx);
#else
- ipu_clk->enable_reg =
- (u32 *)(CCM_BASE_ADDR + offsetof(struct mxc_ccm_reg, CCGR3));
- ipu_clk->enable_shift = MXC_CCM_CCGR3_IPU1_IPU_DI0_OFFSET;
-#endif
+ struct clk *clk;
- ipu_clk->enable = clk_ipu_enable;
- ipu_clk->disable = clk_ipu_disable;
- ipu_clk->usecount = 0;
+ clk = devm_clk_get(ctx->dev, "bus");
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
-#if CONFIG_IS_ENABLED(MX51)
- ipu_clk->rate = IPUV3_CLK_MX51;
-#elif CONFIG_IS_ENABLED(MX53)
- ipu_clk->rate = IPUV3_CLK_MX53;
-#else
- ipu_clk->rate = is_mx6sdl() ? IPUV3_CLK_MX6DL : IPUV3_CLK_MX6Q;
-#endif
-
- ctx->ipu_clk = ipu_clk;
+ ctx->ipu_clk = clk;
return 0;
-};
-
-#if !defined CFG_SYS_LDB_CLOCK
-#define CFG_SYS_LDB_CLOCK 65000000
#endif
+}
/*
* Function to initialize the ldb dummy clock
@@ -268,23 +124,14 @@ static int ipu_clk_init(struct ipu_ctx *ctx)
*/
static int ipu_ldb_clk_init(struct ipu_ctx *ctx)
{
- struct clk *ldb_clk;
-
- ldb_clk = devm_kzalloc(ctx->dev, sizeof(*ldb_clk), GFP_KERNEL);
- if (!ldb_clk)
- return -ENOMEM;
-
- ldb_clk->name = "ldb_clk";
- ldb_clk->ctx = ctx;
- ldb_clk->rate = CFG_SYS_LDB_CLOCK;
- ldb_clk->usecount = 0;
-
- ctx->ldb_clk = ldb_clk;
+#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;
-};
-
-u32 *ipu_cpmem_base;
-u32 *ipu_dc_tmpl_reg;
+#endif
+}
/* Static functions */
@@ -320,124 +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))
-static void ipu_pixel_clk_recalc(struct clk *clk)
-{
- u32 div;
- u64 final_rate = (unsigned long long)clk->parent->rate * 16;
-
- div = __raw_readl(DI_BS_CLKGEN0(clk->id));
- debug("read BS_CLKGEN0 div:%d, final_rate:%lld, prate:%ld\n", div,
- final_rate, clk->parent->rate);
-
- clk->rate = 0;
- if (div != 0) {
- do_div(final_rate, div);
- clk->rate = final_rate;
- }
-}
-
-static unsigned long ipu_pixel_clk_round_rate(struct clk *clk,
- unsigned long rate)
-{
- u64 div, final_rate;
- u32 remainder;
- u64 parent_rate = (unsigned long long)clk->parent->rate * 16;
-
- /*
- * Calculate divider
- * Fractional part is 4 bits,
- * so simply multiply by 2^4 to get fractional part.
- */
- div = parent_rate;
- remainder = do_div(div, rate);
- /* Round the divider value */
- if (remainder > (rate / 2))
- div++;
- if (div < 0x10) /* Min DI disp clock divider is 1 */
- div = 0x10;
- if (div & ~0xFEF)
- div &= 0xFF8;
- else {
- /* Round up divider if it gets us closer to desired pix clk */
- if ((div & 0xC) == 0xC) {
- div += 0x10;
- div &= ~0xF;
- }
- }
- final_rate = parent_rate;
- do_div(final_rate, div);
-
- return final_rate;
-}
-
-static int ipu_pixel_clk_set_rate(struct clk *clk, unsigned long rate)
-{
- u64 div, parent_rate;
- u32 remainder;
-
- parent_rate = (unsigned long long)clk->parent->rate * 16;
- div = parent_rate;
- remainder = do_div(div, rate);
- /* Round the divider value */
- if (remainder > (rate / 2))
- div++;
-
- /* Round up divider if it gets us closer to desired pix clk */
- if ((div & 0xC) == 0xC) {
- div += 0x10;
- div &= ~0xF;
- }
- if (div > 0x1000)
- debug("Overflow, DI_BS_CLKGEN0 div:0x%x\n", (u32)div);
-
- __raw_writel(div, DI_BS_CLKGEN0(clk->id));
-
- /*
- * Setup pixel clock timing
- * Down time is half of period
- */
- __raw_writel((div / 16) << 16, DI_BS_CLKGEN1(clk->id));
-
- do_div(parent_rate, div);
-
- clk->rate = parent_rate;
-
- return 0;
-}
-
-static int ipu_pixel_clk_enable(struct clk *clk)
+/*
+ * 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)
{
- u32 disp_gen = __raw_readl(IPU_DISP_GEN);
- disp_gen |= clk->id ? DI1_COUNTER_RELEASE : DI0_COUNTER_RELEASE;
- __raw_writel(disp_gen, IPU_DISP_GEN);
-
+#if CONFIG_IS_ENABLED(IPU_CLK_LEGACY)
+ ctx->di_clk[id] = NULL;
return 0;
-}
-
-static void ipu_pixel_clk_disable(struct clk *clk)
-{
- u32 disp_gen = __raw_readl(IPU_DISP_GEN);
- disp_gen &= clk->id ? ~DI1_COUNTER_RELEASE : ~DI0_COUNTER_RELEASE;
- __raw_writel(disp_gen, IPU_DISP_GEN);
-}
-
-static int ipu_pixel_clk_set_parent(struct clk *clk, struct clk *parent)
-{
- u32 di_gen = __raw_readl(DI_GENERAL(clk->id));
- struct ipu_ctx *ctx = clk->ctx;
+#else
+ struct clk *clk;
- if (parent == ctx->ipu_clk)
- di_gen &= ~DI_GEN_DI_CLK_EXT;
- else if (!IS_ERR(ctx->di_clk[clk->id]) && parent == ctx->ldb_clk)
- di_gen |= DI_GEN_DI_CLK_EXT;
- else
- return -EINVAL;
+ clk = devm_clk_get(ctx->dev, id ? "di1" : "di0");
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
- __raw_writel(di_gen, DI_GENERAL(clk->id));
- ipu_pixel_clk_recalc(clk);
+ ctx->di_clk[id] = clk;
return 0;
+#endif
}
-
/*
* Function to initialize the pixel clock
*
@@ -447,26 +199,13 @@ static int ipu_pixel_clk_set_parent(struct clk *clk, struct clk *parent)
*/
static int ipu_pixel_clk_init(struct ipu_ctx *ctx, int id)
{
- struct clk *pixel_clk;
-
- pixel_clk = devm_kzalloc(ctx->dev, sizeof(*pixel_clk), GFP_KERNEL);
- if (!pixel_clk)
- return -ENOMEM;
-
- pixel_clk->name = "pixel_clk";
- pixel_clk->id = id;
- pixel_clk->ctx = ctx;
- pixel_clk->recalc = ipu_pixel_clk_recalc;
- pixel_clk->set_rate = ipu_pixel_clk_set_rate;
- pixel_clk->round_rate = ipu_pixel_clk_round_rate;
- pixel_clk->set_parent = ipu_pixel_clk_set_parent;
- pixel_clk->enable = ipu_pixel_clk_enable;
- pixel_clk->disable = ipu_pixel_clk_disable;
- pixel_clk->usecount = 0;
-
- ctx->pixel_clk[id] = pixel_clk;
+#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
+}
/*
* This function resets IPU
@@ -536,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)
@@ -584,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:
diff --git a/drivers/video/imx/ipu_disp.c b/drivers/video/imx/ipu_disp.c
index 6a337b13af6..5e78574da9b 100644
--- a/drivers/video/imx/ipu_disp.c
+++ b/drivers/video/imx/ipu_disp.c
@@ -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);
diff --git a/drivers/video/imx/mxc_ipuv3_fb.c b/drivers/video/imx/mxc_ipuv3_fb.c
index ef5d4faf3b3..3a327b9e97d 100644
--- a/drivers/video/imx/mxc_ipuv3_fb.c
+++ b/drivers/video/imx/mxc_ipuv3_fb.c
@@ -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;