Merge branch 'for-2.6.30' into for-2.6.31
[sfrench/cifs-2.6.git] / drivers / video / s1d13xxxfb.c
1 /* drivers/video/s1d13xxxfb.c
2  *
3  * (c) 2004 Simtec Electronics
4  * (c) 2005 Thibaut VARENE <varenet@parisc-linux.org>
5  *
6  * Driver for Epson S1D13xxx series framebuffer chips
7  *
8  * Adapted from
9  *  linux/drivers/video/skeletonfb.c
10  *  linux/drivers/video/epson1355fb.c
11  *  linux/drivers/video/epson/s1d13xxxfb.c (2.4 driver by Epson)
12  *
13  * Note, currently only tested on S1D13806 with 16bit CRT.
14  * As such, this driver might still contain some hardcoded bits relating to
15  * S1D13806.
16  * Making it work on other S1D13XXX chips should merely be a matter of adding
17  * a few switch()s, some missing glue here and there maybe, and split header
18  * files.
19  *
20  * TODO: - handle dual screen display (CRT and LCD at the same time).
21  *       - check_var(), mode change, etc.
22  *       - PM untested.
23  *       - Accelerated interfaces.
24  *       - Probably not SMP safe :)
25  *
26  * This file is subject to the terms and conditions of the GNU General Public
27  * License. See the file COPYING in the main directory of this archive for
28  * more details.
29  */
30
31 #include <linux/module.h>
32 #include <linux/platform_device.h>
33 #include <linux/delay.h>
34
35 #include <linux/types.h>
36 #include <linux/errno.h>
37 #include <linux/mm.h>
38 #include <linux/mman.h>
39 #include <linux/fb.h>
40
41 #include <asm/io.h>
42
43 #include <video/s1d13xxxfb.h>
44
45 #define PFX "s1d13xxxfb: "
46
47 #if 0
48 #define dbg(fmt, args...) do { printk(KERN_INFO fmt, ## args); } while(0)
49 #else
50 #define dbg(fmt, args...) do { } while (0)
51 #endif
52
53 /*
54  * List of card production ids
55  */
56 static const int s1d13xxxfb_prod_ids[] = {
57         S1D13505_PROD_ID,
58         S1D13506_PROD_ID,
59         S1D13806_PROD_ID,
60 };
61
62 /*
63  * List of card strings
64  */
65 static const char *s1d13xxxfb_prod_names[] = {
66         "S1D13505",
67         "S1D13506",
68         "S1D13806",
69 };
70
71 /*
72  * Here we define the default struct fb_fix_screeninfo
73  */
74 static struct fb_fix_screeninfo __devinitdata s1d13xxxfb_fix = {
75         .id             = S1D_FBID,
76         .type           = FB_TYPE_PACKED_PIXELS,
77         .visual         = FB_VISUAL_PSEUDOCOLOR,
78         .xpanstep       = 0,
79         .ypanstep       = 1,
80         .ywrapstep      = 0,
81         .accel          = FB_ACCEL_NONE,
82 };
83
84 static inline u8
85 s1d13xxxfb_readreg(struct s1d13xxxfb_par *par, u16 regno)
86 {
87 #if defined(CONFIG_PLAT_M32700UT) || defined(CONFIG_PLAT_OPSPUT) || defined(CONFIG_PLAT_MAPPI3)
88         regno=((regno & 1) ? (regno & ~1L) : (regno + 1));
89 #endif
90         return readb(par->regs + regno);
91 }
92
93 static inline void
94 s1d13xxxfb_writereg(struct s1d13xxxfb_par *par, u16 regno, u8 value)
95 {
96 #if defined(CONFIG_PLAT_M32700UT) || defined(CONFIG_PLAT_OPSPUT) || defined(CONFIG_PLAT_MAPPI3)
97         regno=((regno & 1) ? (regno & ~1L) : (regno + 1));
98 #endif
99         writeb(value, par->regs + regno);
100 }
101
102 static inline void
103 s1d13xxxfb_runinit(struct s1d13xxxfb_par *par,
104                         const struct s1d13xxxfb_regval *initregs,
105                         const unsigned int size)
106 {
107         int i;
108
109         for (i = 0; i < size; i++) {
110                 if ((initregs[i].addr == S1DREG_DELAYOFF) ||
111                                 (initregs[i].addr == S1DREG_DELAYON))
112                         mdelay((int)initregs[i].value);
113                 else {
114                         s1d13xxxfb_writereg(par, initregs[i].addr, initregs[i].value);
115                 }
116         }
117
118         /* make sure the hardware can cope with us */
119         mdelay(1);
120 }
121
122 static inline void
123 lcd_enable(struct s1d13xxxfb_par *par, int enable)
124 {
125         u8 mode = s1d13xxxfb_readreg(par, S1DREG_COM_DISP_MODE);
126
127         if (enable)
128                 mode |= 0x01;
129         else
130                 mode &= ~0x01;
131
132         s1d13xxxfb_writereg(par, S1DREG_COM_DISP_MODE, mode);
133 }
134
135 static inline void
136 crt_enable(struct s1d13xxxfb_par *par, int enable)
137 {
138         u8 mode = s1d13xxxfb_readreg(par, S1DREG_COM_DISP_MODE);
139
140         if (enable)
141                 mode |= 0x02;
142         else
143                 mode &= ~0x02;
144
145         s1d13xxxfb_writereg(par, S1DREG_COM_DISP_MODE, mode);
146 }
147
148 /* framebuffer control routines */
149
150 static inline void
151 s1d13xxxfb_setup_pseudocolour(struct fb_info *info)
152 {
153         info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
154
155         info->var.red.length = 4;
156         info->var.green.length = 4;
157         info->var.blue.length = 4;
158 }
159
160 static inline void
161 s1d13xxxfb_setup_truecolour(struct fb_info *info)
162 {
163         info->fix.visual = FB_VISUAL_TRUECOLOR;
164         info->var.bits_per_pixel = 16;
165
166         info->var.red.length = 5;
167         info->var.red.offset = 11;
168
169         info->var.green.length = 6;
170         info->var.green.offset = 5;
171
172         info->var.blue.length = 5;
173         info->var.blue.offset = 0;
174 }
175
176 /**
177  *      s1d13xxxfb_set_par - Alters the hardware state.
178  *      @info: frame buffer structure
179  *
180  *      Using the fb_var_screeninfo in fb_info we set the depth of the
181  *      framebuffer. This function alters the par AND the
182  *      fb_fix_screeninfo stored in fb_info. It doesn't not alter var in
183  *      fb_info since we are using that data. This means we depend on the
184  *      data in var inside fb_info to be supported by the hardware.
185  *      xxxfb_check_var is always called before xxxfb_set_par to ensure this.
186  *
187  *      XXX TODO: write proper s1d13xxxfb_check_var(), without which that
188  *      function is quite useless.
189  */
190 static int
191 s1d13xxxfb_set_par(struct fb_info *info)
192 {
193         struct s1d13xxxfb_par *s1dfb = info->par;
194         unsigned int val;
195
196         dbg("s1d13xxxfb_set_par: bpp=%d\n", info->var.bits_per_pixel);
197
198         if ((s1dfb->display & 0x01))    /* LCD */
199                 val = s1d13xxxfb_readreg(s1dfb, S1DREG_LCD_DISP_MODE);   /* read colour control */
200         else    /* CRT */
201                 val = s1d13xxxfb_readreg(s1dfb, S1DREG_CRT_DISP_MODE);   /* read colour control */
202
203         val &= ~0x07;
204
205         switch (info->var.bits_per_pixel) {
206                 case 4:
207                         dbg("pseudo colour 4\n");
208                         s1d13xxxfb_setup_pseudocolour(info);
209                         val |= 2;
210                         break;
211                 case 8:
212                         dbg("pseudo colour 8\n");
213                         s1d13xxxfb_setup_pseudocolour(info);
214                         val |= 3;
215                         break;
216                 case 16:
217                         dbg("true colour\n");
218                         s1d13xxxfb_setup_truecolour(info);
219                         val |= 5;
220                         break;
221
222                 default:
223                         dbg("bpp not supported!\n");
224                         return -EINVAL;
225         }
226
227         dbg("writing %02x to display mode register\n", val);
228
229         if ((s1dfb->display & 0x01))    /* LCD */
230                 s1d13xxxfb_writereg(s1dfb, S1DREG_LCD_DISP_MODE, val);
231         else    /* CRT */
232                 s1d13xxxfb_writereg(s1dfb, S1DREG_CRT_DISP_MODE, val);
233
234         info->fix.line_length  = info->var.xres * info->var.bits_per_pixel;
235         info->fix.line_length /= 8;
236
237         dbg("setting line_length to %d\n", info->fix.line_length);
238
239         dbg("done setup\n");
240
241         return 0;
242 }
243
244 /**
245  *      s1d13xxxfb_setcolreg - sets a color register.
246  *      @regno: Which register in the CLUT we are programming
247  *      @red: The red value which can be up to 16 bits wide
248  *      @green: The green value which can be up to 16 bits wide
249  *      @blue:  The blue value which can be up to 16 bits wide.
250  *      @transp: If supported the alpha value which can be up to 16 bits wide.
251  *      @info: frame buffer info structure
252  *
253  *      Returns negative errno on error, or zero on success.
254  */
255 static int
256 s1d13xxxfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
257                         u_int transp, struct fb_info *info)
258 {
259         struct s1d13xxxfb_par *s1dfb = info->par;
260         unsigned int pseudo_val;
261
262         if (regno >= S1D_PALETTE_SIZE)
263                 return -EINVAL;
264
265         dbg("s1d13xxxfb_setcolreg: %d: rgb=%d,%d,%d, tr=%d\n",
266                     regno, red, green, blue, transp);
267
268         if (info->var.grayscale)
269                 red = green = blue = (19595*red + 38470*green + 7471*blue) >> 16;
270
271         switch (info->fix.visual) {
272                 case FB_VISUAL_TRUECOLOR:
273                         if (regno >= 16)
274                                 return -EINVAL;
275
276                         /* deal with creating pseudo-palette entries */
277
278                         pseudo_val  = (red   >> 11) << info->var.red.offset;
279                         pseudo_val |= (green >> 10) << info->var.green.offset;
280                         pseudo_val |= (blue  >> 11) << info->var.blue.offset;
281
282                         dbg("s1d13xxxfb_setcolreg: pseudo %d, val %08x\n",
283                                     regno, pseudo_val);
284
285 #if defined(CONFIG_PLAT_MAPPI)
286                         ((u32 *)info->pseudo_palette)[regno] = cpu_to_le16(pseudo_val);
287 #else
288                         ((u32 *)info->pseudo_palette)[regno] = pseudo_val;
289 #endif
290
291                         break;
292                 case FB_VISUAL_PSEUDOCOLOR:
293                         s1d13xxxfb_writereg(s1dfb, S1DREG_LKUP_ADDR, regno);
294                         s1d13xxxfb_writereg(s1dfb, S1DREG_LKUP_DATA, red);
295                         s1d13xxxfb_writereg(s1dfb, S1DREG_LKUP_DATA, green);
296                         s1d13xxxfb_writereg(s1dfb, S1DREG_LKUP_DATA, blue);
297
298                         break;
299                 default:
300                         return -ENOSYS;
301         }
302
303         dbg("s1d13xxxfb_setcolreg: done\n");
304
305         return 0;
306 }
307
308 /**
309  *      s1d13xxxfb_blank - blanks the display.
310  *      @blank_mode: the blank mode we want.
311  *      @info: frame buffer structure that represents a single frame buffer
312  *
313  *      Blank the screen if blank_mode != 0, else unblank. Return 0 if
314  *      blanking succeeded, != 0 if un-/blanking failed due to e.g. a
315  *      video mode which doesn't support it. Implements VESA suspend
316  *      and powerdown modes on hardware that supports disabling hsync/vsync:
317  *      blank_mode == 2: suspend vsync
318  *      blank_mode == 3: suspend hsync
319  *      blank_mode == 4: powerdown
320  *
321  *      Returns negative errno on error, or zero on success.
322  */
323 static int
324 s1d13xxxfb_blank(int blank_mode, struct fb_info *info)
325 {
326         struct s1d13xxxfb_par *par = info->par;
327
328         dbg("s1d13xxxfb_blank: blank=%d, info=%p\n", blank_mode, info);
329
330         switch (blank_mode) {
331                 case FB_BLANK_UNBLANK:
332                 case FB_BLANK_NORMAL:
333                         if ((par->display & 0x01) != 0)
334                                 lcd_enable(par, 1);
335                         if ((par->display & 0x02) != 0)
336                                 crt_enable(par, 1);
337                         break;
338                 case FB_BLANK_VSYNC_SUSPEND:
339                 case FB_BLANK_HSYNC_SUSPEND:
340                         break;
341                 case FB_BLANK_POWERDOWN:
342                         lcd_enable(par, 0);
343                         crt_enable(par, 0);
344                         break;
345                 default:
346                         return -EINVAL;
347         }
348
349         /* let fbcon do a soft blank for us */
350         return ((blank_mode == FB_BLANK_NORMAL) ? 1 : 0);
351 }
352
353 /**
354  *      s1d13xxxfb_pan_display - Pans the display.
355  *      @var: frame buffer variable screen structure
356  *      @info: frame buffer structure that represents a single frame buffer
357  *
358  *      Pan (or wrap, depending on the `vmode' field) the display using the
359  *      `yoffset' field of the `var' structure (`xoffset'  not yet supported).
360  *      If the values don't fit, return -EINVAL.
361  *
362  *      Returns negative errno on error, or zero on success.
363  */
364 static int
365 s1d13xxxfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
366 {
367         struct s1d13xxxfb_par *par = info->par;
368         u32 start;
369
370         if (var->xoffset != 0)  /* not yet ... */
371                 return -EINVAL;
372
373         if (var->yoffset + info->var.yres > info->var.yres_virtual)
374                 return -EINVAL;
375
376         start = (info->fix.line_length >> 1) * var->yoffset;
377
378         if ((par->display & 0x01)) {
379                 /* LCD */
380                 s1d13xxxfb_writereg(par, S1DREG_LCD_DISP_START0, (start & 0xff));
381                 s1d13xxxfb_writereg(par, S1DREG_LCD_DISP_START1, ((start >> 8) & 0xff));
382                 s1d13xxxfb_writereg(par, S1DREG_LCD_DISP_START2, ((start >> 16) & 0x0f));
383         } else {
384                 /* CRT */
385                 s1d13xxxfb_writereg(par, S1DREG_CRT_DISP_START0, (start & 0xff));
386                 s1d13xxxfb_writereg(par, S1DREG_CRT_DISP_START1, ((start >> 8) & 0xff));
387                 s1d13xxxfb_writereg(par, S1DREG_CRT_DISP_START2, ((start >> 16) & 0x0f));
388         }
389
390         return 0;
391 }
392
393 /* framebuffer information structures */
394
395 static struct fb_ops s1d13xxxfb_fbops = {
396         .owner          = THIS_MODULE,
397         .fb_set_par     = s1d13xxxfb_set_par,
398         .fb_setcolreg   = s1d13xxxfb_setcolreg,
399         .fb_blank       = s1d13xxxfb_blank,
400
401         .fb_pan_display = s1d13xxxfb_pan_display,
402
403         /* to be replaced by any acceleration we can */
404         .fb_fillrect    = cfb_fillrect,
405         .fb_copyarea    = cfb_copyarea,
406         .fb_imageblit   = cfb_imageblit,
407 };
408
409 static int s1d13xxxfb_width_tab[2][4] __devinitdata = {
410         {4, 8, 16, -1},
411         {9, 12, 18, -1},
412 };
413
414 /**
415  *      s1d13xxxfb_fetch_hw_state - Configure the framebuffer according to
416  *      hardware setup.
417  *      @info: frame buffer structure
418  *
419  *      We setup the framebuffer structures according to the current
420  *      hardware setup. On some machines, the BIOS will have filled
421  *      the chip registers with such info, on others, these values will
422  *      have been written in some init procedure. In any case, the
423  *      software values needs to match the hardware ones. This is what
424  *      this function ensures.
425  *
426  *      Note: some of the hardcoded values here might need some love to
427  *      work on various chips, and might need to no longer be hardcoded.
428  */
429 static void __devinit
430 s1d13xxxfb_fetch_hw_state(struct fb_info *info)
431 {
432         struct fb_var_screeninfo *var = &info->var;
433         struct fb_fix_screeninfo *fix = &info->fix;
434         struct s1d13xxxfb_par *par = info->par;
435         u8 panel, display;
436         u16 offset;
437         u32 xres, yres;
438         u32 xres_virtual, yres_virtual;
439         int bpp, lcd_bpp;
440         int is_color, is_dual, is_tft;
441         int lcd_enabled, crt_enabled;
442
443         fix->type = FB_TYPE_PACKED_PIXELS;
444
445         /* general info */
446         par->display = s1d13xxxfb_readreg(par, S1DREG_COM_DISP_MODE);
447         crt_enabled = (par->display & 0x02) != 0;
448         lcd_enabled = (par->display & 0x01) != 0;
449
450         if (lcd_enabled && crt_enabled)
451                 printk(KERN_WARNING PFX "Warning: LCD and CRT detected, using LCD\n");
452
453         if (lcd_enabled)
454                 display = s1d13xxxfb_readreg(par, S1DREG_LCD_DISP_MODE);
455         else    /* CRT */
456                 display = s1d13xxxfb_readreg(par, S1DREG_CRT_DISP_MODE);
457
458         bpp = display & 0x07;
459
460         switch (bpp) {
461                 case 2: /* 4 bpp */
462                 case 3: /* 8 bpp */
463                         var->bits_per_pixel = 8;
464                         var->red.offset = var->green.offset = var->blue.offset = 0;
465                         var->red.length = var->green.length = var->blue.length = 8;
466                         break;
467                 case 5: /* 16 bpp */
468                         s1d13xxxfb_setup_truecolour(info);
469                         break;
470                 default:
471                         dbg("bpp: %i\n", bpp);
472         }
473         fb_alloc_cmap(&info->cmap, 256, 0);
474
475         /* LCD info */
476         panel = s1d13xxxfb_readreg(par, S1DREG_PANEL_TYPE);
477         is_color = (panel & 0x04) != 0;
478         is_dual = (panel & 0x02) != 0;
479         is_tft = (panel & 0x01) != 0;
480         lcd_bpp = s1d13xxxfb_width_tab[is_tft][(panel >> 4) & 3];
481
482         if (lcd_enabled) {
483                 xres = (s1d13xxxfb_readreg(par, S1DREG_LCD_DISP_HWIDTH) + 1) * 8;
484                 yres = (s1d13xxxfb_readreg(par, S1DREG_LCD_DISP_VHEIGHT0) +
485                         ((s1d13xxxfb_readreg(par, S1DREG_LCD_DISP_VHEIGHT1) & 0x03) << 8) + 1);
486
487                 offset = (s1d13xxxfb_readreg(par, S1DREG_LCD_MEM_OFF0) +
488                         ((s1d13xxxfb_readreg(par, S1DREG_LCD_MEM_OFF1) & 0x7) << 8));
489         } else { /* crt */
490                 xres = (s1d13xxxfb_readreg(par, S1DREG_CRT_DISP_HWIDTH) + 1) * 8;
491                 yres = (s1d13xxxfb_readreg(par, S1DREG_CRT_DISP_VHEIGHT0) +
492                         ((s1d13xxxfb_readreg(par, S1DREG_CRT_DISP_VHEIGHT1) & 0x03) << 8) + 1);
493
494                 offset = (s1d13xxxfb_readreg(par, S1DREG_CRT_MEM_OFF0) +
495                         ((s1d13xxxfb_readreg(par, S1DREG_CRT_MEM_OFF1) & 0x7) << 8));
496         }
497         xres_virtual = offset * 16 / var->bits_per_pixel;
498         yres_virtual = fix->smem_len / (offset * 2);
499
500         var->xres               = xres;
501         var->yres               = yres;
502         var->xres_virtual       = xres_virtual;
503         var->yres_virtual       = yres_virtual;
504         var->xoffset            = var->yoffset = 0;
505
506         fix->line_length        = offset * 2;
507
508         var->grayscale          = !is_color;
509
510         var->activate           = FB_ACTIVATE_NOW;
511
512         dbg(PFX "bpp=%d, lcd_bpp=%d, "
513                 "crt_enabled=%d, lcd_enabled=%d\n",
514                 var->bits_per_pixel, lcd_bpp, crt_enabled, lcd_enabled);
515         dbg(PFX "xres=%d, yres=%d, vxres=%d, vyres=%d "
516                 "is_color=%d, is_dual=%d, is_tft=%d\n",
517                 xres, yres, xres_virtual, yres_virtual, is_color, is_dual, is_tft);
518 }
519
520
521 static int
522 s1d13xxxfb_remove(struct platform_device *pdev)
523 {
524         struct fb_info *info = platform_get_drvdata(pdev);
525         struct s1d13xxxfb_par *par = NULL;
526
527         if (info) {
528                 par = info->par;
529                 if (par && par->regs) {
530                         /* disable output & enable powersave */
531                         s1d13xxxfb_writereg(par, S1DREG_COM_DISP_MODE, 0x00);
532                         s1d13xxxfb_writereg(par, S1DREG_PS_CNF, 0x11);
533                         iounmap(par->regs);
534                 }
535
536                 fb_dealloc_cmap(&info->cmap);
537
538                 if (info->screen_base)
539                         iounmap(info->screen_base);
540
541                 framebuffer_release(info);
542         }
543
544         release_mem_region(pdev->resource[0].start,
545                         pdev->resource[0].end - pdev->resource[0].start +1);
546         release_mem_region(pdev->resource[1].start,
547                         pdev->resource[1].end - pdev->resource[1].start +1);
548         return 0;
549 }
550
551 static int __devinit
552 s1d13xxxfb_probe(struct platform_device *pdev)
553 {
554         struct s1d13xxxfb_par *default_par;
555         struct fb_info *info;
556         struct s1d13xxxfb_pdata *pdata = NULL;
557         int ret = 0;
558         int i;
559         u8 revision, prod_id;
560
561         dbg("probe called: device is %p\n", pdev);
562
563         printk(KERN_INFO "Epson S1D13XXX FB Driver\n");
564
565         /* enable platform-dependent hardware glue, if any */
566         if (pdev->dev.platform_data)
567                 pdata = pdev->dev.platform_data;
568
569         if (pdata && pdata->platform_init_video)
570                 pdata->platform_init_video();
571
572
573         if (pdev->num_resources != 2) {
574                 dev_err(&pdev->dev, "invalid num_resources: %i\n",
575                        pdev->num_resources);
576                 ret = -ENODEV;
577                 goto bail;
578         }
579
580         /* resource[0] is VRAM, resource[1] is registers */
581         if (pdev->resource[0].flags != IORESOURCE_MEM
582                         || pdev->resource[1].flags != IORESOURCE_MEM) {
583                 dev_err(&pdev->dev, "invalid resource type\n");
584                 ret = -ENODEV;
585                 goto bail;
586         }
587
588         if (!request_mem_region(pdev->resource[0].start,
589                 pdev->resource[0].end - pdev->resource[0].start +1, "s1d13xxxfb mem")) {
590                 dev_dbg(&pdev->dev, "request_mem_region failed\n");
591                 ret = -EBUSY;
592                 goto bail;
593         }
594
595         if (!request_mem_region(pdev->resource[1].start,
596                 pdev->resource[1].end - pdev->resource[1].start +1, "s1d13xxxfb regs")) {
597                 dev_dbg(&pdev->dev, "request_mem_region failed\n");
598                 ret = -EBUSY;
599                 goto bail;
600         }
601
602         info = framebuffer_alloc(sizeof(struct s1d13xxxfb_par) + sizeof(u32) * 256, &pdev->dev);
603         if (!info) {
604                 ret = -ENOMEM;
605                 goto bail;
606         }
607
608         platform_set_drvdata(pdev, info);
609         default_par = info->par;
610         default_par->regs = ioremap_nocache(pdev->resource[1].start,
611                         pdev->resource[1].end - pdev->resource[1].start +1);
612         if (!default_par->regs) {
613                 printk(KERN_ERR PFX "unable to map registers\n");
614                 ret = -ENOMEM;
615                 goto bail;
616         }
617         info->pseudo_palette = default_par->pseudo_palette;
618
619         info->screen_base = ioremap_nocache(pdev->resource[0].start,
620                         pdev->resource[0].end - pdev->resource[0].start +1);
621
622         if (!info->screen_base) {
623                 printk(KERN_ERR PFX "unable to map framebuffer\n");
624                 ret = -ENOMEM;
625                 goto bail;
626         }
627
628         /* production id is top 6 bits */
629         prod_id = s1d13xxxfb_readreg(default_par, S1DREG_REV_CODE) >> 2;
630         /* revision id is lower 2 bits */
631         revision = s1d13xxxfb_readreg(default_par, S1DREG_REV_CODE) & 0x3;
632         ret = -ENODEV;
633
634         for (i = 0; i < ARRAY_SIZE(s1d13xxxfb_prod_ids); i++) {
635                 if (prod_id == s1d13xxxfb_prod_ids[i]) {
636                         /* looks like we got it in our list */
637                         default_par->prod_id = prod_id;
638                         default_par->revision = revision;
639                         ret = 0;
640                         break;
641                 }
642         }
643
644         if (!ret) {
645                 printk(KERN_INFO PFX "chip production id %i = %s\n",
646                         prod_id, s1d13xxxfb_prod_names[i]);
647                 printk(KERN_INFO PFX "chip revision %i\n", revision);
648         } else {
649                 printk(KERN_INFO PFX
650                         "unknown chip production id %i, revision %i\n",
651                         prod_id, revision);
652                 printk(KERN_INFO PFX "please contant maintainer\n");
653                 goto bail;
654         }
655
656         info->fix = s1d13xxxfb_fix;
657         info->fix.mmio_start = pdev->resource[1].start;
658         info->fix.mmio_len = pdev->resource[1].end - pdev->resource[1].start +1;
659         info->fix.smem_start = pdev->resource[0].start;
660         info->fix.smem_len = pdev->resource[0].end - pdev->resource[0].start +1;
661
662         printk(KERN_INFO PFX "regs mapped at 0x%p, fb %d KiB mapped at 0x%p\n",
663                default_par->regs, info->fix.smem_len / 1024, info->screen_base);
664
665         info->par = default_par;
666         info->fbops = &s1d13xxxfb_fbops;
667         info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN;
668
669         /* perform "manual" chip initialization, if needed */
670         if (pdata && pdata->initregs)
671                 s1d13xxxfb_runinit(info->par, pdata->initregs, pdata->initregssize);
672
673         s1d13xxxfb_fetch_hw_state(info);
674
675         if (register_framebuffer(info) < 0) {
676                 ret = -EINVAL;
677                 goto bail;
678         }
679
680         printk(KERN_INFO "fb%d: %s frame buffer device\n",
681                info->node, info->fix.id);
682
683         return 0;
684
685 bail:
686         s1d13xxxfb_remove(pdev);
687         return ret;
688
689 }
690
691 #ifdef CONFIG_PM
692 static int s1d13xxxfb_suspend(struct platform_device *dev, pm_message_t state)
693 {
694         struct fb_info *info = platform_get_drvdata(dev);
695         struct s1d13xxxfb_par *s1dfb = info->par;
696         struct s1d13xxxfb_pdata *pdata = NULL;
697
698         /* disable display */
699         lcd_enable(s1dfb, 0);
700         crt_enable(s1dfb, 0);
701
702         if (dev->dev.platform_data)
703                 pdata = dev->dev.platform_data;
704
705 #if 0
706         if (!s1dfb->disp_save)
707                 s1dfb->disp_save = kmalloc(info->fix.smem_len, GFP_KERNEL);
708
709         if (!s1dfb->disp_save) {
710                 printk(KERN_ERR PFX "no memory to save screen");
711                 return -ENOMEM;
712         }
713
714         memcpy_fromio(s1dfb->disp_save, info->screen_base, info->fix.smem_len);
715 #else
716         s1dfb->disp_save = NULL;
717 #endif
718
719         if (!s1dfb->regs_save)
720                 s1dfb->regs_save = kmalloc(info->fix.mmio_len, GFP_KERNEL);
721
722         if (!s1dfb->regs_save) {
723                 printk(KERN_ERR PFX "no memory to save registers");
724                 return -ENOMEM;
725         }
726
727         /* backup all registers */
728         memcpy_fromio(s1dfb->regs_save, s1dfb->regs, info->fix.mmio_len);
729
730         /* now activate power save mode */
731         s1d13xxxfb_writereg(s1dfb, S1DREG_PS_CNF, 0x11);
732
733         if (pdata && pdata->platform_suspend_video)
734                 return pdata->platform_suspend_video();
735         else
736                 return 0;
737 }
738
739 static int s1d13xxxfb_resume(struct platform_device *dev)
740 {
741         struct fb_info *info = platform_get_drvdata(dev);
742         struct s1d13xxxfb_par *s1dfb = info->par;
743         struct s1d13xxxfb_pdata *pdata = NULL;
744
745         /* awaken the chip */
746         s1d13xxxfb_writereg(s1dfb, S1DREG_PS_CNF, 0x10);
747
748         /* do not let go until SDRAM "wakes up" */
749         while ((s1d13xxxfb_readreg(s1dfb, S1DREG_PS_STATUS) & 0x01))
750                 udelay(10);
751
752         if (dev->dev.platform_data)
753                 pdata = dev->dev.platform_data;
754
755         if (s1dfb->regs_save) {
756                 /* will write RO regs, *should* get away with it :) */
757                 memcpy_toio(s1dfb->regs, s1dfb->regs_save, info->fix.mmio_len);
758                 kfree(s1dfb->regs_save);
759         }
760
761         if (s1dfb->disp_save) {
762                 memcpy_toio(info->screen_base, s1dfb->disp_save,
763                                 info->fix.smem_len);
764                 kfree(s1dfb->disp_save);        /* XXX kmalloc()'d when? */
765         }
766
767         if ((s1dfb->display & 0x01) != 0)
768                 lcd_enable(s1dfb, 1);
769         if ((s1dfb->display & 0x02) != 0)
770                 crt_enable(s1dfb, 1);
771
772         if (pdata && pdata->platform_resume_video)
773                 return pdata->platform_resume_video();
774         else
775                 return 0;
776 }
777 #endif /* CONFIG_PM */
778
779 static struct platform_driver s1d13xxxfb_driver = {
780         .probe          = s1d13xxxfb_probe,
781         .remove         = s1d13xxxfb_remove,
782 #ifdef CONFIG_PM
783         .suspend        = s1d13xxxfb_suspend,
784         .resume         = s1d13xxxfb_resume,
785 #endif
786         .driver         = {
787                 .name   = S1D_DEVICENAME,
788         },
789 };
790
791
792 static int __init
793 s1d13xxxfb_init(void)
794 {
795
796 #ifndef MODULE
797         if (fb_get_options("s1d13xxxfb", NULL))
798                 return -ENODEV;
799 #endif
800
801         return platform_driver_register(&s1d13xxxfb_driver);
802 }
803
804
805 static void __exit
806 s1d13xxxfb_exit(void)
807 {
808         platform_driver_unregister(&s1d13xxxfb_driver);
809 }
810
811 module_init(s1d13xxxfb_init);
812 module_exit(s1d13xxxfb_exit);
813
814
815 MODULE_LICENSE("GPL");
816 MODULE_DESCRIPTION("Framebuffer driver for S1D13xxx devices");
817 MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>, Thibaut VARENE <varenet@parisc-linux.org>");