162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *   Copyright (C) 2007 Advanced Micro Devices, Inc.
462306a36Sopenharmony_ci *   Copyright (C) 2008 Andres Salomon <dilinger@debian.org>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci#include <linux/fb.h>
762306a36Sopenharmony_ci#include <asm/io.h>
862306a36Sopenharmony_ci#include <asm/msr.h>
962306a36Sopenharmony_ci#include <linux/cs5535.h>
1062306a36Sopenharmony_ci#include <asm/delay.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include "gxfb.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistatic void gx_save_regs(struct gxfb_par *par)
1562306a36Sopenharmony_ci{
1662306a36Sopenharmony_ci	int i;
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci	/* wait for the BLT engine to stop being busy */
1962306a36Sopenharmony_ci	do {
2062306a36Sopenharmony_ci		i = read_gp(par, GP_BLT_STATUS);
2162306a36Sopenharmony_ci	} while (i & (GP_BLT_STATUS_BLT_PENDING | GP_BLT_STATUS_BLT_BUSY));
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci	/* save MSRs */
2462306a36Sopenharmony_ci	rdmsrl(MSR_GX_MSR_PADSEL, par->msr.padsel);
2562306a36Sopenharmony_ci	rdmsrl(MSR_GLCP_DOTPLL, par->msr.dotpll);
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	write_dc(par, DC_UNLOCK, DC_UNLOCK_UNLOCK);
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	/* save registers */
3062306a36Sopenharmony_ci	memcpy(par->gp, par->gp_regs, sizeof(par->gp));
3162306a36Sopenharmony_ci	memcpy(par->dc, par->dc_regs, sizeof(par->dc));
3262306a36Sopenharmony_ci	memcpy(par->vp, par->vid_regs, sizeof(par->vp));
3362306a36Sopenharmony_ci	memcpy(par->fp, par->vid_regs + VP_FP_START, sizeof(par->fp));
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	/* save the palette */
3662306a36Sopenharmony_ci	write_dc(par, DC_PAL_ADDRESS, 0);
3762306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(par->pal); i++)
3862306a36Sopenharmony_ci		par->pal[i] = read_dc(par, DC_PAL_DATA);
3962306a36Sopenharmony_ci}
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic void gx_set_dotpll(uint32_t dotpll_hi)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	uint32_t dotpll_lo;
4462306a36Sopenharmony_ci	int i;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	rdmsrl(MSR_GLCP_DOTPLL, dotpll_lo);
4762306a36Sopenharmony_ci	dotpll_lo |= MSR_GLCP_DOTPLL_DOTRESET;
4862306a36Sopenharmony_ci	dotpll_lo &= ~MSR_GLCP_DOTPLL_BYPASS;
4962306a36Sopenharmony_ci	wrmsr(MSR_GLCP_DOTPLL, dotpll_lo, dotpll_hi);
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	/* wait for the PLL to lock */
5262306a36Sopenharmony_ci	for (i = 0; i < 200; i++) {
5362306a36Sopenharmony_ci		rdmsrl(MSR_GLCP_DOTPLL, dotpll_lo);
5462306a36Sopenharmony_ci		if (dotpll_lo & MSR_GLCP_DOTPLL_LOCK)
5562306a36Sopenharmony_ci			break;
5662306a36Sopenharmony_ci		udelay(1);
5762306a36Sopenharmony_ci	}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	/* PLL set, unlock */
6062306a36Sopenharmony_ci	dotpll_lo &= ~MSR_GLCP_DOTPLL_DOTRESET;
6162306a36Sopenharmony_ci	wrmsr(MSR_GLCP_DOTPLL, dotpll_lo, dotpll_hi);
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic void gx_restore_gfx_proc(struct gxfb_par *par)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	int i;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(par->gp); i++) {
6962306a36Sopenharmony_ci		switch (i) {
7062306a36Sopenharmony_ci		case GP_VECTOR_MODE:
7162306a36Sopenharmony_ci		case GP_BLT_MODE:
7262306a36Sopenharmony_ci		case GP_BLT_STATUS:
7362306a36Sopenharmony_ci		case GP_HST_SRC:
7462306a36Sopenharmony_ci			/* don't restore these registers */
7562306a36Sopenharmony_ci			break;
7662306a36Sopenharmony_ci		default:
7762306a36Sopenharmony_ci			write_gp(par, i, par->gp[i]);
7862306a36Sopenharmony_ci		}
7962306a36Sopenharmony_ci	}
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic void gx_restore_display_ctlr(struct gxfb_par *par)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	int i;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(par->dc); i++) {
8762306a36Sopenharmony_ci		switch (i) {
8862306a36Sopenharmony_ci		case DC_UNLOCK:
8962306a36Sopenharmony_ci			/* unlock the DC; runs first */
9062306a36Sopenharmony_ci			write_dc(par, DC_UNLOCK, DC_UNLOCK_UNLOCK);
9162306a36Sopenharmony_ci			break;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci		case DC_GENERAL_CFG:
9462306a36Sopenharmony_ci			/* write without the enables */
9562306a36Sopenharmony_ci			write_dc(par, i, par->dc[i] & ~(DC_GENERAL_CFG_VIDE |
9662306a36Sopenharmony_ci					DC_GENERAL_CFG_ICNE |
9762306a36Sopenharmony_ci					DC_GENERAL_CFG_CURE |
9862306a36Sopenharmony_ci					DC_GENERAL_CFG_DFLE));
9962306a36Sopenharmony_ci			break;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci		case DC_DISPLAY_CFG:
10262306a36Sopenharmony_ci			/* write without the enables */
10362306a36Sopenharmony_ci			write_dc(par, i, par->dc[i] & ~(DC_DISPLAY_CFG_VDEN |
10462306a36Sopenharmony_ci					DC_DISPLAY_CFG_GDEN |
10562306a36Sopenharmony_ci					DC_DISPLAY_CFG_TGEN));
10662306a36Sopenharmony_ci			break;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci		case DC_RSVD_0:
10962306a36Sopenharmony_ci		case DC_RSVD_1:
11062306a36Sopenharmony_ci		case DC_RSVD_2:
11162306a36Sopenharmony_ci		case DC_RSVD_3:
11262306a36Sopenharmony_ci		case DC_RSVD_4:
11362306a36Sopenharmony_ci		case DC_LINE_CNT:
11462306a36Sopenharmony_ci		case DC_PAL_ADDRESS:
11562306a36Sopenharmony_ci		case DC_PAL_DATA:
11662306a36Sopenharmony_ci		case DC_DFIFO_DIAG:
11762306a36Sopenharmony_ci		case DC_CFIFO_DIAG:
11862306a36Sopenharmony_ci		case DC_RSVD_5:
11962306a36Sopenharmony_ci			/* don't restore these registers */
12062306a36Sopenharmony_ci			break;
12162306a36Sopenharmony_ci		default:
12262306a36Sopenharmony_ci			write_dc(par, i, par->dc[i]);
12362306a36Sopenharmony_ci		}
12462306a36Sopenharmony_ci	}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	/* restore the palette */
12762306a36Sopenharmony_ci	write_dc(par, DC_PAL_ADDRESS, 0);
12862306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(par->pal); i++)
12962306a36Sopenharmony_ci		write_dc(par, DC_PAL_DATA, par->pal[i]);
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistatic void gx_restore_video_proc(struct gxfb_par *par)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	int i;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	wrmsrl(MSR_GX_MSR_PADSEL, par->msr.padsel);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(par->vp); i++) {
13962306a36Sopenharmony_ci		switch (i) {
14062306a36Sopenharmony_ci		case VP_VCFG:
14162306a36Sopenharmony_ci			/* don't enable video yet */
14262306a36Sopenharmony_ci			write_vp(par, i, par->vp[i] & ~VP_VCFG_VID_EN);
14362306a36Sopenharmony_ci			break;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci		case VP_DCFG:
14662306a36Sopenharmony_ci			/* don't enable CRT yet */
14762306a36Sopenharmony_ci			write_vp(par, i, par->vp[i] &
14862306a36Sopenharmony_ci					~(VP_DCFG_DAC_BL_EN | VP_DCFG_VSYNC_EN |
14962306a36Sopenharmony_ci					VP_DCFG_HSYNC_EN | VP_DCFG_CRT_EN));
15062306a36Sopenharmony_ci			break;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci		case VP_GAR:
15362306a36Sopenharmony_ci		case VP_GDR:
15462306a36Sopenharmony_ci		case VP_RSVD_0:
15562306a36Sopenharmony_ci		case VP_RSVD_1:
15662306a36Sopenharmony_ci		case VP_RSVD_2:
15762306a36Sopenharmony_ci		case VP_RSVD_3:
15862306a36Sopenharmony_ci		case VP_CRC32:
15962306a36Sopenharmony_ci		case VP_AWT:
16062306a36Sopenharmony_ci		case VP_VTM:
16162306a36Sopenharmony_ci			/* don't restore these registers */
16262306a36Sopenharmony_ci			break;
16362306a36Sopenharmony_ci		default:
16462306a36Sopenharmony_ci			write_vp(par, i, par->vp[i]);
16562306a36Sopenharmony_ci		}
16662306a36Sopenharmony_ci	}
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic void gx_restore_regs(struct gxfb_par *par)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	int i;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	gx_set_dotpll((uint32_t) (par->msr.dotpll >> 32));
17462306a36Sopenharmony_ci	gx_restore_gfx_proc(par);
17562306a36Sopenharmony_ci	gx_restore_display_ctlr(par);
17662306a36Sopenharmony_ci	gx_restore_video_proc(par);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	/* Flat Panel */
17962306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(par->fp); i++) {
18062306a36Sopenharmony_ci		if (i != FP_PM && i != FP_RSVD_0)
18162306a36Sopenharmony_ci			write_fp(par, i, par->fp[i]);
18262306a36Sopenharmony_ci	}
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cistatic void gx_disable_graphics(struct gxfb_par *par)
18662306a36Sopenharmony_ci{
18762306a36Sopenharmony_ci	/* shut down the engine */
18862306a36Sopenharmony_ci	write_vp(par, VP_VCFG, par->vp[VP_VCFG] & ~VP_VCFG_VID_EN);
18962306a36Sopenharmony_ci	write_vp(par, VP_DCFG, par->vp[VP_DCFG] & ~(VP_DCFG_DAC_BL_EN |
19062306a36Sopenharmony_ci			VP_DCFG_VSYNC_EN | VP_DCFG_HSYNC_EN | VP_DCFG_CRT_EN));
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	/* turn off the flat panel */
19362306a36Sopenharmony_ci	write_fp(par, FP_PM, par->fp[FP_PM] & ~FP_PM_P);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	/* turn off display */
19762306a36Sopenharmony_ci	write_dc(par, DC_UNLOCK, DC_UNLOCK_UNLOCK);
19862306a36Sopenharmony_ci	write_dc(par, DC_GENERAL_CFG, par->dc[DC_GENERAL_CFG] &
19962306a36Sopenharmony_ci			~(DC_GENERAL_CFG_VIDE | DC_GENERAL_CFG_ICNE |
20062306a36Sopenharmony_ci			DC_GENERAL_CFG_CURE | DC_GENERAL_CFG_DFLE));
20162306a36Sopenharmony_ci	write_dc(par, DC_DISPLAY_CFG, par->dc[DC_DISPLAY_CFG] &
20262306a36Sopenharmony_ci			~(DC_DISPLAY_CFG_VDEN | DC_DISPLAY_CFG_GDEN |
20362306a36Sopenharmony_ci			DC_DISPLAY_CFG_TGEN));
20462306a36Sopenharmony_ci	write_dc(par, DC_UNLOCK, DC_UNLOCK_LOCK);
20562306a36Sopenharmony_ci}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_cistatic void gx_enable_graphics(struct gxfb_par *par)
20862306a36Sopenharmony_ci{
20962306a36Sopenharmony_ci	uint32_t fp;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	fp = read_fp(par, FP_PM);
21262306a36Sopenharmony_ci	if (par->fp[FP_PM] & FP_PM_P) {
21362306a36Sopenharmony_ci		/* power on the panel if not already power{ed,ing} on */
21462306a36Sopenharmony_ci		if (!(fp & (FP_PM_PANEL_ON|FP_PM_PANEL_PWR_UP)))
21562306a36Sopenharmony_ci			write_fp(par, FP_PM, par->fp[FP_PM]);
21662306a36Sopenharmony_ci	} else {
21762306a36Sopenharmony_ci		/* power down the panel if not already power{ed,ing} down */
21862306a36Sopenharmony_ci		if (!(fp & (FP_PM_PANEL_OFF|FP_PM_PANEL_PWR_DOWN)))
21962306a36Sopenharmony_ci			write_fp(par, FP_PM, par->fp[FP_PM]);
22062306a36Sopenharmony_ci	}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	/* turn everything on */
22362306a36Sopenharmony_ci	write_vp(par, VP_VCFG, par->vp[VP_VCFG]);
22462306a36Sopenharmony_ci	write_vp(par, VP_DCFG, par->vp[VP_DCFG]);
22562306a36Sopenharmony_ci	write_dc(par, DC_DISPLAY_CFG, par->dc[DC_DISPLAY_CFG]);
22662306a36Sopenharmony_ci	/* do this last; it will enable the FIFO load */
22762306a36Sopenharmony_ci	write_dc(par, DC_GENERAL_CFG, par->dc[DC_GENERAL_CFG]);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	/* lock the door behind us */
23062306a36Sopenharmony_ci	write_dc(par, DC_UNLOCK, DC_UNLOCK_LOCK);
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ciint gx_powerdown(struct fb_info *info)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	struct gxfb_par *par = info->par;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	if (par->powered_down)
23862306a36Sopenharmony_ci		return 0;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	gx_save_regs(par);
24162306a36Sopenharmony_ci	gx_disable_graphics(par);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	par->powered_down = 1;
24462306a36Sopenharmony_ci	return 0;
24562306a36Sopenharmony_ci}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ciint gx_powerup(struct fb_info *info)
24862306a36Sopenharmony_ci{
24962306a36Sopenharmony_ci	struct gxfb_par *par = info->par;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	if (!par->powered_down)
25262306a36Sopenharmony_ci		return 0;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	gx_restore_regs(par);
25562306a36Sopenharmony_ci	gx_enable_graphics(par);
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	par->powered_down  = 0;
25862306a36Sopenharmony_ci	return 0;
25962306a36Sopenharmony_ci}
260