s3c-fb: add support for display panning
[platform/adaptation/renesas_rcar/renesas_kernel.git] / drivers / video / s3c-fb.c
1 /* linux/drivers/video/s3c-fb.c
2  *
3  * Copyright 2008 Openmoko Inc.
4  * Copyright 2008-2010 Simtec Electronics
5  *      Ben Dooks <ben@simtec.co.uk>
6  *      http://armlinux.simtec.co.uk/
7  *
8  * Samsung SoC Framebuffer driver
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License version 2 as
12  * published by the Free Software FoundatIon.
13 */
14
15 #include <linux/kernel.h>
16 #include <linux/module.h>
17 #include <linux/platform_device.h>
18 #include <linux/dma-mapping.h>
19 #include <linux/slab.h>
20 #include <linux/init.h>
21 #include <linux/clk.h>
22 #include <linux/fb.h>
23 #include <linux/io.h>
24
25 #include <mach/map.h>
26 #include <plat/regs-fb-v4.h>
27 #include <plat/fb.h>
28
29 /* This driver will export a number of framebuffer interfaces depending
30  * on the configuration passed in via the platform data. Each fb instance
31  * maps to a hardware window. Currently there is no support for runtime
32  * setting of the alpha-blending functions that each window has, so only
33  * window 0 is actually useful.
34  *
35  * Window 0 is treated specially, it is used for the basis of the LCD
36  * output timings and as the control for the output power-down state.
37 */
38
39 /* note, the previous use of <mach/regs-fb.h> to get platform specific data
40  * has been replaced by using the platform device name to pick the correct
41  * configuration data for the system.
42 */
43
44 #ifdef CONFIG_FB_S3C_DEBUG_REGWRITE
45 #undef writel
46 #define writel(v, r) do { \
47         printk(KERN_DEBUG "%s: %08x => %p\n", __func__, (unsigned int)v, r); \
48         __raw_writel(v, r); } while(0)
49 #endif /* FB_S3C_DEBUG_REGWRITE */
50
51 struct s3c_fb;
52
53 #define VALID_BPP(x) (1 << ((x) - 1))
54
55 #define OSD_BASE(win, variant) ((variant).osd + ((win) * (variant).osd_stride))
56 #define VIDOSD_A(win, variant) (OSD_BASE(win, variant) + 0x00)
57 #define VIDOSD_B(win, variant) (OSD_BASE(win, variant) + 0x04)
58 #define VIDOSD_C(win, variant) (OSD_BASE(win, variant) + 0x08)
59 #define VIDOSD_D(win, variant) (OSD_BASE(win, variant) + 0x0C)
60
61 /**
62  * struct s3c_fb_variant - fb variant information
63  * @is_2443: Set if S3C2443/S3C2416 style hardware.
64  * @nr_windows: The number of windows.
65  * @vidtcon: The base for the VIDTCONx registers
66  * @wincon: The base for the WINxCON registers.
67  * @winmap: The base for the WINxMAP registers.
68  * @keycon: The abse for the WxKEYCON registers.
69  * @buf_start: Offset of buffer start registers.
70  * @buf_size: Offset of buffer size registers.
71  * @buf_end: Offset of buffer end registers.
72  * @osd: The base for the OSD registers.
73  * @palette: Address of palette memory, or 0 if none.
74  * @has_prtcon: Set if has PRTCON register.
75  */
76 struct s3c_fb_variant {
77         unsigned int    is_2443:1;
78         unsigned short  nr_windows;
79         unsigned short  vidtcon;
80         unsigned short  wincon;
81         unsigned short  winmap;
82         unsigned short  keycon;
83         unsigned short  buf_start;
84         unsigned short  buf_end;
85         unsigned short  buf_size;
86         unsigned short  osd;
87         unsigned short  osd_stride;
88         unsigned short  palette[S3C_FB_MAX_WIN];
89
90         unsigned int    has_prtcon:1;
91 };
92
93 /**
94  * struct s3c_fb_win_variant
95  * @has_osd_c: Set if has OSD C register.
96  * @has_osd_d: Set if has OSD D register.
97  * @palette_sz: Size of palette in entries.
98  * @palette_16bpp: Set if palette is 16bits wide.
99  * @valid_bpp: 1 bit per BPP setting to show valid bits-per-pixel.
100  *
101  * valid_bpp bit x is set if (x+1)BPP is supported.
102  */
103 struct s3c_fb_win_variant {
104         unsigned int    has_osd_c:1;
105         unsigned int    has_osd_d:1;
106         unsigned int    palette_16bpp:1;
107         unsigned short  palette_sz;
108         u32             valid_bpp;
109 };
110
111 /**
112  * struct s3c_fb_driverdata - per-device type driver data for init time.
113  * @variant: The variant information for this driver.
114  * @win: The window information for each window.
115  */
116 struct s3c_fb_driverdata {
117         struct s3c_fb_variant   variant;
118         struct s3c_fb_win_variant *win[S3C_FB_MAX_WIN];
119 };
120
121 /**
122  * struct s3c_fb_palette - palette information
123  * @r: Red bitfield.
124  * @g: Green bitfield.
125  * @b: Blue bitfield.
126  * @a: Alpha bitfield.
127  */
128 struct s3c_fb_palette {
129         struct fb_bitfield      r;
130         struct fb_bitfield      g;
131         struct fb_bitfield      b;
132         struct fb_bitfield      a;
133 };
134
135 /**
136  * struct s3c_fb_win - per window private data for each framebuffer.
137  * @windata: The platform data supplied for the window configuration.
138  * @parent: The hardware that this window is part of.
139  * @fbinfo: Pointer pack to the framebuffer info for this window.
140  * @varint: The variant information for this window.
141  * @palette_buffer: Buffer/cache to hold palette entries.
142  * @pseudo_palette: For use in TRUECOLOUR modes for entries 0..15/
143  * @index: The window number of this window.
144  * @palette: The bitfields for changing r/g/b into a hardware palette entry.
145  */
146 struct s3c_fb_win {
147         struct s3c_fb_pd_win    *windata;
148         struct s3c_fb           *parent;
149         struct fb_info          *fbinfo;
150         struct s3c_fb_palette    palette;
151         struct s3c_fb_win_variant variant;
152
153         u32                     *palette_buffer;
154         u32                      pseudo_palette[16];
155         unsigned int             index;
156 };
157
158 /**
159  * struct s3c_fb - overall hardware state of the hardware
160  * @dev: The device that we bound to, for printing, etc.
161  * @regs_res: The resource we claimed for the IO registers.
162  * @bus_clk: The clk (hclk) feeding our interface and possibly pixclk.
163  * @regs: The mapped hardware registers.
164  * @variant: Variant information for this hardware.
165  * @enabled: A bitmask of enabled hardware windows.
166  * @pdata: The platform configuration data passed with the device.
167  * @windows: The hardware windows that have been claimed.
168  */
169 struct s3c_fb {
170         struct device           *dev;
171         struct resource         *regs_res;
172         struct clk              *bus_clk;
173         void __iomem            *regs;
174         struct s3c_fb_variant    variant;
175
176         unsigned char            enabled;
177
178         struct s3c_fb_platdata  *pdata;
179         struct s3c_fb_win       *windows[S3C_FB_MAX_WIN];
180 };
181
182 /**
183  * s3c_fb_validate_win_bpp - validate the bits-per-pixel for this mode.
184  * @win: The device window.
185  * @bpp: The bit depth.
186  */
187 static bool s3c_fb_validate_win_bpp(struct s3c_fb_win *win, unsigned int bpp)
188 {
189         return win->variant.valid_bpp & VALID_BPP(bpp);
190 }
191
192 /**
193  * s3c_fb_check_var() - framebuffer layer request to verify a given mode.
194  * @var: The screen information to verify.
195  * @info: The framebuffer device.
196  *
197  * Framebuffer layer call to verify the given information and allow us to
198  * update various information depending on the hardware capabilities.
199  */
200 static int s3c_fb_check_var(struct fb_var_screeninfo *var,
201                             struct fb_info *info)
202 {
203         struct s3c_fb_win *win = info->par;
204         struct s3c_fb_pd_win *windata = win->windata;
205         struct s3c_fb *sfb = win->parent;
206
207         dev_dbg(sfb->dev, "checking parameters\n");
208
209         var->xres_virtual = max((unsigned int)windata->virtual_x, var->xres);
210         var->yres_virtual = max((unsigned int)windata->virtual_y, var->yres);
211
212         if (!s3c_fb_validate_win_bpp(win, var->bits_per_pixel)) {
213                 dev_dbg(sfb->dev, "win %d: unsupported bpp %d\n",
214                         win->index, var->bits_per_pixel);
215                 return -EINVAL;
216         }
217
218         /* always ensure these are zero, for drop through cases below */
219         var->transp.offset = 0;
220         var->transp.length = 0;
221
222         switch (var->bits_per_pixel) {
223         case 1:
224         case 2:
225         case 4:
226         case 8:
227                 if (sfb->variant.palette[win->index] != 0) {
228                         /* non palletised, A:1,R:2,G:3,B:2 mode */
229                         var->red.offset         = 4;
230                         var->green.offset       = 2;
231                         var->blue.offset        = 0;
232                         var->red.length         = 5;
233                         var->green.length       = 3;
234                         var->blue.length        = 2;
235                         var->transp.offset      = 7;
236                         var->transp.length      = 1;
237                 } else {
238                         var->red.offset = 0;
239                         var->red.length = var->bits_per_pixel;
240                         var->green      = var->red;
241                         var->blue       = var->red;
242                 }
243                 break;
244
245         case 19:
246                 /* 666 with one bit alpha/transparency */
247                 var->transp.offset      = 18;
248                 var->transp.length      = 1;
249         case 18:
250                 var->bits_per_pixel     = 32;
251
252                 /* 666 format */
253                 var->red.offset         = 12;
254                 var->green.offset       = 6;
255                 var->blue.offset        = 0;
256                 var->red.length         = 6;
257                 var->green.length       = 6;
258                 var->blue.length        = 6;
259                 break;
260
261         case 16:
262                 /* 16 bpp, 565 format */
263                 var->red.offset         = 11;
264                 var->green.offset       = 5;
265                 var->blue.offset        = 0;
266                 var->red.length         = 5;
267                 var->green.length       = 6;
268                 var->blue.length        = 5;
269                 break;
270
271         case 28:
272         case 25:
273                 var->transp.length      = var->bits_per_pixel - 24;
274                 var->transp.offset      = 24;
275                 /* drop through */
276         case 24:
277                 /* our 24bpp is unpacked, so 32bpp */
278                 var->bits_per_pixel     = 32;
279         case 32:
280                 var->red.offset         = 16;
281                 var->red.length         = 8;
282                 var->green.offset       = 8;
283                 var->green.length       = 8;
284                 var->blue.offset        = 0;
285                 var->blue.length        = 8;
286                 break;
287
288         default:
289                 dev_err(sfb->dev, "invalid bpp\n");
290         }
291
292         dev_dbg(sfb->dev, "%s: verified parameters\n", __func__);
293         return 0;
294 }
295
296 /**
297  * s3c_fb_calc_pixclk() - calculate the divider to create the pixel clock.
298  * @sfb: The hardware state.
299  * @pixclock: The pixel clock wanted, in picoseconds.
300  *
301  * Given the specified pixel clock, work out the necessary divider to get
302  * close to the output frequency.
303  */
304 static int s3c_fb_calc_pixclk(struct s3c_fb *sfb, unsigned int pixclk)
305 {
306         unsigned long clk = clk_get_rate(sfb->bus_clk);
307         unsigned long long tmp;
308         unsigned int result;
309
310         tmp = (unsigned long long)clk;
311         tmp *= pixclk;
312
313         do_div(tmp, 1000000000UL);
314         result = (unsigned int)tmp / 1000;
315
316         dev_dbg(sfb->dev, "pixclk=%u, clk=%lu, div=%d (%lu)\n",
317                 pixclk, clk, result, clk / result);
318
319         return result;
320 }
321
322 /**
323  * s3c_fb_align_word() - align pixel count to word boundary
324  * @bpp: The number of bits per pixel
325  * @pix: The value to be aligned.
326  *
327  * Align the given pixel count so that it will start on an 32bit word
328  * boundary.
329  */
330 static int s3c_fb_align_word(unsigned int bpp, unsigned int pix)
331 {
332         int pix_per_word;
333
334         if (bpp > 16)
335                 return pix;
336
337         pix_per_word = (8 * 32) / bpp;
338         return ALIGN(pix, pix_per_word);
339 }
340
341 /**
342  * s3c_fb_set_par() - framebuffer request to set new framebuffer state.
343  * @info: The framebuffer to change.
344  *
345  * Framebuffer layer request to set a new mode for the specified framebuffer
346  */
347 static int s3c_fb_set_par(struct fb_info *info)
348 {
349         struct fb_var_screeninfo *var = &info->var;
350         struct s3c_fb_win *win = info->par;
351         struct s3c_fb *sfb = win->parent;
352         void __iomem *regs = sfb->regs;
353         void __iomem *buf = regs;
354         int win_no = win->index;
355         u32 osdc_data = 0;
356         u32 data;
357         u32 pagewidth;
358         int clkdiv;
359
360         dev_dbg(sfb->dev, "setting framebuffer parameters\n");
361
362         switch (var->bits_per_pixel) {
363         case 32:
364         case 24:
365         case 16:
366         case 12:
367                 info->fix.visual = FB_VISUAL_TRUECOLOR;
368                 break;
369         case 8:
370                 if (win->variant.palette_sz >= 256)
371                         info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
372                 else
373                         info->fix.visual = FB_VISUAL_TRUECOLOR;
374                 break;
375         case 1:
376                 info->fix.visual = FB_VISUAL_MONO01;
377                 break;
378         default:
379                 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
380                 break;
381         }
382
383         info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8;
384
385         info->fix.xpanstep = info->var.xres_virtual > info->var.xres ? 1 : 0;
386         info->fix.ypanstep = info->var.yres_virtual > info->var.yres ? 1 : 0;
387
388         /* disable the window whilst we update it */
389         writel(0, regs + WINCON(win_no));
390
391         /* use platform specified window as the basis for the lcd timings */
392
393         if (win_no == sfb->pdata->default_win) {
394                 clkdiv = s3c_fb_calc_pixclk(sfb, var->pixclock);
395
396                 data = sfb->pdata->vidcon0;
397                 data &= ~(VIDCON0_CLKVAL_F_MASK | VIDCON0_CLKDIR);
398
399                 if (clkdiv > 1)
400                         data |= VIDCON0_CLKVAL_F(clkdiv-1) | VIDCON0_CLKDIR;
401                 else
402                         data &= ~VIDCON0_CLKDIR;        /* 1:1 clock */
403
404                 /* write the timing data to the panel */
405
406                 if (sfb->variant.is_2443)
407                         data |= (1 << 5);
408
409                 data |= VIDCON0_ENVID | VIDCON0_ENVID_F;
410                 writel(data, regs + VIDCON0);
411
412                 data = VIDTCON0_VBPD(var->upper_margin - 1) |
413                        VIDTCON0_VFPD(var->lower_margin - 1) |
414                        VIDTCON0_VSPW(var->vsync_len - 1);
415
416                 writel(data, regs + sfb->variant.vidtcon);
417
418                 data = VIDTCON1_HBPD(var->left_margin - 1) |
419                        VIDTCON1_HFPD(var->right_margin - 1) |
420                        VIDTCON1_HSPW(var->hsync_len - 1);
421
422                 /* VIDTCON1 */
423                 writel(data, regs + sfb->variant.vidtcon + 4);
424
425                 data = VIDTCON2_LINEVAL(var->yres - 1) |
426                        VIDTCON2_HOZVAL(var->xres - 1);
427                 writel(data, regs +sfb->variant.vidtcon + 8 );
428         }
429
430         /* write the buffer address */
431
432         /* start and end registers stride is 8 */
433         buf = regs + win_no * 8;
434
435         writel(info->fix.smem_start, buf + sfb->variant.buf_start);
436
437         data = info->fix.smem_start + info->fix.line_length * var->yres;
438         writel(data, buf + sfb->variant.buf_end);
439
440         pagewidth = (var->xres * var->bits_per_pixel) >> 3;
441         data = VIDW_BUF_SIZE_OFFSET(info->fix.line_length - pagewidth) |
442                VIDW_BUF_SIZE_PAGEWIDTH(pagewidth);
443         writel(data, regs + sfb->variant.buf_size + (win_no * 4));
444
445         /* write 'OSD' registers to control position of framebuffer */
446
447         data = VIDOSDxA_TOPLEFT_X(0) | VIDOSDxA_TOPLEFT_Y(0);
448         writel(data, regs + VIDOSD_A(win_no, sfb->variant));
449
450         data = VIDOSDxB_BOTRIGHT_X(s3c_fb_align_word(var->bits_per_pixel,
451                                                      var->xres - 1)) |
452                VIDOSDxB_BOTRIGHT_Y(var->yres - 1);
453
454         writel(data, regs + VIDOSD_B(win_no, sfb->variant));
455
456         data = var->xres * var->yres;
457
458         osdc_data = VIDISD14C_ALPHA1_R(0xf) |
459                 VIDISD14C_ALPHA1_G(0xf) |
460                 VIDISD14C_ALPHA1_B(0xf);
461
462         if (win->variant.has_osd_d) {
463                 writel(data, regs + VIDOSD_D(win_no, sfb->variant));
464                 writel(osdc_data, regs + VIDOSD_C(win_no, sfb->variant));
465         } else
466                 writel(data, regs + VIDOSD_C(win_no, sfb->variant));
467
468         data = WINCONx_ENWIN;
469
470         /* note, since we have to round up the bits-per-pixel, we end up
471          * relying on the bitfield information for r/g/b/a to work out
472          * exactly which mode of operation is intended. */
473
474         switch (var->bits_per_pixel) {
475         case 1:
476                 data |= WINCON0_BPPMODE_1BPP;
477                 data |= WINCONx_BITSWP;
478                 data |= WINCONx_BURSTLEN_4WORD;
479                 break;
480         case 2:
481                 data |= WINCON0_BPPMODE_2BPP;
482                 data |= WINCONx_BITSWP;
483                 data |= WINCONx_BURSTLEN_8WORD;
484                 break;
485         case 4:
486                 data |= WINCON0_BPPMODE_4BPP;
487                 data |= WINCONx_BITSWP;
488                 data |= WINCONx_BURSTLEN_8WORD;
489                 break;
490         case 8:
491                 if (var->transp.length != 0)
492                         data |= WINCON1_BPPMODE_8BPP_1232;
493                 else
494                         data |= WINCON0_BPPMODE_8BPP_PALETTE;
495                 data |= WINCONx_BURSTLEN_8WORD;
496                 data |= WINCONx_BYTSWP;
497                 break;
498         case 16:
499                 if (var->transp.length != 0)
500                         data |= WINCON1_BPPMODE_16BPP_A1555;
501                 else
502                         data |= WINCON0_BPPMODE_16BPP_565;
503                 data |= WINCONx_HAWSWP;
504                 data |= WINCONx_BURSTLEN_16WORD;
505                 break;
506         case 24:
507         case 32:
508                 if (var->red.length == 6) {
509                         if (var->transp.length != 0)
510                                 data |= WINCON1_BPPMODE_19BPP_A1666;
511                         else
512                                 data |= WINCON1_BPPMODE_18BPP_666;
513                 } else if (var->transp.length == 1)
514                         data |= WINCON1_BPPMODE_25BPP_A1888
515                                 | WINCON1_BLD_PIX;
516                 else if (var->transp.length == 4)
517                         data |= WINCON1_BPPMODE_28BPP_A4888
518                                 | WINCON1_BLD_PIX | WINCON1_ALPHA_SEL;
519                 else
520                         data |= WINCON0_BPPMODE_24BPP_888;
521
522                 data |= WINCONx_WSWP;
523                 data |= WINCONx_BURSTLEN_16WORD;
524                 break;
525         }
526
527         /* Enable the colour keying for the window below this one */
528         if (win_no > 0) {
529                 u32 keycon0_data = 0, keycon1_data = 0;
530                 void __iomem *keycon = regs + sfb->variant.keycon;
531
532                 keycon0_data = ~(WxKEYCON0_KEYBL_EN |
533                                 WxKEYCON0_KEYEN_F |
534                                 WxKEYCON0_DIRCON) | WxKEYCON0_COMPKEY(0);
535
536                 keycon1_data = WxKEYCON1_COLVAL(0xffffff);
537
538                 keycon += (win_no - 1) * 8;
539
540                 writel(keycon0_data, keycon + WKEYCON0);
541                 writel(keycon1_data, keycon + WKEYCON1);
542         }
543
544         writel(data, regs + sfb->variant.wincon + (win_no * 4));
545         writel(0x0, regs + sfb->variant.winmap + (win_no * 4));
546
547         return 0;
548 }
549
550 /**
551  * s3c_fb_update_palette() - set or schedule a palette update.
552  * @sfb: The hardware information.
553  * @win: The window being updated.
554  * @reg: The palette index being changed.
555  * @value: The computed palette value.
556  *
557  * Change the value of a palette register, either by directly writing to
558  * the palette (this requires the palette RAM to be disconnected from the
559  * hardware whilst this is in progress) or schedule the update for later.
560  *
561  * At the moment, since we have no VSYNC interrupt support, we simply set
562  * the palette entry directly.
563  */
564 static void s3c_fb_update_palette(struct s3c_fb *sfb,
565                                   struct s3c_fb_win *win,
566                                   unsigned int reg,
567                                   u32 value)
568 {
569         void __iomem *palreg;
570         u32 palcon;
571
572         palreg = sfb->regs + sfb->variant.palette[win->index];
573
574         dev_dbg(sfb->dev, "%s: win %d, reg %d (%p): %08x\n",
575                 __func__, win->index, reg, palreg, value);
576
577         win->palette_buffer[reg] = value;
578
579         palcon = readl(sfb->regs + WPALCON);
580         writel(palcon | WPALCON_PAL_UPDATE, sfb->regs + WPALCON);
581
582         if (win->variant.palette_16bpp)
583                 writew(value, palreg + (reg * 2));
584         else
585                 writel(value, palreg + (reg * 4));
586
587         writel(palcon, sfb->regs + WPALCON);
588 }
589
590 static inline unsigned int chan_to_field(unsigned int chan,
591                                          struct fb_bitfield *bf)
592 {
593         chan &= 0xffff;
594         chan >>= 16 - bf->length;
595         return chan << bf->offset;
596 }
597
598 /**
599  * s3c_fb_setcolreg() - framebuffer layer request to change palette.
600  * @regno: The palette index to change.
601  * @red: The red field for the palette data.
602  * @green: The green field for the palette data.
603  * @blue: The blue field for the palette data.
604  * @trans: The transparency (alpha) field for the palette data.
605  * @info: The framebuffer being changed.
606  */
607 static int s3c_fb_setcolreg(unsigned regno,
608                             unsigned red, unsigned green, unsigned blue,
609                             unsigned transp, struct fb_info *info)
610 {
611         struct s3c_fb_win *win = info->par;
612         struct s3c_fb *sfb = win->parent;
613         unsigned int val;
614
615         dev_dbg(sfb->dev, "%s: win %d: %d => rgb=%d/%d/%d\n",
616                 __func__, win->index, regno, red, green, blue);
617
618         switch (info->fix.visual) {
619         case FB_VISUAL_TRUECOLOR:
620                 /* true-colour, use pseudo-palette */
621
622                 if (regno < 16) {
623                         u32 *pal = info->pseudo_palette;
624
625                         val  = chan_to_field(red,   &info->var.red);
626                         val |= chan_to_field(green, &info->var.green);
627                         val |= chan_to_field(blue,  &info->var.blue);
628
629                         pal[regno] = val;
630                 }
631                 break;
632
633         case FB_VISUAL_PSEUDOCOLOR:
634                 if (regno < win->variant.palette_sz) {
635                         val  = chan_to_field(red, &win->palette.r);
636                         val |= chan_to_field(green, &win->palette.g);
637                         val |= chan_to_field(blue, &win->palette.b);
638
639                         s3c_fb_update_palette(sfb, win, regno, val);
640                 }
641
642                 break;
643
644         default:
645                 return 1;       /* unknown type */
646         }
647
648         return 0;
649 }
650
651 /**
652  * s3c_fb_enable() - Set the state of the main LCD output
653  * @sfb: The main framebuffer state.
654  * @enable: The state to set.
655  */
656 static void s3c_fb_enable(struct s3c_fb *sfb, int enable)
657 {
658         u32 vidcon0 = readl(sfb->regs + VIDCON0);
659
660         if (enable)
661                 vidcon0 |= VIDCON0_ENVID | VIDCON0_ENVID_F;
662         else {
663                 /* see the note in the framebuffer datasheet about
664                  * why you cannot take both of these bits down at the
665                  * same time. */
666
667                 if (!(vidcon0 & VIDCON0_ENVID))
668                         return;
669
670                 vidcon0 |= VIDCON0_ENVID;
671                 vidcon0 &= ~VIDCON0_ENVID_F;
672         }
673
674         writel(vidcon0, sfb->regs + VIDCON0);
675 }
676
677 /**
678  * s3c_fb_blank() - blank or unblank the given window
679  * @blank_mode: The blank state from FB_BLANK_*
680  * @info: The framebuffer to blank.
681  *
682  * Framebuffer layer request to change the power state.
683  */
684 static int s3c_fb_blank(int blank_mode, struct fb_info *info)
685 {
686         struct s3c_fb_win *win = info->par;
687         struct s3c_fb *sfb = win->parent;
688         unsigned int index = win->index;
689         u32 wincon;
690
691         dev_dbg(sfb->dev, "blank mode %d\n", blank_mode);
692
693         wincon = readl(sfb->regs + sfb->variant.wincon + (index * 4));
694
695         switch (blank_mode) {
696         case FB_BLANK_POWERDOWN:
697                 wincon &= ~WINCONx_ENWIN;
698                 sfb->enabled &= ~(1 << index);
699                 /* fall through to FB_BLANK_NORMAL */
700
701         case FB_BLANK_NORMAL:
702                 /* disable the DMA and display 0x0 (black) */
703                 writel(WINxMAP_MAP | WINxMAP_MAP_COLOUR(0x0),
704                        sfb->regs + sfb->variant.winmap + (index * 4));
705                 break;
706
707         case FB_BLANK_UNBLANK:
708                 writel(0x0, sfb->regs + sfb->variant.winmap + (index * 4));
709                 wincon |= WINCONx_ENWIN;
710                 sfb->enabled |= (1 << index);
711                 break;
712
713         case FB_BLANK_VSYNC_SUSPEND:
714         case FB_BLANK_HSYNC_SUSPEND:
715         default:
716                 return 1;
717         }
718
719         writel(wincon, sfb->regs + sfb->variant.wincon + (index * 4));
720
721         /* Check the enabled state to see if we need to be running the
722          * main LCD interface, as if there are no active windows then
723          * it is highly likely that we also do not need to output
724          * anything.
725          */
726
727         /* We could do something like the following code, but the current
728          * system of using framebuffer events means that we cannot make
729          * the distinction between just window 0 being inactive and all
730          * the windows being down.
731          *
732          * s3c_fb_enable(sfb, sfb->enabled ? 1 : 0);
733         */
734
735         /* we're stuck with this until we can do something about overriding
736          * the power control using the blanking event for a single fb.
737          */
738         if (index == sfb->pdata->default_win)
739                 s3c_fb_enable(sfb, blank_mode != FB_BLANK_POWERDOWN ? 1 : 0);
740
741         return 0;
742 }
743
744 /**
745  * s3c_fb_pan_display() - Pan the display.
746  *
747  * Note that the offsets can be written to the device at any time, as their
748  * values are latched at each vsync automatically. This also means that only
749  * the last call to this function will have any effect on next vsync, but
750  * there is no need to sleep waiting for it to prevent tearing.
751  *
752  * @var: The screen information to verify.
753  * @info: The framebuffer device.
754  */
755 static int s3c_fb_pan_display(struct fb_var_screeninfo *var,
756                               struct fb_info *info)
757 {
758         struct s3c_fb_win *win  = info->par;
759         struct s3c_fb *sfb      = win->parent;
760         void __iomem *buf       = sfb->regs + win->index * 8;
761         unsigned int start_boff, end_boff;
762
763         /* Offset in bytes to the start of the displayed area */
764         start_boff = var->yoffset * info->fix.line_length;
765         /* X offset depends on the current bpp */
766         if (info->var.bits_per_pixel >= 8) {
767                 start_boff += var->xoffset * (info->var.bits_per_pixel >> 3);
768         } else {
769                 switch (info->var.bits_per_pixel) {
770                 case 4:
771                         start_boff += var->xoffset >> 1;
772                         break;
773                 case 2:
774                         start_boff += var->xoffset >> 2;
775                         break;
776                 case 1:
777                         start_boff += var->xoffset >> 3;
778                         break;
779                 default:
780                         dev_err(sfb->dev, "invalid bpp\n");
781                         return -EINVAL;
782                 }
783         }
784         /* Offset in bytes to the end of the displayed area */
785         end_boff = start_boff + var->yres * info->fix.line_length;
786
787         /* Temporarily turn off per-vsync update from shadow registers until
788          * both start and end addresses are updated to prevent corruption */
789         if (sfb->variant.has_prtcon)
790                 writel(PRTCON_PROTECT, sfb->regs + PRTCON);
791
792         writel(info->fix.smem_start + start_boff, buf + sfb->variant.buf_start);
793         writel(info->fix.smem_start + end_boff, buf + sfb->variant.buf_end);
794
795         if (sfb->variant.has_prtcon)
796                 writel(0, sfb->regs + PRTCON);
797
798         return 0;
799 }
800
801 static struct fb_ops s3c_fb_ops = {
802         .owner          = THIS_MODULE,
803         .fb_check_var   = s3c_fb_check_var,
804         .fb_set_par     = s3c_fb_set_par,
805         .fb_blank       = s3c_fb_blank,
806         .fb_setcolreg   = s3c_fb_setcolreg,
807         .fb_fillrect    = cfb_fillrect,
808         .fb_copyarea    = cfb_copyarea,
809         .fb_imageblit   = cfb_imageblit,
810         .fb_pan_display = s3c_fb_pan_display,
811 };
812
813 /**
814  * s3c_fb_alloc_memory() - allocate display memory for framebuffer window
815  * @sfb: The base resources for the hardware.
816  * @win: The window to initialise memory for.
817  *
818  * Allocate memory for the given framebuffer.
819  */
820 static int __devinit s3c_fb_alloc_memory(struct s3c_fb *sfb,
821                                          struct s3c_fb_win *win)
822 {
823         struct s3c_fb_pd_win *windata = win->windata;
824         unsigned int real_size, virt_size, size;
825         struct fb_info *fbi = win->fbinfo;
826         dma_addr_t map_dma;
827
828         dev_dbg(sfb->dev, "allocating memory for display\n");
829
830         real_size = windata->win_mode.xres * windata->win_mode.yres;
831         virt_size = windata->virtual_x * windata->virtual_y;
832
833         dev_dbg(sfb->dev, "real_size=%u (%u.%u), virt_size=%u (%u.%u)\n",
834                 real_size, windata->win_mode.xres, windata->win_mode.yres,
835                 virt_size, windata->virtual_x, windata->virtual_y);
836
837         size = (real_size > virt_size) ? real_size : virt_size;
838         size *= (windata->max_bpp > 16) ? 32 : windata->max_bpp;
839         size /= 8;
840
841         fbi->fix.smem_len = size;
842         size = PAGE_ALIGN(size);
843
844         dev_dbg(sfb->dev, "want %u bytes for window\n", size);
845
846         fbi->screen_base = dma_alloc_writecombine(sfb->dev, size,
847                                                   &map_dma, GFP_KERNEL);
848         if (!fbi->screen_base)
849                 return -ENOMEM;
850
851         dev_dbg(sfb->dev, "mapped %x to %p\n",
852                 (unsigned int)map_dma, fbi->screen_base);
853
854         memset(fbi->screen_base, 0x0, size);
855         fbi->fix.smem_start = map_dma;
856
857         return 0;
858 }
859
860 /**
861  * s3c_fb_free_memory() - free the display memory for the given window
862  * @sfb: The base resources for the hardware.
863  * @win: The window to free the display memory for.
864  *
865  * Free the display memory allocated by s3c_fb_alloc_memory().
866  */
867 static void s3c_fb_free_memory(struct s3c_fb *sfb, struct s3c_fb_win *win)
868 {
869         struct fb_info *fbi = win->fbinfo;
870
871         if (fbi->screen_base)
872                 dma_free_writecombine(sfb->dev, PAGE_ALIGN(fbi->fix.smem_len),
873                               fbi->screen_base, fbi->fix.smem_start);
874 }
875
876 /**
877  * s3c_fb_release_win() - release resources for a framebuffer window.
878  * @win: The window to cleanup the resources for.
879  *
880  * Release the resources that where claimed for the hardware window,
881  * such as the framebuffer instance and any memory claimed for it.
882  */
883 static void s3c_fb_release_win(struct s3c_fb *sfb, struct s3c_fb_win *win)
884 {
885         if (win->fbinfo) {
886                 unregister_framebuffer(win->fbinfo);
887                 if (win->fbinfo->cmap.len)
888                         fb_dealloc_cmap(&win->fbinfo->cmap);
889                 s3c_fb_free_memory(sfb, win);
890                 framebuffer_release(win->fbinfo);
891         }
892 }
893
894 /**
895  * s3c_fb_probe_win() - register an hardware window
896  * @sfb: The base resources for the hardware
897  * @variant: The variant information for this window.
898  * @res: Pointer to where to place the resultant window.
899  *
900  * Allocate and do the basic initialisation for one of the hardware's graphics
901  * windows.
902  */
903 static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no,
904                                       struct s3c_fb_win_variant *variant,
905                                       struct s3c_fb_win **res)
906 {
907         struct fb_var_screeninfo *var;
908         struct fb_videomode *initmode;
909         struct s3c_fb_pd_win *windata;
910         struct s3c_fb_win *win;
911         struct fb_info *fbinfo;
912         int palette_size;
913         int ret;
914
915         dev_dbg(sfb->dev, "probing window %d, variant %p\n", win_no, variant);
916
917         palette_size = variant->palette_sz * 4;
918
919         fbinfo = framebuffer_alloc(sizeof(struct s3c_fb_win) +
920                                    palette_size * sizeof(u32), sfb->dev);
921         if (!fbinfo) {
922                 dev_err(sfb->dev, "failed to allocate framebuffer\n");
923                 return -ENOENT;
924         }
925
926         windata = sfb->pdata->win[win_no];
927         initmode = &windata->win_mode;
928
929         WARN_ON(windata->max_bpp == 0);
930         WARN_ON(windata->win_mode.xres == 0);
931         WARN_ON(windata->win_mode.yres == 0);
932
933         win = fbinfo->par;
934         *res = win;
935         var = &fbinfo->var;
936         win->variant = *variant;
937         win->fbinfo = fbinfo;
938         win->parent = sfb;
939         win->windata = windata;
940         win->index = win_no;
941         win->palette_buffer = (u32 *)(win + 1);
942
943         ret = s3c_fb_alloc_memory(sfb, win);
944         if (ret) {
945                 dev_err(sfb->dev, "failed to allocate display memory\n");
946                 return ret;
947         }
948
949         /* setup the r/b/g positions for the window's palette */
950         if (win->variant.palette_16bpp) {
951                 /* Set RGB 5:6:5 as default */
952                 win->palette.r.offset = 11;
953                 win->palette.r.length = 5;
954                 win->palette.g.offset = 5;
955                 win->palette.g.length = 6;
956                 win->palette.b.offset = 0;
957                 win->palette.b.length = 5;
958
959         } else {
960                 /* Set 8bpp or 8bpp and 1bit alpha */
961                 win->palette.r.offset = 16;
962                 win->palette.r.length = 8;
963                 win->palette.g.offset = 8;
964                 win->palette.g.length = 8;
965                 win->palette.b.offset = 0;
966                 win->palette.b.length = 8;
967         }
968
969         /* setup the initial video mode from the window */
970         fb_videomode_to_var(&fbinfo->var, initmode);
971
972         fbinfo->fix.type        = FB_TYPE_PACKED_PIXELS;
973         fbinfo->fix.accel       = FB_ACCEL_NONE;
974         fbinfo->var.activate    = FB_ACTIVATE_NOW;
975         fbinfo->var.vmode       = FB_VMODE_NONINTERLACED;
976         fbinfo->var.bits_per_pixel = windata->default_bpp;
977         fbinfo->fbops           = &s3c_fb_ops;
978         fbinfo->flags           = FBINFO_FLAG_DEFAULT;
979         fbinfo->pseudo_palette  = &win->pseudo_palette;
980
981         /* prepare to actually start the framebuffer */
982
983         ret = s3c_fb_check_var(&fbinfo->var, fbinfo);
984         if (ret < 0) {
985                 dev_err(sfb->dev, "check_var failed on initial video params\n");
986                 return ret;
987         }
988
989         /* create initial colour map */
990
991         ret = fb_alloc_cmap(&fbinfo->cmap, win->variant.palette_sz, 1);
992         if (ret == 0)
993                 fb_set_cmap(&fbinfo->cmap, fbinfo);
994         else
995                 dev_err(sfb->dev, "failed to allocate fb cmap\n");
996
997         s3c_fb_set_par(fbinfo);
998
999         dev_dbg(sfb->dev, "about to register framebuffer\n");
1000
1001         /* run the check_var and set_par on our configuration. */
1002
1003         ret = register_framebuffer(fbinfo);
1004         if (ret < 0) {
1005                 dev_err(sfb->dev, "failed to register framebuffer\n");
1006                 return ret;
1007         }
1008
1009         dev_info(sfb->dev, "window %d: fb %s\n", win_no, fbinfo->fix.id);
1010
1011         return 0;
1012 }
1013
1014 /**
1015  * s3c_fb_clear_win() - clear hardware window registers.
1016  * @sfb: The base resources for the hardware.
1017  * @win: The window to process.
1018  *
1019  * Reset the specific window registers to a known state.
1020  */
1021 static void s3c_fb_clear_win(struct s3c_fb *sfb, int win)
1022 {
1023         void __iomem *regs = sfb->regs;
1024
1025         writel(0, regs + sfb->variant.wincon + (win * 4));
1026         writel(0, regs + VIDOSD_A(win, sfb->variant));
1027         writel(0, regs + VIDOSD_B(win, sfb->variant));
1028         writel(0, regs + VIDOSD_C(win, sfb->variant));
1029 }
1030
1031 static int __devinit s3c_fb_probe(struct platform_device *pdev)
1032 {
1033         struct s3c_fb_driverdata *fbdrv;
1034         struct device *dev = &pdev->dev;
1035         struct s3c_fb_platdata *pd;
1036         struct s3c_fb *sfb;
1037         struct resource *res;
1038         int win;
1039         int ret = 0;
1040
1041         fbdrv = (struct s3c_fb_driverdata *)platform_get_device_id(pdev)->driver_data;
1042
1043         if (fbdrv->variant.nr_windows > S3C_FB_MAX_WIN) {
1044                 dev_err(dev, "too many windows, cannot attach\n");
1045                 return -EINVAL;
1046         }
1047
1048         pd = pdev->dev.platform_data;
1049         if (!pd) {
1050                 dev_err(dev, "no platform data specified\n");
1051                 return -EINVAL;
1052         }
1053
1054         sfb = kzalloc(sizeof(struct s3c_fb), GFP_KERNEL);
1055         if (!sfb) {
1056                 dev_err(dev, "no memory for framebuffers\n");
1057                 return -ENOMEM;
1058         }
1059
1060         dev_dbg(dev, "allocate new framebuffer %p\n", sfb);
1061
1062         sfb->dev = dev;
1063         sfb->pdata = pd;
1064         sfb->variant = fbdrv->variant;
1065
1066         sfb->bus_clk = clk_get(dev, "lcd");
1067         if (IS_ERR(sfb->bus_clk)) {
1068                 dev_err(dev, "failed to get bus clock\n");
1069                 goto err_sfb;
1070         }
1071
1072         clk_enable(sfb->bus_clk);
1073
1074         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1075         if (!res) {
1076                 dev_err(dev, "failed to find registers\n");
1077                 ret = -ENOENT;
1078                 goto err_clk;
1079         }
1080
1081         sfb->regs_res = request_mem_region(res->start, resource_size(res),
1082                                            dev_name(dev));
1083         if (!sfb->regs_res) {
1084                 dev_err(dev, "failed to claim register region\n");
1085                 ret = -ENOENT;
1086                 goto err_clk;
1087         }
1088
1089         sfb->regs = ioremap(res->start, resource_size(res));
1090         if (!sfb->regs) {
1091                 dev_err(dev, "failed to map registers\n");
1092                 ret = -ENXIO;
1093                 goto err_req_region;
1094         }
1095
1096         dev_dbg(dev, "got resources (regs %p), probing windows\n", sfb->regs);
1097
1098         /* setup gpio and output polarity controls */
1099
1100         pd->setup_gpio();
1101
1102         writel(pd->vidcon1, sfb->regs + VIDCON1);
1103
1104         /* zero all windows before we do anything */
1105
1106         for (win = 0; win < fbdrv->variant.nr_windows; win++)
1107                 s3c_fb_clear_win(sfb, win);
1108
1109         /* initialise colour key controls */
1110         for (win = 0; win < (fbdrv->variant.nr_windows - 1); win++) {
1111                 void __iomem *regs = sfb->regs + sfb->variant.keycon;
1112
1113                 regs += (win * 8);
1114                 writel(0xffffff, regs + WKEYCON0);
1115                 writel(0xffffff, regs + WKEYCON1);
1116         }
1117
1118         /* we have the register setup, start allocating framebuffers */
1119
1120         for (win = 0; win < fbdrv->variant.nr_windows; win++) {
1121                 if (!pd->win[win])
1122                         continue;
1123
1124                 ret = s3c_fb_probe_win(sfb, win, fbdrv->win[win],
1125                                        &sfb->windows[win]);
1126                 if (ret < 0) {
1127                         dev_err(dev, "failed to create window %d\n", win);
1128                         for (; win >= 0; win--)
1129                                 s3c_fb_release_win(sfb, sfb->windows[win]);
1130                         goto err_ioremap;
1131                 }
1132         }
1133
1134         platform_set_drvdata(pdev, sfb);
1135
1136         return 0;
1137
1138 err_ioremap:
1139         iounmap(sfb->regs);
1140
1141 err_req_region:
1142         release_resource(sfb->regs_res);
1143         kfree(sfb->regs_res);
1144
1145 err_clk:
1146         clk_disable(sfb->bus_clk);
1147         clk_put(sfb->bus_clk);
1148
1149 err_sfb:
1150         kfree(sfb);
1151         return ret;
1152 }
1153
1154 /**
1155  * s3c_fb_remove() - Cleanup on module finalisation
1156  * @pdev: The platform device we are bound to.
1157  *
1158  * Shutdown and then release all the resources that the driver allocated
1159  * on initialisation.
1160  */
1161 static int __devexit s3c_fb_remove(struct platform_device *pdev)
1162 {
1163         struct s3c_fb *sfb = platform_get_drvdata(pdev);
1164         int win;
1165
1166         for (win = 0; win < S3C_FB_MAX_WIN; win++)
1167                 if (sfb->windows[win])
1168                         s3c_fb_release_win(sfb, sfb->windows[win]);
1169
1170         iounmap(sfb->regs);
1171
1172         clk_disable(sfb->bus_clk);
1173         clk_put(sfb->bus_clk);
1174
1175         release_resource(sfb->regs_res);
1176         kfree(sfb->regs_res);
1177
1178         kfree(sfb);
1179
1180         return 0;
1181 }
1182
1183 #ifdef CONFIG_PM
1184 static int s3c_fb_suspend(struct platform_device *pdev, pm_message_t state)
1185 {
1186         struct s3c_fb *sfb = platform_get_drvdata(pdev);
1187         struct s3c_fb_win *win;
1188         int win_no;
1189
1190         for (win_no = S3C_FB_MAX_WIN - 1; win_no >= 0; win_no--) {
1191                 win = sfb->windows[win_no];
1192                 if (!win)
1193                         continue;
1194
1195                 /* use the blank function to push into power-down */
1196                 s3c_fb_blank(FB_BLANK_POWERDOWN, win->fbinfo);
1197         }
1198
1199         clk_disable(sfb->bus_clk);
1200         return 0;
1201 }
1202
1203 static int s3c_fb_resume(struct platform_device *pdev)
1204 {
1205         struct s3c_fb *sfb = platform_get_drvdata(pdev);
1206         struct s3c_fb_platdata *pd = sfb->pdata;
1207         struct s3c_fb_win *win;
1208         int win_no;
1209
1210         clk_enable(sfb->bus_clk);
1211
1212         /* setup registers */
1213         writel(pd->vidcon1, sfb->regs + VIDCON1);
1214
1215         /* zero all windows before we do anything */
1216         for (win_no = 0; win_no < sfb->variant.nr_windows; win_no++)
1217                 s3c_fb_clear_win(sfb, win_no);
1218
1219         for (win_no = 0; win_no < sfb->variant.nr_windows - 1; win_no++) {
1220                 void __iomem *regs = sfb->regs + sfb->variant.keycon;
1221
1222                 regs += (win_no * 8);
1223                 writel(0xffffff, regs + WKEYCON0);
1224                 writel(0xffffff, regs + WKEYCON1);
1225         }
1226
1227         /* restore framebuffers */
1228         for (win_no = 0; win_no < S3C_FB_MAX_WIN; win_no++) {
1229                 win = sfb->windows[win_no];
1230                 if (!win)
1231                         continue;
1232
1233                 dev_dbg(&pdev->dev, "resuming window %d\n", win_no);
1234                 s3c_fb_set_par(win->fbinfo);
1235         }
1236
1237         return 0;
1238 }
1239 #else
1240 #define s3c_fb_suspend NULL
1241 #define s3c_fb_resume  NULL
1242 #endif
1243
1244
1245 #define VALID_BPP124 (VALID_BPP(1) | VALID_BPP(2) | VALID_BPP(4))
1246 #define VALID_BPP1248 (VALID_BPP124 | VALID_BPP(8))
1247
1248 static struct s3c_fb_win_variant s3c_fb_data_64xx_wins[] __devinitdata = {
1249         [0] = {
1250                 .has_osd_c      = 1,
1251                 .palette_sz     = 256,
1252                 .valid_bpp      = VALID_BPP1248 | VALID_BPP(16) | VALID_BPP(24),
1253         },
1254         [1] = {
1255                 .has_osd_c      = 1,
1256                 .has_osd_d      = 1,
1257                 .palette_sz     = 256,
1258                 .valid_bpp      = (VALID_BPP1248 | VALID_BPP(16) |
1259                                    VALID_BPP(18) | VALID_BPP(19) |
1260                                    VALID_BPP(24) | VALID_BPP(25)),
1261         },
1262         [2] = {
1263                 .has_osd_c      = 1,
1264                 .has_osd_d      = 1,
1265                 .palette_sz     = 16,
1266                 .palette_16bpp  = 1,
1267                 .valid_bpp      = (VALID_BPP1248 | VALID_BPP(16) |
1268                                    VALID_BPP(18) | VALID_BPP(19) |
1269                                    VALID_BPP(24) | VALID_BPP(25)),
1270         },
1271         [3] = {
1272                 .has_osd_c      = 1,
1273                 .has_osd_d      = 1,
1274                 .palette_sz     = 16,
1275                 .palette_16bpp  = 1,
1276                 .valid_bpp      = (VALID_BPP124  | VALID_BPP(16) |
1277                                    VALID_BPP(18) | VALID_BPP(19) |
1278                                    VALID_BPP(24) | VALID_BPP(25)),
1279         },
1280         [4] = {
1281                 .has_osd_c      = 1,
1282                 .palette_sz     = 4,
1283                 .palette_16bpp  = 1,
1284                 .valid_bpp      = (VALID_BPP(1) | VALID_BPP(2) |
1285                                    VALID_BPP(16) | VALID_BPP(18) |
1286                                    VALID_BPP(24) | VALID_BPP(25)),
1287         },
1288 };
1289
1290 static struct s3c_fb_driverdata s3c_fb_data_64xx __devinitdata = {
1291         .variant = {
1292                 .nr_windows     = 5,
1293                 .vidtcon        = VIDTCON0,
1294                 .wincon         = WINCON(0),
1295                 .winmap         = WINxMAP(0),
1296                 .keycon         = WKEYCON,
1297                 .osd            = VIDOSD_BASE,
1298                 .osd_stride     = 16,
1299                 .buf_start      = VIDW_BUF_START(0),
1300                 .buf_size       = VIDW_BUF_SIZE(0),
1301                 .buf_end        = VIDW_BUF_END(0),
1302
1303                 .palette = {
1304                         [0] = 0x400,
1305                         [1] = 0x800,
1306                         [2] = 0x300,
1307                         [3] = 0x320,
1308                         [4] = 0x340,
1309                 },
1310
1311                 .has_prtcon     = 1,
1312         },
1313         .win[0] = &s3c_fb_data_64xx_wins[0],
1314         .win[1] = &s3c_fb_data_64xx_wins[1],
1315         .win[2] = &s3c_fb_data_64xx_wins[2],
1316         .win[3] = &s3c_fb_data_64xx_wins[3],
1317         .win[4] = &s3c_fb_data_64xx_wins[4],
1318 };
1319
1320 static struct s3c_fb_driverdata s3c_fb_data_s5pc100 __devinitdata = {
1321         .variant = {
1322                 .nr_windows     = 5,
1323                 .vidtcon        = VIDTCON0,
1324                 .wincon         = WINCON(0),
1325                 .winmap         = WINxMAP(0),
1326                 .keycon         = WKEYCON,
1327                 .osd            = VIDOSD_BASE,
1328                 .osd_stride     = 16,
1329                 .buf_start      = VIDW_BUF_START(0),
1330                 .buf_size       = VIDW_BUF_SIZE(0),
1331                 .buf_end        = VIDW_BUF_END(0),
1332
1333                 .palette = {
1334                         [0] = 0x2400,
1335                         [1] = 0x2800,
1336                         [2] = 0x2c00,
1337                         [3] = 0x3000,
1338                         [4] = 0x3400,
1339                 },
1340
1341                 .has_prtcon     = 1,
1342         },
1343         .win[0] = &s3c_fb_data_64xx_wins[0],
1344         .win[1] = &s3c_fb_data_64xx_wins[1],
1345         .win[2] = &s3c_fb_data_64xx_wins[2],
1346         .win[3] = &s3c_fb_data_64xx_wins[3],
1347         .win[4] = &s3c_fb_data_64xx_wins[4],
1348 };
1349
1350 static struct s3c_fb_driverdata s3c_fb_data_s5pv210 __devinitdata = {
1351         .variant = {
1352                 .nr_windows     = 5,
1353                 .vidtcon        = VIDTCON0,
1354                 .wincon         = WINCON(0),
1355                 .winmap         = WINxMAP(0),
1356                 .keycon         = WKEYCON,
1357                 .osd            = VIDOSD_BASE,
1358                 .osd_stride     = 16,
1359                 .buf_start      = VIDW_BUF_START(0),
1360                 .buf_size       = VIDW_BUF_SIZE(0),
1361                 .buf_end        = VIDW_BUF_END(0),
1362
1363                 .palette = {
1364                         [0] = 0x2400,
1365                         [1] = 0x2800,
1366                         [2] = 0x2c00,
1367                         [3] = 0x3000,
1368                         [4] = 0x3400,
1369                 },
1370         },
1371         .win[0] = &s3c_fb_data_64xx_wins[0],
1372         .win[1] = &s3c_fb_data_64xx_wins[1],
1373         .win[2] = &s3c_fb_data_64xx_wins[2],
1374         .win[3] = &s3c_fb_data_64xx_wins[3],
1375         .win[4] = &s3c_fb_data_64xx_wins[4],
1376 };
1377
1378 /* S3C2443/S3C2416 style hardware */
1379 static struct s3c_fb_driverdata s3c_fb_data_s3c2443 __devinitdata = {
1380         .variant = {
1381                 .nr_windows     = 2,
1382                 .is_2443        = 1,
1383
1384                 .vidtcon        = 0x08,
1385                 .wincon         = 0x14,
1386                 .winmap         = 0xd0,
1387                 .keycon         = 0xb0,
1388                 .osd            = 0x28,
1389                 .osd_stride     = 12,
1390                 .buf_start      = 0x64,
1391                 .buf_size       = 0x94,
1392                 .buf_end        = 0x7c,
1393
1394                 .palette = {
1395                         [0] = 0x400,
1396                         [1] = 0x800,
1397                 },
1398         },
1399         .win[0] = &(struct s3c_fb_win_variant) {
1400                 .palette_sz     = 256,
1401                 .valid_bpp      = VALID_BPP1248 | VALID_BPP(16) | VALID_BPP(24),
1402         },
1403         .win[1] = &(struct s3c_fb_win_variant) {
1404                 .has_osd_c      = 1,
1405                 .palette_sz     = 256,
1406                 .valid_bpp      = (VALID_BPP1248 | VALID_BPP(16) |
1407                                    VALID_BPP(18) | VALID_BPP(19) |
1408                                    VALID_BPP(24) | VALID_BPP(25) |
1409                                    VALID_BPP(28)),
1410         },
1411 };
1412
1413 static struct platform_device_id s3c_fb_driver_ids[] = {
1414         {
1415                 .name           = "s3c-fb",
1416                 .driver_data    = (unsigned long)&s3c_fb_data_64xx,
1417         }, {
1418                 .name           = "s5pc100-fb",
1419                 .driver_data    = (unsigned long)&s3c_fb_data_s5pc100,
1420         }, {
1421                 .name           = "s5pv210-fb",
1422                 .driver_data    = (unsigned long)&s3c_fb_data_s5pv210,
1423         }, {
1424                 .name           = "s3c2443-fb",
1425                 .driver_data    = (unsigned long)&s3c_fb_data_s3c2443,
1426         },
1427         {},
1428 };
1429 MODULE_DEVICE_TABLE(platform, s3c_fb_driver_ids);
1430
1431 static struct platform_driver s3c_fb_driver = {
1432         .probe          = s3c_fb_probe,
1433         .remove         = __devexit_p(s3c_fb_remove),
1434         .suspend        = s3c_fb_suspend,
1435         .resume         = s3c_fb_resume,
1436         .id_table       = s3c_fb_driver_ids,
1437         .driver         = {
1438                 .name   = "s3c-fb",
1439                 .owner  = THIS_MODULE,
1440         },
1441 };
1442
1443 static int __init s3c_fb_init(void)
1444 {
1445         return platform_driver_register(&s3c_fb_driver);
1446 }
1447
1448 static void __exit s3c_fb_cleanup(void)
1449 {
1450         platform_driver_unregister(&s3c_fb_driver);
1451 }
1452
1453 module_init(s3c_fb_init);
1454 module_exit(s3c_fb_cleanup);
1455
1456 MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
1457 MODULE_DESCRIPTION("Samsung S3C SoC Framebuffer driver");
1458 MODULE_LICENSE("GPL");
1459 MODULE_ALIAS("platform:s3c-fb");