162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Geode GX video processor device.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *   Copyright (C) 2006 Arcom Control Systems Ltd.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *   Portions from AMD's original 2.4 driver:
862306a36Sopenharmony_ci *     Copyright (C) 2004 Advanced Micro Devices, Inc.
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci#include <linux/fb.h>
1162306a36Sopenharmony_ci#include <linux/delay.h>
1262306a36Sopenharmony_ci#include <asm/io.h>
1362306a36Sopenharmony_ci#include <asm/delay.h>
1462306a36Sopenharmony_ci#include <asm/msr.h>
1562306a36Sopenharmony_ci#include <linux/cs5535.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include "gxfb.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci/*
2162306a36Sopenharmony_ci * Tables of register settings for various DOTCLKs.
2262306a36Sopenharmony_ci */
2362306a36Sopenharmony_cistruct gx_pll_entry {
2462306a36Sopenharmony_ci	long pixclock; /* ps */
2562306a36Sopenharmony_ci	u32 sys_rstpll_bits;
2662306a36Sopenharmony_ci	u32 dotpll_value;
2762306a36Sopenharmony_ci};
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define POSTDIV3 ((u32)MSR_GLCP_SYS_RSTPLL_DOTPOSTDIV3)
3062306a36Sopenharmony_ci#define PREMULT2 ((u32)MSR_GLCP_SYS_RSTPLL_DOTPREMULT2)
3162306a36Sopenharmony_ci#define PREDIV2  ((u32)MSR_GLCP_SYS_RSTPLL_DOTPOSTDIV3)
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic const struct gx_pll_entry gx_pll_table_48MHz[] = {
3462306a36Sopenharmony_ci	{ 40123, POSTDIV3,	    0x00000BF2 },	/*  24.9230 */
3562306a36Sopenharmony_ci	{ 39721, 0,		    0x00000037 },	/*  25.1750 */
3662306a36Sopenharmony_ci	{ 35308, POSTDIV3|PREMULT2, 0x00000B1A },	/*  28.3220 */
3762306a36Sopenharmony_ci	{ 31746, POSTDIV3,	    0x000002D2 },	/*  31.5000 */
3862306a36Sopenharmony_ci	{ 27777, POSTDIV3|PREMULT2, 0x00000FE2 },	/*  36.0000 */
3962306a36Sopenharmony_ci	{ 26666, POSTDIV3,	    0x0000057A },	/*  37.5000 */
4062306a36Sopenharmony_ci	{ 25000, POSTDIV3,	    0x0000030A },	/*  40.0000 */
4162306a36Sopenharmony_ci	{ 22271, 0,		    0x00000063 },	/*  44.9000 */
4262306a36Sopenharmony_ci	{ 20202, 0,		    0x0000054B },	/*  49.5000 */
4362306a36Sopenharmony_ci	{ 20000, 0,		    0x0000026E },	/*  50.0000 */
4462306a36Sopenharmony_ci	{ 19860, PREMULT2,	    0x00000037 },	/*  50.3500 */
4562306a36Sopenharmony_ci	{ 18518, POSTDIV3|PREMULT2, 0x00000B0D },	/*  54.0000 */
4662306a36Sopenharmony_ci	{ 17777, 0,		    0x00000577 },	/*  56.2500 */
4762306a36Sopenharmony_ci	{ 17733, 0,		    0x000007F7 },	/*  56.3916 */
4862306a36Sopenharmony_ci	{ 17653, 0,		    0x0000057B },	/*  56.6444 */
4962306a36Sopenharmony_ci	{ 16949, PREMULT2,	    0x00000707 },	/*  59.0000 */
5062306a36Sopenharmony_ci	{ 15873, POSTDIV3|PREMULT2, 0x00000B39 },	/*  63.0000 */
5162306a36Sopenharmony_ci	{ 15384, POSTDIV3|PREMULT2, 0x00000B45 },	/*  65.0000 */
5262306a36Sopenharmony_ci	{ 14814, POSTDIV3|PREMULT2, 0x00000FC1 },	/*  67.5000 */
5362306a36Sopenharmony_ci	{ 14124, POSTDIV3,	    0x00000561 },	/*  70.8000 */
5462306a36Sopenharmony_ci	{ 13888, POSTDIV3,	    0x000007E1 },	/*  72.0000 */
5562306a36Sopenharmony_ci	{ 13426, PREMULT2,	    0x00000F4A },	/*  74.4810 */
5662306a36Sopenharmony_ci	{ 13333, 0,		    0x00000052 },	/*  75.0000 */
5762306a36Sopenharmony_ci	{ 12698, 0,		    0x00000056 },	/*  78.7500 */
5862306a36Sopenharmony_ci	{ 12500, POSTDIV3|PREMULT2, 0x00000709 },	/*  80.0000 */
5962306a36Sopenharmony_ci	{ 11135, PREMULT2,	    0x00000262 },	/*  89.8000 */
6062306a36Sopenharmony_ci	{ 10582, 0,		    0x000002D2 },	/*  94.5000 */
6162306a36Sopenharmony_ci	{ 10101, PREMULT2,	    0x00000B4A },	/*  99.0000 */
6262306a36Sopenharmony_ci	{ 10000, PREMULT2,	    0x00000036 },	/* 100.0000 */
6362306a36Sopenharmony_ci	{  9259, 0,		    0x000007E2 },	/* 108.0000 */
6462306a36Sopenharmony_ci	{  8888, 0,		    0x000007F6 },	/* 112.5000 */
6562306a36Sopenharmony_ci	{  7692, POSTDIV3|PREMULT2, 0x00000FB0 },	/* 130.0000 */
6662306a36Sopenharmony_ci	{  7407, POSTDIV3|PREMULT2, 0x00000B50 },	/* 135.0000 */
6762306a36Sopenharmony_ci	{  6349, 0,		    0x00000055 },	/* 157.5000 */
6862306a36Sopenharmony_ci	{  6172, 0,		    0x000009C1 },	/* 162.0000 */
6962306a36Sopenharmony_ci	{  5787, PREMULT2,	    0x0000002D },	/* 172.798  */
7062306a36Sopenharmony_ci	{  5698, 0,		    0x000002C1 },	/* 175.5000 */
7162306a36Sopenharmony_ci	{  5291, 0,		    0x000002D1 },	/* 189.0000 */
7262306a36Sopenharmony_ci	{  4938, 0,		    0x00000551 },	/* 202.5000 */
7362306a36Sopenharmony_ci	{  4357, 0,		    0x0000057D },	/* 229.5000 */
7462306a36Sopenharmony_ci};
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic const struct gx_pll_entry gx_pll_table_14MHz[] = {
7762306a36Sopenharmony_ci	{ 39721, 0, 0x00000037 },	/*  25.1750 */
7862306a36Sopenharmony_ci	{ 35308, 0, 0x00000B7B },	/*  28.3220 */
7962306a36Sopenharmony_ci	{ 31746, 0, 0x000004D3 },	/*  31.5000 */
8062306a36Sopenharmony_ci	{ 27777, 0, 0x00000BE3 },	/*  36.0000 */
8162306a36Sopenharmony_ci	{ 26666, 0, 0x0000074F },	/*  37.5000 */
8262306a36Sopenharmony_ci	{ 25000, 0, 0x0000050B },	/*  40.0000 */
8362306a36Sopenharmony_ci	{ 22271, 0, 0x00000063 },	/*  44.9000 */
8462306a36Sopenharmony_ci	{ 20202, 0, 0x0000054B },	/*  49.5000 */
8562306a36Sopenharmony_ci	{ 20000, 0, 0x0000026E },	/*  50.0000 */
8662306a36Sopenharmony_ci	{ 19860, 0, 0x000007C3 },	/*  50.3500 */
8762306a36Sopenharmony_ci	{ 18518, 0, 0x000007E3 },	/*  54.0000 */
8862306a36Sopenharmony_ci	{ 17777, 0, 0x00000577 },	/*  56.2500 */
8962306a36Sopenharmony_ci	{ 17733, 0, 0x000002FB },	/*  56.3916 */
9062306a36Sopenharmony_ci	{ 17653, 0, 0x0000057B },	/*  56.6444 */
9162306a36Sopenharmony_ci	{ 16949, 0, 0x0000058B },	/*  59.0000 */
9262306a36Sopenharmony_ci	{ 15873, 0, 0x0000095E },	/*  63.0000 */
9362306a36Sopenharmony_ci	{ 15384, 0, 0x0000096A },	/*  65.0000 */
9462306a36Sopenharmony_ci	{ 14814, 0, 0x00000BC2 },	/*  67.5000 */
9562306a36Sopenharmony_ci	{ 14124, 0, 0x0000098A },	/*  70.8000 */
9662306a36Sopenharmony_ci	{ 13888, 0, 0x00000BE2 },	/*  72.0000 */
9762306a36Sopenharmony_ci	{ 13333, 0, 0x00000052 },	/*  75.0000 */
9862306a36Sopenharmony_ci	{ 12698, 0, 0x00000056 },	/*  78.7500 */
9962306a36Sopenharmony_ci	{ 12500, 0, 0x0000050A },	/*  80.0000 */
10062306a36Sopenharmony_ci	{ 11135, 0, 0x0000078E },	/*  89.8000 */
10162306a36Sopenharmony_ci	{ 10582, 0, 0x000002D2 },	/*  94.5000 */
10262306a36Sopenharmony_ci	{ 10101, 0, 0x000011F6 },	/*  99.0000 */
10362306a36Sopenharmony_ci	{ 10000, 0, 0x0000054E },	/* 100.0000 */
10462306a36Sopenharmony_ci	{  9259, 0, 0x000007E2 },	/* 108.0000 */
10562306a36Sopenharmony_ci	{  8888, 0, 0x000002FA },	/* 112.5000 */
10662306a36Sopenharmony_ci	{  7692, 0, 0x00000BB1 },	/* 130.0000 */
10762306a36Sopenharmony_ci	{  7407, 0, 0x00000975 },	/* 135.0000 */
10862306a36Sopenharmony_ci	{  6349, 0, 0x00000055 },	/* 157.5000 */
10962306a36Sopenharmony_ci	{  6172, 0, 0x000009C1 },	/* 162.0000 */
11062306a36Sopenharmony_ci	{  5698, 0, 0x000002C1 },	/* 175.5000 */
11162306a36Sopenharmony_ci	{  5291, 0, 0x00000539 },	/* 189.0000 */
11262306a36Sopenharmony_ci	{  4938, 0, 0x00000551 },	/* 202.5000 */
11362306a36Sopenharmony_ci	{  4357, 0, 0x0000057D },	/* 229.5000 */
11462306a36Sopenharmony_ci};
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_civoid gx_set_dclk_frequency(struct fb_info *info)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	const struct gx_pll_entry *pll_table;
11962306a36Sopenharmony_ci	int pll_table_len;
12062306a36Sopenharmony_ci	int i, best_i;
12162306a36Sopenharmony_ci	long min, diff;
12262306a36Sopenharmony_ci	u64 dotpll, sys_rstpll;
12362306a36Sopenharmony_ci	int timeout = 1000;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	/* Rev. 1 Geode GXs use a 14 MHz reference clock instead of 48 MHz. */
12662306a36Sopenharmony_ci	if (cpu_data(0).x86_stepping == 1) {
12762306a36Sopenharmony_ci		pll_table = gx_pll_table_14MHz;
12862306a36Sopenharmony_ci		pll_table_len = ARRAY_SIZE(gx_pll_table_14MHz);
12962306a36Sopenharmony_ci	} else {
13062306a36Sopenharmony_ci		pll_table = gx_pll_table_48MHz;
13162306a36Sopenharmony_ci		pll_table_len = ARRAY_SIZE(gx_pll_table_48MHz);
13262306a36Sopenharmony_ci	}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	/* Search the table for the closest pixclock. */
13562306a36Sopenharmony_ci	best_i = 0;
13662306a36Sopenharmony_ci	min = abs(pll_table[0].pixclock - info->var.pixclock);
13762306a36Sopenharmony_ci	for (i = 1; i < pll_table_len; i++) {
13862306a36Sopenharmony_ci		diff = abs(pll_table[i].pixclock - info->var.pixclock);
13962306a36Sopenharmony_ci		if (diff < min) {
14062306a36Sopenharmony_ci			min = diff;
14162306a36Sopenharmony_ci			best_i = i;
14262306a36Sopenharmony_ci		}
14362306a36Sopenharmony_ci	}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	rdmsrl(MSR_GLCP_SYS_RSTPLL, sys_rstpll);
14662306a36Sopenharmony_ci	rdmsrl(MSR_GLCP_DOTPLL, dotpll);
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	/* Program new M, N and P. */
14962306a36Sopenharmony_ci	dotpll &= 0x00000000ffffffffull;
15062306a36Sopenharmony_ci	dotpll |= (u64)pll_table[best_i].dotpll_value << 32;
15162306a36Sopenharmony_ci	dotpll |= MSR_GLCP_DOTPLL_DOTRESET;
15262306a36Sopenharmony_ci	dotpll &= ~MSR_GLCP_DOTPLL_BYPASS;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	wrmsrl(MSR_GLCP_DOTPLL, dotpll);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	/* Program dividers. */
15762306a36Sopenharmony_ci	sys_rstpll &= ~( MSR_GLCP_SYS_RSTPLL_DOTPREDIV2
15862306a36Sopenharmony_ci			 | MSR_GLCP_SYS_RSTPLL_DOTPREMULT2
15962306a36Sopenharmony_ci			 | MSR_GLCP_SYS_RSTPLL_DOTPOSTDIV3 );
16062306a36Sopenharmony_ci	sys_rstpll |= pll_table[best_i].sys_rstpll_bits;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	wrmsrl(MSR_GLCP_SYS_RSTPLL, sys_rstpll);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	/* Clear reset bit to start PLL. */
16562306a36Sopenharmony_ci	dotpll &= ~(MSR_GLCP_DOTPLL_DOTRESET);
16662306a36Sopenharmony_ci	wrmsrl(MSR_GLCP_DOTPLL, dotpll);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	/* Wait for LOCK bit. */
16962306a36Sopenharmony_ci	do {
17062306a36Sopenharmony_ci		rdmsrl(MSR_GLCP_DOTPLL, dotpll);
17162306a36Sopenharmony_ci	} while (timeout-- && !(dotpll & MSR_GLCP_DOTPLL_LOCK));
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_cistatic void
17562306a36Sopenharmony_cigx_configure_tft(struct fb_info *info)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	struct gxfb_par *par = info->par;
17862306a36Sopenharmony_ci	unsigned long val;
17962306a36Sopenharmony_ci	unsigned long fp;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	/* Set up the DF pad select MSR */
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	rdmsrl(MSR_GX_MSR_PADSEL, val);
18462306a36Sopenharmony_ci	val &= ~MSR_GX_MSR_PADSEL_MASK;
18562306a36Sopenharmony_ci	val |= MSR_GX_MSR_PADSEL_TFT;
18662306a36Sopenharmony_ci	wrmsrl(MSR_GX_MSR_PADSEL, val);
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	/* Turn off the panel */
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	fp = read_fp(par, FP_PM);
19162306a36Sopenharmony_ci	fp &= ~FP_PM_P;
19262306a36Sopenharmony_ci	write_fp(par, FP_PM, fp);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	/* Set timing 1 */
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	fp = read_fp(par, FP_PT1);
19762306a36Sopenharmony_ci	fp &= FP_PT1_VSIZE_MASK;
19862306a36Sopenharmony_ci	fp |= info->var.yres << FP_PT1_VSIZE_SHIFT;
19962306a36Sopenharmony_ci	write_fp(par, FP_PT1, fp);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	/* Timing 2 */
20262306a36Sopenharmony_ci	/* Set bits that are always on for TFT */
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	fp = 0x0F100000;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	/* Configure sync polarity */
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	if (!(info->var.sync & FB_SYNC_VERT_HIGH_ACT))
20962306a36Sopenharmony_ci		fp |= FP_PT2_VSP;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	if (!(info->var.sync & FB_SYNC_HOR_HIGH_ACT))
21262306a36Sopenharmony_ci		fp |= FP_PT2_HSP;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	write_fp(par, FP_PT2, fp);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	/*  Set the dither control */
21762306a36Sopenharmony_ci	write_fp(par, FP_DFC, FP_DFC_NFI);
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	/* Enable the FP data and power (in case the BIOS didn't) */
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	fp = read_vp(par, VP_DCFG);
22262306a36Sopenharmony_ci	fp |= VP_DCFG_FP_PWR_EN | VP_DCFG_FP_DATA_EN;
22362306a36Sopenharmony_ci	write_vp(par, VP_DCFG, fp);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	/* Unblank the panel */
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	fp = read_fp(par, FP_PM);
22862306a36Sopenharmony_ci	fp |= FP_PM_P;
22962306a36Sopenharmony_ci	write_fp(par, FP_PM, fp);
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_civoid gx_configure_display(struct fb_info *info)
23362306a36Sopenharmony_ci{
23462306a36Sopenharmony_ci	struct gxfb_par *par = info->par;
23562306a36Sopenharmony_ci	u32 dcfg, misc;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	/* Write the display configuration */
23862306a36Sopenharmony_ci	dcfg = read_vp(par, VP_DCFG);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	/* Disable hsync and vsync */
24162306a36Sopenharmony_ci	dcfg &= ~(VP_DCFG_VSYNC_EN | VP_DCFG_HSYNC_EN);
24262306a36Sopenharmony_ci	write_vp(par, VP_DCFG, dcfg);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	/* Clear bits from existing mode. */
24562306a36Sopenharmony_ci	dcfg &= ~(VP_DCFG_CRT_SYNC_SKW
24662306a36Sopenharmony_ci		  | VP_DCFG_CRT_HSYNC_POL   | VP_DCFG_CRT_VSYNC_POL
24762306a36Sopenharmony_ci		  | VP_DCFG_VSYNC_EN        | VP_DCFG_HSYNC_EN);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	/* Set default sync skew.  */
25062306a36Sopenharmony_ci	dcfg |= VP_DCFG_CRT_SYNC_SKW_DEFAULT;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	/* Enable hsync and vsync. */
25362306a36Sopenharmony_ci	dcfg |= VP_DCFG_HSYNC_EN | VP_DCFG_VSYNC_EN;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	misc = read_vp(par, VP_MISC);
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	/* Disable gamma correction */
25862306a36Sopenharmony_ci	misc |= VP_MISC_GAM_EN;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	if (par->enable_crt) {
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci		/* Power up the CRT DACs */
26362306a36Sopenharmony_ci		misc &= ~(VP_MISC_APWRDN | VP_MISC_DACPWRDN);
26462306a36Sopenharmony_ci		write_vp(par, VP_MISC, misc);
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci		/* Only change the sync polarities if we are running
26762306a36Sopenharmony_ci		 * in CRT mode.  The FP polarities will be handled in
26862306a36Sopenharmony_ci		 * gxfb_configure_tft */
26962306a36Sopenharmony_ci		if (!(info->var.sync & FB_SYNC_HOR_HIGH_ACT))
27062306a36Sopenharmony_ci			dcfg |= VP_DCFG_CRT_HSYNC_POL;
27162306a36Sopenharmony_ci		if (!(info->var.sync & FB_SYNC_VERT_HIGH_ACT))
27262306a36Sopenharmony_ci			dcfg |= VP_DCFG_CRT_VSYNC_POL;
27362306a36Sopenharmony_ci	} else {
27462306a36Sopenharmony_ci		/* Power down the CRT DACs if in FP mode */
27562306a36Sopenharmony_ci		misc |= (VP_MISC_APWRDN | VP_MISC_DACPWRDN);
27662306a36Sopenharmony_ci		write_vp(par, VP_MISC, misc);
27762306a36Sopenharmony_ci	}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	/* Enable the display logic */
28062306a36Sopenharmony_ci	/* Set up the DACS to blank normally */
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	dcfg |= VP_DCFG_CRT_EN | VP_DCFG_DAC_BL_EN;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	/* Enable the external DAC VREF? */
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	write_vp(par, VP_DCFG, dcfg);
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	/* Set up the flat panel (if it is enabled) */
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	if (par->enable_crt == 0)
29162306a36Sopenharmony_ci		gx_configure_tft(info);
29262306a36Sopenharmony_ci}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ciint gx_blank_display(struct fb_info *info, int blank_mode)
29562306a36Sopenharmony_ci{
29662306a36Sopenharmony_ci	struct gxfb_par *par = info->par;
29762306a36Sopenharmony_ci	u32 dcfg, fp_pm;
29862306a36Sopenharmony_ci	int blank, hsync, vsync, crt;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	/* CRT power saving modes. */
30162306a36Sopenharmony_ci	switch (blank_mode) {
30262306a36Sopenharmony_ci	case FB_BLANK_UNBLANK:
30362306a36Sopenharmony_ci		blank = 0; hsync = 1; vsync = 1; crt = 1;
30462306a36Sopenharmony_ci		break;
30562306a36Sopenharmony_ci	case FB_BLANK_NORMAL:
30662306a36Sopenharmony_ci		blank = 1; hsync = 1; vsync = 1; crt = 1;
30762306a36Sopenharmony_ci		break;
30862306a36Sopenharmony_ci	case FB_BLANK_VSYNC_SUSPEND:
30962306a36Sopenharmony_ci		blank = 1; hsync = 1; vsync = 0; crt = 1;
31062306a36Sopenharmony_ci		break;
31162306a36Sopenharmony_ci	case FB_BLANK_HSYNC_SUSPEND:
31262306a36Sopenharmony_ci		blank = 1; hsync = 0; vsync = 1; crt = 1;
31362306a36Sopenharmony_ci		break;
31462306a36Sopenharmony_ci	case FB_BLANK_POWERDOWN:
31562306a36Sopenharmony_ci		blank = 1; hsync = 0; vsync = 0; crt = 0;
31662306a36Sopenharmony_ci		break;
31762306a36Sopenharmony_ci	default:
31862306a36Sopenharmony_ci		return -EINVAL;
31962306a36Sopenharmony_ci	}
32062306a36Sopenharmony_ci	dcfg = read_vp(par, VP_DCFG);
32162306a36Sopenharmony_ci	dcfg &= ~(VP_DCFG_DAC_BL_EN | VP_DCFG_HSYNC_EN | VP_DCFG_VSYNC_EN |
32262306a36Sopenharmony_ci			VP_DCFG_CRT_EN);
32362306a36Sopenharmony_ci	if (!blank)
32462306a36Sopenharmony_ci		dcfg |= VP_DCFG_DAC_BL_EN;
32562306a36Sopenharmony_ci	if (hsync)
32662306a36Sopenharmony_ci		dcfg |= VP_DCFG_HSYNC_EN;
32762306a36Sopenharmony_ci	if (vsync)
32862306a36Sopenharmony_ci		dcfg |= VP_DCFG_VSYNC_EN;
32962306a36Sopenharmony_ci	if (crt)
33062306a36Sopenharmony_ci		dcfg |= VP_DCFG_CRT_EN;
33162306a36Sopenharmony_ci	write_vp(par, VP_DCFG, dcfg);
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	/* Power on/off flat panel. */
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	if (par->enable_crt == 0) {
33662306a36Sopenharmony_ci		fp_pm = read_fp(par, FP_PM);
33762306a36Sopenharmony_ci		if (blank_mode == FB_BLANK_POWERDOWN)
33862306a36Sopenharmony_ci			fp_pm &= ~FP_PM_P;
33962306a36Sopenharmony_ci		else
34062306a36Sopenharmony_ci			fp_pm |= FP_PM_P;
34162306a36Sopenharmony_ci		write_fp(par, FP_PM, fp_pm);
34262306a36Sopenharmony_ci	}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	return 0;
34562306a36Sopenharmony_ci}
346