GNU Linux-libre 4.19.245-gnu1
[releases.git] / drivers / staging / sm750fb / ddk750_display.c
1 // SPDX-License-Identifier: GPL-2.0
2 #include "ddk750_reg.h"
3 #include "ddk750_chip.h"
4 #include "ddk750_display.h"
5 #include "ddk750_power.h"
6 #include "ddk750_dvi.h"
7
8 static void setDisplayControl(int ctrl, int disp_state)
9 {
10         /* state != 0 means turn on both timing & plane en_bit */
11         unsigned long reg, val, reserved;
12         int cnt = 0;
13
14         if (!ctrl) {
15                 reg = PANEL_DISPLAY_CTRL;
16                 reserved = PANEL_DISPLAY_CTRL_RESERVED_MASK;
17         } else {
18                 reg = CRT_DISPLAY_CTRL;
19                 reserved = CRT_DISPLAY_CTRL_RESERVED_MASK;
20         }
21
22         val = peek32(reg);
23         if (disp_state) {
24                 /*
25                  * Timing should be enabled first before enabling the
26                  * plane because changing at the same time does not
27                  * guarantee that the plane will also enabled or
28                  * disabled.
29                  */
30                 val |= DISPLAY_CTRL_TIMING;
31                 poke32(reg, val);
32
33                 val |= DISPLAY_CTRL_PLANE;
34
35                 /*
36                  * Somehow the register value on the plane is not set
37                  * until a few delay. Need to write and read it a
38                  * couple times
39                  */
40                 do {
41                         cnt++;
42                         poke32(reg, val);
43                 } while ((peek32(reg) & ~reserved) != (val & ~reserved));
44                 pr_debug("Set Plane enbit:after tried %d times\n", cnt);
45         } else {
46                 /*
47                  * When turning off, there is no rule on the
48                  * programming sequence since whenever the clock is
49                  * off, then it does not matter whether the plane is
50                  * enabled or disabled.  Note: Modifying the plane bit
51                  * will take effect on the next vertical sync. Need to
52                  * find out if it is necessary to wait for 1 vsync
53                  * before modifying the timing enable bit.
54                  */
55                 val &= ~DISPLAY_CTRL_PLANE;
56                 poke32(reg, val);
57
58                 val &= ~DISPLAY_CTRL_TIMING;
59                 poke32(reg, val);
60         }
61 }
62
63 static void primary_wait_vertical_sync(int delay)
64 {
65         unsigned int status;
66
67         /*
68          * Do not wait when the Primary PLL is off or display control is
69          * already off. This will prevent the software to wait forever.
70          */
71         if (!(peek32(PANEL_PLL_CTRL) & PLL_CTRL_POWER) ||
72             !(peek32(PANEL_DISPLAY_CTRL) & DISPLAY_CTRL_TIMING))
73                 return;
74
75         while (delay-- > 0) {
76                 /* Wait for end of vsync. */
77                 do {
78                         status = peek32(SYSTEM_CTRL);
79                 } while (status & SYSTEM_CTRL_PANEL_VSYNC_ACTIVE);
80
81                 /* Wait for start of vsync. */
82                 do {
83                         status = peek32(SYSTEM_CTRL);
84                 } while (!(status & SYSTEM_CTRL_PANEL_VSYNC_ACTIVE));
85         }
86 }
87
88 static void swPanelPowerSequence(int disp, int delay)
89 {
90         unsigned int reg;
91
92         /* disp should be 1 to open sequence */
93         reg = peek32(PANEL_DISPLAY_CTRL);
94         reg |= (disp ? PANEL_DISPLAY_CTRL_FPEN : 0);
95         poke32(PANEL_DISPLAY_CTRL, reg);
96         primary_wait_vertical_sync(delay);
97
98         reg = peek32(PANEL_DISPLAY_CTRL);
99         reg |= (disp ? PANEL_DISPLAY_CTRL_DATA : 0);
100         poke32(PANEL_DISPLAY_CTRL, reg);
101         primary_wait_vertical_sync(delay);
102
103         reg = peek32(PANEL_DISPLAY_CTRL);
104         reg |= (disp ? PANEL_DISPLAY_CTRL_VBIASEN : 0);
105         poke32(PANEL_DISPLAY_CTRL, reg);
106         primary_wait_vertical_sync(delay);
107
108         reg = peek32(PANEL_DISPLAY_CTRL);
109         reg |= (disp ? PANEL_DISPLAY_CTRL_FPEN : 0);
110         poke32(PANEL_DISPLAY_CTRL, reg);
111         primary_wait_vertical_sync(delay);
112 }
113
114 void ddk750_setLogicalDispOut(enum disp_output output)
115 {
116         unsigned int reg;
117
118         if (output & PNL_2_USAGE) {
119                 /* set panel path controller select */
120                 reg = peek32(PANEL_DISPLAY_CTRL);
121                 reg &= ~PANEL_DISPLAY_CTRL_SELECT_MASK;
122                 reg |= (((output & PNL_2_MASK) >> PNL_2_OFFSET) <<
123                         PANEL_DISPLAY_CTRL_SELECT_SHIFT);
124                 poke32(PANEL_DISPLAY_CTRL, reg);
125         }
126
127         if (output & CRT_2_USAGE) {
128                 /* set crt path controller select */
129                 reg = peek32(CRT_DISPLAY_CTRL);
130                 reg &= ~CRT_DISPLAY_CTRL_SELECT_MASK;
131                 reg |= (((output & CRT_2_MASK) >> CRT_2_OFFSET) <<
132                         CRT_DISPLAY_CTRL_SELECT_SHIFT);
133                 /*se blank off */
134                 reg &= ~CRT_DISPLAY_CTRL_BLANK;
135                 poke32(CRT_DISPLAY_CTRL, reg);
136         }
137
138         if (output & PRI_TP_USAGE) {
139                 /* set primary timing and plane en_bit */
140                 setDisplayControl(0, (output & PRI_TP_MASK) >> PRI_TP_OFFSET);
141         }
142
143         if (output & SEC_TP_USAGE) {
144                 /* set secondary timing and plane en_bit*/
145                 setDisplayControl(1, (output & SEC_TP_MASK) >> SEC_TP_OFFSET);
146         }
147
148         if (output & PNL_SEQ_USAGE) {
149                 /* set  panel sequence */
150                 swPanelPowerSequence((output & PNL_SEQ_MASK) >> PNL_SEQ_OFFSET,
151                                      4);
152         }
153
154         if (output & DAC_USAGE)
155                 setDAC((output & DAC_MASK) >> DAC_OFFSET);
156
157         if (output & DPMS_USAGE)
158                 ddk750_set_dpms((output & DPMS_MASK) >> DPMS_OFFSET);
159 }