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