Commit a2ca74ad authored by Russell King's avatar Russell King

[ARM] Update clps711x fbcon driver.

parent fa1625e7
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include <linux/fb.h> #include <linux/fb.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <linux/delay.h>
#include <video/fbcon.h> #include <video/fbcon.h>
...@@ -37,7 +38,7 @@ ...@@ -37,7 +38,7 @@
struct fb_info *cfb; struct fb_info *cfb;
#define CMAP_SIZE 16 #define CMAP_MAX_SIZE 16
/* The /proc entry for the backlight. */ /* The /proc entry for the backlight. */
static struct proc_dir_entry *clps7111fb_backlight_proc_entry = NULL; static struct proc_dir_entry *clps7111fb_backlight_proc_entry = NULL;
...@@ -47,6 +48,13 @@ static int clps7111fb_proc_backlight_read(char *page, char **start, off_t off, ...@@ -47,6 +48,13 @@ static int clps7111fb_proc_backlight_read(char *page, char **start, off_t off,
static int clps7111fb_proc_backlight_write(struct file *file, static int clps7111fb_proc_backlight_write(struct file *file,
const char *buffer, unsigned long count, void *data); const char *buffer, unsigned long count, void *data);
/*
* LCD AC Prescale. This comes from the LCD panel manufacturers specifications.
* This determines how many clocks + 1 of CL1 before the M signal toggles.
* The number of lines on the display must not be divisible by this number.
*/
static unsigned int lcd_ac_prescale = 13;
/* /*
* Set a single color register. Return != 0 for invalid regno. * Set a single color register. Return != 0 for invalid regno.
*/ */
...@@ -56,7 +64,7 @@ clps7111fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, ...@@ -56,7 +64,7 @@ clps7111fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
{ {
unsigned int level, mask, shift, pal; unsigned int level, mask, shift, pal;
if (regno >= CMAP_SIZE) if (regno >= (1 << info->var.bits_per_pixel))
return 1; return 1;
/* gray = 0.30*R + 0.58*G + 0.11*B */ /* gray = 0.30*R + 0.58*G + 0.11*B */
...@@ -104,6 +112,8 @@ clps7111fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) ...@@ -104,6 +112,8 @@ clps7111fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
if (var->bits_per_pixel > 4) if (var->bits_per_pixel > 4)
return -EINVAL; return -EINVAL;
return 0;
} }
/* /*
...@@ -112,32 +122,46 @@ clps7111fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) ...@@ -112,32 +122,46 @@ clps7111fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
static int static int
clps7111fb_set_par(struct fb_info *info) clps7111fb_set_par(struct fb_info *info)
{ {
unsigned int lcdcon, syscon; unsigned int lcdcon, syscon, pixclock;
switch (var->bits_per_pixel) { switch (info->var.bits_per_pixel) {
case 1: case 1:
info->fix.visual = FB_VISUAL_MONO01; info->fix.visual = FB_VISUAL_MONO01;
break; break;
case 2: case 2:
info->fix.visual = FB_VISUAL_PSEUDOCOLOR; info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
break; break;
case 4: case 4:
info->fix.visual = FB_VISUAL_PSEUDOCOLOR; info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
break; break;
} }
info->fix.line_length = info->var.xres_virtual * info->var.bits_per_pixel / 8; info->fix.line_length = info->var.xres_virtual * info->var.bits_per_pixel / 8;
/*
* LCDCON must only be changed while the LCD is disabled
*/
lcdcon = (info->var.xres_virtual * info->var.yres_virtual * info->var.bits_per_pixel) / 128 - 1; lcdcon = (info->var.xres_virtual * info->var.yres_virtual * info->var.bits_per_pixel) / 128 - 1;
lcdcon |= ((info->var.xres_virtual / 16) - 1) << 13; lcdcon |= ((info->var.xres_virtual / 16) - 1) << 13;
lcdcon |= 2 << 19; lcdcon |= lcd_ac_prescale << 25;
lcdcon |= 13 << 25;
lcdcon |= LCDCON_GSEN; /*
lcdcon |= LCDCON_GSMD; * Calculate pixel prescale value from the pixclock. This is:
* 36.864MHz / pixclock_mhz - 1.
* However, pixclock is in picoseconds, so this ends up being:
* 36864000 * pixclock_ps / 10^12 - 1
* and this will overflow the 32-bit math. We perform this as
* (9 * 4096000 == 36864000):
* pixclock_ps * 9 * (4096000 / 10^12) - 1
*/
pixclock = 9 * info->var.pixclock / 244140 - 1;
lcdcon |= pixclock << 19;
if (info->var.bits_per_pixel == 4)
lcdcon |= LCDCON_GSMD;
if (info->var.bits_per_pixel >= 2)
lcdcon |= LCDCON_GSEN;
/*
* LCDCON must only be changed while the LCD is disabled
*/
syscon = clps_readl(SYSCON1); syscon = clps_readl(SYSCON1);
clps_writel(syscon & ~SYSCON1_LCDEN, SYSCON1); clps_writel(syscon & ~SYSCON1_LCDEN, SYSCON1);
clps_writel(lcdcon, LCDCON); clps_writel(lcdcon, LCDCON);
...@@ -149,8 +173,6 @@ static int clps7111fb_blank(int blank, struct fb_info *info) ...@@ -149,8 +173,6 @@ static int clps7111fb_blank(int blank, struct fb_info *info)
{ {
if (blank) { if (blank) {
if (machine_is_edb7211()) { if (machine_is_edb7211()) {
int i;
/* Turn off the LCD backlight. */ /* Turn off the LCD backlight. */
clps_writeb(clps_readb(PDDR) & ~EDB_PD3_LCDBL, PDDR); clps_writeb(clps_readb(PDDR) & ~EDB_PD3_LCDBL, PDDR);
...@@ -169,8 +191,6 @@ static int clps7111fb_blank(int blank, struct fb_info *info) ...@@ -169,8 +191,6 @@ static int clps7111fb_blank(int blank, struct fb_info *info)
} }
} else { } else {
if (machine_is_edb7211()) { if (machine_is_edb7211()) {
int i;
/* Power up the LCD controller. */ /* Power up the LCD controller. */
clps_writel(clps_readl(SYSCON1) | SYSCON1_LCDEN, clps_writel(clps_readl(SYSCON1) | SYSCON1_LCDEN,
SYSCON1); SYSCON1);
...@@ -256,6 +276,93 @@ clps7111fb_proc_backlight_write(struct file *file, const char *buffer, ...@@ -256,6 +276,93 @@ clps7111fb_proc_backlight_write(struct file *file, const char *buffer,
return count; return count;
} }
static void __init clps711x_guess_lcd_params(struct fb_info *info)
{
unsigned int lcdcon, syscon, size;
unsigned long phys_base = PAGE_OFFSET;
void *virt_base = (void *)PAGE_OFFSET;
info->var.xres_virtual = 640;
info->var.yres_virtual = 240;
info->var.bits_per_pixel = 4;
info->var.activate = FB_ACTIVATE_NOW;
info->var.height = -1;
info->var.width = -1;
info->var.pixclock = 93006; /* 10.752MHz pixel clock */
/*
* If the LCD controller is already running, decode the values
* in LCDCON to xres/yres/bpp/pixclock/acprescale
*/
syscon = clps_readl(SYSCON1);
if (syscon & SYSCON1_LCDEN) {
lcdcon = clps_readl(LCDCON);
/*
* Decode GSMD and GSEN bits to bits per pixel
*/
switch (lcdcon & (LCDCON_GSMD | LCDCON_GSEN)) {
case LCDCON_GSMD | LCDCON_GSEN:
info->var.bits_per_pixel = 4;
break;
case LCDCON_GSEN:
info->var.bits_per_pixel = 2;
break;
default:
info->var.bits_per_pixel = 1;
break;
}
/*
* Decode xres/yres
*/
info->var.xres_virtual = (((lcdcon >> 13) & 0x3f) + 1) * 16;
info->var.yres_virtual = (((lcdcon & 0x1fff) + 1) * 128) /
(info->var.xres_virtual *
info->var.bits_per_pixel);
/*
* Calculate pixclock
*/
info->var.pixclock = (((lcdcon >> 19) & 0x3f) + 1) * 244140 / 9;
/*
* Grab AC prescale
*/
lcd_ac_prescale = (lcdcon >> 25) & 0x1f;
}
info->var.xres = info->var.xres_virtual;
info->var.yres = info->var.yres_virtual;
info->var.grayscale = info->var.bits_per_pixel > 1;
size = info->var.xres * info->var.yres * info->var.bits_per_pixel / 8;
/*
* Might be worth checking to see if we can use the on-board
* RAM if size here...
* CLPS7110 - no on-board SRAM
* EP7212 - 38400 bytes
*/
if (size < 38400) {
printk(KERN_INFO "CLPS711xFB: could use on-board SRAM?\n");
}
if ((syscon & SYSCON1_LCDEN) == 0) {
/*
* The display isn't running. Ensure that
* the display memory is empty.
*/
memset(virt_base, 0, size);
}
info->screen_base = virt_base;
info->fix.smem_start = phys_base;
info->fix.smem_len = PAGE_ALIGN(size);
info->fix.type = FB_TYPE_PACKED_PIXELS;
}
int __init clps711xfb_init(void) int __init clps711xfb_init(void)
{ {
...@@ -266,34 +373,19 @@ int __init clps711xfb_init(void) ...@@ -266,34 +373,19 @@ int __init clps711xfb_init(void)
goto out; goto out;
memset(cfb, 0, sizeof(*cfb) + sizeof(struct display)); memset(cfb, 0, sizeof(*cfb) + sizeof(struct display));
memset((void *)PAGE_OFFSET, 0, 0x14000); strcpy(cfb->fix.id, "clps711x");
cfb->currcon = -1; cfb->currcon = -1;
strcpy(cfb->fix.id, "clps7111");
cfb->screen_base = (void *)PAGE_OFFSET;
cfb->fix.smem_start = PAGE_OFFSET;
cfb->fix.smem_len = 0x14000;
cfb->fix.type = FB_TYPE_PACKED_PIXELS;
cfb->var.xres = 640;
cfb->var.xres_virtual = 640;
cfb->var.yres = 240;
cfb->var.yres_virtual = 240;
cfb->var.bits_per_pixel = 4;
cfb->var.grayscale = 1;
cfb->var.activate = FB_ACTIVATE_NOW;
cfb->var.height = -1;
cfb->var.width = -1;
cfb->fbops = &clps7111fb_ops; cfb->fbops = &clps7111fb_ops;
cfb->changevar = NULL; cfb->changevar = NULL;
cfb->switch_con = gen_switch; cfb->switch_con = gen_switch;
cfb->updatevar = gen_update_var; cfb->updatevar = gen_update_var;
cfb->flags = FBINFO_FLAG_DEFAULT; cfb->flags = FBINFO_FLAG_DEFAULT;
cfb->disp = (struct display *)(cfb + 1); cfb->disp = (struct display *)(cfb + 1);
fb_alloc_cmap(&cfb->cmap, CMAP_SIZE, 0); clps711x_guess_lcd_params(cfb);
fb_alloc_cmap(&cfb->cmap, CMAP_MAX_SIZE, 0);
/* Register the /proc entries. */ /* Register the /proc entries. */
clps7111fb_backlight_proc_entry = create_proc_entry("backlight", 0444, clps7111fb_backlight_proc_entry = create_proc_entry("backlight", 0444,
...@@ -317,8 +409,6 @@ int __init clps711xfb_init(void) ...@@ -317,8 +409,6 @@ int __init clps711xfb_init(void)
} }
if (machine_is_edb7211()) { if (machine_is_edb7211()) {
int i;
/* Power up the LCD panel. */ /* Power up the LCD panel. */
clps_writeb(clps_readb(PDDR) | EDB_PD2_LCDEN, PDDR); clps_writeb(clps_readb(PDDR) | EDB_PD2_LCDEN, PDDR);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment