]>
Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
46fb6f11 AS |
2 | /* |
3 | * Copyright (C) 2007 Advanced Micro Devices, Inc. | |
4 | * Copyright (C) 2008 Andres Salomon <dilinger@debian.org> | |
46fb6f11 AS |
5 | */ |
6 | #include <linux/fb.h> | |
7 | #include <asm/io.h> | |
8 | #include <asm/msr.h> | |
f3a57a60 | 9 | #include <linux/cs5535.h> |
46fb6f11 AS |
10 | #include <asm/delay.h> |
11 | ||
12 | #include "gxfb.h" | |
13 | ||
46fb6f11 AS |
14 | static void gx_save_regs(struct gxfb_par *par) |
15 | { | |
16 | int i; | |
17 | ||
18 | /* wait for the BLT engine to stop being busy */ | |
19 | do { | |
20 | i = read_gp(par, GP_BLT_STATUS); | |
21 | } while (i & (GP_BLT_STATUS_BLT_PENDING | GP_BLT_STATUS_BLT_BUSY)); | |
22 | ||
23 | /* save MSRs */ | |
24 | rdmsrl(MSR_GX_MSR_PADSEL, par->msr.padsel); | |
25 | rdmsrl(MSR_GLCP_DOTPLL, par->msr.dotpll); | |
26 | ||
27 | write_dc(par, DC_UNLOCK, DC_UNLOCK_UNLOCK); | |
28 | ||
29 | /* save registers */ | |
30 | memcpy(par->gp, par->gp_regs, sizeof(par->gp)); | |
31 | memcpy(par->dc, par->dc_regs, sizeof(par->dc)); | |
32 | memcpy(par->vp, par->vid_regs, sizeof(par->vp)); | |
33 | memcpy(par->fp, par->vid_regs + VP_FP_START, sizeof(par->fp)); | |
34 | ||
35 | /* save the palette */ | |
36 | write_dc(par, DC_PAL_ADDRESS, 0); | |
37 | for (i = 0; i < ARRAY_SIZE(par->pal); i++) | |
38 | par->pal[i] = read_dc(par, DC_PAL_DATA); | |
39 | } | |
40 | ||
41 | static void gx_set_dotpll(uint32_t dotpll_hi) | |
42 | { | |
43 | uint32_t dotpll_lo; | |
44 | int i; | |
45 | ||
46 | rdmsrl(MSR_GLCP_DOTPLL, dotpll_lo); | |
47 | dotpll_lo |= MSR_GLCP_DOTPLL_DOTRESET; | |
48 | dotpll_lo &= ~MSR_GLCP_DOTPLL_BYPASS; | |
49 | wrmsr(MSR_GLCP_DOTPLL, dotpll_lo, dotpll_hi); | |
50 | ||
51 | /* wait for the PLL to lock */ | |
52 | for (i = 0; i < 200; i++) { | |
53 | rdmsrl(MSR_GLCP_DOTPLL, dotpll_lo); | |
54 | if (dotpll_lo & MSR_GLCP_DOTPLL_LOCK) | |
55 | break; | |
56 | udelay(1); | |
57 | } | |
58 | ||
59 | /* PLL set, unlock */ | |
60 | dotpll_lo &= ~MSR_GLCP_DOTPLL_DOTRESET; | |
61 | wrmsr(MSR_GLCP_DOTPLL, dotpll_lo, dotpll_hi); | |
62 | } | |
63 | ||
64 | static void gx_restore_gfx_proc(struct gxfb_par *par) | |
65 | { | |
66 | int i; | |
67 | ||
68 | for (i = 0; i < ARRAY_SIZE(par->gp); i++) { | |
69 | switch (i) { | |
70 | case GP_VECTOR_MODE: | |
71 | case GP_BLT_MODE: | |
72 | case GP_BLT_STATUS: | |
73 | case GP_HST_SRC: | |
74 | /* don't restore these registers */ | |
75 | break; | |
76 | default: | |
77 | write_gp(par, i, par->gp[i]); | |
78 | } | |
79 | } | |
80 | } | |
81 | ||
82 | static void gx_restore_display_ctlr(struct gxfb_par *par) | |
83 | { | |
84 | int i; | |
85 | ||
86 | for (i = 0; i < ARRAY_SIZE(par->dc); i++) { | |
87 | switch (i) { | |
88 | case DC_UNLOCK: | |
89 | /* unlock the DC; runs first */ | |
90 | write_dc(par, DC_UNLOCK, DC_UNLOCK_UNLOCK); | |
91 | break; | |
92 | ||
93 | case DC_GENERAL_CFG: | |
94 | /* write without the enables */ | |
95 | write_dc(par, i, par->dc[i] & ~(DC_GENERAL_CFG_VIDE | | |
96 | DC_GENERAL_CFG_ICNE | | |
97 | DC_GENERAL_CFG_CURE | | |
98 | DC_GENERAL_CFG_DFLE)); | |
99 | break; | |
100 | ||
101 | case DC_DISPLAY_CFG: | |
102 | /* write without the enables */ | |
103 | write_dc(par, i, par->dc[i] & ~(DC_DISPLAY_CFG_VDEN | | |
104 | DC_DISPLAY_CFG_GDEN | | |
105 | DC_DISPLAY_CFG_TGEN)); | |
106 | break; | |
107 | ||
108 | case DC_RSVD_0: | |
109 | case DC_RSVD_1: | |
110 | case DC_RSVD_2: | |
111 | case DC_RSVD_3: | |
112 | case DC_RSVD_4: | |
113 | case DC_LINE_CNT: | |
114 | case DC_PAL_ADDRESS: | |
115 | case DC_PAL_DATA: | |
116 | case DC_DFIFO_DIAG: | |
117 | case DC_CFIFO_DIAG: | |
118 | case DC_RSVD_5: | |
119 | /* don't restore these registers */ | |
120 | break; | |
121 | default: | |
122 | write_dc(par, i, par->dc[i]); | |
123 | } | |
124 | } | |
125 | ||
126 | /* restore the palette */ | |
127 | write_dc(par, DC_PAL_ADDRESS, 0); | |
128 | for (i = 0; i < ARRAY_SIZE(par->pal); i++) | |
129 | write_dc(par, DC_PAL_DATA, par->pal[i]); | |
130 | } | |
131 | ||
132 | static void gx_restore_video_proc(struct gxfb_par *par) | |
133 | { | |
134 | int i; | |
135 | ||
136 | wrmsrl(MSR_GX_MSR_PADSEL, par->msr.padsel); | |
137 | ||
138 | for (i = 0; i < ARRAY_SIZE(par->vp); i++) { | |
139 | switch (i) { | |
140 | case VP_VCFG: | |
141 | /* don't enable video yet */ | |
142 | write_vp(par, i, par->vp[i] & ~VP_VCFG_VID_EN); | |
143 | break; | |
144 | ||
145 | case VP_DCFG: | |
146 | /* don't enable CRT yet */ | |
147 | write_vp(par, i, par->vp[i] & | |
148 | ~(VP_DCFG_DAC_BL_EN | VP_DCFG_VSYNC_EN | | |
149 | VP_DCFG_HSYNC_EN | VP_DCFG_CRT_EN)); | |
150 | break; | |
151 | ||
152 | case VP_GAR: | |
153 | case VP_GDR: | |
154 | case VP_RSVD_0: | |
155 | case VP_RSVD_1: | |
156 | case VP_RSVD_2: | |
157 | case VP_RSVD_3: | |
158 | case VP_CRC32: | |
159 | case VP_AWT: | |
160 | case VP_VTM: | |
161 | /* don't restore these registers */ | |
162 | break; | |
163 | default: | |
164 | write_vp(par, i, par->vp[i]); | |
165 | } | |
166 | } | |
167 | } | |
168 | ||
169 | static void gx_restore_regs(struct gxfb_par *par) | |
170 | { | |
171 | int i; | |
172 | ||
173 | gx_set_dotpll((uint32_t) (par->msr.dotpll >> 32)); | |
174 | gx_restore_gfx_proc(par); | |
175 | gx_restore_display_ctlr(par); | |
176 | gx_restore_video_proc(par); | |
177 | ||
178 | /* Flat Panel */ | |
179 | for (i = 0; i < ARRAY_SIZE(par->fp); i++) { | |
180 | if (i != FP_PM && i != FP_RSVD_0) | |
181 | write_fp(par, i, par->fp[i]); | |
182 | } | |
183 | } | |
184 | ||
185 | static void gx_disable_graphics(struct gxfb_par *par) | |
186 | { | |
187 | /* shut down the engine */ | |
188 | write_vp(par, VP_VCFG, par->vp[VP_VCFG] & ~VP_VCFG_VID_EN); | |
189 | write_vp(par, VP_DCFG, par->vp[VP_DCFG] & ~(VP_DCFG_DAC_BL_EN | | |
190 | VP_DCFG_VSYNC_EN | VP_DCFG_HSYNC_EN | VP_DCFG_CRT_EN)); | |
191 | ||
192 | /* turn off the flat panel */ | |
193 | write_fp(par, FP_PM, par->fp[FP_PM] & ~FP_PM_P); | |
194 | ||
195 | ||
196 | /* turn off display */ | |
197 | write_dc(par, DC_UNLOCK, DC_UNLOCK_UNLOCK); | |
198 | write_dc(par, DC_GENERAL_CFG, par->dc[DC_GENERAL_CFG] & | |
199 | ~(DC_GENERAL_CFG_VIDE | DC_GENERAL_CFG_ICNE | | |
200 | DC_GENERAL_CFG_CURE | DC_GENERAL_CFG_DFLE)); | |
201 | write_dc(par, DC_DISPLAY_CFG, par->dc[DC_DISPLAY_CFG] & | |
202 | ~(DC_DISPLAY_CFG_VDEN | DC_DISPLAY_CFG_GDEN | | |
203 | DC_DISPLAY_CFG_TGEN)); | |
204 | write_dc(par, DC_UNLOCK, DC_UNLOCK_LOCK); | |
205 | } | |
206 | ||
207 | static void gx_enable_graphics(struct gxfb_par *par) | |
208 | { | |
209 | uint32_t fp; | |
210 | ||
211 | fp = read_fp(par, FP_PM); | |
212 | if (par->fp[FP_PM] & FP_PM_P) { | |
213 | /* power on the panel if not already power{ed,ing} on */ | |
214 | if (!(fp & (FP_PM_PANEL_ON|FP_PM_PANEL_PWR_UP))) | |
215 | write_fp(par, FP_PM, par->fp[FP_PM]); | |
216 | } else { | |
217 | /* power down the panel if not already power{ed,ing} down */ | |
218 | if (!(fp & (FP_PM_PANEL_OFF|FP_PM_PANEL_PWR_DOWN))) | |
219 | write_fp(par, FP_PM, par->fp[FP_PM]); | |
220 | } | |
221 | ||
222 | /* turn everything on */ | |
223 | write_vp(par, VP_VCFG, par->vp[VP_VCFG]); | |
224 | write_vp(par, VP_DCFG, par->vp[VP_DCFG]); | |
225 | write_dc(par, DC_DISPLAY_CFG, par->dc[DC_DISPLAY_CFG]); | |
226 | /* do this last; it will enable the FIFO load */ | |
227 | write_dc(par, DC_GENERAL_CFG, par->dc[DC_GENERAL_CFG]); | |
228 | ||
229 | /* lock the door behind us */ | |
230 | write_dc(par, DC_UNLOCK, DC_UNLOCK_LOCK); | |
231 | } | |
232 | ||
233 | int gx_powerdown(struct fb_info *info) | |
234 | { | |
235 | struct gxfb_par *par = info->par; | |
236 | ||
237 | if (par->powered_down) | |
238 | return 0; | |
239 | ||
240 | gx_save_regs(par); | |
241 | gx_disable_graphics(par); | |
242 | ||
243 | par->powered_down = 1; | |
244 | return 0; | |
245 | } | |
246 | ||
247 | int gx_powerup(struct fb_info *info) | |
248 | { | |
249 | struct gxfb_par *par = info->par; | |
250 | ||
251 | if (!par->powered_down) | |
252 | return 0; | |
253 | ||
254 | gx_restore_regs(par); | |
255 | gx_enable_graphics(par); | |
256 | ||
257 | par->powered_down = 0; | |
258 | return 0; | |
259 | } |