Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /* Geode LX framebuffer driver
0003  *
0004  * Copyright (C) 2006-2007, Advanced Micro Devices,Inc.
0005  */
0006 
0007 #include <linux/kernel.h>
0008 #include <linux/errno.h>
0009 #include <linux/fb.h>
0010 #include <linux/uaccess.h>
0011 #include <linux/delay.h>
0012 #include <linux/cs5535.h>
0013 
0014 #include "lxfb.h"
0015 
0016 /* TODO
0017  * Support panel scaling
0018  * Add acceleration
0019  * Add support for interlacing (TV out)
0020  * Support compression
0021  */
0022 
0023 /* This is the complete list of PLL frequencies that we can set -
0024  * we will choose the closest match to the incoming clock.
0025  * freq is the frequency of the dotclock * 1000 (for example,
0026  * 24823 = 24.983 Mhz).
0027  * pllval is the corresponding PLL value
0028 */
0029 
0030 static const struct {
0031   unsigned int pllval;
0032   unsigned int freq;
0033 } pll_table[] = {
0034   { 0x000131AC,   6231 },
0035   { 0x0001215D,   6294 },
0036   { 0x00011087,   6750 },
0037   { 0x0001216C,   7081 },
0038   { 0x0001218D,   7140 },
0039   { 0x000110C9,   7800 },
0040   { 0x00013147,   7875 },
0041   { 0x000110A7,   8258 },
0042   { 0x00012159,   8778 },
0043   { 0x00014249,   8875 },
0044   { 0x00010057,   9000 },
0045   { 0x0001219A,   9472 },
0046   { 0x00012158,   9792 },
0047   { 0x00010045,  10000 },
0048   { 0x00010089,  10791 },
0049   { 0x000110E7,  11225 },
0050   { 0x00012136,  11430 },
0051   { 0x00013207,  12375 },
0052   { 0x00012187,  12500 },
0053   { 0x00014286,  14063 },
0054   { 0x000110E5,  15016 },
0055   { 0x00014214,  16250 },
0056   { 0x00011105,  17045 },
0057   { 0x000131E4,  18563 },
0058   { 0x00013183,  18750 },
0059   { 0x00014284,  19688 },
0060   { 0x00011104,  20400 },
0061   { 0x00016363,  23625 },
0062   { 0x000031AC,  24923 },
0063   { 0x0000215D,  25175 },
0064   { 0x00001087,  27000 },
0065   { 0x0000216C,  28322 },
0066   { 0x0000218D,  28560 },
0067   { 0x000010C9,  31200 },
0068   { 0x00003147,  31500 },
0069   { 0x000010A7,  33032 },
0070   { 0x00002159,  35112 },
0071   { 0x00004249,  35500 },
0072   { 0x00000057,  36000 },
0073   { 0x0000219A,  37889 },
0074   { 0x00002158,  39168 },
0075   { 0x00000045,  40000 },
0076   { 0x00000089,  43163 },
0077   { 0x000010E7,  44900 },
0078   { 0x00002136,  45720 },
0079   { 0x00003207,  49500 },
0080   { 0x00002187,  50000 },
0081   { 0x00004286,  56250 },
0082   { 0x000010E5,  60065 },
0083   { 0x00004214,  65000 },
0084   { 0x00001105,  68179 },
0085   { 0x000031E4,  74250 },
0086   { 0x00003183,  75000 },
0087   { 0x00004284,  78750 },
0088   { 0x00001104,  81600 },
0089   { 0x00006363,  94500 },
0090   { 0x00005303,  97520 },
0091   { 0x00002183, 100187 },
0092   { 0x00002122, 101420 },
0093   { 0x00001081, 108000 },
0094   { 0x00006201, 113310 },
0095   { 0x00000041, 119650 },
0096   { 0x000041A1, 129600 },
0097   { 0x00002182, 133500 },
0098   { 0x000041B1, 135000 },
0099   { 0x00000051, 144000 },
0100   { 0x000041E1, 148500 },
0101   { 0x000062D1, 157500 },
0102   { 0x000031A1, 162000 },
0103   { 0x00000061, 169203 },
0104   { 0x00004231, 172800 },
0105   { 0x00002151, 175500 },
0106   { 0x000052E1, 189000 },
0107   { 0x00000071, 192000 },
0108   { 0x00003201, 198000 },
0109   { 0x00004291, 202500 },
0110   { 0x00001101, 204750 },
0111   { 0x00007481, 218250 },
0112   { 0x00004170, 229500 },
0113   { 0x00006210, 234000 },
0114   { 0x00003140, 251182 },
0115   { 0x00006250, 261000 },
0116   { 0x000041C0, 278400 },
0117   { 0x00005220, 280640 },
0118   { 0x00000050, 288000 },
0119   { 0x000041E0, 297000 },
0120   { 0x00002130, 320207 }
0121 };
0122 
0123 
0124 static void lx_set_dotpll(u32 pllval)
0125 {
0126     u32 dotpll_lo, dotpll_hi;
0127     int i;
0128 
0129     rdmsr(MSR_GLCP_DOTPLL, dotpll_lo, dotpll_hi);
0130 
0131     if ((dotpll_lo & MSR_GLCP_DOTPLL_LOCK) && (dotpll_hi == pllval))
0132         return;
0133 
0134     dotpll_hi = pllval;
0135     dotpll_lo &= ~(MSR_GLCP_DOTPLL_BYPASS | MSR_GLCP_DOTPLL_HALFPIX);
0136     dotpll_lo |= MSR_GLCP_DOTPLL_DOTRESET;
0137 
0138     wrmsr(MSR_GLCP_DOTPLL, dotpll_lo, dotpll_hi);
0139 
0140     /* Wait 100us for the PLL to lock */
0141 
0142     udelay(100);
0143 
0144     /* Now, loop for the lock bit */
0145 
0146     for (i = 0; i < 1000; i++) {
0147         rdmsr(MSR_GLCP_DOTPLL, dotpll_lo, dotpll_hi);
0148         if (dotpll_lo & MSR_GLCP_DOTPLL_LOCK)
0149             break;
0150     }
0151 
0152     /* Clear the reset bit */
0153 
0154     dotpll_lo &= ~MSR_GLCP_DOTPLL_DOTRESET;
0155     wrmsr(MSR_GLCP_DOTPLL, dotpll_lo, dotpll_hi);
0156 }
0157 
0158 /* Set the clock based on the frequency specified by the current mode */
0159 
0160 static void lx_set_clock(struct fb_info *info)
0161 {
0162     unsigned int diff, min, best = 0;
0163     unsigned int freq, i;
0164 
0165     freq = (unsigned int) (1000000000 / info->var.pixclock);
0166 
0167     min = abs(pll_table[0].freq - freq);
0168 
0169     for (i = 0; i < ARRAY_SIZE(pll_table); i++) {
0170         diff = abs(pll_table[i].freq - freq);
0171         if (diff < min) {
0172             min = diff;
0173             best = i;
0174         }
0175     }
0176 
0177     lx_set_dotpll(pll_table[best].pllval & 0x00017FFF);
0178 }
0179 
0180 static void lx_graphics_disable(struct fb_info *info)
0181 {
0182     struct lxfb_par *par = info->par;
0183     unsigned int val, gcfg;
0184 
0185     /* Note:  This assumes that the video is in a quitet state */
0186 
0187     write_vp(par, VP_A1T, 0);
0188     write_vp(par, VP_A2T, 0);
0189     write_vp(par, VP_A3T, 0);
0190 
0191     /* Turn off the VGA and video enable */
0192     val = read_dc(par, DC_GENERAL_CFG) & ~(DC_GENERAL_CFG_VGAE |
0193             DC_GENERAL_CFG_VIDE);
0194 
0195     write_dc(par, DC_GENERAL_CFG, val);
0196 
0197     val = read_vp(par, VP_VCFG) & ~VP_VCFG_VID_EN;
0198     write_vp(par, VP_VCFG, val);
0199 
0200     write_dc(par, DC_IRQ, DC_IRQ_MASK | DC_IRQ_VIP_VSYNC_LOSS_IRQ_MASK |
0201             DC_IRQ_STATUS | DC_IRQ_VIP_VSYNC_IRQ_STATUS);
0202 
0203     val = read_dc(par, DC_GENLK_CTL) & ~DC_GENLK_CTL_GENLK_EN;
0204     write_dc(par, DC_GENLK_CTL, val);
0205 
0206     val = read_dc(par, DC_CLR_KEY);
0207     write_dc(par, DC_CLR_KEY, val & ~DC_CLR_KEY_CLR_KEY_EN);
0208 
0209     /* turn off the panel */
0210     write_fp(par, FP_PM, read_fp(par, FP_PM) & ~FP_PM_P);
0211 
0212     val = read_vp(par, VP_MISC) | VP_MISC_DACPWRDN;
0213     write_vp(par, VP_MISC, val);
0214 
0215     /* Turn off the display */
0216 
0217     val = read_vp(par, VP_DCFG);
0218     write_vp(par, VP_DCFG, val & ~(VP_DCFG_CRT_EN | VP_DCFG_HSYNC_EN |
0219             VP_DCFG_VSYNC_EN | VP_DCFG_DAC_BL_EN));
0220 
0221     gcfg = read_dc(par, DC_GENERAL_CFG);
0222     gcfg &= ~(DC_GENERAL_CFG_CMPE | DC_GENERAL_CFG_DECE);
0223     write_dc(par, DC_GENERAL_CFG, gcfg);
0224 
0225     /* Turn off the TGEN */
0226     val = read_dc(par, DC_DISPLAY_CFG);
0227     val &= ~DC_DISPLAY_CFG_TGEN;
0228     write_dc(par, DC_DISPLAY_CFG, val);
0229 
0230     /* Wait 1000 usecs to ensure that the TGEN is clear */
0231     udelay(1000);
0232 
0233     /* Turn off the FIFO loader */
0234 
0235     gcfg &= ~DC_GENERAL_CFG_DFLE;
0236     write_dc(par, DC_GENERAL_CFG, gcfg);
0237 
0238     /* Lastly, wait for the GP to go idle */
0239 
0240     do {
0241         val = read_gp(par, GP_BLT_STATUS);
0242     } while ((val & GP_BLT_STATUS_PB) || !(val & GP_BLT_STATUS_CE));
0243 }
0244 
0245 static void lx_graphics_enable(struct fb_info *info)
0246 {
0247     struct lxfb_par *par = info->par;
0248     u32 temp, config;
0249 
0250     /* Set the video request register */
0251     write_vp(par, VP_VRR, 0);
0252 
0253     /* Set up the polarities */
0254 
0255     config = read_vp(par, VP_DCFG);
0256 
0257     config &= ~(VP_DCFG_CRT_SYNC_SKW | VP_DCFG_PWR_SEQ_DELAY |
0258             VP_DCFG_CRT_HSYNC_POL | VP_DCFG_CRT_VSYNC_POL);
0259 
0260     config |= (VP_DCFG_CRT_SYNC_SKW_DEFAULT | VP_DCFG_PWR_SEQ_DELAY_DEFAULT
0261             | VP_DCFG_GV_GAM);
0262 
0263     if (info->var.sync & FB_SYNC_HOR_HIGH_ACT)
0264         config |= VP_DCFG_CRT_HSYNC_POL;
0265 
0266     if (info->var.sync & FB_SYNC_VERT_HIGH_ACT)
0267         config |= VP_DCFG_CRT_VSYNC_POL;
0268 
0269     if (par->output & OUTPUT_PANEL) {
0270         u32 msrlo, msrhi;
0271 
0272         write_fp(par, FP_PT1, 0);
0273         temp = FP_PT2_SCRC;
0274 
0275         if (!(info->var.sync & FB_SYNC_HOR_HIGH_ACT))
0276             temp |= FP_PT2_HSP;
0277 
0278         if (!(info->var.sync & FB_SYNC_VERT_HIGH_ACT))
0279             temp |= FP_PT2_VSP;
0280 
0281         write_fp(par, FP_PT2, temp);
0282         write_fp(par, FP_DFC, FP_DFC_BC);
0283 
0284         msrlo = MSR_LX_MSR_PADSEL_TFT_SEL_LOW;
0285         msrhi = MSR_LX_MSR_PADSEL_TFT_SEL_HIGH;
0286 
0287         wrmsr(MSR_LX_MSR_PADSEL, msrlo, msrhi);
0288     }
0289 
0290     if (par->output & OUTPUT_CRT) {
0291         config |= VP_DCFG_CRT_EN | VP_DCFG_HSYNC_EN |
0292                 VP_DCFG_VSYNC_EN | VP_DCFG_DAC_BL_EN;
0293     }
0294 
0295     write_vp(par, VP_DCFG, config);
0296 
0297     /* Turn the CRT dacs back on */
0298 
0299     if (par->output & OUTPUT_CRT) {
0300         temp = read_vp(par, VP_MISC);
0301         temp &= ~(VP_MISC_DACPWRDN | VP_MISC_APWRDN);
0302         write_vp(par, VP_MISC, temp);
0303     }
0304 
0305     /* Turn the panel on (if it isn't already) */
0306     if (par->output & OUTPUT_PANEL)
0307         write_fp(par, FP_PM, read_fp(par, FP_PM) | FP_PM_P);
0308 }
0309 
0310 unsigned int lx_framebuffer_size(void)
0311 {
0312     unsigned int val;
0313 
0314     if (!cs5535_has_vsa2()) {
0315         uint32_t hi, lo;
0316 
0317         /* The number of pages is (PMAX - PMIN)+1 */
0318         rdmsr(MSR_GLIU_P2D_RO0, lo, hi);
0319 
0320         /* PMAX */
0321         val = ((hi & 0xff) << 12) | ((lo & 0xfff00000) >> 20);
0322         /* PMIN */
0323         val -= (lo & 0x000fffff);
0324         val += 1;
0325 
0326         /* The page size is 4k */
0327         return (val << 12);
0328     }
0329 
0330     /* The frame buffer size is reported by a VSM in VSA II */
0331     /* Virtual Register Class    = 0x02                     */
0332     /* VG_MEM_SIZE (1MB units)   = 0x00                     */
0333 
0334     outw(VSA_VR_UNLOCK, VSA_VRC_INDEX);
0335     outw(VSA_VR_MEM_SIZE, VSA_VRC_INDEX);
0336 
0337     val = (unsigned int)(inw(VSA_VRC_DATA)) & 0xFE;
0338     return (val << 20);
0339 }
0340 
0341 void lx_set_mode(struct fb_info *info)
0342 {
0343     struct lxfb_par *par = info->par;
0344     u64 msrval;
0345 
0346     unsigned int max, dv, val, size;
0347 
0348     unsigned int gcfg, dcfg;
0349     int hactive, hblankstart, hsyncstart, hsyncend, hblankend, htotal;
0350     int vactive, vblankstart, vsyncstart, vsyncend, vblankend, vtotal;
0351 
0352     /* Unlock the DC registers */
0353     write_dc(par, DC_UNLOCK, DC_UNLOCK_UNLOCK);
0354 
0355     lx_graphics_disable(info);
0356 
0357     lx_set_clock(info);
0358 
0359     /* Set output mode */
0360 
0361     rdmsrl(MSR_LX_GLD_MSR_CONFIG, msrval);
0362     msrval &= ~MSR_LX_GLD_MSR_CONFIG_FMT;
0363 
0364     if (par->output & OUTPUT_PANEL) {
0365         msrval |= MSR_LX_GLD_MSR_CONFIG_FMT_FP;
0366 
0367         if (par->output & OUTPUT_CRT)
0368             msrval |= MSR_LX_GLD_MSR_CONFIG_FPC;
0369         else
0370             msrval &= ~MSR_LX_GLD_MSR_CONFIG_FPC;
0371     } else
0372         msrval |= MSR_LX_GLD_MSR_CONFIG_FMT_CRT;
0373 
0374     wrmsrl(MSR_LX_GLD_MSR_CONFIG, msrval);
0375 
0376     /* Clear the various buffers */
0377     /* FIXME:  Adjust for panning here */
0378 
0379     write_dc(par, DC_FB_ST_OFFSET, 0);
0380     write_dc(par, DC_CB_ST_OFFSET, 0);
0381     write_dc(par, DC_CURS_ST_OFFSET, 0);
0382 
0383     /* FIXME: Add support for interlacing */
0384     /* FIXME: Add support for scaling */
0385 
0386     val = read_dc(par, DC_GENLK_CTL);
0387     val &= ~(DC_GENLK_CTL_ALPHA_FLICK_EN | DC_GENLK_CTL_FLICK_EN |
0388             DC_GENLK_CTL_FLICK_SEL_MASK);
0389 
0390     /* Default scaling params */
0391 
0392     write_dc(par, DC_GFX_SCALE, (0x4000 << 16) | 0x4000);
0393     write_dc(par, DC_IRQ_FILT_CTL, 0);
0394     write_dc(par, DC_GENLK_CTL, val);
0395 
0396     /* FIXME:  Support compression */
0397 
0398     if (info->fix.line_length > 4096)
0399         dv = DC_DV_CTL_DV_LINE_SIZE_8K;
0400     else if (info->fix.line_length > 2048)
0401         dv = DC_DV_CTL_DV_LINE_SIZE_4K;
0402     else if (info->fix.line_length > 1024)
0403         dv = DC_DV_CTL_DV_LINE_SIZE_2K;
0404     else
0405         dv = DC_DV_CTL_DV_LINE_SIZE_1K;
0406 
0407     max = info->fix.line_length * info->var.yres;
0408     max = (max + 0x3FF) & 0xFFFFFC00;
0409 
0410     write_dc(par, DC_DV_TOP, max | DC_DV_TOP_DV_TOP_EN);
0411 
0412     val = read_dc(par, DC_DV_CTL) & ~DC_DV_CTL_DV_LINE_SIZE;
0413     write_dc(par, DC_DV_CTL, val | dv);
0414 
0415     size = info->var.xres * (info->var.bits_per_pixel >> 3);
0416 
0417     write_dc(par, DC_GFX_PITCH, info->fix.line_length >> 3);
0418     write_dc(par, DC_LINE_SIZE, (size + 7) >> 3);
0419 
0420     /* Set default watermark values */
0421 
0422     rdmsrl(MSR_LX_SPARE_MSR, msrval);
0423 
0424     msrval &= ~(MSR_LX_SPARE_MSR_DIS_CFIFO_HGO
0425             | MSR_LX_SPARE_MSR_VFIFO_ARB_SEL
0426             | MSR_LX_SPARE_MSR_LOAD_WM_LPEN_M
0427             | MSR_LX_SPARE_MSR_WM_LPEN_OVRD);
0428     msrval |= MSR_LX_SPARE_MSR_DIS_VIFO_WM |
0429             MSR_LX_SPARE_MSR_DIS_INIT_V_PRI;
0430     wrmsrl(MSR_LX_SPARE_MSR, msrval);
0431 
0432     gcfg = DC_GENERAL_CFG_DFLE;   /* Display fifo enable */
0433     gcfg |= (0x6 << DC_GENERAL_CFG_DFHPSL_SHIFT) | /* default priority */
0434             (0xb << DC_GENERAL_CFG_DFHPEL_SHIFT);
0435     gcfg |= DC_GENERAL_CFG_FDTY;  /* Set the frame dirty mode */
0436 
0437     dcfg  = DC_DISPLAY_CFG_VDEN;  /* Enable video data */
0438     dcfg |= DC_DISPLAY_CFG_GDEN;  /* Enable graphics */
0439     dcfg |= DC_DISPLAY_CFG_TGEN;  /* Turn on the timing generator */
0440     dcfg |= DC_DISPLAY_CFG_TRUP;  /* Update timings immediately */
0441     dcfg |= DC_DISPLAY_CFG_PALB;  /* Palette bypass in > 8 bpp modes */
0442     dcfg |= DC_DISPLAY_CFG_VISL;
0443     dcfg |= DC_DISPLAY_CFG_DCEN;  /* Always center the display */
0444 
0445     /* Set the current BPP mode */
0446 
0447     switch (info->var.bits_per_pixel) {
0448     case 8:
0449         dcfg |= DC_DISPLAY_CFG_DISP_MODE_8BPP;
0450         break;
0451 
0452     case 16:
0453         dcfg |= DC_DISPLAY_CFG_DISP_MODE_16BPP;
0454         break;
0455 
0456     case 32:
0457     case 24:
0458         dcfg |= DC_DISPLAY_CFG_DISP_MODE_24BPP;
0459         break;
0460     }
0461 
0462     /* Now - set up the timings */
0463 
0464     hactive = info->var.xres;
0465     hblankstart = hactive;
0466     hsyncstart = hblankstart + info->var.right_margin;
0467     hsyncend =  hsyncstart + info->var.hsync_len;
0468     hblankend = hsyncend + info->var.left_margin;
0469     htotal = hblankend;
0470 
0471     vactive = info->var.yres;
0472     vblankstart = vactive;
0473     vsyncstart = vblankstart + info->var.lower_margin;
0474     vsyncend =  vsyncstart + info->var.vsync_len;
0475     vblankend = vsyncend + info->var.upper_margin;
0476     vtotal = vblankend;
0477 
0478     write_dc(par, DC_H_ACTIVE_TIMING, (hactive - 1) | ((htotal - 1) << 16));
0479     write_dc(par, DC_H_BLANK_TIMING,
0480             (hblankstart - 1) | ((hblankend - 1) << 16));
0481     write_dc(par, DC_H_SYNC_TIMING,
0482             (hsyncstart - 1) | ((hsyncend - 1) << 16));
0483 
0484     write_dc(par, DC_V_ACTIVE_TIMING, (vactive - 1) | ((vtotal - 1) << 16));
0485     write_dc(par, DC_V_BLANK_TIMING,
0486             (vblankstart - 1) | ((vblankend - 1) << 16));
0487     write_dc(par, DC_V_SYNC_TIMING,
0488             (vsyncstart - 1) | ((vsyncend - 1) << 16));
0489 
0490     write_dc(par, DC_FB_ACTIVE,
0491             (info->var.xres - 1) << 16 | (info->var.yres - 1));
0492 
0493     /* And re-enable the graphics output */
0494     lx_graphics_enable(info);
0495 
0496     /* Write the two main configuration registers */
0497     write_dc(par, DC_DISPLAY_CFG, dcfg);
0498     write_dc(par, DC_ARB_CFG, 0);
0499     write_dc(par, DC_GENERAL_CFG, gcfg);
0500 
0501     /* Lock the DC registers */
0502     write_dc(par, DC_UNLOCK, DC_UNLOCK_LOCK);
0503 }
0504 
0505 void lx_set_palette_reg(struct fb_info *info, unsigned regno,
0506             unsigned red, unsigned green, unsigned blue)
0507 {
0508     struct lxfb_par *par = info->par;
0509     int val;
0510 
0511     /* Hardware palette is in RGB 8-8-8 format. */
0512 
0513     val  = (red   << 8) & 0xff0000;
0514     val |= (green)      & 0x00ff00;
0515     val |= (blue  >> 8) & 0x0000ff;
0516 
0517     write_dc(par, DC_PAL_ADDRESS, regno);
0518     write_dc(par, DC_PAL_DATA, val);
0519 }
0520 
0521 int lx_blank_display(struct fb_info *info, int blank_mode)
0522 {
0523     struct lxfb_par *par = info->par;
0524     u32 dcfg, misc, fp_pm;
0525     int blank, hsync, vsync;
0526 
0527     /* CRT power saving modes. */
0528     switch (blank_mode) {
0529     case FB_BLANK_UNBLANK:
0530         blank = 0; hsync = 1; vsync = 1;
0531         break;
0532     case FB_BLANK_NORMAL:
0533         blank = 1; hsync = 1; vsync = 1;
0534         break;
0535     case FB_BLANK_VSYNC_SUSPEND:
0536         blank = 1; hsync = 1; vsync = 0;
0537         break;
0538     case FB_BLANK_HSYNC_SUSPEND:
0539         blank = 1; hsync = 0; vsync = 1;
0540         break;
0541     case FB_BLANK_POWERDOWN:
0542         blank = 1; hsync = 0; vsync = 0;
0543         break;
0544     default:
0545         return -EINVAL;
0546     }
0547 
0548     dcfg = read_vp(par, VP_DCFG);
0549     dcfg &= ~(VP_DCFG_DAC_BL_EN | VP_DCFG_HSYNC_EN | VP_DCFG_VSYNC_EN |
0550             VP_DCFG_CRT_EN);
0551     if (!blank)
0552         dcfg |= VP_DCFG_DAC_BL_EN | VP_DCFG_CRT_EN;
0553     if (hsync)
0554         dcfg |= VP_DCFG_HSYNC_EN;
0555     if (vsync)
0556         dcfg |= VP_DCFG_VSYNC_EN;
0557 
0558     write_vp(par, VP_DCFG, dcfg);
0559 
0560     misc = read_vp(par, VP_MISC);
0561 
0562     if (vsync && hsync)
0563         misc &= ~VP_MISC_DACPWRDN;
0564     else
0565         misc |= VP_MISC_DACPWRDN;
0566 
0567     write_vp(par, VP_MISC, misc);
0568 
0569     /* Power on/off flat panel */
0570 
0571     if (par->output & OUTPUT_PANEL) {
0572         fp_pm = read_fp(par, FP_PM);
0573         if (blank_mode == FB_BLANK_POWERDOWN)
0574             fp_pm &= ~FP_PM_P;
0575         else
0576             fp_pm |= FP_PM_P;
0577         write_fp(par, FP_PM, fp_pm);
0578     }
0579 
0580     return 0;
0581 }
0582 
0583 static void lx_save_regs(struct lxfb_par *par)
0584 {
0585     uint32_t filt;
0586     int i;
0587 
0588     /* wait for the BLT engine to stop being busy */
0589     do {
0590         i = read_gp(par, GP_BLT_STATUS);
0591     } while ((i & GP_BLT_STATUS_PB) || !(i & GP_BLT_STATUS_CE));
0592 
0593     /* save MSRs */
0594     rdmsrl(MSR_LX_MSR_PADSEL, par->msr.padsel);
0595     rdmsrl(MSR_GLCP_DOTPLL, par->msr.dotpll);
0596     rdmsrl(MSR_LX_GLD_MSR_CONFIG, par->msr.dfglcfg);
0597     rdmsrl(MSR_LX_SPARE_MSR, par->msr.dcspare);
0598 
0599     write_dc(par, DC_UNLOCK, DC_UNLOCK_UNLOCK);
0600 
0601     /* save registers */
0602     memcpy(par->gp, par->gp_regs, sizeof(par->gp));
0603     memcpy(par->dc, par->dc_regs, sizeof(par->dc));
0604     memcpy(par->vp, par->vp_regs, sizeof(par->vp));
0605     memcpy(par->fp, par->vp_regs + VP_FP_START, sizeof(par->fp));
0606 
0607     /* save the display controller palette */
0608     write_dc(par, DC_PAL_ADDRESS, 0);
0609     for (i = 0; i < ARRAY_SIZE(par->dc_pal); i++)
0610         par->dc_pal[i] = read_dc(par, DC_PAL_DATA);
0611 
0612     /* save the video processor palette */
0613     write_vp(par, VP_PAR, 0);
0614     for (i = 0; i < ARRAY_SIZE(par->vp_pal); i++)
0615         par->vp_pal[i] = read_vp(par, VP_PDR);
0616 
0617     /* save the horizontal filter coefficients */
0618     filt = par->dc[DC_IRQ_FILT_CTL] | DC_IRQ_FILT_CTL_H_FILT_SEL;
0619     for (i = 0; i < ARRAY_SIZE(par->hcoeff); i += 2) {
0620         write_dc(par, DC_IRQ_FILT_CTL, (filt & 0xffffff00) | i);
0621         par->hcoeff[i] = read_dc(par, DC_FILT_COEFF1);
0622         par->hcoeff[i + 1] = read_dc(par, DC_FILT_COEFF2);
0623     }
0624 
0625     /* save the vertical filter coefficients */
0626     filt &= ~DC_IRQ_FILT_CTL_H_FILT_SEL;
0627     for (i = 0; i < ARRAY_SIZE(par->vcoeff); i++) {
0628         write_dc(par, DC_IRQ_FILT_CTL, (filt & 0xffffff00) | i);
0629         par->vcoeff[i] = read_dc(par, DC_FILT_COEFF1);
0630     }
0631 
0632     /* save video coeff ram */
0633     memcpy(par->vp_coeff, par->vp_regs + VP_VCR, sizeof(par->vp_coeff));
0634 }
0635 
0636 static void lx_restore_gfx_proc(struct lxfb_par *par)
0637 {
0638     int i;
0639 
0640     /* a bunch of registers require GP_RASTER_MODE to be set first */
0641     write_gp(par, GP_RASTER_MODE, par->gp[GP_RASTER_MODE]);
0642 
0643     for (i = 0; i < ARRAY_SIZE(par->gp); i++) {
0644         switch (i) {
0645         case GP_RASTER_MODE:
0646         case GP_VECTOR_MODE:
0647         case GP_BLT_MODE:
0648         case GP_BLT_STATUS:
0649         case GP_HST_SRC:
0650             /* FIXME: restore LUT data */
0651         case GP_LUT_INDEX:
0652         case GP_LUT_DATA:
0653             /* don't restore these registers */
0654             break;
0655 
0656         default:
0657             write_gp(par, i, par->gp[i]);
0658         }
0659     }
0660 }
0661 
0662 static void lx_restore_display_ctlr(struct lxfb_par *par)
0663 {
0664     uint32_t filt;
0665     int i;
0666 
0667     wrmsrl(MSR_LX_SPARE_MSR, par->msr.dcspare);
0668 
0669     for (i = 0; i < ARRAY_SIZE(par->dc); i++) {
0670         switch (i) {
0671         case DC_UNLOCK:
0672             /* unlock the DC; runs first */
0673             write_dc(par, DC_UNLOCK, DC_UNLOCK_UNLOCK);
0674             break;
0675 
0676         case DC_GENERAL_CFG:
0677         case DC_DISPLAY_CFG:
0678             /* disable all while restoring */
0679             write_dc(par, i, 0);
0680             break;
0681 
0682         case DC_DV_CTL:
0683             /* set all ram to dirty */
0684             write_dc(par, i, par->dc[i] | DC_DV_CTL_CLEAR_DV_RAM);
0685             break;
0686 
0687         case DC_RSVD_1:
0688         case DC_RSVD_2:
0689         case DC_RSVD_3:
0690         case DC_LINE_CNT:
0691         case DC_PAL_ADDRESS:
0692         case DC_PAL_DATA:
0693         case DC_DFIFO_DIAG:
0694         case DC_CFIFO_DIAG:
0695         case DC_FILT_COEFF1:
0696         case DC_FILT_COEFF2:
0697         case DC_RSVD_4:
0698         case DC_RSVD_5:
0699             /* don't restore these registers */
0700             break;
0701 
0702         default:
0703             write_dc(par, i, par->dc[i]);
0704         }
0705     }
0706 
0707     /* restore the palette */
0708     write_dc(par, DC_PAL_ADDRESS, 0);
0709     for (i = 0; i < ARRAY_SIZE(par->dc_pal); i++)
0710         write_dc(par, DC_PAL_DATA, par->dc_pal[i]);
0711 
0712     /* restore the horizontal filter coefficients */
0713     filt = par->dc[DC_IRQ_FILT_CTL] | DC_IRQ_FILT_CTL_H_FILT_SEL;
0714     for (i = 0; i < ARRAY_SIZE(par->hcoeff); i += 2) {
0715         write_dc(par, DC_IRQ_FILT_CTL, (filt & 0xffffff00) | i);
0716         write_dc(par, DC_FILT_COEFF1, par->hcoeff[i]);
0717         write_dc(par, DC_FILT_COEFF2, par->hcoeff[i + 1]);
0718     }
0719 
0720     /* restore the vertical filter coefficients */
0721     filt &= ~DC_IRQ_FILT_CTL_H_FILT_SEL;
0722     for (i = 0; i < ARRAY_SIZE(par->vcoeff); i++) {
0723         write_dc(par, DC_IRQ_FILT_CTL, (filt & 0xffffff00) | i);
0724         write_dc(par, DC_FILT_COEFF1, par->vcoeff[i]);
0725     }
0726 }
0727 
0728 static void lx_restore_video_proc(struct lxfb_par *par)
0729 {
0730     int i;
0731 
0732     wrmsrl(MSR_LX_GLD_MSR_CONFIG, par->msr.dfglcfg);
0733     wrmsrl(MSR_LX_MSR_PADSEL, par->msr.padsel);
0734 
0735     for (i = 0; i < ARRAY_SIZE(par->vp); i++) {
0736         switch (i) {
0737         case VP_VCFG:
0738         case VP_DCFG:
0739         case VP_PAR:
0740         case VP_PDR:
0741         case VP_CCS:
0742         case VP_RSVD_0:
0743         /* case VP_VDC: */ /* why should this not be restored? */
0744         case VP_RSVD_1:
0745         case VP_CRC32:
0746             /* don't restore these registers */
0747             break;
0748 
0749         default:
0750             write_vp(par, i, par->vp[i]);
0751         }
0752     }
0753 
0754     /* restore video processor palette */
0755     write_vp(par, VP_PAR, 0);
0756     for (i = 0; i < ARRAY_SIZE(par->vp_pal); i++)
0757         write_vp(par, VP_PDR, par->vp_pal[i]);
0758 
0759     /* restore video coeff ram */
0760     memcpy(par->vp_regs + VP_VCR, par->vp_coeff, sizeof(par->vp_coeff));
0761 }
0762 
0763 static void lx_restore_regs(struct lxfb_par *par)
0764 {
0765     int i;
0766 
0767     lx_set_dotpll((u32) (par->msr.dotpll >> 32));
0768     lx_restore_gfx_proc(par);
0769     lx_restore_display_ctlr(par);
0770     lx_restore_video_proc(par);
0771 
0772     /* Flat Panel */
0773     for (i = 0; i < ARRAY_SIZE(par->fp); i++) {
0774         switch (i) {
0775         case FP_PM:
0776         case FP_RSVD_0:
0777         case FP_RSVD_1:
0778         case FP_RSVD_2:
0779         case FP_RSVD_3:
0780         case FP_RSVD_4:
0781             /* don't restore these registers */
0782             break;
0783 
0784         default:
0785             write_fp(par, i, par->fp[i]);
0786         }
0787     }
0788 
0789     /* control the panel */
0790     if (par->fp[FP_PM] & FP_PM_P) {
0791         /* power on the panel if not already power{ed,ing} on */
0792         if (!(read_fp(par, FP_PM) &
0793                 (FP_PM_PANEL_ON|FP_PM_PANEL_PWR_UP)))
0794             write_fp(par, FP_PM, par->fp[FP_PM]);
0795     } else {
0796         /* power down the panel if not already power{ed,ing} down */
0797         if (!(read_fp(par, FP_PM) &
0798                 (FP_PM_PANEL_OFF|FP_PM_PANEL_PWR_DOWN)))
0799             write_fp(par, FP_PM, par->fp[FP_PM]);
0800     }
0801 
0802     /* turn everything on */
0803     write_vp(par, VP_VCFG, par->vp[VP_VCFG]);
0804     write_vp(par, VP_DCFG, par->vp[VP_DCFG]);
0805     write_dc(par, DC_DISPLAY_CFG, par->dc[DC_DISPLAY_CFG]);
0806     /* do this last; it will enable the FIFO load */
0807     write_dc(par, DC_GENERAL_CFG, par->dc[DC_GENERAL_CFG]);
0808 
0809     /* lock the door behind us */
0810     write_dc(par, DC_UNLOCK, DC_UNLOCK_LOCK);
0811 }
0812 
0813 int lx_powerdown(struct fb_info *info)
0814 {
0815     struct lxfb_par *par = info->par;
0816 
0817     if (par->powered_down)
0818         return 0;
0819 
0820     lx_save_regs(par);
0821     lx_graphics_disable(info);
0822 
0823     par->powered_down = 1;
0824     return 0;
0825 }
0826 
0827 int lx_powerup(struct fb_info *info)
0828 {
0829     struct lxfb_par *par = info->par;
0830 
0831     if (!par->powered_down)
0832         return 0;
0833 
0834     lx_restore_regs(par);
0835 
0836     par->powered_down = 0;
0837     return 0;
0838 }