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