aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLiu Ying <Ying.Liu@freescale.com>2014-03-10 06:31:51 -0400
committerNitin Garg <nitin.garg@freescale.com>2014-04-16 09:57:57 -0400
commitcb9327ca90a29af88a3116edd59d5eabe2c0d79e (patch)
tree7f3cc6d757069c86197b78c34f77c7e811d3373b
parent16a37658d909957b8badc4dc835cdc808e2bd350 (diff)
ENGR00302472-6 video: mxc: LDB driver refactor
This patch almost reworks the LDB driver to make the implementation simpler and clearer. The new version should support all the LDB modules embedded in imx53, imx6qdl and imx6sx. The lvds-channel subsidiary DT node is introduced to represent each LVDS channel. People may specify a channel's CRTC, working mode(dual mode or split mode), data width, data mapping, display timing and if it is a primary channel in the node. Change logs: * Use CTRC concept so that the driver may support both IPU and LCDIF as the display engines. * Add mxc dispdrv enable() callback. * Cache LDB ctrl register value at probe()/setup()/ enable() stages and finally write to the register at enable() stage. * Simplify logics for setting ctrl/bus muxing/clocks. * Use regmap to write crtl and bus muxing registers. * Remove LDB description in DT binding doc fsl_ipuv3_fb.txt. Instead, add a new one in fsl,ldb.txt. Signed-off-by: Liu Ying <Ying.Liu@freescale.com>
-rw-r--r--Documentation/devicetree/bindings/fb/fsl_ipuv3_fb.txt45
-rw-r--r--Documentation/devicetree/bindings/video/fsl,ldb.txt95
-rw-r--r--arch/arm/boot/dts/imx6dl-sabreauto.dts9
-rw-r--r--arch/arm/boot/dts/imx6dl-sabresd-common.dtsi9
-rw-r--r--arch/arm/boot/dts/imx6dl.dtsi17
-rw-r--r--arch/arm/boot/dts/imx6q-sabreauto.dts10
-rw-r--r--arch/arm/boot/dts/imx6q-sabresd.dts10
-rw-r--r--arch/arm/boot/dts/imx6q.dtsi17
-rw-r--r--arch/arm/boot/dts/imx6qdl-sabreauto.dtsi49
-rw-r--r--arch/arm/boot/dts/imx6qdl-sabresd.dtsi49
-rw-r--r--arch/arm/boot/dts/imx6qdl.dtsi27
-rw-r--r--drivers/video/mxc/Kconfig1
-rw-r--r--drivers/video/mxc/ldb.c1595
13 files changed, 984 insertions, 949 deletions
diff --git a/Documentation/devicetree/bindings/fb/fsl_ipuv3_fb.txt b/Documentation/devicetree/bindings/fb/fsl_ipuv3_fb.txt
index 3a59da799620..b8d27ef108fe 100644
--- a/Documentation/devicetree/bindings/fb/fsl_ipuv3_fb.txt
+++ b/Documentation/devicetree/bindings/fb/fsl_ipuv3_fb.txt
@@ -8,21 +8,6 @@ display device.
8Two IPU units are on the imx6q SOC while only one IPU unit on the imx6dl SOC. 8Two IPU units are on the imx6q SOC while only one IPU unit on the imx6dl SOC.
9Each IPU unit has two display interfaces. 9Each IPU unit has two display interfaces.
10 10
11For LDB/LVDS panel, there are two LVDS channels(LVDS0 and LVDS1) which can
12transfer video data, these two channels can be used as
13split/dual/single/separate mode.
14-split mode means display data from DI0 or DI1 will send to both channels
15 LVDS0+LVDS1.
16-dual mode means display data from DI0 or DI1 will be duplicated on LVDS0
17 and LVDS1, it said, LVDS0 and LVDS1 has the same content.
18-single mode means only work for DI0/DI1->LVDS0 or DI0/DI1->LVDS1.
19-separate mode means you can make DI0/DI1->LVDS0 and DI0/DI1->LVDS1 work
20 at the same time.
21 "ldb=spl0/1" -- split mode on DI0/1
22 "ldb=dul0/1" -- dual mode on DI0/1
23 "ldb=sin0/1" -- single mode on LVDS0/1
24 "ldb=sep0/1" -- separate mode begin from LVDS0/1
25
26Required properties for IPU: 11Required properties for IPU:
27- bypass_reset :Bypass reset to avoid display channel being. 12- bypass_reset :Bypass reset to avoid display channel being.
28 stopped by probe since it may start to work in bootloader: 0 or 1. 13 stopped by probe since it may start to work in bootloader: 0 or 1.
@@ -37,8 +22,7 @@ Required properties for IPU:
37Required properties for fb: 22Required properties for fb:
38- compatible : should be "fsl,mxc_sdc_fb". 23- compatible : should be "fsl,mxc_sdc_fb".
39- disp_dev : display device: "ldb", "lcd", "hdmi", "mipi_dsi". 24- disp_dev : display device: "ldb", "lcd", "hdmi", "mipi_dsi".
40- mode_str : video mode string: "LDB-XGA" or "LDB-1080P60" for ldb, 25- mode_str : "CLAA-WVGA" for lcd, "TRULY-WVGA" for TRULY mipi_dsi lcd panel,
41 "CLAA-WVGA" for lcd, "TRULY-WVGA" for TRULY mipi_dsi lcd panel,
42 "1920x1080M@60" for hdmi. 26 "1920x1080M@60" for hdmi.
43- default_bpp : default bits per pixel: 8/16/24/32 27- default_bpp : default bits per pixel: 8/16/24/32
44- int_clk : use internal clock as pixel clock: 0 or 1 28- int_clk : use internal clock as pixel clock: 0 or 1
@@ -51,14 +35,13 @@ Required properties for fb:
51 BGR24 IPU_PIX_FMT_BGR24 35 BGR24 IPU_PIX_FMT_BGR24
52 GBR24 IPU_PIX_FMT_GBR24 36 GBR24 IPU_PIX_FMT_GBR24
53 YUV444 IPU_PIX_FMT_YUV444 37 YUV444 IPU_PIX_FMT_YUV444
54 LVDS666 IPU_PIX_FMT_LVDS666
55 YUYV IPU_PIX_FMT_YUYV 38 YUYV IPU_PIX_FMT_YUYV
56 UYVY IPU_PIX_FMT_UYVY 39 UYVY IPU_PIX_FMT_UYVY
57 YVYV IPU_PIX_FMT_YVYU 40 YVYV IPU_PIX_FMT_YVYU
58 VYUY IPU_PIX_FMT_VYUY 41 VYUY IPU_PIX_FMT_VYUY
59 42
60Required properties for display: 43Required properties for display:
61- compatible : should be "fsl,lcd" for lcd panel, "fsl,imx6q-ldb" for ldb 44- compatible : should be "fsl,lcd" for lcd panel
62- reg : the register address range if necessary to have. 45- reg : the register address range if necessary to have.
63- interrupts : the error and sync interrupts if necessary to have. 46- interrupts : the error and sync interrupts if necessary to have.
64- clocks : the clock sources that it depends on if necessary to have. 47- clocks : the clock sources that it depends on if necessary to have.
@@ -69,19 +52,6 @@ Required properties for display:
69- pinctrl-names : should be "default" 52- pinctrl-names : should be "default"
70- pinctrl-0 : should be pinctrl_ipu1_1 or pinctrl_ipu2_1, which depends on the 53- pinctrl-0 : should be pinctrl_ipu1_1 or pinctrl_ipu2_1, which depends on the
71 IPU connected. 54 IPU connected.
72- sec_ipu_id : secondary ipu id for the second display device(ldb only): 0 or 1
73- sec_disp_id : secondary display interface id for the second display
74 device(ldb only): 0 or 1
75- ext_ref : reference resistor select for ldb only: 0 or 1
76- mode : ldb mode as below:
77 spl0 LDB_SPL_DI0
78 spl1 LDB_SPL_DI1
79 dul0 LDB_DUL_DI0
80 dul1 LDB_DUL_DI1
81 sin0 LDB_SIN0
82 sin1 LDB_SIN1
83 sep0 LDB_SEP0
84 sep1 LDB_SEP1
85- gpr : the mux controller for the display engine's display interfaces and the display encoder 55- gpr : the mux controller for the display engine's display interfaces and the display encoder
86 (only valid for mipi dsi now). 56 (only valid for mipi dsi now).
87- disp-power-on-supply : the regulator to control display panel's power. 57- disp-power-on-supply : the regulator to control display panel's power.
@@ -118,17 +88,6 @@ Example for fb:
118 status = "okay"; 88 status = "okay";
119 }; 89 };
120 90
121Example for ldb display:
122 ldb@020e0000 {
123 ipu_id = <1>;
124 disp_id = <0>;
125 ext_ref = <1>;
126 mode = "sep0";
127 sec_ipu_id = <1>;
128 sec_disp_id = <1>;
129 status = "okay";
130 };
131
132Example for mipi dsi display: 91Example for mipi dsi display:
133 mipi_dsi: mipi@021e0000 { 92 mipi_dsi: mipi@021e0000 {
134 compatible = "fsl,imx6q-mipi-dsi"; 93 compatible = "fsl,imx6q-mipi-dsi";
diff --git a/Documentation/devicetree/bindings/video/fsl,ldb.txt b/Documentation/devicetree/bindings/video/fsl,ldb.txt
new file mode 100644
index 000000000000..f843e184f253
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/fsl,ldb.txt
@@ -0,0 +1,95 @@
1* Freescale LVDS Display Bridge(LDB)
2
3The LVDS Display Bridge (LDB) connects a display engine,
4such as IPU and LCDIF, to an external LVDS display interface.
5The purpose of the LDB is to support flow of synchronous RGB
6data from the display engine to external display devices
7through LVDS interfaces. This support covers all aspects of
8these activities:
9* Connectivity to relevant devices - Displays with LVDS receivers.
10* Arranging the data as required by the external display receiver
11 and by LVDS display standards.
12* Synchronization and control capabilities.
13
14Required properties:
15- compatible: Should be "fsl,<soc>-ldb".
16- #address-cells: Must be <1>.
17- #size-cells: Must be <0>.
18- gpr: gpr is the phandle to general purpose register node.
19- clocks: The clocks provided by the SoC to LDB, including ldb_di0/1,
20 dix_sel, ldb_di0/1_div_3_5, ldb_di0/1_div_7 and ldb_di0/1_div_sel
21 clocks.
22- clock-names: Must contain entries for each entry in clocks.
23
24Optional properties:
25- ext-ref: Provide this bool property if your LDB uses an external
26 reference resistor for bandgap.
27- split-mode: Provide this bool property if your board uses LDB split
28 mode to drive a high resolution display, say 1080P@60. In this
29 mode, two LVDS channels will drive one display.
30- dual-mode: Provide this bool property if your board uses LDB dual
31 mode to drive two displays. In this mode, one display engine will
32 drive two displays which have the same timings and display content.
33
34Subnode for LVDS Channel
35========================
36
37Each LVDS Channel has to contain a display-timings node that describes the
38video timings for the connected LVDS display. For detailed information, also
39have a look at Documentation/devicetree/bindings/video/display-timing.txt.
40
41Required properties:
42- reg: Should be <0> or <1>.
43- fsl,data-mapping: Should be "spwg" or "jeida".
44 This describes how the color bits are laid out in the serialized LVDS signal.
45- fsl,data-width: Should be <18> or <24>.
46- crtc: Should be "ipu1/2-di0/1" for i.MX6Q, "ipu1-di0/1" or "LCDIF" for i.MX6DL,
47 "ipu-di0/1" for i.MX53 and "lcdif1/2" for i.MX6sx.
48
49Optional properties:
50- primary: Provide this bool property if this channel is the one found by a
51 framebuffer with a smaller node id. This property is not valid for
52 dual mode and split mode.
53
54ldb: ldb@020e0008 {
55 #address-cells = <1>;
56 #size-cells = <0>;
57 gpr = <&gpr>;
58 compatible = "fsl,imx6dl-ldb", "fsl,imx53-ldb";
59 gpr = <&gpr>;
60 clocks = <&clks 135>, <&clks 136>,
61 <&clks 39>, <&clks 40>,
62 <&clks 41>,
63 <&clks 184>, <&clks 185>,
64 <&clks 205>, <&clks 206>,
65 <&clks 207>, <&clks 208>;
66 clock-names = "ldb_di0", "ldb_di1",
67 "di0_sel", "di1_sel",
68 "di2_sel",
69 "ldb_di0_div_3_5", "ldb_di1_div_3_5",
70 "ldb_di0_div_7", "ldb_di1_div_7",
71 "ldb_di0_div_sel", "ldb_di1_div_sel";
72
73 lvds-channel@0 {
74 reg = <0>;
75 fsl,data-mapping = "spwg";
76 fsl,data-width = <18>;
77 crtc = "ipu1-di0";
78
79 display-timings {
80 /* ... */
81 };
82 };
83
84 lvds-channel@1 {
85 reg = <1>;
86 fsl,data-mapping = "spwg";
87 fsl,data-width = <18>;
88 crtc = "ipu1-di1";
89 primary;
90
91 display-timings {
92 /* ... */
93 };
94 };
95};
diff --git a/arch/arm/boot/dts/imx6dl-sabreauto.dts b/arch/arm/boot/dts/imx6dl-sabreauto.dts
index ac2e29dfb780..9e633b375574 100644
--- a/arch/arm/boot/dts/imx6dl-sabreauto.dts
+++ b/arch/arm/boot/dts/imx6dl-sabreauto.dts
@@ -17,8 +17,13 @@
17}; 17};
18 18
19&ldb { 19&ldb {
20 ipu_id = <0>; 20 lvds-channel@0 {
21 sec_ipu_id = <0>; 21 crtc = "ipu1-di0";
22 };
23
24 lvds-channel@1 {
25 crtc = "ipu1-di1";
26 };
22}; 27};
23 28
24&mxcfb1 { 29&mxcfb1 {
diff --git a/arch/arm/boot/dts/imx6dl-sabresd-common.dtsi b/arch/arm/boot/dts/imx6dl-sabresd-common.dtsi
index 2a075345f58a..515a2c713d4a 100644
--- a/arch/arm/boot/dts/imx6dl-sabresd-common.dtsi
+++ b/arch/arm/boot/dts/imx6dl-sabresd-common.dtsi
@@ -113,8 +113,13 @@
113}; 113};
114 114
115&ldb { 115&ldb {
116 ipu_id = <0>; 116 lvds-channel@0 {
117 sec_ipu_id = <0>; 117 crtc = "ipu1-di0";
118 };
119
120 lvds-channel@1 {
121 crtc = "ipu1-di1";
122 };
118}; 123};
119 124
120&mxcfb1 { 125&mxcfb1 {
diff --git a/arch/arm/boot/dts/imx6dl.dtsi b/arch/arm/boot/dts/imx6dl.dtsi
index abad136c5487..86ed41366031 100644
--- a/arch/arm/boot/dts/imx6dl.dtsi
+++ b/arch/arm/boot/dts/imx6dl.dtsi
@@ -206,3 +206,20 @@
206 }; 206 };
207 }; 207 };
208}; 208};
209
210&ldb {
211 compatible = "fsl,imx6dl-ldb", "fsl,imx53-ldb";
212
213 clocks = <&clks 135>, <&clks 136>,
214 <&clks 39>, <&clks 40>,
215 <&clks 41>,
216 <&clks 184>, <&clks 185>,
217 <&clks 205>, <&clks 206>,
218 <&clks 207>, <&clks 208>;
219 clock-names = "ldb_di0", "ldb_di1",
220 "di0_sel", "di1_sel",
221 "di2_sel",
222 "ldb_di0_div_3_5", "ldb_di1_div_3_5",
223 "ldb_di0_div_7", "ldb_di1_div_7",
224 "ldb_di0_div_sel", "ldb_di1_div_sel";
225};
diff --git a/arch/arm/boot/dts/imx6q-sabreauto.dts b/arch/arm/boot/dts/imx6q-sabreauto.dts
index 8a50ca1baec7..ddd493111e5d 100644
--- a/arch/arm/boot/dts/imx6q-sabreauto.dts
+++ b/arch/arm/boot/dts/imx6q-sabreauto.dts
@@ -20,6 +20,16 @@
20 compatible = "fsl,imx6q-sabreauto", "fsl,imx6q"; 20 compatible = "fsl,imx6q-sabreauto", "fsl,imx6q";
21}; 21};
22 22
23&ldb {
24 lvds-channel@0 {
25 crtc = "ipu2-di0";
26 };
27
28 lvds-channel@1 {
29 crtc = "ipu2-di1";
30 };
31};
32
23&mxcfb1 { 33&mxcfb1 {
24 status = "okay"; 34 status = "okay";
25}; 35};
diff --git a/arch/arm/boot/dts/imx6q-sabresd.dts b/arch/arm/boot/dts/imx6q-sabresd.dts
index 5e5ff56690e6..4f17cb58ed9f 100644
--- a/arch/arm/boot/dts/imx6q-sabresd.dts
+++ b/arch/arm/boot/dts/imx6q-sabresd.dts
@@ -127,6 +127,16 @@
127 }; 127 };
128}; 128};
129 129
130&ldb {
131 lvds-channel@0 {
132 crtc = "ipu2-di0";
133 };
134
135 lvds-channel@1 {
136 crtc = "ipu2-di1";
137 };
138};
139
130&mxcfb1 { 140&mxcfb1 {
131 status = "okay"; 141 status = "okay";
132}; 142};
diff --git a/arch/arm/boot/dts/imx6q.dtsi b/arch/arm/boot/dts/imx6q.dtsi
index 794b4ce825b8..69c8eacc4f82 100644
--- a/arch/arm/boot/dts/imx6q.dtsi
+++ b/arch/arm/boot/dts/imx6q.dtsi
@@ -242,3 +242,20 @@
242 }; 242 };
243 }; 243 };
244}; 244};
245
246&ldb {
247 compatible = "fsl,imx6q-ldb", "fsl,imx53-ldb";
248
249 clocks = <&clks 135>, <&clks 136>,
250 <&clks 39>, <&clks 40>,
251 <&clks 41>, <&clks 42>,
252 <&clks 184>, <&clks 185>,
253 <&clks 205>, <&clks 206>,
254 <&clks 207>, <&clks 208>;
255 clock-names = "ldb_di0", "ldb_di1",
256 "di0_sel", "di1_sel",
257 "di2_sel", "di3_sel",
258 "ldb_di0_div_3_5", "ldb_di1_div_3_5",
259 "ldb_di0_div_7", "ldb_di1_div_7",
260 "ldb_di0_div_sel", "ldb_di1_div_sel";
261};
diff --git a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
index d9c3af9f58e9..c5dacc0d528d 100644
--- a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
@@ -588,13 +588,50 @@
588}; 588};
589 589
590&ldb { 590&ldb {
591 ipu_id = <1>;
592 disp_id = <0>;
593 ext_ref = <1>;
594 mode = "sep0";
595 sec_ipu_id = <1>;
596 sec_disp_id = <1>;
597 status = "okay"; 591 status = "okay";
592
593 lvds-channel@0 {
594 fsl,data-mapping = "spwg";
595 fsl,data-width = <18>;
596 primary;
597 status = "okay";
598
599 display-timings {
600 native-mode = <&timing0>;
601 timing0: hsd100pxn1 {
602 clock-frequency = <65000000>;
603 hactive = <1024>;
604 vactive = <768>;
605 hback-porch = <220>;
606 hfront-porch = <40>;
607 vback-porch = <21>;
608 vfront-porch = <7>;
609 hsync-len = <60>;
610 vsync-len = <10>;
611 };
612 };
613 };
614
615 lvds-channel@1 {
616 fsl,data-mapping = "spwg";
617 fsl,data-width = <18>;
618 status = "okay";
619
620 display-timings {
621 native-mode = <&timing1>;
622 timing1: hsd100pxn1 {
623 clock-frequency = <65000000>;
624 hactive = <1024>;
625 vactive = <768>;
626 hback-porch = <220>;
627 hfront-porch = <40>;
628 vback-porch = <21>;
629 vfront-porch = <7>;
630 hsync-len = <60>;
631 vsync-len = <10>;
632 };
633 };
634 };
598}; 635};
599 636
600&mipi_csi { 637&mipi_csi {
diff --git a/arch/arm/boot/dts/imx6qdl-sabresd.dtsi b/arch/arm/boot/dts/imx6qdl-sabresd.dtsi
index 5d774e5a7ec7..8caa16b45094 100644
--- a/arch/arm/boot/dts/imx6qdl-sabresd.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-sabresd.dtsi
@@ -502,13 +502,50 @@
502}; 502};
503 503
504&ldb { 504&ldb {
505 ipu_id = <1>;
506 disp_id = <1>;
507 ext_ref = <1>;
508 mode = "sep1";
509 sec_ipu_id = <1>;
510 sec_disp_id = <0>;
511 status = "okay"; 505 status = "okay";
506
507 lvds-channel@0 {
508 fsl,data-mapping = "spwg";
509 fsl,data-width = <18>;
510 status = "okay";
511
512 display-timings {
513 native-mode = <&timing0>;
514 timing0: hsd100pxn1 {
515 clock-frequency = <65000000>;
516 hactive = <1024>;
517 vactive = <768>;
518 hback-porch = <220>;
519 hfront-porch = <40>;
520 vback-porch = <21>;
521 vfront-porch = <7>;
522 hsync-len = <60>;
523 vsync-len = <10>;
524 };
525 };
526 };
527
528 lvds-channel@1 {
529 fsl,data-mapping = "spwg";
530 fsl,data-width = <18>;
531 primary;
532 status = "okay";
533
534 display-timings {
535 native-mode = <&timing1>;
536 timing1: hsd100pxn1 {
537 clock-frequency = <65000000>;
538 hactive = <1024>;
539 vactive = <768>;
540 hback-porch = <220>;
541 hfront-porch = <40>;
542 vback-porch = <21>;
543 vfront-porch = <7>;
544 hsync-len = <60>;
545 vsync-len = <10>;
546 };
547 };
548 };
512}; 549};
513 550
514&mipi_csi { 551&mipi_csi {
diff --git a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi
index a31e566c7da3..01414a575ab6 100644
--- a/arch/arm/boot/dts/imx6qdl.dtsi
+++ b/arch/arm/boot/dts/imx6qdl.dtsi
@@ -690,21 +690,20 @@
690 }; 690 };
691 691
692 ldb: ldb@020e0008 { 692 ldb: ldb@020e0008 {
693 compatible = "fsl,imx6q-ldb", "fsl,imx53-ldb"; 693 #address-cells = <1>;
694 reg = <0x020e0000 0x4000>; 694 #size-cells = <0>;
695 clocks = <&clks 135>, <&clks 136>, 695 gpr = <&gpr>;
696 <&clks 39>, <&clks 40>,
697 <&clks 41>, <&clks 42>,
698 <&clks 184>, <&clks 185>,
699 <&clks 205>, <&clks 206>,
700 <&clks 207>, <&clks 208>;
701 clock-names = "ldb_di0", "ldb_di1",
702 "ipu1_di0_sel", "ipu1_di1_sel",
703 "ipu2_di0_sel", "ipu2_di1_sel",
704 "di0_div_3_5", "di1_div_3_5",
705 "di0_div_7", "di1_div_7",
706 "di0_div_sel", "di1_div_sel";
707 status = "disabled"; 696 status = "disabled";
697
698 lvds-channel@0 {
699 reg = <0>;
700 status = "disabled";
701 };
702
703 lvds-channel@1 {
704 reg = <1>;
705 status = "disabled";
706 };
708 }; 707 };
709 708
710 dcic1: dcic@020e4000 { 709 dcic1: dcic@020e4000 {
diff --git a/drivers/video/mxc/Kconfig b/drivers/video/mxc/Kconfig
index 2269de1e02cb..a86288adedbf 100644
--- a/drivers/video/mxc/Kconfig
+++ b/drivers/video/mxc/Kconfig
@@ -22,6 +22,7 @@ config FB_MXC_LDB
22 tristate "MXC LDB" 22 tristate "MXC LDB"
23 depends on FB_MXC_SYNC_PANEL 23 depends on FB_MXC_SYNC_PANEL
24 depends on MXC_IPU_V3 24 depends on MXC_IPU_V3
25 select VIDEOMODE_HELPERS
25 26
26config FB_MXC_MIPI_DSI 27config FB_MXC_MIPI_DSI
27 tristate "MXC MIPI_DSI" 28 tristate "MXC MIPI_DSI"
diff --git a/drivers/video/mxc/ldb.c b/drivers/video/mxc/ldb.c
index 8e9f26011146..08ca0243a5af 100644
--- a/drivers/video/mxc/ldb.c
+++ b/drivers/video/mxc/ldb.c
@@ -18,1024 +18,867 @@
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */ 19 */
20 20
21/*!
22 * @file mxc_ldb.c
23 *
24 * @brief This file contains the LDB driver device interface and fops
25 * functions.
26 */
27#include <linux/types.h>
28#include <linux/init.h>
29#include <linux/module.h>
30#include <linux/platform_device.h>
31#include <linux/err.h>
32#include <linux/clk.h> 21#include <linux/clk.h>
33#include <linux/console.h> 22#include <linux/err.h>
23#include <linux/init.h>
34#include <linux/io.h> 24#include <linux/io.h>
35#include <linux/ipu.h> 25#include <linux/mfd/syscon.h>
36#include <linux/mxcfb.h> 26#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
37#include <linux/regulator/consumer.h> 27#include <linux/module.h>
38#include <linux/spinlock.h>
39#include <linux/of_device.h> 28#include <linux/of_device.h>
40#include <linux/mod_devicetable.h> 29#include <linux/platform_device.h>
30#include <linux/regmap.h>
31#include <linux/types.h>
32#include <video/of_videomode.h>
33#include <video/videomode.h>
41#include "mxc_dispdrv.h" 34#include "mxc_dispdrv.h"
42 35
43#define DISPDRV_LDB "ldb" 36#define DRIVER_NAME "ldb"
44
45#define LDB_BGREF_RMODE_MASK 0x00008000
46#define LDB_BGREF_RMODE_INT 0x00008000
47#define LDB_BGREF_RMODE_EXT 0x0
48
49#define LDB_DI1_VS_POL_MASK 0x00000400
50#define LDB_DI1_VS_POL_ACT_LOW 0x00000400
51#define LDB_DI1_VS_POL_ACT_HIGH 0x0
52#define LDB_DI0_VS_POL_MASK 0x00000200
53#define LDB_DI0_VS_POL_ACT_LOW 0x00000200
54#define LDB_DI0_VS_POL_ACT_HIGH 0x0
55
56#define LDB_BIT_MAP_CH1_MASK 0x00000100
57#define LDB_BIT_MAP_CH1_JEIDA 0x00000100
58#define LDB_BIT_MAP_CH1_SPWG 0x0
59#define LDB_BIT_MAP_CH0_MASK 0x00000040
60#define LDB_BIT_MAP_CH0_JEIDA 0x00000040
61#define LDB_BIT_MAP_CH0_SPWG 0x0
62
63#define LDB_DATA_WIDTH_CH1_MASK 0x00000080
64#define LDB_DATA_WIDTH_CH1_24 0x00000080
65#define LDB_DATA_WIDTH_CH1_18 0x0
66#define LDB_DATA_WIDTH_CH0_MASK 0x00000020
67#define LDB_DATA_WIDTH_CH0_24 0x00000020
68#define LDB_DATA_WIDTH_CH0_18 0x0
69
70#define LDB_CH1_MODE_MASK 0x0000000C
71#define LDB_CH1_MODE_EN_TO_DI1 0x0000000C
72#define LDB_CH1_MODE_EN_TO_DI0 0x00000004
73#define LDB_CH1_MODE_DISABLE 0x0
74#define LDB_CH0_MODE_MASK 0x00000003
75#define LDB_CH0_MODE_EN_TO_DI1 0x00000003
76#define LDB_CH0_MODE_EN_TO_DI0 0x00000001
77#define LDB_CH0_MODE_DISABLE 0x0
78
79#define LDB_SPLIT_MODE_EN 0x00000010
80 37
81enum { 38#define LDB_BGREF_RMODE_INT (0x1 << 15)
82 IMX6_LDB, 39
40#define LDB_DI1_VS_POL_ACT_LOW (0x1 << 10)
41#define LDB_DI0_VS_POL_ACT_LOW (0x1 << 9)
42
43#define LDB_BIT_MAP_CH1_JEIDA (0x1 << 8)
44#define LDB_BIT_MAP_CH0_JEIDA (0x1 << 6)
45
46#define LDB_DATA_WIDTH_CH1_24 (0x1 << 7)
47#define LDB_DATA_WIDTH_CH0_24 (0x1 << 5)
48
49#define LDB_CH1_MODE_MASK (0x3 << 2)
50#define LDB_CH1_MODE_EN_TO_DI1 (0x3 << 2)
51#define LDB_CH1_MODE_EN_TO_DI0 (0x1 << 2)
52#define LDB_CH0_MODE_MASK (0x3 << 0)
53#define LDB_CH0_MODE_EN_TO_DI1 (0x3 << 0)
54#define LDB_CH0_MODE_EN_TO_DI0 (0x1 << 0)
55
56#define LDB_SPLIT_MODE_EN (0x1 << 4)
57
58#define INVALID_BUS_REG (~0UL)
59
60struct crtc_mux {
61 enum crtc crtc;
62 u32 val;
83}; 63};
84 64
85enum { 65struct bus_mux {
86 LDB_IMX6 = 1, 66 int reg;
67 int shift;
68 int mask;
69 int crtc_mux_num;
70 const struct crtc_mux *crtcs;
71};
72
73struct ldb_info {
74 bool split_cap;
75 bool dual_cap;
76 bool ext_bgref_cap;
77 int ctrl_reg;
78 int bus_mux_num;
79 const struct bus_mux *buses;
87}; 80};
88 81
89struct fsl_mxc_ldb_platform_data { 82struct ldb_data;
90 int devtype; 83
91 u32 ext_ref; 84struct ldb_chan {
92#define LDB_SPL_DI0 1 85 struct ldb_data *ldb;
93#define LDB_SPL_DI1 2 86 struct fb_info *fbi;
94#define LDB_DUL_DI0 3 87 struct videomode vm;
95#define LDB_DUL_DI1 4 88 enum crtc crtc;
96#define LDB_SIN0 5 89 int chno;
97#define LDB_SIN1 6 90 bool is_used;
98#define LDB_SEP0 7 91 bool online;
99#define LDB_SEP1 8
100 int mode;
101 int ipu_id;
102 int disp_id;
103
104 /*only work for separate mode*/
105 int sec_ipu_id;
106 int sec_disp_id;
107}; 92};
108 93
109struct ldb_data { 94struct ldb_data {
110 struct platform_device *pdev; 95 struct regmap *regmap;
111 struct mxc_dispdrv_handle *disp_ldb; 96 struct device *dev;
112 uint32_t *reg; 97 struct mxc_dispdrv_handle *mddh;
113 uint32_t *control_reg; 98 struct ldb_chan chan[2];
114 uint32_t *gpr3_reg; 99 int bus_mux_num;
115 uint32_t control_reg_data; 100 const struct bus_mux *buses;
116 struct regulator *lvds_bg_reg; 101 int primary_chno;
117 int mode; 102 int ctrl_reg;
118 bool inited; 103 u32 ctrl;
119 struct ldb_setting { 104 bool spl_mode;
120 struct clk *di_clk; 105 bool dual_mode;
121 struct clk *ldb_di_clk; 106 struct clk *di_clk[4];
122 struct clk *div_3_5_clk; 107 struct clk *ldb_di_clk[2];
123 struct clk *div_7_clk; 108 struct clk *div_3_5_clk[2];
124 struct clk *div_sel_clk; 109 struct clk *div_7_clk[2];
125 bool active; 110 struct clk *div_sel_clk[2];
126 bool clk_en;
127 int ipu;
128 int di;
129 uint32_t ch_mask;
130 uint32_t ch_val;
131 } setting[2];
132 struct notifier_block nb;
133}; 111};
134 112
135static int g_ldb_mode; 113static const struct crtc_mux imx6q_lvds0_crtc_mux[] = {
114 {
115 .crtc = CRTC_IPU1_DI0,
116 .val = IMX6Q_GPR3_LVDS0_MUX_CTL_IPU1_DI0,
117 }, {
118 .crtc = CRTC_IPU1_DI1,
119 .val = IMX6Q_GPR3_LVDS0_MUX_CTL_IPU1_DI1,
120 }, {
121 .crtc = CRTC_IPU2_DI0,
122 .val = IMX6Q_GPR3_LVDS0_MUX_CTL_IPU2_DI0,
123 }, {
124 .crtc = CRTC_IPU2_DI1,
125 .val = IMX6Q_GPR3_LVDS0_MUX_CTL_IPU2_DI1,
126 }
127};
136 128
137static struct fb_videomode ldb_modedb[] = { 129static const struct crtc_mux imx6q_lvds1_crtc_mux[] = {
138 { 130 {
139 "LDB-WXGA", 60, 1280, 800, 14065, 131 .crtc = CRTC_IPU1_DI0,
140 40, 40, 132 .val = IMX6Q_GPR3_LVDS1_MUX_CTL_IPU1_DI0,
141 10, 3, 133 }, {
142 80, 10, 134 .crtc = CRTC_IPU1_DI1,
143 0, 135 .val = IMX6Q_GPR3_LVDS1_MUX_CTL_IPU1_DI1,
144 FB_VMODE_NONINTERLACED, 136 }, {
145 FB_MODE_IS_DETAILED,}, 137 .crtc = CRTC_IPU2_DI0,
138 .val = IMX6Q_GPR3_LVDS1_MUX_CTL_IPU2_DI0,
139 }, {
140 .crtc = CRTC_IPU2_DI1,
141 .val = IMX6Q_GPR3_LVDS1_MUX_CTL_IPU2_DI1,
142 }
143};
144
145static const struct bus_mux imx6q_ldb_buses[] = {
146 { 146 {
147 "LDB-XGA", 60, 1024, 768, 15385, 147 .reg = IOMUXC_GPR3,
148 220, 40, 148 .shift = 6,
149 21, 7, 149 .mask = IMX6Q_GPR3_LVDS0_MUX_CTL_MASK,
150 60, 10, 150 .crtc_mux_num = ARRAY_SIZE(imx6q_lvds0_crtc_mux),
151 0, 151 .crtcs = imx6q_lvds0_crtc_mux,
152 FB_VMODE_NONINTERLACED, 152 }, {
153 FB_MODE_IS_DETAILED,}, 153 .reg = IOMUXC_GPR3,
154 .shift = 8,
155 .mask = IMX6Q_GPR3_LVDS1_MUX_CTL_MASK,
156 .crtc_mux_num = ARRAY_SIZE(imx6q_lvds1_crtc_mux),
157 .crtcs = imx6q_lvds1_crtc_mux,
158 }
159};
160
161static const struct ldb_info imx6q_ldb_info = {
162 .split_cap = true,
163 .dual_cap = true,
164 .ext_bgref_cap = false,
165 .ctrl_reg = IOMUXC_GPR2,
166 .bus_mux_num = ARRAY_SIZE(imx6q_ldb_buses),
167 .buses = imx6q_ldb_buses,
168};
169
170static const struct crtc_mux imx6dl_lvds0_crtc_mux[] = {
154 { 171 {
155 "LDB-1080P60", 60, 1920, 1080, 7692, 172 .crtc = CRTC_IPU1_DI0,
156 100, 40, 173 .val = IMX6DL_GPR3_LVDS0_MUX_CTL_IPU1_DI0,
157 30, 3, 174 }, {
158 10, 2, 175 .crtc = CRTC_IPU1_DI1,
159 0, 176 .val = IMX6DL_GPR3_LVDS0_MUX_CTL_IPU1_DI1,
160 FB_VMODE_NONINTERLACED, 177 }, {
161 FB_MODE_IS_DETAILED,}, 178 .crtc = CRTC_LCDIF1,
179 .val = IMX6DL_GPR3_LVDS0_MUX_CTL_LCDIF,
180 }
162}; 181};
163static int ldb_modedb_sz = ARRAY_SIZE(ldb_modedb);
164 182
165static inline int is_imx6_ldb(struct fsl_mxc_ldb_platform_data *plat_data) 183static const struct crtc_mux imx6dl_lvds1_crtc_mux[] = {
166{ 184 {
167 return (plat_data->devtype == LDB_IMX6); 185 .crtc = CRTC_IPU1_DI0,
168} 186 .val = IMX6DL_GPR3_LVDS1_MUX_CTL_IPU1_DI0,
187 }, {
188 .crtc = CRTC_IPU1_DI1,
189 .val = IMX6DL_GPR3_LVDS1_MUX_CTL_IPU1_DI1,
190 }, {
191 .crtc = CRTC_LCDIF1,
192 .val = IMX6DL_GPR3_LVDS1_MUX_CTL_LCDIF,
193 }
194};
195
196static const struct bus_mux imx6dl_ldb_buses[] = {
197 {
198 .reg = IOMUXC_GPR3,
199 .shift = 6,
200 .mask = IMX6DL_GPR3_LVDS0_MUX_CTL_MASK,
201 .crtc_mux_num = ARRAY_SIZE(imx6dl_lvds0_crtc_mux),
202 .crtcs = imx6dl_lvds0_crtc_mux,
203 }, {
204 .reg = IOMUXC_GPR3,
205 .shift = 8,
206 .mask = IMX6DL_GPR3_LVDS1_MUX_CTL_MASK,
207 .crtc_mux_num = ARRAY_SIZE(imx6dl_lvds1_crtc_mux),
208 .crtcs = imx6dl_lvds1_crtc_mux,
209 }
210};
211
212static const struct ldb_info imx6dl_ldb_info = {
213 .split_cap = true,
214 .dual_cap = true,
215 .ext_bgref_cap = false,
216 .ctrl_reg = IOMUXC_GPR2,
217 .bus_mux_num = ARRAY_SIZE(imx6dl_ldb_buses),
218 .buses = imx6dl_ldb_buses,
219};
220
221static const struct crtc_mux imx6sx_lvds_crtc_mux[] = {
222 {
223 .crtc = CRTC_LCDIF1,
224 .val = IMX6SX_GPR5_DISP_MUX_LDB_CTRL_LCDIF1,
225 }, {
226 .crtc = CRTC_LCDIF2,
227 .val = IMX6SX_GPR5_DISP_MUX_LDB_CTRL_LCDIF2,
228 }
229};
230
231static const struct bus_mux imx6sx_ldb_buses[] = {
232 {
233 .reg = IOMUXC_GPR5,
234 .shift = 3,
235 .mask = IMX6SX_GPR5_DISP_MUX_LDB_CTRL_MASK,
236 .crtc_mux_num = ARRAY_SIZE(imx6sx_lvds_crtc_mux),
237 .crtcs = imx6sx_lvds_crtc_mux,
238 }
239};
240
241static const struct ldb_info imx6sx_ldb_info = {
242 .split_cap = false,
243 .dual_cap = false,
244 .ext_bgref_cap = false,
245 .ctrl_reg = IOMUXC_GPR6,
246 .bus_mux_num = ARRAY_SIZE(imx6sx_ldb_buses),
247 .buses = imx6sx_ldb_buses,
248};
249
250static const struct crtc_mux imx53_lvds0_crtc_mux[] = {
251 { .crtc = CRTC_IPU1_DI0, },
252};
253
254static const struct crtc_mux imx53_lvds1_crtc_mux[] = {
255 { .crtc = CRTC_IPU1_DI1, }
256};
257
258static const struct bus_mux imx53_ldb_buses[] = {
259 {
260 .reg = INVALID_BUS_REG,
261 .crtc_mux_num = ARRAY_SIZE(imx53_lvds0_crtc_mux),
262 .crtcs = imx53_lvds0_crtc_mux,
263 }, {
264 .reg = INVALID_BUS_REG,
265 .crtc_mux_num = ARRAY_SIZE(imx53_lvds1_crtc_mux),
266 .crtcs = imx53_lvds1_crtc_mux,
267 }
268};
269
270static const struct ldb_info imx53_ldb_info = {
271 .split_cap = true,
272 .dual_cap = false,
273 .ext_bgref_cap = true,
274 .ctrl_reg = IOMUXC_GPR2,
275 .bus_mux_num = ARRAY_SIZE(imx53_ldb_buses),
276 .buses = imx53_ldb_buses,
277};
169 278
170static int bits_per_pixel(int pixel_fmt) 279static const struct of_device_id ldb_dt_ids[] = {
280 { .compatible = "fsl,imx6q-ldb", .data = &imx6q_ldb_info, },
281 { .compatible = "fsl,imx6dl-ldb", .data = &imx6dl_ldb_info, },
282 { .compatible = "fsl,imx6sx-ldb", .data = &imx6sx_ldb_info, },
283 { .compatible = "fsl,imx53-ldb", .data = &imx53_ldb_info, },
284 { /* sentinel */ }
285};
286MODULE_DEVICE_TABLE(of, ldb_dt_ids);
287
288static int ldb_init(struct mxc_dispdrv_handle *mddh,
289 struct mxc_dispdrv_setting *setting)
171{ 290{
172 switch (pixel_fmt) { 291 struct ldb_data *ldb = mxc_dispdrv_getdata(mddh);
173 case IPU_PIX_FMT_BGR24: 292 struct device *dev = ldb->dev;
174 case IPU_PIX_FMT_RGB24: 293 struct fb_info *fbi = setting->fbi;
175 return 24; 294 struct ldb_chan *chan;
176 break; 295 struct fb_videomode fb_vm;
177 case IPU_PIX_FMT_BGR666: 296 int chno;
178 case IPU_PIX_FMT_RGB666: 297
179 case IPU_PIX_FMT_LVDS666: 298 chno = ldb->chan[ldb->primary_chno].is_used ?
180 return 18; 299 !ldb->primary_chno : ldb->primary_chno;
181 break; 300
182 default: 301 chan = &ldb->chan[chno];
183 break; 302
303 if (chan->is_used) {
304 dev_err(dev, "LVDS channel%d is already used\n", chno);
305 return -EBUSY;
306 }
307 if (!chan->online) {
308 dev_err(dev, "LVDS channel%d is not online\n", chno);
309 return -ENODEV;
184 } 310 }
311
312 chan->is_used = true;
313
314 chan->fbi = fbi;
315
316 fb_videomode_from_videomode(&chan->vm, &fb_vm);
317
318 INIT_LIST_HEAD(&fbi->modelist);
319 fb_add_videomode(&fb_vm, &fbi->modelist);
320 fb_videomode_to_var(&fbi->var, &fb_vm);
321
322 setting->crtc = chan->crtc;
323
185 return 0; 324 return 0;
186} 325}
187 326
188static int valid_mode(int pixel_fmt) 327static int get_di_clk_id(struct ldb_chan chan, int *id)
189{ 328{
190 return ((pixel_fmt == IPU_PIX_FMT_RGB24) || 329 struct ldb_data *ldb = chan.ldb;
191 (pixel_fmt == IPU_PIX_FMT_BGR24) || 330 int i = 0, chno = chan.chno, mask, shift;
192 (pixel_fmt == IPU_PIX_FMT_LVDS666) || 331 enum crtc crtc;
193 (pixel_fmt == IPU_PIX_FMT_RGB666) || 332 u32 val;
194 (pixel_fmt == IPU_PIX_FMT_BGR666)); 333
195} 334 /* no pre-muxing, such as mx53 */
335 if (ldb->buses[chno].reg == INVALID_BUS_REG) {
336 *id = chno;
337 return 0;
338 }
196 339
197static int parse_ldb_mode(char *mode) 340 for (; i < ldb->buses[chno].crtc_mux_num; i++) {
198{ 341 crtc = ldb->buses[chno].crtcs[i].crtc;
199 int ldb_mode; 342 val = ldb->buses[chno].crtcs[i].val;
200 343 mask = ldb->buses[chno].mask;
201 if (!strcmp(mode, "spl0")) 344 shift = ldb->buses[chno].shift;
202 ldb_mode = LDB_SPL_DI0; 345 if (chan.crtc == crtc) {
203 else if (!strcmp(mode, "spl1")) 346 *id = (val & mask) >> shift;
204 ldb_mode = LDB_SPL_DI1; 347 return 0;
205 else if (!strcmp(mode, "dul0")) 348 }
206 ldb_mode = LDB_DUL_DI0; 349 }
207 else if (!strcmp(mode, "dul1"))
208 ldb_mode = LDB_DUL_DI1;
209 else if (!strcmp(mode, "sin0"))
210 ldb_mode = LDB_SIN0;
211 else if (!strcmp(mode, "sin1"))
212 ldb_mode = LDB_SIN1;
213 else if (!strcmp(mode, "sep0"))
214 ldb_mode = LDB_SEP0;
215 else if (!strcmp(mode, "sep1"))
216 ldb_mode = LDB_SEP1;
217 else
218 ldb_mode = -EINVAL;
219
220 return ldb_mode;
221}
222 350
223#ifndef MODULE 351 return -EINVAL;
224/*
225 * "ldb=spl0/1" -- split mode on DI0/1
226 * "ldb=dul0/1" -- dual mode on DI0/1
227 * "ldb=sin0/1" -- single mode on LVDS0/1
228 * "ldb=sep0/1" -- separate mode begin from LVDS0/1
229 *
230 * there are two LVDS channels(LVDS0 and LVDS1) which can transfer video
231 * datas, there two channels can be used as split/dual/single/separate mode.
232 *
233 * split mode means display data from DI0 or DI1 will send to both channels
234 * LVDS0+LVDS1.
235 * dual mode means display data from DI0 or DI1 will be duplicated on LVDS0
236 * and LVDS1, it said, LVDS0 and LVDS1 has the same content.
237 * single mode means only work for DI0/DI1->LVDS0 or DI0/DI1->LVDS1.
238 * separate mode means you can make DI0/DI1->LVDS0 and DI0/DI1->LVDS1 work
239 * at the same time.
240 */
241static int __init ldb_setup(char *options)
242{
243 g_ldb_mode = parse_ldb_mode(options);
244 return (g_ldb_mode < 0) ? 0 : 1;
245} 352}
246__setup("ldb=", ldb_setup);
247#endif
248 353
249static int ldb_get_of_property(struct platform_device *pdev, 354static int get_mux_val(struct bus_mux bus_mux, enum crtc crtc,
250 struct fsl_mxc_ldb_platform_data *plat_data) 355 u32 *mux_val)
251{ 356{
252 struct device_node *np = pdev->dev.of_node; 357 int i = 0;
253 int err;
254 u32 ipu_id, disp_id;
255 u32 sec_ipu_id, sec_disp_id;
256 char *mode;
257 u32 ext_ref;
258
259 err = of_property_read_string(np, "mode", (const char **)&mode);
260 if (err) {
261 dev_dbg(&pdev->dev, "get of property mode fail\n");
262 return err;
263 }
264 err = of_property_read_u32(np, "ext_ref", &ext_ref);
265 if (err) {
266 dev_dbg(&pdev->dev, "get of property ext_ref fail\n");
267 return err;
268 }
269 err = of_property_read_u32(np, "ipu_id", &ipu_id);
270 if (err) {
271 dev_dbg(&pdev->dev, "get of property ipu_id fail\n");
272 return err;
273 }
274 err = of_property_read_u32(np, "disp_id", &disp_id);
275 if (err) {
276 dev_dbg(&pdev->dev, "get of property disp_id fail\n");
277 return err;
278 }
279 err = of_property_read_u32(np, "sec_ipu_id", &sec_ipu_id);
280 if (err) {
281 dev_dbg(&pdev->dev, "get of property sec_ipu_id fail\n");
282 return err;
283 }
284 err = of_property_read_u32(np, "sec_disp_id", &sec_disp_id);
285 if (err) {
286 dev_dbg(&pdev->dev, "get of property sec_disp_id fail\n");
287 return err;
288 }
289 358
290 plat_data->mode = parse_ldb_mode(mode); 359 for (; i < bus_mux.crtc_mux_num; i++)
291 plat_data->ext_ref = ext_ref; 360 if (bus_mux.crtcs[i].crtc == crtc) {
292 plat_data->ipu_id = ipu_id; 361 *mux_val = bus_mux.crtcs[i].val;
293 plat_data->disp_id = disp_id; 362 return 0;
294 plat_data->sec_ipu_id = sec_ipu_id; 363 }
295 plat_data->sec_disp_id = sec_disp_id;
296 364
297 return err; 365 return -EINVAL;
298} 366}
299 367
300static int find_ldb_setting(struct ldb_data *ldb, struct fb_info *fbi) 368static int find_ldb_chno(struct ldb_data *ldb,
369 struct fb_info *fbi, int *chno)
301{ 370{
302 char *id_di[] = { 371 struct device *dev = ldb->dev;
303 "DISP3 BG", 372 int i = 0;
304 "DISP3 BG - DI1", 373
305 }; 374 for (; i < 2; i++)
306 char id[16]; 375 if (ldb->chan[i].fbi == fbi) {
307 int i; 376 *chno = ldb->chan[i].chno;
308 377 return 0;
309 for (i = 0; i < 2; i++) {
310 if (ldb->setting[i].active) {
311 memset(id, 0, 16);
312 memcpy(id, id_di[ldb->setting[i].di],
313 strlen(id_di[ldb->setting[i].di]));
314 id[4] += ldb->setting[i].ipu;
315 if (!strcmp(id, fbi->fix.id))
316 return i;
317 } 378 }
318 } 379 dev_err(dev, "failed to find channel number\n");
319 return -EINVAL; 380 return -EINVAL;
320} 381}
321 382
322static int ldb_disp_setup(struct mxc_dispdrv_handle *disp, struct fb_info *fbi) 383static int ldb_setup(struct mxc_dispdrv_handle *mddh,
384 struct fb_info *fbi)
323{ 385{
324 uint32_t reg, val; 386 struct ldb_data *ldb = mxc_dispdrv_getdata(mddh);
325 uint32_t pixel_clk, rounded_pixel_clk; 387 struct ldb_chan chan;
326 struct clk *ldb_clk_parent; 388 struct device *dev = ldb->dev;
327 struct ldb_data *ldb = mxc_dispdrv_getdata(disp); 389 struct clk *ldb_di_parent, *ldb_di_sel, *ldb_di_sel_parent;
328 int setting_idx, di; 390 struct clk *other_ldb_di_sel = NULL;
329 int ret; 391 struct bus_mux bus_mux;
392 int ret = 0, id = 0, chno, other_chno;
393 unsigned long serial_clk;
394 u32 mux_val;
395
396 ret = find_ldb_chno(ldb, fbi, &chno);
397 if (ret < 0)
398 return ret;
330 399
331 setting_idx = find_ldb_setting(ldb, fbi); 400 other_chno = chno ? 0 : 1;
332 if (setting_idx < 0) 401
333 return setting_idx; 402 chan = ldb->chan[chno];
334 403
335 di = ldb->setting[setting_idx].di; 404 bus_mux = ldb->buses[chno];
336 405
337 /* restore channel mode setting */ 406 ret = get_di_clk_id(chan, &id);
338 val = readl(ldb->control_reg);
339 val |= ldb->setting[setting_idx].ch_val;
340 writel(val, ldb->control_reg);
341 dev_dbg(&ldb->pdev->dev, "LDB setup, control reg:0x%x\n",
342 readl(ldb->control_reg));
343
344 /* vsync setup */
345 reg = readl(ldb->control_reg);
346 if (fbi->var.sync & FB_SYNC_VERT_HIGH_ACT) {
347 if (di == 0)
348 reg = (reg & ~LDB_DI0_VS_POL_MASK)
349 | LDB_DI0_VS_POL_ACT_HIGH;
350 else
351 reg = (reg & ~LDB_DI1_VS_POL_MASK)
352 | LDB_DI1_VS_POL_ACT_HIGH;
353 } else {
354 if (di == 0)
355 reg = (reg & ~LDB_DI0_VS_POL_MASK)
356 | LDB_DI0_VS_POL_ACT_LOW;
357 else
358 reg = (reg & ~LDB_DI1_VS_POL_MASK)
359 | LDB_DI1_VS_POL_ACT_LOW;
360 }
361 writel(reg, ldb->control_reg);
362
363 /* clk setup */
364 if (ldb->setting[setting_idx].clk_en)
365 clk_disable_unprepare(ldb->setting[setting_idx].ldb_di_clk);
366 pixel_clk = (PICOS2KHZ(fbi->var.pixclock)) * 1000UL;
367 ldb_clk_parent = clk_get_parent(ldb->setting[setting_idx].ldb_di_clk);
368 if (IS_ERR(ldb_clk_parent)) {
369 dev_err(&ldb->pdev->dev, "get ldb di parent clk fail\n");
370 return PTR_ERR(ldb_clk_parent);
371 }
372 if ((ldb->mode == LDB_SPL_DI0) || (ldb->mode == LDB_SPL_DI1))
373 ret = clk_set_rate(ldb_clk_parent, pixel_clk * 7 / 2);
374 else
375 ret = clk_set_rate(ldb_clk_parent, pixel_clk * 7);
376 if (ret < 0) { 407 if (ret < 0) {
377 dev_err(&ldb->pdev->dev, "set ldb parent clk fail:%d\n", ret); 408 dev_err(dev, "failed to get ch%d di clk id\n",
409 chan.chno);
378 return ret; 410 return ret;
379 } 411 }
380 rounded_pixel_clk = clk_round_rate(ldb->setting[setting_idx].ldb_di_clk, 412
381 pixel_clk); 413 ret = get_mux_val(bus_mux, chan.crtc, &mux_val);
382 dev_dbg(&ldb->pdev->dev, "pixel_clk:%d, rounded_pixel_clk:%d\n",
383 pixel_clk, rounded_pixel_clk);
384 ret = clk_set_rate(ldb->setting[setting_idx].ldb_di_clk,
385 rounded_pixel_clk);
386 if (ret < 0) { 414 if (ret < 0) {
387 dev_err(&ldb->pdev->dev, "set ldb di clk fail:%d\n", ret); 415 dev_err(dev, "failed to get ch%d mux val\n",
416 chan.chno);
388 return ret; 417 return ret;
389 } 418 }
390 ret = clk_prepare_enable(ldb->setting[setting_idx].ldb_di_clk); 419
391 if (ret < 0) { 420 /*
392 dev_err(&ldb->pdev->dev, "enable ldb di clk fail:%d\n", ret); 421 * ldb_di_sel_parent(plls) -> ldb_di_sel ->
393 return ret; 422 *
423 * -> div_3_5[chno] ->
424 * -> | |-> div_sel[chno] ->
425 * -> div_7[chno] ->
426 *
427 * -> ldb_di[chno] -> di[id]
428 */
429 clk_set_parent(ldb->di_clk[id], ldb->ldb_di_clk[chno]);
430 ldb_di_parent = ldb->spl_mode ? ldb->div_3_5_clk[chno] :
431 ldb->div_7_clk[chno];
432 clk_set_parent(ldb->div_sel_clk[chno], ldb_di_parent);
433 ldb_di_sel = clk_get_parent(ldb_di_parent);
434 ldb_di_sel_parent = clk_get_parent(ldb_di_sel);
435 serial_clk = ldb->spl_mode ? chan.vm.pixelclock * 7 / 2 :
436 chan.vm.pixelclock * 7;
437 clk_set_rate(ldb_di_sel_parent, serial_clk);
438
439 /*
440 * split mode or dual mode:
441 * clock tree for the other channel
442 */
443 if (ldb->spl_mode) {
444 clk_set_parent(ldb->div_sel_clk[other_chno],
445 ldb->div_3_5_clk[other_chno]);
446 other_ldb_di_sel =
447 clk_get_parent(ldb->div_3_5_clk[other_chno]);;
448 }
449
450 if (ldb->dual_mode) {
451 clk_set_parent(ldb->div_sel_clk[other_chno],
452 ldb->div_7_clk[other_chno]);
453 other_ldb_di_sel =
454 clk_get_parent(ldb->div_7_clk[other_chno]);;
455 }
456
457 if (ldb->spl_mode || ldb->dual_mode)
458 clk_set_parent(other_ldb_di_sel, ldb_di_sel_parent);
459
460 if (!(chan.fbi->var.sync & FB_SYNC_VERT_HIGH_ACT)) {
461 if (ldb->spl_mode && bus_mux.reg == INVALID_BUS_REG)
462 /* no pre-muxing, such as mx53 */
463 ldb->ctrl |= (id == 0 ? LDB_DI0_VS_POL_ACT_LOW :
464 LDB_DI1_VS_POL_ACT_LOW);
465 else
466 ldb->ctrl |= (chno == 0 ? LDB_DI0_VS_POL_ACT_LOW :
467 LDB_DI1_VS_POL_ACT_LOW);
394 } 468 }
395 469
396 if (!ldb->setting[setting_idx].clk_en) 470 if (bus_mux.reg != INVALID_BUS_REG)
397 ldb->setting[setting_idx].clk_en = true; 471 regmap_update_bits(ldb->regmap, bus_mux.reg,
472 bus_mux.mask, mux_val);
398 473
399 return 0; 474 regmap_write(ldb->regmap, ldb->ctrl_reg, ldb->ctrl);
475 return ret;
400} 476}
401 477
402int ldb_fb_event(struct notifier_block *nb, unsigned long val, void *v) 478static int ldb_enable(struct mxc_dispdrv_handle *mddh,
479 struct fb_info *fbi)
403{ 480{
404 struct ldb_data *ldb = container_of(nb, struct ldb_data, nb); 481 struct ldb_data *ldb = mxc_dispdrv_getdata(mddh);
405 struct fb_event *event = v; 482 struct ldb_chan chan;
406 struct fb_info *fbi = event->info; 483 struct device *dev = ldb->dev;
407 int index; 484 struct bus_mux bus_mux;
408 uint32_t data; 485 int ret = 0, id = 0, chno, other_chno;
409
410 index = find_ldb_setting(ldb, fbi);
411 if (index < 0)
412 return 0;
413 486
414 fbi->mode = (struct fb_videomode *)fb_match_mode(&fbi->var, 487 ret = find_ldb_chno(ldb, fbi, &chno);
415 &fbi->modelist); 488 if (ret < 0)
416 489 return ret;
417 if (!fbi->mode) {
418 dev_warn(&ldb->pdev->dev,
419 "LDB: can not find mode for xres=%d, yres=%d\n",
420 fbi->var.xres, fbi->var.yres);
421 if (ldb->setting[index].clk_en) {
422 clk_disable(ldb->setting[index].ldb_di_clk);
423 ldb->setting[index].clk_en = false;
424 data = readl(ldb->control_reg);
425 data &= ~ldb->setting[index].ch_mask;
426 writel(data, ldb->control_reg);
427 }
428 return 0;
429 }
430 490
431 switch (val) { 491 chan = ldb->chan[chno];
432 case FB_EVENT_BLANK: 492
433 { 493 bus_mux = ldb->buses[chno];
434 if (*((int *)event->data) == FB_BLANK_UNBLANK) { 494
435 if (!ldb->setting[index].clk_en) { 495 if (ldb->spl_mode || ldb->dual_mode) {
436 clk_enable(ldb->setting[index].ldb_di_clk); 496 other_chno = chno ? 0 : 1;
437 ldb->setting[index].clk_en = true; 497 clk_prepare_enable(ldb->ldb_di_clk[other_chno]);
438 }
439 } else {
440 if (ldb->setting[index].clk_en) {
441 clk_disable(ldb->setting[index].ldb_di_clk);
442 ldb->setting[index].clk_en = false;
443 data = readl(ldb->control_reg);
444 data &= ~ldb->setting[index].ch_mask;
445 writel(data, ldb->control_reg);
446 dev_dbg(&ldb->pdev->dev,
447 "LDB blank, control reg:0x%x\n",
448 readl(ldb->control_reg));
449 }
450 }
451 break;
452 } 498 }
453 case FB_EVENT_SUSPEND: 499
454 if (ldb->setting[index].clk_en) { 500 if ((ldb->spl_mode || ldb->dual_mode) &&
455 clk_disable(ldb->setting[index].ldb_di_clk); 501 bus_mux.reg == INVALID_BUS_REG) {
456 ldb->setting[index].clk_en = false; 502 /* no pre-muxing, such as mx53 */
503 ret = get_di_clk_id(chan, &id);
504 if (ret < 0) {
505 dev_err(dev, "failed to get ch%d di clk id\n",
506 chan.chno);
507 return ret;
457 } 508 }
458 break; 509
459 default: 510 ldb->ctrl |= id ?
460 break; 511 (LDB_CH0_MODE_EN_TO_DI1 | LDB_CH1_MODE_EN_TO_DI1) :
512 (LDB_CH0_MODE_EN_TO_DI0 | LDB_CH1_MODE_EN_TO_DI0);
513 } else {
514 if (ldb->spl_mode || ldb->dual_mode)
515 ldb->ctrl |= LDB_CH0_MODE_EN_TO_DI0 |
516 LDB_CH1_MODE_EN_TO_DI0;
517 else
518 ldb->ctrl |= chno ? LDB_CH1_MODE_EN_TO_DI1 :
519 LDB_CH0_MODE_EN_TO_DI0;
461 } 520 }
521
522 regmap_write(ldb->regmap, ldb->ctrl_reg, ldb->ctrl);
462 return 0; 523 return 0;
463} 524}
464 525
465#define LVDS_MUX_CTL_WIDTH 2 526static void ldb_disable(struct mxc_dispdrv_handle *mddh,
466#define LVDS_MUX_CTL_MASK 3 527 struct fb_info *fbi)
467#define LVDS0_MUX_CTL_OFFS 6
468#define LVDS1_MUX_CTL_OFFS 8
469#define LVDS0_MUX_CTL_MASK (LVDS_MUX_CTL_MASK << 6)
470#define LVDS1_MUX_CTL_MASK (LVDS_MUX_CTL_MASK << 8)
471#define ROUTE_IPU_DI(ipu, di) (((ipu << 1) | di) & LVDS_MUX_CTL_MASK)
472static int ldb_ipu_ldb_route(int ipu, int di, struct ldb_data *ldb)
473{ 528{
474 uint32_t reg; 529 struct ldb_data *ldb = mxc_dispdrv_getdata(mddh);
475 int channel; 530 int ret, chno, other_chno;
476 int shift;
477 int mode = ldb->mode;
478
479 reg = readl(ldb->gpr3_reg);
480 if (mode < LDB_SIN0) {
481 reg &= ~(LVDS0_MUX_CTL_MASK | LVDS1_MUX_CTL_MASK);
482 reg |= (ROUTE_IPU_DI(ipu, di) << LVDS0_MUX_CTL_OFFS) |
483 (ROUTE_IPU_DI(ipu, di) << LVDS1_MUX_CTL_OFFS);
484 dev_dbg(&ldb->pdev->dev,
485 "Dual/Split mode both channels route to IPU%d-DI%d\n",
486 ipu, di);
487 } else if ((mode == LDB_SIN0) || (mode == LDB_SIN1)) {
488 reg &= ~(LVDS0_MUX_CTL_MASK | LVDS1_MUX_CTL_MASK);
489 channel = mode - LDB_SIN0;
490 shift = LVDS0_MUX_CTL_OFFS + channel * LVDS_MUX_CTL_WIDTH;
491 reg |= ROUTE_IPU_DI(ipu, di) << shift;
492 dev_dbg(&ldb->pdev->dev,
493 "Single mode channel %d route to IPU%d-DI%d\n",
494 channel, ipu, di);
495 } else {
496 static bool first = true;
497
498 if (first) {
499 if (mode == LDB_SEP0) {
500 reg &= ~LVDS0_MUX_CTL_MASK;
501 channel = 0;
502 } else {
503 reg &= ~LVDS1_MUX_CTL_MASK;
504 channel = 1;
505 }
506 first = false;
507 } else {
508 if (mode == LDB_SEP0) {
509 reg &= ~LVDS1_MUX_CTL_MASK;
510 channel = 1;
511 } else {
512 reg &= ~LVDS0_MUX_CTL_MASK;
513 channel = 0;
514 }
515 }
516 531
517 shift = LVDS0_MUX_CTL_OFFS + channel * LVDS_MUX_CTL_WIDTH; 532 ret = find_ldb_chno(ldb, fbi, &chno);
518 reg |= ROUTE_IPU_DI(ipu, di) << shift; 533 if (ret < 0)
534 return;
519 535
520 dev_dbg(&ldb->pdev->dev, 536 if (ldb->spl_mode || ldb->dual_mode) {
521 "Separate mode channel %d route to IPU%d-DI%d\n", 537 ldb->ctrl &= ~(LDB_CH1_MODE_MASK | LDB_CH0_MODE_MASK);
522 channel, ipu, di); 538 other_chno = chno ? 0 : 1;
539 clk_disable_unprepare(ldb->ldb_di_clk[other_chno]);
540 } else {
541 ldb->ctrl &= ~(chno ? LDB_CH1_MODE_MASK :
542 LDB_CH0_MODE_MASK);
523 } 543 }
524 writel(reg, ldb->gpr3_reg);
525 544
526 return 0; 545 regmap_write(ldb->regmap, ldb->ctrl_reg, ldb->ctrl);
546 return;
527} 547}
528 548
529static int ldb_disp_init(struct mxc_dispdrv_handle *disp, 549static struct mxc_dispdrv_driver ldb_drv = {
530 struct mxc_dispdrv_setting *setting) 550 .name = DRIVER_NAME,
531{ 551 .init = ldb_init,
532 int ret = 0, i, lvds_channel = 0, dev_id, ipu_di; 552 .setup = ldb_setup,
533 struct ldb_data *ldb = mxc_dispdrv_getdata(disp); 553 .enable = ldb_enable,
534 struct fsl_mxc_ldb_platform_data *plat_data = ldb->pdev->dev.platform_data; 554 .disable = ldb_disable
535 struct resource *res; 555};
536 uint32_t reg, setting_idx;
537 uint32_t ch_mask = 0, ch_val = 0;
538 uint32_t ipu_id, disp_id;
539 char di_clk[] = "ipu1_di0_sel";
540 char ldb_clk[] = "ldb_di0";
541 char div_3_5_clk[] = "di0_div_3_5";
542 char div_7_clk[] = "di0_div_7";
543 char div_sel_clk[] = "di0_div_sel";
544
545 /* if input format not valid, make RGB666 as default*/
546 if (!valid_mode(setting->if_fmt)) {
547 dev_warn(&ldb->pdev->dev, "Input pixel format not valid"
548 " use default RGB666\n");
549 setting->if_fmt = IPU_PIX_FMT_RGB666;
550 }
551 556
552 if (!ldb->inited) { 557enum {
553 setting_idx = 0; 558 LVDS_BIT_MAP_SPWG,
554 res = platform_get_resource(ldb->pdev, IORESOURCE_MEM, 0); 559 LVDS_BIT_MAP_JEIDA,
555 if (!res) { 560};
556 dev_err(&ldb->pdev->dev, "get iomem fail.\n");
557 return -ENOMEM;
558 }
559 561
560 ldb->reg = devm_ioremap(&ldb->pdev->dev, res->start, 562static const char *ldb_bit_mappings[] = {
561 resource_size(res)); 563 [LVDS_BIT_MAP_SPWG] = "spwg",
562 ldb->control_reg = ldb->reg + 2; 564 [LVDS_BIT_MAP_JEIDA] = "jeida",
563 ldb->gpr3_reg = ldb->reg + 3; 565};
564 566
565 /* ipu selected by platform data setting */ 567static int of_get_data_mapping(struct device_node *np)
566 dev_id = plat_data->ipu_id; 568{
569 const char *bm;
570 int ret, i;
567 571
568 reg = readl(ldb->control_reg); 572 ret = of_property_read_string(np, "fsl,data-mapping", &bm);
573 if (ret < 0)
574 return ret;
569 575
570 /* refrence resistor select */ 576 for (i = 0; i < ARRAY_SIZE(ldb_bit_mappings); i++)
571 reg &= ~LDB_BGREF_RMODE_MASK; 577 if (!strcasecmp(bm, ldb_bit_mappings[i]))
572 if (plat_data->ext_ref) 578 return i;
573 reg |= LDB_BGREF_RMODE_EXT;
574 else
575 reg |= LDB_BGREF_RMODE_INT;
576 579
577 /* TODO: now only use SPWG data mapping for both channel */ 580 return -EINVAL;
578 reg &= ~(LDB_BIT_MAP_CH0_MASK | LDB_BIT_MAP_CH1_MASK); 581}
579 reg |= LDB_BIT_MAP_CH0_SPWG | LDB_BIT_MAP_CH1_SPWG;
580 582
581 /* channel mode setting */ 583static const char *ldb_crtc_mappings[] = {
582 reg &= ~(LDB_CH0_MODE_MASK | LDB_CH1_MODE_MASK); 584 [CRTC_IPU_DI0] = "ipu-di0",
583 reg &= ~(LDB_DATA_WIDTH_CH0_MASK | LDB_DATA_WIDTH_CH1_MASK); 585 [CRTC_IPU_DI1] = "ipu-di1",
586 [CRTC_IPU1_DI0] = "ipu1-di0",
587 [CRTC_IPU1_DI1] = "ipu1-di1",
588 [CRTC_IPU2_DI0] = "ipu2-di0",
589 [CRTC_IPU2_DI1] = "ipu2-di1",
590 [CRTC_LCDIF] = "lcdif",
591 [CRTC_LCDIF1] = "lcdif1",
592 [CRTC_LCDIF2] = "lcdif2",
593};
584 594
585 if (bits_per_pixel(setting->if_fmt) == 24) 595static enum crtc of_get_crtc_mapping(struct device_node *np)
586 reg |= LDB_DATA_WIDTH_CH0_24 | LDB_DATA_WIDTH_CH1_24; 596{
587 else 597 const char *cm;
588 reg |= LDB_DATA_WIDTH_CH0_18 | LDB_DATA_WIDTH_CH1_18; 598 enum crtc i;
599 int ret;
589 600
590 if (g_ldb_mode >= LDB_SPL_DI0) 601 ret = of_property_read_string(np, "crtc", &cm);
591 ldb->mode = g_ldb_mode; 602 if (ret < 0)
592 else 603 return ret;
593 ldb->mode = plat_data->mode;
594
595 if ((ldb->mode == LDB_SIN0) || (ldb->mode == LDB_SIN1)) {
596 ret = ldb->mode - LDB_SIN0;
597 if (plat_data->disp_id != ret) {
598 dev_warn(&ldb->pdev->dev,
599 "change IPU DI%d to IPU DI%d for LDB "
600 "channel%d.\n",
601 plat_data->disp_id, ret, ret);
602 plat_data->disp_id = ret;
603 }
604 } else if (((ldb->mode == LDB_SEP0) || (ldb->mode == LDB_SEP1))
605 && is_imx6_ldb(plat_data)) {
606 if (plat_data->disp_id == plat_data->sec_disp_id) {
607 dev_err(&ldb->pdev->dev,
608 "For LVDS separate mode,"
609 "two DIs should be different!\n");
610 return -EINVAL;
611 }
612 604
613 if (((!plat_data->disp_id) && (ldb->mode == LDB_SEP1)) 605 for (i = 0; i < ARRAY_SIZE(ldb_crtc_mappings); i++)
614 || ((plat_data->disp_id) && 606 if (!strcasecmp(cm, ldb_crtc_mappings[i])) {
615 (ldb->mode == LDB_SEP0))) { 607 switch (i) {
616 dev_dbg(&ldb->pdev->dev, 608 case CRTC_IPU_DI0:
617 "LVDS separate mode:" 609 i = CRTC_IPU1_DI0;
618 "swap DI configuration!\n"); 610 break;
619 ipu_id = plat_data->ipu_id; 611 case CRTC_IPU_DI1:
620 disp_id = plat_data->disp_id; 612 i = CRTC_IPU1_DI1;
621 plat_data->ipu_id = plat_data->sec_ipu_id; 613 break;
622 plat_data->disp_id = plat_data->sec_disp_id; 614 case CRTC_LCDIF:
623 plat_data->sec_ipu_id = ipu_id; 615 i = CRTC_LCDIF1;
624 plat_data->sec_disp_id = disp_id; 616 break;
617 default:
618 break;
625 } 619 }
620 return i;
626 } 621 }
627 622
628 if (ldb->mode == LDB_SPL_DI0) { 623 return -EINVAL;
629 reg |= LDB_SPLIT_MODE_EN | LDB_CH0_MODE_EN_TO_DI0 624}
630 | LDB_CH1_MODE_EN_TO_DI0; 625
631 ipu_di = 0; 626static int mux_count(struct ldb_data *ldb)
632 } else if (ldb->mode == LDB_SPL_DI1) { 627{
633 reg |= LDB_SPLIT_MODE_EN | LDB_CH0_MODE_EN_TO_DI1 628 int i, j, count = 0;
634 | LDB_CH1_MODE_EN_TO_DI1; 629 bool should_count[CRTC_MAX];
635 ipu_di = 1; 630 enum crtc crtc;
636 } else if (ldb->mode == LDB_DUL_DI0) { 631
637 reg &= ~LDB_SPLIT_MODE_EN; 632 for (i = 0; i < CRTC_MAX; i++)
638 reg |= LDB_CH0_MODE_EN_TO_DI0 | LDB_CH1_MODE_EN_TO_DI0; 633 should_count[i] = true;
639 ipu_di = 0; 634
640 } else if (ldb->mode == LDB_DUL_DI1) { 635 for (i = 0; i < ldb->bus_mux_num; i++) {
641 reg &= ~LDB_SPLIT_MODE_EN; 636 for (j = 0; j < ldb->buses[i].crtc_mux_num; j++) {
642 reg |= LDB_CH0_MODE_EN_TO_DI1 | LDB_CH1_MODE_EN_TO_DI1; 637 crtc = ldb->buses[i].crtcs[j].crtc;
643 ipu_di = 1; 638 if (should_count[crtc]) {
644 } else if (ldb->mode == LDB_SIN0) { 639 count++;
645 reg &= ~LDB_SPLIT_MODE_EN; 640 should_count[crtc] = false;
646 ipu_di = plat_data->disp_id;
647 if (ipu_di == 0)
648 reg |= LDB_CH0_MODE_EN_TO_DI0;
649 else
650 reg |= LDB_CH0_MODE_EN_TO_DI1;
651 ch_mask = LDB_CH0_MODE_MASK;
652 ch_val = reg & LDB_CH0_MODE_MASK;
653 } else if (ldb->mode == LDB_SIN1) {
654 reg &= ~LDB_SPLIT_MODE_EN;
655 ipu_di = plat_data->disp_id;
656 if (ipu_di == 0)
657 reg |= LDB_CH1_MODE_EN_TO_DI0;
658 else
659 reg |= LDB_CH1_MODE_EN_TO_DI1;
660 ch_mask = LDB_CH1_MODE_MASK;
661 ch_val = reg & LDB_CH1_MODE_MASK;
662 } else { /* separate mode*/
663 ipu_di = plat_data->disp_id;
664
665 /* first output is LVDS0 or LVDS1 */
666 if (ldb->mode == LDB_SEP0)
667 lvds_channel = 0;
668 else
669 lvds_channel = 1;
670
671 reg &= ~LDB_SPLIT_MODE_EN;
672
673 if ((lvds_channel == 0) && (ipu_di == 0))
674 reg |= LDB_CH0_MODE_EN_TO_DI0;
675 else if ((lvds_channel == 0) && (ipu_di == 1))
676 reg |= LDB_CH0_MODE_EN_TO_DI1;
677 else if ((lvds_channel == 1) && (ipu_di == 0))
678 reg |= LDB_CH1_MODE_EN_TO_DI0;
679 else
680 reg |= LDB_CH1_MODE_EN_TO_DI1;
681 ch_mask = lvds_channel ? LDB_CH1_MODE_MASK :
682 LDB_CH0_MODE_MASK;
683 ch_val = reg & ch_mask;
684
685 if (bits_per_pixel(setting->if_fmt) == 24) {
686 if (lvds_channel == 0)
687 reg &= ~LDB_DATA_WIDTH_CH1_24;
688 else
689 reg &= ~LDB_DATA_WIDTH_CH0_24;
690 } else {
691 if (lvds_channel == 0)
692 reg &= ~LDB_DATA_WIDTH_CH1_18;
693 else
694 reg &= ~LDB_DATA_WIDTH_CH0_18;
695 } 641 }
696 } 642 }
643 }
697 644
698 writel(reg, ldb->control_reg); 645 return count;
699 if (ldb->mode < LDB_SIN0) { 646}
700 ch_mask = LDB_CH0_MODE_MASK | LDB_CH1_MODE_MASK;
701 ch_val = reg & (LDB_CH0_MODE_MASK | LDB_CH1_MODE_MASK);
702 }
703 } else { /* second time for separate mode */
704 if ((ldb->mode == LDB_SPL_DI0) ||
705 (ldb->mode == LDB_SPL_DI1) ||
706 (ldb->mode == LDB_DUL_DI0) ||
707 (ldb->mode == LDB_DUL_DI1) ||
708 (ldb->mode == LDB_SIN0) ||
709 (ldb->mode == LDB_SIN1)) {
710 dev_err(&ldb->pdev->dev, "for second ldb disp"
711 "ldb mode should in separate mode\n");
712 return -EINVAL;
713 }
714 647
715 setting_idx = 1; 648static bool is_valid_crtc(struct ldb_data *ldb, enum crtc crtc,
716 if (is_imx6_ldb(plat_data)) { 649 int chno)
717 dev_id = plat_data->sec_ipu_id; 650{
718 ipu_di = plat_data->sec_disp_id; 651 int i = 0;
719 } else {
720 dev_id = plat_data->ipu_id;
721 ipu_di = !plat_data->disp_id;
722 }
723 if (ipu_di == ldb->setting[0].di) {
724 dev_err(&ldb->pdev->dev, "Err: for second ldb disp in"
725 "separate mode, DI should be different!\n");
726 return -EINVAL;
727 }
728 652
729 /* second output is LVDS0 or LVDS1 */ 653 if (chno > ldb->bus_mux_num - 1)
730 if (ldb->mode == LDB_SEP0) 654 return false;
731 lvds_channel = 1;
732 else
733 lvds_channel = 0;
734
735 reg = readl(ldb->control_reg);
736 if ((lvds_channel == 0) && (ipu_di == 0))
737 reg |= LDB_CH0_MODE_EN_TO_DI0;
738 else if ((lvds_channel == 0) && (ipu_di == 1))
739 reg |= LDB_CH0_MODE_EN_TO_DI1;
740 else if ((lvds_channel == 1) && (ipu_di == 0))
741 reg |= LDB_CH1_MODE_EN_TO_DI0;
742 else
743 reg |= LDB_CH1_MODE_EN_TO_DI1;
744 ch_mask = lvds_channel ? LDB_CH1_MODE_MASK :
745 LDB_CH0_MODE_MASK;
746 ch_val = reg & ch_mask;
747
748 if (bits_per_pixel(setting->if_fmt) == 24) {
749 if (lvds_channel == 0)
750 reg |= LDB_DATA_WIDTH_CH0_24;
751 else
752 reg |= LDB_DATA_WIDTH_CH1_24;
753 } else {
754 if (lvds_channel == 0)
755 reg |= LDB_DATA_WIDTH_CH0_18;
756 else
757 reg |= LDB_DATA_WIDTH_CH1_18;
758 }
759 writel(reg, ldb->control_reg);
760 }
761 655
762 /* get clocks */ 656 for (; i < ldb->buses[chno].crtc_mux_num; i++)
763 if (is_imx6_ldb(plat_data) && 657 if (ldb->buses[chno].crtcs[i].crtc == crtc)
764 ((ldb->mode == LDB_SEP0) || (ldb->mode == LDB_SEP1))) { 658 return true;
765 ldb_clk[6] += lvds_channel;
766 div_3_5_clk[2] += lvds_channel;
767 div_7_clk[2] += lvds_channel;
768 div_sel_clk[2] += lvds_channel;
769 } else {
770 ldb_clk[6] += ipu_di;
771 div_3_5_clk[2] += ipu_di;
772 div_7_clk[2] += ipu_di;
773 div_sel_clk[2] += ipu_di;
774 }
775 ldb->setting[setting_idx].ldb_di_clk = clk_get(&ldb->pdev->dev,
776 ldb_clk);
777 if (IS_ERR(ldb->setting[setting_idx].ldb_di_clk)) {
778 dev_err(&ldb->pdev->dev, "get ldb clk failed\n");
779 return PTR_ERR(ldb->setting[setting_idx].ldb_di_clk);
780 }
781 659
782 ldb->setting[setting_idx].div_3_5_clk = clk_get(&ldb->pdev->dev, 660 return false;
783 div_3_5_clk); 661}
784 if (IS_ERR(ldb->setting[setting_idx].div_3_5_clk)) {
785 dev_err(&ldb->pdev->dev, "get div 3.5 clk failed\n");
786 return PTR_ERR(ldb->setting[setting_idx].div_3_5_clk);
787 }
788 ldb->setting[setting_idx].div_7_clk = clk_get(&ldb->pdev->dev,
789 div_7_clk);
790 if (IS_ERR(ldb->setting[setting_idx].div_7_clk)) {
791 dev_err(&ldb->pdev->dev, "get div 7 clk failed\n");
792 return PTR_ERR(ldb->setting[setting_idx].div_7_clk);
793 }
794 662
795 ldb->setting[setting_idx].div_sel_clk = clk_get(&ldb->pdev->dev, 663static int ldb_probe(struct platform_device *pdev)
796 div_sel_clk); 664{
797 if (IS_ERR(ldb->setting[setting_idx].div_sel_clk)) { 665 struct device *dev = &pdev->dev;
798 dev_err(&ldb->pdev->dev, "get div sel clk failed\n"); 666 const struct of_device_id *of_id =
799 return PTR_ERR(ldb->setting[setting_idx].div_sel_clk); 667 of_match_device(ldb_dt_ids, dev);
800 } 668 const struct ldb_info *ldb_info =
669 (const struct ldb_info *)of_id->data;
670 struct device_node *np = dev->of_node, *child;
671 struct ldb_data *ldb;
672 bool ext_ref;
673 int i, data_width, mapping, child_count = 0;
674 char clkname[16];
801 675
802 di_clk[3] += dev_id; 676 ldb = devm_kzalloc(dev, sizeof(*ldb), GFP_KERNEL);
803 di_clk[7] += ipu_di; 677 if (!ldb)
804 ldb->setting[setting_idx].di_clk = clk_get(&ldb->pdev->dev, 678 return -ENOMEM;
805 di_clk);
806 if (IS_ERR(ldb->setting[setting_idx].di_clk)) {
807 dev_err(&ldb->pdev->dev, "get di clk failed\n");
808 return PTR_ERR(ldb->setting[setting_idx].di_clk);
809 }
810 679
811 ldb->setting[setting_idx].ch_mask = ch_mask; 680 ldb->regmap = syscon_regmap_lookup_by_phandle(np, "gpr");
812 ldb->setting[setting_idx].ch_val = ch_val; 681 if (IS_ERR(ldb->regmap)) {
813 682 dev_err(dev, "failed to get parent regmap\n");
814 if (is_imx6_ldb(plat_data)) 683 return PTR_ERR(ldb->regmap);
815 ldb_ipu_ldb_route(dev_id, ipu_di, ldb);
816
817 /* must use spec video mode defined by driver */
818 ret = fb_find_mode(&setting->fbi->var, setting->fbi, setting->dft_mode_str,
819 ldb_modedb, ldb_modedb_sz, NULL, setting->default_bpp);
820 if (ret != 1)
821 fb_videomode_to_var(&setting->fbi->var, &ldb_modedb[0]);
822
823 INIT_LIST_HEAD(&setting->fbi->modelist);
824 for (i = 0; i < ldb_modedb_sz; i++) {
825 struct fb_videomode m;
826 fb_var_to_videomode(&m, &setting->fbi->var);
827 if (fb_mode_is_equal(&m, &ldb_modedb[i])) {
828 fb_add_videomode(&ldb_modedb[i],
829 &setting->fbi->modelist);
830 break;
831 }
832 } 684 }
833 685
834 ret = ipu_di_to_crtc(&ldb->pdev->dev, dev_id, 686 ldb->dev = dev;
835 ipu_di, &setting->crtc); 687 ldb->bus_mux_num = ldb_info->bus_mux_num;
836 if (ret < 0) 688 ldb->buses = ldb_info->buses;
837 return ret; 689 ldb->ctrl_reg = ldb_info->ctrl_reg;
690 ldb->primary_chno = -1;
838 691
839 ldb->setting[setting_idx].ipu = dev_id; 692 ext_ref = of_property_read_bool(np, "ext-ref");
840 ldb->setting[setting_idx].di = ipu_di; 693 if (!ext_ref && ldb_info->ext_bgref_cap)
694 ldb->ctrl |= LDB_BGREF_RMODE_INT;
841 695
842 return ret; 696 ldb->spl_mode = of_property_read_bool(np, "split-mode");
843} 697 if (ldb->spl_mode) {
844 698 if (ldb_info->split_cap) {
845static int ldb_post_disp_init(struct mxc_dispdrv_handle *disp, 699 ldb->ctrl |= LDB_SPLIT_MODE_EN;
846 int ipu_id, int disp_id) 700 dev_info(dev, "split mode\n");
847{ 701 } else {
848 struct ldb_data *ldb = mxc_dispdrv_getdata(disp); 702 dev_err(dev, "cannot support split mode\n");
849 int setting_idx = ldb->inited ? 1 : 0; 703 return -EINVAL;
850 int ret = 0; 704 }
705 }
851 706
852 if (!ldb->inited) { 707 ldb->dual_mode = of_property_read_bool(np, "dual-mode");
853 ldb->nb.notifier_call = ldb_fb_event; 708 if (ldb->dual_mode) {
854 fb_register_client(&ldb->nb); 709 if (ldb_info->dual_cap) {
710 dev_info(dev, "dual mode\n");
711 } else {
712 dev_err(dev, "cannot support dual mode\n");
713 return -EINVAL;
714 }
855 } 715 }
856 716
857 ret = clk_set_parent(ldb->setting[setting_idx].di_clk, 717 if (ldb->dual_mode && ldb->spl_mode) {
858 ldb->setting[setting_idx].ldb_di_clk); 718 dev_err(dev, "cannot support dual mode and split mode "
859 if (ret) { 719 "simultaneously\n");
860 dev_err(&ldb->pdev->dev, "fail to set ldb_di clk as" 720 return -EINVAL;
861 "the parent of ipu_di clk\n");
862 return ret;
863 } 721 }
864 722
865 if ((ldb->mode == LDB_SPL_DI0) || (ldb->mode == LDB_SPL_DI1)) { 723 for (i = 0; i < mux_count(ldb); i++) {
866 ret = clk_set_parent(ldb->setting[setting_idx].div_sel_clk, 724 sprintf(clkname, "di%d_sel", i);
867 ldb->setting[setting_idx].div_3_5_clk); 725 ldb->di_clk[i] = devm_clk_get(dev, clkname);
868 if (ret) { 726 if (IS_ERR(ldb->di_clk[i])) {
869 dev_err(&ldb->pdev->dev, "fail to set div 3.5 clk as" 727 dev_err(dev, "failed to get clk %s\n", clkname);
870 "the parent of div sel clk\n"); 728 return PTR_ERR(ldb->di_clk[i]);
871 return ret;
872 }
873 } else {
874 ret = clk_set_parent(ldb->setting[setting_idx].div_sel_clk,
875 ldb->setting[setting_idx].div_7_clk);
876 if (ret) {
877 dev_err(&ldb->pdev->dev, "fail to set div 7 clk as"
878 "the parent of div sel clk\n");
879 return ret;
880 } 729 }
881 } 730 }
882 731
883 /* save active ldb setting for fb notifier */ 732 for_each_child_of_node(np, child) {
884 ldb->setting[setting_idx].active = true; 733 struct ldb_chan *chan;
734 enum crtc crtc;
735 bool is_primary;
736 int ret;
885 737
886 ldb->inited = true; 738 ret = of_property_read_u32(child, "reg", &i);
887 return ret; 739 if (ret || i < 0 || i > 1 || i >= ldb->bus_mux_num) {
888} 740 dev_err(dev, "wrong LVDS channel number\n");
889 741 return -EINVAL;
890static void ldb_disp_deinit(struct mxc_dispdrv_handle *disp) 742 }
891{
892 struct ldb_data *ldb = mxc_dispdrv_getdata(disp);
893 int i;
894
895 writel(0, ldb->control_reg);
896 743
897 for (i = 0; i < 2; i++) { 744 if ((ldb->spl_mode || ldb->dual_mode) && i > 0) {
898 clk_disable(ldb->setting[i].ldb_di_clk); 745 dev_warn(dev, "split mode or dual mode, ignoring "
899 clk_put(ldb->setting[i].ldb_di_clk); 746 "second output\n");
900 clk_put(ldb->setting[i].div_3_5_clk); 747 continue;
901 clk_put(ldb->setting[i].div_7_clk); 748 }
902 clk_put(ldb->setting[i].div_sel_clk);
903 }
904 749
905 fb_unregister_client(&ldb->nb); 750 if (!of_device_is_available(child))
906} 751 continue;
907 752
908static struct mxc_dispdrv_driver ldb_drv = { 753 if (++child_count > ldb->bus_mux_num) {
909 .name = DISPDRV_LDB, 754 dev_err(dev, "too many LVDS channels\n");
910 .init = ldb_disp_init, 755 return -EINVAL;
911 .post_init = ldb_post_disp_init, 756 }
912 .deinit = ldb_disp_deinit,
913 .setup = ldb_disp_setup,
914};
915 757
916static int ldb_suspend(struct platform_device *pdev, pm_message_t state) 758 chan = &ldb->chan[i];
917{ 759 chan->chno = i;
918 struct ldb_data *ldb = dev_get_drvdata(&pdev->dev); 760 chan->ldb = ldb;
919 uint32_t data; 761 chan->online = true;
920 762
921 if (!ldb->inited) 763 is_primary = of_property_read_bool(child, "primary");
922 return 0;
923 data = readl(ldb->control_reg);
924 ldb->control_reg_data = data;
925 data &= ~(LDB_CH0_MODE_MASK | LDB_CH1_MODE_MASK);
926 writel(data, ldb->control_reg);
927 764
928 return 0; 765 if (ldb->bus_mux_num == 1 || (ldb->primary_chno == -1 &&
929} 766 (is_primary || ldb->spl_mode || ldb->dual_mode)))
767 ldb->primary_chno = chan->chno;
930 768
931static int ldb_resume(struct platform_device *pdev) 769 ret = of_property_read_u32(child, "fsl,data-width",
932{ 770 &data_width);
933 struct ldb_data *ldb = dev_get_drvdata(&pdev->dev); 771 if (ret || (data_width != 18 && data_width != 24)) {
772 dev_err(dev, "data width not specified or invalid\n");
773 return -EINVAL;
774 }
934 775
935 if (!ldb->inited) 776 mapping = of_get_data_mapping(child);
936 return 0; 777 switch (mapping) {
937 writel(ldb->control_reg_data, ldb->control_reg); 778 case LVDS_BIT_MAP_SPWG:
779 if (data_width == 24) {
780 if (i == 0 || ldb->spl_mode || ldb->dual_mode)
781 ldb->ctrl |= LDB_DATA_WIDTH_CH0_24;
782 if (i == 1 || ldb->spl_mode || ldb->dual_mode)
783 ldb->ctrl |= LDB_DATA_WIDTH_CH1_24;
784 }
785 break;
786 case LVDS_BIT_MAP_JEIDA:
787 if (data_width == 18) {
788 dev_err(dev, "JEIDA only support 24bit\n");
789 return -EINVAL;
790 }
791 if (i == 0 || ldb->spl_mode || ldb->dual_mode)
792 ldb->ctrl |= LDB_DATA_WIDTH_CH0_24 |
793 LDB_BIT_MAP_CH0_JEIDA;
794 if (i == 1 || ldb->spl_mode || ldb->dual_mode)
795 ldb->ctrl |= LDB_DATA_WIDTH_CH1_24 |
796 LDB_BIT_MAP_CH1_JEIDA;
797 break;
798 default:
799 dev_err(dev, "data mapping not specified or invalid\n");
800 return -EINVAL;
801 }
938 802
939 return 0; 803 crtc = of_get_crtc_mapping(child);
940} 804 if (is_valid_crtc(ldb, crtc, chan->chno)) {
805 ldb->chan[i].crtc = crtc;
806 } else {
807 dev_err(dev, "crtc not specified or invalid\n");
808 return -EINVAL;
809 }
941 810
942static struct platform_device_id imx_ldb_devtype[] = { 811 ret = of_get_videomode(child, &chan->vm, 0);
943 { 812 if (ret)
944 .name = "ldb-imx6", 813 return -EINVAL;
945 .driver_data = LDB_IMX6,
946 }, {
947 /* sentinel */
948 }
949};
950 814
951static const struct of_device_id imx_ldb_dt_ids[] = { 815 sprintf(clkname, "ldb_di%d", i);
952 { .compatible = "fsl,imx6q-ldb", .data = &imx_ldb_devtype[IMX6_LDB],}, 816 ldb->ldb_di_clk[i] = devm_clk_get(dev, clkname);
953 { /* sentinel */ } 817 if (IS_ERR(ldb->ldb_di_clk[i])) {
954}; 818 dev_err(dev, "failed to get clk %s\n", clkname);
819 return PTR_ERR(ldb->ldb_di_clk[i]);
820 }
955 821
956/*! 822 sprintf(clkname, "ldb_di%d_div_3_5", i);
957 * This function is called by the driver framework to initialize the LDB 823 ldb->div_3_5_clk[i] = devm_clk_get(dev, clkname);
958 * device. 824 if (IS_ERR(ldb->div_3_5_clk[i])) {
959 * 825 dev_err(dev, "failed to get clk %s\n", clkname);
960 * @param dev The device structure for the LDB passed in by the 826 return PTR_ERR(ldb->div_3_5_clk[i]);
961 * driver framework. 827 }
962 *
963 * @return Returns 0 on success or negative error code on error
964 */
965static int ldb_probe(struct platform_device *pdev)
966{
967 int ret = 0;
968 struct ldb_data *ldb;
969 struct fsl_mxc_ldb_platform_data *plat_data;
970 const struct of_device_id *of_id =
971 of_match_device(imx_ldb_dt_ids, &pdev->dev);
972 828
973 dev_dbg(&pdev->dev, "%s enter\n", __func__); 829 sprintf(clkname, "ldb_di%d_div_7", i);
974 ldb = devm_kzalloc(&pdev->dev, sizeof(struct ldb_data), GFP_KERNEL); 830 ldb->div_7_clk[i] = devm_clk_get(dev, clkname);
975 if (!ldb) 831 if (IS_ERR(ldb->div_7_clk[i])) {
976 return -ENOMEM; 832 dev_err(dev, "failed to get clk %s\n", clkname);
833 return PTR_ERR(ldb->div_7_clk[i]);
834 }
977 835
978 plat_data = devm_kzalloc(&pdev->dev, 836 sprintf(clkname, "ldb_di%d_div_sel", i);
979 sizeof(struct fsl_mxc_ldb_platform_data), 837 ldb->div_sel_clk[i] = devm_clk_get(dev, clkname);
980 GFP_KERNEL); 838 if (IS_ERR(ldb->div_sel_clk[i])) {
981 if (!plat_data) 839 dev_err(dev, "failed to get clk %s\n", clkname);
982 return -ENOMEM; 840 return PTR_ERR(ldb->div_sel_clk[i]);
983 pdev->dev.platform_data = plat_data; 841 }
984 if (of_id) 842 }
985 pdev->id_entry = of_id->data;
986 plat_data->devtype = pdev->id_entry->driver_data;
987 843
988 ret = ldb_get_of_property(pdev, plat_data); 844 if (child_count == 0) {
989 if (ret < 0) { 845 dev_err(dev, "failed to find valid LVDS channel\n");
990 dev_err(&pdev->dev, "get ldb of property fail\n"); 846 return -EINVAL;
991 return ret;
992 } 847 }
993 848
994 ldb->pdev = pdev; 849 if (ldb->primary_chno == -1) {
995 ldb->disp_ldb = mxc_dispdrv_register(&ldb_drv); 850 dev_err(dev, "failed to know primary channel\n");
996 mxc_dispdrv_setdata(ldb->disp_ldb, ldb); 851 return -EINVAL;
852 }
997 853
854 ldb->mddh = mxc_dispdrv_register(&ldb_drv);
855 mxc_dispdrv_setdata(ldb->mddh, ldb);
998 dev_set_drvdata(&pdev->dev, ldb); 856 dev_set_drvdata(&pdev->dev, ldb);
999 857
1000 dev_dbg(&pdev->dev, "%s exit\n", __func__); 858 return 0;
1001 return ret;
1002} 859}
1003 860
1004static int ldb_remove(struct platform_device *pdev) 861static int ldb_remove(struct platform_device *pdev)
1005{ 862{
1006 struct ldb_data *ldb = dev_get_drvdata(&pdev->dev); 863 struct ldb_data *ldb = dev_get_drvdata(&pdev->dev);
1007 864
1008 if (!ldb->inited) 865 mxc_dispdrv_puthandle(ldb->mddh);
1009 return 0; 866 mxc_dispdrv_unregister(ldb->mddh);
1010 mxc_dispdrv_puthandle(ldb->disp_ldb);
1011 mxc_dispdrv_unregister(ldb->disp_ldb);
1012 return 0; 867 return 0;
1013} 868}
1014 869
1015static struct platform_driver mxcldb_driver = { 870static struct platform_driver ldb_driver = {
1016 .driver = { 871 .driver = {
1017 .name = "mxc_ldb", 872 .name = DRIVER_NAME,
1018 .of_match_table = imx_ldb_dt_ids, 873 .of_match_table = ldb_dt_ids,
1019 }, 874 },
1020 .probe = ldb_probe, 875 .probe = ldb_probe,
1021 .remove = ldb_remove, 876 .remove = ldb_remove,
1022 .suspend = ldb_suspend,
1023 .resume = ldb_resume,
1024}; 877};
1025 878
1026static int __init ldb_init(void) 879module_platform_driver(ldb_driver);
1027{
1028 return platform_driver_register(&mxcldb_driver);
1029}
1030
1031static void __exit ldb_uninit(void)
1032{
1033 platform_driver_unregister(&mxcldb_driver);
1034}
1035
1036module_init(ldb_init);
1037module_exit(ldb_uninit);
1038 880
1039MODULE_AUTHOR("Freescale Semiconductor, Inc."); 881MODULE_AUTHOR("Freescale Semiconductor, Inc.");
1040MODULE_DESCRIPTION("MXC LDB driver"); 882MODULE_DESCRIPTION("LDB driver");
1041MODULE_LICENSE("GPL"); 883MODULE_LICENSE("GPL");
884MODULE_ALIAS("platform:" DRIVER_NAME);