Merge branch 'driver-core-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[pandora-kernel.git] / drivers / video / pxa168fb.c
1 /*
2  * linux/drivers/video/pxa168fb.c -- Marvell PXA168 LCD Controller
3  *
4  *  Copyright (C) 2008 Marvell International Ltd.
5  *  All rights reserved.
6  *
7  *  2009-02-16  adapted from original version for PXA168/910
8  *              Jun Nie <njun@marvell.com>
9  *
10  * This file is subject to the terms and conditions of the GNU General Public
11  * License. See the file COPYING in the main directory of this archive for
12  * more details.
13  */
14
15 #include <linux/module.h>
16 #include <linux/kernel.h>
17 #include <linux/sched.h>
18 #include <linux/string.h>
19 #include <linux/interrupt.h>
20 #include <linux/slab.h>
21 #include <linux/fb.h>
22 #include <linux/delay.h>
23 #include <linux/init.h>
24 #include <linux/ioport.h>
25 #include <linux/platform_device.h>
26 #include <linux/dma-mapping.h>
27 #include <linux/clk.h>
28 #include <linux/err.h>
29 #include <linux/uaccess.h>
30 #include <video/pxa168fb.h>
31
32 #include "pxa168fb.h"
33
34 #define DEFAULT_REFRESH         60      /* Hz */
35
36 static int determine_best_pix_fmt(struct fb_var_screeninfo *var)
37 {
38         /*
39          * Pseudocolor mode?
40          */
41         if (var->bits_per_pixel == 8)
42                 return PIX_FMT_PSEUDOCOLOR;
43
44         /*
45          * Check for 565/1555.
46          */
47         if (var->bits_per_pixel == 16 && var->red.length <= 5 &&
48             var->green.length <= 6 && var->blue.length <= 5) {
49                 if (var->transp.length == 0) {
50                         if (var->red.offset >= var->blue.offset)
51                                 return PIX_FMT_RGB565;
52                         else
53                                 return PIX_FMT_BGR565;
54                 }
55
56                 if (var->transp.length == 1 && var->green.length <= 5) {
57                         if (var->red.offset >= var->blue.offset)
58                                 return PIX_FMT_RGB1555;
59                         else
60                                 return PIX_FMT_BGR1555;
61                 }
62
63                 /* fall through */
64         }
65
66         /*
67          * Check for 888/A888.
68          */
69         if (var->bits_per_pixel <= 32 && var->red.length <= 8 &&
70             var->green.length <= 8 && var->blue.length <= 8) {
71                 if (var->bits_per_pixel == 24 && var->transp.length == 0) {
72                         if (var->red.offset >= var->blue.offset)
73                                 return PIX_FMT_RGB888PACK;
74                         else
75                                 return PIX_FMT_BGR888PACK;
76                 }
77
78                 if (var->bits_per_pixel == 32 && var->transp.length == 8) {
79                         if (var->red.offset >= var->blue.offset)
80                                 return PIX_FMT_RGBA888;
81                         else
82                                 return PIX_FMT_BGRA888;
83                 } else {
84                         if (var->red.offset >= var->blue.offset)
85                                 return PIX_FMT_RGB888UNPACK;
86                         else
87                                 return PIX_FMT_BGR888UNPACK;
88                 }
89
90                 /* fall through */
91         }
92
93         return -EINVAL;
94 }
95
96 static void set_pix_fmt(struct fb_var_screeninfo *var, int pix_fmt)
97 {
98         switch (pix_fmt) {
99         case PIX_FMT_RGB565:
100                 var->bits_per_pixel = 16;
101                 var->red.offset = 11;    var->red.length = 5;
102                 var->green.offset = 5;   var->green.length = 6;
103                 var->blue.offset = 0;    var->blue.length = 5;
104                 var->transp.offset = 0;  var->transp.length = 0;
105                 break;
106         case PIX_FMT_BGR565:
107                 var->bits_per_pixel = 16;
108                 var->red.offset = 0;     var->red.length = 5;
109                 var->green.offset = 5;   var->green.length = 6;
110                 var->blue.offset = 11;   var->blue.length = 5;
111                 var->transp.offset = 0;  var->transp.length = 0;
112                 break;
113         case PIX_FMT_RGB1555:
114                 var->bits_per_pixel = 16;
115                 var->red.offset = 10;    var->red.length = 5;
116                 var->green.offset = 5;   var->green.length = 5;
117                 var->blue.offset = 0;    var->blue.length = 5;
118                 var->transp.offset = 15; var->transp.length = 1;
119                 break;
120         case PIX_FMT_BGR1555:
121                 var->bits_per_pixel = 16;
122                 var->red.offset = 0;     var->red.length = 5;
123                 var->green.offset = 5;   var->green.length = 5;
124                 var->blue.offset = 10;   var->blue.length = 5;
125                 var->transp.offset = 15; var->transp.length = 1;
126                 break;
127         case PIX_FMT_RGB888PACK:
128                 var->bits_per_pixel = 24;
129                 var->red.offset = 16;    var->red.length = 8;
130                 var->green.offset = 8;   var->green.length = 8;
131                 var->blue.offset = 0;    var->blue.length = 8;
132                 var->transp.offset = 0;  var->transp.length = 0;
133                 break;
134         case PIX_FMT_BGR888PACK:
135                 var->bits_per_pixel = 24;
136                 var->red.offset = 0;     var->red.length = 8;
137                 var->green.offset = 8;   var->green.length = 8;
138                 var->blue.offset = 16;   var->blue.length = 8;
139                 var->transp.offset = 0;  var->transp.length = 0;
140                 break;
141         case PIX_FMT_RGBA888:
142                 var->bits_per_pixel = 32;
143                 var->red.offset = 16;    var->red.length = 8;
144                 var->green.offset = 8;   var->green.length = 8;
145                 var->blue.offset = 0;    var->blue.length = 8;
146                 var->transp.offset = 24; var->transp.length = 8;
147                 break;
148         case PIX_FMT_BGRA888:
149                 var->bits_per_pixel = 32;
150                 var->red.offset = 0;     var->red.length = 8;
151                 var->green.offset = 8;   var->green.length = 8;
152                 var->blue.offset = 16;   var->blue.length = 8;
153                 var->transp.offset = 24; var->transp.length = 8;
154                 break;
155         case PIX_FMT_PSEUDOCOLOR:
156                 var->bits_per_pixel = 8;
157                 var->red.offset = 0;     var->red.length = 8;
158                 var->green.offset = 0;   var->green.length = 8;
159                 var->blue.offset = 0;    var->blue.length = 8;
160                 var->transp.offset = 0;  var->transp.length = 0;
161                 break;
162         }
163 }
164
165 static void set_mode(struct pxa168fb_info *fbi, struct fb_var_screeninfo *var,
166                      struct fb_videomode *mode, int pix_fmt, int ystretch)
167 {
168         struct fb_info *info = fbi->info;
169
170         set_pix_fmt(var, pix_fmt);
171
172         var->xres = mode->xres;
173         var->yres = mode->yres;
174         var->xres_virtual = max(var->xres, var->xres_virtual);
175         if (ystretch)
176                 var->yres_virtual = info->fix.smem_len /
177                         (var->xres_virtual * (var->bits_per_pixel >> 3));
178         else
179                 var->yres_virtual = max(var->yres, var->yres_virtual);
180         var->grayscale = 0;
181         var->accel_flags = FB_ACCEL_NONE;
182         var->pixclock = mode->pixclock;
183         var->left_margin = mode->left_margin;
184         var->right_margin = mode->right_margin;
185         var->upper_margin = mode->upper_margin;
186         var->lower_margin = mode->lower_margin;
187         var->hsync_len = mode->hsync_len;
188         var->vsync_len = mode->vsync_len;
189         var->sync = mode->sync;
190         var->vmode = FB_VMODE_NONINTERLACED;
191         var->rotate = FB_ROTATE_UR;
192 }
193
194 static int pxa168fb_check_var(struct fb_var_screeninfo *var,
195                               struct fb_info *info)
196 {
197         struct pxa168fb_info *fbi = info->par;
198         int pix_fmt;
199
200         /*
201          * Determine which pixel format we're going to use.
202          */
203         pix_fmt = determine_best_pix_fmt(var);
204         if (pix_fmt < 0)
205                 return pix_fmt;
206         set_pix_fmt(var, pix_fmt);
207         fbi->pix_fmt = pix_fmt;
208
209         /*
210          * Basic geometry sanity checks.
211          */
212         if (var->xoffset + var->xres > var->xres_virtual)
213                 return -EINVAL;
214         if (var->yoffset + var->yres > var->yres_virtual)
215                 return -EINVAL;
216         if (var->xres + var->right_margin +
217             var->hsync_len + var->left_margin > 2048)
218                 return -EINVAL;
219         if (var->yres + var->lower_margin +
220             var->vsync_len + var->upper_margin > 2048)
221                 return -EINVAL;
222
223         /*
224          * Check size of framebuffer.
225          */
226         if (var->xres_virtual * var->yres_virtual *
227             (var->bits_per_pixel >> 3) > info->fix.smem_len)
228                 return -EINVAL;
229
230         return 0;
231 }
232
233 /*
234  * The hardware clock divider has an integer and a fractional
235  * stage:
236  *
237  *      clk2 = clk_in / integer_divider
238  *      clk_out = clk2 * (1 - (fractional_divider >> 12))
239  *
240  * Calculate integer and fractional divider for given clk_in
241  * and clk_out.
242  */
243 static void set_clock_divider(struct pxa168fb_info *fbi,
244                               const struct fb_videomode *m)
245 {
246         int divider_int;
247         int needed_pixclk;
248         u64 div_result;
249         u32 x = 0;
250
251         /*
252          * Notice: The field pixclock is used by linux fb
253          * is in pixel second. E.g. struct fb_videomode &
254          * struct fb_var_screeninfo
255          */
256
257         /*
258          * Check input values.
259          */
260         if (!m || !m->pixclock || !m->refresh) {
261                 dev_err(fbi->dev, "Input refresh or pixclock is wrong.\n");
262                 return;
263         }
264
265         /*
266          * Using PLL/AXI clock.
267          */
268         x = 0x80000000;
269
270         /*
271          * Calc divider according to refresh rate.
272          */
273         div_result = 1000000000000ll;
274         do_div(div_result, m->pixclock);
275         needed_pixclk = (u32)div_result;
276
277         divider_int = clk_get_rate(fbi->clk) / needed_pixclk;
278
279         /* check whether divisor is too small. */
280         if (divider_int < 2) {
281                 dev_warn(fbi->dev, "Warning: clock source is too slow."
282                                 "Try smaller resolution\n");
283                 divider_int = 2;
284         }
285
286         /*
287          * Set setting to reg.
288          */
289         x |= divider_int;
290         writel(x, fbi->reg_base + LCD_CFG_SCLK_DIV);
291 }
292
293 static void set_dma_control0(struct pxa168fb_info *fbi)
294 {
295         u32 x;
296
297         /*
298          * Set bit to enable graphics DMA.
299          */
300         x = readl(fbi->reg_base + LCD_SPU_DMA_CTRL0);
301         x &= ~CFG_GRA_ENA_MASK;
302         x |= fbi->active ? CFG_GRA_ENA(1) : CFG_GRA_ENA(0);
303
304         /*
305          * If we are in a pseudo-color mode, we need to enable
306          * palette lookup.
307          */
308         if (fbi->pix_fmt == PIX_FMT_PSEUDOCOLOR)
309                 x |= 0x10000000;
310
311         /*
312          * Configure hardware pixel format.
313          */
314         x &= ~(0xF << 16);
315         x |= (fbi->pix_fmt >> 1) << 16;
316
317         /*
318          * Check red and blue pixel swap.
319          * 1. source data swap
320          * 2. panel output data swap
321          */
322         x &= ~(1 << 12);
323         x |= ((fbi->pix_fmt & 1) ^ (fbi->panel_rbswap)) << 12;
324
325         writel(x, fbi->reg_base + LCD_SPU_DMA_CTRL0);
326 }
327
328 static void set_dma_control1(struct pxa168fb_info *fbi, int sync)
329 {
330         u32 x;
331
332         /*
333          * Configure default bits: vsync triggers DMA, gated clock
334          * enable, power save enable, configure alpha registers to
335          * display 100% graphics, and set pixel command.
336          */
337         x = readl(fbi->reg_base + LCD_SPU_DMA_CTRL1);
338         x |= 0x2032ff81;
339
340         /*
341          * We trigger DMA on the falling edge of vsync if vsync is
342          * active low, or on the rising edge if vsync is active high.
343          */
344         if (!(sync & FB_SYNC_VERT_HIGH_ACT))
345                 x |= 0x08000000;
346
347         writel(x, fbi->reg_base + LCD_SPU_DMA_CTRL1);
348 }
349
350 static void set_graphics_start(struct fb_info *info, int xoffset, int yoffset)
351 {
352         struct pxa168fb_info *fbi = info->par;
353         struct fb_var_screeninfo *var = &info->var;
354         int pixel_offset;
355         unsigned long addr;
356
357         pixel_offset = (yoffset * var->xres_virtual) + xoffset;
358
359         addr = fbi->fb_start_dma + (pixel_offset * (var->bits_per_pixel >> 3));
360         writel(addr, fbi->reg_base + LCD_CFG_GRA_START_ADDR0);
361 }
362
363 static void set_dumb_panel_control(struct fb_info *info)
364 {
365         struct pxa168fb_info *fbi = info->par;
366         struct pxa168fb_mach_info *mi = fbi->dev->platform_data;
367         u32 x;
368
369         /*
370          * Preserve enable flag.
371          */
372         x = readl(fbi->reg_base + LCD_SPU_DUMB_CTRL) & 0x00000001;
373
374         x |= (fbi->is_blanked ? 0x7 : mi->dumb_mode) << 28;
375         x |= mi->gpio_output_data << 20;
376         x |= mi->gpio_output_mask << 12;
377         x |= mi->panel_rgb_reverse_lanes ? 0x00000080 : 0;
378         x |= mi->invert_composite_blank ? 0x00000040 : 0;
379         x |= (info->var.sync & FB_SYNC_COMP_HIGH_ACT) ? 0x00000020 : 0;
380         x |= mi->invert_pix_val_ena ? 0x00000010 : 0;
381         x |= (info->var.sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : 0x00000008;
382         x |= (info->var.sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : 0x00000004;
383         x |= mi->invert_pixclock ? 0x00000002 : 0;
384
385         writel(x, fbi->reg_base + LCD_SPU_DUMB_CTRL);
386 }
387
388 static void set_dumb_screen_dimensions(struct fb_info *info)
389 {
390         struct pxa168fb_info *fbi = info->par;
391         struct fb_var_screeninfo *v = &info->var;
392         int x;
393         int y;
394
395         x = v->xres + v->right_margin + v->hsync_len + v->left_margin;
396         y = v->yres + v->lower_margin + v->vsync_len + v->upper_margin;
397
398         writel((y << 16) | x, fbi->reg_base + LCD_SPUT_V_H_TOTAL);
399 }
400
401 static int pxa168fb_set_par(struct fb_info *info)
402 {
403         struct pxa168fb_info *fbi = info->par;
404         struct fb_var_screeninfo *var = &info->var;
405         struct fb_videomode mode;
406         u32 x;
407         struct pxa168fb_mach_info *mi;
408
409         mi = fbi->dev->platform_data;
410
411         /*
412          * Set additional mode info.
413          */
414         if (fbi->pix_fmt == PIX_FMT_PSEUDOCOLOR)
415                 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
416         else
417                 info->fix.visual = FB_VISUAL_TRUECOLOR;
418         info->fix.line_length = var->xres_virtual * var->bits_per_pixel / 8;
419         info->fix.ypanstep = var->yres;
420
421         /*
422          * Disable panel output while we setup the display.
423          */
424         x = readl(fbi->reg_base + LCD_SPU_DUMB_CTRL);
425         writel(x & ~1, fbi->reg_base + LCD_SPU_DUMB_CTRL);
426
427         /*
428          * Configure global panel parameters.
429          */
430         writel((var->yres << 16) | var->xres,
431                 fbi->reg_base + LCD_SPU_V_H_ACTIVE);
432
433         /*
434          * convet var to video mode
435          */
436         fb_var_to_videomode(&mode, &info->var);
437
438         /* Calculate clock divisor. */
439         set_clock_divider(fbi, &mode);
440
441         /* Configure dma ctrl regs. */
442         set_dma_control0(fbi);
443         set_dma_control1(fbi, info->var.sync);
444
445         /*
446          * Configure graphics DMA parameters.
447          */
448         x = readl(fbi->reg_base + LCD_CFG_GRA_PITCH);
449         x = (x & ~0xFFFF) | ((var->xres_virtual * var->bits_per_pixel) >> 3);
450         writel(x, fbi->reg_base + LCD_CFG_GRA_PITCH);
451         writel((var->yres << 16) | var->xres,
452                 fbi->reg_base + LCD_SPU_GRA_HPXL_VLN);
453         writel((var->yres << 16) | var->xres,
454                 fbi->reg_base + LCD_SPU_GZM_HPXL_VLN);
455
456         /*
457          * Configure dumb panel ctrl regs & timings.
458          */
459         set_dumb_panel_control(info);
460         set_dumb_screen_dimensions(info);
461
462         writel((var->left_margin << 16) | var->right_margin,
463                         fbi->reg_base + LCD_SPU_H_PORCH);
464         writel((var->upper_margin << 16) | var->lower_margin,
465                         fbi->reg_base + LCD_SPU_V_PORCH);
466
467         /*
468          * Re-enable panel output.
469          */
470         x = readl(fbi->reg_base + LCD_SPU_DUMB_CTRL);
471         writel(x | 1, fbi->reg_base + LCD_SPU_DUMB_CTRL);
472
473         return 0;
474 }
475
476 static unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)
477 {
478         return ((chan & 0xffff) >> (16 - bf->length)) << bf->offset;
479 }
480
481 static u32 to_rgb(u16 red, u16 green, u16 blue)
482 {
483         red >>= 8;
484         green >>= 8;
485         blue >>= 8;
486
487         return (red << 16) | (green << 8) | blue;
488 }
489
490 static int
491 pxa168fb_setcolreg(unsigned int regno, unsigned int red, unsigned int green,
492                  unsigned int blue, unsigned int trans, struct fb_info *info)
493 {
494         struct pxa168fb_info *fbi = info->par;
495         u32 val;
496
497         if (info->var.grayscale)
498                 red = green = blue = (19595 * red + 38470 * green +
499                                         7471 * blue) >> 16;
500
501         if (info->fix.visual == FB_VISUAL_TRUECOLOR && regno < 16) {
502                 val =  chan_to_field(red,   &info->var.red);
503                 val |= chan_to_field(green, &info->var.green);
504                 val |= chan_to_field(blue , &info->var.blue);
505                 fbi->pseudo_palette[regno] = val;
506         }
507
508         if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR && regno < 256) {
509                 val = to_rgb(red, green, blue);
510                 writel(val, fbi->reg_base + LCD_SPU_SRAM_WRDAT);
511                 writel(0x8300 | regno, fbi->reg_base + LCD_SPU_SRAM_CTRL);
512         }
513
514         return 0;
515 }
516
517 static int pxa168fb_blank(int blank, struct fb_info *info)
518 {
519         struct pxa168fb_info *fbi = info->par;
520
521         fbi->is_blanked = (blank == FB_BLANK_UNBLANK) ? 0 : 1;
522         set_dumb_panel_control(info);
523
524         return 0;
525 }
526
527 static int pxa168fb_pan_display(struct fb_var_screeninfo *var,
528                                 struct fb_info *info)
529 {
530         set_graphics_start(info, var->xoffset, var->yoffset);
531
532         return 0;
533 }
534
535 static irqreturn_t pxa168fb_handle_irq(int irq, void *dev_id)
536 {
537         struct pxa168fb_info *fbi = dev_id;
538         u32 isr = readl(fbi->reg_base + SPU_IRQ_ISR);
539
540         if ((isr & GRA_FRAME_IRQ0_ENA_MASK)) {
541
542                 writel(isr & (~GRA_FRAME_IRQ0_ENA_MASK),
543                         fbi->reg_base + SPU_IRQ_ISR);
544
545                 return IRQ_HANDLED;
546         }
547         return IRQ_NONE;
548 }
549
550 static struct fb_ops pxa168fb_ops = {
551         .owner          = THIS_MODULE,
552         .fb_check_var   = pxa168fb_check_var,
553         .fb_set_par     = pxa168fb_set_par,
554         .fb_setcolreg   = pxa168fb_setcolreg,
555         .fb_blank       = pxa168fb_blank,
556         .fb_pan_display = pxa168fb_pan_display,
557         .fb_fillrect    = cfb_fillrect,
558         .fb_copyarea    = cfb_copyarea,
559         .fb_imageblit   = cfb_imageblit,
560 };
561
562 static int __devinit pxa168fb_init_mode(struct fb_info *info,
563                               struct pxa168fb_mach_info *mi)
564 {
565         struct pxa168fb_info *fbi = info->par;
566         struct fb_var_screeninfo *var = &info->var;
567         int ret = 0;
568         u32 total_w, total_h, refresh;
569         u64 div_result;
570         const struct fb_videomode *m;
571
572         /*
573          * Set default value
574          */
575         refresh = DEFAULT_REFRESH;
576
577         /* try to find best video mode. */
578         m = fb_find_best_mode(&info->var, &info->modelist);
579         if (m)
580                 fb_videomode_to_var(&info->var, m);
581
582         /* Init settings. */
583         var->xres_virtual = var->xres;
584         var->yres_virtual = info->fix.smem_len /
585                 (var->xres_virtual * (var->bits_per_pixel >> 3));
586         dev_dbg(fbi->dev, "pxa168fb: find best mode: res = %dx%d\n",
587                                 var->xres, var->yres);
588
589         /* correct pixclock. */
590         total_w = var->xres + var->left_margin + var->right_margin +
591                   var->hsync_len;
592         total_h = var->yres + var->upper_margin + var->lower_margin +
593                   var->vsync_len;
594
595         div_result = 1000000000000ll;
596         do_div(div_result, total_w * total_h * refresh);
597         var->pixclock = (u32)div_result;
598
599         return ret;
600 }
601
602 static int __devinit pxa168fb_probe(struct platform_device *pdev)
603 {
604         struct pxa168fb_mach_info *mi;
605         struct fb_info *info = 0;
606         struct pxa168fb_info *fbi = 0;
607         struct resource *res;
608         struct clk *clk;
609         int irq, ret;
610
611         mi = pdev->dev.platform_data;
612         if (mi == NULL) {
613                 dev_err(&pdev->dev, "no platform data defined\n");
614                 return -EINVAL;
615         }
616
617         clk = clk_get(&pdev->dev, "LCDCLK");
618         if (IS_ERR(clk)) {
619                 dev_err(&pdev->dev, "unable to get LCDCLK");
620                 return PTR_ERR(clk);
621         }
622
623         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
624         if (res == NULL) {
625                 dev_err(&pdev->dev, "no IO memory defined\n");
626                 ret = -ENOENT;
627                 goto failed_put_clk;
628         }
629
630         irq = platform_get_irq(pdev, 0);
631         if (irq < 0) {
632                 dev_err(&pdev->dev, "no IRQ defined\n");
633                 ret = -ENOENT;
634                 goto failed_put_clk;
635         }
636
637         info = framebuffer_alloc(sizeof(struct pxa168fb_info), &pdev->dev);
638         if (info == NULL) {
639                 ret = -ENOMEM;
640                 goto failed_put_clk;
641         }
642
643         /* Initialize private data */
644         fbi = info->par;
645         fbi->info = info;
646         fbi->clk = clk;
647         fbi->dev = info->dev = &pdev->dev;
648         fbi->panel_rbswap = mi->panel_rbswap;
649         fbi->is_blanked = 0;
650         fbi->active = mi->active;
651
652         /*
653          * Initialise static fb parameters.
654          */
655         info->flags = FBINFO_DEFAULT | FBINFO_PARTIAL_PAN_OK |
656                       FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN;
657         info->node = -1;
658         strlcpy(info->fix.id, mi->id, 16);
659         info->fix.type = FB_TYPE_PACKED_PIXELS;
660         info->fix.type_aux = 0;
661         info->fix.xpanstep = 0;
662         info->fix.ypanstep = 0;
663         info->fix.ywrapstep = 0;
664         info->fix.mmio_start = res->start;
665         info->fix.mmio_len = res->end - res->start + 1;
666         info->fix.accel = FB_ACCEL_NONE;
667         info->fbops = &pxa168fb_ops;
668         info->pseudo_palette = fbi->pseudo_palette;
669
670         /*
671          * Map LCD controller registers.
672          */
673         fbi->reg_base = ioremap_nocache(res->start, resource_size(res));
674         if (fbi->reg_base == NULL) {
675                 ret = -ENOMEM;
676                 goto failed_free_info;
677         }
678
679         /*
680          * Allocate framebuffer memory.
681          */
682         info->fix.smem_len = PAGE_ALIGN(DEFAULT_FB_SIZE);
683
684         info->screen_base = dma_alloc_writecombine(fbi->dev, info->fix.smem_len,
685                                                 &fbi->fb_start_dma, GFP_KERNEL);
686         if (info->screen_base == NULL) {
687                 ret = -ENOMEM;
688                 goto failed_free_info;
689         }
690
691         info->fix.smem_start = (unsigned long)fbi->fb_start_dma;
692         set_graphics_start(info, 0, 0);
693
694         /*
695          * Set video mode according to platform data.
696          */
697         set_mode(fbi, &info->var, mi->modes, mi->pix_fmt, 1);
698
699         fb_videomode_to_modelist(mi->modes, mi->num_modes, &info->modelist);
700
701         /*
702          * init video mode data.
703          */
704         pxa168fb_init_mode(info, mi);
705
706         /*
707          * Fill in sane defaults.
708          */
709         ret = pxa168fb_check_var(&info->var, info);
710         if (ret)
711                 goto failed_free_fbmem;
712
713         /*
714          * enable controller clock
715          */
716         clk_enable(fbi->clk);
717
718         pxa168fb_set_par(info);
719
720         /*
721          * Configure default register values.
722          */
723         writel(0, fbi->reg_base + LCD_SPU_BLANKCOLOR);
724         writel(mi->io_pin_allocation_mode, fbi->reg_base + SPU_IOPAD_CONTROL);
725         writel(0, fbi->reg_base + LCD_CFG_GRA_START_ADDR1);
726         writel(0, fbi->reg_base + LCD_SPU_GRA_OVSA_HPXL_VLN);
727         writel(0, fbi->reg_base + LCD_SPU_SRAM_PARA0);
728         writel(CFG_CSB_256x32(0x1)|CFG_CSB_256x24(0x1)|CFG_CSB_256x8(0x1),
729                 fbi->reg_base + LCD_SPU_SRAM_PARA1);
730
731         /*
732          * Allocate color map.
733          */
734         if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) {
735                 ret = -ENOMEM;
736                 goto failed_free_clk;
737         }
738
739         /*
740          * Register irq handler.
741          */
742         ret = request_irq(irq, pxa168fb_handle_irq, IRQF_SHARED,
743                                         info->fix.id, fbi);
744         if (ret < 0) {
745                 dev_err(&pdev->dev, "unable to request IRQ\n");
746                 ret = -ENXIO;
747                 goto failed_free_cmap;
748         }
749
750         /*
751          * Enable GFX interrupt
752          */
753         writel(GRA_FRAME_IRQ0_ENA(0x1), fbi->reg_base + SPU_IRQ_ENA);
754
755         /*
756          * Register framebuffer.
757          */
758         ret = register_framebuffer(info);
759         if (ret < 0) {
760                 dev_err(&pdev->dev, "Failed to register pxa168-fb: %d\n", ret);
761                 ret = -ENXIO;
762                 goto failed_free_irq;
763         }
764
765         platform_set_drvdata(pdev, fbi);
766         return 0;
767
768 failed_free_irq:
769         free_irq(irq, fbi);
770 failed_free_cmap:
771         fb_dealloc_cmap(&info->cmap);
772 failed_free_clk:
773         clk_disable(fbi->clk);
774 failed_free_fbmem:
775         dma_free_coherent(fbi->dev, info->fix.smem_len,
776                         info->screen_base, fbi->fb_start_dma);
777 failed_free_info:
778         kfree(info);
779 failed_put_clk:
780         clk_put(clk);
781
782         dev_err(&pdev->dev, "frame buffer device init failed with %d\n", ret);
783         return ret;
784 }
785
786 static int __devexit pxa168fb_remove(struct platform_device *pdev)
787 {
788         struct pxa168fb_info *fbi = platform_get_drvdata(pdev);
789         struct fb_info *info;
790         int irq;
791         unsigned int data;
792
793         if (!fbi)
794                 return 0;
795
796         /* disable DMA transfer */
797         data = readl(fbi->reg_base + LCD_SPU_DMA_CTRL0);
798         data &= ~CFG_GRA_ENA_MASK;
799         writel(data, fbi->reg_base + LCD_SPU_DMA_CTRL0);
800
801         info = fbi->info;
802
803         unregister_framebuffer(info);
804
805         writel(GRA_FRAME_IRQ0_ENA(0x0), fbi->reg_base + SPU_IRQ_ENA);
806
807         if (info->cmap.len)
808                 fb_dealloc_cmap(&info->cmap);
809
810         irq = platform_get_irq(pdev, 0);
811         free_irq(irq, fbi);
812
813         dma_free_writecombine(fbi->dev, PAGE_ALIGN(info->fix.smem_len),
814                                 info->screen_base, info->fix.smem_start);
815
816         iounmap(fbi->reg_base);
817
818         clk_disable(fbi->clk);
819         clk_put(fbi->clk);
820
821         framebuffer_release(info);
822
823         return 0;
824 }
825
826 static struct platform_driver pxa168fb_driver = {
827         .driver         = {
828                 .name   = "pxa168-fb",
829                 .owner  = THIS_MODULE,
830         },
831         .probe          = pxa168fb_probe,
832         .remove         = __devexit_p(pxa168fb_remove),
833 };
834
835 static int __init pxa168fb_init(void)
836 {
837         return platform_driver_register(&pxa168fb_driver);
838 }
839 module_init(pxa168fb_init);
840
841 static void __exit pxa168fb_exit(void)
842 {
843         platform_driver_unregister(&pxa168fb_driver);
844 }
845 module_exit(pxa168fb_exit);
846
847 MODULE_AUTHOR("Lennert Buytenhek <buytenh@marvell.com> "
848               "Green Wan <gwan@marvell.com>");
849 MODULE_DESCRIPTION("Framebuffer driver for PXA168/910");
850 MODULE_LICENSE("GPL");