linux/drivers/video/clps711xfb.c
<<
>>
Prefs
   1/*
   2 *  linux/drivers/video/clps711xfb.c
   3 *
   4 *  Copyright (C) 2000-2001 Deep Blue Solutions Ltd.
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by
   8 * the Free Software Foundation; either version 2 of the License, or
   9 * (at your option) any later version.
  10 *
  11 * This program is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 * GNU General Public License for more details.
  15 *
  16 * You should have received a copy of the GNU General Public License
  17 * along with this program; if not, write to the Free Software
  18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  19 *
  20 *  Framebuffer driver for the CLPS7111 and EP7212 processors.
  21 */
  22#include <linux/mm.h>
  23#include <linux/module.h>
  24#include <linux/kernel.h>
  25#include <linux/seq_file.h>
  26#include <linux/slab.h>
  27#include <linux/fb.h>
  28#include <linux/init.h>
  29#include <linux/proc_fs.h>
  30#include <linux/delay.h>
  31
  32#include <mach/hardware.h>
  33#include <asm/mach-types.h>
  34#include <linux/uaccess.h>
  35
  36#include <mach/syspld.h>
  37
  38struct fb_info  *cfb;
  39
  40#define CMAP_MAX_SIZE   16
  41
  42/*
  43 * LCD AC Prescale.  This comes from the LCD panel manufacturers specifications.
  44 * This determines how many clocks + 1 of CL1 before the M signal toggles.
  45 * The number of lines on the display must not be divisible by this number.
  46 */
  47static unsigned int lcd_ac_prescale = 13;
  48
  49/*
  50 *    Set a single color register. Return != 0 for invalid regno.
  51 */
  52static int
  53clps7111fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
  54                     u_int transp, struct fb_info *info)
  55{
  56        unsigned int level, mask, shift, pal;
  57
  58        if (regno >= (1 << info->var.bits_per_pixel))
  59                return 1;
  60
  61        /* gray = 0.30*R + 0.58*G + 0.11*B */
  62        level = (red * 77 + green * 151 + blue * 28) >> 20;
  63
  64        /*
  65         * On an LCD, a high value is dark, while a low value is light. 
  66         * So we invert the level.
  67         *
  68         * This isn't true on all machines, so we only do it on EDB7211.
  69         *  --rmk
  70         */
  71        if (machine_is_edb7211()) {
  72                level = 15 - level;
  73        }
  74
  75        shift = 4 * (regno & 7);
  76        level <<= shift;
  77        mask  = 15 << shift;
  78        level &= mask;
  79
  80        regno = regno < 8 ? PALLSW : PALMSW;
  81
  82        pal = clps_readl(regno);
  83        pal = (pal & ~mask) | level;
  84        clps_writel(pal, regno);
  85
  86        return 0;
  87}
  88
  89/*
  90 * Validate the purposed mode.
  91 */     
  92static int
  93clps7111fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
  94{
  95        var->transp.msb_right   = 0;
  96        var->transp.offset      = 0;
  97        var->transp.length      = 0;
  98        var->red.msb_right      = 0;
  99        var->red.offset         = 0;
 100        var->red.length         = var->bits_per_pixel;
 101        var->green              = var->red;
 102        var->blue               = var->red;
 103
 104        if (var->bits_per_pixel > 4) 
 105                return -EINVAL;
 106
 107        return 0;
 108}
 109
 110/*
 111 * Set the hardware state.
 112 */ 
 113static int 
 114clps7111fb_set_par(struct fb_info *info)
 115{
 116        unsigned int lcdcon, syscon, pixclock;
 117
 118        switch (info->var.bits_per_pixel) {
 119        case 1:
 120                info->fix.visual = FB_VISUAL_MONO01;
 121                break;
 122        case 2:
 123                info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
 124                break;
 125        case 4:
 126                info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
 127                break;
 128        }
 129
 130        info->fix.line_length = info->var.xres_virtual * info->var.bits_per_pixel / 8;
 131
 132        lcdcon = (info->var.xres_virtual * info->var.yres_virtual * info->var.bits_per_pixel) / 128 - 1;
 133        lcdcon |= ((info->var.xres_virtual / 16) - 1) << 13;
 134        lcdcon |= lcd_ac_prescale << 25;
 135
 136        /*
 137         * Calculate pixel prescale value from the pixclock.  This is:
 138         *  36.864MHz / pixclock_mhz - 1.
 139         * However, pixclock is in picoseconds, so this ends up being:
 140         *  36864000 * pixclock_ps / 10^12 - 1
 141         * and this will overflow the 32-bit math.  We perform this as
 142         * (9 * 4096000 == 36864000):
 143         *  pixclock_ps * 9 * (4096000 / 10^12) - 1
 144         */
 145        pixclock = 9 * info->var.pixclock / 244140 - 1;
 146        lcdcon |= pixclock << 19;
 147
 148        if (info->var.bits_per_pixel == 4)
 149                lcdcon |= LCDCON_GSMD;
 150        if (info->var.bits_per_pixel >= 2)
 151                lcdcon |= LCDCON_GSEN;
 152
 153        /*
 154         * LCDCON must only be changed while the LCD is disabled
 155         */
 156        syscon = clps_readl(SYSCON1);
 157        clps_writel(syscon & ~SYSCON1_LCDEN, SYSCON1);
 158        clps_writel(lcdcon, LCDCON);
 159        clps_writel(syscon | SYSCON1_LCDEN, SYSCON1);
 160        return 0;
 161}
 162
 163static int clps7111fb_blank(int blank, struct fb_info *info)
 164{
 165        if (blank) {
 166                if (machine_is_edb7211()) {
 167                        /* Turn off the LCD backlight. */
 168                        clps_writeb(clps_readb(PDDR) & ~EDB_PD3_LCDBL, PDDR);
 169
 170                        /* Power off the LCD DC-DC converter. */
 171                        clps_writeb(clps_readb(PDDR) & ~EDB_PD1_LCD_DC_DC_EN, PDDR);
 172
 173                        /* Delay for a little while (half a second). */
 174                        udelay(100);
 175
 176                        /* Power off the LCD panel. */
 177                        clps_writeb(clps_readb(PDDR) & ~EDB_PD2_LCDEN, PDDR);
 178
 179                        /* Power off the LCD controller. */
 180                        clps_writel(clps_readl(SYSCON1) & ~SYSCON1_LCDEN, 
 181                                        SYSCON1);
 182                }
 183        } else {
 184                if (machine_is_edb7211()) {
 185                        /* Power up the LCD controller. */
 186                        clps_writel(clps_readl(SYSCON1) | SYSCON1_LCDEN,
 187                                        SYSCON1);
 188
 189                        /* Power up the LCD panel. */
 190                        clps_writeb(clps_readb(PDDR) | EDB_PD2_LCDEN, PDDR);
 191
 192                        /* Delay for a little while. */
 193                        udelay(100);
 194
 195                        /* Power up the LCD DC-DC converter. */
 196                        clps_writeb(clps_readb(PDDR) | EDB_PD1_LCD_DC_DC_EN,
 197                                        PDDR);
 198
 199                        /* Turn on the LCD backlight. */
 200                        clps_writeb(clps_readb(PDDR) | EDB_PD3_LCDBL, PDDR);
 201                }
 202        }
 203        return 0;
 204}
 205
 206static struct fb_ops clps7111fb_ops = {
 207        .owner          = THIS_MODULE,
 208        .fb_check_var   = clps7111fb_check_var,
 209        .fb_set_par     = clps7111fb_set_par,
 210        .fb_setcolreg   = clps7111fb_setcolreg,
 211        .fb_blank       = clps7111fb_blank,
 212        .fb_fillrect    = cfb_fillrect,
 213        .fb_copyarea    = cfb_copyarea,
 214        .fb_imageblit   = cfb_imageblit,
 215};
 216
 217static int backlight_proc_show(struct seq_file *m, void *v)
 218{
 219        if (machine_is_edb7211()) {
 220                seq_printf(m, "%d\n",
 221                                (clps_readb(PDDR) & EDB_PD3_LCDBL) ? 1 : 0);
 222        }
 223
 224        return 0;
 225}
 226
 227static int backlight_proc_open(struct inode *inode, struct file *file)
 228{
 229        return single_open(file, backlight_proc_show, NULL);
 230}
 231
 232static ssize_t backlight_proc_write(struct file *file, const char *buffer,
 233                                    size_t count, loff_t *pos)
 234{
 235        unsigned char char_value;
 236        int value;
 237
 238        if (count < 1) {
 239                return -EINVAL;
 240        }
 241
 242        if (copy_from_user(&char_value, buffer, 1)) 
 243                return -EFAULT;
 244
 245        value = char_value - '0';
 246
 247        if (machine_is_edb7211()) {
 248                unsigned char port_d;
 249
 250                port_d = clps_readb(PDDR);
 251
 252                if (value) {
 253                        port_d |= EDB_PD3_LCDBL;
 254                } else {
 255                        port_d &= ~EDB_PD3_LCDBL;
 256                }
 257
 258                clps_writeb(port_d, PDDR);
 259        }
 260
 261        return count;
 262}
 263
 264static const struct file_operations backlight_proc_fops = {
 265        .owner          = THIS_MODULE,
 266        .open           = backlight_proc_open,
 267        .read           = seq_read,
 268        .llseek         = seq_lseek,
 269        .release        = single_release,
 270        .write          = backlight_proc_write,
 271};
 272
 273static void __init clps711x_guess_lcd_params(struct fb_info *info)
 274{
 275        unsigned int lcdcon, syscon, size;
 276        unsigned long phys_base = PAGE_OFFSET;
 277        void *virt_base = (void *)PAGE_OFFSET;
 278
 279        info->var.xres_virtual   = 640;
 280        info->var.yres_virtual   = 240;
 281        info->var.bits_per_pixel = 4;
 282        info->var.activate       = FB_ACTIVATE_NOW;
 283        info->var.height         = -1;
 284        info->var.width          = -1;
 285        info->var.pixclock       = 93006; /* 10.752MHz pixel clock */
 286
 287        /*
 288         * If the LCD controller is already running, decode the values
 289         * in LCDCON to xres/yres/bpp/pixclock/acprescale
 290         */
 291        syscon = clps_readl(SYSCON1);
 292        if (syscon & SYSCON1_LCDEN) {
 293                lcdcon = clps_readl(LCDCON);
 294
 295                /*
 296                 * Decode GSMD and GSEN bits to bits per pixel
 297                 */
 298                switch (lcdcon & (LCDCON_GSMD | LCDCON_GSEN)) {
 299                case LCDCON_GSMD | LCDCON_GSEN:
 300                        info->var.bits_per_pixel = 4;
 301                        break;
 302
 303                case LCDCON_GSEN:
 304                        info->var.bits_per_pixel = 2;
 305                        break;
 306
 307                default:
 308                        info->var.bits_per_pixel = 1;
 309                        break;
 310                }
 311
 312                /*
 313                 * Decode xres/yres
 314                 */
 315                info->var.xres_virtual = (((lcdcon >> 13) & 0x3f) + 1) * 16;
 316                info->var.yres_virtual = (((lcdcon & 0x1fff) + 1) * 128) /
 317                                          (info->var.xres_virtual *
 318                                           info->var.bits_per_pixel);
 319
 320                /*
 321                 * Calculate pixclock
 322                 */
 323                info->var.pixclock = (((lcdcon >> 19) & 0x3f) + 1) * 244140 / 9;
 324
 325                /*
 326                 * Grab AC prescale
 327                 */
 328                lcd_ac_prescale = (lcdcon >> 25) & 0x1f;
 329        }
 330
 331        info->var.xres = info->var.xres_virtual;
 332        info->var.yres = info->var.yres_virtual;
 333        info->var.grayscale = info->var.bits_per_pixel > 1;
 334
 335        size = info->var.xres * info->var.yres * info->var.bits_per_pixel / 8;
 336
 337        /*
 338         * Might be worth checking to see if we can use the on-board
 339         * RAM if size here...
 340         * CLPS7110 - no on-board SRAM
 341         * EP7212   - 38400 bytes
 342         */
 343        if (size <= 38400) {
 344                printk(KERN_INFO "CLPS711xFB: could use on-board SRAM?\n");
 345        }
 346
 347        if ((syscon & SYSCON1_LCDEN) == 0) {
 348                /*
 349                 * The display isn't running.  Ensure that
 350                 * the display memory is empty.
 351                 */
 352                memset(virt_base, 0, size);
 353        }
 354
 355        info->screen_base    = virt_base;
 356        info->fix.smem_start = phys_base;
 357        info->fix.smem_len   = PAGE_ALIGN(size);
 358        info->fix.type       = FB_TYPE_PACKED_PIXELS;
 359}
 360
 361int __init clps711xfb_init(void)
 362{
 363        int err = -ENOMEM;
 364
 365        if (fb_get_options("clps711xfb", NULL))
 366                return -ENODEV;
 367
 368        cfb = kzalloc(sizeof(*cfb), GFP_KERNEL);
 369        if (!cfb)
 370                goto out;
 371
 372        strcpy(cfb->fix.id, "clps711x");
 373
 374        cfb->fbops              = &clps7111fb_ops;
 375        cfb->flags              = FBINFO_DEFAULT;
 376
 377        clps711x_guess_lcd_params(cfb);
 378
 379        fb_alloc_cmap(&cfb->cmap, CMAP_MAX_SIZE, 0);
 380
 381        if (!proc_create("backlight", 0444, NULL, &backlight_proc_fops)) {
 382                printk("Couldn't create the /proc entry for the backlight.\n");
 383                return -EINVAL;
 384        }
 385
 386        /*
 387         * Power up the LCD
 388         */
 389        if (machine_is_p720t()) {
 390                PLD_LCDEN = PLD_LCDEN_EN;
 391                PLD_PWR |= (PLD_S4_ON|PLD_S3_ON|PLD_S2_ON|PLD_S1_ON);
 392        }
 393
 394        if (machine_is_edb7211()) {
 395                /* Power up the LCD panel. */
 396                clps_writeb(clps_readb(PDDR) | EDB_PD2_LCDEN, PDDR);
 397
 398                /* Delay for a little while. */
 399                udelay(100);
 400
 401                /* Power up the LCD DC-DC converter. */
 402                clps_writeb(clps_readb(PDDR) | EDB_PD1_LCD_DC_DC_EN, PDDR);
 403
 404                /* Turn on the LCD backlight. */
 405                clps_writeb(clps_readb(PDDR) | EDB_PD3_LCDBL, PDDR);
 406        }
 407
 408        err = register_framebuffer(cfb);
 409
 410out:    return err;
 411}
 412
 413static void __exit clps711xfb_exit(void)
 414{
 415        unregister_framebuffer(cfb);
 416        kfree(cfb);
 417
 418        /*
 419         * Power down the LCD
 420         */
 421        if (machine_is_p720t()) {
 422                PLD_LCDEN = 0;
 423                PLD_PWR &= ~(PLD_S4_ON|PLD_S3_ON|PLD_S2_ON|PLD_S1_ON);
 424        }
 425}
 426
 427module_init(clps711xfb_init);
 428module_exit(clps711xfb_exit);
 429
 430MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
 431MODULE_DESCRIPTION("CLPS711x framebuffer driver");
 432MODULE_LICENSE("GPL");
 433
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.