[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