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