1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (C) 2007 Advanced Micro Devices, Inc. |
4 | * Copyright (C) 2008 Andres Salomon <dilinger@debian.org> |
5 | */ |
6 | #include <linux/fb.h> |
7 | #include <asm/io.h> |
8 | #include <asm/msr.h> |
9 | #include <linux/cs5535.h> |
10 | #include <asm/delay.h> |
11 | |
12 | #include "gxfb.h" |
13 | |
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, reg: 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, reg: 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, reg: DC_PAL_ADDRESS, val: 0); |
37 | for (i = 0; i < ARRAY_SIZE(par->pal); i++) |
38 | par->pal[i] = read_dc(par, reg: 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, reg: i, val: 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, reg: DC_UNLOCK, DC_UNLOCK_UNLOCK); |
91 | break; |
92 | |
93 | case DC_GENERAL_CFG: |
94 | /* write without the enables */ |
95 | write_dc(par, reg: i, val: 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, reg: i, val: 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, reg: i, val: par->dc[i]); |
123 | } |
124 | } |
125 | |
126 | /* restore the palette */ |
127 | write_dc(par, reg: DC_PAL_ADDRESS, val: 0); |
128 | for (i = 0; i < ARRAY_SIZE(par->pal); i++) |
129 | write_dc(par, reg: DC_PAL_DATA, val: 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, val: 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, reg: i, val: par->vp[i] & ~VP_VCFG_VID_EN); |
143 | break; |
144 | |
145 | case VP_DCFG: |
146 | /* don't enable CRT yet */ |
147 | write_vp(par, reg: i, val: 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, reg: i, val: 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(dotpll_hi: (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, reg: i, val: 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, reg: VP_VCFG, val: par->vp[VP_VCFG] & ~VP_VCFG_VID_EN); |
189 | write_vp(par, reg: VP_DCFG, val: 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, reg: FP_PM, val: par->fp[FP_PM] & ~FP_PM_P); |
194 | |
195 | |
196 | /* turn off display */ |
197 | write_dc(par, reg: DC_UNLOCK, DC_UNLOCK_UNLOCK); |
198 | write_dc(par, reg: DC_GENERAL_CFG, val: 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, reg: DC_DISPLAY_CFG, val: par->dc[DC_DISPLAY_CFG] & |
202 | ~(DC_DISPLAY_CFG_VDEN | DC_DISPLAY_CFG_GDEN | |
203 | DC_DISPLAY_CFG_TGEN)); |
204 | write_dc(par, reg: 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, reg: 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, reg: FP_PM, val: 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, reg: FP_PM, val: par->fp[FP_PM]); |
220 | } |
221 | |
222 | /* turn everything on */ |
223 | write_vp(par, reg: VP_VCFG, val: par->vp[VP_VCFG]); |
224 | write_vp(par, reg: VP_DCFG, val: par->vp[VP_DCFG]); |
225 | write_dc(par, reg: DC_DISPLAY_CFG, val: par->dc[DC_DISPLAY_CFG]); |
226 | /* do this last; it will enable the FIFO load */ |
227 | write_dc(par, reg: DC_GENERAL_CFG, val: par->dc[DC_GENERAL_CFG]); |
228 | |
229 | /* lock the door behind us */ |
230 | write_dc(par, reg: 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 | } |
260 | |