cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

lxfb_ops.c (20767B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/* Geode LX framebuffer driver
      3 *
      4 * Copyright (C) 2006-2007, Advanced Micro Devices,Inc.
      5 */
      6
      7#include <linux/kernel.h>
      8#include <linux/errno.h>
      9#include <linux/fb.h>
     10#include <linux/uaccess.h>
     11#include <linux/delay.h>
     12#include <linux/cs5535.h>
     13
     14#include "lxfb.h"
     15
     16/* TODO
     17 * Support panel scaling
     18 * Add acceleration
     19 * Add support for interlacing (TV out)
     20 * Support compression
     21 */
     22
     23/* This is the complete list of PLL frequencies that we can set -
     24 * we will choose the closest match to the incoming clock.
     25 * freq is the frequency of the dotclock * 1000 (for example,
     26 * 24823 = 24.983 Mhz).
     27 * pllval is the corresponding PLL value
     28*/
     29
     30static const struct {
     31  unsigned int pllval;
     32  unsigned int freq;
     33} pll_table[] = {
     34  { 0x000131AC,   6231 },
     35  { 0x0001215D,   6294 },
     36  { 0x00011087,   6750 },
     37  { 0x0001216C,   7081 },
     38  { 0x0001218D,   7140 },
     39  { 0x000110C9,   7800 },
     40  { 0x00013147,   7875 },
     41  { 0x000110A7,   8258 },
     42  { 0x00012159,   8778 },
     43  { 0x00014249,   8875 },
     44  { 0x00010057,   9000 },
     45  { 0x0001219A,   9472 },
     46  { 0x00012158,   9792 },
     47  { 0x00010045,  10000 },
     48  { 0x00010089,  10791 },
     49  { 0x000110E7,  11225 },
     50  { 0x00012136,  11430 },
     51  { 0x00013207,  12375 },
     52  { 0x00012187,  12500 },
     53  { 0x00014286,  14063 },
     54  { 0x000110E5,  15016 },
     55  { 0x00014214,  16250 },
     56  { 0x00011105,  17045 },
     57  { 0x000131E4,  18563 },
     58  { 0x00013183,  18750 },
     59  { 0x00014284,  19688 },
     60  { 0x00011104,  20400 },
     61  { 0x00016363,  23625 },
     62  { 0x000031AC,  24923 },
     63  { 0x0000215D,  25175 },
     64  { 0x00001087,  27000 },
     65  { 0x0000216C,  28322 },
     66  { 0x0000218D,  28560 },
     67  { 0x000010C9,  31200 },
     68  { 0x00003147,  31500 },
     69  { 0x000010A7,  33032 },
     70  { 0x00002159,  35112 },
     71  { 0x00004249,  35500 },
     72  { 0x00000057,  36000 },
     73  { 0x0000219A,  37889 },
     74  { 0x00002158,  39168 },
     75  { 0x00000045,  40000 },
     76  { 0x00000089,  43163 },
     77  { 0x000010E7,  44900 },
     78  { 0x00002136,  45720 },
     79  { 0x00003207,  49500 },
     80  { 0x00002187,  50000 },
     81  { 0x00004286,  56250 },
     82  { 0x000010E5,  60065 },
     83  { 0x00004214,  65000 },
     84  { 0x00001105,  68179 },
     85  { 0x000031E4,  74250 },
     86  { 0x00003183,  75000 },
     87  { 0x00004284,  78750 },
     88  { 0x00001104,  81600 },
     89  { 0x00006363,  94500 },
     90  { 0x00005303,  97520 },
     91  { 0x00002183, 100187 },
     92  { 0x00002122, 101420 },
     93  { 0x00001081, 108000 },
     94  { 0x00006201, 113310 },
     95  { 0x00000041, 119650 },
     96  { 0x000041A1, 129600 },
     97  { 0x00002182, 133500 },
     98  { 0x000041B1, 135000 },
     99  { 0x00000051, 144000 },
    100  { 0x000041E1, 148500 },
    101  { 0x000062D1, 157500 },
    102  { 0x000031A1, 162000 },
    103  { 0x00000061, 169203 },
    104  { 0x00004231, 172800 },
    105  { 0x00002151, 175500 },
    106  { 0x000052E1, 189000 },
    107  { 0x00000071, 192000 },
    108  { 0x00003201, 198000 },
    109  { 0x00004291, 202500 },
    110  { 0x00001101, 204750 },
    111  { 0x00007481, 218250 },
    112  { 0x00004170, 229500 },
    113  { 0x00006210, 234000 },
    114  { 0x00003140, 251182 },
    115  { 0x00006250, 261000 },
    116  { 0x000041C0, 278400 },
    117  { 0x00005220, 280640 },
    118  { 0x00000050, 288000 },
    119  { 0x000041E0, 297000 },
    120  { 0x00002130, 320207 }
    121};
    122
    123
    124static void lx_set_dotpll(u32 pllval)
    125{
    126	u32 dotpll_lo, dotpll_hi;
    127	int i;
    128
    129	rdmsr(MSR_GLCP_DOTPLL, dotpll_lo, dotpll_hi);
    130
    131	if ((dotpll_lo & MSR_GLCP_DOTPLL_LOCK) && (dotpll_hi == pllval))
    132		return;
    133
    134	dotpll_hi = pllval;
    135	dotpll_lo &= ~(MSR_GLCP_DOTPLL_BYPASS | MSR_GLCP_DOTPLL_HALFPIX);
    136	dotpll_lo |= MSR_GLCP_DOTPLL_DOTRESET;
    137
    138	wrmsr(MSR_GLCP_DOTPLL, dotpll_lo, dotpll_hi);
    139
    140	/* Wait 100us for the PLL to lock */
    141
    142	udelay(100);
    143
    144	/* Now, loop for the lock bit */
    145
    146	for (i = 0; i < 1000; i++) {
    147		rdmsr(MSR_GLCP_DOTPLL, dotpll_lo, dotpll_hi);
    148		if (dotpll_lo & MSR_GLCP_DOTPLL_LOCK)
    149			break;
    150	}
    151
    152	/* Clear the reset bit */
    153
    154	dotpll_lo &= ~MSR_GLCP_DOTPLL_DOTRESET;
    155	wrmsr(MSR_GLCP_DOTPLL, dotpll_lo, dotpll_hi);
    156}
    157
    158/* Set the clock based on the frequency specified by the current mode */
    159
    160static void lx_set_clock(struct fb_info *info)
    161{
    162	unsigned int diff, min, best = 0;
    163	unsigned int freq, i;
    164
    165	freq = (unsigned int) (1000000000 / info->var.pixclock);
    166
    167	min = abs(pll_table[0].freq - freq);
    168
    169	for (i = 0; i < ARRAY_SIZE(pll_table); i++) {
    170		diff = abs(pll_table[i].freq - freq);
    171		if (diff < min) {
    172			min = diff;
    173			best = i;
    174		}
    175	}
    176
    177	lx_set_dotpll(pll_table[best].pllval & 0x00017FFF);
    178}
    179
    180static void lx_graphics_disable(struct fb_info *info)
    181{
    182	struct lxfb_par *par = info->par;
    183	unsigned int val, gcfg;
    184
    185	/* Note:  This assumes that the video is in a quitet state */
    186
    187	write_vp(par, VP_A1T, 0);
    188	write_vp(par, VP_A2T, 0);
    189	write_vp(par, VP_A3T, 0);
    190
    191	/* Turn off the VGA and video enable */
    192	val = read_dc(par, DC_GENERAL_CFG) & ~(DC_GENERAL_CFG_VGAE |
    193			DC_GENERAL_CFG_VIDE);
    194
    195	write_dc(par, DC_GENERAL_CFG, val);
    196
    197	val = read_vp(par, VP_VCFG) & ~VP_VCFG_VID_EN;
    198	write_vp(par, VP_VCFG, val);
    199
    200	write_dc(par, DC_IRQ, DC_IRQ_MASK | DC_IRQ_VIP_VSYNC_LOSS_IRQ_MASK |
    201			DC_IRQ_STATUS | DC_IRQ_VIP_VSYNC_IRQ_STATUS);
    202
    203	val = read_dc(par, DC_GENLK_CTL) & ~DC_GENLK_CTL_GENLK_EN;
    204	write_dc(par, DC_GENLK_CTL, val);
    205
    206	val = read_dc(par, DC_CLR_KEY);
    207	write_dc(par, DC_CLR_KEY, val & ~DC_CLR_KEY_CLR_KEY_EN);
    208
    209	/* turn off the panel */
    210	write_fp(par, FP_PM, read_fp(par, FP_PM) & ~FP_PM_P);
    211
    212	val = read_vp(par, VP_MISC) | VP_MISC_DACPWRDN;
    213	write_vp(par, VP_MISC, val);
    214
    215	/* Turn off the display */
    216
    217	val = read_vp(par, VP_DCFG);
    218	write_vp(par, VP_DCFG, val & ~(VP_DCFG_CRT_EN | VP_DCFG_HSYNC_EN |
    219			VP_DCFG_VSYNC_EN | VP_DCFG_DAC_BL_EN));
    220
    221	gcfg = read_dc(par, DC_GENERAL_CFG);
    222	gcfg &= ~(DC_GENERAL_CFG_CMPE | DC_GENERAL_CFG_DECE);
    223	write_dc(par, DC_GENERAL_CFG, gcfg);
    224
    225	/* Turn off the TGEN */
    226	val = read_dc(par, DC_DISPLAY_CFG);
    227	val &= ~DC_DISPLAY_CFG_TGEN;
    228	write_dc(par, DC_DISPLAY_CFG, val);
    229
    230	/* Wait 1000 usecs to ensure that the TGEN is clear */
    231	udelay(1000);
    232
    233	/* Turn off the FIFO loader */
    234
    235	gcfg &= ~DC_GENERAL_CFG_DFLE;
    236	write_dc(par, DC_GENERAL_CFG, gcfg);
    237
    238	/* Lastly, wait for the GP to go idle */
    239
    240	do {
    241		val = read_gp(par, GP_BLT_STATUS);
    242	} while ((val & GP_BLT_STATUS_PB) || !(val & GP_BLT_STATUS_CE));
    243}
    244
    245static void lx_graphics_enable(struct fb_info *info)
    246{
    247	struct lxfb_par *par = info->par;
    248	u32 temp, config;
    249
    250	/* Set the video request register */
    251	write_vp(par, VP_VRR, 0);
    252
    253	/* Set up the polarities */
    254
    255	config = read_vp(par, VP_DCFG);
    256
    257	config &= ~(VP_DCFG_CRT_SYNC_SKW | VP_DCFG_PWR_SEQ_DELAY |
    258			VP_DCFG_CRT_HSYNC_POL | VP_DCFG_CRT_VSYNC_POL);
    259
    260	config |= (VP_DCFG_CRT_SYNC_SKW_DEFAULT | VP_DCFG_PWR_SEQ_DELAY_DEFAULT
    261			| VP_DCFG_GV_GAM);
    262
    263	if (info->var.sync & FB_SYNC_HOR_HIGH_ACT)
    264		config |= VP_DCFG_CRT_HSYNC_POL;
    265
    266	if (info->var.sync & FB_SYNC_VERT_HIGH_ACT)
    267		config |= VP_DCFG_CRT_VSYNC_POL;
    268
    269	if (par->output & OUTPUT_PANEL) {
    270		u32 msrlo, msrhi;
    271
    272		write_fp(par, FP_PT1, 0);
    273		temp = FP_PT2_SCRC;
    274
    275		if (!(info->var.sync & FB_SYNC_HOR_HIGH_ACT))
    276			temp |= FP_PT2_HSP;
    277
    278		if (!(info->var.sync & FB_SYNC_VERT_HIGH_ACT))
    279			temp |= FP_PT2_VSP;
    280
    281		write_fp(par, FP_PT2, temp);
    282		write_fp(par, FP_DFC, FP_DFC_BC);
    283
    284		msrlo = MSR_LX_MSR_PADSEL_TFT_SEL_LOW;
    285		msrhi = MSR_LX_MSR_PADSEL_TFT_SEL_HIGH;
    286
    287		wrmsr(MSR_LX_MSR_PADSEL, msrlo, msrhi);
    288	}
    289
    290	if (par->output & OUTPUT_CRT) {
    291		config |= VP_DCFG_CRT_EN | VP_DCFG_HSYNC_EN |
    292				VP_DCFG_VSYNC_EN | VP_DCFG_DAC_BL_EN;
    293	}
    294
    295	write_vp(par, VP_DCFG, config);
    296
    297	/* Turn the CRT dacs back on */
    298
    299	if (par->output & OUTPUT_CRT) {
    300		temp = read_vp(par, VP_MISC);
    301		temp &= ~(VP_MISC_DACPWRDN | VP_MISC_APWRDN);
    302		write_vp(par, VP_MISC, temp);
    303	}
    304
    305	/* Turn the panel on (if it isn't already) */
    306	if (par->output & OUTPUT_PANEL)
    307		write_fp(par, FP_PM, read_fp(par, FP_PM) | FP_PM_P);
    308}
    309
    310unsigned int lx_framebuffer_size(void)
    311{
    312	unsigned int val;
    313
    314	if (!cs5535_has_vsa2()) {
    315		uint32_t hi, lo;
    316
    317		/* The number of pages is (PMAX - PMIN)+1 */
    318		rdmsr(MSR_GLIU_P2D_RO0, lo, hi);
    319
    320		/* PMAX */
    321		val = ((hi & 0xff) << 12) | ((lo & 0xfff00000) >> 20);
    322		/* PMIN */
    323		val -= (lo & 0x000fffff);
    324		val += 1;
    325
    326		/* The page size is 4k */
    327		return (val << 12);
    328	}
    329
    330	/* The frame buffer size is reported by a VSM in VSA II */
    331	/* Virtual Register Class    = 0x02                     */
    332	/* VG_MEM_SIZE (1MB units)   = 0x00                     */
    333
    334	outw(VSA_VR_UNLOCK, VSA_VRC_INDEX);
    335	outw(VSA_VR_MEM_SIZE, VSA_VRC_INDEX);
    336
    337	val = (unsigned int)(inw(VSA_VRC_DATA)) & 0xFE;
    338	return (val << 20);
    339}
    340
    341void lx_set_mode(struct fb_info *info)
    342{
    343	struct lxfb_par *par = info->par;
    344	u64 msrval;
    345
    346	unsigned int max, dv, val, size;
    347
    348	unsigned int gcfg, dcfg;
    349	int hactive, hblankstart, hsyncstart, hsyncend, hblankend, htotal;
    350	int vactive, vblankstart, vsyncstart, vsyncend, vblankend, vtotal;
    351
    352	/* Unlock the DC registers */
    353	write_dc(par, DC_UNLOCK, DC_UNLOCK_UNLOCK);
    354
    355	lx_graphics_disable(info);
    356
    357	lx_set_clock(info);
    358
    359	/* Set output mode */
    360
    361	rdmsrl(MSR_LX_GLD_MSR_CONFIG, msrval);
    362	msrval &= ~MSR_LX_GLD_MSR_CONFIG_FMT;
    363
    364	if (par->output & OUTPUT_PANEL) {
    365		msrval |= MSR_LX_GLD_MSR_CONFIG_FMT_FP;
    366
    367		if (par->output & OUTPUT_CRT)
    368			msrval |= MSR_LX_GLD_MSR_CONFIG_FPC;
    369		else
    370			msrval &= ~MSR_LX_GLD_MSR_CONFIG_FPC;
    371	} else
    372		msrval |= MSR_LX_GLD_MSR_CONFIG_FMT_CRT;
    373
    374	wrmsrl(MSR_LX_GLD_MSR_CONFIG, msrval);
    375
    376	/* Clear the various buffers */
    377	/* FIXME:  Adjust for panning here */
    378
    379	write_dc(par, DC_FB_ST_OFFSET, 0);
    380	write_dc(par, DC_CB_ST_OFFSET, 0);
    381	write_dc(par, DC_CURS_ST_OFFSET, 0);
    382
    383	/* FIXME: Add support for interlacing */
    384	/* FIXME: Add support for scaling */
    385
    386	val = read_dc(par, DC_GENLK_CTL);
    387	val &= ~(DC_GENLK_CTL_ALPHA_FLICK_EN | DC_GENLK_CTL_FLICK_EN |
    388			DC_GENLK_CTL_FLICK_SEL_MASK);
    389
    390	/* Default scaling params */
    391
    392	write_dc(par, DC_GFX_SCALE, (0x4000 << 16) | 0x4000);
    393	write_dc(par, DC_IRQ_FILT_CTL, 0);
    394	write_dc(par, DC_GENLK_CTL, val);
    395
    396	/* FIXME:  Support compression */
    397
    398	if (info->fix.line_length > 4096)
    399		dv = DC_DV_CTL_DV_LINE_SIZE_8K;
    400	else if (info->fix.line_length > 2048)
    401		dv = DC_DV_CTL_DV_LINE_SIZE_4K;
    402	else if (info->fix.line_length > 1024)
    403		dv = DC_DV_CTL_DV_LINE_SIZE_2K;
    404	else
    405		dv = DC_DV_CTL_DV_LINE_SIZE_1K;
    406
    407	max = info->fix.line_length * info->var.yres;
    408	max = (max + 0x3FF) & 0xFFFFFC00;
    409
    410	write_dc(par, DC_DV_TOP, max | DC_DV_TOP_DV_TOP_EN);
    411
    412	val = read_dc(par, DC_DV_CTL) & ~DC_DV_CTL_DV_LINE_SIZE;
    413	write_dc(par, DC_DV_CTL, val | dv);
    414
    415	size = info->var.xres * (info->var.bits_per_pixel >> 3);
    416
    417	write_dc(par, DC_GFX_PITCH, info->fix.line_length >> 3);
    418	write_dc(par, DC_LINE_SIZE, (size + 7) >> 3);
    419
    420	/* Set default watermark values */
    421
    422	rdmsrl(MSR_LX_SPARE_MSR, msrval);
    423
    424	msrval &= ~(MSR_LX_SPARE_MSR_DIS_CFIFO_HGO
    425			| MSR_LX_SPARE_MSR_VFIFO_ARB_SEL
    426			| MSR_LX_SPARE_MSR_LOAD_WM_LPEN_M
    427			| MSR_LX_SPARE_MSR_WM_LPEN_OVRD);
    428	msrval |= MSR_LX_SPARE_MSR_DIS_VIFO_WM |
    429			MSR_LX_SPARE_MSR_DIS_INIT_V_PRI;
    430	wrmsrl(MSR_LX_SPARE_MSR, msrval);
    431
    432	gcfg = DC_GENERAL_CFG_DFLE;   /* Display fifo enable */
    433	gcfg |= (0x6 << DC_GENERAL_CFG_DFHPSL_SHIFT) | /* default priority */
    434			(0xb << DC_GENERAL_CFG_DFHPEL_SHIFT);
    435	gcfg |= DC_GENERAL_CFG_FDTY;  /* Set the frame dirty mode */
    436
    437	dcfg  = DC_DISPLAY_CFG_VDEN;  /* Enable video data */
    438	dcfg |= DC_DISPLAY_CFG_GDEN;  /* Enable graphics */
    439	dcfg |= DC_DISPLAY_CFG_TGEN;  /* Turn on the timing generator */
    440	dcfg |= DC_DISPLAY_CFG_TRUP;  /* Update timings immediately */
    441	dcfg |= DC_DISPLAY_CFG_PALB;  /* Palette bypass in > 8 bpp modes */
    442	dcfg |= DC_DISPLAY_CFG_VISL;
    443	dcfg |= DC_DISPLAY_CFG_DCEN;  /* Always center the display */
    444
    445	/* Set the current BPP mode */
    446
    447	switch (info->var.bits_per_pixel) {
    448	case 8:
    449		dcfg |= DC_DISPLAY_CFG_DISP_MODE_8BPP;
    450		break;
    451
    452	case 16:
    453		dcfg |= DC_DISPLAY_CFG_DISP_MODE_16BPP;
    454		break;
    455
    456	case 32:
    457	case 24:
    458		dcfg |= DC_DISPLAY_CFG_DISP_MODE_24BPP;
    459		break;
    460	}
    461
    462	/* Now - set up the timings */
    463
    464	hactive = info->var.xres;
    465	hblankstart = hactive;
    466	hsyncstart = hblankstart + info->var.right_margin;
    467	hsyncend =  hsyncstart + info->var.hsync_len;
    468	hblankend = hsyncend + info->var.left_margin;
    469	htotal = hblankend;
    470
    471	vactive = info->var.yres;
    472	vblankstart = vactive;
    473	vsyncstart = vblankstart + info->var.lower_margin;
    474	vsyncend =  vsyncstart + info->var.vsync_len;
    475	vblankend = vsyncend + info->var.upper_margin;
    476	vtotal = vblankend;
    477
    478	write_dc(par, DC_H_ACTIVE_TIMING, (hactive - 1) | ((htotal - 1) << 16));
    479	write_dc(par, DC_H_BLANK_TIMING,
    480			(hblankstart - 1) | ((hblankend - 1) << 16));
    481	write_dc(par, DC_H_SYNC_TIMING,
    482			(hsyncstart - 1) | ((hsyncend - 1) << 16));
    483
    484	write_dc(par, DC_V_ACTIVE_TIMING, (vactive - 1) | ((vtotal - 1) << 16));
    485	write_dc(par, DC_V_BLANK_TIMING,
    486			(vblankstart - 1) | ((vblankend - 1) << 16));
    487	write_dc(par, DC_V_SYNC_TIMING,
    488			(vsyncstart - 1) | ((vsyncend - 1) << 16));
    489
    490	write_dc(par, DC_FB_ACTIVE,
    491			(info->var.xres - 1) << 16 | (info->var.yres - 1));
    492
    493	/* And re-enable the graphics output */
    494	lx_graphics_enable(info);
    495
    496	/* Write the two main configuration registers */
    497	write_dc(par, DC_DISPLAY_CFG, dcfg);
    498	write_dc(par, DC_ARB_CFG, 0);
    499	write_dc(par, DC_GENERAL_CFG, gcfg);
    500
    501	/* Lock the DC registers */
    502	write_dc(par, DC_UNLOCK, DC_UNLOCK_LOCK);
    503}
    504
    505void lx_set_palette_reg(struct fb_info *info, unsigned regno,
    506			unsigned red, unsigned green, unsigned blue)
    507{
    508	struct lxfb_par *par = info->par;
    509	int val;
    510
    511	/* Hardware palette is in RGB 8-8-8 format. */
    512
    513	val  = (red   << 8) & 0xff0000;
    514	val |= (green)      & 0x00ff00;
    515	val |= (blue  >> 8) & 0x0000ff;
    516
    517	write_dc(par, DC_PAL_ADDRESS, regno);
    518	write_dc(par, DC_PAL_DATA, val);
    519}
    520
    521int lx_blank_display(struct fb_info *info, int blank_mode)
    522{
    523	struct lxfb_par *par = info->par;
    524	u32 dcfg, misc, fp_pm;
    525	int blank, hsync, vsync;
    526
    527	/* CRT power saving modes. */
    528	switch (blank_mode) {
    529	case FB_BLANK_UNBLANK:
    530		blank = 0; hsync = 1; vsync = 1;
    531		break;
    532	case FB_BLANK_NORMAL:
    533		blank = 1; hsync = 1; vsync = 1;
    534		break;
    535	case FB_BLANK_VSYNC_SUSPEND:
    536		blank = 1; hsync = 1; vsync = 0;
    537		break;
    538	case FB_BLANK_HSYNC_SUSPEND:
    539		blank = 1; hsync = 0; vsync = 1;
    540		break;
    541	case FB_BLANK_POWERDOWN:
    542		blank = 1; hsync = 0; vsync = 0;
    543		break;
    544	default:
    545		return -EINVAL;
    546	}
    547
    548	dcfg = read_vp(par, VP_DCFG);
    549	dcfg &= ~(VP_DCFG_DAC_BL_EN | VP_DCFG_HSYNC_EN | VP_DCFG_VSYNC_EN |
    550			VP_DCFG_CRT_EN);
    551	if (!blank)
    552		dcfg |= VP_DCFG_DAC_BL_EN | VP_DCFG_CRT_EN;
    553	if (hsync)
    554		dcfg |= VP_DCFG_HSYNC_EN;
    555	if (vsync)
    556		dcfg |= VP_DCFG_VSYNC_EN;
    557
    558	write_vp(par, VP_DCFG, dcfg);
    559
    560	misc = read_vp(par, VP_MISC);
    561
    562	if (vsync && hsync)
    563		misc &= ~VP_MISC_DACPWRDN;
    564	else
    565		misc |= VP_MISC_DACPWRDN;
    566
    567	write_vp(par, VP_MISC, misc);
    568
    569	/* Power on/off flat panel */
    570
    571	if (par->output & OUTPUT_PANEL) {
    572		fp_pm = read_fp(par, FP_PM);
    573		if (blank_mode == FB_BLANK_POWERDOWN)
    574			fp_pm &= ~FP_PM_P;
    575		else
    576			fp_pm |= FP_PM_P;
    577		write_fp(par, FP_PM, fp_pm);
    578	}
    579
    580	return 0;
    581}
    582
    583static void lx_save_regs(struct lxfb_par *par)
    584{
    585	uint32_t filt;
    586	int i;
    587
    588	/* wait for the BLT engine to stop being busy */
    589	do {
    590		i = read_gp(par, GP_BLT_STATUS);
    591	} while ((i & GP_BLT_STATUS_PB) || !(i & GP_BLT_STATUS_CE));
    592
    593	/* save MSRs */
    594	rdmsrl(MSR_LX_MSR_PADSEL, par->msr.padsel);
    595	rdmsrl(MSR_GLCP_DOTPLL, par->msr.dotpll);
    596	rdmsrl(MSR_LX_GLD_MSR_CONFIG, par->msr.dfglcfg);
    597	rdmsrl(MSR_LX_SPARE_MSR, par->msr.dcspare);
    598
    599	write_dc(par, DC_UNLOCK, DC_UNLOCK_UNLOCK);
    600
    601	/* save registers */
    602	memcpy(par->gp, par->gp_regs, sizeof(par->gp));
    603	memcpy(par->dc, par->dc_regs, sizeof(par->dc));
    604	memcpy(par->vp, par->vp_regs, sizeof(par->vp));
    605	memcpy(par->fp, par->vp_regs + VP_FP_START, sizeof(par->fp));
    606
    607	/* save the display controller palette */
    608	write_dc(par, DC_PAL_ADDRESS, 0);
    609	for (i = 0; i < ARRAY_SIZE(par->dc_pal); i++)
    610		par->dc_pal[i] = read_dc(par, DC_PAL_DATA);
    611
    612	/* save the video processor palette */
    613	write_vp(par, VP_PAR, 0);
    614	for (i = 0; i < ARRAY_SIZE(par->vp_pal); i++)
    615		par->vp_pal[i] = read_vp(par, VP_PDR);
    616
    617	/* save the horizontal filter coefficients */
    618	filt = par->dc[DC_IRQ_FILT_CTL] | DC_IRQ_FILT_CTL_H_FILT_SEL;
    619	for (i = 0; i < ARRAY_SIZE(par->hcoeff); i += 2) {
    620		write_dc(par, DC_IRQ_FILT_CTL, (filt & 0xffffff00) | i);
    621		par->hcoeff[i] = read_dc(par, DC_FILT_COEFF1);
    622		par->hcoeff[i + 1] = read_dc(par, DC_FILT_COEFF2);
    623	}
    624
    625	/* save the vertical filter coefficients */
    626	filt &= ~DC_IRQ_FILT_CTL_H_FILT_SEL;
    627	for (i = 0; i < ARRAY_SIZE(par->vcoeff); i++) {
    628		write_dc(par, DC_IRQ_FILT_CTL, (filt & 0xffffff00) | i);
    629		par->vcoeff[i] = read_dc(par, DC_FILT_COEFF1);
    630	}
    631
    632	/* save video coeff ram */
    633	memcpy(par->vp_coeff, par->vp_regs + VP_VCR, sizeof(par->vp_coeff));
    634}
    635
    636static void lx_restore_gfx_proc(struct lxfb_par *par)
    637{
    638	int i;
    639
    640	/* a bunch of registers require GP_RASTER_MODE to be set first */
    641	write_gp(par, GP_RASTER_MODE, par->gp[GP_RASTER_MODE]);
    642
    643	for (i = 0; i < ARRAY_SIZE(par->gp); i++) {
    644		switch (i) {
    645		case GP_RASTER_MODE:
    646		case GP_VECTOR_MODE:
    647		case GP_BLT_MODE:
    648		case GP_BLT_STATUS:
    649		case GP_HST_SRC:
    650			/* FIXME: restore LUT data */
    651		case GP_LUT_INDEX:
    652		case GP_LUT_DATA:
    653			/* don't restore these registers */
    654			break;
    655
    656		default:
    657			write_gp(par, i, par->gp[i]);
    658		}
    659	}
    660}
    661
    662static void lx_restore_display_ctlr(struct lxfb_par *par)
    663{
    664	uint32_t filt;
    665	int i;
    666
    667	wrmsrl(MSR_LX_SPARE_MSR, par->msr.dcspare);
    668
    669	for (i = 0; i < ARRAY_SIZE(par->dc); i++) {
    670		switch (i) {
    671		case DC_UNLOCK:
    672			/* unlock the DC; runs first */
    673			write_dc(par, DC_UNLOCK, DC_UNLOCK_UNLOCK);
    674			break;
    675
    676		case DC_GENERAL_CFG:
    677		case DC_DISPLAY_CFG:
    678			/* disable all while restoring */
    679			write_dc(par, i, 0);
    680			break;
    681
    682		case DC_DV_CTL:
    683			/* set all ram to dirty */
    684			write_dc(par, i, par->dc[i] | DC_DV_CTL_CLEAR_DV_RAM);
    685			break;
    686
    687		case DC_RSVD_1:
    688		case DC_RSVD_2:
    689		case DC_RSVD_3:
    690		case DC_LINE_CNT:
    691		case DC_PAL_ADDRESS:
    692		case DC_PAL_DATA:
    693		case DC_DFIFO_DIAG:
    694		case DC_CFIFO_DIAG:
    695		case DC_FILT_COEFF1:
    696		case DC_FILT_COEFF2:
    697		case DC_RSVD_4:
    698		case DC_RSVD_5:
    699			/* don't restore these registers */
    700			break;
    701
    702		default:
    703			write_dc(par, i, par->dc[i]);
    704		}
    705	}
    706
    707	/* restore the palette */
    708	write_dc(par, DC_PAL_ADDRESS, 0);
    709	for (i = 0; i < ARRAY_SIZE(par->dc_pal); i++)
    710		write_dc(par, DC_PAL_DATA, par->dc_pal[i]);
    711
    712	/* restore the horizontal filter coefficients */
    713	filt = par->dc[DC_IRQ_FILT_CTL] | DC_IRQ_FILT_CTL_H_FILT_SEL;
    714	for (i = 0; i < ARRAY_SIZE(par->hcoeff); i += 2) {
    715		write_dc(par, DC_IRQ_FILT_CTL, (filt & 0xffffff00) | i);
    716		write_dc(par, DC_FILT_COEFF1, par->hcoeff[i]);
    717		write_dc(par, DC_FILT_COEFF2, par->hcoeff[i + 1]);
    718	}
    719
    720	/* restore the vertical filter coefficients */
    721	filt &= ~DC_IRQ_FILT_CTL_H_FILT_SEL;
    722	for (i = 0; i < ARRAY_SIZE(par->vcoeff); i++) {
    723		write_dc(par, DC_IRQ_FILT_CTL, (filt & 0xffffff00) | i);
    724		write_dc(par, DC_FILT_COEFF1, par->vcoeff[i]);
    725	}
    726}
    727
    728static void lx_restore_video_proc(struct lxfb_par *par)
    729{
    730	int i;
    731
    732	wrmsrl(MSR_LX_GLD_MSR_CONFIG, par->msr.dfglcfg);
    733	wrmsrl(MSR_LX_MSR_PADSEL, par->msr.padsel);
    734
    735	for (i = 0; i < ARRAY_SIZE(par->vp); i++) {
    736		switch (i) {
    737		case VP_VCFG:
    738		case VP_DCFG:
    739		case VP_PAR:
    740		case VP_PDR:
    741		case VP_CCS:
    742		case VP_RSVD_0:
    743		/* case VP_VDC: */ /* why should this not be restored? */
    744		case VP_RSVD_1:
    745		case VP_CRC32:
    746			/* don't restore these registers */
    747			break;
    748
    749		default:
    750			write_vp(par, i, par->vp[i]);
    751		}
    752	}
    753
    754	/* restore video processor palette */
    755	write_vp(par, VP_PAR, 0);
    756	for (i = 0; i < ARRAY_SIZE(par->vp_pal); i++)
    757		write_vp(par, VP_PDR, par->vp_pal[i]);
    758
    759	/* restore video coeff ram */
    760	memcpy(par->vp_regs + VP_VCR, par->vp_coeff, sizeof(par->vp_coeff));
    761}
    762
    763static void lx_restore_regs(struct lxfb_par *par)
    764{
    765	int i;
    766
    767	lx_set_dotpll((u32) (par->msr.dotpll >> 32));
    768	lx_restore_gfx_proc(par);
    769	lx_restore_display_ctlr(par);
    770	lx_restore_video_proc(par);
    771
    772	/* Flat Panel */
    773	for (i = 0; i < ARRAY_SIZE(par->fp); i++) {
    774		switch (i) {
    775		case FP_PM:
    776		case FP_RSVD_0:
    777		case FP_RSVD_1:
    778		case FP_RSVD_2:
    779		case FP_RSVD_3:
    780		case FP_RSVD_4:
    781			/* don't restore these registers */
    782			break;
    783
    784		default:
    785			write_fp(par, i, par->fp[i]);
    786		}
    787	}
    788
    789	/* control the panel */
    790	if (par->fp[FP_PM] & FP_PM_P) {
    791		/* power on the panel if not already power{ed,ing} on */
    792		if (!(read_fp(par, FP_PM) &
    793				(FP_PM_PANEL_ON|FP_PM_PANEL_PWR_UP)))
    794			write_fp(par, FP_PM, par->fp[FP_PM]);
    795	} else {
    796		/* power down the panel if not already power{ed,ing} down */
    797		if (!(read_fp(par, FP_PM) &
    798				(FP_PM_PANEL_OFF|FP_PM_PANEL_PWR_DOWN)))
    799			write_fp(par, FP_PM, par->fp[FP_PM]);
    800	}
    801
    802	/* turn everything on */
    803	write_vp(par, VP_VCFG, par->vp[VP_VCFG]);
    804	write_vp(par, VP_DCFG, par->vp[VP_DCFG]);
    805	write_dc(par, DC_DISPLAY_CFG, par->dc[DC_DISPLAY_CFG]);
    806	/* do this last; it will enable the FIFO load */
    807	write_dc(par, DC_GENERAL_CFG, par->dc[DC_GENERAL_CFG]);
    808
    809	/* lock the door behind us */
    810	write_dc(par, DC_UNLOCK, DC_UNLOCK_LOCK);
    811}
    812
    813int lx_powerdown(struct fb_info *info)
    814{
    815	struct lxfb_par *par = info->par;
    816
    817	if (par->powered_down)
    818		return 0;
    819
    820	lx_save_regs(par);
    821	lx_graphics_disable(info);
    822
    823	par->powered_down = 1;
    824	return 0;
    825}
    826
    827int lx_powerup(struct fb_info *info)
    828{
    829	struct lxfb_par *par = info->par;
    830
    831	if (!par->powered_down)
    832		return 0;
    833
    834	lx_restore_regs(par);
    835
    836	par->powered_down = 0;
    837	return 0;
    838}