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
14static 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
41static 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
64static 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
82static 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
132static 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
169static 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
185static 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
207static 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
233int 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
247int 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

source code of linux/drivers/video/fbdev/geode/suspend_gx.c