[PATCH] gxfb: Add a command line option 'noclear'
Jordan Crouse
jordan.crouse at amd.unroutablecom
Tue Jan 2 10:59:18 EST 2007
Commit: 6963b1e0dba2f7b743b15aae3cdb109607875230
Parent: f8bb8fa159d91b4ba31be74cba650b35a1c97f21
commit 6963b1e0dba2f7b743b15aae3cdb109607875230
Author: Jordan Crouse <jordan.crouse at amd.com>
AuthorDate: Thu Dec 21 14:14:46 2006 -0700
Commit: Jordan Crouse <jordan.crouse at amd.com>
CommitDate: Thu Dec 21 14:17:53 2006 -0700
[PATCH] gxfb: Add a command line option 'noclear'
Add a command line option to not clear the framebuffer space - this assumes
that the firmware has already initialized the memory to something sane, so
we avoid the extra time to clear the screen.
Signed-off-by: Jordan Crouse <jordan.crouse at amd.com>
---
drivers/video/geode/display_gx.c | 58 +++++++------
drivers/video/geode/geodefb.h | 2
drivers/video/geode/gxfb_core.c | 18 +++-
drivers/video/geode/video_gx.c | 168 +++++++++++++++++++++++---------------
4 files changed, 149 insertions(+), 97 deletions(-)
diff --git a/drivers/video/geode/display_gx.c b/drivers/video/geode/display_gx.c
index 8cd7572..2f2985b 100644
--- a/drivers/video/geode/display_gx.c
+++ b/drivers/video/geode/display_gx.c
@@ -21,6 +21,13 @@ #include <asm/delay.h>
#include "geodefb.h"
#include "display_gx.h"
+static inline void rmwl(u32 val, u32 *reg)
+{
+ u32 in = readl(reg);
+ if (in != val)
+ writel(val, reg);
+}
+
unsigned int gx_frame_buffer_size(void)
{
unsigned int val;
@@ -56,23 +63,23 @@ static void gx_set_mode(struct fb_info *
gcfg = readl(par->dc_regs + DC_GENERAL_CFG);
dcfg = readl(par->dc_regs + DC_DISPLAY_CFG);
- /* Disable the timing generator. */
- dcfg &= ~(DC_DCFG_TGEN);
- writel(dcfg, par->dc_regs + DC_DISPLAY_CFG);
+ /* Programming the clock is costly and ugly, so avoid if if we can */
- /* Wait for pending memory requests before disabling the FIFO load. */
- udelay(100);
+ if (par->curdclk != info->var.pixclock) {
+ /* Disable the timing generator. */
+ dcfg &= ~(DC_DCFG_TGEN);
+ writel(dcfg, par->dc_regs + DC_DISPLAY_CFG);
- /* Disable FIFO load and compression. */
- gcfg &= ~(DC_GCFG_DFLE | DC_GCFG_CMPE | DC_GCFG_DECE);
- writel(gcfg, par->dc_regs + DC_GENERAL_CFG);
+ /* Wait for pending memory requests before disabling the FIFO load. */
+ udelay(100);
- /* Setup DCLK and its divisor. */
- par->vid_ops->set_dclk(info);
+ /* Disable FIFO load and compression. */
+ gcfg &= ~(DC_GCFG_DFLE | DC_GCFG_CMPE | DC_GCFG_DECE);
+ writel(gcfg, par->dc_regs + DC_GENERAL_CFG);
- /*
- * Setup new mode.
- */
+ /* Setup DCLK and its divisor. */
+ par->vid_ops->set_dclk(info);
+ }
/* Clear all unused feature bits. */
gcfg &= DC_GCFG_YUVM | DC_GCFG_VDSE;
@@ -83,13 +90,13 @@ static void gx_set_mode(struct fb_info *
gcfg |= (6 << DC_GCFG_DFHPEL_POS) | (5 << DC_GCFG_DFHPSL_POS) | DC_GCFG_DFLE;
/* Framebuffer start offset. */
- writel(0, par->dc_regs + DC_FB_ST_OFFSET);
+ rmwl(0, par->dc_regs + DC_FB_ST_OFFSET);
/* 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);
+ rmwl(info->fix.line_length >> 3, par->dc_regs + DC_GFX_PITCH);
+ rmwl(((info->var.xres * info->var.bits_per_pixel/8) >> 3) + 2,
+ par->dc_regs + DC_LINE_SIZE);
/* Enable graphics and video data and unmask address lines. */
dcfg |= DC_DCFG_GDEN | DC_DCFG_VDEN | DC_DCFG_A20M | DC_DCFG_A18M;
@@ -127,17 +134,16 @@ static void gx_set_mode(struct fb_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);
- 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);
+ rmwl((hactive - 1) | ((htotal - 1) << 16), par->dc_regs + DC_H_ACTIVE_TIMING);
+ rmwl((hblankstart - 1) | ((hblankend - 1) << 16), par->dc_regs + DC_H_BLANK_TIMING);
+ rmwl((hsyncstart - 1) | ((hsyncend - 1) << 16), par->dc_regs + DC_H_SYNC_TIMING);
+ rmwl((vactive - 1) | ((vtotal - 1) << 16), par->dc_regs + DC_V_ACTIVE_TIMING);
+ rmwl((vblankstart - 1) | ((vblankend - 1) << 16), par->dc_regs + DC_V_BLANK_TIMING);
+ rmwl((vsyncstart - 1) | ((vsyncend - 1) << 16), par->dc_regs + DC_V_SYNC_TIMING);
/* Write final register values. */
- writel(dcfg, par->dc_regs + DC_DISPLAY_CFG);
- writel(gcfg, par->dc_regs + DC_GENERAL_CFG);
+ rmwl(dcfg, par->dc_regs + DC_DISPLAY_CFG);
+ rmwl(gcfg, par->dc_regs + DC_GENERAL_CFG);
par->vid_ops->configure_display(info);
diff --git a/drivers/video/geode/geodefb.h b/drivers/video/geode/geodefb.h
index ae04820..979bc2b 100644
--- a/drivers/video/geode/geodefb.h
+++ b/drivers/video/geode/geodefb.h
@@ -21,6 +21,7 @@ struct geode_dc_ops {
struct geode_vid_ops {
void (*set_dclk)(struct fb_info *);
+ unsigned int (*get_dclk)(struct fb_info *);
void (*configure_display)(struct fb_info *);
int (*blank_display)(struct fb_info *, int blank_mode);
};
@@ -29,6 +30,7 @@ struct geodefb_par {
int enable_crt;
int panel_x; /* dimensions of an attached flat panel, non-zero => enable panel */
int panel_y;
+ unsigned int curdclk; /* Used by GX to avoid unnessesary clock switching */
void __iomem *dc_regs;
void __iomem *vid_regs;
struct geode_dc_ops *dc_ops;
diff --git a/drivers/video/geode/gxfb_core.c b/drivers/video/geode/gxfb_core.c
index f95bc03..56f4d1a 100644
--- a/drivers/video/geode/gxfb_core.c
+++ b/drivers/video/geode/gxfb_core.c
@@ -40,6 +40,7 @@ #define FBIOSGAMMA _IOW('F', 0x20, void
#define FBIOGGAMMA _IOW('F', 0x21, void *)
static char *mode_option;
+static int noclear;
/* Modes relevant to the GX (taken from modedb.c) */
@@ -316,7 +317,7 @@ static int gxfb_ioctl( struct fb_info *i
case FBIOGGAMMA:
if (readl(par->vid_regs + GX_MISC) & GX_MISC_GAM_EN)
return -EINVAL;
-
+
memset(gamma, 0, GXFB_GAMMA_SIZE);
writel(0, par->vid_regs + GX_GAR);
@@ -426,6 +427,10 @@ static int __init gxfb_probe(struct pci_
else
par->enable_crt = 1;
+ /* Get the current dotclock */
+
+ par->curdclk = (par->vid_ops->get_dclk) ? par->vid_ops->get_dclk(info) : 0;
+
/* We need to determine a display mode right now, so we will
* check to see if the DCON was previously detected by the BIOS
* and use that to make our mode database decision.
@@ -450,8 +455,11 @@ #endif
goto err;
}
- /* Clear the frame buffer of garbage. */
- memset_io(info->screen_base, 0, info->fix.smem_len);
+ /* Clear the screen of garbage, unless noclear was specified,
+ * in which case we assume the user knows what he is doing */
+
+ if (!noclear)
+ memset_io(info->screen_base, 0, info->fix.smem_len);
gxfb_check_var(&info->var, info);
gxfb_set_par(info);
@@ -534,7 +542,9 @@ static int __init gxfb_setup(char *optio
if (!strncmp(opt, "fbsize:", 7))
fbsize = simple_strtoul(opt+7, NULL, 0);
- else
+ else if (!strcmp(opt, "noclear"))
+ noclear = 1;
+ else
mode_option = opt;
}
diff --git a/drivers/video/geode/video_gx.c b/drivers/video/geode/video_gx.c
index ff224de..caa9983 100644
--- a/drivers/video/geode/video_gx.c
+++ b/drivers/video/geode/video_gx.c
@@ -120,6 +120,7 @@ static const struct gx_pll_entry gx_pll_
static void gx_set_dclk_frequency(struct fb_info *info)
{
+ struct geodefb_par *par = info->par;
const struct gx_pll_entry *pll_table;
int pll_table_len;
int i, best_i;
@@ -174,70 +175,106 @@ static void gx_set_dclk_frequency(struct
do {
rdmsrl(MSR_GLCP_DOTPLL, dotpll);
} while (timeout-- && !(dotpll & MSR_GLCP_DOTPLL_LOCK));
+
+ par->curdclk = pll_table[best_i].dotpll_value;
+}
+
+/* Find out the current clock - we will use this information to avoid
+ re-programming it if we don't need to */
+
+static unsigned int gx_get_dclk(struct fb_info *info)
+{
+ const struct gx_pll_entry *pll_table;
+ int pll_table_len;
+ u64 dotpll;
+ int i;
+
+ if (cpu_data->x86_mask == 1) {
+ pll_table = gx_pll_table_14MHz;
+ pll_table_len = ARRAY_SIZE(gx_pll_table_14MHz);
+ } else {
+ pll_table = gx_pll_table_48MHz;
+ pll_table_len = ARRAY_SIZE(gx_pll_table_48MHz);
+ }
+
+ rdmsrl(MSR_GLCP_DOTPLL, dotpll);
+
+ for(i = 0; i < pll_table_len; i++) {
+ if (pll_table[i].dotpll_value == (u32) (dotpll >> 32))
+ break;
+ }
+
+ return (i == pll_table_len) ? 0 : pll_table[i].pixclock;
}
+#define CMP(val, mask, res) (((val) & (mask)) == (res))
+
static void
gx_configure_tft(struct fb_info *info) {
struct geodefb_par *par = info->par;
- unsigned long val;
- unsigned long fp;
+ u32 val, fp, fp1, fp2, sync = 0;
/* 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);
- /* Turn off the panel */
+ if ((val & GX_VP_PAD_SELECT_MASK) != GX_VP_PAD_SELECT_TFT) {
+ val &= ~GX_VP_PAD_SELECT_MASK;
+ val |= GX_VP_PAD_SELECT_TFT;
+ wrmsrl(GX_VP_MSR_PAD_SELECT, val);
+ }
- fp = readl(par->vid_regs + GX_FP_PM);
- fp &= ~GX_FP_PM_P;
- writel(fp, par->vid_regs + GX_FP_PM);
+ if (!(info->var.sync & FB_SYNC_VERT_HIGH_ACT))
+ sync |= GX_FP_PT2_VSP;
- /* Set timing 1 */
+ if (!(info->var.sync & FB_SYNC_HOR_HIGH_ACT))
+ sync |= GX_FP_PT2_HSP;
- 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);
+ /* We only need to turn off the panel if something changed */
- /* Timing 2 */
- /* Set bits that are always on for TFT */
+ fp1 = readl(par->vid_regs + GX_FP_PT1);
+ fp2 = readl(par->vid_regs + GX_FP_PT2);
- fp = 0x0F100000;
+ if (!CMP(fp1, GX_FP_PT1_VSIZE_MASK, info->var.yres << GX_FP_PT1_VSIZE_SHIFT) ||
+ (fp2 != (0x0F100000 | sync))) {
- /* Configure sync polarity */
+ /* Turn off the panel */
- if (!(info->var.sync & FB_SYNC_VERT_HIGH_ACT))
- fp |= GX_FP_PT2_VSP;
+ fp = readl(par->vid_regs + GX_FP_PM);
+ fp &= ~GX_FP_PM_P;
+ writel(fp, par->vid_regs + GX_FP_PM);
- if (!(info->var.sync & FB_SYNC_HOR_HIGH_ACT))
- fp |= GX_FP_PT2_HSP;
+ /* Timing 1 */
+ fp1 &= GX_FP_PT1_VSIZE_MASK;
+ fp1 |= info->var.yres << GX_FP_PT1_VSIZE_SHIFT;
+ writel(fp, par->vid_regs + GX_FP_PT1);
- writel(fp, par->vid_regs + GX_FP_PT2);
+ /* Timing 2 */
+ writel(0x0F100000 | sync, par->vid_regs + GX_FP_PT2);
+ }
/* Set the dither control */
- writel(0x70, par->vid_regs + GX_FP_DFC);
-
- /* 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);
+ if (readl(par->vid_regs + GX_FP_DFC) != 0x70) {
+ writel(0x70, par->vid_regs + GX_FP_DFC);
+ }
/* Unblank the panel */
fp = readl(par->vid_regs + GX_FP_PM);
- fp |= GX_FP_PM_P;
- writel(fp, par->vid_regs + GX_FP_PM);
+
+ if (!(fp & GX_FP_PM_P)) {
+ writel(fp | GX_FP_PM_P, par->vid_regs + GX_FP_PM);
+ }
}
+#define DCFG_DEFAULT_VAL GX_DCFG_CRT_SYNC_SKW_DFLT | GX_DCFG_HSYNC_EN | GX_DCFG_VSYNC_EN | \
+GX_DCFG_CRT_EN | GX_DCFG_DAC_BL_EN
+
static void gx_configure_display(struct fb_info *info)
{
struct geodefb_par *par = info->par;
- u32 dcfg, misc;
+ u32 dcfg, misc, sync = 0;
/* Set up the MISC register */
misc = readl(par->vid_regs + GX_MISC);
@@ -248,50 +285,46 @@ static void gx_configure_display(struct
to have either disabled it or loaded a table by now */
/* Power up the DAC */
- misc &= ~(GX_MISC_A_PWRDN | GX_MISC_DAC_PWRDN);
+ if (misc & ( GX_MISC_A_PWRDN | GX_MISC_DAC_PWRDN)) {
+ misc &= ~(GX_MISC_A_PWRDN | GX_MISC_DAC_PWRDN);
+ writel(misc, par->vid_regs + GX_MISC);
+ }
+
+ if (par->enable_crt) {
+ if (!(info->var.sync & FB_SYNC_HOR_HIGH_ACT))
+ sync |= GX_DCFG_CRT_HSYNC_POL;
- writel(misc, par->vid_regs + GX_MISC);
+ if (!(info->var.sync & FB_SYNC_VERT_HIGH_ACT))
+ sync |= GX_DCFG_CRT_VSYNC_POL;
+ }
/* Write the display configuration */
dcfg = readl(par->vid_regs + GX_DCFG);
- /* Disable hsync and vsync */
- dcfg &= ~(GX_DCFG_VSYNC_EN | GX_DCFG_HSYNC_EN);
- writel(dcfg, par->vid_regs + GX_DCFG);
+ if (!CMP(dcfg, DCFG_DEFAULT_VAL | GX_DCFG_CRT_HSYNC_POL | GX_DCFG_CRT_VSYNC_POL,
+ DCFG_DEFAULT_VAL | sync)) {
- /* 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);
+ /* Disable hsync and vsync */
+ dcfg &= ~(GX_DCFG_VSYNC_EN | GX_DCFG_HSYNC_EN);
+ writel(dcfg, par->vid_regs + GX_DCFG);
- /* Set default sync skew. */
- dcfg |= GX_DCFG_CRT_SYNC_SKW_DFLT;
+ /* 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);
- /* Enable hsync and vsync. */
- dcfg |= GX_DCFG_HSYNC_EN | GX_DCFG_VSYNC_EN;
+ /* Set default sync skew. */
+ dcfg |= GX_DCFG_CRT_SYNC_SKW_DFLT;
- /* Only change the sync polarities if we are running
- * in CRT mode. The FP polarities will be handled in
- * gxfb_configure_tft
- */
+ /* Enable hsync and vsync. */
+ dcfg |= GX_DCFG_HSYNC_EN | GX_DCFG_VSYNC_EN;
- if (par->enable_crt) {
- if (!(info->var.sync & FB_SYNC_HOR_HIGH_ACT))
- dcfg |= GX_DCFG_CRT_HSYNC_POL;
+ /* Enable the display logic */
+ dcfg |= GX_DCFG_CRT_EN | GX_DCFG_DAC_BL_EN;
- if (!(info->var.sync & FB_SYNC_VERT_HIGH_ACT))
- dcfg |= GX_DCFG_CRT_VSYNC_POL;
+ writel(dcfg, par->vid_regs + GX_DCFG);
}
- /* Enable the display logic */
- /* Set up the DACS to blank normally */
-
- dcfg |= GX_DCFG_CRT_EN | GX_DCFG_DAC_BL_EN;
-
- /* Enable the external DAC VREF? */
-
- writel(dcfg, par->vid_regs + GX_DCFG);
-
/* Set up the flat panel (if it is enabled) */
if (par->enable_crt == 0)
@@ -338,7 +371,7 @@ int gxfb_powerdown(struct fb_info *info)
/* Disable video, icon, cursor and the FIFO */
gx_pm_regs[DC_GCFG] = readl(par->dc_regs + DC_GENERAL_CFG);
writel(gx_pm_regs[DC_GCFG] & ~0x0F, par->dc_regs + DC_GENERAL_CFG);
-
+
/* Disable video data enable, graphics data enable and the timing generator */
gx_pm_regs[DC_DCFG] = readl(par->dc_regs + DC_DISPLAY_CFG);
writel(gx_pm_regs[DC_DCFG] & ~0x19, par->dc_regs + DC_DISPLAY_CFG);
@@ -347,7 +380,7 @@ int gxfb_powerdown(struct fb_info *info)
return 0;
}
-
+
int gxfb_pre_powerup(struct fb_info *info)
{
@@ -358,7 +391,7 @@ int gxfb_pre_powerup(struct fb_info *inf
writel(gx_pm_regs[VC_VCFG], par->vid_regs + GX_VCFG);
writel(gx_pm_regs[VC_DCFG], par->vid_regs + GX_DCFG);
- writel(gx_pm_regs[VC_FP_PM], par->vid_regs + GX_FP_PM);
+ writel(gx_pm_regs[VC_FP_PM], par->vid_regs + GX_FP_PM);
msleep(64);
return 0;
@@ -433,6 +466,7 @@ static int gx_blank_display(struct fb_in
struct geode_vid_ops gx_vid_ops = {
.set_dclk = gx_set_dclk_frequency,
+ .get_dclk = gx_get_dclk,
.configure_display = gx_configure_display,
.blank_display = gx_blank_display,
};
More information about the Commits-kernel
mailing list