Back to home page

OSCL-LXR

 
 

    


0001 /* drivers/video/s1d13xxxfb.c
0002  *
0003  * (c) 2004 Simtec Electronics
0004  * (c) 2005 Thibaut VARENE <varenet@parisc-linux.org>
0005  * (c) 2009 Kristoffer Ericson <kristoffer.ericson@gmail.com>
0006  *
0007  * Driver for Epson S1D13xxx series framebuffer chips
0008  *
0009  * Adapted from
0010  *  linux/drivers/video/skeletonfb.c
0011  *  linux/drivers/video/epson1355fb.c
0012  *  linux/drivers/video/epson/s1d13xxxfb.c (2.4 driver by Epson)
0013  *
0014  * TODO: - handle dual screen display (CRT and LCD at the same time).
0015  *   - check_var(), mode change, etc.
0016  *   - probably not SMP safe :)
0017  *       - support all bitblt operations on all cards
0018  *
0019  * This file is subject to the terms and conditions of the GNU General Public
0020  * License. See the file COPYING in the main directory of this archive for
0021  * more details.
0022  */
0023 
0024 #include <linux/module.h>
0025 #include <linux/platform_device.h>
0026 #include <linux/delay.h>
0027 #include <linux/types.h>
0028 #include <linux/errno.h>
0029 #include <linux/mm.h>
0030 #include <linux/mman.h>
0031 #include <linux/fb.h>
0032 #include <linux/spinlock_types.h>
0033 #include <linux/spinlock.h>
0034 #include <linux/slab.h>
0035 #include <linux/io.h>
0036 
0037 #include <video/s1d13xxxfb.h>
0038 
0039 #define PFX "s1d13xxxfb: "
0040 #define BLIT    "s1d13xxxfb_bitblt: "
0041 
0042 /*
0043  * set this to enable debugging on general functions
0044  */
0045 #if 0
0046 #define dbg(fmt, args...) do { printk(KERN_INFO fmt, ## args); } while(0)
0047 #else
0048 #define dbg(fmt, args...) do { no_printk(KERN_INFO fmt, ## args); } while (0)
0049 #endif
0050 
0051 /*
0052  * set this to enable debugging on 2D acceleration
0053  */
0054 #if 0
0055 #define dbg_blit(fmt, args...) do { printk(KERN_INFO BLIT fmt, ## args); } while (0)
0056 #else
0057 #define dbg_blit(fmt, args...) do { } while (0)
0058 #endif
0059 
0060 /*
0061  * we make sure only one bitblt operation is running
0062  */
0063 static DEFINE_SPINLOCK(s1d13xxxfb_bitblt_lock);
0064 
0065 /*
0066  * list of card production ids
0067  */
0068 static const int s1d13xxxfb_prod_ids[] = {
0069     S1D13505_PROD_ID,
0070     S1D13506_PROD_ID,
0071     S1D13806_PROD_ID,
0072 };
0073 
0074 /*
0075  * List of card strings
0076  */
0077 static const char *s1d13xxxfb_prod_names[] = {
0078     "S1D13505",
0079     "S1D13506",
0080     "S1D13806",
0081 };
0082 
0083 /*
0084  * here we define the default struct fb_fix_screeninfo
0085  */
0086 static const struct fb_fix_screeninfo s1d13xxxfb_fix = {
0087     .id     = S1D_FBID,
0088     .type       = FB_TYPE_PACKED_PIXELS,
0089     .visual     = FB_VISUAL_PSEUDOCOLOR,
0090     .xpanstep   = 0,
0091     .ypanstep   = 1,
0092     .ywrapstep  = 0,
0093     .accel      = FB_ACCEL_NONE,
0094 };
0095 
0096 static inline u8
0097 s1d13xxxfb_readreg(struct s1d13xxxfb_par *par, u16 regno)
0098 {
0099     return readb(par->regs + regno);
0100 }
0101 
0102 static inline void
0103 s1d13xxxfb_writereg(struct s1d13xxxfb_par *par, u16 regno, u8 value)
0104 {
0105     writeb(value, par->regs + regno);
0106 }
0107 
0108 static inline void
0109 s1d13xxxfb_runinit(struct s1d13xxxfb_par *par,
0110             const struct s1d13xxxfb_regval *initregs,
0111             const unsigned int size)
0112 {
0113     int i;
0114 
0115     for (i = 0; i < size; i++) {
0116             if ((initregs[i].addr == S1DREG_DELAYOFF) ||
0117                 (initregs[i].addr == S1DREG_DELAYON))
0118             mdelay((int)initregs[i].value);
0119             else {
0120             s1d13xxxfb_writereg(par, initregs[i].addr, initregs[i].value);
0121         }
0122         }
0123 
0124     /* make sure the hardware can cope with us */
0125     mdelay(1);
0126 }
0127 
0128 static inline void
0129 lcd_enable(struct s1d13xxxfb_par *par, int enable)
0130 {
0131     u8 mode = s1d13xxxfb_readreg(par, S1DREG_COM_DISP_MODE);
0132 
0133     if (enable)
0134         mode |= 0x01;
0135     else
0136         mode &= ~0x01;
0137 
0138     s1d13xxxfb_writereg(par, S1DREG_COM_DISP_MODE, mode);
0139 }
0140 
0141 static inline void
0142 crt_enable(struct s1d13xxxfb_par *par, int enable)
0143 {
0144     u8 mode = s1d13xxxfb_readreg(par, S1DREG_COM_DISP_MODE);
0145 
0146     if (enable)
0147         mode |= 0x02;
0148     else
0149         mode &= ~0x02;
0150 
0151     s1d13xxxfb_writereg(par, S1DREG_COM_DISP_MODE, mode);
0152 }
0153 
0154 
0155 /*************************************************************
0156  framebuffer control functions
0157  *************************************************************/
0158 static inline void
0159 s1d13xxxfb_setup_pseudocolour(struct fb_info *info)
0160 {
0161     info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
0162 
0163     info->var.red.length = 4;
0164     info->var.green.length = 4;
0165     info->var.blue.length = 4;
0166 }
0167 
0168 static inline void
0169 s1d13xxxfb_setup_truecolour(struct fb_info *info)
0170 {
0171     info->fix.visual = FB_VISUAL_TRUECOLOR;
0172     info->var.bits_per_pixel = 16;
0173 
0174     info->var.red.length = 5;
0175     info->var.red.offset = 11;
0176 
0177     info->var.green.length = 6;
0178     info->var.green.offset = 5;
0179 
0180     info->var.blue.length = 5;
0181     info->var.blue.offset = 0;
0182 }
0183 
0184 /**
0185  *      s1d13xxxfb_set_par - Alters the hardware state.
0186  *      @info: frame buffer structure
0187  *
0188  *  Using the fb_var_screeninfo in fb_info we set the depth of the
0189  *  framebuffer. This function alters the par AND the
0190  *  fb_fix_screeninfo stored in fb_info. It doesn't not alter var in
0191  *  fb_info since we are using that data. This means we depend on the
0192  *  data in var inside fb_info to be supported by the hardware.
0193  *  xxxfb_check_var is always called before xxxfb_set_par to ensure this.
0194  *
0195  *  XXX TODO: write proper s1d13xxxfb_check_var(), without which that
0196  *  function is quite useless.
0197  */
0198 static int
0199 s1d13xxxfb_set_par(struct fb_info *info)
0200 {
0201     struct s1d13xxxfb_par *s1dfb = info->par;
0202     unsigned int val;
0203 
0204     dbg("s1d13xxxfb_set_par: bpp=%d\n", info->var.bits_per_pixel);
0205 
0206     if ((s1dfb->display & 0x01))    /* LCD */
0207         val = s1d13xxxfb_readreg(s1dfb, S1DREG_LCD_DISP_MODE);   /* read colour control */
0208     else    /* CRT */
0209         val = s1d13xxxfb_readreg(s1dfb, S1DREG_CRT_DISP_MODE);   /* read colour control */
0210 
0211     val &= ~0x07;
0212 
0213     switch (info->var.bits_per_pixel) {
0214         case 4:
0215             dbg("pseudo colour 4\n");
0216             s1d13xxxfb_setup_pseudocolour(info);
0217             val |= 2;
0218             break;
0219         case 8:
0220             dbg("pseudo colour 8\n");
0221             s1d13xxxfb_setup_pseudocolour(info);
0222             val |= 3;
0223             break;
0224         case 16:
0225             dbg("true colour\n");
0226             s1d13xxxfb_setup_truecolour(info);
0227             val |= 5;
0228             break;
0229 
0230         default:
0231             dbg("bpp not supported!\n");
0232             return -EINVAL;
0233     }
0234 
0235     dbg("writing %02x to display mode register\n", val);
0236 
0237     if ((s1dfb->display & 0x01))    /* LCD */
0238         s1d13xxxfb_writereg(s1dfb, S1DREG_LCD_DISP_MODE, val);
0239     else    /* CRT */
0240         s1d13xxxfb_writereg(s1dfb, S1DREG_CRT_DISP_MODE, val);
0241 
0242     info->fix.line_length  = info->var.xres * info->var.bits_per_pixel;
0243     info->fix.line_length /= 8;
0244 
0245     dbg("setting line_length to %d\n", info->fix.line_length);
0246 
0247     dbg("done setup\n");
0248 
0249     return 0;
0250 }
0251 
0252 /**
0253  *  s1d13xxxfb_setcolreg - sets a color register.
0254  *  @regno: Which register in the CLUT we are programming
0255  *  @red: The red value which can be up to 16 bits wide
0256  *  @green: The green value which can be up to 16 bits wide
0257  *  @blue:  The blue value which can be up to 16 bits wide.
0258  *  @transp: If supported the alpha value which can be up to 16 bits wide.
0259  *  @info: frame buffer info structure
0260  *
0261  *  Returns negative errno on error, or zero on success.
0262  */
0263 static int
0264 s1d13xxxfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
0265             u_int transp, struct fb_info *info)
0266 {
0267     struct s1d13xxxfb_par *s1dfb = info->par;
0268     unsigned int pseudo_val;
0269 
0270     if (regno >= S1D_PALETTE_SIZE)
0271         return -EINVAL;
0272 
0273     dbg("s1d13xxxfb_setcolreg: %d: rgb=%d,%d,%d, tr=%d\n",
0274             regno, red, green, blue, transp);
0275 
0276     if (info->var.grayscale)
0277         red = green = blue = (19595*red + 38470*green + 7471*blue) >> 16;
0278 
0279     switch (info->fix.visual) {
0280         case FB_VISUAL_TRUECOLOR:
0281             if (regno >= 16)
0282                 return -EINVAL;
0283 
0284             /* deal with creating pseudo-palette entries */
0285 
0286             pseudo_val  = (red   >> 11) << info->var.red.offset;
0287             pseudo_val |= (green >> 10) << info->var.green.offset;
0288             pseudo_val |= (blue  >> 11) << info->var.blue.offset;
0289 
0290             dbg("s1d13xxxfb_setcolreg: pseudo %d, val %08x\n",
0291                     regno, pseudo_val);
0292 
0293             ((u32 *)info->pseudo_palette)[regno] = pseudo_val;
0294 
0295             break;
0296         case FB_VISUAL_PSEUDOCOLOR:
0297             s1d13xxxfb_writereg(s1dfb, S1DREG_LKUP_ADDR, regno);
0298             s1d13xxxfb_writereg(s1dfb, S1DREG_LKUP_DATA, red);
0299             s1d13xxxfb_writereg(s1dfb, S1DREG_LKUP_DATA, green);
0300             s1d13xxxfb_writereg(s1dfb, S1DREG_LKUP_DATA, blue);
0301 
0302             break;
0303         default:
0304             return -ENOSYS;
0305     }
0306 
0307     dbg("s1d13xxxfb_setcolreg: done\n");
0308 
0309     return 0;
0310 }
0311 
0312 /**
0313  *      s1d13xxxfb_blank - blanks the display.
0314  *      @blank_mode: the blank mode we want.
0315  *      @info: frame buffer structure that represents a single frame buffer
0316  *
0317  *      Blank the screen if blank_mode != 0, else unblank. Return 0 if
0318  *      blanking succeeded, != 0 if un-/blanking failed due to e.g. a
0319  *      video mode which doesn't support it. Implements VESA suspend
0320  *      and powerdown modes on hardware that supports disabling hsync/vsync:
0321  *      blank_mode == 2: suspend vsync
0322  *      blank_mode == 3: suspend hsync
0323  *      blank_mode == 4: powerdown
0324  *
0325  *      Returns negative errno on error, or zero on success.
0326  */
0327 static int
0328 s1d13xxxfb_blank(int blank_mode, struct fb_info *info)
0329 {
0330     struct s1d13xxxfb_par *par = info->par;
0331 
0332     dbg("s1d13xxxfb_blank: blank=%d, info=%p\n", blank_mode, info);
0333 
0334     switch (blank_mode) {
0335         case FB_BLANK_UNBLANK:
0336         case FB_BLANK_NORMAL:
0337             if ((par->display & 0x01) != 0)
0338                 lcd_enable(par, 1);
0339             if ((par->display & 0x02) != 0)
0340                 crt_enable(par, 1);
0341             break;
0342         case FB_BLANK_VSYNC_SUSPEND:
0343         case FB_BLANK_HSYNC_SUSPEND:
0344             break;
0345         case FB_BLANK_POWERDOWN:
0346             lcd_enable(par, 0);
0347             crt_enable(par, 0);
0348             break;
0349         default:
0350             return -EINVAL;
0351     }
0352 
0353     /* let fbcon do a soft blank for us */
0354     return ((blank_mode == FB_BLANK_NORMAL) ? 1 : 0);
0355 }
0356 
0357 /**
0358  *  s1d13xxxfb_pan_display - Pans the display.
0359  *  @var: frame buffer variable screen structure
0360  *  @info: frame buffer structure that represents a single frame buffer
0361  *
0362  *  Pan (or wrap, depending on the `vmode' field) the display using the
0363  *  `yoffset' field of the `var' structure (`xoffset'  not yet supported).
0364  *  If the values don't fit, return -EINVAL.
0365  *
0366  *  Returns negative errno on error, or zero on success.
0367  */
0368 static int
0369 s1d13xxxfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
0370 {
0371     struct s1d13xxxfb_par *par = info->par;
0372     u32 start;
0373 
0374     if (var->xoffset != 0)  /* not yet ... */
0375         return -EINVAL;
0376 
0377     if (var->yoffset + info->var.yres > info->var.yres_virtual)
0378         return -EINVAL;
0379 
0380     start = (info->fix.line_length >> 1) * var->yoffset;
0381 
0382     if ((par->display & 0x01)) {
0383         /* LCD */
0384         s1d13xxxfb_writereg(par, S1DREG_LCD_DISP_START0, (start & 0xff));
0385         s1d13xxxfb_writereg(par, S1DREG_LCD_DISP_START1, ((start >> 8) & 0xff));
0386         s1d13xxxfb_writereg(par, S1DREG_LCD_DISP_START2, ((start >> 16) & 0x0f));
0387     } else {
0388         /* CRT */
0389         s1d13xxxfb_writereg(par, S1DREG_CRT_DISP_START0, (start & 0xff));
0390         s1d13xxxfb_writereg(par, S1DREG_CRT_DISP_START1, ((start >> 8) & 0xff));
0391         s1d13xxxfb_writereg(par, S1DREG_CRT_DISP_START2, ((start >> 16) & 0x0f));
0392     }
0393 
0394     return 0;
0395 }
0396 
0397 /************************************************************
0398  functions to handle bitblt acceleration
0399  ************************************************************/
0400 
0401 /**
0402  *  bltbit_wait_bitclear - waits for change in register value
0403  *  @info : frambuffer structure
0404  *  @bit  : value currently in register
0405  *  @timeout : ...
0406  *
0407  *  waits until value changes FROM bit
0408  *
0409  */
0410 static u8
0411 bltbit_wait_bitclear(struct fb_info *info, u8 bit, int timeout)
0412 {
0413     while (s1d13xxxfb_readreg(info->par, S1DREG_BBLT_CTL0) & bit) {
0414         udelay(10);
0415         if (!--timeout) {
0416             dbg_blit("wait_bitclear timeout\n");
0417             break;
0418         }
0419     }
0420 
0421     return timeout;
0422 }
0423 
0424 /*
0425  *  s1d13xxxfb_bitblt_copyarea - accelerated copyarea function
0426  *  @info : framebuffer structure
0427  *  @area : fb_copyarea structure
0428  *
0429  *  supports (atleast) S1D13506
0430  *
0431  */
0432 static void
0433 s1d13xxxfb_bitblt_copyarea(struct fb_info *info, const struct fb_copyarea *area)
0434 {
0435     u32 dst, src;
0436     u32 stride;
0437     u16 reverse = 0;
0438     u16 sx = area->sx, sy = area->sy;
0439     u16 dx = area->dx, dy = area->dy;
0440     u16 width = area->width, height = area->height;
0441     u16 bpp;
0442 
0443     spin_lock(&s1d13xxxfb_bitblt_lock);
0444 
0445     /* bytes per xres line */
0446     bpp = (info->var.bits_per_pixel >> 3);
0447     stride = bpp * info->var.xres;
0448 
0449     /* reverse, calculate the last pixel in rectangle */
0450     if ((dy > sy) || ((dy == sy) && (dx >= sx))) {
0451         dst = (((dy + height - 1) * stride) + (bpp * (dx + width - 1)));
0452         src = (((sy + height - 1) * stride) + (bpp * (sx + width - 1)));
0453         reverse = 1;
0454     /* not reverse, calculate the first pixel in rectangle */
0455     } else { /* (y * xres) + (bpp * x) */
0456         dst = (dy * stride) + (bpp * dx);
0457         src = (sy * stride) + (bpp * sx);
0458     }
0459 
0460     /* set source address */
0461     s1d13xxxfb_writereg(info->par, S1DREG_BBLT_SRC_START0, (src & 0xff));
0462     s1d13xxxfb_writereg(info->par, S1DREG_BBLT_SRC_START1, (src >> 8) & 0x00ff);
0463     s1d13xxxfb_writereg(info->par, S1DREG_BBLT_SRC_START2, (src >> 16) & 0x00ff);
0464 
0465     /* set destination address */
0466     s1d13xxxfb_writereg(info->par, S1DREG_BBLT_DST_START0, (dst & 0xff));
0467     s1d13xxxfb_writereg(info->par, S1DREG_BBLT_DST_START1, (dst >> 8) & 0x00ff);
0468     s1d13xxxfb_writereg(info->par, S1DREG_BBLT_DST_START2, (dst >> 16) & 0x00ff);
0469 
0470     /* program height and width */
0471     s1d13xxxfb_writereg(info->par, S1DREG_BBLT_WIDTH0, (width & 0xff) - 1);
0472     s1d13xxxfb_writereg(info->par, S1DREG_BBLT_WIDTH1, (width >> 8));
0473 
0474     s1d13xxxfb_writereg(info->par, S1DREG_BBLT_HEIGHT0, (height & 0xff) - 1);
0475     s1d13xxxfb_writereg(info->par, S1DREG_BBLT_HEIGHT1, (height >> 8));
0476 
0477     /* negative direction ROP */
0478     if (reverse == 1) {
0479         dbg_blit("(copyarea) negative rop\n");
0480         s1d13xxxfb_writereg(info->par, S1DREG_BBLT_OP, 0x03);
0481     } else /* positive direction ROP */ {
0482         s1d13xxxfb_writereg(info->par, S1DREG_BBLT_OP, 0x02);
0483         dbg_blit("(copyarea) positive rop\n");
0484     }
0485 
0486     /* set for rectangel mode and not linear */
0487     s1d13xxxfb_writereg(info->par, S1DREG_BBLT_CTL0, 0x0);
0488 
0489     /* setup the bpp 1 = 16bpp, 0 = 8bpp*/
0490     s1d13xxxfb_writereg(info->par, S1DREG_BBLT_CTL1, (bpp >> 1));
0491 
0492     /* set words per xres */
0493     s1d13xxxfb_writereg(info->par, S1DREG_BBLT_MEM_OFF0, (stride >> 1) & 0xff);
0494     s1d13xxxfb_writereg(info->par, S1DREG_BBLT_MEM_OFF1, (stride >> 9));
0495 
0496     dbg_blit("(copyarea) dx=%d, dy=%d\n", dx, dy);
0497     dbg_blit("(copyarea) sx=%d, sy=%d\n", sx, sy);
0498     dbg_blit("(copyarea) width=%d, height=%d\n", width - 1, height - 1);
0499     dbg_blit("(copyarea) stride=%d\n", stride);
0500     dbg_blit("(copyarea) bpp=%d=0x0%d, mem_offset1=%d, mem_offset2=%d\n", bpp, (bpp >> 1),
0501         (stride >> 1) & 0xff, stride >> 9);
0502 
0503     s1d13xxxfb_writereg(info->par, S1DREG_BBLT_CC_EXP, 0x0c);
0504 
0505     /* initialize the engine */
0506     s1d13xxxfb_writereg(info->par, S1DREG_BBLT_CTL0, 0x80);
0507 
0508     /* wait to complete */
0509     bltbit_wait_bitclear(info, 0x80, 8000);
0510 
0511     spin_unlock(&s1d13xxxfb_bitblt_lock);
0512 }
0513 
0514 /**
0515  *  s1d13xxxfb_bitblt_solidfill - accelerated solidfill function
0516  *  @info : framebuffer structure
0517  *  @rect : fb_fillrect structure
0518  *
0519  *  supports (atleast 13506)
0520  *
0521  **/
0522 static void
0523 s1d13xxxfb_bitblt_solidfill(struct fb_info *info, const struct fb_fillrect *rect)
0524 {
0525     u32 screen_stride, dest;
0526     u32 fg;
0527     u16 bpp = (info->var.bits_per_pixel >> 3);
0528 
0529     /* grab spinlock */
0530     spin_lock(&s1d13xxxfb_bitblt_lock);
0531 
0532     /* bytes per x width */
0533     screen_stride = (bpp * info->var.xres);
0534 
0535     /* bytes to starting point */
0536     dest = ((rect->dy * screen_stride) + (bpp * rect->dx));
0537 
0538     dbg_blit("(solidfill) dx=%d, dy=%d, stride=%d, dest=%d\n"
0539          "(solidfill) : rect_width=%d, rect_height=%d\n",
0540                 rect->dx, rect->dy, screen_stride, dest,
0541                 rect->width - 1, rect->height - 1);
0542 
0543     dbg_blit("(solidfill) : xres=%d, yres=%d, bpp=%d\n",
0544                 info->var.xres, info->var.yres,
0545                 info->var.bits_per_pixel);
0546     dbg_blit("(solidfill) : rop=%d\n", rect->rop);
0547 
0548     /* We split the destination into the three registers */
0549     s1d13xxxfb_writereg(info->par, S1DREG_BBLT_DST_START0, (dest & 0x00ff));
0550     s1d13xxxfb_writereg(info->par, S1DREG_BBLT_DST_START1, ((dest >> 8) & 0x00ff));
0551     s1d13xxxfb_writereg(info->par, S1DREG_BBLT_DST_START2, ((dest >> 16) & 0x00ff));
0552 
0553     /* give information regarding rectangel width */
0554     s1d13xxxfb_writereg(info->par, S1DREG_BBLT_WIDTH0, ((rect->width) & 0x00ff) - 1);
0555     s1d13xxxfb_writereg(info->par, S1DREG_BBLT_WIDTH1, (rect->width >> 8));
0556 
0557     /* give information regarding rectangel height */
0558     s1d13xxxfb_writereg(info->par, S1DREG_BBLT_HEIGHT0, ((rect->height) & 0x00ff) - 1);
0559     s1d13xxxfb_writereg(info->par, S1DREG_BBLT_HEIGHT1, (rect->height >> 8));
0560 
0561     if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
0562         info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
0563         fg = ((u32 *)info->pseudo_palette)[rect->color];
0564         dbg_blit("(solidfill) truecolor/directcolor\n");
0565         dbg_blit("(solidfill) pseudo_palette[%d] = %d\n", rect->color, fg);
0566     } else {
0567         fg = rect->color;
0568         dbg_blit("(solidfill) color = %d\n", rect->color);
0569     }
0570 
0571     /* set foreground color */
0572     s1d13xxxfb_writereg(info->par, S1DREG_BBLT_FGC0, (fg & 0xff));
0573     s1d13xxxfb_writereg(info->par, S1DREG_BBLT_FGC1, (fg >> 8) & 0xff);
0574 
0575     /* set rectangual region of memory (rectangle and not linear) */
0576     s1d13xxxfb_writereg(info->par, S1DREG_BBLT_CTL0, 0x0);
0577 
0578     /* set operation mode SOLID_FILL */
0579     s1d13xxxfb_writereg(info->par, S1DREG_BBLT_OP, BBLT_SOLID_FILL);
0580 
0581     /* set bits per pixel (1 = 16bpp, 0 = 8bpp) */
0582     s1d13xxxfb_writereg(info->par, S1DREG_BBLT_CTL1, (info->var.bits_per_pixel >> 4));
0583 
0584     /* set the memory offset for the bblt in word sizes */
0585     s1d13xxxfb_writereg(info->par, S1DREG_BBLT_MEM_OFF0, (screen_stride >> 1) & 0x00ff);
0586     s1d13xxxfb_writereg(info->par, S1DREG_BBLT_MEM_OFF1, (screen_stride >> 9));
0587 
0588     /* and away we go.... */
0589     s1d13xxxfb_writereg(info->par, S1DREG_BBLT_CTL0, 0x80);
0590 
0591     /* wait until its done */
0592     bltbit_wait_bitclear(info, 0x80, 8000);
0593 
0594     /* let others play */
0595     spin_unlock(&s1d13xxxfb_bitblt_lock);
0596 }
0597 
0598 /* framebuffer information structures */
0599 static struct fb_ops s1d13xxxfb_fbops = {
0600     .owner      = THIS_MODULE,
0601     .fb_set_par = s1d13xxxfb_set_par,
0602     .fb_setcolreg   = s1d13xxxfb_setcolreg,
0603     .fb_blank   = s1d13xxxfb_blank,
0604 
0605     .fb_pan_display = s1d13xxxfb_pan_display,
0606 
0607     /* gets replaced at chip detection time */
0608     .fb_fillrect    = cfb_fillrect,
0609     .fb_copyarea    = cfb_copyarea,
0610     .fb_imageblit   = cfb_imageblit,
0611 };
0612 
0613 static int s1d13xxxfb_width_tab[2][4] = {
0614     {4, 8, 16, -1},
0615     {9, 12, 18, -1},
0616 };
0617 
0618 /**
0619  *  s1d13xxxfb_fetch_hw_state - Configure the framebuffer according to
0620  *  hardware setup.
0621  *  @info: frame buffer structure
0622  *
0623  *  We setup the framebuffer structures according to the current
0624  *  hardware setup. On some machines, the BIOS will have filled
0625  *  the chip registers with such info, on others, these values will
0626  *  have been written in some init procedure. In any case, the
0627  *  software values needs to match the hardware ones. This is what
0628  *  this function ensures.
0629  *
0630  *  Note: some of the hardcoded values here might need some love to
0631  *  work on various chips, and might need to no longer be hardcoded.
0632  */
0633 static void s1d13xxxfb_fetch_hw_state(struct fb_info *info)
0634 {
0635     struct fb_var_screeninfo *var = &info->var;
0636     struct fb_fix_screeninfo *fix = &info->fix;
0637     struct s1d13xxxfb_par *par = info->par;
0638     u8 panel, display;
0639     u16 offset;
0640     u32 xres, yres;
0641     u32 xres_virtual, yres_virtual;
0642     int bpp, lcd_bpp;
0643     int is_color, is_dual, is_tft;
0644     int lcd_enabled, crt_enabled;
0645 
0646     fix->type = FB_TYPE_PACKED_PIXELS;
0647 
0648     /* general info */
0649     par->display = s1d13xxxfb_readreg(par, S1DREG_COM_DISP_MODE);
0650     crt_enabled = (par->display & 0x02) != 0;
0651     lcd_enabled = (par->display & 0x01) != 0;
0652 
0653     if (lcd_enabled && crt_enabled)
0654         printk(KERN_WARNING PFX "Warning: LCD and CRT detected, using LCD\n");
0655 
0656     if (lcd_enabled)
0657         display = s1d13xxxfb_readreg(par, S1DREG_LCD_DISP_MODE);
0658     else    /* CRT */
0659         display = s1d13xxxfb_readreg(par, S1DREG_CRT_DISP_MODE);
0660 
0661     bpp = display & 0x07;
0662 
0663     switch (bpp) {
0664         case 2: /* 4 bpp */
0665         case 3: /* 8 bpp */
0666             var->bits_per_pixel = 8;
0667             var->red.offset = var->green.offset = var->blue.offset = 0;
0668             var->red.length = var->green.length = var->blue.length = 8;
0669             break;
0670         case 5: /* 16 bpp */
0671             s1d13xxxfb_setup_truecolour(info);
0672             break;
0673         default:
0674             dbg("bpp: %i\n", bpp);
0675     }
0676     fb_alloc_cmap(&info->cmap, 256, 0);
0677 
0678     /* LCD info */
0679     panel = s1d13xxxfb_readreg(par, S1DREG_PANEL_TYPE);
0680     is_color = (panel & 0x04) != 0;
0681     is_dual = (panel & 0x02) != 0;
0682     is_tft = (panel & 0x01) != 0;
0683     lcd_bpp = s1d13xxxfb_width_tab[is_tft][(panel >> 4) & 3];
0684 
0685     if (lcd_enabled) {
0686         xres = (s1d13xxxfb_readreg(par, S1DREG_LCD_DISP_HWIDTH) + 1) * 8;
0687         yres = (s1d13xxxfb_readreg(par, S1DREG_LCD_DISP_VHEIGHT0) +
0688             ((s1d13xxxfb_readreg(par, S1DREG_LCD_DISP_VHEIGHT1) & 0x03) << 8) + 1);
0689 
0690         offset = (s1d13xxxfb_readreg(par, S1DREG_LCD_MEM_OFF0) +
0691             ((s1d13xxxfb_readreg(par, S1DREG_LCD_MEM_OFF1) & 0x7) << 8));
0692     } else { /* crt */
0693         xres = (s1d13xxxfb_readreg(par, S1DREG_CRT_DISP_HWIDTH) + 1) * 8;
0694         yres = (s1d13xxxfb_readreg(par, S1DREG_CRT_DISP_VHEIGHT0) +
0695             ((s1d13xxxfb_readreg(par, S1DREG_CRT_DISP_VHEIGHT1) & 0x03) << 8) + 1);
0696 
0697         offset = (s1d13xxxfb_readreg(par, S1DREG_CRT_MEM_OFF0) +
0698             ((s1d13xxxfb_readreg(par, S1DREG_CRT_MEM_OFF1) & 0x7) << 8));
0699     }
0700     xres_virtual = offset * 16 / var->bits_per_pixel;
0701     yres_virtual = fix->smem_len / (offset * 2);
0702 
0703     var->xres       = xres;
0704     var->yres       = yres;
0705     var->xres_virtual   = xres_virtual;
0706     var->yres_virtual   = yres_virtual;
0707     var->xoffset        = var->yoffset = 0;
0708 
0709     fix->line_length    = offset * 2;
0710 
0711     var->grayscale      = !is_color;
0712 
0713     var->activate       = FB_ACTIVATE_NOW;
0714 
0715     dbg(PFX "bpp=%d, lcd_bpp=%d, "
0716         "crt_enabled=%d, lcd_enabled=%d\n",
0717         var->bits_per_pixel, lcd_bpp, crt_enabled, lcd_enabled);
0718     dbg(PFX "xres=%d, yres=%d, vxres=%d, vyres=%d "
0719         "is_color=%d, is_dual=%d, is_tft=%d\n",
0720         xres, yres, xres_virtual, yres_virtual, is_color, is_dual, is_tft);
0721 }
0722 
0723 static void __s1d13xxxfb_remove(struct platform_device *pdev)
0724 {
0725     struct fb_info *info = platform_get_drvdata(pdev);
0726     struct s1d13xxxfb_par *par = NULL;
0727 
0728     if (info) {
0729         par = info->par;
0730         if (par && par->regs) {
0731             /* disable output & enable powersave */
0732             s1d13xxxfb_writereg(par, S1DREG_COM_DISP_MODE, 0x00);
0733             s1d13xxxfb_writereg(par, S1DREG_PS_CNF, 0x11);
0734             iounmap(par->regs);
0735         }
0736 
0737         fb_dealloc_cmap(&info->cmap);
0738 
0739         if (info->screen_base)
0740             iounmap(info->screen_base);
0741 
0742         framebuffer_release(info);
0743     }
0744 
0745     release_mem_region(pdev->resource[0].start,
0746                resource_size(&pdev->resource[0]));
0747     release_mem_region(pdev->resource[1].start,
0748                resource_size(&pdev->resource[1]));
0749 }
0750 
0751 static int s1d13xxxfb_remove(struct platform_device *pdev)
0752 {
0753     struct fb_info *info = platform_get_drvdata(pdev);
0754 
0755     unregister_framebuffer(info);
0756     __s1d13xxxfb_remove(pdev);
0757     return 0;
0758 }
0759 
0760 static int s1d13xxxfb_probe(struct platform_device *pdev)
0761 {
0762     struct s1d13xxxfb_par *default_par;
0763     struct fb_info *info;
0764     struct s1d13xxxfb_pdata *pdata = NULL;
0765     int ret = 0;
0766     int i;
0767     u8 revision, prod_id;
0768 
0769     dbg("probe called: device is %p\n", pdev);
0770 
0771     printk(KERN_INFO "Epson S1D13XXX FB Driver\n");
0772 
0773     /* enable platform-dependent hardware glue, if any */
0774     if (dev_get_platdata(&pdev->dev))
0775         pdata = dev_get_platdata(&pdev->dev);
0776 
0777     if (pdata && pdata->platform_init_video)
0778         pdata->platform_init_video();
0779 
0780     if (pdev->num_resources != 2) {
0781         dev_err(&pdev->dev, "invalid num_resources: %i\n",
0782                pdev->num_resources);
0783         ret = -ENODEV;
0784         goto bail;
0785     }
0786 
0787     /* resource[0] is VRAM, resource[1] is registers */
0788     if (pdev->resource[0].flags != IORESOURCE_MEM
0789             || pdev->resource[1].flags != IORESOURCE_MEM) {
0790         dev_err(&pdev->dev, "invalid resource type\n");
0791         ret = -ENODEV;
0792         goto bail;
0793     }
0794 
0795     if (!request_mem_region(pdev->resource[0].start,
0796         resource_size(&pdev->resource[0]), "s1d13xxxfb mem")) {
0797         dev_dbg(&pdev->dev, "request_mem_region failed\n");
0798         ret = -EBUSY;
0799         goto bail;
0800     }
0801 
0802     if (!request_mem_region(pdev->resource[1].start,
0803         resource_size(&pdev->resource[1]), "s1d13xxxfb regs")) {
0804         dev_dbg(&pdev->dev, "request_mem_region failed\n");
0805         ret = -EBUSY;
0806         goto bail;
0807     }
0808 
0809     info = framebuffer_alloc(sizeof(struct s1d13xxxfb_par) + sizeof(u32) * 256, &pdev->dev);
0810     if (!info) {
0811         ret = -ENOMEM;
0812         goto bail;
0813     }
0814 
0815     platform_set_drvdata(pdev, info);
0816     default_par = info->par;
0817     default_par->regs = ioremap(pdev->resource[1].start,
0818                     resource_size(&pdev->resource[1]));
0819     if (!default_par->regs) {
0820         printk(KERN_ERR PFX "unable to map registers\n");
0821         ret = -ENOMEM;
0822         goto bail;
0823     }
0824     info->pseudo_palette = default_par->pseudo_palette;
0825 
0826     info->screen_base = ioremap(pdev->resource[0].start,
0827                     resource_size(&pdev->resource[0]));
0828 
0829     if (!info->screen_base) {
0830         printk(KERN_ERR PFX "unable to map framebuffer\n");
0831         ret = -ENOMEM;
0832         goto bail;
0833     }
0834 
0835     /* production id is top 6 bits */
0836     prod_id = s1d13xxxfb_readreg(default_par, S1DREG_REV_CODE) >> 2;
0837     /* revision id is lower 2 bits */
0838     revision = s1d13xxxfb_readreg(default_par, S1DREG_REV_CODE) & 0x3;
0839     ret = -ENODEV;
0840 
0841     for (i = 0; i < ARRAY_SIZE(s1d13xxxfb_prod_ids); i++) {
0842         if (prod_id == s1d13xxxfb_prod_ids[i]) {
0843             /* looks like we got it in our list */
0844             default_par->prod_id = prod_id;
0845             default_par->revision = revision;
0846             ret = 0;
0847             break;
0848         }
0849     }
0850 
0851     if (!ret) {
0852         printk(KERN_INFO PFX "chip production id %i = %s\n",
0853             prod_id, s1d13xxxfb_prod_names[i]);
0854         printk(KERN_INFO PFX "chip revision %i\n", revision);
0855     } else {
0856         printk(KERN_INFO PFX
0857             "unknown chip production id %i, revision %i\n",
0858             prod_id, revision);
0859         printk(KERN_INFO PFX "please contact maintainer\n");
0860         goto bail;
0861     }
0862 
0863     info->fix = s1d13xxxfb_fix;
0864     info->fix.mmio_start = pdev->resource[1].start;
0865     info->fix.mmio_len = resource_size(&pdev->resource[1]);
0866     info->fix.smem_start = pdev->resource[0].start;
0867     info->fix.smem_len = resource_size(&pdev->resource[0]);
0868 
0869     printk(KERN_INFO PFX "regs mapped at 0x%p, fb %d KiB mapped at 0x%p\n",
0870            default_par->regs, info->fix.smem_len / 1024, info->screen_base);
0871 
0872     info->par = default_par;
0873     info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN;
0874     info->fbops = &s1d13xxxfb_fbops;
0875 
0876     switch(prod_id) {
0877     case S1D13506_PROD_ID:  /* activate acceleration */
0878         s1d13xxxfb_fbops.fb_fillrect = s1d13xxxfb_bitblt_solidfill;
0879         s1d13xxxfb_fbops.fb_copyarea = s1d13xxxfb_bitblt_copyarea;
0880         info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN |
0881             FBINFO_HWACCEL_FILLRECT | FBINFO_HWACCEL_COPYAREA;
0882         break;
0883     default:
0884         break;
0885     }
0886 
0887     /* perform "manual" chip initialization, if needed */
0888     if (pdata && pdata->initregs)
0889         s1d13xxxfb_runinit(info->par, pdata->initregs, pdata->initregssize);
0890 
0891     s1d13xxxfb_fetch_hw_state(info);
0892 
0893     if (register_framebuffer(info) < 0) {
0894         ret = -EINVAL;
0895         goto bail;
0896     }
0897 
0898     fb_info(info, "%s frame buffer device\n", info->fix.id);
0899 
0900     return 0;
0901 
0902 bail:
0903     __s1d13xxxfb_remove(pdev);
0904     return ret;
0905 
0906 }
0907 
0908 #ifdef CONFIG_PM
0909 static int s1d13xxxfb_suspend(struct platform_device *dev, pm_message_t state)
0910 {
0911     struct fb_info *info = platform_get_drvdata(dev);
0912     struct s1d13xxxfb_par *s1dfb = info->par;
0913     struct s1d13xxxfb_pdata *pdata = NULL;
0914 
0915     /* disable display */
0916     lcd_enable(s1dfb, 0);
0917     crt_enable(s1dfb, 0);
0918 
0919     if (dev_get_platdata(&dev->dev))
0920         pdata = dev_get_platdata(&dev->dev);
0921 
0922 #if 0
0923     if (!s1dfb->disp_save)
0924         s1dfb->disp_save = kmalloc(info->fix.smem_len, GFP_KERNEL);
0925 
0926     if (!s1dfb->disp_save) {
0927         printk(KERN_ERR PFX "no memory to save screen\n");
0928         return -ENOMEM;
0929     }
0930 
0931     memcpy_fromio(s1dfb->disp_save, info->screen_base, info->fix.smem_len);
0932 #else
0933     s1dfb->disp_save = NULL;
0934 #endif
0935 
0936     if (!s1dfb->regs_save)
0937         s1dfb->regs_save = kmalloc(info->fix.mmio_len, GFP_KERNEL);
0938 
0939     if (!s1dfb->regs_save) {
0940         printk(KERN_ERR PFX "no memory to save registers");
0941         return -ENOMEM;
0942     }
0943 
0944     /* backup all registers */
0945     memcpy_fromio(s1dfb->regs_save, s1dfb->regs, info->fix.mmio_len);
0946 
0947     /* now activate power save mode */
0948     s1d13xxxfb_writereg(s1dfb, S1DREG_PS_CNF, 0x11);
0949 
0950     if (pdata && pdata->platform_suspend_video)
0951         return pdata->platform_suspend_video();
0952     else
0953         return 0;
0954 }
0955 
0956 static int s1d13xxxfb_resume(struct platform_device *dev)
0957 {
0958     struct fb_info *info = platform_get_drvdata(dev);
0959     struct s1d13xxxfb_par *s1dfb = info->par;
0960     struct s1d13xxxfb_pdata *pdata = NULL;
0961 
0962     /* awaken the chip */
0963     s1d13xxxfb_writereg(s1dfb, S1DREG_PS_CNF, 0x10);
0964 
0965     /* do not let go until SDRAM "wakes up" */
0966     while ((s1d13xxxfb_readreg(s1dfb, S1DREG_PS_STATUS) & 0x01))
0967         udelay(10);
0968 
0969     if (dev_get_platdata(&dev->dev))
0970         pdata = dev_get_platdata(&dev->dev);
0971 
0972     if (s1dfb->regs_save) {
0973         /* will write RO regs, *should* get away with it :) */
0974         memcpy_toio(s1dfb->regs, s1dfb->regs_save, info->fix.mmio_len);
0975         kfree(s1dfb->regs_save);
0976     }
0977 
0978     if (s1dfb->disp_save) {
0979         memcpy_toio(info->screen_base, s1dfb->disp_save,
0980                 info->fix.smem_len);
0981         kfree(s1dfb->disp_save);    /* XXX kmalloc()'d when? */
0982     }
0983 
0984     if ((s1dfb->display & 0x01) != 0)
0985         lcd_enable(s1dfb, 1);
0986     if ((s1dfb->display & 0x02) != 0)
0987         crt_enable(s1dfb, 1);
0988 
0989     if (pdata && pdata->platform_resume_video)
0990         return pdata->platform_resume_video();
0991     else
0992         return 0;
0993 }
0994 #endif /* CONFIG_PM */
0995 
0996 static struct platform_driver s1d13xxxfb_driver = {
0997     .probe      = s1d13xxxfb_probe,
0998     .remove     = s1d13xxxfb_remove,
0999 #ifdef CONFIG_PM
1000     .suspend    = s1d13xxxfb_suspend,
1001     .resume     = s1d13xxxfb_resume,
1002 #endif
1003     .driver     = {
1004         .name   = S1D_DEVICENAME,
1005     },
1006 };
1007 
1008 
1009 static int __init
1010 s1d13xxxfb_init(void)
1011 {
1012 
1013 #ifndef MODULE
1014     if (fb_get_options("s1d13xxxfb", NULL))
1015         return -ENODEV;
1016 #endif
1017 
1018     return platform_driver_register(&s1d13xxxfb_driver);
1019 }
1020 
1021 
1022 static void __exit
1023 s1d13xxxfb_exit(void)
1024 {
1025     platform_driver_unregister(&s1d13xxxfb_driver);
1026 }
1027 
1028 module_init(s1d13xxxfb_init);
1029 module_exit(s1d13xxxfb_exit);
1030 
1031 
1032 MODULE_LICENSE("GPL");
1033 MODULE_DESCRIPTION("Framebuffer driver for S1D13xxx devices");
1034 MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>, Thibaut VARENE <varenet@parisc-linux.org>");