imxfb: add mx27 support
[linux-2.6] / drivers / video / sh7760fb.c
1 /*
2  * SH7760/SH7763 LCDC Framebuffer driver.
3  *
4  * (c) 2006-2008 MSC Vertriebsges.m.b.H.,
5  *             Manuel Lauss <mano@roarinelk.homelinux.net>
6  * (c) 2008 Nobuhiro Iwamatsu <iwamatsu.nobuhiro@renesas.com>
7  *
8  *  This file is subject to the terms and conditions of the GNU General
9  *  Public License.  See the file COPYING in the main directory of this
10  *  archive for more details.
11  *
12  * PLEASE HAVE A LOOK AT Documentation/fb/sh7760fb.txt!
13  *
14  * Thanks to Siegfried Schaefer <s.schaefer at schaefer-edv.de>
15  *     for his original source and testing!
16  *
17  * sh7760_setcolreg get from drivers/video/sh_mobile_lcdcfb.c
18  */
19
20 #include <linux/completion.h>
21 #include <linux/delay.h>
22 #include <linux/dma-mapping.h>
23 #include <linux/fb.h>
24 #include <linux/interrupt.h>
25 #include <linux/io.h>
26 #include <linux/kernel.h>
27 #include <linux/module.h>
28 #include <linux/platform_device.h>
29
30 #include <asm/sh7760fb.h>
31
32 struct sh7760fb_par {
33         void __iomem *base;
34         int irq;
35
36         struct sh7760fb_platdata *pd;   /* display information */
37
38         dma_addr_t fbdma;       /* physical address */
39
40         int rot;                /* rotation enabled? */
41
42         u32 pseudo_palette[16];
43
44         struct platform_device *dev;
45         struct resource *ioarea;
46         struct completion vsync;        /* vsync irq event */
47 };
48
49 static irqreturn_t sh7760fb_irq(int irq, void *data)
50 {
51         struct completion *c = data;
52
53         complete(c);
54
55         return IRQ_HANDLED;
56 }
57
58 /* wait_for_lps - wait until power supply has reached a certain state. */
59 static int wait_for_lps(struct sh7760fb_par *par, int val)
60 {
61         int i = 100;
62         while (--i && ((ioread16(par->base + LDPMMR) & 3) != val))
63                 msleep(1);
64
65         if (i <= 0)
66                 return -ETIMEDOUT;
67
68         return 0;
69 }
70
71 /* en/disable the LCDC */
72 static int sh7760fb_blank(int blank, struct fb_info *info)
73 {
74         struct sh7760fb_par *par = info->par;
75         struct sh7760fb_platdata *pd = par->pd;
76         unsigned short cntr = ioread16(par->base + LDCNTR);
77         unsigned short intr = ioread16(par->base + LDINTR);
78         int lps;
79
80         if (blank == FB_BLANK_UNBLANK) {
81                 intr |= VINT_START;
82                 cntr = LDCNTR_DON2 | LDCNTR_DON;
83                 lps = 3;
84         } else {
85                 intr &= ~VINT_START;
86                 cntr = LDCNTR_DON2;
87                 lps = 0;
88         }
89
90         if (pd->blank)
91                 pd->blank(blank);
92
93         iowrite16(intr, par->base + LDINTR);
94         iowrite16(cntr, par->base + LDCNTR);
95
96         return wait_for_lps(par, lps);
97 }
98
99 static int sh7760_setcolreg (u_int regno,
100         u_int red, u_int green, u_int blue,
101         u_int transp, struct fb_info *info)
102 {
103         u32 *palette = info->pseudo_palette;
104
105         if (regno >= 16)
106                 return -EINVAL;
107
108         /* only FB_VISUAL_TRUECOLOR supported */
109
110         red >>= 16 - info->var.red.length;
111         green >>= 16 - info->var.green.length;
112         blue >>= 16 - info->var.blue.length;
113         transp >>= 16 - info->var.transp.length;
114
115         palette[regno] = (red << info->var.red.offset) |
116                 (green << info->var.green.offset) |
117                 (blue << info->var.blue.offset) |
118                 (transp << info->var.transp.offset);
119
120         return 0;
121 }
122
123 static void encode_fix(struct fb_fix_screeninfo *fix, struct fb_info *info,
124                        unsigned long stride)
125 {
126         memset(fix, 0, sizeof(struct fb_fix_screeninfo));
127         strcpy(fix->id, "sh7760-lcdc");
128
129         fix->smem_start = (unsigned long)info->screen_base;
130         fix->smem_len = info->screen_size;
131
132         fix->line_length = stride;
133 }
134
135 static int sh7760fb_get_color_info(struct device *dev,
136                                    u16 lddfr, int *bpp, int *gray)
137 {
138         int lbpp, lgray;
139
140         lgray = lbpp = 0;
141
142         switch (lddfr & LDDFR_COLOR_MASK) {
143         case LDDFR_1BPP_MONO:
144                 lgray = 1;
145                 lbpp = 1;
146                 break;
147         case LDDFR_2BPP_MONO:
148                 lgray = 1;
149                 lbpp = 2;
150                 break;
151         case LDDFR_4BPP_MONO:
152                 lgray = 1;
153         case LDDFR_4BPP:
154                 lbpp = 4;
155                 break;
156         case LDDFR_6BPP_MONO:
157                 lgray = 1;
158         case LDDFR_8BPP:
159                 lbpp = 8;
160                 break;
161         case LDDFR_16BPP_RGB555:
162         case LDDFR_16BPP_RGB565:
163                 lbpp = 16;
164                 lgray = 0;
165                 break;
166         default:
167                 dev_dbg(dev, "unsupported LDDFR bit depth.\n");
168                 return -EINVAL;
169         }
170
171         if (bpp)
172                 *bpp = lbpp;
173         if (gray)
174                 *gray = lgray;
175
176         return 0;
177 }
178
179 static int sh7760fb_check_var(struct fb_var_screeninfo *var,
180                               struct fb_info *info)
181 {
182         struct fb_fix_screeninfo *fix = &info->fix;
183         struct sh7760fb_par *par = info->par;
184         int ret, bpp;
185
186         /* get color info from register value */
187         ret = sh7760fb_get_color_info(info->dev, par->pd->lddfr, &bpp, NULL);
188         if (ret)
189                 return ret;
190
191         var->bits_per_pixel = bpp;
192
193         if ((var->grayscale) && (var->bits_per_pixel == 1))
194                 fix->visual = FB_VISUAL_MONO10;
195         else if (var->bits_per_pixel >= 15)
196                 fix->visual = FB_VISUAL_TRUECOLOR;
197         else
198                 fix->visual = FB_VISUAL_PSEUDOCOLOR;
199
200         /* TODO: add some more validation here */
201         return 0;
202 }
203
204 /*
205  * sh7760fb_set_par - set videomode.
206  *
207  * NOTE: The rotation, grayscale and DSTN codepaths are
208  *     totally untested!
209  */
210 static int sh7760fb_set_par(struct fb_info *info)
211 {
212         struct sh7760fb_par *par = info->par;
213         struct fb_videomode *vm = par->pd->def_mode;
214         unsigned long sbase, dstn_off, ldsarl, stride;
215         unsigned short hsynp, hsynw, htcn, hdcn;
216         unsigned short vsynp, vsynw, vtln, vdln;
217         unsigned short lddfr, ldmtr;
218         int ret, bpp, gray;
219
220         par->rot = par->pd->rotate;
221
222         /* rotate only works with xres <= 320 */
223         if (par->rot && (vm->xres > 320)) {
224                 dev_dbg(info->dev, "rotation disabled due to display size\n");
225                 par->rot = 0;
226         }
227
228         /* calculate LCDC reg vals from display parameters */
229         hsynp = vm->right_margin + vm->xres;
230         hsynw = vm->hsync_len;
231         htcn = vm->left_margin + hsynp + hsynw;
232         hdcn = vm->xres;
233         vsynp = vm->lower_margin + vm->yres;
234         vsynw = vm->vsync_len;
235         vtln = vm->upper_margin + vsynp + vsynw;
236         vdln = vm->yres;
237
238         /* get color info from register value */
239         ret = sh7760fb_get_color_info(info->dev, par->pd->lddfr, &bpp, &gray);
240         if (ret)
241                 return ret;
242
243         dev_dbg(info->dev, "%dx%d %dbpp %s (orientation %s)\n", hdcn,
244                 vdln, bpp, gray ? "grayscale" : "color",
245                 par->rot ? "rotated" : "normal");
246
247 #ifdef CONFIG_CPU_LITTLE_ENDIAN
248         lddfr = par->pd->lddfr | (1 << 8);
249 #else
250         lddfr = par->pd->lddfr & ~(1 << 8);
251 #endif
252
253         ldmtr = par->pd->ldmtr;
254
255         if (!(vm->sync & FB_SYNC_HOR_HIGH_ACT))
256                 ldmtr |= LDMTR_CL1POL;
257         if (!(vm->sync & FB_SYNC_VERT_HIGH_ACT))
258                 ldmtr |= LDMTR_FLMPOL;
259
260         /* shut down LCDC before changing display parameters */
261         sh7760fb_blank(FB_BLANK_POWERDOWN, info);
262
263         iowrite16(par->pd->ldickr, par->base + LDICKR); /* pixclock */
264         iowrite16(ldmtr, par->base + LDMTR);    /* polarities */
265         iowrite16(lddfr, par->base + LDDFR);    /* color/depth */
266         iowrite16((par->rot ? 1 << 13 : 0), par->base + LDSMR); /* rotate */
267         iowrite16(par->pd->ldpmmr, par->base + LDPMMR); /* Power Management */
268         iowrite16(par->pd->ldpspr, par->base + LDPSPR); /* Power Supply Ctrl */
269
270         /* display resolution */
271         iowrite16(((htcn >> 3) - 1) | (((hdcn >> 3) - 1) << 8),
272                   par->base + LDHCNR);
273         iowrite16(vdln - 1, par->base + LDVDLNR);
274         iowrite16(vtln - 1, par->base + LDVTLNR);
275         /* h/v sync signals */
276         iowrite16((vsynp - 1) | ((vsynw - 1) << 12), par->base + LDVSYNR);
277         iowrite16(((hsynp >> 3) - 1) | (((hsynw >> 3) - 1) << 12),
278                   par->base + LDHSYNR);
279         /* AC modulation sig */
280         iowrite16(par->pd->ldaclnr, par->base + LDACLNR);
281
282         stride = (par->rot) ? vtln : hdcn;
283         if (!gray)
284                 stride *= (bpp + 7) >> 3;
285         else {
286                 if (bpp == 1)
287                         stride >>= 3;
288                 else if (bpp == 2)
289                         stride >>= 2;
290                 else if (bpp == 4)
291                         stride >>= 1;
292                 /* 6 bpp == 8 bpp */
293         }
294
295         /* if rotated, stride must be power of 2 */
296         if (par->rot) {
297                 unsigned long bit = 1 << 31;
298                 while (bit) {
299                         if (stride & bit)
300                                 break;
301                         bit >>= 1;
302                 }
303                 if (stride & ~bit)
304                         stride = bit << 1;      /* not P-o-2, round up */
305         }
306         iowrite16(stride, par->base + LDLAOR);
307
308         /* set display mem start address */
309         sbase = (unsigned long)par->fbdma;
310         if (par->rot)
311                 sbase += (hdcn - 1) * stride;
312
313         iowrite32(sbase, par->base + LDSARU);
314
315         /*
316          * for DSTN need to set address for lower half.
317          * I (mlau) don't know which address to set it to,
318          * so I guessed at (stride * yres/2).
319          */
320         if (((ldmtr & 0x003f) >= LDMTR_DSTN_MONO_8) &&
321             ((ldmtr & 0x003f) <= LDMTR_DSTN_COLOR_16)) {
322
323                 dev_dbg(info->dev, " ***** DSTN untested! *****\n");
324
325                 dstn_off = stride;
326                 if (par->rot)
327                         dstn_off *= hdcn >> 1;
328                 else
329                         dstn_off *= vdln >> 1;
330
331                 ldsarl = sbase + dstn_off;
332         } else
333                 ldsarl = 0;
334
335         iowrite32(ldsarl, par->base + LDSARL);  /* mem for lower half of DSTN */
336
337         encode_fix(&info->fix, info, stride);
338         sh7760fb_check_var(&info->var, info);
339
340         sh7760fb_blank(FB_BLANK_UNBLANK, info); /* panel on! */
341
342         dev_dbg(info->dev, "hdcn  : %6d htcn  : %6d\n", hdcn, htcn);
343         dev_dbg(info->dev, "hsynw : %6d hsynp : %6d\n", hsynw, hsynp);
344         dev_dbg(info->dev, "vdln  : %6d vtln  : %6d\n", vdln, vtln);
345         dev_dbg(info->dev, "vsynw : %6d vsynp : %6d\n", vsynw, vsynp);
346         dev_dbg(info->dev, "clksrc: %6d clkdiv: %6d\n",
347                 (par->pd->ldickr >> 12) & 3, par->pd->ldickr & 0x1f);
348         dev_dbg(info->dev, "ldpmmr: 0x%04x ldpspr: 0x%04x\n", par->pd->ldpmmr,
349                 par->pd->ldpspr);
350         dev_dbg(info->dev, "ldmtr : 0x%04x lddfr : 0x%04x\n", ldmtr, lddfr);
351         dev_dbg(info->dev, "ldlaor: %ld\n", stride);
352         dev_dbg(info->dev, "ldsaru: 0x%08lx ldsarl: 0x%08lx\n", sbase, ldsarl);
353
354         return 0;
355 }
356
357 static struct fb_ops sh7760fb_ops = {
358         .owner = THIS_MODULE,
359         .fb_blank = sh7760fb_blank,
360         .fb_check_var = sh7760fb_check_var,
361         .fb_setcolreg = sh7760_setcolreg,
362         .fb_set_par = sh7760fb_set_par,
363         .fb_fillrect = cfb_fillrect,
364         .fb_copyarea = cfb_copyarea,
365         .fb_imageblit = cfb_imageblit,
366 };
367
368 static void sh7760fb_free_mem(struct fb_info *info)
369 {
370         struct sh7760fb_par *par = info->par;
371
372         if (!info->screen_base)
373                 return;
374
375         dma_free_coherent(info->dev, info->screen_size,
376                           info->screen_base, par->fbdma);
377
378         par->fbdma = 0;
379         info->screen_base = NULL;
380         info->screen_size = 0;
381 }
382
383 /* allocate the framebuffer memory. This memory must be in Area3,
384  * (dictated by the DMA engine) and contiguous, at a 512 byte boundary.
385  */
386 static int sh7760fb_alloc_mem(struct fb_info *info)
387 {
388         struct sh7760fb_par *par = info->par;
389         void *fbmem;
390         unsigned long vram;
391         int ret, bpp;
392
393         if (info->screen_base)
394                 return 0;
395
396         /* get color info from register value */
397         ret = sh7760fb_get_color_info(info->dev, par->pd->lddfr, &bpp, NULL);
398         if (ret) {
399                 printk(KERN_ERR "colinfo\n");
400                 return ret;
401         }
402
403         /* min VRAM: xres_min = 16, yres_min = 1, bpp = 1: 2byte -> 1 page
404            max VRAM: xres_max = 1024, yres_max = 1024, bpp = 16: 2MB */
405
406         vram = info->var.xres * info->var.yres;
407         if (info->var.grayscale) {
408                 if (bpp == 1)
409                         vram >>= 3;
410                 else if (bpp == 2)
411                         vram >>= 2;
412                 else if (bpp == 4)
413                         vram >>= 1;
414         } else if (bpp > 8)
415                 vram *= 2;
416         if ((vram < 1) || (vram > 1024 * 2048)) {
417                 dev_dbg(info->dev, "too much VRAM required. Check settings\n");
418                 return -ENODEV;
419         }
420
421         if (vram < PAGE_SIZE)
422                 vram = PAGE_SIZE;
423
424         fbmem = dma_alloc_coherent(info->dev, vram, &par->fbdma, GFP_KERNEL);
425
426         if (!fbmem)
427                 return -ENOMEM;
428
429         if ((par->fbdma & SH7760FB_DMA_MASK) != SH7760FB_DMA_MASK) {
430                 sh7760fb_free_mem(info);
431                 dev_err(info->dev, "kernel gave me memory at 0x%08lx, which is"
432                         "unusable for the LCDC\n", (unsigned long)par->fbdma);
433                 return -ENOMEM;
434         }
435
436         info->screen_base = fbmem;
437         info->screen_size = vram;
438
439         return 0;
440 }
441
442 static int __devinit sh7760fb_probe(struct platform_device *pdev)
443 {
444         struct fb_info *info;
445         struct resource *res;
446         struct sh7760fb_par *par;
447         int ret;
448
449         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
450         if (unlikely(res == NULL)) {
451                 dev_err(&pdev->dev, "invalid resource\n");
452                 return -EINVAL;
453         }
454
455         info = framebuffer_alloc(sizeof(struct sh7760fb_par), &pdev->dev);
456         if (!info)
457                 return -ENOMEM;
458
459         par = info->par;
460         par->dev = pdev;
461
462         par->pd = pdev->dev.platform_data;
463         if (!par->pd) {
464                 dev_dbg(info->dev, "no display setup data!\n");
465                 ret = -ENODEV;
466                 goto out_fb;
467         }
468
469         par->ioarea = request_mem_region(res->start,
470                                          (res->end - res->start), pdev->name);
471         if (!par->ioarea) {
472                 dev_err(&pdev->dev, "mmio area busy\n");
473                 ret = -EBUSY;
474                 goto out_fb;
475         }
476
477         par->base = ioremap_nocache(res->start, res->end - res->start + 1);
478         if (!par->base) {
479                 dev_err(&pdev->dev, "cannot remap\n");
480                 ret = -ENODEV;
481                 goto out_res;
482         }
483
484         iowrite16(0, par->base + LDINTR);       /* disable vsync irq */
485         par->irq = platform_get_irq(pdev, 0);
486         if (par->irq >= 0) {
487                 ret = request_irq(par->irq, sh7760fb_irq, 0,
488                                   "sh7760-lcdc", &par->vsync);
489                 if (ret) {
490                         dev_err(&pdev->dev, "cannot grab IRQ\n");
491                         par->irq = -ENXIO;
492                 } else
493                         disable_irq_nosync(par->irq);
494         }
495
496         fb_videomode_to_var(&info->var, par->pd->def_mode);
497
498         ret = sh7760fb_alloc_mem(info);
499         if (ret) {
500                 dev_dbg(info->dev, "framebuffer memory allocation failed!\n");
501                 goto out_unmap;
502         }
503
504         info->pseudo_palette = par->pseudo_palette;
505
506         /* fixup color register bitpositions. These are fixed by hardware */
507         info->var.red.offset = 11;
508         info->var.red.length = 5;
509         info->var.red.msb_right = 0;
510
511         info->var.green.offset = 5;
512         info->var.green.length = 6;
513         info->var.green.msb_right = 0;
514
515         info->var.blue.offset = 0;
516         info->var.blue.length = 5;
517         info->var.blue.msb_right = 0;
518
519         info->var.transp.offset = 0;
520         info->var.transp.length = 0;
521         info->var.transp.msb_right = 0;
522
523         /* set the DON2 bit now, before cmap allocation, as it will randomize
524          * palette memory.
525          */
526         iowrite16(LDCNTR_DON2, par->base + LDCNTR);
527         info->fbops = &sh7760fb_ops;
528
529         ret = fb_alloc_cmap(&info->cmap, 256, 0);
530         if (ret) {
531                 dev_dbg(info->dev, "Unable to allocate cmap memory\n");
532                 goto out_mem;
533         }
534
535         ret = register_framebuffer(info);
536         if (ret < 0) {
537                 dev_dbg(info->dev, "cannot register fb!\n");
538                 goto out_cmap;
539         }
540         platform_set_drvdata(pdev, info);
541
542         printk(KERN_INFO "%s: memory at phys 0x%08lx-0x%08lx, size %ld KiB\n",
543                pdev->name,
544                (unsigned long)par->fbdma,
545                (unsigned long)(par->fbdma + info->screen_size - 1),
546                info->screen_size >> 10);
547
548         return 0;
549
550 out_cmap:
551         sh7760fb_blank(FB_BLANK_POWERDOWN, info);
552         fb_dealloc_cmap(&info->cmap);
553 out_mem:
554         sh7760fb_free_mem(info);
555 out_unmap:
556         if (par->irq >= 0)
557                 free_irq(par->irq, &par->vsync);
558         iounmap(par->base);
559 out_res:
560         release_resource(par->ioarea);
561         kfree(par->ioarea);
562 out_fb:
563         framebuffer_release(info);
564         return ret;
565 }
566
567 static int __devexit sh7760fb_remove(struct platform_device *dev)
568 {
569         struct fb_info *info = platform_get_drvdata(dev);
570         struct sh7760fb_par *par = info->par;
571
572         sh7760fb_blank(FB_BLANK_POWERDOWN, info);
573         unregister_framebuffer(info);
574         fb_dealloc_cmap(&info->cmap);
575         sh7760fb_free_mem(info);
576         if (par->irq >= 0)
577                 free_irq(par->irq, par);
578         iounmap(par->base);
579         release_resource(par->ioarea);
580         kfree(par->ioarea);
581         framebuffer_release(info);
582         platform_set_drvdata(dev, NULL);
583
584         return 0;
585 }
586
587 static struct platform_driver sh7760_lcdc_driver = {
588         .driver = {
589                    .name = "sh7760-lcdc",
590                    .owner = THIS_MODULE,
591                    },
592         .probe = sh7760fb_probe,
593         .remove = __devexit_p(sh7760fb_remove),
594 };
595
596 static int __init sh7760fb_init(void)
597 {
598         return platform_driver_register(&sh7760_lcdc_driver);
599 }
600
601 static void __exit sh7760fb_exit(void)
602 {
603         platform_driver_unregister(&sh7760_lcdc_driver);
604 }
605
606 module_init(sh7760fb_init);
607 module_exit(sh7760fb_exit);
608
609 MODULE_AUTHOR("Nobuhiro Iwamatsu, Manuel Lauss");
610 MODULE_DESCRIPTION("FBdev for SH7760/63 integrated LCD Controller");
611 MODULE_LICENSE("GPL");