diff options
-rw-r--r-- | Documentation/devicetree/bindings/fb/fsl_ipuv3_fb.txt | 45 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/video/fsl,ldb.txt | 95 | ||||
-rw-r--r-- | arch/arm/boot/dts/imx6dl-sabreauto.dts | 9 | ||||
-rw-r--r-- | arch/arm/boot/dts/imx6dl-sabresd-common.dtsi | 9 | ||||
-rw-r--r-- | arch/arm/boot/dts/imx6dl.dtsi | 17 | ||||
-rw-r--r-- | arch/arm/boot/dts/imx6q-sabreauto.dts | 10 | ||||
-rw-r--r-- | arch/arm/boot/dts/imx6q-sabresd.dts | 10 | ||||
-rw-r--r-- | arch/arm/boot/dts/imx6q.dtsi | 17 | ||||
-rw-r--r-- | arch/arm/boot/dts/imx6qdl-sabreauto.dtsi | 49 | ||||
-rw-r--r-- | arch/arm/boot/dts/imx6qdl-sabresd.dtsi | 49 | ||||
-rw-r--r-- | arch/arm/boot/dts/imx6qdl.dtsi | 27 | ||||
-rw-r--r-- | drivers/video/mxc/Kconfig | 1 | ||||
-rw-r--r-- | drivers/video/mxc/ldb.c | 1595 |
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. | |||
8 | Two IPU units are on the imx6q SOC while only one IPU unit on the imx6dl SOC. | 8 | Two IPU units are on the imx6q SOC while only one IPU unit on the imx6dl SOC. |
9 | Each IPU unit has two display interfaces. | 9 | Each IPU unit has two display interfaces. |
10 | 10 | ||
11 | For LDB/LVDS panel, there are two LVDS channels(LVDS0 and LVDS1) which can | ||
12 | transfer video data, these two channels can be used as | ||
13 | split/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 | |||
26 | Required properties for IPU: | 11 | Required 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: | |||
37 | Required properties for fb: | 22 | Required 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 | ||
60 | Required properties for display: | 43 | Required 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 | ||
121 | Example 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 | |||
132 | Example for mipi dsi display: | 91 | Example 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 | |||
3 | The LVDS Display Bridge (LDB) connects a display engine, | ||
4 | such as IPU and LCDIF, to an external LVDS display interface. | ||
5 | The purpose of the LDB is to support flow of synchronous RGB | ||
6 | data from the display engine to external display devices | ||
7 | through LVDS interfaces. This support covers all aspects of | ||
8 | these 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 | |||
14 | Required 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 | |||
24 | Optional 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 | |||
34 | Subnode for LVDS Channel | ||
35 | ======================== | ||
36 | |||
37 | Each LVDS Channel has to contain a display-timings node that describes the | ||
38 | video timings for the connected LVDS display. For detailed information, also | ||
39 | have a look at Documentation/devicetree/bindings/video/display-timing.txt. | ||
40 | |||
41 | Required 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 | |||
49 | Optional 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 | |||
54 | ldb: 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 | ||
26 | config FB_MXC_MIPI_DSI | 27 | config 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 | ||
81 | enum { | 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 | |||
60 | struct crtc_mux { | ||
61 | enum crtc crtc; | ||
62 | u32 val; | ||
83 | }; | 63 | }; |
84 | 64 | ||
85 | enum { | 65 | struct 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 | |||
73 | struct 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 | ||
89 | struct fsl_mxc_ldb_platform_data { | 82 | struct ldb_data; |
90 | int devtype; | 83 | |
91 | u32 ext_ref; | 84 | struct 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 | ||
109 | struct ldb_data { | 94 | struct 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 | ||
135 | static int g_ldb_mode; | 113 | static 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 | ||
137 | static struct fb_videomode ldb_modedb[] = { | 129 | static 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 | |||
145 | static 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 | |||
161 | static 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 | |||
170 | static 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 | }; |
163 | static int ldb_modedb_sz = ARRAY_SIZE(ldb_modedb); | ||
164 | 182 | ||
165 | static inline int is_imx6_ldb(struct fsl_mxc_ldb_platform_data *plat_data) | 183 | static 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 | |||
196 | static 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 | |||
212 | static 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 | |||
221 | static 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 | |||
231 | static 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 | |||
241 | static 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 | |||
250 | static const struct crtc_mux imx53_lvds0_crtc_mux[] = { | ||
251 | { .crtc = CRTC_IPU1_DI0, }, | ||
252 | }; | ||
253 | |||
254 | static const struct crtc_mux imx53_lvds1_crtc_mux[] = { | ||
255 | { .crtc = CRTC_IPU1_DI1, } | ||
256 | }; | ||
257 | |||
258 | static 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 | |||
270 | static 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 | ||
170 | static int bits_per_pixel(int pixel_fmt) | 279 | static 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 | }; | ||
286 | MODULE_DEVICE_TABLE(of, ldb_dt_ids); | ||
287 | |||
288 | static 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 | ||
188 | static int valid_mode(int pixel_fmt) | 327 | static 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 | ||
197 | static 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 | */ | ||
241 | static 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 | ||
249 | static int ldb_get_of_property(struct platform_device *pdev, | 354 | static 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 | ||
300 | static int find_ldb_setting(struct ldb_data *ldb, struct fb_info *fbi) | 368 | static 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 | ||
322 | static int ldb_disp_setup(struct mxc_dispdrv_handle *disp, struct fb_info *fbi) | 383 | static 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 | ||
402 | int ldb_fb_event(struct notifier_block *nb, unsigned long val, void *v) | 478 | static 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 | 526 | static 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) | ||
472 | static 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 | ||
529 | static int ldb_disp_init(struct mxc_dispdrv_handle *disp, | 549 | static 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) { | 557 | enum { |
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, | 562 | static 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 */ | 567 | static 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 */ | 583 | static 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) | 595 | static 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; | 626 | static 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; | 648 | static 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, | 663 | static 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) { | |
845 | static 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; | |
890 | static 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 | ||
908 | static 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 | ||
916 | static 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 | ||
931 | static 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 | ||
942 | static 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 | ||
951 | static 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 | */ | ||
965 | static 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 | ||
1004 | static int ldb_remove(struct platform_device *pdev) | 861 | static 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 | ||
1015 | static struct platform_driver mxcldb_driver = { | 870 | static 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 | ||
1026 | static int __init ldb_init(void) | 879 | module_platform_driver(ldb_driver); |
1027 | { | ||
1028 | return platform_driver_register(&mxcldb_driver); | ||
1029 | } | ||
1030 | |||
1031 | static void __exit ldb_uninit(void) | ||
1032 | { | ||
1033 | platform_driver_unregister(&mxcldb_driver); | ||
1034 | } | ||
1035 | |||
1036 | module_init(ldb_init); | ||
1037 | module_exit(ldb_uninit); | ||
1038 | 880 | ||
1039 | MODULE_AUTHOR("Freescale Semiconductor, Inc."); | 881 | MODULE_AUTHOR("Freescale Semiconductor, Inc."); |
1040 | MODULE_DESCRIPTION("MXC LDB driver"); | 882 | MODULE_DESCRIPTION("LDB driver"); |
1041 | MODULE_LICENSE("GPL"); | 883 | MODULE_LICENSE("GPL"); |
884 | MODULE_ALIAS("platform:" DRIVER_NAME); | ||