aboutsummaryrefslogtreecommitdiff
path: root/drivers/video/geode
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video/geode')
-rw-r--r--drivers/video/geode/Kconfig20
-rw-r--r--drivers/video/geode/Makefile2
-rw-r--r--drivers/video/geode/display_gx.c125
-rw-r--r--drivers/video/geode/display_gx.h101
-rw-r--r--drivers/video/geode/gxfb.h358
-rw-r--r--drivers/video/geode/gxfb_core.c160
-rw-r--r--drivers/video/geode/lxfb.h527
-rw-r--r--drivers/video/geode/lxfb_core.c118
-rw-r--r--drivers/video/geode/lxfb_ops.c677
-rw-r--r--drivers/video/geode/suspend_gx.c267
-rw-r--r--drivers/video/geode/video_gx.c162
-rw-r--r--drivers/video/geode/video_gx.h72
12 files changed, 1857 insertions, 732 deletions
diff --git a/drivers/video/geode/Kconfig b/drivers/video/geode/Kconfig
index 7608429b394..c5d8ba4b9fc 100644
--- a/drivers/video/geode/Kconfig
+++ b/drivers/video/geode/Kconfig
@@ -38,26 +38,6 @@ config FB_GEODE_GX
If unsure, say N.
-config FB_GEODE_GX_SET_FBSIZE
- bool "Manually specify the Geode GX framebuffer size"
- depends on FB_GEODE_GX
- default n
- ---help---
- If you want to manually specify the size of your GX framebuffer,
- say Y here, otherwise say N to dynamically probe it.
-
- Say N unless you know what you are doing.
-
-config FB_GEODE_GX_FBSIZE
- hex "Size of the GX framebuffer, in bytes"
- depends on FB_GEODE_GX_SET_FBSIZE
- default "0x1600000"
- ---help---
- Specify the size of the GX framebuffer. Normally, you will
- want this to be MB aligned. Common values are 0x80000 (8MB)
- and 0x1600000 (16MB). Don't change this unless you know what
- you are doing
-
config FB_GEODE_GX1
tristate "AMD Geode GX1 framebuffer support (EXPERIMENTAL)"
depends on FB && FB_GEODE && EXPERIMENTAL
diff --git a/drivers/video/geode/Makefile b/drivers/video/geode/Makefile
index 957304b45fb..5c98da12688 100644
--- a/drivers/video/geode/Makefile
+++ b/drivers/video/geode/Makefile
@@ -5,5 +5,5 @@ obj-$(CONFIG_FB_GEODE_GX) += gxfb.o
obj-$(CONFIG_FB_GEODE_LX) += lxfb.o
gx1fb-objs := gx1fb_core.o display_gx1.o video_cs5530.o
-gxfb-objs := gxfb_core.o display_gx.o video_gx.o
+gxfb-objs := gxfb_core.o display_gx.o video_gx.o suspend_gx.o
lxfb-objs := lxfb_core.o lxfb_ops.o
diff --git a/drivers/video/geode/display_gx.c b/drivers/video/geode/display_gx.c
index 0f16e4bffc6..e759895bf3d 100644
--- a/drivers/video/geode/display_gx.c
+++ b/drivers/video/geode/display_gx.c
@@ -17,31 +17,40 @@
#include <asm/io.h>
#include <asm/div64.h>
#include <asm/delay.h>
+#include <asm/geode.h>
-#include "geodefb.h"
-#include "display_gx.h"
+#include "gxfb.h"
-#ifdef CONFIG_FB_GEODE_GX_SET_FBSIZE
-unsigned int gx_frame_buffer_size(void)
-{
- return CONFIG_FB_GEODE_GX_FBSIZE;
-}
-#else
unsigned int gx_frame_buffer_size(void)
{
unsigned int val;
- /* FB size is reported by a virtual register */
+ if (!geode_has_vsa2()) {
+ uint32_t hi, lo;
+
+ /* The number of pages is (PMAX - PMIN)+1 */
+ rdmsr(MSR_GLIU_P2D_RO0, lo, hi);
+
+ /* PMAX */
+ val = ((hi & 0xff) << 12) | ((lo & 0xfff00000) >> 20);
+ /* PMIN */
+ val -= (lo & 0x000fffff);
+ val += 1;
+
+ /* The page size is 4k */
+ return (val << 12);
+ }
+
+ /* FB size can be obtained from the VSA II */
/* Virtual register class = 0x02 */
/* VG_MEM_SIZE(512Kb units) = 0x00 */
- outw(0xFC53, 0xAC1C);
- outw(0x0200, 0xAC1C);
+ outw(VSA_VR_UNLOCK, VSA_VRC_INDEX);
+ outw(VSA_VR_MEM_SIZE, VSA_VRC_INDEX);
- val = (unsigned int)(inw(0xAC1E)) & 0xFFl;
+ val = (unsigned int)(inw(VSA_VRC_DATA)) & 0xFFl;
return (val << 19);
}
-#endif
int gx_line_delta(int xres, int bpp)
{
@@ -49,75 +58,76 @@ int gx_line_delta(int xres, int bpp)
return (xres * (bpp >> 3) + 7) & ~0x7;
}
-static void gx_set_mode(struct fb_info *info)
+void gx_set_mode(struct fb_info *info)
{
- struct geodefb_par *par = info->par;
+ struct gxfb_par *par = info->par;
u32 gcfg, dcfg;
int hactive, hblankstart, hsyncstart, hsyncend, hblankend, htotal;
int vactive, vblankstart, vsyncstart, vsyncend, vblankend, vtotal;
/* Unlock the display controller registers. */
- readl(par->dc_regs + DC_UNLOCK);
- writel(DC_UNLOCK_CODE, par->dc_regs + DC_UNLOCK);
+ write_dc(par, DC_UNLOCK, DC_UNLOCK_UNLOCK);
- gcfg = readl(par->dc_regs + DC_GENERAL_CFG);
- dcfg = readl(par->dc_regs + DC_DISPLAY_CFG);
+ gcfg = read_dc(par, DC_GENERAL_CFG);
+ dcfg = read_dc(par, DC_DISPLAY_CFG);
/* Disable the timing generator. */
- dcfg &= ~(DC_DCFG_TGEN);
- writel(dcfg, par->dc_regs + DC_DISPLAY_CFG);
+ dcfg &= ~DC_DISPLAY_CFG_TGEN;
+ write_dc(par, DC_DISPLAY_CFG, dcfg);
/* Wait for pending memory requests before disabling the FIFO load. */
udelay(100);
/* Disable FIFO load and compression. */
- gcfg &= ~(DC_GCFG_DFLE | DC_GCFG_CMPE | DC_GCFG_DECE);
- writel(gcfg, par->dc_regs + DC_GENERAL_CFG);
+ gcfg &= ~(DC_GENERAL_CFG_DFLE | DC_GENERAL_CFG_CMPE |
+ DC_GENERAL_CFG_DECE);
+ write_dc(par, DC_GENERAL_CFG, gcfg);
/* Setup DCLK and its divisor. */
- par->vid_ops->set_dclk(info);
+ gx_set_dclk_frequency(info);
/*
* Setup new mode.
*/
/* Clear all unused feature bits. */
- gcfg &= DC_GCFG_YUVM | DC_GCFG_VDSE;
+ gcfg &= DC_GENERAL_CFG_YUVM | DC_GENERAL_CFG_VDSE;
dcfg = 0;
/* Set FIFO priority (default 6/5) and enable. */
/* FIXME: increase fifo priority for 1280x1024 and higher modes? */
- gcfg |= (6 << DC_GCFG_DFHPEL_POS) | (5 << DC_GCFG_DFHPSL_POS) | DC_GCFG_DFLE;
+ gcfg |= (6 << DC_GENERAL_CFG_DFHPEL_SHIFT) |
+ (5 << DC_GENERAL_CFG_DFHPSL_SHIFT) | DC_GENERAL_CFG_DFLE;
/* Framebuffer start offset. */
- writel(0, par->dc_regs + DC_FB_ST_OFFSET);
+ write_dc(par, DC_FB_ST_OFFSET, 0);
/* Line delta and line buffer length. */
- writel(info->fix.line_length >> 3, par->dc_regs + DC_GFX_PITCH);
- writel(((info->var.xres * info->var.bits_per_pixel/8) >> 3) + 2,
- par->dc_regs + DC_LINE_SIZE);
+ write_dc(par, DC_GFX_PITCH, info->fix.line_length >> 3);
+ write_dc(par, DC_LINE_SIZE,
+ ((info->var.xres * info->var.bits_per_pixel/8) >> 3) + 2);
/* Enable graphics and video data and unmask address lines. */
- dcfg |= DC_DCFG_GDEN | DC_DCFG_VDEN | DC_DCFG_A20M | DC_DCFG_A18M;
+ dcfg |= DC_DISPLAY_CFG_GDEN | DC_DISPLAY_CFG_VDEN |
+ DC_DISPLAY_CFG_A20M | DC_DISPLAY_CFG_A18M;
/* Set pixel format. */
switch (info->var.bits_per_pixel) {
case 8:
- dcfg |= DC_DCFG_DISP_MODE_8BPP;
+ dcfg |= DC_DISPLAY_CFG_DISP_MODE_8BPP;
break;
case 16:
- dcfg |= DC_DCFG_DISP_MODE_16BPP;
- dcfg |= DC_DCFG_16BPP_MODE_565;
+ dcfg |= DC_DISPLAY_CFG_DISP_MODE_16BPP;
break;
case 32:
- dcfg |= DC_DCFG_DISP_MODE_24BPP;
- dcfg |= DC_DCFG_PALB;
+ dcfg |= DC_DISPLAY_CFG_DISP_MODE_24BPP;
+ dcfg |= DC_DISPLAY_CFG_PALB;
break;
}
/* Enable timing generator. */
- dcfg |= DC_DCFG_TGEN;
+ dcfg |= DC_DISPLAY_CFG_TGEN;
/* Horizontal and vertical timings. */
hactive = info->var.xres;
@@ -134,28 +144,34 @@ static void gx_set_mode(struct fb_info *info)
vblankend = vsyncend + info->var.upper_margin;
vtotal = vblankend;
- writel((hactive - 1) | ((htotal - 1) << 16), par->dc_regs + DC_H_ACTIVE_TIMING);
- writel((hblankstart - 1) | ((hblankend - 1) << 16), par->dc_regs + DC_H_BLANK_TIMING);
- writel((hsyncstart - 1) | ((hsyncend - 1) << 16), par->dc_regs + DC_H_SYNC_TIMING);
+ write_dc(par, DC_H_ACTIVE_TIMING, (hactive - 1) |
+ ((htotal - 1) << 16));
+ write_dc(par, DC_H_BLANK_TIMING, (hblankstart - 1) |
+ ((hblankend - 1) << 16));
+ write_dc(par, DC_H_SYNC_TIMING, (hsyncstart - 1) |
+ ((hsyncend - 1) << 16));
- writel((vactive - 1) | ((vtotal - 1) << 16), par->dc_regs + DC_V_ACTIVE_TIMING);
- writel((vblankstart - 1) | ((vblankend - 1) << 16), par->dc_regs + DC_V_BLANK_TIMING);
- writel((vsyncstart - 1) | ((vsyncend - 1) << 16), par->dc_regs + DC_V_SYNC_TIMING);
+ write_dc(par, DC_V_ACTIVE_TIMING, (vactive - 1) |
+ ((vtotal - 1) << 16));
+ write_dc(par, DC_V_BLANK_TIMING, (vblankstart - 1) |
+ ((vblankend - 1) << 16));
+ write_dc(par, DC_V_SYNC_TIMING, (vsyncstart - 1) |
+ ((vsyncend - 1) << 16));
/* Write final register values. */
- writel(dcfg, par->dc_regs + DC_DISPLAY_CFG);
- writel(gcfg, par->dc_regs + DC_GENERAL_CFG);
+ write_dc(par, DC_DISPLAY_CFG, dcfg);
+ write_dc(par, DC_GENERAL_CFG, gcfg);
- par->vid_ops->configure_display(info);
+ gx_configure_display(info);
/* Relock display controller registers */
- writel(0, par->dc_regs + DC_UNLOCK);
+ write_dc(par, DC_UNLOCK, DC_UNLOCK_LOCK);
}
-static void gx_set_hw_palette_reg(struct fb_info *info, unsigned regno,
- unsigned red, unsigned green, unsigned blue)
+void gx_set_hw_palette_reg(struct fb_info *info, unsigned regno,
+ unsigned red, unsigned green, unsigned blue)
{
- struct geodefb_par *par = info->par;
+ struct gxfb_par *par = info->par;
int val;
/* Hardware palette is in RGB 8-8-8 format. */
@@ -163,11 +179,6 @@ static void gx_set_hw_palette_reg(struct fb_info *info, unsigned regno,
val |= (green) & 0x00ff00;
val |= (blue >> 8) & 0x0000ff;
- writel(regno, par->dc_regs + DC_PAL_ADDRESS);
- writel(val, par->dc_regs + DC_PAL_DATA);
+ write_dc(par, DC_PAL_ADDRESS, regno);
+ write_dc(par, DC_PAL_DATA, val);
}
-
-struct geode_dc_ops gx_dc_ops = {
- .set_mode = gx_set_mode,
- .set_palette_reg = gx_set_hw_palette_reg,
-};
diff --git a/drivers/video/geode/display_gx.h b/drivers/video/geode/display_gx.h
deleted file mode 100644
index 0af33f329e8..00000000000
--- a/drivers/video/geode/display_gx.h
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Geode GX display controller
- *
- * Copyright (C) 2006 Arcom Control Systems Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-#ifndef __DISPLAY_GX_H__
-#define __DISPLAY_GX_H__
-
-unsigned int gx_frame_buffer_size(void);
-int gx_line_delta(int xres, int bpp);
-
-extern struct geode_dc_ops gx_dc_ops;
-
-/* MSR that tells us if a TFT or CRT is attached */
-#define GLD_MSR_CONFIG 0xC0002001
-#define GLD_MSR_CONFIG_DM_FP 0x40
-
-/* Display controller registers */
-
-#define DC_UNLOCK 0x00
-# define DC_UNLOCK_CODE 0x00004758
-
-#define DC_GENERAL_CFG 0x04
-# define DC_GCFG_DFLE 0x00000001
-# define DC_GCFG_CURE 0x00000002
-# define DC_GCFG_ICNE 0x00000004
-# define DC_GCFG_VIDE 0x00000008
-# define DC_GCFG_CMPE 0x00000020
-# define DC_GCFG_DECE 0x00000040
-# define DC_GCFG_VGAE 0x00000080
-# define DC_GCFG_DFHPSL_MASK 0x00000F00
-# define DC_GCFG_DFHPSL_POS 8
-# define DC_GCFG_DFHPEL_MASK 0x0000F000
-# define DC_GCFG_DFHPEL_POS 12
-# define DC_GCFG_STFM 0x00010000
-# define DC_GCFG_FDTY 0x00020000
-# define DC_GCFG_VGAFT 0x00040000
-# define DC_GCFG_VDSE 0x00080000
-# define DC_GCFG_YUVM 0x00100000
-# define DC_GCFG_VFSL 0x00800000
-# define DC_GCFG_SIGE 0x01000000
-# define DC_GCFG_SGRE 0x02000000
-# define DC_GCFG_SGFR 0x04000000
-# define DC_GCFG_CRC_MODE 0x08000000
-# define DC_GCFG_DIAG 0x10000000
-# define DC_GCFG_CFRW 0x20000000
-
-#define DC_DISPLAY_CFG 0x08
-# define DC_DCFG_TGEN 0x00000001
-# define DC_DCFG_GDEN 0x00000008
-# define DC_DCFG_VDEN 0x00000010
-# define DC_DCFG_TRUP 0x00000040
-# define DC_DCFG_DISP_MODE_MASK 0x00000300
-# define DC_DCFG_DISP_MODE_8BPP 0x00000000
-# define DC_DCFG_DISP_MODE_16BPP 0x00000100
-# define DC_DCFG_DISP_MODE_24BPP 0x00000200
-# define DC_DCFG_16BPP_MODE_MASK 0x00000c00
-# define DC_DCFG_16BPP_MODE_565 0x00000000
-# define DC_DCFG_16BPP_MODE_555 0x00000100
-# define DC_DCFG_16BPP_MODE_444 0x00000200
-# define DC_DCFG_DCEN 0x00080000
-# define DC_DCFG_PALB 0x02000000
-# define DC_DCFG_FRLK 0x04000000
-# define DC_DCFG_VISL 0x08000000
-# define DC_DCFG_FRSL 0x20000000
-# define DC_DCFG_A18M 0x40000000
-# define DC_DCFG_A20M 0x80000000
-
-#define DC_FB_ST_OFFSET 0x10
-
-#define DC_LINE_SIZE 0x30
-# define DC_LINE_SIZE_FB_LINE_SIZE_MASK 0x000007ff
-# define DC_LINE_SIZE_FB_LINE_SIZE_POS 0
-# define DC_LINE_SIZE_CB_LINE_SIZE_MASK 0x007f0000
-# define DC_LINE_SIZE_CB_LINE_SIZE_POS 16
-# define DC_LINE_SIZE_VID_LINE_SIZE_MASK 0xff000000
-# define DC_LINE_SIZE_VID_LINE_SIZE_POS 24
-
-#define DC_GFX_PITCH 0x34
-# define DC_GFX_PITCH_FB_PITCH_MASK 0x0000ffff
-# define DC_GFX_PITCH_FB_PITCH_POS 0
-# define DC_GFX_PITCH_CB_PITCH_MASK 0xffff0000
-# define DC_GFX_PITCH_CB_PITCH_POS 16
-
-#define DC_H_ACTIVE_TIMING 0x40
-#define DC_H_BLANK_TIMING 0x44
-#define DC_H_SYNC_TIMING 0x48
-#define DC_V_ACTIVE_TIMING 0x50
-#define DC_V_BLANK_TIMING 0x54
-#define DC_V_SYNC_TIMING 0x58
-
-#define DC_PAL_ADDRESS 0x70
-#define DC_PAL_DATA 0x74
-
-#define DC_GLIU0_MEM_OFFSET 0x84
-#endif /* !__DISPLAY_GX1_H__ */
diff --git a/drivers/video/geode/gxfb.h b/drivers/video/geode/gxfb.h
new file mode 100644
index 00000000000..16a96f8fd8c
--- /dev/null
+++ b/drivers/video/geode/gxfb.h
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2008 Andres Salomon <dilinger@debian.org>
+ *
+ * Geode GX2 header information
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef _GXFB_H_
+#define _GXFB_H_
+
+#include <linux/io.h>
+
+#define GP_REG_COUNT (0x50 / 4)
+#define DC_REG_COUNT (0x90 / 4)
+#define VP_REG_COUNT (0x138 / 8)
+#define FP_REG_COUNT (0x68 / 8)
+
+#define DC_PAL_COUNT 0x104
+
+struct gxfb_par {
+ int enable_crt;
+ void __iomem *dc_regs;
+ void __iomem *vid_regs;
+ void __iomem *gp_regs;
+#ifdef CONFIG_PM
+ int powered_down;
+
+ /* register state, for power management functionality */
+ struct {
+ uint64_t padsel;
+ uint64_t dotpll;
+ } msr;
+
+ uint32_t gp[GP_REG_COUNT];
+ uint32_t dc[DC_REG_COUNT];
+ uint64_t vp[VP_REG_COUNT];
+ uint64_t fp[FP_REG_COUNT];
+
+ uint32_t pal[DC_PAL_COUNT];
+#endif
+};
+
+unsigned int gx_frame_buffer_size(void);
+int gx_line_delta(int xres, int bpp);
+void gx_set_mode(struct fb_info *info);
+void gx_set_hw_palette_reg(struct fb_info *info, unsigned regno,
+ unsigned red, unsigned green, unsigned blue);
+
+void gx_set_dclk_frequency(struct fb_info *info);
+void gx_configure_display(struct fb_info *info);
+int gx_blank_display(struct fb_info *info, int blank_mode);
+
+#ifdef CONFIG_PM
+int gx_powerdown(struct fb_info *info);
+int gx_powerup(struct fb_info *info);
+#endif
+
+
+/* Graphics Processor registers (table 6-23 from the data book) */
+enum gp_registers {
+ GP_DST_OFFSET = 0,
+ GP_SRC_OFFSET,
+ GP_STRIDE,
+ GP_WID_HEIGHT,
+
+ GP_SRC_COLOR_FG,
+ GP_SRC_COLOR_BG,
+ GP_PAT_COLOR_0,
+ GP_PAT_COLOR_1,
+
+ GP_PAT_COLOR_2,
+ GP_PAT_COLOR_3,
+ GP_PAT_COLOR_4,
+ GP_PAT_COLOR_5,
+
+ GP_PAT_DATA_0,
+ GP_PAT_DATA_1,
+ GP_RASTER_MODE,
+ GP_VECTOR_MODE,
+
+ GP_BLT_MODE,
+ GP_BLT_STATUS,
+ GP_HST_SRC,
+ GP_BASE_OFFSET, /* 0x4c */
+};
+
+#define GP_BLT_STATUS_BLT_PENDING (1 << 2)
+#define GP_BLT_STATUS_BLT_BUSY (1 << 0)
+
+
+/* Display Controller registers (table 6-38 from the data book) */
+enum dc_registers {
+ DC_UNLOCK = 0,
+ DC_GENERAL_CFG,
+ DC_DISPLAY_CFG,
+ DC_RSVD_0,
+
+ DC_FB_ST_OFFSET,
+ DC_CB_ST_OFFSET,
+ DC_CURS_ST_OFFSET,
+ DC_ICON_ST_OFFSET,
+
+ DC_VID_Y_ST_OFFSET,
+ DC_VID_U_ST_OFFSET,
+ DC_VID_V_ST_OFFSET,
+ DC_RSVD_1,
+
+ DC_LINE_SIZE,
+ DC_GFX_PITCH,
+ DC_VID_YUV_PITCH,
+ DC_RSVD_2,
+
+ DC_H_ACTIVE_TIMING,
+ DC_H_BLANK_TIMING,
+ DC_H_SYNC_TIMING,
+ DC_RSVD_3,
+
+ DC_V_ACTIVE_TIMING,
+ DC_V_BLANK_TIMING,
+ DC_V_SYNC_TIMING,
+ DC_RSVD_4,
+
+ DC_CURSOR_X,
+ DC_CURSOR_Y,
+ DC_ICON_X,
+ DC_LINE_CNT,
+
+ DC_PAL_ADDRESS,
+ DC_PAL_DATA,
+ DC_DFIFO_DIAG,
+ DC_CFIFO_DIAG,
+
+ DC_VID_DS_DELTA,
+ DC_GLIU0_MEM_OFFSET,
+ DC_RSVD_5,
+ DC_DV_ACC, /* 0x8c */
+};
+
+#define DC_UNLOCK_LOCK 0x00000000
+#define DC_UNLOCK_UNLOCK 0x00004758 /* magic value */
+
+#define DC_GENERAL_CFG_YUVM (1 << 20)
+#define DC_GENERAL_CFG_VDSE (1 << 19)
+#define DC_GENERAL_CFG_DFHPEL_SHIFT 12
+#define DC_GENERAL_CFG_DFHPSL_SHIFT 8
+#define DC_GENERAL_CFG_DECE (1 << 6)
+#define DC_GENERAL_CFG_CMPE (1 << 5)
+#define DC_GENERAL_CFG_VIDE (1 << 3)
+#define DC_GENERAL_CFG_ICNE (1 << 2)
+#define DC_GENERAL_CFG_CURE (1 << 1)
+#define DC_GENERAL_CFG_DFLE (1 << 0)
+
+#define DC_DISPLAY_CFG_A20M (1 << 31)
+#define DC_DISPLAY_CFG_A18M (1 << 30)
+#define DC_DISPLAY_CFG_PALB (1 << 25)
+#define DC_DISPLAY_CFG_DISP_MODE_24BPP (1 << 9)
+#define DC_DISPLAY_CFG_DISP_MODE_16BPP (1 << 8)
+#define DC_DISPLAY_CFG_DISP_MODE_8BPP (0)
+#define DC_DISPLAY_CFG_VDEN (1 << 4)
+#define DC_DISPLAY_CFG_GDEN (1 << 3)
+#define DC_DISPLAY_CFG_TGEN (1 << 0)
+
+
+/*
+ * Video Processor registers (table 6-54).
+ * There is space for 64 bit values, but we never use more than the
+ * lower 32 bits. The actual register save/restore code only bothers
+ * to restore those 32 bits.
+ */
+enum vp_registers {
+ VP_VCFG = 0,
+ VP_DCFG,
+
+ VP_VX,
+ VP_VY,
+
+ VP_VS,
+ VP_VCK,
+
+ VP_VCM,
+ VP_GAR,
+
+ VP_GDR,
+ VP_RSVD_0,
+
+ VP_MISC,
+ VP_CCS,
+
+ VP_RSVD_1,
+ VP_RSVD_2,
+
+ VP_RSVD_3,
+ VP_VDC,
+
+ VP_VCO,
+ VP_CRC,
+
+ VP_CRC32,
+ VP_VDE,
+
+ VP_CCK,
+ VP_CCM,
+
+ VP_CC1,
+ VP_CC2,
+
+ VP_A1X,
+ VP_A1Y,
+
+ VP_A1C,
+ VP_A1T,
+
+ VP_A2X,
+ VP_A2Y,
+
+ VP_A2C,
+ VP_A2T,
+
+ VP_A3X,
+ VP_A3Y,
+
+ VP_A3C,
+ VP_A3T,
+
+ VP_VRR,
+ VP_AWT,
+
+ VP_VTM, /* 0x130 */
+};
+
+#define VP_VCFG_VID_EN (1 << 0)
+
+#define VP_DCFG_DAC_VREF (1 << 26)
+#define VP_DCFG_GV_GAM (1 << 21)
+#define VP_DCFG_VG_CK (1 << 20)
+#define VP_DCFG_CRT_SYNC_SKW_DEFAULT (1 << 16)
+#define VP_DCFG_CRT_SYNC_SKW ((1 << 14) | (1 << 15) | (1 << 16))
+#define VP_DCFG_CRT_VSYNC_POL (1 << 9)
+#define VP_DCFG_CRT_HSYNC_POL (1 << 8)
+#define VP_DCFG_FP_DATA_EN (1 << 7) /* undocumented */
+#define VP_DCFG_FP_PWR_EN (1 << 6) /* undocumented */
+#define VP_DCFG_DAC_BL_EN (1 << 3)
+#define VP_DCFG_VSYNC_EN (1 << 2)
+#define VP_DCFG_HSYNC_EN (1 << 1)
+#define VP_DCFG_CRT_EN (1 << 0)
+
+#define VP_MISC_GAM_EN (1 << 0)
+#define VP_MISC_DACPWRDN (1 << 10)
+#define VP_MISC_APWRDN (1 << 11)
+
+
+/*
+ * Flat Panel registers (table 6-55).
+ * Also 64 bit registers; see above note about 32-bit handling.
+ */
+
+/* we're actually in the VP register space, starting at address 0x400 */
+#define VP_FP_START 0x400
+
+enum fp_registers {
+ FP_PT1 = 0,
+ FP_PT2,
+
+ FP_PM,
+ FP_DFC,
+
+ FP_BLFSR,
+ FP_RLFSR,
+
+ FP_FMI,
+ FP_FMD,
+
+ FP_RSVD_0,
+ FP_DCA,
+
+ FP_DMD,
+ FP_CRC,
+
+ FP_FBB, /* 0x460 */
+};
+
+#define FP_PT1_VSIZE_SHIFT 16 /* undocumented? */
+#define FP_PT1_VSIZE_MASK 0x7FF0000 /* undocumented? */
+
+#define FP_PT2_HSP (1 << 22)
+#define FP_PT2_VSP (1 << 23)
+
+#define FP_PM_P (1 << 24) /* panel power on */
+#define FP_PM_PANEL_PWR_UP (1 << 3) /* r/o */
+#define FP_PM_PANEL_PWR_DOWN (1 << 2) /* r/o */
+#define FP_PM_PANEL_OFF (1 << 1) /* r/o */
+#define FP_PM_PANEL_ON (1 << 0) /* r/o */
+
+#define FP_DFC_NFI ((1 << 4) | (1 << 5) | (1 << 6))
+
+
+/* register access functions */
+
+static inline uint32_t read_gp(struct gxfb_par *par, int reg)
+{
+ return readl(par->gp_regs + 4*reg);
+}
+
+static inline void write_gp(struct gxfb_par *par, int reg, uint32_t val)
+{
+ writel(val, par->gp_regs + 4*reg);
+}
+
+static inline uint32_t read_dc(struct gxfb_par *par, int reg)
+{
+ return readl(par->dc_regs + 4*reg);
+}
+
+static inline void write_dc(struct gxfb_par *par, int reg, uint32_t val)
+{
+ writel(val, par->dc_regs + 4*reg);
+}
+
+static inline uint32_t read_vp(struct gxfb_par *par, int reg)
+{
+ return readl(par->vid_regs + 8*reg);
+}
+
+static inline void write_vp(struct gxfb_par *par, int reg, uint32_t val)
+{
+ writel(val, par->vid_regs + 8*reg);
+}
+
+static inline uint32_t read_fp(struct gxfb_par *par, int reg)
+{
+ return readl(par->vid_regs + 8*reg + VP_FP_START);
+}
+
+static inline void write_fp(struct gxfb_par *par, int reg, uint32_t val)
+{
+ writel(val, par->vid_regs + 8*reg + VP_FP_START);
+}
+
+
+/* MSRs are defined in asm/geode.h; their bitfields are here */
+
+#define MSR_GLCP_SYS_RSTPLL_DOTPOSTDIV3 (1 << 3)
+#define MSR_GLCP_SYS_RSTPLL_DOTPREMULT2 (1 << 2)
+#define MSR_GLCP_SYS_RSTPLL_DOTPREDIV2 (1 << 1)
+
+#define MSR_GLCP_DOTPLL_LOCK (1 << 25) /* r/o */
+#define MSR_GLCP_DOTPLL_BYPASS (1 << 15)
+#define MSR_GLCP_DOTPLL_DOTRESET (1 << 0)
+
+#define MSR_GX_MSR_PADSEL_MASK 0x3FFFFFFF /* undocumented? */
+#define MSR_GX_MSR_PADSEL_TFT 0x1FFFFFFF /* undocumented? */
+
+#define MSR_GX_GLD_MSR_CONFIG_FP (1 << 3)
+
+#endif
diff --git a/drivers/video/geode/gxfb_core.c b/drivers/video/geode/gxfb_core.c
index cf841efa229..de2b8f9876a 100644
--- a/drivers/video/geode/gxfb_core.c
+++ b/drivers/video/geode/gxfb_core.c
@@ -28,17 +28,20 @@
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/fb.h>
+#include <linux/console.h>
+#include <linux/suspend.h>
#include <linux/init.h>
#include <linux/pci.h>
+#include <asm/geode.h>
-#include "geodefb.h"
-#include "display_gx.h"
-#include "video_gx.h"
+#include "gxfb.h"
static char *mode_option;
+static int vram;
+static int vt_switch;
/* Modes relevant to the GX (taken from modedb.c) */
-static const struct fb_videomode gx_modedb[] __initdata = {
+static struct fb_videomode gx_modedb[] __initdata = {
/* 640x480-60 VESA */
{ NULL, 60, 640, 480, 39682, 48, 16, 33, 10, 96, 2,
0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
@@ -105,6 +108,35 @@ static const struct fb_videomode gx_modedb[] __initdata = {
FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
};
+#ifdef CONFIG_OLPC
+#include <asm/olpc.h>
+
+static struct fb_videomode gx_dcon_modedb[] __initdata = {
+ /* The only mode the DCON has is 1200x900 */
+ { NULL, 50, 1200, 900, 17460, 24, 8, 4, 5, 8, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED, 0 }
+};
+
+static void __init get_modedb(struct fb_videomode **modedb, unsigned int *size)
+{
+ if (olpc_has_dcon()) {
+ *modedb = (struct fb_videomode *) gx_dcon_modedb;
+ *size = ARRAY_SIZE(gx_dcon_modedb);
+ } else {
+ *modedb = (struct fb_videomode *) gx_modedb;
+ *size = ARRAY_SIZE(gx_modedb);
+ }
+}
+
+#else
+static void __init get_modedb(struct fb_videomode **modedb, unsigned int *size)
+{
+ *modedb = (struct fb_videomode *) gx_modedb;
+ *size = ARRAY_SIZE(gx_modedb);
+}
+#endif
+
static int gxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
if (var->xres > 1600 || var->yres > 1200)
@@ -139,8 +171,6 @@ static int gxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
static int gxfb_set_par(struct fb_info *info)
{
- struct geodefb_par *par = info->par;
-
if (info->var.bits_per_pixel > 8) {
info->fix.visual = FB_VISUAL_TRUECOLOR;
fb_dealloc_cmap(&info->cmap);
@@ -151,7 +181,7 @@ static int gxfb_set_par(struct fb_info *info)
info->fix.line_length = gx_line_delta(info->var.xres, info->var.bits_per_pixel);
- par->dc_ops->set_mode(info);
+ gx_set_mode(info);
return 0;
}
@@ -167,8 +197,6 @@ static int gxfb_setcolreg(unsigned regno, unsigned red, unsigned green,
unsigned blue, unsigned transp,
struct fb_info *info)
{
- struct geodefb_par *par = info->par;
-
if (info->var.grayscale) {
/* grayscale = 0.30*R + 0.59*G + 0.11*B */
red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
@@ -191,7 +219,7 @@ static int gxfb_setcolreg(unsigned regno, unsigned red, unsigned green,
if (regno >= 256)
return -EINVAL;
- par->dc_ops->set_palette_reg(info, regno, red, green, blue);
+ gx_set_hw_palette_reg(info, regno, red, green, blue);
}
return 0;
@@ -199,15 +227,12 @@ static int gxfb_setcolreg(unsigned regno, unsigned red, unsigned green,
static int gxfb_blank(int blank_mode, struct fb_info *info)
{
- struct geodefb_par *par = info->par;
-
- return par->vid_ops->blank_display(info, blank_mode);
+ return gx_blank_display(info, blank_mode);
}
static int __init gxfb_map_video_memory(struct fb_info *info, struct pci_dev *dev)
{
- struct geodefb_par *par = info->par;
- int fb_len;
+ struct gxfb_par *par = info->par;
int ret;
ret = pci_enable_device(dev);
@@ -229,24 +254,31 @@ static int __init gxfb_map_video_memory(struct fb_info *info, struct pci_dev *de
if (!par->dc_regs)
return -ENOMEM;
- ret = pci_request_region(dev, 0, "gxfb (framebuffer)");
+ ret = pci_request_region(dev, 1, "gxfb (graphics processor)");
if (ret < 0)
return ret;
- if ((fb_len = gx_frame_buffer_size()) < 0)
+ par->gp_regs = ioremap(pci_resource_start(dev, 1),
+ pci_resource_len(dev, 1));
+
+ if (!par->gp_regs)
return -ENOMEM;
+
+ ret = pci_request_region(dev, 0, "gxfb (framebuffer)");
+ if (ret < 0)
+ return ret;
+
info->fix.smem_start = pci_resource_start(dev, 0);
- info->fix.smem_len = fb_len;
+ info->fix.smem_len = vram ? vram : gx_frame_buffer_size();
info->screen_base = ioremap(info->fix.smem_start, info->fix.smem_len);
if (!info->screen_base)
return -ENOMEM;
- /* Set the 16MB aligned base address of the graphics memory region
+ /* Set the 16MiB aligned base address of the graphics memory region
* in the display controller */
- writel(info->fix.smem_start & 0xFF000000,
- par->dc_regs + DC_GLIU0_MEM_OFFSET);
+ write_dc(par, DC_GLIU0_MEM_OFFSET, info->fix.smem_start & 0xFF000000);
- dev_info(&dev->dev, "%d Kibyte of video memory at 0x%lx\n",
+ dev_info(&dev->dev, "%d KiB of video memory at 0x%lx\n",
info->fix.smem_len / 1024, info->fix.smem_start);
return 0;
@@ -266,11 +298,12 @@ static struct fb_ops gxfb_ops = {
static struct fb_info * __init gxfb_init_fbinfo(struct device *dev)
{
- struct geodefb_par *par;
+ struct gxfb_par *par;
struct fb_info *info;
/* Alloc enough space for the pseudo palette. */
- info = framebuffer_alloc(sizeof(struct geodefb_par) + sizeof(u32) * 16, dev);
+ info = framebuffer_alloc(sizeof(struct gxfb_par) + sizeof(u32) * 16,
+ dev);
if (!info)
return NULL;
@@ -296,29 +329,64 @@ static struct fb_info * __init gxfb_init_fbinfo(struct device *dev)
info->flags = FBINFO_DEFAULT;
info->node = -1;
- info->pseudo_palette = (void *)par + sizeof(struct geodefb_par);
+ info->pseudo_palette = (void *)par + sizeof(struct gxfb_par);
info->var.grayscale = 0;
return info;
}
+#ifdef CONFIG_PM
+static int gxfb_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct fb_info *info = pci_get_drvdata(pdev);
+
+ if (state.event == PM_EVENT_SUSPEND) {
+ acquire_console_sem();
+ gx_powerdown(info);
+ fb_set_suspend(info, 1);
+ release_console_sem();
+ }
+
+ /* there's no point in setting PCI states; we emulate PCI, so
+ * we don't end up getting power savings anyways */
+
+ return 0;
+}
+
+static int gxfb_resume(struct pci_dev *pdev)
+{
+ struct fb_info *info = pci_get_drvdata(pdev);
+ int ret;
+
+ acquire_console_sem();
+ ret = gx_powerup(info);
+ if (ret) {
+ printk(KERN_ERR "gxfb: power up failed!\n");
+ return ret;
+ }
+
+ fb_set_suspend(info, 0);
+ release_console_sem();
+ return 0;
+}
+#endif
+
static int __init gxfb_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
- struct geodefb_par *par;
+ struct gxfb_par *par;
struct fb_info *info;
int ret;
unsigned long val;
+ struct fb_videomode *modedb_ptr;
+ unsigned int modedb_size;
+
info = gxfb_init_fbinfo(&pdev->dev);
if (!info)
return -ENOMEM;
par = info->par;
- /* GX display controller and GX video device. */
- par->dc_ops = &gx_dc_ops;
- par->vid_ops = &gx_vid_ops;
-
if ((ret = gxfb_map_video_memory(info, pdev)) < 0) {
dev_err(&pdev->dev, "failed to map frame buffer or controller registers\n");
goto err;
@@ -326,15 +394,16 @@ static int __init gxfb_probe(struct pci_dev *pdev, const struct pci_device_id *i
/* Figure out if this is a TFT or CRT part */
- rdmsrl(GLD_MSR_CONFIG, val);
+ rdmsrl(MSR_GX_GLD_MSR_CONFIG, val);
- if ((val & GLD_MSR_CONFIG_DM_FP) == GLD_MSR_CONFIG_DM_FP)
+ if ((val & MSR_GX_GLD_MSR_CONFIG_FP) == MSR_GX_GLD_MSR_CONFIG_FP)
par->enable_crt = 0;
else
par->enable_crt = 1;
+ get_modedb(&modedb_ptr, &modedb_size);
ret = fb_find_mode(&info->var, info, mode_option,
- gx_modedb, ARRAY_SIZE(gx_modedb), NULL, 16);
+ modedb_ptr, modedb_size, NULL, 16);
if (ret == 0 || ret == 4) {
dev_err(&pdev->dev, "could not find valid video mode\n");
ret = -EINVAL;
@@ -348,6 +417,8 @@ static int __init gxfb_probe(struct pci_dev *pdev, const struct pci_device_id *i
gxfb_check_var(&info->var, info);
gxfb_set_par(info);
+ pm_set_vt_switch(vt_switch);
+
if (register_framebuffer(info) < 0) {
ret = -EINVAL;
goto err;
@@ -369,6 +440,10 @@ static int __init gxfb_probe(struct pci_dev *pdev, const struct pci_device_id *i
iounmap(par->dc_regs);
pci_release_region(pdev, 2);
}
+ if (par->gp_regs) {
+ iounmap(par->gp_regs);
+ pci_release_region(pdev, 1);
+ }
if (info)
framebuffer_release(info);
@@ -378,7 +453,7 @@ static int __init gxfb_probe(struct pci_dev *pdev, const struct pci_device_id *i
static void gxfb_remove(struct pci_dev *pdev)
{
struct fb_info *info = pci_get_drvdata(pdev);
- struct geodefb_par *par = info->par;
+ struct gxfb_par *par = info->par;
unregister_framebuffer(info);
@@ -391,15 +466,16 @@ static void gxfb_remove(struct pci_dev *pdev)
iounmap(par->dc_regs);
pci_release_region(pdev, 2);
+ iounmap(par->gp_regs);
+ pci_release_region(pdev, 1);
+
pci_set_drvdata(pdev, NULL);
framebuffer_release(info);
}
static struct pci_device_id gxfb_id_table[] = {
- { PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_GX_VIDEO,
- PCI_ANY_ID, PCI_ANY_ID, PCI_BASE_CLASS_DISPLAY << 16,
- 0xff0000, 0 },
+ { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_GX_VIDEO) },
{ 0, }
};
@@ -410,6 +486,10 @@ static struct pci_driver gxfb_driver = {
.id_table = gxfb_id_table,
.probe = gxfb_probe,
.remove = gxfb_remove,
+#ifdef CONFIG_PM
+ .suspend = gxfb_suspend,
+ .resume = gxfb_resume,
+#endif
};
#ifndef MODULE
@@ -456,5 +536,11 @@ module_exit(gxfb_cleanup);
module_param(mode_option, charp, 0);
MODULE_PARM_DESC(mode_option, "video mode (<x>x<y>[-<bpp>][@<refr>])");
+module_param(vram, int, 0);
+MODULE_PARM_DESC(vram, "video memory size");
+
+module_param(vt_switch, int, 0);
+MODULE_PARM_DESC(vt_switch, "enable VT switch during suspend/resume");
+
MODULE_DESCRIPTION("Framebuffer driver for the AMD Geode GX");
MODULE_LICENSE("GPL");
diff --git a/drivers/video/geode/lxfb.h b/drivers/video/geode/lxfb.h
index ca13c48d19b..3b9416f4ee2 100644
--- a/drivers/video/geode/lxfb.h
+++ b/drivers/video/geode/lxfb.h
@@ -3,17 +3,46 @@
#include <linux/fb.h>
+#define GP_REG_COUNT (0x7c / 4)
+#define DC_REG_COUNT (0xf0 / 4)
+#define VP_REG_COUNT (0x158 / 8)
+#define FP_REG_COUNT (0x60 / 8)
+
+#define DC_PAL_COUNT 0x104
+#define DC_HFILT_COUNT 0x100
+#define DC_VFILT_COUNT 0x100
+#define VP_COEFF_SIZE 0x1000
+
#define OUTPUT_CRT 0x01
#define OUTPUT_PANEL 0x02
struct lxfb_par {
int output;
- int panel_width;
- int panel_height;
void __iomem *gp_regs;
void __iomem *dc_regs;
- void __iomem *df_regs;
+ void __iomem *vp_regs;
+#ifdef CONFIG_PM
+ int powered_down;
+
+ /* register state, for power mgmt functionality */
+ struct {
+ uint64_t padsel;
+ uint64_t dotpll;
+ uint64_t dfglcfg;
+ uint64_t dcspare;
+ } msr;
+
+ uint32_t gp[GP_REG_COUNT];
+ uint32_t dc[DC_REG_COUNT];
+ uint64_t vp[VP_REG_COUNT];
+ uint64_t fp[FP_REG_COUNT];
+
+ uint32_t pal[DC_PAL_COUNT];
+ uint32_t hcoeff[DC_HFILT_COUNT * 2];
+ uint32_t vcoeff[DC_VFILT_COUNT];
+ uint32_t vp_coeff[VP_COEFF_SIZE / 4];
+#endif
};
static inline unsigned int lx_get_pitch(unsigned int xres, int bpp)
@@ -29,171 +58,383 @@ int lx_blank_display(struct fb_info *, int);
void lx_set_palette_reg(struct fb_info *, unsigned int, unsigned int,
unsigned int, unsigned int);
-/* MSRS */
+#ifdef CONFIG_PM
+int lx_powerdown(struct fb_info *info);
+int lx_powerup(struct fb_info *info);
+#endif
+
+
+/* Graphics Processor registers (table 6-29 from the data book) */
+enum gp_registers {
+ GP_DST_OFFSET = 0,
+ GP_SRC_OFFSET,
+ GP_STRIDE,
+ GP_WID_HEIGHT,
+
+ GP_SRC_COLOR_FG,
+ GP_SRC_COLOR_BG,
+ GP_PAT_COLOR_0,
+ GP_PAT_COLOR_1,
+
+ GP_PAT_COLOR_2,
+ GP_PAT_COLOR_3,
+ GP_PAT_COLOR_4,
+ GP_PAT_COLOR_5,
+
+ GP_PAT_DATA_0,
+ GP_PAT_DATA_1,
+ GP_RASTER_MODE,
+ GP_VECTOR_MODE,
+
+ GP_BLT_MODE,
+ GP_BLT_STATUS,
+ GP_HST_SRC,
+ GP_BASE_OFFSET,
+
+ GP_CMD_TOP,
+ GP_CMD_BOT,
+ GP_CMD_READ,
+ GP_CMD_WRITE,
+
+ GP_CH3_OFFSET,
+ GP_CH3_MODE_STR,
+ GP_CH3_WIDHI,
+ GP_CH3_HSRC,
+
+ GP_LUT_INDEX,
+ GP_LUT_DATA,
+ GP_INT_CNTRL, /* 0x78 */
+};
+
+#define GP_BLT_STATUS_CE (1 << 4) /* cmd buf empty */
+#define GP_BLT_STATUS_PB (1 << 0) /* primative busy */
+
+
+/* Display Controller registers (table 6-47 from the data book) */
+enum dc_registers {
+ DC_UNLOCK = 0,
+ DC_GENERAL_CFG,
+ DC_DISPLAY_CFG,
+ DC_ARB_CFG,
+
+ DC_FB_ST_OFFSET,
+ DC_CB_ST_OFFSET,
+ DC_CURS_ST_OFFSET,
+ DC_RSVD_0,
+
+ DC_VID_Y_ST_OFFSET,
+ DC_VID_U_ST_OFFSET,
+ DC_VID_V_ST_OFFSET,
+ DC_DV_TOP,
+
+ DC_LINE_SIZE,
+ DC_GFX_PITCH,
+ DC_VID_YUV_PITCH,
+ DC_RSVD_1,
+
+ DC_H_ACTIVE_TIMING,
+ DC_H_BLANK_TIMING,
+ DC_H_SYNC_TIMING,
+ DC_RSVD_2,
+
+ DC_V_ACTIVE_TIMING,
+ DC_V_BLANK_TIMING,
+ DC_V_SYNC_TIMING,
+ DC_FB_ACTIVE,
+
+ DC_CURSOR_X,
+ DC_CURSOR_Y,
+ DC_RSVD_3,
+ DC_LINE_CNT,
+
+ DC_PAL_ADDRESS,
+ DC_PAL_DATA,
+ DC_DFIFO_DIAG,
+ DC_CFIFO_DIAG,
+
+ DC_VID_DS_DELTA,
+ DC_GLIU0_MEM_OFFSET,
+ DC_DV_CTL,
+ DC_DV_ACCESS,
+
+ DC_GFX_SCALE,
+ DC_IRQ_FILT_CTL,
+ DC_FILT_COEFF1,
+ DC_FILT_COEFF2,
+
+ DC_VBI_EVEN_CTL,
+ DC_VBI_ODD_CTL,
+ DC_VBI_HOR,
+ DC_VBI_LN_ODD,
+
+ DC_VBI_LN_EVEN,
+ DC_VBI_PITCH,
+ DC_CLR_KEY,
+ DC_CLR_KEY_MASK,
+
+ DC_CLR_KEY_X,
+ DC_CLR_KEY_Y,
+ DC_IRQ,
+ DC_RSVD_4,
+
+ DC_RSVD_5,
+ DC_GENLK_CTL,
+ DC_VID_EVEN_Y_ST_OFFSET,
+ DC_VID_EVEN_U_ST_OFFSET,
+
+ DC_VID_EVEN_V_ST_OFFSET,
+ DC_V_ACTIVE_EVEN_TIMING,
+ DC_V_BLANK_EVEN_TIMING,
+ DC_V_SYNC_EVEN_TIMING, /* 0xec */
+};
+
+#define DC_UNLOCK_LOCK 0x00000000
+#define DC_UNLOCK_UNLOCK 0x00004758 /* magic value */
+
+#define DC_GENERAL_CFG_FDTY (1 << 17)
+#define DC_GENERAL_CFG_DFHPEL_SHIFT (12)
+#define DC_GENERAL_CFG_DFHPSL_SHIFT (8)
+#define DC_GENERAL_CFG_VGAE (1 << 7)
+#define DC_GENERAL_CFG_DECE (1 << 6)
+#define DC_GENERAL_CFG_CMPE (1 << 5)
+#define DC_GENERAL_CFG_VIDE (1 << 3)
+#define DC_GENERAL_CFG_DFLE (1 << 0)
+
+#define DC_DISPLAY_CFG_VISL (1 << 27)
+#define DC_DISPLAY_CFG_PALB (1 << 25)
+#define DC_DISPLAY_CFG_DCEN (1 << 24)
+#define DC_DISPLAY_CFG_DISP_MODE_24BPP (1 << 9)
+#define DC_DISPLAY_CFG_DISP_MODE_16BPP (1 << 8)
+#define DC_DISPLAY_CFG_DISP_MODE_8BPP (0)
+#define DC_DISPLAY_CFG_TRUP (1 << 6)
+#define DC_DISPLAY_CFG_VDEN (1 << 4)
+#define DC_DISPLAY_CFG_GDEN (1 << 3)
+#define DC_DISPLAY_CFG_TGEN (1 << 0)
+
+#define DC_DV_TOP_DV_TOP_EN (1 << 0)
+
+#define DC_DV_CTL_DV_LINE_SIZE ((1 << 10) | (1 << 11))
+#define DC_DV_CTL_DV_LINE_SIZE_1K (0)
+#define DC_DV_CTL_DV_LINE_SIZE_2K (1 << 10)
+#define DC_DV_CTL_DV_LINE_SIZE_4K (1 << 11)
+#define DC_DV_CTL_DV_LINE_SIZE_8K ((1 << 10) | (1 << 11))
+#define DC_DV_CTL_CLEAR_DV_RAM (1 << 0)
+
+#define DC_IRQ_FILT_CTL_H_FILT_SEL (1 << 10)
+
+#define DC_CLR_KEY_CLR_KEY_EN (1 << 24)
+
+#define DC_IRQ_VIP_VSYNC_IRQ_STATUS (1 << 21) /* undocumented? */
+#define DC_IRQ_STATUS (1 << 20) /* undocumented? */
+#define DC_IRQ_VIP_VSYNC_LOSS_IRQ_MASK (1 << 1)
+#define DC_IRQ_MASK (1 << 0)
-#define MSR_LX_GLD_CONFIG 0x48002001
-#define MSR_LX_GLCP_DOTPLL 0x4c000015
-#define MSR_LX_DF_PADSEL 0x48002011
-#define MSR_LX_DC_SPARE 0x80000011
-#define MSR_LX_DF_GLCONFIG 0x48002001
-
-#define MSR_LX_GLIU0_P2D_RO0 0x10000029
-
-#define GLCP_DOTPLL_RESET (1 << 0)
-#define GLCP_DOTPLL_BYPASS (1 << 15)
-#define GLCP_DOTPLL_HALFPIX (1 << 24)
-#define GLCP_DOTPLL_LOCK (1 << 25)
-
-#define DF_CONFIG_OUTPUT_MASK 0x38
-#define DF_OUTPUT_PANEL 0x08
-#define DF_OUTPUT_CRT 0x00
-#define DF_SIMULTANEOUS_CRT_AND_FP (1 << 15)
-
-#define DF_DEFAULT_TFT_PAD_SEL_LOW 0xDFFFFFFF
-#define DF_DEFAULT_TFT_PAD_SEL_HIGH 0x0000003F
-
-#define DC_SPARE_DISABLE_CFIFO_HGO 0x00000800
-#define DC_SPARE_VFIFO_ARB_SELECT 0x00000400
-#define DC_SPARE_WM_LPEN_OVRD 0x00000200
-#define DC_SPARE_LOAD_WM_LPEN_MASK 0x00000100
-#define DC_SPARE_DISABLE_INIT_VID_PRI 0x00000080
-#define DC_SPARE_DISABLE_VFIFO_WM 0x00000040
-#define DC_SPARE_DISABLE_CWD_CHECK 0x00000020
-#define DC_SPARE_PIX8_PAN_FIX 0x00000010
-#define DC_SPARE_FIRST_REQ_MASK 0x00000002
-
-/* Registers */
-
-#define DC_UNLOCK 0x00
-#define DC_UNLOCK_CODE 0x4758
+#define DC_GENLK_CTL_FLICK_SEL_MASK (0x0F << 28)
+#define DC_GENLK_CTL_ALPHA_FLICK_EN (1 << 25)
+#define DC_GENLK_CTL_FLICK_EN (1 << 24)
+#define DC_GENLK_CTL_GENLK_EN (1 << 18)
-#define DC_GENERAL_CFG 0x04
-#define DC_GCFG_DFLE (1 << 0)
-#define DC_GCFG_VIDE (1 << 3)
-#define DC_GCFG_VGAE (1 << 7)
-#define DC_GCFG_CMPE (1 << 5)
-#define DC_GCFG_DECE (1 << 6)
-#define DC_GCFG_FDTY (1 << 17)
-#define DC_DISPLAY_CFG 0x08
-#define DC_DCFG_TGEN (1 << 0)
-#define DC_DCFG_GDEN (1 << 3)
-#define DC_DCFG_VDEN (1 << 4)
-#define DC_DCFG_TRUP (1 << 6)
-#define DC_DCFG_DCEN (1 << 24)
-#define DC_DCFG_PALB (1 << 25)
-#define DC_DCFG_VISL (1 << 27)
+/*
+ * Video Processor registers (table 6-71).
+ * There is space for 64 bit values, but we never use more than the
+ * lower 32 bits. The actual register save/restore code only bothers
+ * to restore those 32 bits.
+ */
+enum vp_registers {
+ VP_VCFG = 0,
+ VP_DCFG,
-#define DC_DCFG_16BPP 0x0
+ VP_VX,
+ VP_VY,
-#define DC_DCFG_DISP_MODE_MASK 0x00000300
-#define DC_DCFG_DISP_MODE_8BPP 0x00000000
-#define DC_DCFG_DISP_MODE_16BPP 0x00000100
-#define DC_DCFG_DISP_MODE_24BPP 0x00000200
-#define DC_DCFG_DISP_MODE_32BPP 0x00000300
+ VP_SCL,
+ VP_VCK,
+ VP_VCM,
+ VP_PAR,
-#define DC_ARB_CFG 0x0C
+ VP_PDR,
+ VP_SLR,
-#define DC_FB_START 0x10
-#define DC_CB_START 0x14
-#define DC_CURSOR_START 0x18
+ VP_MISC,
+ VP_CCS,
-#define DC_DV_TOP 0x2C
-#define DC_DV_TOP_ENABLE (1 << 0)
+ VP_VYS,
+ VP_VXS,
-#define DC_LINE_SIZE 0x30
-#define DC_GRAPHICS_PITCH 0x34
-#define DC_H_ACTIVE_TIMING 0x40
-#define DC_H_BLANK_TIMING 0x44
-#define DC_H_SYNC_TIMING 0x48
-#define DC_V_ACTIVE_TIMING 0x50
-#define DC_V_BLANK_TIMING 0x54
-#define DC_V_SYNC_TIMING 0x58
-#define DC_FB_ACTIVE 0x5C
+ VP_RSVD_0,
+ VP_VDC,
+
+ VP_RSVD_1,
+ VP_CRC,
+
+ VP_CRC32,
+ VP_VDE,
+
+ VP_CCK,
+ VP_CCM,
+
+ VP_CC1,
+ VP_CC2,
+
+ VP_A1X,
+ VP_A1Y,
+
+ VP_A1C,
+ VP_A1T,
+
+ VP_A2X,
+ VP_A2Y,
+
+ VP_A2C,
+ VP_A2T,
+
+ VP_A3X,
+ VP_A3Y,
+
+ VP_A3C,
+ VP_A3T,
+
+ VP_VRR,
+ VP_AWT,
+
+ VP_VTM,
+ VP_VYE,
+
+ VP_A1YE,
+ VP_A2YE,
+
+ VP_A3YE, /* 0x150 */
+
+ VP_VCR = 0x1000, /* 0x1000 - 0x1fff */
+};
-#define DC_PAL_ADDRESS 0x70
-#define DC_PAL_DATA 0x74
+#define VP_VCFG_VID_EN (1 << 0)
-#define DC_PHY_MEM_OFFSET 0x84
+#define VP_DCFG_GV_GAM (1 << 21)
+#define VP_DCFG_PWR_SEQ_DELAY ((1 << 17) | (1 << 18) | (1 << 19))
+#define VP_DCFG_PWR_SEQ_DELAY_DEFAULT (1 << 19) /* undocumented */
+#define VP_DCFG_CRT_SYNC_SKW ((1 << 14) | (1 << 15) | (1 << 16))
+#define VP_DCFG_CRT_SYNC_SKW_DEFAULT (1 << 16)
+#define VP_DCFG_CRT_VSYNC_POL (1 << 9)
+#define VP_DCFG_CRT_HSYNC_POL (1 << 8)
+#define VP_DCFG_DAC_BL_EN (1 << 3)
+#define VP_DCFG_VSYNC_EN (1 << 2)
+#define VP_DCFG_HSYNC_EN (1 << 1)
+#define VP_DCFG_CRT_EN (1 << 0)
-#define DC_DV_CTL 0x88
-#define DC_DV_LINE_SIZE_MASK 0x00000C00
-#define DC_DV_LINE_SIZE_1024 0x00000000
-#define DC_DV_LINE_SIZE_2048 0x00000400
-#define DC_DV_LINE_SIZE_4096 0x00000800
-#define DC_DV_LINE_SIZE_8192 0x00000C00
+#define VP_MISC_APWRDN (1 << 11)
+#define VP_MISC_DACPWRDN (1 << 10)
+#define VP_MISC_BYP_BOTH (1 << 0)
-#define DC_GFX_SCALE 0x90
-#define DC_IRQ_FILT_CTL 0x94
+/*
+ * Flat Panel registers (table 6-71).
+ * Also 64 bit registers; see above note about 32-bit handling.
+ */
+/* we're actually in the VP register space, starting at address 0x400 */
+#define VP_FP_START 0x400
-#define DC_IRQ 0xC8
-#define DC_IRQ_MASK (1 << 0)
-#define DC_VSYNC_IRQ_MASK (1 << 1)
-#define DC_IRQ_STATUS (1 << 20)
-#define DC_VSYNC_IRQ_STATUS (1 << 21)
-
-#define DC_GENLCK_CTRL 0xD4
-#define DC_GENLCK_ENABLE (1 << 18)
-#define DC_GC_ALPHA_FLICK_ENABLE (1 << 25)
-#define DC_GC_FLICKER_FILTER_ENABLE (1 << 24)
-#define DC_GC_FLICKER_FILTER_MASK (0x0F << 28)
-
-#define DC_COLOR_KEY 0xB8
-#define DC_CLR_KEY_ENABLE (1 << 24)
-
-
-#define DC3_DV_LINE_SIZE_MASK 0x00000C00
-#define DC3_DV_LINE_SIZE_1024 0x00000000
-#define DC3_DV_LINE_SIZE_2048 0x00000400
-#define DC3_DV_LINE_SIZE_4096 0x00000800
-#define DC3_DV_LINE_SIZE_8192 0x00000C00
-
-#define DF_VIDEO_CFG 0x0
-#define DF_VCFG_VID_EN (1 << 0)
-
-#define DF_DISPLAY_CFG 0x08
-
-#define DF_DCFG_CRT_EN (1 << 0)
-#define DF_DCFG_HSYNC_EN (1 << 1)
-#define DF_DCFG_VSYNC_EN (1 << 2)
-#define DF_DCFG_DAC_BL_EN (1 << 3)
-#define DF_DCFG_CRT_HSYNC_POL (1 << 8)
-#define DF_DCFG_CRT_VSYNC_POL (1 << 9)
-#define DF_DCFG_GV_PAL_BYP (1 << 21)
+enum fp_registers {
+ FP_PT1 = 0,
+ FP_PT2,
-#define DF_DCFG_CRT_SYNC_SKW_INIT 0x10000
-#define DF_DCFG_CRT_SYNC_SKW_MASK 0x1c000
+ FP_PM,
+ FP_DFC,
-#define DF_DCFG_PWR_SEQ_DLY_INIT 0x80000
-#define DF_DCFG_PWR_SEQ_DLY_MASK 0xe0000
+ FP_RSVD_0,
+ FP_RSVD_1,
-#define DF_MISC 0x50
+ FP_RSVD_2,
+ FP_RSVD_3,
+
+ FP_RSVD_4,
+ FP_DCA,
+
+ FP_DMD,
+ FP_CRC, /* 0x458 */
+};
+
+#define FP_PT2_SCRC (1 << 27) /* shfclk free */
+
+#define FP_PM_P (1 << 24) /* panel power ctl */
+#define FP_PM_PANEL_PWR_UP (1 << 3) /* r/o */
+#define FP_PM_PANEL_PWR_DOWN (1 << 2) /* r/o */
+#define FP_PM_PANEL_OFF (1 << 1) /* r/o */
+#define FP_PM_PANEL_ON (1 << 0) /* r/o */
+
+#define FP_DFC_BC ((1 << 4) | (1 << 5) | (1 << 6))
+
+
+/* register access functions */
+
+static inline uint32_t read_gp(struct lxfb_par *par, int reg)
+{
+ return readl(par->gp_regs + 4*reg);
+}
+
+static inline void write_gp(struct lxfb_par *par, int reg, uint32_t val)
+{
+ writel(val, par->gp_regs + 4*reg);
+}
+
+static inline uint32_t read_dc(struct lxfb_par *par, int reg)
+{
+ return readl(par->dc_regs + 4*reg);
+}
+
+static inline void write_dc(struct lxfb_par *par, int reg, uint32_t val)
+{
+ writel(val, par->dc_regs + 4*reg);
+}
+
+static inline uint32_t read_vp(struct lxfb_par *par, int reg)
+{
+ return readl(par->vp_regs + 8*reg);
+}
+
+static inline void write_vp(struct lxfb_par *par, int reg, uint32_t val)
+{
+ writel(val, par->vp_regs + 8*reg);
+}
+
+static inline uint32_t read_fp(struct lxfb_par *par, int reg)
+{
+ return readl(par->vp_regs + 8*reg + VP_FP_START);
+}
+
+static inline void write_fp(struct lxfb_par *par, int reg, uint32_t val)
+{
+ writel(val, par->vp_regs + 8*reg + VP_FP_START);
+}
-#define DF_MISC_GAM_BYPASS (1 << 0)
-#define DF_MISC_DAC_PWRDN (1 << 10)
-#define DF_MISC_A_PWRDN (1 << 11)
-#define DF_PAR 0x38
-#define DF_PDR 0x40
-#define DF_ALPHA_CONTROL_1 0xD8
-#define DF_VIDEO_REQUEST 0x120
+/* MSRs are defined in asm/geode.h; their bitfields are here */
-#define DF_PANEL_TIM1 0x400
-#define DF_DEFAULT_TFT_PMTIM1 0x0
+#define MSR_GLCP_DOTPLL_LOCK (1 << 25) /* r/o */
+#define MSR_GLCP_DOTPLL_HALFPIX (1 << 24)
+#define MSR_GLCP_DOTPLL_BYPASS (1 << 15)
+#define MSR_GLCP_DOTPLL_DOTRESET (1 << 0)
-#define DF_PANEL_TIM2 0x408
-#define DF_DEFAULT_TFT_PMTIM2 0x08000000
+/* note: this is actually the VP's GLD_MSR_CONFIG */
+#define MSR_LX_GLD_MSR_CONFIG_FMT ((1 << 3) | (1 << 4) | (1 << 5))
+#define MSR_LX_GLD_MSR_CONFIG_FMT_FP (1 << 3)
+#define MSR_LX_GLD_MSR_CONFIG_FMT_CRT (0)
+#define MSR_LX_GLD_MSR_CONFIG_FPC (1 << 15) /* FP *and* CRT */
-#define DF_FP_PM 0x410
-#define DF_FP_PM_P (1 << 24)
+#define MSR_LX_MSR_PADSEL_TFT_SEL_LOW 0xDFFFFFFF /* ??? */
+#define MSR_LX_MSR_PADSEL_TFT_SEL_HIGH 0x0000003F /* ??? */
-#define DF_DITHER_CONTROL 0x418
-#define DF_DEFAULT_TFT_DITHCTL 0x00000070
-#define GP_BLT_STATUS 0x44
-#define GP_BS_BLT_BUSY (1 << 0)
-#define GP_BS_CB_EMPTY (1 << 4)
+#define MSR_LX_SPARE_MSR_DIS_CFIFO_HGO (1 << 11) /* undocumented */
+#define MSR_LX_SPARE_MSR_VFIFO_ARB_SEL (1 << 10) /* undocumented */
+#define MSR_LX_SPARE_MSR_WM_LPEN_OVRD (1 << 9) /* undocumented */
+#define MSR_LX_SPARE_MSR_LOAD_WM_LPEN_M (1 << 8) /* undocumented */
+#define MSR_LX_SPARE_MSR_DIS_INIT_V_PRI (1 << 7) /* undocumented */
+#define MSR_LX_SPARE_MSR_DIS_VIFO_WM (1 << 6)
+#define MSR_LX_SPARE_MSR_DIS_CWD_CHECK (1 << 5) /* undocumented */
+#define MSR_LX_SPARE_MSR_PIX8_PAN_FIX (1 << 4) /* undocumented */
+#define MSR_LX_SPARE_MSR_FIRST_REQ_MASK (1 << 1) /* undocumented */
#endif
diff --git a/drivers/video/geode/lxfb_core.c b/drivers/video/geode/lxfb_core.c
index eb6b8817153..2cd9b74d222 100644
--- a/drivers/video/geode/lxfb_core.c
+++ b/drivers/video/geode/lxfb_core.c
@@ -17,6 +17,7 @@
#include <linux/console.h>
#include <linux/mm.h>
#include <linux/slab.h>
+#include <linux/suspend.h>
#include <linux/delay.h>
#include <linux/fb.h>
#include <linux/init.h>
@@ -27,14 +28,15 @@
static char *mode_option;
static int noclear, nopanel, nocrt;
-static int fbsize;
+static int vram;
+static int vt_switch;
/* Most of these modes are sorted in ascending order, but
* since the first entry in this table is the "default" mode,
* we try to make it something sane - 640x480-60 is sane
*/
-static const struct fb_videomode geode_modedb[] __initdata = {
+static struct fb_videomode geode_modedb[] __initdata = {
/* 640x480-60 */
{ NULL, 60, 640, 480, 39682, 48, 8, 25, 2, 88, 2,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
@@ -215,6 +217,35 @@ static const struct fb_videomode geode_modedb[] __initdata = {
0, FB_VMODE_NONINTERLACED, 0 },
};
+#ifdef CONFIG_OLPC
+#include <asm/olpc.h>
+
+static struct fb_videomode olpc_dcon_modedb[] __initdata = {
+ /* The only mode the DCON has is 1200x900 */
+ { NULL, 50, 1200, 900, 17460, 24, 8, 4, 5, 8, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED, 0 }
+};
+
+static void __init get_modedb(struct fb_videomode **modedb, unsigned int *size)
+{
+ if (olpc_has_dcon()) {
+ *modedb = (struct fb_videomode *) olpc_dcon_modedb;
+ *size = ARRAY_SIZE(olpc_dcon_modedb);
+ } else {
+ *modedb = (struct fb_videomode *) geode_modedb;
+ *size = ARRAY_SIZE(geode_modedb);
+ }
+}
+
+#else
+static void __init get_modedb(struct fb_videomode **modedb, unsigned int *size)
+{
+ *modedb = (struct fb_videomode *) geode_modedb;
+ *size = ARRAY_SIZE(geode_modedb);
+}
+#endif
+
static int lxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
if (var->xres > 1920 || var->yres > 1440)
@@ -333,13 +364,13 @@ static int __init lxfb_map_video_memory(struct fb_info *info,
if (ret)
return ret;
- ret = pci_request_region(dev, 3, "lxfb-vip");
+ ret = pci_request_region(dev, 3, "lxfb-vp");
if (ret)
return ret;
info->fix.smem_start = pci_resource_start(dev, 0);
- info->fix.smem_len = fbsize ? fbsize : lx_framebuffer_size();
+ info->fix.smem_len = vram ? vram : lx_framebuffer_size();
info->screen_base = ioremap(info->fix.smem_start, info->fix.smem_len);
@@ -360,18 +391,15 @@ static int __init lxfb_map_video_memory(struct fb_info *info,
if (par->dc_regs == NULL)
return ret;
- par->df_regs = ioremap(pci_resource_start(dev, 3),
+ par->vp_regs = ioremap(pci_resource_start(dev, 3),
pci_resource_len(dev, 3));
- if (par->df_regs == NULL)
+ if (par->vp_regs == NULL)
return ret;
- writel(DC_UNLOCK_CODE, par->dc_regs + DC_UNLOCK);
-
- writel(info->fix.smem_start & 0xFF000000,
- par->dc_regs + DC_PHY_MEM_OFFSET);
-
- writel(0, par->dc_regs + DC_UNLOCK);
+ write_dc(par, DC_UNLOCK, DC_UNLOCK_UNLOCK);
+ write_dc(par, DC_GLIU0_MEM_OFFSET, info->fix.smem_start & 0xFF000000);
+ write_dc(par, DC_UNLOCK, DC_UNLOCK_LOCK);
dev_info(&dev->dev, "%d KB of video memory at 0x%lx\n",
info->fix.smem_len / 1024, info->fix.smem_start);
@@ -431,6 +459,45 @@ static struct fb_info * __init lxfb_init_fbinfo(struct device *dev)
return info;
}
+#ifdef CONFIG_PM
+static int lxfb_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct fb_info *info = pci_get_drvdata(pdev);
+
+ if (state.event == PM_EVENT_SUSPEND) {
+ acquire_console_sem();
+ lx_powerdown(info);
+ fb_set_suspend(info, 1);
+ release_console_sem();
+ }
+
+ /* there's no point in setting PCI states; we emulate PCI, so
+ * we don't end up getting power savings anyways */
+
+ return 0;
+}
+
+static int lxfb_resume(struct pci_dev *pdev)
+{
+ struct fb_info *info = pci_get_drvdata(pdev);
+ int ret;
+
+ acquire_console_sem();
+ ret = lx_powerup(info);
+ if (ret) {
+ printk(KERN_ERR "lxfb: power up failed!\n");
+ return ret;
+ }
+
+ fb_set_suspend(info, 0);
+ release_console_sem();
+ return 0;
+}
+#else
+#define lxfb_suspend NULL
+#define lxfb_resume NULL
+#endif
+
static int __init lxfb_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
@@ -439,7 +506,7 @@ static int __init lxfb_probe(struct pci_dev *pdev,
int ret;
struct fb_videomode *modedb_ptr;
- int modedb_size;
+ unsigned int modedb_size;
info = lxfb_init_fbinfo(&pdev->dev);
@@ -464,9 +531,7 @@ static int __init lxfb_probe(struct pci_dev *pdev,
/* Set up the mode database */
- modedb_ptr = (struct fb_videomode *) geode_modedb;
- modedb_size = ARRAY_SIZE(geode_modedb);
-
+ get_modedb(&modedb_ptr, &modedb_size);
ret = fb_find_mode(&info->var, info, mode_option,
modedb_ptr, modedb_size, NULL, 16);
@@ -487,6 +552,8 @@ static int __init lxfb_probe(struct pci_dev *pdev,
lxfb_check_var(&info->var, info);
lxfb_set_par(info);
+ pm_set_vt_switch(vt_switch);
+
if (register_framebuffer(info) < 0) {
ret = -EINVAL;
goto err;
@@ -510,8 +577,8 @@ err:
iounmap(par->dc_regs);
pci_release_region(pdev, 2);
}
- if (par->df_regs) {
- iounmap(par->df_regs);
+ if (par->vp_regs) {
+ iounmap(par->vp_regs);
pci_release_region(pdev, 3);
}
@@ -537,7 +604,7 @@ static void lxfb_remove(struct pci_dev *pdev)
iounmap(par->dc_regs);
pci_release_region(pdev, 2);
- iounmap(par->df_regs);
+ iounmap(par->vp_regs);
pci_release_region(pdev, 3);
pci_set_drvdata(pdev, NULL);
@@ -556,6 +623,8 @@ static struct pci_driver lxfb_driver = {
.id_table = lxfb_id_table,
.probe = lxfb_probe,
.remove = lxfb_remove,
+ .suspend = lxfb_suspend,
+ .resume = lxfb_resume,
};
#ifndef MODULE
@@ -570,9 +639,7 @@ static int __init lxfb_setup(char *options)
if (!*opt)
continue;
- if (!strncmp(opt, "fbsize:", 7))
- fbsize = simple_strtoul(opt+7, NULL, 0);
- else if (!strcmp(opt, "noclear"))
+ if (!strcmp(opt, "noclear"))
noclear = 1;
else if (!strcmp(opt, "nopanel"))
nopanel = 1;
@@ -609,8 +676,11 @@ module_exit(lxfb_cleanup);
module_param(mode_option, charp, 0);
MODULE_PARM_DESC(mode_option, "video mode (<x>x<y>[-<bpp>][@<refr>])");
-module_param(fbsize, int, 0);
-MODULE_PARM_DESC(fbsize, "video memory size");
+module_param(vram, int, 0);
+MODULE_PARM_DESC(vram, "video memory size");
+
+module_param(vt_switch, int, 0);
+MODULE_PARM_DESC(vt_switch, "enable VT switch during suspend/resume");
MODULE_DESCRIPTION("Framebuffer driver for the AMD Geode LX");
MODULE_LICENSE("GPL");
diff --git a/drivers/video/geode/lxfb_ops.c b/drivers/video/geode/lxfb_ops.c
index 4fbc99be96e..aaef9165ec9 100644
--- a/drivers/video/geode/lxfb_ops.c
+++ b/drivers/video/geode/lxfb_ops.c
@@ -13,6 +13,7 @@
#include <linux/fb.h>
#include <linux/uaccess.h>
#include <linux/delay.h>
+#include <asm/geode.h>
#include "lxfb.h"
@@ -34,35 +35,63 @@ static const struct {
unsigned int pllval;
unsigned int freq;
} pll_table[] = {
- { 0x000031AC, 24923 },
- { 0x0000215D, 25175 },
- { 0x00001087, 27000 },
- { 0x0000216C, 28322 },
- { 0x0000218D, 28560 },
- { 0x000010C9, 31200 },
- { 0x00003147, 31500 },
- { 0x000010A7, 33032 },
- { 0x00002159, 35112 },
- { 0x00004249, 35500 },
- { 0x00000057, 36000 },
- { 0x0000219A, 37889 },
- { 0x00002158, 39168 },
- { 0x00000045, 40000 },
- { 0x00000089, 43163 },
- { 0x000010E7, 44900 },
- { 0x00002136, 45720 },
- { 0x00003207, 49500 },
- { 0x00002187, 50000 },
- { 0x00004286, 56250 },
- { 0x000010E5, 60065 },
- { 0x00004214, 65000 },
- { 0x00001105, 68179 },
- { 0x000031E4, 74250 },
- { 0x00003183, 75000 },
- { 0x00004284, 78750 },
- { 0x00001104, 81600 },
- { 0x00006363, 94500 },
- { 0x00005303, 97520 },
+ { 0x000131AC, 6231 },
+ { 0x0001215D, 6294 },
+ { 0x00011087, 6750 },
+ { 0x0001216C, 7081 },
+ { 0x0001218D, 7140 },
+ { 0x000110C9, 7800 },
+ { 0x00013147, 7875 },
+ { 0x000110A7, 8258 },
+ { 0x00012159, 8778 },
+ { 0x00014249, 8875 },
+ { 0x00010057, 9000 },
+ { 0x0001219A, 9472 },
+ { 0x00012158, 9792 },
+ { 0x00010045, 10000 },
+ { 0x00010089, 10791 },
+ { 0x000110E7, 11225 },
+ { 0x00012136, 11430 },
+ { 0x00013207, 12375 },
+ { 0x00012187, 12500 },
+ { 0x00014286, 14063 },
+ { 0x000110E5, 15016 },
+ { 0x00014214, 16250 },
+ { 0x00011105, 17045 },
+ { 0x000131E4, 18563 },
+ { 0x00013183, 18750 },
+ { 0x00014284, 19688 },
+ { 0x00011104, 20400 },
+ { 0x00016363, 23625 },
+ { 0x000031AC, 24923 },
+ { 0x0000215D, 25175 },
+ { 0x00001087, 27000 },
+ { 0x0000216C, 28322 },
+ { 0x0000218D, 28560 },
+ { 0x000010C9, 31200 },
+ { 0x00003147, 31500 },
+ { 0x000010A7, 33032 },
+ { 0x00002159, 35112 },
+ { 0x00004249, 35500 },
+ { 0x00000057, 36000 },
+ { 0x0000219A, 37889 },
+ { 0x00002158, 39168 },
+ { 0x00000045, 40000 },
+ { 0x00000089, 43163 },
+ { 0x000010E7, 44900 },
+ { 0x00002136, 45720 },
+ { 0x00003207, 49500 },
+ { 0x00002187, 50000 },
+ { 0x00004286, 56250 },
+ { 0x000010E5, 60065 },
+ { 0x00004214, 65000 },
+ { 0x00001105, 68179 },
+ { 0x000031E4, 74250 },
+ { 0x00003183, 75000 },
+ { 0x00004284, 78750 },
+ { 0x00001104, 81600 },
+ { 0x00006363, 94500 },
+ { 0x00005303, 97520 },
{ 0x00002183, 100187 },
{ 0x00002122, 101420 },
{ 0x00001081, 108000 },
@@ -101,16 +130,16 @@ static void lx_set_dotpll(u32 pllval)
u32 dotpll_lo, dotpll_hi;
int i;
- rdmsr(MSR_LX_GLCP_DOTPLL, dotpll_lo, dotpll_hi);
+ rdmsr(MSR_GLCP_DOTPLL, dotpll_lo, dotpll_hi);
- if ((dotpll_lo & GLCP_DOTPLL_LOCK) && (dotpll_hi == pllval))
+ if ((dotpll_lo & MSR_GLCP_DOTPLL_LOCK) && (dotpll_hi == pllval))
return;
dotpll_hi = pllval;
- dotpll_lo &= ~(GLCP_DOTPLL_BYPASS | GLCP_DOTPLL_HALFPIX);
- dotpll_lo |= GLCP_DOTPLL_RESET;
+ dotpll_lo &= ~(MSR_GLCP_DOTPLL_BYPASS | MSR_GLCP_DOTPLL_HALFPIX);
+ dotpll_lo |= MSR_GLCP_DOTPLL_DOTRESET;
- wrmsr(MSR_LX_GLCP_DOTPLL, dotpll_lo, dotpll_hi);
+ wrmsr(MSR_GLCP_DOTPLL, dotpll_lo, dotpll_hi);
/* Wait 100us for the PLL to lock */
@@ -119,15 +148,15 @@ static void lx_set_dotpll(u32 pllval)
/* Now, loop for the lock bit */
for (i = 0; i < 1000; i++) {
- rdmsr(MSR_LX_GLCP_DOTPLL, dotpll_lo, dotpll_hi);
- if (dotpll_lo & GLCP_DOTPLL_LOCK)
+ rdmsr(MSR_GLCP_DOTPLL, dotpll_lo, dotpll_hi);
+ if (dotpll_lo & MSR_GLCP_DOTPLL_LOCK)
break;
}
/* Clear the reset bit */
- dotpll_lo &= ~GLCP_DOTPLL_RESET;
- wrmsr(MSR_LX_GLCP_DOTPLL, dotpll_lo, dotpll_hi);
+ dotpll_lo &= ~MSR_GLCP_DOTPLL_DOTRESET;
+ wrmsr(MSR_GLCP_DOTPLL, dotpll_lo, dotpll_hi);
}
/* Set the clock based on the frequency specified by the current mode */
@@ -137,7 +166,7 @@ static void lx_set_clock(struct fb_info *info)
unsigned int diff, min, best = 0;
unsigned int freq, i;
- freq = (unsigned int) (0x3b9aca00 / info->var.pixclock);
+ freq = (unsigned int) (1000000000 / info->var.pixclock);
min = abs(pll_table[0].freq - freq);
@@ -149,7 +178,7 @@ static void lx_set_clock(struct fb_info *info)
}
}
- lx_set_dotpll(pll_table[best].pllval & 0x7FFF);
+ lx_set_dotpll(pll_table[best].pllval & 0x00017FFF);
}
static void lx_graphics_disable(struct fb_info *info)
@@ -159,63 +188,62 @@ static void lx_graphics_disable(struct fb_info *info)
/* Note: This assumes that the video is in a quitet state */
- writel(0, par->df_regs + DF_ALPHA_CONTROL_1);
- writel(0, par->df_regs + DF_ALPHA_CONTROL_1 + 32);
- writel(0, par->df_regs + DF_ALPHA_CONTROL_1 + 64);
+ write_vp(par, VP_A1T, 0);
+ write_vp(par, VP_A2T, 0);
+ write_vp(par, VP_A3T, 0);
/* Turn off the VGA and video enable */
- val = readl (par->dc_regs + DC_GENERAL_CFG) &
- ~(DC_GCFG_VGAE | DC_GCFG_VIDE);
+ val = read_dc(par, DC_GENERAL_CFG) & ~(DC_GENERAL_CFG_VGAE |
+ DC_GENERAL_CFG_VIDE);
- writel(val, par->dc_regs + DC_GENERAL_CFG);
+ write_dc(par, DC_GENERAL_CFG, val);
- val = readl(par->df_regs + DF_VIDEO_CFG) & ~DF_VCFG_VID_EN;
- writel(val, par->df_regs + DF_VIDEO_CFG);
+ val = read_vp(par, VP_VCFG) & ~VP_VCFG_VID_EN;
+ write_vp(par, VP_VCFG, val);
- writel( DC_IRQ_MASK | DC_VSYNC_IRQ_MASK |
- DC_IRQ_STATUS | DC_VSYNC_IRQ_STATUS,
- par->dc_regs + DC_IRQ);
+ write_dc(par, DC_IRQ, DC_IRQ_MASK | DC_IRQ_VIP_VSYNC_LOSS_IRQ_MASK |
+ DC_IRQ_STATUS | DC_IRQ_VIP_VSYNC_IRQ_STATUS);
- val = readl(par->dc_regs + DC_GENLCK_CTRL) & ~DC_GENLCK_ENABLE;
- writel(val, par->dc_regs + DC_GENLCK_CTRL);
+ val = read_dc(par, DC_GENLK_CTL) & ~DC_GENLK_CTL_GENLK_EN;
+ write_dc(par, DC_GENLK_CTL, val);
- val = readl(par->dc_regs + DC_COLOR_KEY) & ~DC_CLR_KEY_ENABLE;
- writel(val & ~DC_CLR_KEY_ENABLE, par->dc_regs + DC_COLOR_KEY);
+ val = read_dc(par, DC_CLR_KEY);
+ write_dc(par, DC_CLR_KEY, val & ~DC_CLR_KEY_CLR_KEY_EN);
- /* We don't actually blank the panel, due to the long latency
- involved with bringing it back */
+ /* turn off the panel */
+ write_fp(par, FP_PM, read_fp(par, FP_PM) & ~FP_PM_P);
- val = readl(par->df_regs + DF_MISC) | DF_MISC_DAC_PWRDN;
- writel(val, par->df_regs + DF_MISC);
+ val = read_vp(par, VP_MISC) | VP_MISC_DACPWRDN;
+ write_vp(par, VP_MISC, val);
/* Turn off the display */
- val = readl(par->df_regs + DF_DISPLAY_CFG);
- writel(val & ~(DF_DCFG_CRT_EN | DF_DCFG_HSYNC_EN | DF_DCFG_VSYNC_EN |
- DF_DCFG_DAC_BL_EN), par->df_regs + DF_DISPLAY_CFG);
+ val = read_vp(par, VP_DCFG);
+ write_vp(par, VP_DCFG, val & ~(VP_DCFG_CRT_EN | VP_DCFG_HSYNC_EN |
+ VP_DCFG_VSYNC_EN | VP_DCFG_DAC_BL_EN));
- gcfg = readl(par->dc_regs + DC_GENERAL_CFG);
- gcfg &= ~(DC_GCFG_CMPE | DC_GCFG_DECE);
- writel(gcfg, par->dc_regs + DC_GENERAL_CFG);
+ gcfg = read_dc(par, DC_GENERAL_CFG);
+ gcfg &= ~(DC_GENERAL_CFG_CMPE | DC_GENERAL_CFG_DECE);
+ write_dc(par, DC_GENERAL_CFG, gcfg);
/* Turn off the TGEN */
- val = readl(par->dc_regs + DC_DISPLAY_CFG);
- val &= ~DC_DCFG_TGEN;
- writel(val, par->dc_regs + DC_DISPLAY_CFG);
+ val = read_dc(par, DC_DISPLAY_CFG);
+ val &= ~DC_DISPLAY_CFG_TGEN;
+ write_dc(par, DC_DISPLAY_CFG, val);
/* Wait 1000 usecs to ensure that the TGEN is clear */
udelay(1000);
/* Turn off the FIFO loader */
- gcfg &= ~DC_GCFG_DFLE;
- writel(gcfg, par->dc_regs + DC_GENERAL_CFG);
+ gcfg &= ~DC_GENERAL_CFG_DFLE;
+ write_dc(par, DC_GENERAL_CFG, gcfg);
/* Lastly, wait for the GP to go idle */
do {
- val = readl(par->gp_regs + GP_BLT_STATUS);
- } while ((val & GP_BS_BLT_BUSY) || !(val & GP_BS_CB_EMPTY));
+ val = read_gp(par, GP_BLT_STATUS);
+ } while ((val & GP_BLT_STATUS_PB) || !(val & GP_BLT_STATUS_CE));
}
static void lx_graphics_enable(struct fb_info *info)
@@ -224,80 +252,85 @@ static void lx_graphics_enable(struct fb_info *info)
u32 temp, config;
/* Set the video request register */
- writel(0, par->df_regs + DF_VIDEO_REQUEST);
+ write_vp(par, VP_VRR, 0);
/* Set up the polarities */
- config = readl(par->df_regs + DF_DISPLAY_CFG);
+ config = read_vp(par, VP_DCFG);
- config &= ~(DF_DCFG_CRT_SYNC_SKW_MASK | DF_DCFG_PWR_SEQ_DLY_MASK |
- DF_DCFG_CRT_HSYNC_POL | DF_DCFG_CRT_VSYNC_POL);
+ config &= ~(VP_DCFG_CRT_SYNC_SKW | VP_DCFG_PWR_SEQ_DELAY |
+ VP_DCFG_CRT_HSYNC_POL | VP_DCFG_CRT_VSYNC_POL);
- config |= (DF_DCFG_CRT_SYNC_SKW_INIT | DF_DCFG_PWR_SEQ_DLY_INIT |
- DF_DCFG_GV_PAL_BYP);
+ config |= (VP_DCFG_CRT_SYNC_SKW_DEFAULT | VP_DCFG_PWR_SEQ_DELAY_DEFAULT
+ | VP_DCFG_GV_GAM);
if (info->var.sync & FB_SYNC_HOR_HIGH_ACT)
- config |= DF_DCFG_CRT_HSYNC_POL;
+ config |= VP_DCFG_CRT_HSYNC_POL;
if (info->var.sync & FB_SYNC_VERT_HIGH_ACT)
- config |= DF_DCFG_CRT_VSYNC_POL;
+ config |= VP_DCFG_CRT_VSYNC_POL;
if (par->output & OUTPUT_PANEL) {
u32 msrlo, msrhi;
- writel(DF_DEFAULT_TFT_PMTIM1,
- par->df_regs + DF_PANEL_TIM1);
- writel(DF_DEFAULT_TFT_PMTIM2,
- par->df_regs + DF_PANEL_TIM2);
- writel(DF_DEFAULT_TFT_DITHCTL,
- par->df_regs + DF_DITHER_CONTROL);
+ write_fp(par, FP_PT1, 0);
+ write_fp(par, FP_PT2, FP_PT2_SCRC);
+ write_fp(par, FP_DFC, FP_DFC_BC);
- msrlo = DF_DEFAULT_TFT_PAD_SEL_LOW;
- msrhi = DF_DEFAULT_TFT_PAD_SEL_HIGH;
+ msrlo = MSR_LX_MSR_PADSEL_TFT_SEL_LOW;
+ msrhi = MSR_LX_MSR_PADSEL_TFT_SEL_HIGH;
- wrmsr(MSR_LX_DF_PADSEL, msrlo, msrhi);
+ wrmsr(MSR_LX_MSR_PADSEL, msrlo, msrhi);
}
if (par->output & OUTPUT_CRT) {
- config |= DF_DCFG_CRT_EN | DF_DCFG_HSYNC_EN |
- DF_DCFG_VSYNC_EN | DF_DCFG_DAC_BL_EN;
+ config |= VP_DCFG_CRT_EN | VP_DCFG_HSYNC_EN |
+ VP_DCFG_VSYNC_EN | VP_DCFG_DAC_BL_EN;
}
- writel(config, par->df_regs + DF_DISPLAY_CFG);
+ write_vp(par, VP_DCFG, config);
/* Turn the CRT dacs back on */
if (par->output & OUTPUT_CRT) {
- temp = readl(par->df_regs + DF_MISC);
- temp &= ~(DF_MISC_DAC_PWRDN | DF_MISC_A_PWRDN);
- writel(temp, par->df_regs + DF_MISC);
+ temp = read_vp(par, VP_MISC);
+ temp &= ~(VP_MISC_DACPWRDN | VP_MISC_APWRDN);
+ write_vp(par, VP_MISC, temp);
}
/* Turn the panel on (if it isn't already) */
-
- if (par->output & OUTPUT_PANEL) {
- temp = readl(par->df_regs + DF_FP_PM);
-
- if (!(temp & 0x09))
- writel(temp | DF_FP_PM_P, par->df_regs + DF_FP_PM);
- }
-
- temp = readl(par->df_regs + DF_MISC);
- temp = readl(par->df_regs + DF_DISPLAY_CFG);
+ if (par->output & OUTPUT_PANEL)
+ write_fp(par, FP_PM, read_fp(par, FP_PM) | FP_PM_P);
}
unsigned int lx_framebuffer_size(void)
{
unsigned int val;
+ if (!geode_has_vsa2()) {
+ uint32_t hi, lo;
+
+ /* The number of pages is (PMAX - PMIN)+1 */
+ rdmsr(MSR_GLIU_P2D_RO0, lo, hi);
+
+ /* PMAX */
+ val = ((hi & 0xff) << 12) | ((lo & 0xfff00000) >> 20);
+ /* PMIN */
+ val -= (lo & 0x000fffff);
+ val += 1;
+
+ /* The page size is 4k */
+ return (val << 12);
+ }
+
/* The frame buffer size is reported by a VSM in VSA II */
/* Virtual Register Class = 0x02 */
/* VG_MEM_SIZE (1MB units) = 0x00 */
- outw(0xFC53, 0xAC1C);
- outw(0x0200, 0xAC1C);
+ outw(VSA_VR_UNLOCK, VSA_VRC_INDEX);
+ outw(VSA_VR_MEM_SIZE, VSA_VRC_INDEX);
- val = (unsigned int)(inw(0xAC1E)) & 0xFE;
+ val = (unsigned int)(inw(VSA_VRC_DATA)) & 0xFE;
return (val << 20);
}
@@ -313,7 +346,7 @@ void lx_set_mode(struct fb_info *info)
int vactive, vblankstart, vsyncstart, vsyncend, vblankend, vtotal;
/* Unlock the DC registers */
- writel(DC_UNLOCK_CODE, par->dc_regs + DC_UNLOCK);
+ write_dc(par, DC_UNLOCK, DC_UNLOCK_UNLOCK);
lx_graphics_disable(info);
@@ -321,102 +354,104 @@ void lx_set_mode(struct fb_info *info)
/* Set output mode */
- rdmsrl(MSR_LX_DF_GLCONFIG, msrval);
- msrval &= ~DF_CONFIG_OUTPUT_MASK;
+ rdmsrl(MSR_LX_GLD_MSR_CONFIG, msrval);
+ msrval &= ~MSR_LX_GLD_MSR_CONFIG_FMT;
if (par->output & OUTPUT_PANEL) {
- msrval |= DF_OUTPUT_PANEL;
+ msrval |= MSR_LX_GLD_MSR_CONFIG_FMT_FP;
if (par->output & OUTPUT_CRT)
- msrval |= DF_SIMULTANEOUS_CRT_AND_FP;
+ msrval |= MSR_LX_GLD_MSR_CONFIG_FPC;
else
- msrval &= ~DF_SIMULTANEOUS_CRT_AND_FP;
- } else {
- msrval |= DF_OUTPUT_CRT;
- }
+ msrval &= ~MSR_LX_GLD_MSR_CONFIG_FPC;
+ } else
+ msrval |= MSR_LX_GLD_MSR_CONFIG_FMT_CRT;
- wrmsrl(MSR_LX_DF_GLCONFIG, msrval);
+ wrmsrl(MSR_LX_GLD_MSR_CONFIG, msrval);
/* Clear the various buffers */
/* FIXME: Adjust for panning here */
- writel(0, par->dc_regs + DC_FB_START);
- writel(0, par->dc_regs + DC_CB_START);
- writel(0, par->dc_regs + DC_CURSOR_START);
+ write_dc(par, DC_FB_ST_OFFSET, 0);
+ write_dc(par, DC_CB_ST_OFFSET, 0);
+ write_dc(par, DC_CURS_ST_OFFSET, 0);
/* FIXME: Add support for interlacing */
/* FIXME: Add support for scaling */
- val = readl(par->dc_regs + DC_GENLCK_CTRL);
- val &= ~(DC_GC_ALPHA_FLICK_ENABLE |
- DC_GC_FLICKER_FILTER_ENABLE | DC_GC_FLICKER_FILTER_MASK);
+ val = read_dc(par, DC_GENLK_CTL);
+ val &= ~(DC_GENLK_CTL_ALPHA_FLICK_EN | DC_GENLK_CTL_FLICK_EN |
+ DC_GENLK_CTL_FLICK_SEL_MASK);
/* Default scaling params */
- writel((0x4000 << 16) | 0x4000, par->dc_regs + DC_GFX_SCALE);
- writel(0, par->dc_regs + DC_IRQ_FILT_CTL);
- writel(val, par->dc_regs + DC_GENLCK_CTRL);
+ write_dc(par, DC_GFX_SCALE, (0x4000 << 16) | 0x4000);
+ write_dc(par, DC_IRQ_FILT_CTL, 0);
+ write_dc(par, DC_GENLK_CTL, val);
/* FIXME: Support compression */
if (info->fix.line_length > 4096)
- dv = DC_DV_LINE_SIZE_8192;
+ dv = DC_DV_CTL_DV_LINE_SIZE_8K;
else if (info->fix.line_length > 2048)
- dv = DC_DV_LINE_SIZE_4096;
+ dv = DC_DV_CTL_DV_LINE_SIZE_4K;
else if (info->fix.line_length > 1024)
- dv = DC_DV_LINE_SIZE_2048;
+ dv = DC_DV_CTL_DV_LINE_SIZE_2K;
else
- dv = DC_DV_LINE_SIZE_1024;
+ dv = DC_DV_CTL_DV_LINE_SIZE_1K;
max = info->fix.line_length * info->var.yres;
max = (max + 0x3FF) & 0xFFFFFC00;
- writel(max | DC_DV_TOP_ENABLE, par->dc_regs + DC_DV_TOP);
+ write_dc(par, DC_DV_TOP, max | DC_DV_TOP_DV_TOP_EN);
- val = readl(par->dc_regs + DC_DV_CTL) & ~DC_DV_LINE_SIZE_MASK;
- writel(val | dv, par->dc_regs + DC_DV_CTL);
+ val = read_dc(par, DC_DV_CTL) & ~DC_DV_CTL_DV_LINE_SIZE;
+ write_dc(par, DC_DV_CTL, val | dv);
size = info->var.xres * (info->var.bits_per_pixel >> 3);
- writel(info->fix.line_length >> 3, par->dc_regs + DC_GRAPHICS_PITCH);
- writel((size + 7) >> 3, par->dc_regs + DC_LINE_SIZE);
+ write_dc(par, DC_GFX_PITCH, info->fix.line_length >> 3);
+ write_dc(par, DC_LINE_SIZE, (size + 7) >> 3);
/* Set default watermark values */
- rdmsrl(MSR_LX_DC_SPARE, msrval);
-
- msrval &= ~(DC_SPARE_DISABLE_CFIFO_HGO | DC_SPARE_VFIFO_ARB_SELECT |
- DC_SPARE_LOAD_WM_LPEN_MASK | DC_SPARE_WM_LPEN_OVRD |
- DC_SPARE_DISABLE_INIT_VID_PRI | DC_SPARE_DISABLE_VFIFO_WM);
- msrval |= DC_SPARE_DISABLE_VFIFO_WM | DC_SPARE_DISABLE_INIT_VID_PRI;
- wrmsrl(MSR_LX_DC_SPARE, msrval);
-
- gcfg = DC_GCFG_DFLE; /* Display fifo enable */
- gcfg |= 0xB600; /* Set default priority */
- gcfg |= DC_GCFG_FDTY; /* Set the frame dirty mode */
-
- dcfg = DC_DCFG_VDEN; /* Enable video data */
- dcfg |= DC_DCFG_GDEN; /* Enable graphics */
- dcfg |= DC_DCFG_TGEN; /* Turn on the timing generator */
- dcfg |= DC_DCFG_TRUP; /* Update timings immediately */
- dcfg |= DC_DCFG_PALB; /* Palette bypass in > 8 bpp modes */
- dcfg |= DC_DCFG_VISL;
- dcfg |= DC_DCFG_DCEN; /* Always center the display */
+ rdmsrl(MSR_LX_SPARE_MSR, msrval);
+
+ msrval &= ~(MSR_LX_SPARE_MSR_DIS_CFIFO_HGO
+ | MSR_LX_SPARE_MSR_VFIFO_ARB_SEL
+ | MSR_LX_SPARE_MSR_LOAD_WM_LPEN_M
+ | MSR_LX_SPARE_MSR_WM_LPEN_OVRD);
+ msrval |= MSR_LX_SPARE_MSR_DIS_VIFO_WM |
+ MSR_LX_SPARE_MSR_DIS_INIT_V_PRI;
+ wrmsrl(MSR_LX_SPARE_MSR, msrval);
+
+ gcfg = DC_GENERAL_CFG_DFLE; /* Display fifo enable */
+ gcfg |= (0x6 << DC_GENERAL_CFG_DFHPSL_SHIFT) | /* default priority */
+ (0xb << DC_GENERAL_CFG_DFHPEL_SHIFT);
+ gcfg |= DC_GENERAL_CFG_FDTY; /* Set the frame dirty mode */
+
+ dcfg = DC_DISPLAY_CFG_VDEN; /* Enable video data */
+ dcfg |= DC_DISPLAY_CFG_GDEN; /* Enable graphics */
+ dcfg |= DC_DISPLAY_CFG_TGEN; /* Turn on the timing generator */
+ dcfg |= DC_DISPLAY_CFG_TRUP; /* Update timings immediately */
+ dcfg |= DC_DISPLAY_CFG_PALB; /* Palette bypass in > 8 bpp modes */
+ dcfg |= DC_DISPLAY_CFG_VISL;
+ dcfg |= DC_DISPLAY_CFG_DCEN; /* Always center the display */
/* Set the current BPP mode */
switch (info->var.bits_per_pixel) {
case 8:
- dcfg |= DC_DCFG_DISP_MODE_8BPP;
+ dcfg |= DC_DISPLAY_CFG_DISP_MODE_8BPP;
break;
case 16:
- dcfg |= DC_DCFG_DISP_MODE_16BPP | DC_DCFG_16BPP;
+ dcfg |= DC_DISPLAY_CFG_DISP_MODE_16BPP;
break;
case 32:
case 24:
- dcfg |= DC_DCFG_DISP_MODE_24BPP;
+ dcfg |= DC_DISPLAY_CFG_DISP_MODE_24BPP;
break;
}
@@ -436,35 +471,31 @@ void lx_set_mode(struct fb_info *info)
vblankend = vsyncend + info->var.upper_margin;
vtotal = vblankend;
- writel((hactive - 1) | ((htotal - 1) << 16),
- par->dc_regs + DC_H_ACTIVE_TIMING);
- writel((hblankstart - 1) | ((hblankend - 1) << 16),
- par->dc_regs + DC_H_BLANK_TIMING);
- writel((hsyncstart - 1) | ((hsyncend - 1) << 16),
- par->dc_regs + DC_H_SYNC_TIMING);
-
- writel((vactive - 1) | ((vtotal - 1) << 16),
- par->dc_regs + DC_V_ACTIVE_TIMING);
+ write_dc(par, DC_H_ACTIVE_TIMING, (hactive - 1) | ((htotal - 1) << 16));
+ write_dc(par, DC_H_BLANK_TIMING,
+ (hblankstart - 1) | ((hblankend - 1) << 16));
+ write_dc(par, DC_H_SYNC_TIMING,
+ (hsyncstart - 1) | ((hsyncend - 1) << 16));
- writel((vblankstart - 1) | ((vblankend - 1) << 16),
- par->dc_regs + DC_V_BLANK_TIMING);
+ write_dc(par, DC_V_ACTIVE_TIMING, (vactive - 1) | ((vtotal - 1) << 16));
+ write_dc(par, DC_V_BLANK_TIMING,
+ (vblankstart - 1) | ((vblankend - 1) << 16));
+ write_dc(par, DC_V_SYNC_TIMING,
+ (vsyncstart - 1) | ((vsyncend - 1) << 16));
- writel((vsyncstart - 1) | ((vsyncend - 1) << 16),
- par->dc_regs + DC_V_SYNC_TIMING);
-
- writel( (info->var.xres - 1) << 16 | (info->var.yres - 1),
- par->dc_regs + DC_FB_ACTIVE);
+ write_dc(par, DC_FB_ACTIVE,
+ (info->var.xres - 1) << 16 | (info->var.yres - 1));
/* And re-enable the graphics output */
lx_graphics_enable(info);
/* Write the two main configuration registers */
- writel(dcfg, par->dc_regs + DC_DISPLAY_CFG);
- writel(0, par->dc_regs + DC_ARB_CFG);
- writel(gcfg, par->dc_regs + DC_GENERAL_CFG);
+ write_dc(par, DC_DISPLAY_CFG, dcfg);
+ write_dc(par, DC_ARB_CFG, 0);
+ write_dc(par, DC_GENERAL_CFG, gcfg);
/* Lock the DC registers */
- writel(0, par->dc_regs + DC_UNLOCK);
+ write_dc(par, DC_UNLOCK, DC_UNLOCK_LOCK);
}
void lx_set_palette_reg(struct fb_info *info, unsigned regno,
@@ -479,58 +510,310 @@ void lx_set_palette_reg(struct fb_info *info, unsigned regno,
val |= (green) & 0x00ff00;
val |= (blue >> 8) & 0x0000ff;
- writel(regno, par->dc_regs + DC_PAL_ADDRESS);
- writel(val, par->dc_regs + DC_PAL_DATA);
+ write_dc(par, DC_PAL_ADDRESS, regno);
+ write_dc(par, DC_PAL_DATA, val);
}
int lx_blank_display(struct fb_info *info, int blank_mode)
{
struct lxfb_par *par = info->par;
u32 dcfg, fp_pm;
- int blank, hsync, vsync;
+ int blank, hsync, vsync, crt;
/* CRT power saving modes. */
switch (blank_mode) {
case FB_BLANK_UNBLANK:
- blank = 0; hsync = 1; vsync = 1;
+ blank = 0; hsync = 1; vsync = 1; crt = 1;
break;
case FB_BLANK_NORMAL:
- blank = 1; hsync = 1; vsync = 1;
+ blank = 1; hsync = 1; vsync = 1; crt = 1;
break;
case FB_BLANK_VSYNC_SUSPEND:
- blank = 1; hsync = 1; vsync = 0;
+ blank = 1; hsync = 1; vsync = 0; crt = 1;
break;
case FB_BLANK_HSYNC_SUSPEND:
- blank = 1; hsync = 0; vsync = 1;
+ blank = 1; hsync = 0; vsync = 1; crt = 1;
break;
case FB_BLANK_POWERDOWN:
- blank = 1; hsync = 0; vsync = 0;
+ blank = 1; hsync = 0; vsync = 0; crt = 0;
break;
default:
return -EINVAL;
}
- dcfg = readl(par->df_regs + DF_DISPLAY_CFG);
- dcfg &= ~(DF_DCFG_DAC_BL_EN
- | DF_DCFG_HSYNC_EN | DF_DCFG_VSYNC_EN);
+ dcfg = read_vp(par, VP_DCFG);
+ dcfg &= ~(VP_DCFG_DAC_BL_EN | VP_DCFG_HSYNC_EN | VP_DCFG_VSYNC_EN |
+ VP_DCFG_CRT_EN);
if (!blank)
- dcfg |= DF_DCFG_DAC_BL_EN;
+ dcfg |= VP_DCFG_DAC_BL_EN;
if (hsync)
- dcfg |= DF_DCFG_HSYNC_EN;
+ dcfg |= VP_DCFG_HSYNC_EN;
if (vsync)
- dcfg |= DF_DCFG_VSYNC_EN;
- writel(dcfg, par->df_regs + DF_DISPLAY_CFG);
+ dcfg |= VP_DCFG_VSYNC_EN;
+ if (crt)
+ dcfg |= VP_DCFG_CRT_EN;
+ write_vp(par, VP_DCFG, dcfg);
/* Power on/off flat panel */
if (par->output & OUTPUT_PANEL) {
- fp_pm = readl(par->df_regs + DF_FP_PM);
+ fp_pm = read_fp(par, FP_PM);
if (blank_mode == FB_BLANK_POWERDOWN)
- fp_pm &= ~DF_FP_PM_P;
+ fp_pm &= ~FP_PM_P;
else
- fp_pm |= DF_FP_PM_P;
- writel(fp_pm, par->df_regs + DF_FP_PM);
+ fp_pm |= FP_PM_P;
+ write_fp(par, FP_PM, fp_pm);
}
return 0;
}
+
+#ifdef CONFIG_PM
+
+static void lx_save_regs(struct lxfb_par *par)
+{
+ uint32_t filt;
+ int i;
+
+ /* wait for the BLT engine to stop being busy */
+ do {
+ i = read_gp(par, GP_BLT_STATUS);
+ } while ((i & GP_BLT_STATUS_PB) || !(i & GP_BLT_STATUS_CE));
+
+ /* save MSRs */
+ rdmsrl(MSR_LX_MSR_PADSEL, par->msr.padsel);
+ rdmsrl(MSR_GLCP_DOTPLL, par->msr.dotpll);
+ rdmsrl(MSR_LX_GLD_MSR_CONFIG, par->msr.dfglcfg);
+ rdmsrl(MSR_LX_SPARE_MSR, par->msr.dcspare);
+
+ write_dc(par, DC_UNLOCK, DC_UNLOCK_UNLOCK);
+
+ /* save registers */
+ memcpy(par->gp, par->gp_regs, sizeof(par->gp));
+ memcpy(par->dc, par->dc_regs, sizeof(par->dc));
+ memcpy(par->vp, par->vp_regs, sizeof(par->vp));
+ memcpy(par->fp, par->vp_regs + VP_FP_START, sizeof(par->fp));
+
+ /* save the palette */
+ write_dc(par, DC_PAL_ADDRESS, 0);
+ for (i = 0; i < ARRAY_SIZE(par->pal); i++)
+ par->pal[i] = read_dc(par, DC_PAL_DATA);
+
+ /* save the horizontal filter coefficients */
+ filt = par->dc[DC_IRQ_FILT_CTL] | DC_IRQ_FILT_CTL_H_FILT_SEL;
+ for (i = 0; i < ARRAY_SIZE(par->hcoeff); i += 2) {
+ write_dc(par, DC_IRQ_FILT_CTL, (filt & 0xffffff00) | i);
+ par->hcoeff[i] = read_dc(par, DC_FILT_COEFF1);
+ par->hcoeff[i + 1] = read_dc(par, DC_FILT_COEFF2);
+ }
+
+ /* save the vertical filter coefficients */
+ filt &= ~DC_IRQ_FILT_CTL_H_FILT_SEL;
+ for (i = 0; i < ARRAY_SIZE(par->vcoeff); i++) {
+ write_dc(par, DC_IRQ_FILT_CTL, (filt & 0xffffff00) | i);
+ par->vcoeff[i] = read_dc(par, DC_FILT_COEFF1);
+ }
+
+ /* save video coeff ram */
+ memcpy(par->vp_coeff, par->vp_regs + VP_VCR, sizeof(par->vp_coeff));
+}
+
+static void lx_restore_gfx_proc(struct lxfb_par *par)
+{
+ int i;
+
+ /* a bunch of registers require GP_RASTER_MODE to be set first */
+ write_gp(par, GP_RASTER_MODE, par->gp[GP_RASTER_MODE]);
+
+ for (i = 0; i < ARRAY_SIZE(par->gp); i++) {
+ switch (i) {
+ case GP_RASTER_MODE:
+ case GP_VECTOR_MODE:
+ case GP_BLT_MODE:
+ case GP_BLT_STATUS:
+ case GP_HST_SRC:
+ /* FIXME: restore LUT data */
+ case GP_LUT_INDEX:
+ case GP_LUT_DATA:
+ /* don't restore these registers */
+ break;
+
+ default:
+ write_gp(par, i, par->gp[i]);
+ }
+ }
+}
+
+static void lx_restore_display_ctlr(struct lxfb_par *par)
+{
+ uint32_t filt;
+ int i;
+
+ wrmsrl(MSR_LX_SPARE_MSR, par->msr.dcspare);
+
+ for (i = 0; i < ARRAY_SIZE(par->dc); i++) {
+ switch (i) {
+ case DC_UNLOCK:
+ /* unlock the DC; runs first */
+ write_dc(par, DC_UNLOCK, DC_UNLOCK_UNLOCK);
+ break;
+
+ case DC_GENERAL_CFG:
+ case DC_DISPLAY_CFG:
+ /* disable all while restoring */
+ write_dc(par, i, 0);
+ break;
+
+ case DC_DV_CTL:
+ /* set all ram to dirty */
+ write_dc(par, i, par->dc[i] | DC_DV_CTL_CLEAR_DV_RAM);
+
+ case DC_RSVD_1:
+ case DC_RSVD_2:
+ case DC_RSVD_3:
+ case DC_LINE_CNT:
+ case DC_PAL_ADDRESS:
+ case DC_PAL_DATA:
+ case DC_DFIFO_DIAG:
+ case DC_CFIFO_DIAG:
+ case DC_FILT_COEFF1:
+ case DC_FILT_COEFF2:
+ case DC_RSVD_4:
+ case DC_RSVD_5:
+ /* don't restore these registers */
+ break;
+
+ default:
+ write_dc(par, i, par->dc[i]);
+ }
+ }
+
+ /* restore the palette */
+ write_dc(par, DC_PAL_ADDRESS, 0);
+ for (i = 0; i < ARRAY_SIZE(par->pal); i++)
+ write_dc(par, DC_PAL_DATA, par->pal[i]);
+
+ /* restore the horizontal filter coefficients */
+ filt = par->dc[DC_IRQ_FILT_CTL] | DC_IRQ_FILT_CTL_H_FILT_SEL;
+ for (i = 0; i < ARRAY_SIZE(par->hcoeff); i += 2) {
+ write_dc(par, DC_IRQ_FILT_CTL, (filt & 0xffffff00) | i);
+ write_dc(par, DC_FILT_COEFF1, par->hcoeff[i]);
+ write_dc(par, DC_FILT_COEFF2, par->hcoeff[i + 1]);
+ }
+
+ /* restore the vertical filter coefficients */
+ filt &= ~DC_IRQ_FILT_CTL_H_FILT_SEL;
+ for (i = 0; i < ARRAY_SIZE(par->vcoeff); i++) {
+ write_dc(par, DC_IRQ_FILT_CTL, (filt & 0xffffff00) | i);
+ write_dc(par, DC_FILT_COEFF1, par->vcoeff[i]);
+ }
+}
+
+static void lx_restore_video_proc(struct lxfb_par *par)
+{
+ int i;
+
+ wrmsrl(MSR_LX_GLD_MSR_CONFIG, par->msr.dfglcfg);
+ wrmsrl(MSR_LX_MSR_PADSEL, par->msr.padsel);
+
+ for (i = 0; i < ARRAY_SIZE(par->vp); i++) {
+ switch (i) {
+ case VP_VCFG:
+ case VP_DCFG:
+ case VP_PAR:
+ case VP_PDR:
+ case VP_CCS:
+ case VP_RSVD_0:
+ /* case VP_VDC: */ /* why should this not be restored? */
+ case VP_RSVD_1:
+ case VP_CRC32:
+ /* don't restore these registers */
+ break;
+
+ default:
+ write_vp(par, i, par->vp[i]);
+ }
+ }
+
+ /* restore video coeff ram */
+ memcpy(par->vp_regs + VP_VCR, par->vp_coeff, sizeof(par->vp_coeff));
+}
+
+static void lx_restore_regs(struct lxfb_par *par)
+{
+ int i;
+
+ lx_set_dotpll((u32) (par->msr.dotpll >> 32));
+ lx_restore_gfx_proc(par);
+ lx_restore_display_ctlr(par);
+ lx_restore_video_proc(par);
+
+ /* Flat Panel */
+ for (i = 0; i < ARRAY_SIZE(par->fp); i++) {
+ switch (i) {
+ case FP_PM:
+ case FP_RSVD_0:
+ case FP_RSVD_1:
+ case FP_RSVD_2:
+ case FP_RSVD_3:
+ case FP_RSVD_4:
+ /* don't restore these registers */
+ break;
+
+ default:
+ write_fp(par, i, par->fp[i]);
+ }
+ }
+
+ /* control the panel */
+ if (par->fp[FP_PM] & FP_PM_P) {
+ /* power on the panel if not already power{ed,ing} on */
+ if (!(read_fp(par, FP_PM) &
+ (FP_PM_PANEL_ON|FP_PM_PANEL_PWR_UP)))
+ write_fp(par, FP_PM, par->fp[FP_PM]);
+ } else {
+ /* power down the panel if not already power{ed,ing} down */
+ if (!(read_fp(par, FP_PM) &
+ (FP_PM_PANEL_OFF|FP_PM_PANEL_PWR_DOWN)))
+ write_fp(par, FP_PM, par->fp[FP_PM]);
+ }
+
+ /* turn everything on */
+ write_vp(par, VP_VCFG, par->vp[VP_VCFG]);
+ write_vp(par, VP_DCFG, par->vp[VP_DCFG]);
+ write_dc(par, DC_DISPLAY_CFG, par->dc[DC_DISPLAY_CFG]);
+ /* do this last; it will enable the FIFO load */
+ write_dc(par, DC_GENERAL_CFG, par->dc[DC_GENERAL_CFG]);
+
+ /* lock the door behind us */
+ write_dc(par, DC_UNLOCK, DC_UNLOCK_LOCK);
+}
+
+int lx_powerdown(struct fb_info *info)
+{
+ struct lxfb_par *par = info->par;
+
+ if (par->powered_down)
+ return 0;
+
+ lx_save_regs(par);
+ lx_graphics_disable(info);
+
+ par->powered_down = 1;
+ return 0;
+}
+
+int lx_powerup(struct fb_info *info)
+{
+ struct lxfb_par *par = info->par;
+
+ if (!par->powered_down)
+ return 0;
+
+ lx_restore_regs(par);
+
+ par->powered_down = 0;
+ return 0;
+}
+
+#endif
diff --git a/drivers/video/geode/suspend_gx.c b/drivers/video/geode/suspend_gx.c
new file mode 100644
index 00000000000..9aff32ef8bb
--- /dev/null
+++ b/drivers/video/geode/suspend_gx.c
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2007 Advanced Micro Devices, Inc.
+ * Copyright (C) 2008 Andres Salomon <dilinger@debian.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+#include <linux/fb.h>
+#include <asm/io.h>
+#include <asm/msr.h>
+#include <asm/geode.h>
+#include <asm/delay.h>
+
+#include "gxfb.h"
+
+#ifdef CONFIG_PM
+
+static void gx_save_regs(struct gxfb_par *par)
+{
+ int i;
+
+ /* wait for the BLT engine to stop being busy */
+ do {
+ i = read_gp(par, GP_BLT_STATUS);
+ } while (i & (GP_BLT_STATUS_BLT_PENDING | GP_BLT_STATUS_BLT_BUSY));
+
+ /* save MSRs */
+ rdmsrl(MSR_GX_MSR_PADSEL, par->msr.padsel);
+ rdmsrl(MSR_GLCP_DOTPLL, par->msr.dotpll);
+
+ write_dc(par, DC_UNLOCK, DC_UNLOCK_UNLOCK);
+
+ /* save registers */
+ memcpy(par->gp, par->gp_regs, sizeof(par->gp));
+ memcpy(par->dc, par->dc_regs, sizeof(par->dc));
+ memcpy(par->vp, par->vid_regs, sizeof(par->vp));
+ memcpy(par->fp, par->vid_regs + VP_FP_START, sizeof(par->fp));
+
+ /* save the palette */
+ write_dc(par, DC_PAL_ADDRESS, 0);
+ for (i = 0; i < ARRAY_SIZE(par->pal); i++)
+ par->pal[i] = read_dc(par, DC_PAL_DATA);
+}
+
+static void gx_set_dotpll(uint32_t dotpll_hi)
+{
+ uint32_t dotpll_lo;
+ int i;
+
+ rdmsrl(MSR_GLCP_DOTPLL, dotpll_lo);
+ dotpll_lo |= MSR_GLCP_DOTPLL_DOTRESET;
+ dotpll_lo &= ~MSR_GLCP_DOTPLL_BYPASS;
+ wrmsr(MSR_GLCP_DOTPLL, dotpll_lo, dotpll_hi);
+
+ /* wait for the PLL to lock */
+ for (i = 0; i < 200; i++) {
+ rdmsrl(MSR_GLCP_DOTPLL, dotpll_lo);
+ if (dotpll_lo & MSR_GLCP_DOTPLL_LOCK)
+ break;
+ udelay(1);
+ }
+
+ /* PLL set, unlock */
+ dotpll_lo &= ~MSR_GLCP_DOTPLL_DOTRESET;
+ wrmsr(MSR_GLCP_DOTPLL, dotpll_lo, dotpll_hi);
+}
+
+static void gx_restore_gfx_proc(struct gxfb_par *par)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(par->gp); i++) {
+ switch (i) {
+ case GP_VECTOR_MODE:
+ case GP_BLT_MODE:
+ case GP_BLT_STATUS:
+ case GP_HST_SRC:
+ /* don't restore these registers */
+ break;
+ default:
+ write_gp(par, i, par->gp[i]);
+ }
+ }
+}
+
+static void gx_restore_display_ctlr(struct gxfb_par *par)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(par->dc); i++) {
+ switch (i) {
+ case DC_UNLOCK:
+ /* unlock the DC; runs first */
+ write_dc(par, DC_UNLOCK, DC_UNLOCK_UNLOCK);
+ break;
+
+ case DC_GENERAL_CFG:
+ /* write without the enables */
+ write_dc(par, i, par->dc[i] & ~(DC_GENERAL_CFG_VIDE |
+ DC_GENERAL_CFG_ICNE |
+ DC_GENERAL_CFG_CURE |
+ DC_GENERAL_CFG_DFLE));
+ break;
+
+ case DC_DISPLAY_CFG:
+ /* write without the enables */
+ write_dc(par, i, par->dc[i] & ~(DC_DISPLAY_CFG_VDEN |
+ DC_DISPLAY_CFG_GDEN |
+ DC_DISPLAY_CFG_TGEN));
+ break;
+
+ case DC_RSVD_0:
+ case DC_RSVD_1:
+ case DC_RSVD_2:
+ case DC_RSVD_3:
+ case DC_RSVD_4:
+ case DC_LINE_CNT:
+ case DC_PAL_ADDRESS:
+ case DC_PAL_DATA:
+ case DC_DFIFO_DIAG:
+ case DC_CFIFO_DIAG:
+ case DC_RSVD_5:
+ /* don't restore these registers */
+ break;
+ default:
+ write_dc(par, i, par->dc[i]);
+ }
+ }
+
+ /* restore the palette */
+ write_dc(par, DC_PAL_ADDRESS, 0);
+ for (i = 0; i < ARRAY_SIZE(par->pal); i++)
+ write_dc(par, DC_PAL_DATA, par->pal[i]);
+}
+
+static void gx_restore_video_proc(struct gxfb_par *par)
+{
+ int i;
+
+ wrmsrl(MSR_GX_MSR_PADSEL, par->msr.padsel);
+
+ for (i = 0; i < ARRAY_SIZE(par->vp); i++) {
+ switch (i) {
+ case VP_VCFG:
+ /* don't enable video yet */
+ write_vp(par, i, par->vp[i] & ~VP_VCFG_VID_EN);
+ break;
+
+ case VP_DCFG:
+ /* don't enable CRT yet */
+ write_vp(par, i, par->vp[i] &
+ ~(VP_DCFG_DAC_BL_EN | VP_DCFG_VSYNC_EN |
+ VP_DCFG_HSYNC_EN | VP_DCFG_CRT_EN));
+ break;
+
+ case VP_GAR:
+ case VP_GDR:
+ case VP_RSVD_0:
+ case VP_RSVD_1:
+ case VP_RSVD_2:
+ case VP_RSVD_3:
+ case VP_CRC32:
+ case VP_AWT:
+ case VP_VTM:
+ /* don't restore these registers */
+ break;
+ default:
+ write_vp(par, i, par->vp[i]);
+ }
+ }
+}
+
+static void gx_restore_regs(struct gxfb_par *par)
+{
+ int i;
+
+ gx_set_dotpll((uint32_t) (par->msr.dotpll >> 32));
+ gx_restore_gfx_proc(par);
+ gx_restore_display_ctlr(par);
+ gx_restore_video_proc(par);
+
+ /* Flat Panel */
+ for (i = 0; i < ARRAY_SIZE(par->fp); i++) {
+ if (i != FP_PM && i != FP_RSVD_0)
+ write_fp(par, i, par->fp[i]);
+ }
+}
+
+static void gx_disable_graphics(struct gxfb_par *par)
+{
+ /* shut down the engine */
+ write_vp(par, VP_VCFG, par->vp[VP_VCFG] & ~VP_VCFG_VID_EN);
+ write_vp(par, VP_DCFG, par->vp[VP_DCFG] & ~(VP_DCFG_DAC_BL_EN |
+ VP_DCFG_VSYNC_EN | VP_DCFG_HSYNC_EN | VP_DCFG_CRT_EN));
+
+ /* turn off the flat panel */
+ write_fp(par, FP_PM, par->fp[FP_PM] & ~FP_PM_P);
+
+
+ /* turn off display */
+ write_dc(par, DC_UNLOCK, DC_UNLOCK_UNLOCK);
+ write_dc(par, DC_GENERAL_CFG, par->dc[DC_GENERAL_CFG] &
+ ~(DC_GENERAL_CFG_VIDE | DC_GENERAL_CFG_ICNE |
+ DC_GENERAL_CFG_CURE | DC_GENERAL_CFG_DFLE));
+ write_dc(par, DC_DISPLAY_CFG, par->dc[DC_DISPLAY_CFG] &
+ ~(DC_DISPLAY_CFG_VDEN | DC_DISPLAY_CFG_GDEN |
+ DC_DISPLAY_CFG_TGEN));
+ write_dc(par, DC_UNLOCK, DC_UNLOCK_LOCK);
+}
+
+static void gx_enable_graphics(struct gxfb_par *par)
+{
+ uint32_t fp;
+
+ fp = read_fp(par, FP_PM);
+ if (par->fp[FP_PM] & FP_PM_P) {
+ /* power on the panel if not already power{ed,ing} on */
+ if (!(fp & (FP_PM_PANEL_ON|FP_PM_PANEL_PWR_UP)))
+ write_fp(par, FP_PM, par->fp[FP_PM]);
+ } else {
+ /* power down the panel if not already power{ed,ing} down */
+ if (!(fp & (FP_PM_PANEL_OFF|FP_PM_PANEL_PWR_DOWN)))
+ write_fp(par, FP_PM, par->fp[FP_PM]);
+ }
+
+ /* turn everything on */
+ write_vp(par, VP_VCFG, par->vp[VP_VCFG]);
+ write_vp(par, VP_DCFG, par->vp[VP_DCFG]);
+ write_dc(par, DC_DISPLAY_CFG, par->dc[DC_DISPLAY_CFG]);
+ /* do this last; it will enable the FIFO load */
+ write_dc(par, DC_GENERAL_CFG, par->dc[DC_GENERAL_CFG]);
+
+ /* lock the door behind us */
+ write_dc(par, DC_UNLOCK, DC_UNLOCK_LOCK);
+}
+
+int gx_powerdown(struct fb_info *info)
+{
+ struct gxfb_par *par = info->par;
+
+ if (par->powered_down)
+ return 0;
+
+ gx_save_regs(par);
+ gx_disable_graphics(par);
+
+ par->powered_down = 1;
+ return 0;
+}
+
+int gx_powerup(struct fb_info *info)
+{
+ struct gxfb_par *par = info->par;
+
+ if (!par->powered_down)
+ return 0;
+
+ gx_restore_regs(par);
+ gx_enable_graphics(par);
+
+ par->powered_down = 0;
+ return 0;
+}
+
+#endif
diff --git a/drivers/video/geode/video_gx.c b/drivers/video/geode/video_gx.c
index febf09c6349..b8d52a8360d 100644
--- a/drivers/video/geode/video_gx.c
+++ b/drivers/video/geode/video_gx.c
@@ -16,9 +16,9 @@
#include <asm/io.h>
#include <asm/delay.h>
#include <asm/msr.h>
+#include <asm/geode.h>
-#include "geodefb.h"
-#include "video_gx.h"
+#include "gxfb.h"
/*
@@ -117,7 +117,7 @@ static const struct gx_pll_entry gx_pll_table_14MHz[] = {
{ 4357, 0, 0x0000057D }, /* 229.5000 */
};
-static void gx_set_dclk_frequency(struct fb_info *info)
+void gx_set_dclk_frequency(struct fb_info *info)
{
const struct gx_pll_entry *pll_table;
int pll_table_len;
@@ -178,110 +178,116 @@ static void gx_set_dclk_frequency(struct fb_info *info)
static void
gx_configure_tft(struct fb_info *info)
{
- struct geodefb_par *par = info->par;
+ struct gxfb_par *par = info->par;
unsigned long val;
unsigned long fp;
/* Set up the DF pad select MSR */
- rdmsrl(GX_VP_MSR_PAD_SELECT, val);
- val &= ~GX_VP_PAD_SELECT_MASK;
- val |= GX_VP_PAD_SELECT_TFT;
- wrmsrl(GX_VP_MSR_PAD_SELECT, val);
+ rdmsrl(MSR_GX_MSR_PADSEL, val);
+ val &= ~MSR_GX_MSR_PADSEL_MASK;
+ val |= MSR_GX_MSR_PADSEL_TFT;
+ wrmsrl(MSR_GX_MSR_PADSEL, val);
/* Turn off the panel */
- fp = readl(par->vid_regs + GX_FP_PM);
- fp &= ~GX_FP_PM_P;
- writel(fp, par->vid_regs + GX_FP_PM);
+ fp = read_fp(par, FP_PM);
+ fp &= ~FP_PM_P;
+ write_fp(par, FP_PM, fp);
/* Set timing 1 */
- fp = readl(par->vid_regs + GX_FP_PT1);
- fp &= GX_FP_PT1_VSIZE_MASK;
- fp |= info->var.yres << GX_FP_PT1_VSIZE_SHIFT;
- writel(fp, par->vid_regs + GX_FP_PT1);
+ fp = read_fp(par, FP_PT1);
+ fp &= FP_PT1_VSIZE_MASK;
+ fp |= info->var.yres << FP_PT1_VSIZE_SHIFT;
+ write_fp(par, FP_PT1, fp);
/* Timing 2 */
/* Set bits that are always on for TFT */
fp = 0x0F100000;
- /* Add sync polarity */
+ /* Configure sync polarity */
if (!(info->var.sync & FB_SYNC_VERT_HIGH_ACT))
- fp |= GX_FP_PT2_VSP;
+ fp |= FP_PT2_VSP;
if (!(info->var.sync & FB_SYNC_HOR_HIGH_ACT))
- fp |= GX_FP_PT2_HSP;
+ fp |= FP_PT2_HSP;
- writel(fp, par->vid_regs + GX_FP_PT2);
+ write_fp(par, FP_PT2, fp);
/* Set the dither control */
- writel(0x70, par->vid_regs + GX_FP_DFC);
+ write_fp(par, FP_DFC, FP_DFC_NFI);
/* Enable the FP data and power (in case the BIOS didn't) */
- fp = readl(par->vid_regs + GX_DCFG);
- fp |= GX_DCFG_FP_PWR_EN | GX_DCFG_FP_DATA_EN;
- writel(fp, par->vid_regs + GX_DCFG);
+ fp = read_vp(par, VP_DCFG);
+ fp |= VP_DCFG_FP_PWR_EN | VP_DCFG_FP_DATA_EN;
+ write_vp(par, VP_DCFG, fp);
/* Unblank the panel */
- fp = readl(par->vid_regs + GX_FP_PM);
- fp |= GX_FP_PM_P;
- writel(fp, par->vid_regs + GX_FP_PM);
+ fp = read_fp(par, FP_PM);
+ fp |= FP_PM_P;
+ write_fp(par, FP_PM, fp);
}
-static void gx_configure_display(struct fb_info *info)
+void gx_configure_display(struct fb_info *info)
{
- struct geodefb_par *par = info->par;
+ struct gxfb_par *par = info->par;
u32 dcfg, misc;
- /* Set up the MISC register */
-
- misc = readl(par->vid_regs + GX_MISC);
-
- /* Power up the DAC */
- misc &= ~(GX_MISC_A_PWRDN | GX_MISC_DAC_PWRDN);
-
- /* Disable gamma correction */
- misc |= GX_MISC_GAM_EN;
-
- writel(misc, par->vid_regs + GX_MISC);
-
/* Write the display configuration */
- dcfg = readl(par->vid_regs + GX_DCFG);
+ dcfg = read_vp(par, VP_DCFG);
/* Disable hsync and vsync */
- dcfg &= ~(GX_DCFG_VSYNC_EN | GX_DCFG_HSYNC_EN);
- writel(dcfg, par->vid_regs + GX_DCFG);
+ dcfg &= ~(VP_DCFG_VSYNC_EN | VP_DCFG_HSYNC_EN);
+ write_vp(par, VP_DCFG, dcfg);
/* Clear bits from existing mode. */
- dcfg &= ~(GX_DCFG_CRT_SYNC_SKW_MASK
- | GX_DCFG_CRT_HSYNC_POL | GX_DCFG_CRT_VSYNC_POL
- | GX_DCFG_VSYNC_EN | GX_DCFG_HSYNC_EN);
+ dcfg &= ~(VP_DCFG_CRT_SYNC_SKW
+ | VP_DCFG_CRT_HSYNC_POL | VP_DCFG_CRT_VSYNC_POL
+ | VP_DCFG_VSYNC_EN | VP_DCFG_HSYNC_EN);
/* Set default sync skew. */
- dcfg |= GX_DCFG_CRT_SYNC_SKW_DFLT;
+ dcfg |= VP_DCFG_CRT_SYNC_SKW_DEFAULT;
/* Enable hsync and vsync. */
- dcfg |= GX_DCFG_HSYNC_EN | GX_DCFG_VSYNC_EN;
+ dcfg |= VP_DCFG_HSYNC_EN | VP_DCFG_VSYNC_EN;
- /* Sync polarities. */
- if (info->var.sync & FB_SYNC_HOR_HIGH_ACT)
- dcfg |= GX_DCFG_CRT_HSYNC_POL;
- if (info->var.sync & FB_SYNC_VERT_HIGH_ACT)
- dcfg |= GX_DCFG_CRT_VSYNC_POL;
+ misc = read_vp(par, VP_MISC);
+
+ /* Disable gamma correction */
+ misc |= VP_MISC_GAM_EN;
+
+ if (par->enable_crt) {
+
+ /* Power up the CRT DACs */
+ misc &= ~(VP_MISC_APWRDN | VP_MISC_DACPWRDN);
+ write_vp(par, VP_MISC, misc);
+
+ /* Only change the sync polarities if we are running
+ * in CRT mode. The FP polarities will be handled in
+ * gxfb_configure_tft */
+ if (!(info->var.sync & FB_SYNC_HOR_HIGH_ACT))
+ dcfg |= VP_DCFG_CRT_HSYNC_POL;
+ if (!(info->var.sync & FB_SYNC_VERT_HIGH_ACT))
+ dcfg |= VP_DCFG_CRT_VSYNC_POL;
+ } else {
+ /* Power down the CRT DACs if in FP mode */
+ misc |= (VP_MISC_APWRDN | VP_MISC_DACPWRDN);
+ write_vp(par, VP_MISC, misc);
+ }
/* Enable the display logic */
/* Set up the DACS to blank normally */
- dcfg |= GX_DCFG_CRT_EN | GX_DCFG_DAC_BL_EN;
+ dcfg |= VP_DCFG_CRT_EN | VP_DCFG_DAC_BL_EN;
/* Enable the external DAC VREF? */
- writel(dcfg, par->vid_regs + GX_DCFG);
+ write_vp(par, VP_DCFG, dcfg);
/* Set up the flat panel (if it is enabled) */
@@ -289,59 +295,55 @@ static void gx_configure_display(struct fb_info *info)
gx_configure_tft(info);
}
-static int gx_blank_display(struct fb_info *info, int blank_mode)
+int gx_blank_display(struct fb_info *info, int blank_mode)
{
- struct geodefb_par *par = info->par;
+ struct gxfb_par *par = info->par;
u32 dcfg, fp_pm;
- int blank, hsync, vsync;
+ int blank, hsync, vsync, crt;
/* CRT power saving modes. */
switch (blank_mode) {
case FB_BLANK_UNBLANK:
- blank = 0; hsync = 1; vsync = 1;
+ blank = 0; hsync = 1; vsync = 1; crt = 1;
break;
case FB_BLANK_NORMAL:
- blank = 1; hsync = 1; vsync = 1;
+ blank = 1; hsync = 1; vsync = 1; crt = 1;
break;
case FB_BLANK_VSYNC_SUSPEND:
- blank = 1; hsync = 1; vsync = 0;
+ blank = 1; hsync = 1; vsync = 0; crt = 1;
break;
case FB_BLANK_HSYNC_SUSPEND:
- blank = 1; hsync = 0; vsync = 1;
+ blank = 1; hsync = 0; vsync = 1; crt = 1;
break;
case FB_BLANK_POWERDOWN:
- blank = 1; hsync = 0; vsync = 0;
+ blank = 1; hsync = 0; vsync = 0; crt = 0;
break;
default:
return -EINVAL;
}
- dcfg = readl(par->vid_regs + GX_DCFG);
- dcfg &= ~(GX_DCFG_DAC_BL_EN
- | GX_DCFG_HSYNC_EN | GX_DCFG_VSYNC_EN);
+ dcfg = read_vp(par, VP_DCFG);
+ dcfg &= ~(VP_DCFG_DAC_BL_EN | VP_DCFG_HSYNC_EN | VP_DCFG_VSYNC_EN |
+ VP_DCFG_CRT_EN);
if (!blank)
- dcfg |= GX_DCFG_DAC_BL_EN;
+ dcfg |= VP_DCFG_DAC_BL_EN;
if (hsync)
- dcfg |= GX_DCFG_HSYNC_EN;
+ dcfg |= VP_DCFG_HSYNC_EN;
if (vsync)
- dcfg |= GX_DCFG_VSYNC_EN;
- writel(dcfg, par->vid_regs + GX_DCFG);
+ dcfg |= VP_DCFG_VSYNC_EN;
+ if (crt)
+ dcfg |= VP_DCFG_CRT_EN;
+ write_vp(par, VP_DCFG, dcfg);
/* Power on/off flat panel. */
if (par->enable_crt == 0) {
- fp_pm = readl(par->vid_regs + GX_FP_PM);
+ fp_pm = read_fp(par, FP_PM);
if (blank_mode == FB_BLANK_POWERDOWN)
- fp_pm &= ~GX_FP_PM_P;
+ fp_pm &= ~FP_PM_P;
else
- fp_pm |= GX_FP_PM_P;
- writel(fp_pm, par->vid_regs + GX_FP_PM);
+ fp_pm |= FP_PM_P;
+ write_fp(par, FP_PM, fp_pm);
}
return 0;
}
-
-struct geode_vid_ops gx_vid_ops = {
- .set_dclk = gx_set_dclk_frequency,
- .configure_display = gx_configure_display,
- .blank_display = gx_blank_display,
-};
diff --git a/drivers/video/geode/video_gx.h b/drivers/video/geode/video_gx.h
deleted file mode 100644
index ce28d8f382d..00000000000
--- a/drivers/video/geode/video_gx.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Geode GX video device
- *
- * Copyright (C) 2006 Arcom Control Systems Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-#ifndef __VIDEO_GX_H__
-#define __VIDEO_GX_H__
-
-extern struct geode_vid_ops gx_vid_ops;
-
-/* GX Flatpanel control MSR */
-#define GX_VP_MSR_PAD_SELECT 0xC0002011
-#define GX_VP_PAD_SELECT_MASK 0x3FFFFFFF
-#define GX_VP_PAD_SELECT_TFT 0x1FFFFFFF
-
-/* Geode GX video processor registers */
-
-#define GX_DCFG 0x0008
-# define GX_DCFG_CRT_EN 0x00000001
-# define GX_DCFG_HSYNC_EN 0x00000002
-# define GX_DCFG_VSYNC_EN 0x00000004
-# define GX_DCFG_DAC_BL_EN 0x00000008
-# define GX_DCFG_FP_PWR_EN 0x00000040
-# define GX_DCFG_FP_DATA_EN 0x00000080
-# define GX_DCFG_CRT_HSYNC_POL 0x00000100
-# define GX_DCFG_CRT_VSYNC_POL 0x00000200
-# define GX_DCFG_CRT_SYNC_SKW_MASK 0x0001C000
-# define GX_DCFG_CRT_SYNC_SKW_DFLT 0x00010000
-# define GX_DCFG_VG_CK 0x00100000
-# define GX_DCFG_GV_GAM 0x00200000
-# define GX_DCFG_DAC_VREF 0x04000000
-
-/* Geode GX MISC video configuration */
-
-#define GX_MISC 0x50
-#define GX_MISC_GAM_EN 0x00000001
-#define GX_MISC_DAC_PWRDN 0x00000400
-#define GX_MISC_A_PWRDN 0x00000800
-
-/* Geode GX flat panel display control registers */
-
-#define GX_FP_PT1 0x0400
-#define GX_FP_PT1_VSIZE_MASK 0x7FF0000
-#define GX_FP_PT1_VSIZE_SHIFT 16
-
-#define GX_FP_PT2 0x408
-#define GX_FP_PT2_VSP (1 << 23)
-#define GX_FP_PT2_HSP (1 << 22)
-
-#define GX_FP_PM 0x410
-# define GX_FP_PM_P 0x01000000
-
-#define GX_FP_DFC 0x418
-
-/* Geode GX clock control MSRs */
-
-#define MSR_GLCP_SYS_RSTPLL 0x4c000014
-# define MSR_GLCP_SYS_RSTPLL_DOTPREDIV2 (0x0000000000000002ull)
-# define MSR_GLCP_SYS_RSTPLL_DOTPREMULT2 (0x0000000000000004ull)
-# define MSR_GLCP_SYS_RSTPLL_DOTPOSTDIV3 (0x0000000000000008ull)
-
-#define MSR_GLCP_DOTPLL 0x4c000015
-# define MSR_GLCP_DOTPLL_DOTRESET (0x0000000000000001ull)
-# define MSR_GLCP_DOTPLL_BYPASS (0x0000000000008000ull)
-# define MSR_GLCP_DOTPLL_LOCK (0x0000000002000000ull)
-
-#endif /* !__VIDEO_GX_H__ */