* [PATCH v3 0/11] LVDS Display Bridge support for i.MX
@ 2013-03-28 15:23 Philipp Zabel
2013-03-28 15:23 ` [PATCH v3 02/11] ARM i.MX6q: export imx6q_revision Philipp Zabel
` (10 more replies)
0 siblings, 11 replies; 20+ messages in thread
From: Philipp Zabel @ 2013-03-28 15:23 UTC (permalink / raw)
To: linux-arm-kernel
Cc: devel, Fabio Estevam, Greg Kroah-Hartman, devicetree-discuss,
kernel, Martin Fuzzey, Sean Cross, Shawn Guo, Sascha Hauer
Hi,
the following patches add support for LVDS displays on
i.MX53 and i.MX6q boards. I have reordered the patches,
as Shawn has already applied the now first five patches.
The clock patches are needed because the LVDS serial clock
needs to be in lockstep with the IPU display interface clock
providing the pixel data. A fixed factor of 7:1 (or 3.5:1 in
dual link mode) needs to be maintained. This is achieved on
i.MX by clocking the LDB DI clock directly from a PLL, and
manually setting the 3.5/7:1 divider depending on dual/single
link mode. The IPU display interface clock is then sourced
from the divided LDB clock.
Changes since v2:
- Removed commented out code from LDB driver
- Replaced magic constants in LDB driver
- Let LDB driver select OF_VIDEOMODE
- Renamed pll[45]_test_div to pll[45]_post_div
- Renamed pll5_control3 to pll5_video_div
- Reformatted clk_div_tables
- Added missing imx_ccm_lock to audio/video post dividers
- Added new clocks to device tree bindings documentation
- Removed clocks properties from the ldb node in imx6qdl.dtsi,
those are to be set in imx6q.dtsi and imx6dl.dtsi
regards
Philipp
---
.../devicetree/bindings/clock/imx6q-clock.txt | 3 +
.../devicetree/bindings/staging/imx-drm/ldb.txt | 99 ++++
arch/arm/boot/dts/imx51.dtsi | 2 +
arch/arm/boot/dts/imx53.dtsi | 34 ++
arch/arm/boot/dts/imx6q.dtsi | 17 +
arch/arm/boot/dts/imx6qdl.dtsi | 20 +
arch/arm/mach-imx/clk-imx51-imx53.c | 19 +-
arch/arm/mach-imx/clk-imx6q.c | 55 +-
arch/arm/mach-imx/clk.h | 17 +
arch/arm/mach-imx/common.h | 1 +
arch/arm/mach-imx/mach-imx6q.c | 2 +-
drivers/staging/imx-drm/Kconfig | 8 +
drivers/staging/imx-drm/Makefile | 1 +
drivers/staging/imx-drm/imx-ldb.c | 609 +++++++++++++++++++++
14 files changed, 862 insertions(+), 25 deletions(-)
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH v3 01/11] ARM i.MX5: Move IPU clock lookups into device tree
[not found] ` <1364484215-13935-1-git-send-email-p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
@ 2013-03-28 15:23 ` Philipp Zabel
2013-03-29 11:19 ` [PATCH v3 0/11] LVDS Display Bridge support for i.MX Shawn Guo
2013-03-29 11:44 ` Shawn Guo
2 siblings, 0 replies; 20+ messages in thread
From: Philipp Zabel @ 2013-03-28 15:23 UTC (permalink / raw)
To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
Cc: devel-gWbeCf7V1WCQmaza687I9mD2FQJk+8+b, Fabio Estevam,
Greg Kroah-Hartman, devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ,
kernel-bIcnvbaLZ9MEGnE8C9+IrQ, Martin Fuzzey, Philipp Zabel,
Sean Cross, Sascha Hauer
Signed-off-by: Philipp Zabel <p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
---
arch/arm/boot/dts/imx51.dtsi | 2 ++
arch/arm/boot/dts/imx53.dtsi | 2 ++
arch/arm/mach-imx/clk-imx51-imx53.c | 7 -------
3 files changed, 4 insertions(+), 7 deletions(-)
diff --git a/arch/arm/boot/dts/imx51.dtsi b/arch/arm/boot/dts/imx51.dtsi
index fcf035b..e9480b8 100644
--- a/arch/arm/boot/dts/imx51.dtsi
+++ b/arch/arm/boot/dts/imx51.dtsi
@@ -67,6 +67,8 @@
compatible = "fsl,imx51-ipu";
reg = <0x40000000 0x20000000>;
interrupts = <11 10>;
+ clocks = <&clks 59>, <&clks 110>, <&clks 61>;
+ clock-names = "bus", "di0", "di1";
};
aips@70000000 { /* AIPS1 */
diff --git a/arch/arm/boot/dts/imx53.dtsi b/arch/arm/boot/dts/imx53.dtsi
index d05aa21..7a6f5a8 100644
--- a/arch/arm/boot/dts/imx53.dtsi
+++ b/arch/arm/boot/dts/imx53.dtsi
@@ -72,6 +72,8 @@
compatible = "fsl,imx53-ipu";
reg = <0x18000000 0x080000000>;
interrupts = <11 10>;
+ clocks = <&clks 59>, <&clks 110>, <&clks 61>;
+ clock-names = "bus", "di0", "di1";
};
aips@50000000 { /* AIPS1 */
diff --git a/arch/arm/mach-imx/clk-imx51-imx53.c b/arch/arm/mach-imx/clk-imx51-imx53.c
index 0f39f8c..d22ee6a 100644
--- a/arch/arm/mach-imx/clk-imx51-imx53.c
+++ b/arch/arm/mach-imx/clk-imx51-imx53.c
@@ -362,9 +362,6 @@ int __init mx51_clocks_init(unsigned long rate_ckil, unsigned long rate_osc,
clk_register_clkdev(clk[mx51_mipi], "mipi_hsp", NULL);
clk_register_clkdev(clk[vpu_gate], NULL, "imx51-vpu.0");
clk_register_clkdev(clk[fec_gate], NULL, "imx27-fec.0");
- clk_register_clkdev(clk[ipu_gate], "bus", "40000000.ipu");
- clk_register_clkdev(clk[ipu_di0_gate], "di0", "40000000.ipu");
- clk_register_clkdev(clk[ipu_di1_gate], "di1", "40000000.ipu");
clk_register_clkdev(clk[usb_phy_gate], "phy", "mxc-ehci.0");
clk_register_clkdev(clk[esdhc1_ipg_gate], "ipg", "sdhci-esdhc-imx51.0");
clk_register_clkdev(clk[dummy], "ahb", "sdhci-esdhc-imx51.0");
@@ -471,10 +468,6 @@ int __init mx53_clocks_init(unsigned long rate_ckil, unsigned long rate_osc,
clk_register_clkdev(clk[vpu_gate], NULL, "imx53-vpu.0");
clk_register_clkdev(clk[i2c3_gate], NULL, "imx21-i2c.2");
clk_register_clkdev(clk[fec_gate], NULL, "imx25-fec.0");
- clk_register_clkdev(clk[ipu_gate], "bus", "18000000.ipu");
- clk_register_clkdev(clk[ipu_di0_gate], "di0", "18000000.ipu");
- clk_register_clkdev(clk[ipu_di1_gate], "di1", "18000000.ipu");
- clk_register_clkdev(clk[ipu_gate], "hsp", "18000000.ipu");
clk_register_clkdev(clk[usb_phy1_gate], "usb_phy1", "mxc-ehci.0");
clk_register_clkdev(clk[esdhc1_ipg_gate], "ipg", "sdhci-esdhc-imx53.0");
clk_register_clkdev(clk[dummy], "ahb", "sdhci-esdhc-imx53.0");
--
1.8.2.rc2
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v3 02/11] ARM i.MX6q: export imx6q_revision
2013-03-28 15:23 [PATCH v3 0/11] LVDS Display Bridge support for i.MX Philipp Zabel
@ 2013-03-28 15:23 ` Philipp Zabel
2013-03-28 15:23 ` [PATCH v3 03/11] ARM i.MX: Add imx_clk_divider_flags and imx_clk_mux_flags Philipp Zabel
` (9 subsequent siblings)
10 siblings, 0 replies; 20+ messages in thread
From: Philipp Zabel @ 2013-03-28 15:23 UTC (permalink / raw)
To: linux-arm-kernel
Cc: devel, Fabio Estevam, Greg Kroah-Hartman, devicetree-discuss,
kernel, Martin Fuzzey, Philipp Zabel, Sean Cross, Shawn Guo,
Sascha Hauer
So it can be used in clk-imx6q.c for revision dependent clock tree setup.
Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
---
arch/arm/mach-imx/common.h | 1 +
arch/arm/mach-imx/mach-imx6q.c | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/arch/arm/mach-imx/common.h b/arch/arm/mach-imx/common.h
index 5a800bf..6c909d1 100644
--- a/arch/arm/mach-imx/common.h
+++ b/arch/arm/mach-imx/common.h
@@ -74,6 +74,7 @@ extern void mxc_set_cpu_type(unsigned int type);
extern void mxc_restart(char, const char *);
extern void mxc_arch_reset_init(void __iomem *);
extern int mx53_revision(void);
+extern int imx6q_revision(void);
extern int mx53_display_revision(void);
extern void imx_set_aips(void __iomem *);
extern int mxc_device_init(void);
diff --git a/arch/arm/mach-imx/mach-imx6q.c b/arch/arm/mach-imx/mach-imx6q.c
index 9ffd103..ca10acc 100644
--- a/arch/arm/mach-imx/mach-imx6q.c
+++ b/arch/arm/mach-imx/mach-imx6q.c
@@ -41,7 +41,7 @@
#define IMX6Q_ANALOG_DIGPROG 0x260
-static int imx6q_revision(void)
+int imx6q_revision(void)
{
struct device_node *np;
void __iomem *base;
--
1.8.2.rc2
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v3 03/11] ARM i.MX: Add imx_clk_divider_flags and imx_clk_mux_flags
2013-03-28 15:23 [PATCH v3 0/11] LVDS Display Bridge support for i.MX Philipp Zabel
2013-03-28 15:23 ` [PATCH v3 02/11] ARM i.MX6q: export imx6q_revision Philipp Zabel
@ 2013-03-28 15:23 ` Philipp Zabel
2013-03-28 15:23 ` [PATCH v3 04/11] ARM i.MX53: fix ldb di divider and selector clocks Philipp Zabel
` (8 subsequent siblings)
10 siblings, 0 replies; 20+ messages in thread
From: Philipp Zabel @ 2013-03-28 15:23 UTC (permalink / raw)
To: linux-arm-kernel
Cc: devel, Fabio Estevam, Greg Kroah-Hartman, devicetree-discuss,
kernel, Martin Fuzzey, Philipp Zabel, Sean Cross, Shawn Guo,
Sascha Hauer
The default is for dividers to set CLK_SET_PARENT_RATE and for muxes to
not set that flag. In the LDB clock tree, we need the opposite, so add
functions to create divider and mux clocks with configurable flags.
Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
---
arch/arm/mach-imx/clk.h | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/arch/arm/mach-imx/clk.h b/arch/arm/mach-imx/clk.h
index 9d1f3b9..d9d9d9c 100644
--- a/arch/arm/mach-imx/clk.h
+++ b/arch/arm/mach-imx/clk.h
@@ -59,6 +59,14 @@ static inline struct clk *imx_clk_divider(const char *name, const char *parent,
reg, shift, width, 0, &imx_ccm_lock);
}
+static inline struct clk *imx_clk_divider_flags(const char *name,
+ const char *parent, void __iomem *reg, u8 shift, u8 width,
+ unsigned long flags)
+{
+ return clk_register_divider(NULL, name, parent, flags,
+ reg, shift, width, 0, &imx_ccm_lock);
+}
+
static inline struct clk *imx_clk_gate(const char *name, const char *parent,
void __iomem *reg, u8 shift)
{
@@ -73,6 +81,15 @@ static inline struct clk *imx_clk_mux(const char *name, void __iomem *reg,
width, 0, &imx_ccm_lock);
}
+static inline struct clk *imx_clk_mux_flags(const char *name,
+ void __iomem *reg, u8 shift, u8 width, const char **parents,
+ int num_parents, unsigned long flags)
+{
+ return clk_register_mux(NULL, name, parents, num_parents,
+ flags, reg, shift, width, 0,
+ &imx_ccm_lock);
+}
+
static inline struct clk *imx_clk_fixed_factor(const char *name,
const char *parent, unsigned int mult, unsigned int div)
{
--
1.8.2.rc2
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v3 04/11] ARM i.MX53: fix ldb di divider and selector clocks
2013-03-28 15:23 [PATCH v3 0/11] LVDS Display Bridge support for i.MX Philipp Zabel
2013-03-28 15:23 ` [PATCH v3 02/11] ARM i.MX6q: export imx6q_revision Philipp Zabel
2013-03-28 15:23 ` [PATCH v3 03/11] ARM i.MX: Add imx_clk_divider_flags and imx_clk_mux_flags Philipp Zabel
@ 2013-03-28 15:23 ` Philipp Zabel
2013-03-28 15:23 ` [PATCH v3 05/11] ARM i.MX6q: " Philipp Zabel
` (7 subsequent siblings)
10 siblings, 0 replies; 20+ messages in thread
From: Philipp Zabel @ 2013-03-28 15:23 UTC (permalink / raw)
To: linux-arm-kernel
Cc: devel, Fabio Estevam, Greg Kroah-Hartman, devicetree-discuss,
kernel, Martin Fuzzey, Philipp Zabel, Sean Cross, Shawn Guo,
Sascha Hauer
Use imx_clk_mux_flags and imx_clk_divider_flags to set the appropriate
flags for the LDB display interface divider and selector clocks.
Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
---
arch/arm/mach-imx/clk-imx51-imx53.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/arch/arm/mach-imx/clk-imx51-imx53.c b/arch/arm/mach-imx/clk-imx51-imx53.c
index d22ee6a..ed08aba 100644
--- a/arch/arm/mach-imx/clk-imx51-imx53.c
+++ b/arch/arm/mach-imx/clk-imx51-imx53.c
@@ -420,15 +420,15 @@ int __init mx53_clocks_init(unsigned long rate_ckil, unsigned long rate_osc,
clk[pll3_sw] = imx_clk_pllv2("pll3_sw", "osc", MX53_DPLL3_BASE);
clk[pll4_sw] = imx_clk_pllv2("pll4_sw", "osc", MX53_DPLL4_BASE);
- clk[ldb_di1_sel] = imx_clk_mux("ldb_di1_sel", MXC_CCM_CSCMR2, 9, 1,
- mx53_ldb_di1_sel, ARRAY_SIZE(mx53_ldb_di1_sel));
clk[ldb_di1_div_3_5] = imx_clk_fixed_factor("ldb_di1_div_3_5", "ldb_di1_sel", 2, 7);
- clk[ldb_di1_div] = imx_clk_divider("ldb_di1_div", "ldb_di1_div_3_5", MXC_CCM_CSCMR2, 11, 1);
+ clk[ldb_di1_div] = imx_clk_divider_flags("ldb_di1_div", "ldb_di1_div_3_5", MXC_CCM_CSCMR2, 11, 1, 0);
+ clk[ldb_di1_sel] = imx_clk_mux_flags("ldb_di1_sel", MXC_CCM_CSCMR2, 9, 1,
+ mx53_ldb_di1_sel, ARRAY_SIZE(mx53_ldb_di1_sel), CLK_SET_RATE_PARENT);
clk[di_pll4_podf] = imx_clk_divider("di_pll4_podf", "pll4_sw", MXC_CCM_CDCDR, 16, 3);
- clk[ldb_di0_sel] = imx_clk_mux("ldb_di0_sel", MXC_CCM_CSCMR2, 8, 1,
- mx53_ldb_di0_sel, ARRAY_SIZE(mx53_ldb_di0_sel));
clk[ldb_di0_div_3_5] = imx_clk_fixed_factor("ldb_di0_div_3_5", "ldb_di0_sel", 2, 7);
- clk[ldb_di0_div] = imx_clk_divider("ldb_di0_div", "ldb_di0_div_3_5", MXC_CCM_CSCMR2, 10, 1);
+ clk[ldb_di0_div] = imx_clk_divider_flags("ldb_di0_div", "ldb_di0_div_3_5", MXC_CCM_CSCMR2, 10, 1, 0);
+ clk[ldb_di0_sel] = imx_clk_mux_flags("ldb_di0_sel", MXC_CCM_CSCMR2, 8, 1,
+ mx53_ldb_di0_sel, ARRAY_SIZE(mx53_ldb_di0_sel), CLK_SET_RATE_PARENT);
clk[ldb_di0_gate] = imx_clk_gate2("ldb_di0_gate", "ldb_di0_div", MXC_CCM_CCGR6, 28);
clk[ldb_di1_gate] = imx_clk_gate2("ldb_di1_gate", "ldb_di1_div", MXC_CCM_CCGR6, 30);
clk[ipu_di0_sel] = imx_clk_mux("ipu_di0_sel", MXC_CCM_CSCMR2, 26, 3,
--
1.8.2.rc2
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v3 05/11] ARM i.MX6q: fix ldb di divider and selector clocks
2013-03-28 15:23 [PATCH v3 0/11] LVDS Display Bridge support for i.MX Philipp Zabel
` (2 preceding siblings ...)
2013-03-28 15:23 ` [PATCH v3 04/11] ARM i.MX53: fix ldb di divider and selector clocks Philipp Zabel
@ 2013-03-28 15:23 ` Philipp Zabel
2013-03-28 15:23 ` [PATCH v3 06/11] staging: drm/imx: Add LDB support Philipp Zabel
` (6 subsequent siblings)
10 siblings, 0 replies; 20+ messages in thread
From: Philipp Zabel @ 2013-03-28 15:23 UTC (permalink / raw)
To: linux-arm-kernel
Cc: devel, Fabio Estevam, Greg Kroah-Hartman, devicetree-discuss,
kernel, Martin Fuzzey, Philipp Zabel, Sean Cross, Shawn Guo,
Sascha Hauer
Use imx_clk_mux_flags and imx_clk_divider_flags to set the appropriate
flags for the LDB display interface divider and selector clocks.
Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
---
arch/arm/mach-imx/clk-imx6q.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/arch/arm/mach-imx/clk-imx6q.c b/arch/arm/mach-imx/clk-imx6q.c
index 2f9ff93..4b0cab4 100644
--- a/arch/arm/mach-imx/clk-imx6q.c
+++ b/arch/arm/mach-imx/clk-imx6q.c
@@ -283,8 +283,8 @@ int __init mx6q_clocks_init(void)
clk[gpu3d_shader_sel] = imx_clk_mux("gpu3d_shader_sel", base + 0x18, 8, 2, gpu3d_shader_sels, ARRAY_SIZE(gpu3d_shader_sels));
clk[ipu1_sel] = imx_clk_mux("ipu1_sel", base + 0x3c, 9, 2, ipu_sels, ARRAY_SIZE(ipu_sels));
clk[ipu2_sel] = imx_clk_mux("ipu2_sel", base + 0x3c, 14, 2, ipu_sels, ARRAY_SIZE(ipu_sels));
- clk[ldb_di0_sel] = imx_clk_mux("ldb_di0_sel", base + 0x2c, 9, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels));
- clk[ldb_di1_sel] = imx_clk_mux("ldb_di1_sel", base + 0x2c, 12, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels));
+ clk[ldb_di0_sel] = imx_clk_mux_flags("ldb_di0_sel", base + 0x2c, 9, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels), CLK_SET_RATE_PARENT);
+ clk[ldb_di1_sel] = imx_clk_mux_flags("ldb_di1_sel", base + 0x2c, 12, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels), CLK_SET_RATE_PARENT);
clk[ipu1_di0_pre_sel] = imx_clk_mux("ipu1_di0_pre_sel", base + 0x34, 6, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels));
clk[ipu1_di1_pre_sel] = imx_clk_mux("ipu1_di1_pre_sel", base + 0x34, 15, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels));
clk[ipu2_di0_pre_sel] = imx_clk_mux("ipu2_di0_pre_sel", base + 0x38, 6, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels));
@@ -332,9 +332,9 @@ int __init mx6q_clocks_init(void)
clk[ipu1_podf] = imx_clk_divider("ipu1_podf", "ipu1_sel", base + 0x3c, 11, 3);
clk[ipu2_podf] = imx_clk_divider("ipu2_podf", "ipu2_sel", base + 0x3c, 16, 3);
clk[ldb_di0_div_3_5] = imx_clk_fixed_factor("ldb_di0_div_3_5", "ldb_di0_sel", 2, 7);
- clk[ldb_di0_podf] = imx_clk_divider("ldb_di0_podf", "ldb_di0_div_3_5", base + 0x20, 10, 1);
+ clk[ldb_di0_podf] = imx_clk_divider_flags("ldb_di0_podf", "ldb_di0_div_3_5", base + 0x20, 10, 1, 0);
clk[ldb_di1_div_3_5] = imx_clk_fixed_factor("ldb_di1_div_3_5", "ldb_di1_sel", 2, 7);
- clk[ldb_di1_podf] = imx_clk_divider("ldb_di1_podf", "ldb_di1_div_3_5", base + 0x20, 11, 1);
+ clk[ldb_di1_podf] = imx_clk_divider_flags("ldb_di1_podf", "ldb_di1_div_3_5", base + 0x20, 11, 1, 0);
clk[ipu1_di0_pre] = imx_clk_divider("ipu1_di0_pre", "ipu1_di0_pre_sel", base + 0x34, 3, 3);
clk[ipu1_di1_pre] = imx_clk_divider("ipu1_di1_pre", "ipu1_di1_pre_sel", base + 0x34, 12, 3);
clk[ipu2_di0_pre] = imx_clk_divider("ipu2_di0_pre", "ipu2_di0_pre_sel", base + 0x38, 3, 3);
--
1.8.2.rc2
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v3 06/11] staging: drm/imx: Add LDB support
2013-03-28 15:23 [PATCH v3 0/11] LVDS Display Bridge support for i.MX Philipp Zabel
` (3 preceding siblings ...)
2013-03-28 15:23 ` [PATCH v3 05/11] ARM i.MX6q: " Philipp Zabel
@ 2013-03-28 15:23 ` Philipp Zabel
[not found] ` <1364484215-13935-7-git-send-email-p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
2013-06-07 7:40 ` Markus Niebel
2013-03-28 15:23 ` [PATCH v3 07/11] ARM i.MX6q: Add audio/video PLL post dividers for i.MX6q rev 1.1 Philipp Zabel
` (5 subsequent siblings)
10 siblings, 2 replies; 20+ messages in thread
From: Philipp Zabel @ 2013-03-28 15:23 UTC (permalink / raw)
To: linux-arm-kernel
Cc: devel, Fabio Estevam, Greg Kroah-Hartman, devicetree-discuss,
kernel, Martin Fuzzey, Philipp Zabel, Sean Cross, Shawn Guo,
Sascha Hauer
From: Sascha Hauer <s.hauer@pengutronix.de>
This adds support for the LVDS Display Bridge contained
in i.MX5 and i.MX6 SoCs.
Bit mapping, data width, and video timings are configurable
via device tree. Dual-channel mode is supported for a single
high-resolution source.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
---
Changes since v2:
- Removed commented out code
- Replaced magic constants
- Select OF_VIDEOMODE
---
.../devicetree/bindings/staging/imx-drm/ldb.txt | 99 ++++
drivers/staging/imx-drm/Kconfig | 8 +
drivers/staging/imx-drm/Makefile | 1 +
drivers/staging/imx-drm/imx-ldb.c | 609 +++++++++++++++++++++
4 files changed, 717 insertions(+)
create mode 100644 Documentation/devicetree/bindings/staging/imx-drm/ldb.txt
create mode 100644 drivers/staging/imx-drm/imx-ldb.c
diff --git a/Documentation/devicetree/bindings/staging/imx-drm/ldb.txt b/Documentation/devicetree/bindings/staging/imx-drm/ldb.txt
new file mode 100644
index 0000000..ed93778
--- /dev/null
+++ b/Documentation/devicetree/bindings/staging/imx-drm/ldb.txt
@@ -0,0 +1,99 @@
+Device-Tree bindings for LVDS Display Bridge (ldb)
+
+LVDS Display Bridge
+===================
+
+The LVDS Display Bridge device tree node contains up to two lvds-channel
+nodes describing each of the two LVDS encoder channels of the bridge.
+
+Required properties:
+ - #address-cells : should be <1>
+ - #size-cells : should be <0>
+ - compatible : should be "fsl,imx53-ldb" or "fsl,imx6q-ldb".
+ Both LDB versions are similar, but i.MX6 has an additional
+ multiplexer in the front to select any of the four IPU display
+ interfaces as input for each LVDS channel.
+ - gpr : should be <&gpr> on i.MX53 and i.MX6q.
+ The phandle points to the iomuxc-gpr region containing the LVDS
+ control register.
+- clocks, clock-names : phandles to the LDB divider and selector clocks and to
+ the display interface selector clocks, as described in
+ Documentation/devicetree/bindings/clock/clock-bindings.txt
+ The following clocks are expected on i.MX53:
+ "di0_pll" - LDB LVDS channel 0 mux
+ "di1_pll" - LDB LVDS channel 1 mux
+ "di0" - LDB LVDS channel 0 gate
+ "di1" - LDB LVDS channel 1 gate
+ "di0_sel" - IPU1 DI0 mux
+ "di1_sel" - IPU1 DI1 mux
+ On i.MX6q the following additional clocks are needed:
+ "di2_sel" - IPU2 DI0 mux
+ "di3_sel" - IPU2 DI1 mux
+ The needed clock numbers for each are documented in
+ Documentation/devicetree/bindings/clock/imx5-clock.txt, and in
+ Documentation/devicetree/bindings/clock/imx6q-clock.txt.
+
+Optional properties:
+ - pinctrl-names : should be "default" on i.MX53, not used on i.MX6q
+ - pinctrl-0 : a phandle pointing to LVDS pin settings on i.MX53,
+ not used on i.MX6q
+ - fsl,dual-channel : boolean. if it exists, only LVDS channel 0 should
+ be configured - one input will be distributed on both outputs in dual
+ channel mode
+
+LVDS Channel
+============
+
+Each LVDS Channel has to contain a display-timings node that describes the
+video timings for the connected LVDS display. For detailed information, also
+have a look at Documentation/devicetree/bindings/video/display-timing.txt.
+
+Required properties:
+ - reg : should be <0> or <1>
+ - crtcs : a list of phandles with index pointing to the IPU display interfaces
+ that can be used as video source for this channel.
+ - fsl,data-mapping : should be "spwg" or "jeida"
+ This describes how the color bits are laid out in the
+ serialized LVDS signal.
+ - fsl,data-width : should be <18> or <24>
+
+example:
+
+gpr: iomuxc-gpr@53fa8000 {
+ /* ... */
+};
+
+ldb: ldb@53fa8008 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,imx53-ldb";
+ gpr = <&gpr>;
+ clocks = <&clks 122>, <&clks 120>,
+ <&clks 115>, <&clks 116>,
+ <&clks 123>, <&clks 85>;
+ clock-names = "di0_pll", "di1_pll",
+ "di0_sel", "di1_sel",
+ "di0", "di1";
+
+ lvds-channel@0 {
+ reg = <0>;
+ crtcs = <&ipu 0>;
+ fsl,data-mapping = "spwg";
+ fsl,data-width = <24>;
+
+ display-timings {
+ /* ... */
+ };
+ };
+
+ lvds-channel@1 {
+ reg = <1>;
+ crtcs = <&ipu 1>;
+ fsl,data-mapping = "spwg";
+ fsl,data-width = <24>;
+
+ display-timings {
+ /* ... */
+ };
+ };
+};
diff --git a/drivers/staging/imx-drm/Kconfig b/drivers/staging/imx-drm/Kconfig
index be7e2e3..3b1355c 100644
--- a/drivers/staging/imx-drm/Kconfig
+++ b/drivers/staging/imx-drm/Kconfig
@@ -20,6 +20,14 @@ config DRM_IMX_PARALLEL_DISPLAY
tristate "Support for parallel displays"
depends on DRM_IMX
+config DRM_IMX_LDB
+ tristate "Support for LVDS displays"
+ depends on DRM_IMX
+ select OF_VIDEOMODE
+ help
+ Choose this to enable the internal LVDS Display Bridge (LDB)
+ found on i.MX53 and i.MX6 processors.
+
config DRM_IMX_IPUV3_CORE
tristate "IPUv3 core support"
depends on DRM_IMX
diff --git a/drivers/staging/imx-drm/Makefile b/drivers/staging/imx-drm/Makefile
index 83a9056..9bfac13 100644
--- a/drivers/staging/imx-drm/Makefile
+++ b/drivers/staging/imx-drm/Makefile
@@ -4,6 +4,7 @@ imxdrm-objs := imx-drm-core.o imx-fb.o
obj-$(CONFIG_DRM_IMX) += imxdrm.o
obj-$(CONFIG_DRM_IMX_PARALLEL_DISPLAY) += parallel-display.o
+obj-$(CONFIG_DRM_IMX_LDB) += imx-ldb.o
obj-$(CONFIG_DRM_IMX_FB_HELPER) += imx-fbdev.o
obj-$(CONFIG_DRM_IMX_IPUV3_CORE) += ipu-v3/
obj-$(CONFIG_DRM_IMX_IPUV3) += ipuv3-crtc.o
diff --git a/drivers/staging/imx-drm/imx-ldb.c b/drivers/staging/imx-drm/imx-ldb.c
new file mode 100644
index 0000000..75607b3
--- /dev/null
+++ b/drivers/staging/imx-drm/imx-ldb.c
@@ -0,0 +1,609 @@
+/*
+ * i.MX drm driver - LVDS display bridge
+ *
+ * Copyright (C) 2012 Sascha Hauer, Pengutronix
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <drm/drmP.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <video/of_videomode.h>
+#include <linux/regmap.h>
+#include <linux/videodev2.h>
+
+#include "imx-drm.h"
+
+#define DRIVER_NAME "imx-ldb"
+
+#define LDB_CH0_MODE_EN_TO_DI0 (1 << 0)
+#define LDB_CH0_MODE_EN_TO_DI1 (3 << 0)
+#define LDB_CH0_MODE_EN_MASK (3 << 0)
+#define LDB_CH1_MODE_EN_TO_DI0 (1 << 2)
+#define LDB_CH1_MODE_EN_TO_DI1 (3 << 2)
+#define LDB_CH1_MODE_EN_MASK (3 << 2)
+#define LDB_SPLIT_MODE_EN (1 << 4)
+#define LDB_DATA_WIDTH_CH0_24 (1 << 5)
+#define LDB_BIT_MAP_CH0_JEIDA (1 << 6)
+#define LDB_DATA_WIDTH_CH1_24 (1 << 7)
+#define LDB_BIT_MAP_CH1_JEIDA (1 << 8)
+#define LDB_DI0_VS_POL_ACT_LOW (1 << 9)
+#define LDB_DI1_VS_POL_ACT_LOW (1 << 10)
+#define LDB_BGREF_RMODE_INT (1 << 15)
+
+#define con_to_imx_ldb_ch(x) container_of(x, struct imx_ldb_channel, connector)
+#define enc_to_imx_ldb_ch(x) container_of(x, struct imx_ldb_channel, encoder)
+
+struct imx_ldb;
+
+struct imx_ldb_channel {
+ struct imx_ldb *ldb;
+ struct drm_connector connector;
+ struct imx_drm_connector *imx_drm_connector;
+ struct drm_encoder encoder;
+ struct imx_drm_encoder *imx_drm_encoder;
+ int chno;
+ void *edid;
+ int edid_len;
+ struct drm_display_mode mode;
+ int mode_valid;
+};
+
+struct bus_mux {
+ int reg;
+ int shift;
+ int mask;
+};
+
+struct imx_ldb {
+ struct regmap *regmap;
+ struct device *dev;
+ struct imx_ldb_channel channel[2];
+ struct clk *clk[2]; /* our own clock */
+ struct clk *clk_sel[4]; /* parent of display clock */
+ struct clk *clk_pll[2]; /* upstream clock we can adjust */
+ u32 ldb_ctrl;
+ const struct bus_mux *lvds_mux;
+};
+
+static enum drm_connector_status imx_ldb_connector_detect(
+ struct drm_connector *connector, bool force)
+{
+ return connector_status_connected;
+}
+
+static void imx_ldb_connector_destroy(struct drm_connector *connector)
+{
+ /* do not free here */
+}
+
+static int imx_ldb_connector_get_modes(struct drm_connector *connector)
+{
+ struct imx_ldb_channel *imx_ldb_ch = con_to_imx_ldb_ch(connector);
+ int num_modes = 0;
+
+ if (imx_ldb_ch->edid) {
+ drm_mode_connector_update_edid_property(connector,
+ imx_ldb_ch->edid);
+ num_modes = drm_add_edid_modes(connector, imx_ldb_ch->edid);
+ }
+
+ if (imx_ldb_ch->mode_valid) {
+ struct drm_display_mode *mode;
+
+ mode = drm_mode_create(connector->dev);
+ drm_mode_copy(mode, &imx_ldb_ch->mode);
+ mode->type |= DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+ drm_mode_probed_add(connector, mode);
+ num_modes++;
+ }
+
+ return num_modes;
+}
+
+static int imx_ldb_connector_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ return 0;
+}
+
+static struct drm_encoder *imx_ldb_connector_best_encoder(
+ struct drm_connector *connector)
+{
+ struct imx_ldb_channel *imx_ldb_ch = con_to_imx_ldb_ch(connector);
+
+ return &imx_ldb_ch->encoder;
+}
+
+static void imx_ldb_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+}
+
+static bool imx_ldb_encoder_mode_fixup(struct drm_encoder *encoder,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ return true;
+}
+
+static void imx_ldb_set_clock(struct imx_ldb *ldb, int mux, int chno,
+ unsigned long serial_clk, unsigned long di_clk)
+{
+ int ret;
+
+ dev_dbg(ldb->dev, "%s: now: %ld want: %ld\n", __func__,
+ clk_get_rate(ldb->clk_pll[chno]), serial_clk);
+ clk_set_rate(ldb->clk_pll[chno], serial_clk);
+
+ dev_dbg(ldb->dev, "%s after: %ld\n", __func__,
+ clk_get_rate(ldb->clk_pll[chno]));
+
+ dev_dbg(ldb->dev, "%s: now: %ld want: %ld\n", __func__,
+ clk_get_rate(ldb->clk[chno]),
+ (long int)di_clk);
+ clk_set_rate(ldb->clk[chno], di_clk);
+
+ dev_dbg(ldb->dev, "%s after: %ld\n", __func__,
+ clk_get_rate(ldb->clk[chno]));
+
+ /* set display clock mux to LDB input clock */
+ ret = clk_set_parent(ldb->clk_sel[mux], ldb->clk[chno]);
+ if (ret) {
+ dev_err(ldb->dev, "unable to set di%d parent clock to ldb_di%d\n", mux, chno);
+ }
+}
+
+static void imx_ldb_encoder_prepare(struct drm_encoder *encoder)
+{
+ struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder);
+ struct imx_ldb *ldb = imx_ldb_ch->ldb;
+ struct drm_display_mode *mode = &encoder->crtc->mode;
+ unsigned long serial_clk;
+ unsigned long di_clk = mode->clock * 1000;
+ int mux = imx_drm_encoder_get_mux_id(imx_ldb_ch->imx_drm_encoder,
+ encoder->crtc);
+
+ if (ldb->ldb_ctrl & LDB_SPLIT_MODE_EN) {
+ /* dual channel LVDS mode */
+ serial_clk = 3500UL * mode->clock;
+ imx_ldb_set_clock(ldb, mux, 0, serial_clk, di_clk);
+ imx_ldb_set_clock(ldb, mux, 1, serial_clk, di_clk);
+ } else {
+ serial_clk = 7000UL * mode->clock;
+ imx_ldb_set_clock(ldb, mux, imx_ldb_ch->chno, serial_clk, di_clk);
+ }
+
+ imx_drm_crtc_panel_format(encoder->crtc, DRM_MODE_ENCODER_LVDS,
+ V4L2_PIX_FMT_RGB24);
+}
+
+static void imx_ldb_encoder_commit(struct drm_encoder *encoder)
+{
+ struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder);
+ struct imx_ldb *ldb = imx_ldb_ch->ldb;
+ int dual = ldb->ldb_ctrl & LDB_SPLIT_MODE_EN;
+ int mux = imx_drm_encoder_get_mux_id(imx_ldb_ch->imx_drm_encoder,
+ encoder->crtc);
+
+ if (dual) {
+ clk_prepare_enable(ldb->clk[0]);
+ clk_prepare_enable(ldb->clk[1]);
+ }
+
+ if (imx_ldb_ch == &ldb->channel[0] || dual) {
+ ldb->ldb_ctrl &= ~LDB_CH0_MODE_EN_MASK;
+ if (mux == 0 || ldb->lvds_mux)
+ ldb->ldb_ctrl |= LDB_CH0_MODE_EN_TO_DI0;
+ else if (mux == 1)
+ ldb->ldb_ctrl |= LDB_CH0_MODE_EN_TO_DI1;
+ }
+ if (imx_ldb_ch == &ldb->channel[1] || dual) {
+ ldb->ldb_ctrl &= ~LDB_CH1_MODE_EN_MASK;
+ if (mux == 1 || ldb->lvds_mux)
+ ldb->ldb_ctrl |= LDB_CH1_MODE_EN_TO_DI1;
+ else if (mux == 0)
+ ldb->ldb_ctrl |= LDB_CH1_MODE_EN_TO_DI0;
+ }
+
+ if (ldb->lvds_mux) {
+ const struct bus_mux *lvds_mux = NULL;
+
+ if (imx_ldb_ch == &ldb->channel[0])
+ lvds_mux = &ldb->lvds_mux[0];
+ else if (imx_ldb_ch == &ldb->channel[1])
+ lvds_mux = &ldb->lvds_mux[1];
+
+ regmap_update_bits(ldb->regmap, lvds_mux->reg, lvds_mux->mask,
+ mux << lvds_mux->shift);
+ }
+
+ regmap_write(ldb->regmap, IOMUXC_GPR2, ldb->ldb_ctrl);
+}
+
+static void imx_ldb_encoder_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder);
+ struct imx_ldb *ldb = imx_ldb_ch->ldb;
+ int dual = ldb->ldb_ctrl & LDB_SPLIT_MODE_EN;
+
+ if (mode->clock > 170000) {
+ dev_warn(ldb->dev,
+ "%s: mode exceeds 170 MHz pixel clock\n", __func__);
+ }
+ if (mode->clock > 85000 && !dual) {
+ dev_warn(ldb->dev,
+ "%s: mode exceeds 85 MHz pixel clock\n", __func__);
+ }
+
+ /* FIXME - assumes straight connections DI0 --> CH0, DI1 --> CH1 */
+ if (imx_ldb_ch == &ldb->channel[0]) {
+ if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+ ldb->ldb_ctrl |= LDB_DI0_VS_POL_ACT_LOW;
+ else if (mode->flags & DRM_MODE_FLAG_PVSYNC)
+ ldb->ldb_ctrl &= ~LDB_DI0_VS_POL_ACT_LOW;
+ }
+ if (imx_ldb_ch == &ldb->channel[1]) {
+ if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+ ldb->ldb_ctrl |= LDB_DI1_VS_POL_ACT_LOW;
+ else if (mode->flags & DRM_MODE_FLAG_PVSYNC)
+ ldb->ldb_ctrl &= ~LDB_DI1_VS_POL_ACT_LOW;
+ }
+}
+
+static void imx_ldb_encoder_disable(struct drm_encoder *encoder)
+{
+ struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder);
+ struct imx_ldb *ldb = imx_ldb_ch->ldb;
+
+ /*
+ * imx_ldb_encoder_disable is called by
+ * drm_helper_disable_unused_functions without
+ * the encoder being enabled before.
+ */
+ if (imx_ldb_ch == &ldb->channel[0] &&
+ (ldb->ldb_ctrl & LDB_CH0_MODE_EN_MASK) == 0)
+ return;
+ else if (imx_ldb_ch == &ldb->channel[1] &&
+ (ldb->ldb_ctrl & LDB_CH1_MODE_EN_MASK) == 0)
+ return;
+
+ if (imx_ldb_ch == &ldb->channel[0])
+ ldb->ldb_ctrl &= ~LDB_CH0_MODE_EN_MASK;
+ else if (imx_ldb_ch == &ldb->channel[1])
+ ldb->ldb_ctrl &= ~LDB_CH1_MODE_EN_MASK;
+
+ regmap_write(ldb->regmap, IOMUXC_GPR2, ldb->ldb_ctrl);
+
+ if (ldb->ldb_ctrl & LDB_SPLIT_MODE_EN) {
+ clk_disable_unprepare(ldb->clk[0]);
+ clk_disable_unprepare(ldb->clk[1]);
+ }
+}
+
+static void imx_ldb_encoder_destroy(struct drm_encoder *encoder)
+{
+ /* do not free here */
+}
+
+static struct drm_connector_funcs imx_ldb_connector_funcs = {
+ .dpms = drm_helper_connector_dpms,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .detect = imx_ldb_connector_detect,
+ .destroy = imx_ldb_connector_destroy,
+};
+
+static struct drm_connector_helper_funcs imx_ldb_connector_helper_funcs = {
+ .get_modes = imx_ldb_connector_get_modes,
+ .best_encoder = imx_ldb_connector_best_encoder,
+ .mode_valid = imx_ldb_connector_mode_valid,
+};
+
+static struct drm_encoder_funcs imx_ldb_encoder_funcs = {
+ .destroy = imx_ldb_encoder_destroy,
+};
+
+static struct drm_encoder_helper_funcs imx_ldb_encoder_helper_funcs = {
+ .dpms = imx_ldb_encoder_dpms,
+ .mode_fixup = imx_ldb_encoder_mode_fixup,
+ .prepare = imx_ldb_encoder_prepare,
+ .commit = imx_ldb_encoder_commit,
+ .mode_set = imx_ldb_encoder_mode_set,
+ .disable = imx_ldb_encoder_disable,
+};
+
+static int imx_ldb_get_clk(struct imx_ldb *ldb, int chno)
+{
+ char clkname[16];
+
+ sprintf(clkname, "di%d", chno);
+ ldb->clk[chno] = devm_clk_get(ldb->dev, clkname);
+ if (IS_ERR(ldb->clk[chno]))
+ return PTR_ERR(ldb->clk[chno]);
+
+ sprintf(clkname, "di%d_pll", chno);
+ ldb->clk_pll[chno] = devm_clk_get(ldb->dev, clkname);
+ if (IS_ERR(ldb->clk_pll[chno]))
+ return PTR_ERR(ldb->clk_pll[chno]);
+
+ return 0;
+}
+
+static int imx_ldb_register(struct imx_ldb_channel *imx_ldb_ch)
+{
+ int ret;
+ struct imx_ldb *ldb = imx_ldb_ch->ldb;
+
+ ret = imx_ldb_get_clk(ldb, imx_ldb_ch->chno);
+ if (ret)
+ return ret;
+ if (ldb->ldb_ctrl & LDB_SPLIT_MODE_EN) {
+ ret |= imx_ldb_get_clk(ldb, 1);
+ if (ret)
+ return ret;
+ }
+
+ imx_ldb_ch->connector.funcs = &imx_ldb_connector_funcs;
+ imx_ldb_ch->encoder.funcs = &imx_ldb_encoder_funcs;
+
+ imx_ldb_ch->encoder.encoder_type = DRM_MODE_ENCODER_LVDS;
+ imx_ldb_ch->connector.connector_type = DRM_MODE_CONNECTOR_LVDS;
+
+ drm_encoder_helper_add(&imx_ldb_ch->encoder,
+ &imx_ldb_encoder_helper_funcs);
+ ret = imx_drm_add_encoder(&imx_ldb_ch->encoder,
+ &imx_ldb_ch->imx_drm_encoder, THIS_MODULE);
+ if (ret) {
+ dev_err(ldb->dev, "adding encoder failed with %d\n", ret);
+ return ret;
+ }
+
+ drm_connector_helper_add(&imx_ldb_ch->connector,
+ &imx_ldb_connector_helper_funcs);
+
+ ret = imx_drm_add_connector(&imx_ldb_ch->connector,
+ &imx_ldb_ch->imx_drm_connector, THIS_MODULE);
+ if (ret) {
+ imx_drm_remove_encoder(imx_ldb_ch->imx_drm_encoder);
+ dev_err(ldb->dev, "adding connector failed with %d\n", ret);
+ return ret;
+ }
+
+ drm_mode_connector_attach_encoder(&imx_ldb_ch->connector,
+ &imx_ldb_ch->encoder);
+
+ return 0;
+}
+
+enum {
+ LVDS_BIT_MAP_SPWG,
+ LVDS_BIT_MAP_JEIDA
+};
+
+static const char *imx_ldb_bit_mappings[] = {
+ [LVDS_BIT_MAP_SPWG] = "spwg",
+ [LVDS_BIT_MAP_JEIDA] = "jeida",
+};
+
+const int of_get_data_mapping(struct device_node *np)
+{
+ const char *bm;
+ int ret, i;
+
+ ret = of_property_read_string(np, "fsl,data-mapping", &bm);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(imx_ldb_bit_mappings); i++)
+ if (!strcasecmp(bm, imx_ldb_bit_mappings[i]))
+ return i;
+
+ return -EINVAL;
+}
+
+static struct bus_mux imx6q_lvds_mux[2] = {
+ {
+ .reg = IOMUXC_GPR3,
+ .shift = 6,
+ .mask = IMX6Q_GPR3_LVDS0_MUX_CTL_MASK,
+ }, {
+ .reg = IOMUXC_GPR3,
+ .shift = 8,
+ .mask = IMX6Q_GPR3_LVDS1_MUX_CTL_MASK,
+ }
+};
+
+/*
+ * For a device declaring compatible = "fsl,imx6q-ldb", "fsl,imx53-ldb",
+ * of_match_device will walk through this list and take the first entry
+ * matching any of its compatible values. Therefore, the more generic
+ * entries (in this case fsl,imx53-ldb) need to be ordered last.
+ */
+static const struct of_device_id imx_ldb_dt_ids[] = {
+ { .compatible = "fsl,imx6q-ldb", .data = imx6q_lvds_mux, },
+ { .compatible = "fsl,imx53-ldb", .data = NULL, },
+ { }
+};
+MODULE_DEVICE_TABLE(of, imx_ldb_dt_ids);
+
+static int imx_ldb_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ const struct of_device_id *of_id =
+ of_match_device(of_match_ptr(imx_ldb_dt_ids),
+ &pdev->dev);
+ struct device_node *child;
+ const u8 *edidp;
+ struct imx_ldb *imx_ldb;
+ int datawidth;
+ int mapping;
+ int dual;
+ int ret;
+ int i;
+
+ imx_ldb = devm_kzalloc(&pdev->dev, sizeof(*imx_ldb), GFP_KERNEL);
+ if (!imx_ldb)
+ return -ENOMEM;
+
+ imx_ldb->regmap = syscon_regmap_lookup_by_phandle(np, "gpr");
+ if (IS_ERR(imx_ldb->regmap)) {
+ dev_err(&pdev->dev, "failed to get parent regmap\n");
+ return PTR_ERR(imx_ldb->regmap);
+ }
+
+ imx_ldb->dev = &pdev->dev;
+
+ if (of_id)
+ imx_ldb->lvds_mux = of_id->data;
+
+ dual = of_property_read_bool(np, "fsl,dual-channel");
+ if (dual)
+ imx_ldb->ldb_ctrl |= LDB_SPLIT_MODE_EN;
+
+ /*
+ * There are three diferent possible clock mux configurations:
+ * i.MX53: ipu1_di0_sel, ipu1_di1_sel
+ * i.MX6q: ipu1_di0_sel, ipu1_di1_sel, ipu2_di0_sel, ipu2_di1_sel
+ * i.MX6dl: ipu1_di0_sel, ipu1_di1_sel, lcdif_sel
+ * Map them all to di0_sel...di3_sel.
+ */
+ for (i = 0; i < 4; i++) {
+ char clkname[16];
+
+ sprintf(clkname, "di%d_sel", i);
+ imx_ldb->clk_sel[i] = devm_clk_get(imx_ldb->dev, clkname);
+ if (IS_ERR(imx_ldb->clk_sel[i])) {
+ ret = PTR_ERR(imx_ldb->clk_sel[i]);
+ imx_ldb->clk_sel[i] = NULL;
+ break;
+ }
+ }
+ if (i == 0)
+ return ret;
+
+ for_each_child_of_node(np, child) {
+ struct imx_ldb_channel *channel;
+
+ ret = of_property_read_u32(child, "reg", &i);
+ if (ret || i < 0 || i > 1)
+ return -EINVAL;
+
+ if (dual && i > 0) {
+ dev_warn(&pdev->dev, "dual-channel mode, ignoring second output\n");
+ continue;
+ }
+
+ if (!of_device_is_available(child))
+ continue;
+
+ channel = &imx_ldb->channel[i];
+ channel->ldb = imx_ldb;
+ channel->chno = i;
+
+ edidp = of_get_property(child, "edid", &channel->edid_len);
+ if (edidp) {
+ channel->edid = kmemdup(edidp, channel->edid_len,
+ GFP_KERNEL);
+ } else {
+ ret = of_get_drm_display_mode(child, &channel->mode, 0);
+ if (!ret)
+ channel->mode_valid = 1;
+ }
+
+ ret = of_property_read_u32(child, "fsl,data-width", &datawidth);
+ if (ret)
+ datawidth = 0;
+ else if (datawidth != 18 && datawidth != 24)
+ return -EINVAL;
+
+ mapping = of_get_data_mapping(child);
+ switch (mapping) {
+ case LVDS_BIT_MAP_SPWG:
+ if (datawidth == 24) {
+ if (i == 0 || dual)
+ imx_ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH0_24;
+ if (i == 1 || dual)
+ imx_ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH1_24;
+ }
+ break;
+ case LVDS_BIT_MAP_JEIDA:
+ if (datawidth == 18) {
+ dev_err(&pdev->dev, "JEIDA standard only supported in 24 bit\n");
+ return -EINVAL;
+ }
+ if (i == 0 || dual)
+ imx_ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH0_24 | LDB_BIT_MAP_CH0_JEIDA;
+ if (i == 1 || dual)
+ imx_ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH1_24 | LDB_BIT_MAP_CH1_JEIDA;
+ break;
+ default:
+ dev_err(&pdev->dev, "data mapping not specified or invalid\n");
+ return -EINVAL;
+ }
+
+ ret = imx_ldb_register(channel);
+ if (ret)
+ return ret;
+
+ imx_drm_encoder_add_possible_crtcs(channel->imx_drm_encoder, child);
+ }
+
+ platform_set_drvdata(pdev, imx_ldb);
+
+ return 0;
+}
+
+static int imx_ldb_remove(struct platform_device *pdev)
+{
+ struct imx_ldb *imx_ldb = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ struct imx_ldb_channel *channel = &imx_ldb->channel[i];
+ struct drm_connector *connector = &channel->connector;
+ struct drm_encoder *encoder = &channel->encoder;
+
+ drm_mode_connector_detach_encoder(connector, encoder);
+
+ imx_drm_remove_connector(channel->imx_drm_connector);
+ imx_drm_remove_encoder(channel->imx_drm_encoder);
+ }
+
+ return 0;
+}
+
+static struct platform_driver imx_ldb_driver = {
+ .probe = imx_ldb_probe,
+ .remove = imx_ldb_remove,
+ .driver = {
+ .of_match_table = imx_ldb_dt_ids,
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(imx_ldb_driver);
+
+MODULE_DESCRIPTION("i.MX LVDS driver");
+MODULE_AUTHOR("Sascha Hauer, Pengutronix");
+MODULE_LICENSE("GPL");
--
1.8.2.rc2
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v3 07/11] ARM i.MX6q: Add audio/video PLL post dividers for i.MX6q rev 1.1
2013-03-28 15:23 [PATCH v3 0/11] LVDS Display Bridge support for i.MX Philipp Zabel
` (4 preceding siblings ...)
2013-03-28 15:23 ` [PATCH v3 06/11] staging: drm/imx: Add LDB support Philipp Zabel
@ 2013-03-28 15:23 ` Philipp Zabel
2013-03-28 15:23 ` [PATCH v3 08/11] ARM i.MX6q: set the LDB serial clock parent to the video PLL Philipp Zabel
` (4 subsequent siblings)
10 siblings, 0 replies; 20+ messages in thread
From: Philipp Zabel @ 2013-03-28 15:23 UTC (permalink / raw)
To: linux-arm-kernel
Cc: devel, Fabio Estevam, Greg Kroah-Hartman, devicetree-discuss,
kernel, Martin Fuzzey, Philipp Zabel, Sean Cross, Shawn Guo,
Sascha Hauer
Query silicon revision to determine clock tree and add post
dividers for newer revisions.
Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
---
Changes since v2:
- Renamed pll[45]_test_div to pll[45]_post_div
- Renamed pll5_control3 to pll5_video_div
- Reformatted clk_div_tables
- Added missing imx_ccm_lock
- Added clocks to device tree bindings documentation
---
.../devicetree/bindings/clock/imx6q-clock.txt | 3 ++
arch/arm/mach-imx/clk-imx6q.c | 42 ++++++++++++++++++----
2 files changed, 38 insertions(+), 7 deletions(-)
diff --git a/Documentation/devicetree/bindings/clock/imx6q-clock.txt b/Documentation/devicetree/bindings/clock/imx6q-clock.txt
index 969b38e..6deb6fd 100644
--- a/Documentation/devicetree/bindings/clock/imx6q-clock.txt
+++ b/Documentation/devicetree/bindings/clock/imx6q-clock.txt
@@ -205,6 +205,9 @@ clocks and IDs.
enet_ref 190
usbphy1_gate 191
usbphy2_gate 192
+ pll4_post_div 193
+ pll5_post_div 194
+ pll5_video_div 195
Examples:
diff --git a/arch/arm/mach-imx/clk-imx6q.c b/arch/arm/mach-imx/clk-imx6q.c
index 4b0cab4..9c6a08e 100644
--- a/arch/arm/mach-imx/clk-imx6q.c
+++ b/arch/arm/mach-imx/clk-imx6q.c
@@ -22,6 +22,7 @@
#include "clk.h"
#include "common.h"
+#include "hardware.h"
#define CCGR0 0x68
#define CCGR1 0x6c
@@ -109,29 +110,29 @@ static const char *periph_clk2_sels[] = { "pll3_usb_otg", "osc", };
static const char *periph_sels[] = { "periph_pre", "periph_clk2", };
static const char *periph2_sels[] = { "periph2_pre", "periph2_clk2", };
static const char *axi_sels[] = { "periph", "pll2_pfd2_396m", "pll3_pfd1_540m", };
-static const char *audio_sels[] = { "pll4_audio", "pll3_pfd2_508m", "pll3_pfd3_454m", "pll3_usb_otg", };
+static const char *audio_sels[] = { "pll4_post_div", "pll3_pfd2_508m", "pll3_pfd3_454m", "pll3_usb_otg", };
static const char *gpu_axi_sels[] = { "axi", "ahb", };
static const char *gpu2d_core_sels[] = { "axi", "pll3_usb_otg", "pll2_pfd0_352m", "pll2_pfd2_396m", };
static const char *gpu3d_core_sels[] = { "mmdc_ch0_axi", "pll3_usb_otg", "pll2_pfd1_594m", "pll2_pfd2_396m", };
static const char *gpu3d_shader_sels[] = { "mmdc_ch0_axi", "pll3_usb_otg", "pll2_pfd1_594m", "pll2_pfd9_720m", };
static const char *ipu_sels[] = { "mmdc_ch0_axi", "pll2_pfd2_396m", "pll3_120m", "pll3_pfd1_540m", };
-static const char *ldb_di_sels[] = { "pll5_video", "pll2_pfd0_352m", "pll2_pfd2_396m", "mmdc_ch1_axi", "pll3_pfd1_540m", };
-static const char *ipu_di_pre_sels[] = { "mmdc_ch0_axi", "pll3_usb_otg", "pll5_video", "pll2_pfd0_352m", "pll2_pfd2_396m", "pll3_pfd1_540m", };
+static const char *ldb_di_sels[] = { "pll5_video_div", "pll2_pfd0_352m", "pll2_pfd2_396m", "mmdc_ch1_axi", "pll3_pfd1_540m", };
+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", "ldb_di1", };
static const char *ipu1_di1_sels[] = { "ipu1_di1_pre", "dummy", "dummy", "ldb_di0", "ldb_di1", };
static const char *ipu2_di0_sels[] = { "ipu2_di0_pre", "dummy", "dummy", "ldb_di0", "ldb_di1", };
static const char *ipu2_di1_sels[] = { "ipu2_di1_pre", "dummy", "dummy", "ldb_di0", "ldb_di1", };
static const char *hsi_tx_sels[] = { "pll3_120m", "pll2_pfd2_396m", };
static const char *pcie_axi_sels[] = { "axi", "ahb", };
-static const char *ssi_sels[] = { "pll3_pfd2_508m", "pll3_pfd3_454m", "pll4_audio", };
+static const char *ssi_sels[] = { "pll3_pfd2_508m", "pll3_pfd3_454m", "pll4_post_div", };
static const char *usdhc_sels[] = { "pll2_pfd2_396m", "pll2_pfd0_352m", };
static const char *enfc_sels[] = { "pll2_pfd0_352m", "pll2_bus", "pll3_usb_otg", "pll2_pfd2_396m", };
static const char *emi_sels[] = { "axi", "pll3_usb_otg", "pll2_pfd2_396m", "pll2_pfd0_352m", };
static const char *vdo_axi_sels[] = { "axi", "ahb", };
static const char *vpu_axi_sels[] = { "axi", "pll2_pfd2_396m", "pll2_pfd0_352m", };
-static const char *cko1_sels[] = { "pll3_usb_otg", "pll2_bus", "pll1_sys", "pll5_video",
+static const char *cko1_sels[] = { "pll3_usb_otg", "pll2_bus", "pll1_sys", "pll5_video_div",
"dummy", "axi", "enfc", "ipu1_di0", "ipu1_di1", "ipu2_di0",
- "ipu2_di1", "ahb", "ipg", "ipg_per", "ckil", "pll4_audio", };
+ "ipu2_di1", "ahb", "ipg", "ipg_per", "ckil", "pll4_post_div", };
enum mx6q_clks {
dummy, ckil, ckih, osc, pll2_pfd0_352m, pll2_pfd1_594m, pll2_pfd2_396m,
@@ -165,7 +166,7 @@ enum mx6q_clks {
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, sata_ref_100m, pcie_ref, pcie_ref_125m, enet_ref, usbphy1_gate,
- usbphy2_gate, clk_max
+ usbphy2_gate, pll4_post_div, pll5_post_div, pll5_video_div, clk_max
};
static struct clk *clk[clk_max];
@@ -182,6 +183,21 @@ static struct clk_div_table clk_enet_ref_table[] = {
{ .val = 3, .div = 4, },
};
+static struct clk_div_table post_div_table[] = {
+ { .val = 2, .div = 1, },
+ { .val = 1, .div = 2, },
+ { .val = 0, .div = 4, },
+ { }
+};
+
+static struct clk_div_table video_div_table[] = {
+ { .val = 0, .div = 1, },
+ { .val = 1, .div = 2, },
+ { .val = 2, .div = 1, },
+ { .val = 3, .div = 4, },
+ { }
+};
+
int __init mx6q_clocks_init(void)
{
struct device_node *np;
@@ -208,6 +224,14 @@ int __init mx6q_clocks_init(void)
base = of_iomap(np, 0);
WARN_ON(!base);
+ /* Audio/video PLL post dividers do not work on i.MX6q revision 1.0 */
+ if (imx6q_revision() == IMX_CHIP_REVISION_1_0) {
+ post_div_table[1].div = 1;
+ post_div_table[2].div = 1;
+ video_div_table[1].div = 1;
+ video_div_table[2].div = 1;
+ };
+
/* type name parent_name base div_mask */
clk[pll1_sys] = imx_clk_pllv3(IMX_PLLV3_SYS, "pll1_sys", "osc", base, 0x7f);
clk[pll2_bus] = imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll2_bus", "osc", base + 0x30, 0x1);
@@ -260,6 +284,10 @@ int __init mx6q_clocks_init(void)
clk[pll3_60m] = imx_clk_fixed_factor("pll3_60m", "pll3_usb_otg", 1, 8);
clk[twd] = imx_clk_fixed_factor("twd", "arm", 1, 2);
+ clk[pll4_post_div] = clk_register_divider_table(NULL, "pll4_post_div", "pll4_audio", CLK_SET_RATE_PARENT, base + 0x70, 19, 2, 0, post_div_table, &imx_ccm_lock);
+ clk[pll5_post_div] = clk_register_divider_table(NULL, "pll5_post_div", "pll5_video", CLK_SET_RATE_PARENT, base + 0xa0, 19, 2, 0, post_div_table, &imx_ccm_lock);
+ clk[pll5_video_div] = clk_register_divider_table(NULL, "pll5_video_div", "pll5_post_div", CLK_SET_RATE_PARENT, base + 0x170, 30, 2, 0, video_div_table, &imx_ccm_lock);
+
np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-ccm");
base = of_iomap(np, 0);
WARN_ON(!base);
--
1.8.2.rc2
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v3 08/11] ARM i.MX6q: set the LDB serial clock parent to the video PLL
2013-03-28 15:23 [PATCH v3 0/11] LVDS Display Bridge support for i.MX Philipp Zabel
` (5 preceding siblings ...)
2013-03-28 15:23 ` [PATCH v3 07/11] ARM i.MX6q: Add audio/video PLL post dividers for i.MX6q rev 1.1 Philipp Zabel
@ 2013-03-28 15:23 ` Philipp Zabel
2013-03-28 15:23 ` [PATCH v3 09/11] ARM i.MX53: Add IOMUXC GPR to device tree Philipp Zabel
` (3 subsequent siblings)
10 siblings, 0 replies; 20+ messages in thread
From: Philipp Zabel @ 2013-03-28 15:23 UTC (permalink / raw)
To: linux-arm-kernel
Cc: devel, Fabio Estevam, Greg Kroah-Hartman, devicetree-discuss,
kernel, Martin Fuzzey, Philipp Zabel, Sean Cross, Shawn Guo,
Sascha Hauer
On i.MX6q revision 1.1 and later, set the video PLL as parent for
the LDB clock branch. On revision 1.0, the video PLL is useless
due to missing dividers, so keep the default parent (mmdc_ch1_axi).
Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
---
Changes since v2:
- Renamed pll5_control3 to pll5_video_div
---
arch/arm/mach-imx/clk-imx6q.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/arch/arm/mach-imx/clk-imx6q.c b/arch/arm/mach-imx/clk-imx6q.c
index 9c6a08e..398d672 100644
--- a/arch/arm/mach-imx/clk-imx6q.c
+++ b/arch/arm/mach-imx/clk-imx6q.c
@@ -477,6 +477,11 @@ int __init mx6q_clocks_init(void)
clk_register_clkdev(clk[cko1], "cko1", NULL);
clk_register_clkdev(clk[arm], NULL, "cpu0");
+ if (imx6q_revision() != IMX_CHIP_REVISION_1_0) {
+ clk_set_parent(clk[ldb_di0_sel], clk[pll5_video_div]);
+ clk_set_parent(clk[ldb_di1_sel], clk[pll5_video_div]);
+ }
+
/*
* The gpmi needs 100MHz frequency in the EDO/Sync mode,
* We can not get the 100MHz from the pll2_pfd0_352m.
--
1.8.2.rc2
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v3 09/11] ARM i.MX53: Add IOMUXC GPR to device tree
2013-03-28 15:23 [PATCH v3 0/11] LVDS Display Bridge support for i.MX Philipp Zabel
` (6 preceding siblings ...)
2013-03-28 15:23 ` [PATCH v3 08/11] ARM i.MX6q: set the LDB serial clock parent to the video PLL Philipp Zabel
@ 2013-03-28 15:23 ` Philipp Zabel
2013-03-28 15:23 ` [PATCH v3 10/11] ARM i.MX53: Add LDB device " Philipp Zabel
` (2 subsequent siblings)
10 siblings, 0 replies; 20+ messages in thread
From: Philipp Zabel @ 2013-03-28 15:23 UTC (permalink / raw)
To: linux-arm-kernel
Cc: devel, Fabio Estevam, Greg Kroah-Hartman, devicetree-discuss,
kernel, Martin Fuzzey, Philipp Zabel, Sean Cross, Shawn Guo,
Sascha Hauer
Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
---
arch/arm/boot/dts/imx53.dtsi | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/arch/arm/boot/dts/imx53.dtsi b/arch/arm/boot/dts/imx53.dtsi
index 7a6f5a8..b07bbdcc 100644
--- a/arch/arm/boot/dts/imx53.dtsi
+++ b/arch/arm/boot/dts/imx53.dtsi
@@ -506,6 +506,11 @@
};
+ gpr: iomuxc-gpr@53fa8000 {
+ compatible = "fsl,imx53-iomuxc-gpr", "syscon";
+ reg = <0x53fa8000 0xc>;
+ };
+
pwm1: pwm@53fb4000 {
#pwm-cells = <2>;
compatible = "fsl,imx53-pwm", "fsl,imx27-pwm";
--
1.8.2.rc2
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v3 10/11] ARM i.MX53: Add LDB device to device tree
2013-03-28 15:23 [PATCH v3 0/11] LVDS Display Bridge support for i.MX Philipp Zabel
` (7 preceding siblings ...)
2013-03-28 15:23 ` [PATCH v3 09/11] ARM i.MX53: Add IOMUXC GPR to device tree Philipp Zabel
@ 2013-03-28 15:23 ` Philipp Zabel
2013-03-28 15:23 ` [PATCH v3 11/11] ARM i.MX6q: " Philipp Zabel
[not found] ` <1364484215-13935-1-git-send-email-p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
10 siblings, 0 replies; 20+ messages in thread
From: Philipp Zabel @ 2013-03-28 15:23 UTC (permalink / raw)
To: linux-arm-kernel
Cc: devel, Fabio Estevam, Greg Kroah-Hartman, devicetree-discuss,
kernel, Martin Fuzzey, Philipp Zabel, Sean Cross, Shawn Guo,
Sascha Hauer
Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
---
arch/arm/boot/dts/imx53.dtsi | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/arch/arm/boot/dts/imx53.dtsi b/arch/arm/boot/dts/imx53.dtsi
index b07bbdcc..30aed40 100644
--- a/arch/arm/boot/dts/imx53.dtsi
+++ b/arch/arm/boot/dts/imx53.dtsi
@@ -511,6 +511,33 @@
reg = <0x53fa8000 0xc>;
};
+ ldb: ldb@53fa8008 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,imx53-ldb";
+ reg = <0x53fa8008 0x4>;
+ gpr = <&gpr>;
+ clocks = <&clks 122>, <&clks 120>,
+ <&clks 115>, <&clks 116>,
+ <&clks 123>, <&clks 85>;
+ clock-names = "di0_pll", "di1_pll",
+ "di0_sel", "di1_sel",
+ "di0", "di1";
+ status = "disabled";
+
+ lvds-channel@0 {
+ reg = <0>;
+ crtcs = <&ipu 0>;
+ status = "disabled";
+ };
+
+ lvds-channel@1 {
+ reg = <1>;
+ crtcs = <&ipu 1>;
+ status = "disabled";
+ };
+ };
+
pwm1: pwm@53fb4000 {
#pwm-cells = <2>;
compatible = "fsl,imx53-pwm", "fsl,imx27-pwm";
--
1.8.2.rc2
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v3 11/11] ARM i.MX6q: Add LDB device to device tree
2013-03-28 15:23 [PATCH v3 0/11] LVDS Display Bridge support for i.MX Philipp Zabel
` (8 preceding siblings ...)
2013-03-28 15:23 ` [PATCH v3 10/11] ARM i.MX53: Add LDB device " Philipp Zabel
@ 2013-03-28 15:23 ` Philipp Zabel
[not found] ` <1364484215-13935-1-git-send-email-p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
10 siblings, 0 replies; 20+ messages in thread
From: Philipp Zabel @ 2013-03-28 15:23 UTC (permalink / raw)
To: linux-arm-kernel
Cc: devel, Fabio Estevam, Steffen Trumtrar, Greg Kroah-Hartman,
devicetree-discuss, kernel, Martin Fuzzey, Philipp Zabel,
Sean Cross, Shawn Guo, Sascha Hauer
From: Steffen Trumtrar <s.trumtrar@pengutronix.de>
Add ldb device tree node and clock lookups.
Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
---
Changes since v2:
- Removed clocks properties from imx6qdl.dtsi,
those are to be set in imx6q.dtsi and imx6dl.
---
arch/arm/boot/dts/imx6q.dtsi | 17 +++++++++++++++++
arch/arm/boot/dts/imx6qdl.dtsi | 26 ++++++++++++++++++++++++++
2 files changed, 43 insertions(+)
diff --git a/arch/arm/boot/dts/imx6q.dtsi b/arch/arm/boot/dts/imx6q.dtsi
index cba021e..1a30227 100644
--- a/arch/arm/boot/dts/imx6q.dtsi
+++ b/arch/arm/boot/dts/imx6q.dtsi
@@ -294,3 +294,20 @@
};
};
};
+
+&ldb {
+ clocks = <&clks 33>, <&clks 34>,
+ <&clks 39>, <&clks 40>, <&clks 41>, <&clks 42>,
+ <&clks 135>, <&clks 136>;
+ clock-names = "di0_pll", "di1_pll",
+ "di0_sel", "di1_sel", "di2_sel", "di3_sel",
+ "di0", "di1";
+
+ lvds-channel@0 {
+ crtcs = <&ipu1 0>, <&ipu1 1>, <&ipu2 0>, <&ipu2 1>;
+ };
+
+ lvds-channel@1 {
+ crtcs = <&ipu1 0>, <&ipu1 1>, <&ipu2 0>, <&ipu2 1>;
+ };
+};
diff --git a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi
index 06ec460..dd5ef96 100644
--- a/arch/arm/boot/dts/imx6qdl.dtsi
+++ b/arch/arm/boot/dts/imx6qdl.dtsi
@@ -529,6 +529,26 @@
reg = <0x020e0000 0x38>;
};
+ ldb: ldb@020e0008 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,imx6q-ldb", "fsl,imx53-ldb";
+ gpr = <&gpr>;
+ status = "disabled";
+
+ lvds-channel@0 {
+ reg = <0>;
+ crtcs = <&ipu1 0>;
+ status = "disabled";
+ };
+
+ lvds-channel@1 {
+ reg = <1>;
+ crtcs = <&ipu1 1>;
+ status = "disabled";
+ };
+ };
+
dcic1: dcic@020e4000 {
reg = <0x020e4000 0x4000>;
interrupts = <0 124 0x04>;
--
1.8.2.rc2
^ permalink raw reply related [flat|nested] 20+ messages in thread
* Re: [PATCH v3 0/11] LVDS Display Bridge support for i.MX
[not found] ` <1364484215-13935-1-git-send-email-p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
2013-03-28 15:23 ` [PATCH v3 01/11] ARM i.MX5: Move IPU clock lookups into " Philipp Zabel
@ 2013-03-29 11:19 ` Shawn Guo
2013-03-30 11:24 ` Philipp Zabel
2013-03-29 11:44 ` Shawn Guo
2 siblings, 1 reply; 20+ messages in thread
From: Shawn Guo @ 2013-03-29 11:19 UTC (permalink / raw)
To: Philipp Zabel
Cc: devel-gWbeCf7V1WCQmaza687I9mD2FQJk+8+b, Fabio Estevam,
Greg Kroah-Hartman, devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ,
kernel-bIcnvbaLZ9MEGnE8C9+IrQ, Martin Fuzzey, Sean Cross,
Sascha Hauer, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
On Thu, Mar 28, 2013 at 04:23:24PM +0100, Philipp Zabel wrote:
> Hi,
>
> the following patches add support for LVDS displays on
> i.MX53 and i.MX6q boards. I have reordered the patches,
> as Shawn has already applied the now first five patches.
I do not think you need to necessarily resend the patches that have
already been applied.
BTW, I applied "ARM i.MX53: Add IOMUXC GPR to device tree" as well last
night.
Shawn
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v3 0/11] LVDS Display Bridge support for i.MX
[not found] ` <1364484215-13935-1-git-send-email-p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
2013-03-28 15:23 ` [PATCH v3 01/11] ARM i.MX5: Move IPU clock lookups into " Philipp Zabel
2013-03-29 11:19 ` [PATCH v3 0/11] LVDS Display Bridge support for i.MX Shawn Guo
@ 2013-03-29 11:44 ` Shawn Guo
2 siblings, 0 replies; 20+ messages in thread
From: Shawn Guo @ 2013-03-29 11:44 UTC (permalink / raw)
To: Philipp Zabel
Cc: devel-gWbeCf7V1WCQmaza687I9mD2FQJk+8+b, Fabio Estevam,
Greg Kroah-Hartman, devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ,
kernel-bIcnvbaLZ9MEGnE8C9+IrQ, Martin Fuzzey, Sean Cross,
Sascha Hauer, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
On Thu, Mar 28, 2013 at 04:23:24PM +0100, Philipp Zabel wrote:
> Hi,
>
> the following patches add support for LVDS displays on
> i.MX53 and i.MX6q boards. I have reordered the patches,
> as Shawn has already applied the now first five patches.
Just applied patches #7, #8 and #11. #9 and #10 were applied yesterday
actually. So all "ARM:" patches are applied.
Shawn
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v3 0/11] LVDS Display Bridge support for i.MX
2013-03-29 11:19 ` [PATCH v3 0/11] LVDS Display Bridge support for i.MX Shawn Guo
@ 2013-03-30 11:24 ` Philipp Zabel
0 siblings, 0 replies; 20+ messages in thread
From: Philipp Zabel @ 2013-03-30 11:24 UTC (permalink / raw)
To: Shawn Guo
Cc: devel, Fabio Estevam, Philipp Zabel, Greg Kroah-Hartman,
devicetree-discuss, kernel, Martin Fuzzey, Sean Cross,
Sascha Hauer, linux-arm-kernel
On Fri, Mar 29, 2013 at 07:19:58PM +0800, Shawn Guo wrote:
> On Thu, Mar 28, 2013 at 04:23:24PM +0100, Philipp Zabel wrote:
> > Hi,
> >
> > the following patches add support for LVDS displays on
> > i.MX53 and i.MX6q boards. I have reordered the patches,
> > as Shawn has already applied the now first five patches.
>
> I do not think you need to necessarily resend the patches that have
> already been applied.
Alright, now I can see the applied patches in your public tree.
> BTW, I applied "ARM i.MX53: Add IOMUXC GPR to device tree" as well last
> night.
thank you
Philipp
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v3 06/11] staging: drm/imx: Add LDB support
[not found] ` <1364484215-13935-7-git-send-email-p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
@ 2013-06-06 15:16 ` Shawn Guo
2013-06-06 15:54 ` Philipp Zabel
0 siblings, 1 reply; 20+ messages in thread
From: Shawn Guo @ 2013-06-06 15:16 UTC (permalink / raw)
To: Philipp Zabel
Cc: devel-gWbeCf7V1WCQmaza687I9mD2FQJk+8+b, Fabio Estevam,
Greg Kroah-Hartman, devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ,
kernel-bIcnvbaLZ9MEGnE8C9+IrQ, Martin Fuzzey, Sean Cross,
Sascha Hauer, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
On Thu, Mar 28, 2013 at 04:23:30PM +0100, Philipp Zabel wrote:
> +static void imx_ldb_encoder_prepare(struct drm_encoder *encoder)
> +{
> + struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder);
> + struct imx_ldb *ldb = imx_ldb_ch->ldb;
> + struct drm_display_mode *mode = &encoder->crtc->mode;
> + unsigned long serial_clk;
> + unsigned long di_clk = mode->clock * 1000;
> + int mux = imx_drm_encoder_get_mux_id(imx_ldb_ch->imx_drm_encoder,
> + encoder->crtc);
> +
> + if (ldb->ldb_ctrl & LDB_SPLIT_MODE_EN) {
> + /* dual channel LVDS mode */
> + serial_clk = 3500UL * mode->clock;
> + imx_ldb_set_clock(ldb, mux, 0, serial_clk, di_clk);
> + imx_ldb_set_clock(ldb, mux, 1, serial_clk, di_clk);
> + } else {
> + serial_clk = 7000UL * mode->clock;
> + imx_ldb_set_clock(ldb, mux, imx_ldb_ch->chno, serial_clk, di_clk);
> + }
> +
> + imx_drm_crtc_panel_format(encoder->crtc, DRM_MODE_ENCODER_LVDS,
> + V4L2_PIX_FMT_RGB24);
I have panel which needs it to be V4L2_PIX_FMT_BGR666. We should
probably have a device tree property for that like interface_pix_fmt in
parallel display support?
Shawn
> +}
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v3 06/11] staging: drm/imx: Add LDB support
2013-06-06 15:16 ` Shawn Guo
@ 2013-06-06 15:54 ` Philipp Zabel
[not found] ` <1370534082.3931.7.camel-/rZezPiN1rtR6QfukMTsflXZhhPuCNm+@public.gmane.org>
0 siblings, 1 reply; 20+ messages in thread
From: Philipp Zabel @ 2013-06-06 15:54 UTC (permalink / raw)
To: Shawn Guo
Cc: devel, Fabio Estevam, Greg Kroah-Hartman, devicetree-discuss,
kernel, Martin Fuzzey, Sean Cross, Sascha Hauer, linux-arm-kernel
Hi Shawn,
Am Donnerstag, den 06.06.2013, 23:16 +0800 schrieb Shawn Guo:
> On Thu, Mar 28, 2013 at 04:23:30PM +0100, Philipp Zabel wrote:
> > +static void imx_ldb_encoder_prepare(struct drm_encoder *encoder)
> > +{
> > + struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder);
> > + struct imx_ldb *ldb = imx_ldb_ch->ldb;
> > + struct drm_display_mode *mode = &encoder->crtc->mode;
> > + unsigned long serial_clk;
> > + unsigned long di_clk = mode->clock * 1000;
> > + int mux = imx_drm_encoder_get_mux_id(imx_ldb_ch->imx_drm_encoder,
> > + encoder->crtc);
> > +
> > + if (ldb->ldb_ctrl & LDB_SPLIT_MODE_EN) {
> > + /* dual channel LVDS mode */
> > + serial_clk = 3500UL * mode->clock;
> > + imx_ldb_set_clock(ldb, mux, 0, serial_clk, di_clk);
> > + imx_ldb_set_clock(ldb, mux, 1, serial_clk, di_clk);
> > + } else {
> > + serial_clk = 7000UL * mode->clock;
> > + imx_ldb_set_clock(ldb, mux, imx_ldb_ch->chno, serial_clk, di_clk);
> > + }
> > +
> > + imx_drm_crtc_panel_format(encoder->crtc, DRM_MODE_ENCODER_LVDS,
> > + V4L2_PIX_FMT_RGB24);
>
> I have panel which needs it to be V4L2_PIX_FMT_BGR666. We should
> probably have a device tree property for that like interface_pix_fmt in
> parallel display support?
I'm not sure. Is this something that should be done unconditionally for
fsl,data-width = <18>?
regards
Philipp
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v3 06/11] staging: drm/imx: Add LDB support
[not found] ` <1370534082.3931.7.camel-/rZezPiN1rtR6QfukMTsflXZhhPuCNm+@public.gmane.org>
@ 2013-06-07 0:26 ` Shawn Guo
0 siblings, 0 replies; 20+ messages in thread
From: Shawn Guo @ 2013-06-07 0:26 UTC (permalink / raw)
To: Philipp Zabel
Cc: devel-gWbeCf7V1WCQmaza687I9mD2FQJk+8+b, Fabio Estevam,
Greg Kroah-Hartman, devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ,
kernel-bIcnvbaLZ9MEGnE8C9+IrQ, Martin Fuzzey, Sean Cross,
Sascha Hauer, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
On Thu, Jun 06, 2013 at 05:54:42PM +0200, Philipp Zabel wrote:
> I'm not sure. Is this something that should be done unconditionally for
> fsl,data-width = <18>?
>
Ah, yes, that's better.
Shawn
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v3 06/11] staging: drm/imx: Add LDB support
2013-03-28 15:23 ` [PATCH v3 06/11] staging: drm/imx: Add LDB support Philipp Zabel
[not found] ` <1364484215-13935-7-git-send-email-p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
@ 2013-06-07 7:40 ` Markus Niebel
2013-06-07 7:54 ` Philipp Zabel
1 sibling, 1 reply; 20+ messages in thread
From: Markus Niebel @ 2013-06-07 7:40 UTC (permalink / raw)
To: Philipp Zabel
Cc: devel, Fabio Estevam, Sascha Hauer, Greg Kroah-Hartman,
devicetree-discuss, kernel, Martin Fuzzey, Sean Cross, Shawn Guo,
linux-arm-kernel
Am 28.03.2013 16:23, wrote Philipp Zabel:
> From: Sascha Hauer <s.hauer@pengutronix.de>
>
> This adds support for the LVDS Display Bridge contained
> in i.MX5 and i.MX6 SoCs.
>
> Bit mapping, data width, and video timings are configurable
> via device tree. Dual-channel mode is supported for a single
> high-resolution source.
>
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
> ---
> Changes since v2:
> - Removed commented out code
> - Replaced magic constants
> - Select OF_VIDEOMODE
> ---
> .../devicetree/bindings/staging/imx-drm/ldb.txt | 99 ++++
> drivers/staging/imx-drm/Kconfig | 8 +
> drivers/staging/imx-drm/Makefile | 1 +
> drivers/staging/imx-drm/imx-ldb.c | 609 +++++++++++++++++++++
> 4 files changed, 717 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/staging/imx-drm/ldb.txt
> create mode 100644 drivers/staging/imx-drm/imx-ldb.c
>
> diff --git a/Documentation/devicetree/bindings/staging/imx-drm/ldb.txt b/Documentation/devicetree/bindings/staging/imx-drm/ldb.txt
> new file mode 100644
> index 0000000..ed93778
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/staging/imx-drm/ldb.txt
> @@ -0,0 +1,99 @@
> +Device-Tree bindings for LVDS Display Bridge (ldb)
> +
> +LVDS Display Bridge
> +===================
> +
> +The LVDS Display Bridge device tree node contains up to two lvds-channel
> +nodes describing each of the two LVDS encoder channels of the bridge.
> +
> +Required properties:
> + - #address-cells : should be <1>
> + - #size-cells : should be <0>
> + - compatible : should be "fsl,imx53-ldb" or "fsl,imx6q-ldb".
> + Both LDB versions are similar, but i.MX6 has an additional
> + multiplexer in the front to select any of the four IPU display
> + interfaces as input for each LVDS channel.
> + - gpr : should be <&gpr> on i.MX53 and i.MX6q.
> + The phandle points to the iomuxc-gpr region containing the LVDS
> + control register.
> +- clocks, clock-names : phandles to the LDB divider and selector clocks and to
> + the display interface selector clocks, as described in
> + Documentation/devicetree/bindings/clock/clock-bindings.txt
> + The following clocks are expected on i.MX53:
> + "di0_pll" - LDB LVDS channel 0 mux
> + "di1_pll" - LDB LVDS channel 1 mux
> + "di0" - LDB LVDS channel 0 gate
> + "di1" - LDB LVDS channel 1 gate
> + "di0_sel" - IPU1 DI0 mux
> + "di1_sel" - IPU1 DI1 mux
> + On i.MX6q the following additional clocks are needed:
> + "di2_sel" - IPU2 DI0 mux
> + "di3_sel" - IPU2 DI1 mux
> + The needed clock numbers for each are documented in
> + Documentation/devicetree/bindings/clock/imx5-clock.txt, and in
> + Documentation/devicetree/bindings/clock/imx6q-clock.txt.
> +
> +Optional properties:
> + - pinctrl-names : should be "default" on i.MX53, not used on i.MX6q
> + - pinctrl-0 : a phandle pointing to LVDS pin settings on i.MX53,
> + not used on i.MX6q
> + - fsl,dual-channel : boolean. if it exists, only LVDS channel 0 should
> + be configured - one input will be distributed on both outputs in dual
> + channel mode
> +
> +LVDS Channel
> +============
> +
> +Each LVDS Channel has to contain a display-timings node that describes the
> +video timings for the connected LVDS display. For detailed information, also
> +have a look at Documentation/devicetree/bindings/video/display-timing.txt.
> +
> +Required properties:
> + - reg : should be <0> or <1>
> + - crtcs : a list of phandles with index pointing to the IPU display interfaces
> + that can be used as video source for this channel.
> + - fsl,data-mapping : should be "spwg" or "jeida"
> + This describes how the color bits are laid out in the
> + serialized LVDS signal.
> + - fsl,data-width : should be <18> or <24>
> +
> +example:
> +
> +gpr: iomuxc-gpr@53fa8000 {
> + /* ... */
> +};
> +
> +ldb: ldb@53fa8008 {
> + #address-cells = <1>;
> + #size-cells = <0>;
> + compatible = "fsl,imx53-ldb";
> + gpr = <&gpr>;
> + clocks = <&clks 122>, <&clks 120>,
> + <&clks 115>, <&clks 116>,
> + <&clks 123>, <&clks 85>;
> + clock-names = "di0_pll", "di1_pll",
> + "di0_sel", "di1_sel",
> + "di0", "di1";
> +
> + lvds-channel@0 {
> + reg = <0>;
> + crtcs = <&ipu 0>;
> + fsl,data-mapping = "spwg";
> + fsl,data-width = <24>;
> +
> + display-timings {
> + /* ... */
> + };
> + };
> +
> + lvds-channel@1 {
> + reg = <1>;
> + crtcs = <&ipu 1>;
> + fsl,data-mapping = "spwg";
> + fsl,data-width = <24>;
> +
> + display-timings {
> + /* ... */
> + };
> + };
> +};
> diff --git a/drivers/staging/imx-drm/Kconfig b/drivers/staging/imx-drm/Kconfig
> index be7e2e3..3b1355c 100644
> --- a/drivers/staging/imx-drm/Kconfig
> +++ b/drivers/staging/imx-drm/Kconfig
> @@ -20,6 +20,14 @@ config DRM_IMX_PARALLEL_DISPLAY
> tristate "Support for parallel displays"
> depends on DRM_IMX
>
> +config DRM_IMX_LDB
> + tristate "Support for LVDS displays"
> + depends on DRM_IMX
> + select OF_VIDEOMODE
> + help
> + Choose this to enable the internal LVDS Display Bridge (LDB)
> + found on i.MX53 and i.MX6 processors.
> +
> config DRM_IMX_IPUV3_CORE
> tristate "IPUv3 core support"
> depends on DRM_IMX
> diff --git a/drivers/staging/imx-drm/Makefile b/drivers/staging/imx-drm/Makefile
> index 83a9056..9bfac13 100644
> --- a/drivers/staging/imx-drm/Makefile
> +++ b/drivers/staging/imx-drm/Makefile
> @@ -4,6 +4,7 @@ imxdrm-objs := imx-drm-core.o imx-fb.o
> obj-$(CONFIG_DRM_IMX) += imxdrm.o
>
> obj-$(CONFIG_DRM_IMX_PARALLEL_DISPLAY) += parallel-display.o
> +obj-$(CONFIG_DRM_IMX_LDB) += imx-ldb.o
> obj-$(CONFIG_DRM_IMX_FB_HELPER) += imx-fbdev.o
> obj-$(CONFIG_DRM_IMX_IPUV3_CORE) += ipu-v3/
> obj-$(CONFIG_DRM_IMX_IPUV3) += ipuv3-crtc.o
> diff --git a/drivers/staging/imx-drm/imx-ldb.c b/drivers/staging/imx-drm/imx-ldb.c
> new file mode 100644
> index 0000000..75607b3
> --- /dev/null
> +++ b/drivers/staging/imx-drm/imx-ldb.c
> @@ -0,0 +1,609 @@
> +/*
> + * i.MX drm driver - LVDS display bridge
> + *
> + * Copyright (C) 2012 Sascha Hauer, Pengutronix
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
> + * MA 02110-1301, USA.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/clk.h>
> +#include <drm/drmP.h>
> +#include <drm/drm_fb_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
> +#include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <video/of_videomode.h>
> +#include <linux/regmap.h>
> +#include <linux/videodev2.h>
> +
> +#include "imx-drm.h"
> +
> +#define DRIVER_NAME "imx-ldb"
> +
> +#define LDB_CH0_MODE_EN_TO_DI0 (1 << 0)
> +#define LDB_CH0_MODE_EN_TO_DI1 (3 << 0)
> +#define LDB_CH0_MODE_EN_MASK (3 << 0)
> +#define LDB_CH1_MODE_EN_TO_DI0 (1 << 2)
> +#define LDB_CH1_MODE_EN_TO_DI1 (3 << 2)
> +#define LDB_CH1_MODE_EN_MASK (3 << 2)
> +#define LDB_SPLIT_MODE_EN (1 << 4)
> +#define LDB_DATA_WIDTH_CH0_24 (1 << 5)
> +#define LDB_BIT_MAP_CH0_JEIDA (1 << 6)
> +#define LDB_DATA_WIDTH_CH1_24 (1 << 7)
> +#define LDB_BIT_MAP_CH1_JEIDA (1 << 8)
> +#define LDB_DI0_VS_POL_ACT_LOW (1 << 9)
> +#define LDB_DI1_VS_POL_ACT_LOW (1 << 10)
> +#define LDB_BGREF_RMODE_INT (1 << 15)
> +
> +#define con_to_imx_ldb_ch(x) container_of(x, struct imx_ldb_channel, connector)
> +#define enc_to_imx_ldb_ch(x) container_of(x, struct imx_ldb_channel, encoder)
> +
> +struct imx_ldb;
> +
> +struct imx_ldb_channel {
> + struct imx_ldb *ldb;
> + struct drm_connector connector;
> + struct imx_drm_connector *imx_drm_connector;
> + struct drm_encoder encoder;
> + struct imx_drm_encoder *imx_drm_encoder;
> + int chno;
> + void *edid;
> + int edid_len;
> + struct drm_display_mode mode;
> + int mode_valid;
> +};
> +
> +struct bus_mux {
> + int reg;
> + int shift;
> + int mask;
> +};
> +
> +struct imx_ldb {
> + struct regmap *regmap;
> + struct device *dev;
> + struct imx_ldb_channel channel[2];
> + struct clk *clk[2]; /* our own clock */
> + struct clk *clk_sel[4]; /* parent of display clock */
> + struct clk *clk_pll[2]; /* upstream clock we can adjust */
> + u32 ldb_ctrl;
> + const struct bus_mux *lvds_mux;
> +};
> +
> +static enum drm_connector_status imx_ldb_connector_detect(
> + struct drm_connector *connector, bool force)
> +{
> + return connector_status_connected;
> +}
> +
> +static void imx_ldb_connector_destroy(struct drm_connector *connector)
> +{
> + /* do not free here */
> +}
> +
> +static int imx_ldb_connector_get_modes(struct drm_connector *connector)
> +{
> + struct imx_ldb_channel *imx_ldb_ch = con_to_imx_ldb_ch(connector);
> + int num_modes = 0;
> +
> + if (imx_ldb_ch->edid) {
> + drm_mode_connector_update_edid_property(connector,
> + imx_ldb_ch->edid);
> + num_modes = drm_add_edid_modes(connector, imx_ldb_ch->edid);
> + }
> +
> + if (imx_ldb_ch->mode_valid) {
> + struct drm_display_mode *mode;
> +
> + mode = drm_mode_create(connector->dev);
> + drm_mode_copy(mode, &imx_ldb_ch->mode);
> + mode->type |= DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
> + drm_mode_probed_add(connector, mode);
> + num_modes++;
> + }
> +
> + return num_modes;
> +}
> +
> +static int imx_ldb_connector_mode_valid(struct drm_connector *connector,
> + struct drm_display_mode *mode)
> +{
> + return 0;
> +}
> +
> +static struct drm_encoder *imx_ldb_connector_best_encoder(
> + struct drm_connector *connector)
> +{
> + struct imx_ldb_channel *imx_ldb_ch = con_to_imx_ldb_ch(connector);
> +
> + return &imx_ldb_ch->encoder;
> +}
> +
> +static void imx_ldb_encoder_dpms(struct drm_encoder *encoder, int mode)
> +{
> +}
> +
> +static bool imx_ldb_encoder_mode_fixup(struct drm_encoder *encoder,
> + const struct drm_display_mode *mode,
> + struct drm_display_mode *adjusted_mode)
> +{
> + return true;
> +}
> +
> +static void imx_ldb_set_clock(struct imx_ldb *ldb, int mux, int chno,
> + unsigned long serial_clk, unsigned long di_clk)
> +{
> + int ret;
> +
> + dev_dbg(ldb->dev, "%s: now: %ld want: %ld\n", __func__,
> + clk_get_rate(ldb->clk_pll[chno]), serial_clk);
> + clk_set_rate(ldb->clk_pll[chno], serial_clk);
> +
> + dev_dbg(ldb->dev, "%s after: %ld\n", __func__,
> + clk_get_rate(ldb->clk_pll[chno]));
> +
> + dev_dbg(ldb->dev, "%s: now: %ld want: %ld\n", __func__,
> + clk_get_rate(ldb->clk[chno]),
> + (long int)di_clk);
> + clk_set_rate(ldb->clk[chno], di_clk);
> +
> + dev_dbg(ldb->dev, "%s after: %ld\n", __func__,
> + clk_get_rate(ldb->clk[chno]));
> +
> + /* set display clock mux to LDB input clock */
I think this will fail on i.MX53 with split mode enable in case of mux != chno, since ipu_di<n> can only be muxed to ldb_di<n>_gate.
> + ret = clk_set_parent(ldb->clk_sel[mux], ldb->clk[chno]);
> + if (ret) {
> + dev_err(ldb->dev, "unable to set di%d parent clock to ldb_di%d\n", mux, chno);
> + }
> +}
> +
> +static void imx_ldb_encoder_prepare(struct drm_encoder *encoder)
> +{
> + struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder);
> + struct imx_ldb *ldb = imx_ldb_ch->ldb;
> + struct drm_display_mode *mode = &encoder->crtc->mode;
> + unsigned long serial_clk;
> + unsigned long di_clk = mode->clock * 1000;
> + int mux = imx_drm_encoder_get_mux_id(imx_ldb_ch->imx_drm_encoder,
> + encoder->crtc);
> +
> + if (ldb->ldb_ctrl & LDB_SPLIT_MODE_EN) {
> + /* dual channel LVDS mode */
> + serial_clk = 3500UL * mode->clock;
See comment in imx_ldb_set_clock
> + imx_ldb_set_clock(ldb, mux, 0, serial_clk, di_clk);
> + imx_ldb_set_clock(ldb, mux, 1, serial_clk, di_clk);
> + } else {
> + serial_clk = 7000UL * mode->clock;
> + imx_ldb_set_clock(ldb, mux, imx_ldb_ch->chno, serial_clk, di_clk);
> + }
> +
> + imx_drm_crtc_panel_format(encoder->crtc, DRM_MODE_ENCODER_LVDS,
> + V4L2_PIX_FMT_RGB24);
> +}
> +
> +static void imx_ldb_encoder_commit(struct drm_encoder *encoder)
> +{
> + struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder);
> + struct imx_ldb *ldb = imx_ldb_ch->ldb;
> + int dual = ldb->ldb_ctrl & LDB_SPLIT_MODE_EN;
> + int mux = imx_drm_encoder_get_mux_id(imx_ldb_ch->imx_drm_encoder,
> + encoder->crtc);
> +
> + if (dual) {
> + clk_prepare_enable(ldb->clk[0]);
> + clk_prepare_enable(ldb->clk[1]);
> + }
> +
> + if (imx_ldb_ch == &ldb->channel[0] || dual) {
> + ldb->ldb_ctrl &= ~LDB_CH0_MODE_EN_MASK;
> + if (mux == 0 || ldb->lvds_mux)
> + ldb->ldb_ctrl |= LDB_CH0_MODE_EN_TO_DI0;
> + else if (mux == 1)
> + ldb->ldb_ctrl |= LDB_CH0_MODE_EN_TO_DI1;
> + }
> + if (imx_ldb_ch == &ldb->channel[1] || dual) {
> + ldb->ldb_ctrl &= ~LDB_CH1_MODE_EN_MASK;
> + if (mux == 1 || ldb->lvds_mux)
> + ldb->ldb_ctrl |= LDB_CH1_MODE_EN_TO_DI1;
> + else if (mux == 0)
> + ldb->ldb_ctrl |= LDB_CH1_MODE_EN_TO_DI0;
> + }
> +
> + if (ldb->lvds_mux) {
> + const struct bus_mux *lvds_mux = NULL;
> +
> + if (imx_ldb_ch == &ldb->channel[0])
> + lvds_mux = &ldb->lvds_mux[0];
> + else if (imx_ldb_ch == &ldb->channel[1])
> + lvds_mux = &ldb->lvds_mux[1];
> +
> + regmap_update_bits(ldb->regmap, lvds_mux->reg, lvds_mux->mask,
> + mux << lvds_mux->shift);
> + }
> +
> + regmap_write(ldb->regmap, IOMUXC_GPR2, ldb->ldb_ctrl);
> +}
> +
> +static void imx_ldb_encoder_mode_set(struct drm_encoder *encoder,
> + struct drm_display_mode *mode,
> + struct drm_display_mode *adjusted_mode)
> +{
> + struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder);
> + struct imx_ldb *ldb = imx_ldb_ch->ldb;
> + int dual = ldb->ldb_ctrl & LDB_SPLIT_MODE_EN;
> +
> + if (mode->clock > 170000) {
> + dev_warn(ldb->dev,
> + "%s: mode exceeds 170 MHz pixel clock\n", __func__);
> + }
> + if (mode->clock > 85000 && !dual) {
> + dev_warn(ldb->dev,
> + "%s: mode exceeds 85 MHz pixel clock\n", __func__);
> + }
> +
> + /* FIXME - assumes straight connections DI0 --> CH0, DI1 --> CH1 */
> + if (imx_ldb_ch == &ldb->channel[0]) {
> + if (mode->flags & DRM_MODE_FLAG_NVSYNC)
> + ldb->ldb_ctrl |= LDB_DI0_VS_POL_ACT_LOW;
> + else if (mode->flags & DRM_MODE_FLAG_PVSYNC)
> + ldb->ldb_ctrl &= ~LDB_DI0_VS_POL_ACT_LOW;
> + }
> + if (imx_ldb_ch == &ldb->channel[1]) {
> + if (mode->flags & DRM_MODE_FLAG_NVSYNC)
> + ldb->ldb_ctrl |= LDB_DI1_VS_POL_ACT_LOW;
> + else if (mode->flags & DRM_MODE_FLAG_PVSYNC)
> + ldb->ldb_ctrl &= ~LDB_DI1_VS_POL_ACT_LOW;
> + }
> +}
> +
> +static void imx_ldb_encoder_disable(struct drm_encoder *encoder)
> +{
> + struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder);
> + struct imx_ldb *ldb = imx_ldb_ch->ldb;
> +
> + /*
> + * imx_ldb_encoder_disable is called by
> + * drm_helper_disable_unused_functions without
> + * the encoder being enabled before.
> + */
> + if (imx_ldb_ch == &ldb->channel[0] &&
> + (ldb->ldb_ctrl & LDB_CH0_MODE_EN_MASK) == 0)
> + return;
> + else if (imx_ldb_ch == &ldb->channel[1] &&
> + (ldb->ldb_ctrl & LDB_CH1_MODE_EN_MASK) == 0)
> + return;
> +
> + if (imx_ldb_ch == &ldb->channel[0])
> + ldb->ldb_ctrl &= ~LDB_CH0_MODE_EN_MASK;
> + else if (imx_ldb_ch == &ldb->channel[1])
> + ldb->ldb_ctrl &= ~LDB_CH1_MODE_EN_MASK;
> +
> + regmap_write(ldb->regmap, IOMUXC_GPR2, ldb->ldb_ctrl);
> +
> + if (ldb->ldb_ctrl & LDB_SPLIT_MODE_EN) {
> + clk_disable_unprepare(ldb->clk[0]);
> + clk_disable_unprepare(ldb->clk[1]);
> + }
> +}
> +
> +static void imx_ldb_encoder_destroy(struct drm_encoder *encoder)
> +{
> + /* do not free here */
> +}
> +
> +static struct drm_connector_funcs imx_ldb_connector_funcs = {
> + .dpms = drm_helper_connector_dpms,
> + .fill_modes = drm_helper_probe_single_connector_modes,
> + .detect = imx_ldb_connector_detect,
> + .destroy = imx_ldb_connector_destroy,
> +};
> +
> +static struct drm_connector_helper_funcs imx_ldb_connector_helper_funcs = {
> + .get_modes = imx_ldb_connector_get_modes,
> + .best_encoder = imx_ldb_connector_best_encoder,
> + .mode_valid = imx_ldb_connector_mode_valid,
> +};
> +
> +static struct drm_encoder_funcs imx_ldb_encoder_funcs = {
> + .destroy = imx_ldb_encoder_destroy,
> +};
> +
> +static struct drm_encoder_helper_funcs imx_ldb_encoder_helper_funcs = {
> + .dpms = imx_ldb_encoder_dpms,
> + .mode_fixup = imx_ldb_encoder_mode_fixup,
> + .prepare = imx_ldb_encoder_prepare,
> + .commit = imx_ldb_encoder_commit,
> + .mode_set = imx_ldb_encoder_mode_set,
> + .disable = imx_ldb_encoder_disable,
> +};
> +
> +static int imx_ldb_get_clk(struct imx_ldb *ldb, int chno)
> +{
> + char clkname[16];
> +
> + sprintf(clkname, "di%d", chno);
> + ldb->clk[chno] = devm_clk_get(ldb->dev, clkname);
> + if (IS_ERR(ldb->clk[chno]))
> + return PTR_ERR(ldb->clk[chno]);
> +
> + sprintf(clkname, "di%d_pll", chno);
> + ldb->clk_pll[chno] = devm_clk_get(ldb->dev, clkname);
> + if (IS_ERR(ldb->clk_pll[chno]))
> + return PTR_ERR(ldb->clk_pll[chno]);
> +
> + return 0;
> +}
> +
> +static int imx_ldb_register(struct imx_ldb_channel *imx_ldb_ch)
> +{
> + int ret;
> + struct imx_ldb *ldb = imx_ldb_ch->ldb;
> +
> + ret = imx_ldb_get_clk(ldb, imx_ldb_ch->chno);
> + if (ret)
> + return ret;
> + if (ldb->ldb_ctrl & LDB_SPLIT_MODE_EN) {
> + ret |= imx_ldb_get_clk(ldb, 1);
> + if (ret)
> + return ret;
> + }
> +
> + imx_ldb_ch->connector.funcs = &imx_ldb_connector_funcs;
> + imx_ldb_ch->encoder.funcs = &imx_ldb_encoder_funcs;
> +
> + imx_ldb_ch->encoder.encoder_type = DRM_MODE_ENCODER_LVDS;
> + imx_ldb_ch->connector.connector_type = DRM_MODE_CONNECTOR_LVDS;
> +
> + drm_encoder_helper_add(&imx_ldb_ch->encoder,
> + &imx_ldb_encoder_helper_funcs);
> + ret = imx_drm_add_encoder(&imx_ldb_ch->encoder,
> + &imx_ldb_ch->imx_drm_encoder, THIS_MODULE);
> + if (ret) {
> + dev_err(ldb->dev, "adding encoder failed with %d\n", ret);
> + return ret;
> + }
> +
> + drm_connector_helper_add(&imx_ldb_ch->connector,
> + &imx_ldb_connector_helper_funcs);
> +
> + ret = imx_drm_add_connector(&imx_ldb_ch->connector,
> + &imx_ldb_ch->imx_drm_connector, THIS_MODULE);
> + if (ret) {
> + imx_drm_remove_encoder(imx_ldb_ch->imx_drm_encoder);
> + dev_err(ldb->dev, "adding connector failed with %d\n", ret);
> + return ret;
> + }
> +
> + drm_mode_connector_attach_encoder(&imx_ldb_ch->connector,
> + &imx_ldb_ch->encoder);
> +
> + return 0;
> +}
> +
> +enum {
> + LVDS_BIT_MAP_SPWG,
> + LVDS_BIT_MAP_JEIDA
> +};
> +
> +static const char *imx_ldb_bit_mappings[] = {
> + [LVDS_BIT_MAP_SPWG] = "spwg",
> + [LVDS_BIT_MAP_JEIDA] = "jeida",
> +};
> +
> +const int of_get_data_mapping(struct device_node *np)
> +{
> + const char *bm;
> + int ret, i;
> +
> + ret = of_property_read_string(np, "fsl,data-mapping", &bm);
> + if (ret < 0)
> + return ret;
> +
> + for (i = 0; i < ARRAY_SIZE(imx_ldb_bit_mappings); i++)
> + if (!strcasecmp(bm, imx_ldb_bit_mappings[i]))
> + return i;
> +
> + return -EINVAL;
> +}
> +
> +static struct bus_mux imx6q_lvds_mux[2] = {
> + {
> + .reg = IOMUXC_GPR3,
> + .shift = 6,
> + .mask = IMX6Q_GPR3_LVDS0_MUX_CTL_MASK,
> + }, {
> + .reg = IOMUXC_GPR3,
> + .shift = 8,
> + .mask = IMX6Q_GPR3_LVDS1_MUX_CTL_MASK,
> + }
> +};
> +
> +/*
> + * For a device declaring compatible = "fsl,imx6q-ldb", "fsl,imx53-ldb",
> + * of_match_device will walk through this list and take the first entry
> + * matching any of its compatible values. Therefore, the more generic
> + * entries (in this case fsl,imx53-ldb) need to be ordered last.
> + */
> +static const struct of_device_id imx_ldb_dt_ids[] = {
> + { .compatible = "fsl,imx6q-ldb", .data = imx6q_lvds_mux, },
> + { .compatible = "fsl,imx53-ldb", .data = NULL, },
> + { }
> +};
> +MODULE_DEVICE_TABLE(of, imx_ldb_dt_ids);
> +
> +static int imx_ldb_probe(struct platform_device *pdev)
> +{
> + struct device_node *np = pdev->dev.of_node;
> + const struct of_device_id *of_id =
> + of_match_device(of_match_ptr(imx_ldb_dt_ids),
> + &pdev->dev);
> + struct device_node *child;
> + const u8 *edidp;
> + struct imx_ldb *imx_ldb;
> + int datawidth;
> + int mapping;
> + int dual;
> + int ret;
> + int i;
> +
> + imx_ldb = devm_kzalloc(&pdev->dev, sizeof(*imx_ldb), GFP_KERNEL);
> + if (!imx_ldb)
> + return -ENOMEM;
> +
> + imx_ldb->regmap = syscon_regmap_lookup_by_phandle(np, "gpr");
> + if (IS_ERR(imx_ldb->regmap)) {
> + dev_err(&pdev->dev, "failed to get parent regmap\n");
> + return PTR_ERR(imx_ldb->regmap);
> + }
> +
> + imx_ldb->dev = &pdev->dev;
> +
> + if (of_id)
> + imx_ldb->lvds_mux = of_id->data;
> +
> + dual = of_property_read_bool(np, "fsl,dual-channel");
> + if (dual)
> + imx_ldb->ldb_ctrl |= LDB_SPLIT_MODE_EN;
> +
> + /*
> + * There are three diferent possible clock mux configurations:
> + * i.MX53: ipu1_di0_sel, ipu1_di1_sel
> + * i.MX6q: ipu1_di0_sel, ipu1_di1_sel, ipu2_di0_sel, ipu2_di1_sel
> + * i.MX6dl: ipu1_di0_sel, ipu1_di1_sel, lcdif_sel
> + * Map them all to di0_sel...di3_sel.
> + */
> + for (i = 0; i < 4; i++) {
> + char clkname[16];
> +
> + sprintf(clkname, "di%d_sel", i);
> + imx_ldb->clk_sel[i] = devm_clk_get(imx_ldb->dev, clkname);
> + if (IS_ERR(imx_ldb->clk_sel[i])) {
> + ret = PTR_ERR(imx_ldb->clk_sel[i]);
> + imx_ldb->clk_sel[i] = NULL;
> + break;
> + }
> + }
> + if (i == 0)
> + return ret;
> +
> + for_each_child_of_node(np, child) {
> + struct imx_ldb_channel *channel;
> +
> + ret = of_property_read_u32(child, "reg", &i);
> + if (ret || i < 0 || i > 1)
> + return -EINVAL;
> +
> + if (dual && i > 0) {
> + dev_warn(&pdev->dev, "dual-channel mode, ignoring second output\n");
> + continue;
> + }
> +
> + if (!of_device_is_available(child))
> + continue;
> +
> + channel = &imx_ldb->channel[i];
> + channel->ldb = imx_ldb;
> + channel->chno = i;
> +
> + edidp = of_get_property(child, "edid", &channel->edid_len);
> + if (edidp) {
> + channel->edid = kmemdup(edidp, channel->edid_len,
> + GFP_KERNEL);
> + } else {
> + ret = of_get_drm_display_mode(child, &channel->mode, 0);
> + if (!ret)
> + channel->mode_valid = 1;
> + }
> +
> + ret = of_property_read_u32(child, "fsl,data-width", &datawidth);
> + if (ret)
> + datawidth = 0;
> + else if (datawidth != 18 && datawidth != 24)
> + return -EINVAL;
> +
> + mapping = of_get_data_mapping(child);
> + switch (mapping) {
> + case LVDS_BIT_MAP_SPWG:
> + if (datawidth == 24) {
> + if (i == 0 || dual)
> + imx_ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH0_24;
> + if (i == 1 || dual)
> + imx_ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH1_24;
> + }
> + break;
> + case LVDS_BIT_MAP_JEIDA:
> + if (datawidth == 18) {
> + dev_err(&pdev->dev, "JEIDA standard only supported in 24 bit\n");
> + return -EINVAL;
> + }
> + if (i == 0 || dual)
> + imx_ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH0_24 | LDB_BIT_MAP_CH0_JEIDA;
> + if (i == 1 || dual)
> + imx_ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH1_24 | LDB_BIT_MAP_CH1_JEIDA;
> + break;
> + default:
> + dev_err(&pdev->dev, "data mapping not specified or invalid\n");
> + return -EINVAL;
> + }
> +
> + ret = imx_ldb_register(channel);
> + if (ret)
> + return ret;
> +
> + imx_drm_encoder_add_possible_crtcs(channel->imx_drm_encoder, child);
> + }
> +
> + platform_set_drvdata(pdev, imx_ldb);
> +
> + return 0;
> +}
> +
> +static int imx_ldb_remove(struct platform_device *pdev)
> +{
> + struct imx_ldb *imx_ldb = platform_get_drvdata(pdev);
> + int i;
> +
> + for (i = 0; i < 2; i++) {
> + struct imx_ldb_channel *channel = &imx_ldb->channel[i];
> + struct drm_connector *connector = &channel->connector;
> + struct drm_encoder *encoder = &channel->encoder;
> +
> + drm_mode_connector_detach_encoder(connector, encoder);
> +
> + imx_drm_remove_connector(channel->imx_drm_connector);
> + imx_drm_remove_encoder(channel->imx_drm_encoder);
> + }
> +
> + return 0;
> +}
> +
> +static struct platform_driver imx_ldb_driver = {
> + .probe = imx_ldb_probe,
> + .remove = imx_ldb_remove,
> + .driver = {
> + .of_match_table = imx_ldb_dt_ids,
> + .name = DRIVER_NAME,
> + .owner = THIS_MODULE,
> + },
> +};
> +
> +module_platform_driver(imx_ldb_driver);
> +
> +MODULE_DESCRIPTION("i.MX LVDS driver");
> +MODULE_AUTHOR("Sascha Hauer, Pengutronix");
> +MODULE_LICENSE("GPL");
>
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v3 06/11] staging: drm/imx: Add LDB support
2013-06-07 7:40 ` Markus Niebel
@ 2013-06-07 7:54 ` Philipp Zabel
0 siblings, 0 replies; 20+ messages in thread
From: Philipp Zabel @ 2013-06-07 7:54 UTC (permalink / raw)
To: list-09
Cc: devel, Fabio Estevam, Sascha Hauer, Greg Kroah-Hartman,
devicetree-discuss, kernel, Martin Fuzzey, Sean Cross, Shawn Guo,
linux-arm-kernel
Am Freitag, den 07.06.2013, 09:40 +0200 schrieb Markus Niebel:
> Am 28.03.2013 16:23, wrote Philipp Zabel:
> > From: Sascha Hauer <s.hauer@pengutronix.de>
> >
> > This adds support for the LVDS Display Bridge contained
> > in i.MX5 and i.MX6 SoCs.
> >
> > Bit mapping, data width, and video timings are configurable
> > via device tree. Dual-channel mode is supported for a single
> > high-resolution source.
> >
> > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> > Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
> > ---
> > Changes since v2:
> > - Removed commented out code
> > - Replaced magic constants
> > - Select OF_VIDEOMODE
> > ---
> > .../devicetree/bindings/staging/imx-drm/ldb.txt | 99 ++++
> > drivers/staging/imx-drm/Kconfig | 8 +
> > drivers/staging/imx-drm/Makefile | 1 +
> > drivers/staging/imx-drm/imx-ldb.c | 609 +++++++++++++++++++++
> > 4 files changed, 717 insertions(+)
> > create mode 100644 Documentation/devicetree/bindings/staging/imx-drm/ldb.txt
> > create mode 100644 drivers/staging/imx-drm/imx-ldb.c
> >
[...]
> > diff --git a/drivers/staging/imx-drm/imx-ldb.c b/drivers/staging/imx-drm/imx-ldb.c
> > new file mode 100644
> > index 0000000..75607b3
> > --- /dev/null
> > +++ b/drivers/staging/imx-drm/imx-ldb.c
> > @@ -0,0 +1,609 @@
[...]
> > +static void imx_ldb_set_clock(struct imx_ldb *ldb, int mux, int chno,
> > + unsigned long serial_clk, unsigned long di_clk)
> > +{
> > + int ret;
> > +
> > + dev_dbg(ldb->dev, "%s: now: %ld want: %ld\n", __func__,
> > + clk_get_rate(ldb->clk_pll[chno]), serial_clk);
> > + clk_set_rate(ldb->clk_pll[chno], serial_clk);
> > +
> > + dev_dbg(ldb->dev, "%s after: %ld\n", __func__,
> > + clk_get_rate(ldb->clk_pll[chno]));
> > +
> > + dev_dbg(ldb->dev, "%s: now: %ld want: %ld\n", __func__,
> > + clk_get_rate(ldb->clk[chno]),
> > + (long int)di_clk);
> > + clk_set_rate(ldb->clk[chno], di_clk);
> > +
> > + dev_dbg(ldb->dev, "%s after: %ld\n", __func__,
> > + clk_get_rate(ldb->clk[chno]));
> > +
> > + /* set display clock mux to LDB input clock */
>
> I think this will fail on i.MX53 with split mode enable in case of
> mux != chno, since ipu_di<n> can only be muxed to ldb_di<n>_gate.
>
>> + ret = clk_set_parent(ldb->clk_sel[mux], ldb->clk[chno]);
That is true. We should use both ldb_di<n>_gates in split mode on
i.MX53, with both ldb_di<n>_sel derived from the same parent.
>> + if (ret) {
>> + dev_err(ldb->dev, "unable to set di%d parent clock to ldb_di%d\n", mux, chno);
>> + }
>> +}
>> +
>> +static void imx_ldb_encoder_prepare(struct drm_encoder *encoder)
>> +{
>> + struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder);
>> + struct imx_ldb *ldb = imx_ldb_ch->ldb;
>> + struct drm_display_mode *mode = &encoder->crtc->mode;
>> + unsigned long serial_clk;
>> + unsigned long di_clk = mode->clock * 1000;
>> + int mux = imx_drm_encoder_get_mux_id(imx_ldb_ch->imx_drm_encoder,
>> + encoder->crtc);
>> +
>> + if (ldb->ldb_ctrl & LDB_SPLIT_MODE_EN) {
>> + /* dual channel LVDS mode */
>> + serial_clk = 3500UL * mode->clock;
>
> See comment in imx_ldb_set_clock
>
>> + imx_ldb_set_clock(ldb, mux, 0, serial_clk, di_clk);
>> + imx_ldb_set_clock(ldb, mux, 1, serial_clk, di_clk);
[...]
regards
Philipp
^ permalink raw reply [flat|nested] 20+ messages in thread
end of thread, other threads:[~2013-06-07 7:54 UTC | newest]
Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-03-28 15:23 [PATCH v3 0/11] LVDS Display Bridge support for i.MX Philipp Zabel
2013-03-28 15:23 ` [PATCH v3 02/11] ARM i.MX6q: export imx6q_revision Philipp Zabel
2013-03-28 15:23 ` [PATCH v3 03/11] ARM i.MX: Add imx_clk_divider_flags and imx_clk_mux_flags Philipp Zabel
2013-03-28 15:23 ` [PATCH v3 04/11] ARM i.MX53: fix ldb di divider and selector clocks Philipp Zabel
2013-03-28 15:23 ` [PATCH v3 05/11] ARM i.MX6q: " Philipp Zabel
2013-03-28 15:23 ` [PATCH v3 06/11] staging: drm/imx: Add LDB support Philipp Zabel
[not found] ` <1364484215-13935-7-git-send-email-p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
2013-06-06 15:16 ` Shawn Guo
2013-06-06 15:54 ` Philipp Zabel
[not found] ` <1370534082.3931.7.camel-/rZezPiN1rtR6QfukMTsflXZhhPuCNm+@public.gmane.org>
2013-06-07 0:26 ` Shawn Guo
2013-06-07 7:40 ` Markus Niebel
2013-06-07 7:54 ` Philipp Zabel
2013-03-28 15:23 ` [PATCH v3 07/11] ARM i.MX6q: Add audio/video PLL post dividers for i.MX6q rev 1.1 Philipp Zabel
2013-03-28 15:23 ` [PATCH v3 08/11] ARM i.MX6q: set the LDB serial clock parent to the video PLL Philipp Zabel
2013-03-28 15:23 ` [PATCH v3 09/11] ARM i.MX53: Add IOMUXC GPR to device tree Philipp Zabel
2013-03-28 15:23 ` [PATCH v3 10/11] ARM i.MX53: Add LDB device " Philipp Zabel
2013-03-28 15:23 ` [PATCH v3 11/11] ARM i.MX6q: " Philipp Zabel
[not found] ` <1364484215-13935-1-git-send-email-p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
2013-03-28 15:23 ` [PATCH v3 01/11] ARM i.MX5: Move IPU clock lookups into " Philipp Zabel
2013-03-29 11:19 ` [PATCH v3 0/11] LVDS Display Bridge support for i.MX Shawn Guo
2013-03-30 11:24 ` Philipp Zabel
2013-03-29 11:44 ` Shawn Guo
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).