[PATCH 19/29] ARM: i.MX6: Add video clocks
Sascha Hauer
s.hauer at pengutronix.de
Fri Mar 14 10:32:39 EDT 2014
This adds the IPU, LVDS and HDMI clocks. As these are many, depend
on the IPU driver being compiled in.
Signed-off-by: Sascha Hauer <s.hauer at pengutronix.de>
---
arch/arm/mach-imx/clk-imx6.c | 153 ++++++++++++++++++++++++++++-
arch/arm/mach-imx/clk.h | 6 ++
arch/arm/mach-imx/include/mach/imx6-regs.h | 3 +
3 files changed, 158 insertions(+), 4 deletions(-)
diff --git a/arch/arm/mach-imx/clk-imx6.c b/arch/arm/mach-imx/clk-imx6.c
index db16bb5..518fc00 100644
--- a/arch/arm/mach-imx/clk-imx6.c
+++ b/arch/arm/mach-imx/clk-imx6.c
@@ -19,6 +19,8 @@
#include <linux/clkdev.h>
#include <linux/err.h>
#include <mach/imx6-regs.h>
+#include <mach/revision.h>
+#include <mach/imx6.h>
#include "clk.h"
@@ -84,8 +86,10 @@ enum mx6q_clks {
usdhc4, vdo_axi, vpu_axi, cko1, pll1_sys, pll2_bus, pll3_usb_otg,
pll4_audio, pll5_video, pll8_mlb, pll7_usb_host, pll6_enet, ssi1_ipg,
ssi2_ipg, ssi3_ipg, rom, usbphy1, usbphy2, ldb_di0_div_3_5, ldb_di1_div_3_5,
- sata_ref, pcie_ref, sata_ref_100m, pcie_ref_125m, enet_ref,
- clk_max
+ sata_ref, sata_ref_100m, pcie_ref, pcie_ref_125m, enet_ref, usbphy1_gate,
+ usbphy2_gate, pll4_post_div, pll5_post_div, pll5_video_div, eim_slow,
+ spdif, cko2_sel, cko2_podf, cko2, cko, vdoa, pll4_audio_div,
+ lvds1_sel, lvds2_sel, lvds1_gate, lvds2_gate, clk_max
};
static struct clk *clks[clk_max];
@@ -171,6 +175,62 @@ static const char *cko1_sels[] = {
"pll4_audio",
};
+static const char *ipu_sels[] = {
+ "mmdc_ch0_axi_podf",
+ "pll2_pfd2_396m",
+ "pll3_120m",
+ "pll3_pfd1_540m",
+};
+
+static const char *ldb_di_sels[] = {
+ "pll5_video_div",
+ "pll2_pfd0_352m",
+ "pll2_pfd2_396m",
+ "mmdc_ch1_axi_podf",
+ "pll3_usb_otg",
+};
+
+static const char *ipu_di_pre_sels[] = {
+ "mmdc_ch0_axi",
+ "pll3_usb_otg",
+ "pll5_video_div",
+ "pll2_pfd0_352m",
+ "pll2_pfd2_396m",
+ "pll3_pfd1_540m",
+};
+
+static const char *ipu1_di0_sels[] = {
+ "ipu1_di0_pre",
+ "dummy",
+ "dummy",
+ "ldb_di0_podf",
+ "ldb_di1_podf",
+};
+
+static const char *ipu1_di1_sels[] = {
+ "ipu1_di1_pre",
+ "dummy",
+ "dummy",
+ "ldb_di0_podf",
+ "ldb_di1_podf",
+};
+
+static const char *ipu2_di0_sels[] = {
+ "ipu2_di0_pre",
+ "dummy",
+ "dummy",
+ "ldb_di0_podf",
+ "ldb_di1_podf",
+};
+
+static const char *ipu2_di1_sels[] = {
+ "ipu2_di1_pre",
+ "dummy",
+ "dummy",
+ "ldb_di0_podf",
+ "ldb_di1_podf",
+};
+
static struct clk_div_table clk_enet_ref_table[] = {
{ .val = 0, .div = 20, },
{ .val = 1, .div = 10, },
@@ -179,6 +239,86 @@ static struct clk_div_table clk_enet_ref_table[] = {
{ },
};
+static struct clk_div_table post_div_table[] = {
+ { .val = 2, .div = 1, },
+ { .val = 1, .div = 2, },
+ { .val = 0, .div = 4, },
+ { /* sentinel */ }
+};
+
+static struct clk_div_table video_div_table[] = {
+ { .val = 0, .div = 1, },
+ { .val = 1, .div = 2, },
+ { .val = 2, .div = 1, },
+ { .val = 3, .div = 4, },
+ { /* sentinel */ }
+};
+
+static void imx6_add_video_clks(void __iomem *anab, void __iomem *cb)
+{
+ clks[pll5_post_div] = imx_clk_divider_table("pll5_post_div", "pll5_video", anab + 0xa0, 19, 2, post_div_table);
+ clks[pll5_video_div] = imx_clk_divider_table("pll5_video_div", "pll5_post_div", anab + 0x170, 30, 2, video_div_table);
+
+ clks[ipu1_sel] = imx_clk_mux("ipu1_sel", cb + 0x3c, 9, 2, ipu_sels, ARRAY_SIZE(ipu_sels));
+ clks[ipu2_sel] = imx_clk_mux("ipu2_sel", cb + 0x3c, 14, 2, ipu_sels, ARRAY_SIZE(ipu_sels));
+ clks[ldb_di0_sel] = imx_clk_mux_p("ldb_di0_sel", cb + 0x2c, 9, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels));
+ clks[ldb_di1_sel] = imx_clk_mux_p("ldb_di1_sel", cb + 0x2c, 12, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels));
+ clks[ipu1_di0_pre_sel] = imx_clk_mux_p("ipu1_di0_pre_sel", cb + 0x34, 6, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels));
+ clks[ipu1_di1_pre_sel] = imx_clk_mux_p("ipu1_di1_pre_sel", cb + 0x34, 15, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels));
+ clks[ipu2_di0_pre_sel] = imx_clk_mux_p("ipu2_di0_pre_sel", cb + 0x38, 6, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels));
+ clks[ipu2_di1_pre_sel] = imx_clk_mux_p("ipu2_di1_pre_sel", cb + 0x38, 15, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels));
+ clks[ipu1_di0_sel] = imx_clk_mux_p("ipu1_di0_sel", cb + 0x34, 0, 3, ipu1_di0_sels, ARRAY_SIZE(ipu1_di0_sels));
+ clks[ipu1_di1_sel] = imx_clk_mux_p("ipu1_di1_sel", cb + 0x34, 9, 3, ipu1_di1_sels, ARRAY_SIZE(ipu1_di1_sels));
+ clks[ipu2_di0_sel] = imx_clk_mux_p("ipu2_di0_sel", cb + 0x38, 0, 3, ipu2_di0_sels, ARRAY_SIZE(ipu2_di0_sels));
+ clks[ipu2_di1_sel] = imx_clk_mux_p("ipu2_di1_sel", cb + 0x38, 9, 3, ipu2_di1_sels, ARRAY_SIZE(ipu2_di1_sels));
+
+ clks[ipu1_podf] = imx_clk_divider("ipu1_podf", "ipu1_sel", cb + 0x3c, 11, 3);
+ clks[ipu2_podf] = imx_clk_divider("ipu2_podf", "ipu2_sel", cb + 0x3c, 16, 3);
+ clks[ldb_di0_div_3_5] = imx_clk_fixed_factor("ldb_di0_div_3_5", "ldb_di0_sel", 2, 7);
+ clks[ldb_di0_podf] = imx_clk_divider("ldb_di0_podf", "ldb_di0_div_3_5", cb + 0x20, 10, 1);
+ clks[ldb_di1_div_3_5] = imx_clk_fixed_factor("ldb_di1_div_3_5", "ldb_di1_sel", 2, 7);
+ clks[ldb_di1_podf] = imx_clk_divider("ldb_di1_podf", "ldb_di1_div_3_5", cb + 0x20, 11, 1);
+ clks[ipu1_di0_pre] = imx_clk_divider("ipu1_di0_pre", "ipu1_di0_pre_sel", cb + 0x34, 3, 3);
+ clks[ipu1_di1_pre] = imx_clk_divider("ipu1_di1_pre", "ipu1_di1_pre_sel", cb + 0x34, 12, 3);
+ clks[ipu2_di0_pre] = imx_clk_divider("ipu2_di0_pre", "ipu2_di0_pre_sel", cb + 0x38, 3, 3);
+ clks[ipu2_di1_pre] = imx_clk_divider("ipu2_di1_pre", "ipu2_di1_pre_sel", cb + 0x38, 12, 3);
+
+ clkdev_add_physbase(clks[ipu1_podf], MX6_IPU1_BASE_ADDR, "bus");
+ clkdev_add_physbase(clks[ipu1_di0_sel], MX6_IPU1_BASE_ADDR, "di0");
+ clkdev_add_physbase(clks[ipu1_di1_sel], MX6_IPU1_BASE_ADDR, "di1");
+ clkdev_add_physbase(clks[ipu2_podf], MX6_IPU2_BASE_ADDR, "bus");
+ clkdev_add_physbase(clks[ipu2_di0_sel], MX6_IPU2_BASE_ADDR, "di0");
+ clkdev_add_physbase(clks[ipu2_di1_sel], MX6_IPU2_BASE_ADDR, "di1");
+
+ clkdev_add_physbase(clks[ldb_di0_sel], 0x020e0008, "di0_pll");
+ clkdev_add_physbase(clks[ldb_di1_sel], 0x020e0008, "di1_pll");
+ clkdev_add_physbase(clks[ipu1_di0_sel], 0x020e0008, "di0_sel");
+ clkdev_add_physbase(clks[ipu1_di1_sel], 0x020e0008, "di1_sel");
+ clkdev_add_physbase(clks[ipu2_di0_sel], 0x020e0008, "di2_sel");
+ clkdev_add_physbase(clks[ipu2_di1_sel], 0x020e0008, "di3_sel");
+ clkdev_add_physbase(clks[ldb_di0], 0x020e0008, "di0");
+ clkdev_add_physbase(clks[ldb_di1], 0x020e0008, "di1");
+ clkdev_add_physbase(clks[ahb], 0x00120000, "iahb");
+ clkdev_add_physbase(clks[pll3_pfd1_540m], 0x00120000, "isfr");
+
+ clk_set_parent(clks[ipu1_di0_sel], clks[ipu1_di0_pre]);
+ clk_set_parent(clks[ipu1_di1_sel], clks[ipu1_di1_pre]);
+ clk_set_parent(clks[ipu2_di0_sel], clks[ipu2_di0_pre]);
+ clk_set_parent(clks[ipu2_di1_sel], clks[ipu2_di1_pre]);
+
+ clk_set_parent(clks[ipu1_di0_pre_sel], clks[pll5_video_div]);
+ clk_set_parent(clks[ipu1_di1_pre_sel], clks[pll5_video_div]);
+ clk_set_parent(clks[ipu2_di0_pre_sel], clks[pll5_video_div]);
+ clk_set_parent(clks[ipu2_di1_pre_sel], clks[pll5_video_div]);
+
+ if ((imx_silicon_revision() != IMX_CHIP_REV_1_0) ||
+ cpu_is_mx6dl()) {
+ clk_set_parent(clks[ldb_di0_sel], clks[pll5_video_div]);
+ clk_set_parent(clks[ldb_di1_sel], clks[pll5_video_div]);
+ }
+
+}
+
static int imx6_ccm_probe(struct device_d *dev)
{
void __iomem *base, *anatop_base, *ccm_base;
@@ -281,7 +421,6 @@ static int imx6_ccm_probe(struct device_d *dev)
clks[arm] = imx_clk_busy_divider("arm", "pll1_sw", base + 0x10, 0, 3, base + 0x48, 16);
clks[ahb] = imx_clk_busy_divider("ahb", "periph", base + 0x14, 10, 3, base + 0x48, 1);
-
clkdev_add_physbase(clks[uart_serial_podf], MX6_UART1_BASE_ADDR, NULL);
clkdev_add_physbase(clks[uart_serial_podf], MX6_UART2_BASE_ADDR, NULL);
clkdev_add_physbase(clks[uart_serial_podf], MX6_UART3_BASE_ADDR, NULL);
@@ -310,10 +449,16 @@ static int imx6_ccm_probe(struct device_d *dev)
clkdev_add_physbase(clks[ipg_per], MX6_PWM3_BASE_ADDR, "per");
clkdev_add_physbase(clks[ipg_per], MX6_PWM4_BASE_ADDR, "per");
+ if (IS_ENABLED(CONFIG_DRIVER_VIDEO_IMX_IPUV3))
+ imx6_add_video_clks(anatop_base, ccm_base);
+
writel(0xffffffff, ccm_base + CCGR0);
writel(0xf0ffffff, ccm_base + CCGR1); /* gate GPU3D, GPU2D */
writel(0xffffffff, ccm_base + CCGR2);
- writel(0x3fff0000, ccm_base + CCGR3); /* gate OpenVG, LDB, IPU1, IPU2 */
+ if (IS_ENABLED(CONFIG_DRIVER_VIDEO_IMX_IPUV3))
+ writel(0xffffffff, ccm_base + CCGR3); /* gate OpenVG */
+ else
+ writel(0x3fffffff, ccm_base + CCGR3); /* gate OpenVG, LDB, IPU1, IPU2 */
writel(0xffffffff, ccm_base + CCGR4);
writel(0xffffffff, ccm_base + CCGR5);
writel(0xffff3fff, ccm_base + CCGR6); /* gate VPU */
diff --git a/arch/arm/mach-imx/clk.h b/arch/arm/mach-imx/clk.h
index d83266b..32a02db 100644
--- a/arch/arm/mach-imx/clk.h
+++ b/arch/arm/mach-imx/clk.h
@@ -27,6 +27,12 @@ static inline struct clk *imx_clk_mux(const char *name, void __iomem *reg,
return clk_mux(name, reg, shift, width, parents, num_parents, 0);
}
+static inline struct clk *imx_clk_mux_p(const char *name, void __iomem *reg,
+ u8 shift, u8 width, const char **parents, u8 num_parents)
+{
+ return clk_mux(name, reg, shift, width, parents, num_parents, CLK_SET_RATE_PARENT);
+}
+
static inline struct clk *imx_clk_gate(const char *name, const char *parent,
void __iomem *reg, u8 shift)
{
diff --git a/arch/arm/mach-imx/include/mach/imx6-regs.h b/arch/arm/mach-imx/include/mach/imx6-regs.h
index 833280a..facbe51 100644
--- a/arch/arm/mach-imx/include/mach/imx6-regs.h
+++ b/arch/arm/mach-imx/include/mach/imx6-regs.h
@@ -26,6 +26,9 @@
#define MX6_SPBA_BASE_ADDR (MX6_ATZ1_BASE_ADDR + 0x3C000)
#define MX6_VPU_BASE_ADDR (MX6_ATZ1_BASE_ADDR + 0x40000)
+#define MX6_IPU1_BASE_ADDR 0x02400000
+#define MX6_IPU2_BASE_ADDR 0x02800000
+
/* ATZ#1- On Platform */
#define MX6_AIPS1_ON_BASE_ADDR (MX6_ATZ1_BASE_ADDR + 0x7C000)
--
1.9.0
More information about the barebox
mailing list