260 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*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, 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 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, i, 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, 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 132static 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 169static 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 185static 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 207static 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 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