18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2007 Advanced Micro Devices, Inc. 48c2ecf20Sopenharmony_ci * Copyright (C) 2008 Andres Salomon <dilinger@debian.org> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci#include <linux/fb.h> 78c2ecf20Sopenharmony_ci#include <asm/io.h> 88c2ecf20Sopenharmony_ci#include <asm/msr.h> 98c2ecf20Sopenharmony_ci#include <linux/cs5535.h> 108c2ecf20Sopenharmony_ci#include <asm/delay.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include "gxfb.h" 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_cistatic void gx_save_regs(struct gxfb_par *par) 158c2ecf20Sopenharmony_ci{ 168c2ecf20Sopenharmony_ci int i; 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci /* wait for the BLT engine to stop being busy */ 198c2ecf20Sopenharmony_ci do { 208c2ecf20Sopenharmony_ci i = read_gp(par, GP_BLT_STATUS); 218c2ecf20Sopenharmony_ci } while (i & (GP_BLT_STATUS_BLT_PENDING | GP_BLT_STATUS_BLT_BUSY)); 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci /* save MSRs */ 248c2ecf20Sopenharmony_ci rdmsrl(MSR_GX_MSR_PADSEL, par->msr.padsel); 258c2ecf20Sopenharmony_ci rdmsrl(MSR_GLCP_DOTPLL, par->msr.dotpll); 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci write_dc(par, DC_UNLOCK, DC_UNLOCK_UNLOCK); 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci /* save registers */ 308c2ecf20Sopenharmony_ci memcpy(par->gp, par->gp_regs, sizeof(par->gp)); 318c2ecf20Sopenharmony_ci memcpy(par->dc, par->dc_regs, sizeof(par->dc)); 328c2ecf20Sopenharmony_ci memcpy(par->vp, par->vid_regs, sizeof(par->vp)); 338c2ecf20Sopenharmony_ci memcpy(par->fp, par->vid_regs + VP_FP_START, sizeof(par->fp)); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci /* save the palette */ 368c2ecf20Sopenharmony_ci write_dc(par, DC_PAL_ADDRESS, 0); 378c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(par->pal); i++) 388c2ecf20Sopenharmony_ci par->pal[i] = read_dc(par, DC_PAL_DATA); 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic void gx_set_dotpll(uint32_t dotpll_hi) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci uint32_t dotpll_lo; 448c2ecf20Sopenharmony_ci int i; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci rdmsrl(MSR_GLCP_DOTPLL, dotpll_lo); 478c2ecf20Sopenharmony_ci dotpll_lo |= MSR_GLCP_DOTPLL_DOTRESET; 488c2ecf20Sopenharmony_ci dotpll_lo &= ~MSR_GLCP_DOTPLL_BYPASS; 498c2ecf20Sopenharmony_ci wrmsr(MSR_GLCP_DOTPLL, dotpll_lo, dotpll_hi); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci /* wait for the PLL to lock */ 528c2ecf20Sopenharmony_ci for (i = 0; i < 200; i++) { 538c2ecf20Sopenharmony_ci rdmsrl(MSR_GLCP_DOTPLL, dotpll_lo); 548c2ecf20Sopenharmony_ci if (dotpll_lo & MSR_GLCP_DOTPLL_LOCK) 558c2ecf20Sopenharmony_ci break; 568c2ecf20Sopenharmony_ci udelay(1); 578c2ecf20Sopenharmony_ci } 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci /* PLL set, unlock */ 608c2ecf20Sopenharmony_ci dotpll_lo &= ~MSR_GLCP_DOTPLL_DOTRESET; 618c2ecf20Sopenharmony_ci wrmsr(MSR_GLCP_DOTPLL, dotpll_lo, dotpll_hi); 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic void gx_restore_gfx_proc(struct gxfb_par *par) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci int i; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(par->gp); i++) { 698c2ecf20Sopenharmony_ci switch (i) { 708c2ecf20Sopenharmony_ci case GP_VECTOR_MODE: 718c2ecf20Sopenharmony_ci case GP_BLT_MODE: 728c2ecf20Sopenharmony_ci case GP_BLT_STATUS: 738c2ecf20Sopenharmony_ci case GP_HST_SRC: 748c2ecf20Sopenharmony_ci /* don't restore these registers */ 758c2ecf20Sopenharmony_ci break; 768c2ecf20Sopenharmony_ci default: 778c2ecf20Sopenharmony_ci write_gp(par, i, par->gp[i]); 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci } 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic void gx_restore_display_ctlr(struct gxfb_par *par) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci int i; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(par->dc); i++) { 878c2ecf20Sopenharmony_ci switch (i) { 888c2ecf20Sopenharmony_ci case DC_UNLOCK: 898c2ecf20Sopenharmony_ci /* unlock the DC; runs first */ 908c2ecf20Sopenharmony_ci write_dc(par, DC_UNLOCK, DC_UNLOCK_UNLOCK); 918c2ecf20Sopenharmony_ci break; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci case DC_GENERAL_CFG: 948c2ecf20Sopenharmony_ci /* write without the enables */ 958c2ecf20Sopenharmony_ci write_dc(par, i, par->dc[i] & ~(DC_GENERAL_CFG_VIDE | 968c2ecf20Sopenharmony_ci DC_GENERAL_CFG_ICNE | 978c2ecf20Sopenharmony_ci DC_GENERAL_CFG_CURE | 988c2ecf20Sopenharmony_ci DC_GENERAL_CFG_DFLE)); 998c2ecf20Sopenharmony_ci break; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci case DC_DISPLAY_CFG: 1028c2ecf20Sopenharmony_ci /* write without the enables */ 1038c2ecf20Sopenharmony_ci write_dc(par, i, par->dc[i] & ~(DC_DISPLAY_CFG_VDEN | 1048c2ecf20Sopenharmony_ci DC_DISPLAY_CFG_GDEN | 1058c2ecf20Sopenharmony_ci DC_DISPLAY_CFG_TGEN)); 1068c2ecf20Sopenharmony_ci break; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci case DC_RSVD_0: 1098c2ecf20Sopenharmony_ci case DC_RSVD_1: 1108c2ecf20Sopenharmony_ci case DC_RSVD_2: 1118c2ecf20Sopenharmony_ci case DC_RSVD_3: 1128c2ecf20Sopenharmony_ci case DC_RSVD_4: 1138c2ecf20Sopenharmony_ci case DC_LINE_CNT: 1148c2ecf20Sopenharmony_ci case DC_PAL_ADDRESS: 1158c2ecf20Sopenharmony_ci case DC_PAL_DATA: 1168c2ecf20Sopenharmony_ci case DC_DFIFO_DIAG: 1178c2ecf20Sopenharmony_ci case DC_CFIFO_DIAG: 1188c2ecf20Sopenharmony_ci case DC_RSVD_5: 1198c2ecf20Sopenharmony_ci /* don't restore these registers */ 1208c2ecf20Sopenharmony_ci break; 1218c2ecf20Sopenharmony_ci default: 1228c2ecf20Sopenharmony_ci write_dc(par, i, par->dc[i]); 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci /* restore the palette */ 1278c2ecf20Sopenharmony_ci write_dc(par, DC_PAL_ADDRESS, 0); 1288c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(par->pal); i++) 1298c2ecf20Sopenharmony_ci write_dc(par, DC_PAL_DATA, par->pal[i]); 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic void gx_restore_video_proc(struct gxfb_par *par) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci int i; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci wrmsrl(MSR_GX_MSR_PADSEL, par->msr.padsel); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(par->vp); i++) { 1398c2ecf20Sopenharmony_ci switch (i) { 1408c2ecf20Sopenharmony_ci case VP_VCFG: 1418c2ecf20Sopenharmony_ci /* don't enable video yet */ 1428c2ecf20Sopenharmony_ci write_vp(par, i, par->vp[i] & ~VP_VCFG_VID_EN); 1438c2ecf20Sopenharmony_ci break; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci case VP_DCFG: 1468c2ecf20Sopenharmony_ci /* don't enable CRT yet */ 1478c2ecf20Sopenharmony_ci write_vp(par, i, par->vp[i] & 1488c2ecf20Sopenharmony_ci ~(VP_DCFG_DAC_BL_EN | VP_DCFG_VSYNC_EN | 1498c2ecf20Sopenharmony_ci VP_DCFG_HSYNC_EN | VP_DCFG_CRT_EN)); 1508c2ecf20Sopenharmony_ci break; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci case VP_GAR: 1538c2ecf20Sopenharmony_ci case VP_GDR: 1548c2ecf20Sopenharmony_ci case VP_RSVD_0: 1558c2ecf20Sopenharmony_ci case VP_RSVD_1: 1568c2ecf20Sopenharmony_ci case VP_RSVD_2: 1578c2ecf20Sopenharmony_ci case VP_RSVD_3: 1588c2ecf20Sopenharmony_ci case VP_CRC32: 1598c2ecf20Sopenharmony_ci case VP_AWT: 1608c2ecf20Sopenharmony_ci case VP_VTM: 1618c2ecf20Sopenharmony_ci /* don't restore these registers */ 1628c2ecf20Sopenharmony_ci break; 1638c2ecf20Sopenharmony_ci default: 1648c2ecf20Sopenharmony_ci write_vp(par, i, par->vp[i]); 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic void gx_restore_regs(struct gxfb_par *par) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci int i; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci gx_set_dotpll((uint32_t) (par->msr.dotpll >> 32)); 1748c2ecf20Sopenharmony_ci gx_restore_gfx_proc(par); 1758c2ecf20Sopenharmony_ci gx_restore_display_ctlr(par); 1768c2ecf20Sopenharmony_ci gx_restore_video_proc(par); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci /* Flat Panel */ 1798c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(par->fp); i++) { 1808c2ecf20Sopenharmony_ci if (i != FP_PM && i != FP_RSVD_0) 1818c2ecf20Sopenharmony_ci write_fp(par, i, par->fp[i]); 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic void gx_disable_graphics(struct gxfb_par *par) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci /* shut down the engine */ 1888c2ecf20Sopenharmony_ci write_vp(par, VP_VCFG, par->vp[VP_VCFG] & ~VP_VCFG_VID_EN); 1898c2ecf20Sopenharmony_ci write_vp(par, VP_DCFG, par->vp[VP_DCFG] & ~(VP_DCFG_DAC_BL_EN | 1908c2ecf20Sopenharmony_ci VP_DCFG_VSYNC_EN | VP_DCFG_HSYNC_EN | VP_DCFG_CRT_EN)); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci /* turn off the flat panel */ 1938c2ecf20Sopenharmony_ci write_fp(par, FP_PM, par->fp[FP_PM] & ~FP_PM_P); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci /* turn off display */ 1978c2ecf20Sopenharmony_ci write_dc(par, DC_UNLOCK, DC_UNLOCK_UNLOCK); 1988c2ecf20Sopenharmony_ci write_dc(par, DC_GENERAL_CFG, par->dc[DC_GENERAL_CFG] & 1998c2ecf20Sopenharmony_ci ~(DC_GENERAL_CFG_VIDE | DC_GENERAL_CFG_ICNE | 2008c2ecf20Sopenharmony_ci DC_GENERAL_CFG_CURE | DC_GENERAL_CFG_DFLE)); 2018c2ecf20Sopenharmony_ci write_dc(par, DC_DISPLAY_CFG, par->dc[DC_DISPLAY_CFG] & 2028c2ecf20Sopenharmony_ci ~(DC_DISPLAY_CFG_VDEN | DC_DISPLAY_CFG_GDEN | 2038c2ecf20Sopenharmony_ci DC_DISPLAY_CFG_TGEN)); 2048c2ecf20Sopenharmony_ci write_dc(par, DC_UNLOCK, DC_UNLOCK_LOCK); 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic void gx_enable_graphics(struct gxfb_par *par) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci uint32_t fp; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci fp = read_fp(par, FP_PM); 2128c2ecf20Sopenharmony_ci if (par->fp[FP_PM] & FP_PM_P) { 2138c2ecf20Sopenharmony_ci /* power on the panel if not already power{ed,ing} on */ 2148c2ecf20Sopenharmony_ci if (!(fp & (FP_PM_PANEL_ON|FP_PM_PANEL_PWR_UP))) 2158c2ecf20Sopenharmony_ci write_fp(par, FP_PM, par->fp[FP_PM]); 2168c2ecf20Sopenharmony_ci } else { 2178c2ecf20Sopenharmony_ci /* power down the panel if not already power{ed,ing} down */ 2188c2ecf20Sopenharmony_ci if (!(fp & (FP_PM_PANEL_OFF|FP_PM_PANEL_PWR_DOWN))) 2198c2ecf20Sopenharmony_ci write_fp(par, FP_PM, par->fp[FP_PM]); 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci /* turn everything on */ 2238c2ecf20Sopenharmony_ci write_vp(par, VP_VCFG, par->vp[VP_VCFG]); 2248c2ecf20Sopenharmony_ci write_vp(par, VP_DCFG, par->vp[VP_DCFG]); 2258c2ecf20Sopenharmony_ci write_dc(par, DC_DISPLAY_CFG, par->dc[DC_DISPLAY_CFG]); 2268c2ecf20Sopenharmony_ci /* do this last; it will enable the FIFO load */ 2278c2ecf20Sopenharmony_ci write_dc(par, DC_GENERAL_CFG, par->dc[DC_GENERAL_CFG]); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci /* lock the door behind us */ 2308c2ecf20Sopenharmony_ci write_dc(par, DC_UNLOCK, DC_UNLOCK_LOCK); 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ciint gx_powerdown(struct fb_info *info) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci struct gxfb_par *par = info->par; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci if (par->powered_down) 2388c2ecf20Sopenharmony_ci return 0; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci gx_save_regs(par); 2418c2ecf20Sopenharmony_ci gx_disable_graphics(par); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci par->powered_down = 1; 2448c2ecf20Sopenharmony_ci return 0; 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ciint gx_powerup(struct fb_info *info) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci struct gxfb_par *par = info->par; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci if (!par->powered_down) 2528c2ecf20Sopenharmony_ci return 0; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci gx_restore_regs(par); 2558c2ecf20Sopenharmony_ci gx_enable_graphics(par); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci par->powered_down = 0; 2588c2ecf20Sopenharmony_ci return 0; 2598c2ecf20Sopenharmony_ci} 260