vga16fb: source code improvement
[linux-2.6] / drivers / video / leo.c
1 /* leo.c: LEO frame buffer driver
2  *
3  * Copyright (C) 2003, 2006 David S. Miller (davem@davemloft.net)
4  * Copyright (C) 1996-1999 Jakub Jelinek (jj@ultra.linux.cz)
5  * Copyright (C) 1997 Michal Rehacek (Michal.Rehacek@st.mff.cuni.cz)
6  *
7  * Driver layout based loosely on tgafb.c, see that file for credits.
8  */
9
10 #include <linux/module.h>
11 #include <linux/kernel.h>
12 #include <linux/errno.h>
13 #include <linux/string.h>
14 #include <linux/slab.h>
15 #include <linux/delay.h>
16 #include <linux/init.h>
17 #include <linux/fb.h>
18 #include <linux/mm.h>
19 #include <linux/of_device.h>
20 #include <linux/io.h>
21
22 #include <asm/fbio.h>
23
24 #include "sbuslib.h"
25
26 /*
27  * Local functions.
28  */
29
30 static int leo_setcolreg(unsigned, unsigned, unsigned, unsigned,
31                          unsigned, struct fb_info *);
32 static int leo_blank(int, struct fb_info *);
33
34 static int leo_mmap(struct fb_info *, struct vm_area_struct *);
35 static int leo_ioctl(struct fb_info *, unsigned int, unsigned long);
36
37 /*
38  *  Frame buffer operations
39  */
40
41 static struct fb_ops leo_ops = {
42         .owner                  = THIS_MODULE,
43         .fb_setcolreg           = leo_setcolreg,
44         .fb_blank               = leo_blank,
45         .fb_fillrect            = cfb_fillrect,
46         .fb_copyarea            = cfb_copyarea,
47         .fb_imageblit           = cfb_imageblit,
48         .fb_mmap                = leo_mmap,
49         .fb_ioctl               = leo_ioctl,
50 #ifdef CONFIG_COMPAT
51         .fb_compat_ioctl        = sbusfb_compat_ioctl,
52 #endif
53 };
54
55 #define LEO_OFF_LC_SS0_KRN      0x00200000UL
56 #define LEO_OFF_LC_SS0_USR      0x00201000UL
57 #define LEO_OFF_LC_SS1_KRN      0x01200000UL
58 #define LEO_OFF_LC_SS1_USR      0x01201000UL
59 #define LEO_OFF_LD_SS0          0x00400000UL
60 #define LEO_OFF_LD_SS1          0x01400000UL
61 #define LEO_OFF_LD_GBL          0x00401000UL
62 #define LEO_OFF_LX_KRN          0x00600000UL
63 #define LEO_OFF_LX_CURSOR       0x00601000UL
64 #define LEO_OFF_SS0             0x00800000UL
65 #define LEO_OFF_SS1             0x01800000UL
66 #define LEO_OFF_UNK             0x00602000UL
67 #define LEO_OFF_UNK2            0x00000000UL
68
69 #define LEO_CUR_ENABLE          0x00000080
70 #define LEO_CUR_UPDATE          0x00000030
71 #define LEO_CUR_PROGRESS        0x00000006
72 #define LEO_CUR_UPDATECMAP      0x00000003
73
74 #define LEO_CUR_TYPE_MASK       0x00000000
75 #define LEO_CUR_TYPE_IMAGE      0x00000020
76 #define LEO_CUR_TYPE_CMAP       0x00000050
77
78 struct leo_cursor {
79         u8      xxx0[16];
80         u32     cur_type;
81         u32     cur_misc;
82         u32     cur_cursxy;
83         u32     cur_data;
84 };
85
86 #define LEO_KRN_TYPE_CLUT0      0x00001000
87 #define LEO_KRN_TYPE_CLUT1      0x00001001
88 #define LEO_KRN_TYPE_CLUT2      0x00001002
89 #define LEO_KRN_TYPE_WID        0x00001003
90 #define LEO_KRN_TYPE_UNK        0x00001006
91 #define LEO_KRN_TYPE_VIDEO      0x00002003
92 #define LEO_KRN_TYPE_CLUTDATA   0x00004000
93 #define LEO_KRN_CSR_ENABLE      0x00000008
94 #define LEO_KRN_CSR_PROGRESS    0x00000004
95 #define LEO_KRN_CSR_UNK         0x00000002
96 #define LEO_KRN_CSR_UNK2        0x00000001
97
98 struct leo_lx_krn {
99         u32     krn_type;
100         u32     krn_csr;
101         u32     krn_value;
102 };
103
104 struct leo_lc_ss0_krn {
105         u32     misc;
106         u8      xxx0[0x800-4];
107         u32     rev;
108 };
109
110 struct leo_lc_ss0_usr {
111         u32     csr;
112         u32     addrspace;
113         u32     fontmsk;
114         u32     fontt;
115         u32     extent;
116         u32     src;
117         u32     dst;
118         u32     copy;
119         u32     fill;
120 };
121
122 struct leo_lc_ss1_krn {
123         u8      unknown;
124 };
125
126 struct leo_lc_ss1_usr {
127         u8      unknown;
128 };
129
130 struct leo_ld_ss0 {
131         u8      xxx0[0xe00];
132         u32     csr;
133         u32     wid;
134         u32     wmask;
135         u32     widclip;
136         u32     vclipmin;
137         u32     vclipmax;
138         u32     pickmin;        /* SS1 only */
139         u32     pickmax;        /* SS1 only */
140         u32     fg;
141         u32     bg;
142         u32     src;            /* Copy/Scroll (SS0 only) */
143         u32     dst;            /* Copy/Scroll/Fill (SS0 only) */
144         u32     extent;         /* Copy/Scroll/Fill size (SS0 only) */
145         u32     xxx1[3];
146         u32     setsem;         /* SS1 only */
147         u32     clrsem;         /* SS1 only */
148         u32     clrpick;        /* SS1 only */
149         u32     clrdat;         /* SS1 only */
150         u32     alpha;          /* SS1 only */
151         u8      xxx2[0x2c];
152         u32     winbg;
153         u32     planemask;
154         u32     rop;
155         u32     z;
156         u32     dczf;           /* SS1 only */
157         u32     dczb;           /* SS1 only */
158         u32     dcs;            /* SS1 only */
159         u32     dczs;           /* SS1 only */
160         u32     pickfb;         /* SS1 only */
161         u32     pickbb;         /* SS1 only */
162         u32     dcfc;           /* SS1 only */
163         u32     forcecol;       /* SS1 only */
164         u32     door[8];        /* SS1 only */
165         u32     pick[5];        /* SS1 only */
166 };
167
168 #define LEO_SS1_MISC_ENABLE     0x00000001
169 #define LEO_SS1_MISC_STEREO     0x00000002
170 struct leo_ld_ss1 {
171         u8      xxx0[0xef4];
172         u32     ss1_misc;
173 };
174
175 struct leo_ld_gbl {
176         u8      unknown;
177 };
178
179 struct leo_par {
180         spinlock_t              lock;
181         struct leo_lx_krn       __iomem *lx_krn;
182         struct leo_lc_ss0_usr   __iomem *lc_ss0_usr;
183         struct leo_ld_ss0       __iomem *ld_ss0;
184         struct leo_ld_ss1       __iomem *ld_ss1;
185         struct leo_cursor       __iomem *cursor;
186         u32                     extent;
187         u32                     clut_data[256];
188
189         u32                     flags;
190 #define LEO_FLAG_BLANKED        0x00000001
191
192         unsigned long           physbase;
193         unsigned long           which_io;
194         unsigned long           fbsize;
195 };
196
197 static void leo_wait(struct leo_lx_krn __iomem *lx_krn)
198 {
199         int i;
200
201         for (i = 0;
202              (sbus_readl(&lx_krn->krn_csr) & LEO_KRN_CSR_PROGRESS) &&
203              i < 300000;
204              i++)
205                 udelay(1); /* Busy wait at most 0.3 sec */
206         return;
207 }
208
209 /**
210  *      leo_setcolreg - Optional function. Sets a color register.
211  *      @regno: boolean, 0 copy local, 1 get_user() function
212  *      @red: frame buffer colormap structure
213  *      @green: The green value which can be up to 16 bits wide
214  *      @blue:  The blue value which can be up to 16 bits wide.
215  *      @transp: If supported the alpha value which can be up to 16 bits wide.
216  *      @info: frame buffer info structure
217  */
218 static int leo_setcolreg(unsigned regno,
219                          unsigned red, unsigned green, unsigned blue,
220                          unsigned transp, struct fb_info *info)
221 {
222         struct leo_par *par = (struct leo_par *) info->par;
223         struct leo_lx_krn __iomem *lx_krn = par->lx_krn;
224         unsigned long flags;
225         u32 val;
226         int i;
227
228         if (regno >= 256)
229                 return 1;
230
231         red >>= 8;
232         green >>= 8;
233         blue >>= 8;
234
235         par->clut_data[regno] = red | (green << 8) | (blue << 16);
236
237         spin_lock_irqsave(&par->lock, flags);
238
239         leo_wait(lx_krn);
240
241         sbus_writel(LEO_KRN_TYPE_CLUTDATA, &lx_krn->krn_type);
242         for (i = 0; i < 256; i++)
243                 sbus_writel(par->clut_data[i], &lx_krn->krn_value);
244         sbus_writel(LEO_KRN_TYPE_CLUT0, &lx_krn->krn_type);
245
246         val = sbus_readl(&lx_krn->krn_csr);
247         val |= (LEO_KRN_CSR_UNK | LEO_KRN_CSR_UNK2);
248         sbus_writel(val, &lx_krn->krn_csr);
249
250         spin_unlock_irqrestore(&par->lock, flags);
251
252         return 0;
253 }
254
255 /**
256  *      leo_blank - Optional function.  Blanks the display.
257  *      @blank_mode: the blank mode we want.
258  *      @info: frame buffer structure that represents a single frame buffer
259  */
260 static int leo_blank(int blank, struct fb_info *info)
261 {
262         struct leo_par *par = (struct leo_par *) info->par;
263         struct leo_lx_krn __iomem *lx_krn = par->lx_krn;
264         unsigned long flags;
265         u32 val;
266
267         spin_lock_irqsave(&par->lock, flags);
268
269         switch (blank) {
270         case FB_BLANK_UNBLANK: /* Unblanking */
271                 val = sbus_readl(&lx_krn->krn_csr);
272                 val |= LEO_KRN_CSR_ENABLE;
273                 sbus_writel(val, &lx_krn->krn_csr);
274                 par->flags &= ~LEO_FLAG_BLANKED;
275                 break;
276
277         case FB_BLANK_NORMAL: /* Normal blanking */
278         case FB_BLANK_VSYNC_SUSPEND: /* VESA blank (vsync off) */
279         case FB_BLANK_HSYNC_SUSPEND: /* VESA blank (hsync off) */
280         case FB_BLANK_POWERDOWN: /* Poweroff */
281                 val = sbus_readl(&lx_krn->krn_csr);
282                 val &= ~LEO_KRN_CSR_ENABLE;
283                 sbus_writel(val, &lx_krn->krn_csr);
284                 par->flags |= LEO_FLAG_BLANKED;
285                 break;
286         }
287
288         spin_unlock_irqrestore(&par->lock, flags);
289
290         return 0;
291 }
292
293 static struct sbus_mmap_map leo_mmap_map[] = {
294         {
295                 .voff   = LEO_SS0_MAP,
296                 .poff   = LEO_OFF_SS0,
297                 .size   = 0x800000
298         },
299         {
300                 .voff   = LEO_LC_SS0_USR_MAP,
301                 .poff   = LEO_OFF_LC_SS0_USR,
302                 .size   = 0x1000
303         },
304         {
305                 .voff   = LEO_LD_SS0_MAP,
306                 .poff   = LEO_OFF_LD_SS0,
307                 .size   = 0x1000
308         },
309         {
310                 .voff   = LEO_LX_CURSOR_MAP,
311                 .poff   = LEO_OFF_LX_CURSOR,
312                 .size   = 0x1000
313         },
314         {
315                 .voff   = LEO_SS1_MAP,
316                 .poff   = LEO_OFF_SS1,
317                 .size   = 0x800000
318         },
319         {
320                 .voff   = LEO_LC_SS1_USR_MAP,
321                 .poff   = LEO_OFF_LC_SS1_USR,
322                 .size   = 0x1000
323         },
324         {
325                 .voff   = LEO_LD_SS1_MAP,
326                 .poff   = LEO_OFF_LD_SS1,
327                 .size   = 0x1000
328         },
329         {
330                 .voff   = LEO_UNK_MAP,
331                 .poff   = LEO_OFF_UNK,
332                 .size   = 0x1000
333         },
334         {
335                 .voff   = LEO_LX_KRN_MAP,
336                 .poff   = LEO_OFF_LX_KRN,
337                 .size   = 0x1000
338         },
339         {
340                 .voff   = LEO_LC_SS0_KRN_MAP,
341                 .poff   = LEO_OFF_LC_SS0_KRN,
342                 .size   = 0x1000
343         },
344         {
345                 .voff   = LEO_LC_SS1_KRN_MAP,
346                 .poff   = LEO_OFF_LC_SS1_KRN,
347                 .size   = 0x1000
348         },
349         {
350                 .voff   = LEO_LD_GBL_MAP,
351                 .poff   = LEO_OFF_LD_GBL,
352                 .size   = 0x1000
353         },
354         {
355                 .voff   = LEO_UNK2_MAP,
356                 .poff   = LEO_OFF_UNK2,
357                 .size   = 0x100000
358         },
359         { .size = 0 }
360 };
361
362 static int leo_mmap(struct fb_info *info, struct vm_area_struct *vma)
363 {
364         struct leo_par *par = (struct leo_par *)info->par;
365
366         return sbusfb_mmap_helper(leo_mmap_map,
367                                   par->physbase, par->fbsize,
368                                   par->which_io, vma);
369 }
370
371 static int leo_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
372 {
373         struct leo_par *par = (struct leo_par *) info->par;
374
375         return sbusfb_ioctl_helper(cmd, arg, info,
376                                    FBTYPE_SUNLEO, 32, par->fbsize);
377 }
378
379 /*
380  *  Initialisation
381  */
382
383 static void
384 leo_init_fix(struct fb_info *info, struct device_node *dp)
385 {
386         strlcpy(info->fix.id, dp->name, sizeof(info->fix.id));
387
388         info->fix.type = FB_TYPE_PACKED_PIXELS;
389         info->fix.visual = FB_VISUAL_TRUECOLOR;
390
391         info->fix.line_length = 8192;
392
393         info->fix.accel = FB_ACCEL_SUN_LEO;
394 }
395
396 static void leo_wid_put(struct fb_info *info, struct fb_wid_list *wl)
397 {
398         struct leo_par *par = (struct leo_par *) info->par;
399         struct leo_lx_krn __iomem *lx_krn = par->lx_krn;
400         struct fb_wid_item *wi;
401         unsigned long flags;
402         u32 val;
403         int i, j;
404
405         spin_lock_irqsave(&par->lock, flags);
406
407         leo_wait(lx_krn);
408
409         for (i = 0, wi = wl->wl_list; i < wl->wl_count; i++, wi++) {
410                 switch (wi->wi_type) {
411                 case FB_WID_DBL_8:
412                         j = (wi->wi_index & 0xf) + 0x40;
413                         break;
414
415                 case FB_WID_DBL_24:
416                         j = wi->wi_index & 0x3f;
417                         break;
418
419                 default:
420                         continue;
421                 };
422                 sbus_writel(0x5800 + j, &lx_krn->krn_type);
423                 sbus_writel(wi->wi_values[0], &lx_krn->krn_value);
424         }
425         sbus_writel(LEO_KRN_TYPE_WID, &lx_krn->krn_type);
426
427         val = sbus_readl(&lx_krn->krn_csr);
428         val |= (LEO_KRN_CSR_UNK | LEO_KRN_CSR_UNK2);
429         sbus_writel(val, &lx_krn->krn_csr);
430
431         spin_unlock_irqrestore(&par->lock, flags);
432 }
433
434 static void leo_init_wids(struct fb_info *info)
435 {
436         struct fb_wid_item wi;
437         struct fb_wid_list wl;
438
439         wl.wl_count = 1;
440         wl.wl_list = &wi;
441         wi.wi_type = FB_WID_DBL_8;
442         wi.wi_index = 0;
443         wi.wi_values [0] = 0x2c0;
444         leo_wid_put(info, &wl);
445         wi.wi_index = 1;
446         wi.wi_values [0] = 0x30;
447         leo_wid_put(info, &wl);
448         wi.wi_index = 2;
449         wi.wi_values [0] = 0x20;
450         leo_wid_put(info, &wl);
451         wi.wi_type = FB_WID_DBL_24;
452         wi.wi_index = 1;
453         wi.wi_values [0] = 0x30;
454         leo_wid_put(info, &wl);
455 }
456
457 static void leo_switch_from_graph(struct fb_info *info)
458 {
459         struct leo_par *par = (struct leo_par *) info->par;
460         struct leo_ld_ss0 __iomem *ss = par->ld_ss0;
461         unsigned long flags;
462         u32 val;
463
464         spin_lock_irqsave(&par->lock, flags);
465
466         par->extent = ((info->var.xres - 1) |
467                        ((info->var.yres - 1) << 16));
468
469         sbus_writel(0xffffffff, &ss->wid);
470         sbus_writel(0xffff, &ss->wmask);
471         sbus_writel(0, &ss->vclipmin);
472         sbus_writel(par->extent, &ss->vclipmax);
473         sbus_writel(0, &ss->fg);
474         sbus_writel(0xff000000, &ss->planemask);
475         sbus_writel(0x310850, &ss->rop);
476         sbus_writel(0, &ss->widclip);
477         sbus_writel((info->var.xres-1) | ((info->var.yres-1) << 11),
478                     &par->lc_ss0_usr->extent);
479         sbus_writel(4, &par->lc_ss0_usr->addrspace);
480         sbus_writel(0x80000000, &par->lc_ss0_usr->fill);
481         sbus_writel(0, &par->lc_ss0_usr->fontt);
482         do {
483                 val = sbus_readl(&par->lc_ss0_usr->csr);
484         } while (val & 0x20000000);
485
486         /* setup screen buffer for cfb_* functions */
487         sbus_writel(1, &ss->wid);
488         sbus_writel(0x00ffffff, &ss->planemask);
489         sbus_writel(0x310b90, &ss->rop);
490         sbus_writel(0, &par->lc_ss0_usr->addrspace);
491
492         spin_unlock_irqrestore(&par->lock, flags);
493 }
494
495 static void leo_init_hw(struct fb_info *info)
496 {
497         struct leo_par *par = (struct leo_par *) info->par;
498         u32 val;
499
500         val = sbus_readl(&par->ld_ss1->ss1_misc);
501         val |= LEO_SS1_MISC_ENABLE;
502         sbus_writel(val, &par->ld_ss1->ss1_misc);
503
504         leo_switch_from_graph(info);
505 }
506
507 static void leo_fixup_var_rgb(struct fb_var_screeninfo *var)
508 {
509         var->red.offset = 0;
510         var->red.length = 8;
511         var->green.offset = 8;
512         var->green.length = 8;
513         var->blue.offset = 16;
514         var->blue.length = 8;
515         var->transp.offset = 0;
516         var->transp.length = 0;
517 }
518
519 static void leo_unmap_regs(struct of_device *op, struct fb_info *info,
520                            struct leo_par *par)
521 {
522         if (par->lc_ss0_usr)
523                 of_iounmap(&op->resource[0], par->lc_ss0_usr, 0x1000);
524         if (par->ld_ss0)
525                 of_iounmap(&op->resource[0], par->ld_ss0, 0x1000);
526         if (par->ld_ss1)
527                 of_iounmap(&op->resource[0], par->ld_ss1, 0x1000);
528         if (par->lx_krn)
529                 of_iounmap(&op->resource[0], par->lx_krn, 0x1000);
530         if (par->cursor)
531                 of_iounmap(&op->resource[0],
532                            par->cursor, sizeof(struct leo_cursor));
533         if (info->screen_base)
534                 of_iounmap(&op->resource[0], info->screen_base, 0x800000);
535 }
536
537 static int __devinit leo_probe(struct of_device *op,
538                                const struct of_device_id *match)
539 {
540         struct device_node *dp = op->node;
541         struct fb_info *info;
542         struct leo_par *par;
543         int linebytes, err;
544
545         info = framebuffer_alloc(sizeof(struct leo_par), &op->dev);
546
547         err = -ENOMEM;
548         if (!info)
549                 goto out_err;
550         par = info->par;
551
552         spin_lock_init(&par->lock);
553
554         par->physbase = op->resource[0].start;
555         par->which_io = op->resource[0].flags & IORESOURCE_BITS;
556
557         sbusfb_fill_var(&info->var, dp, 32);
558         leo_fixup_var_rgb(&info->var);
559
560         linebytes = of_getintprop_default(dp, "linebytes",
561                                           info->var.xres);
562         par->fbsize = PAGE_ALIGN(linebytes * info->var.yres);
563
564         par->lc_ss0_usr =
565                 of_ioremap(&op->resource[0], LEO_OFF_LC_SS0_USR,
566                            0x1000, "leolc ss0usr");
567         par->ld_ss0 =
568                 of_ioremap(&op->resource[0], LEO_OFF_LD_SS0,
569                            0x1000, "leold ss0");
570         par->ld_ss1 =
571                 of_ioremap(&op->resource[0], LEO_OFF_LD_SS1,
572                            0x1000, "leold ss1");
573         par->lx_krn =
574                 of_ioremap(&op->resource[0], LEO_OFF_LX_KRN,
575                            0x1000, "leolx krn");
576         par->cursor =
577                 of_ioremap(&op->resource[0], LEO_OFF_LX_CURSOR,
578                            sizeof(struct leo_cursor), "leolx cursor");
579         info->screen_base =
580                 of_ioremap(&op->resource[0], LEO_OFF_SS0,
581                            0x800000, "leo ram");
582         if (!par->lc_ss0_usr ||
583             !par->ld_ss0 ||
584             !par->ld_ss1 ||
585             !par->lx_krn ||
586             !par->cursor ||
587             !info->screen_base)
588                 goto out_unmap_regs;
589
590         info->flags = FBINFO_DEFAULT;
591         info->fbops = &leo_ops;
592         info->pseudo_palette = par->clut_data;
593
594         leo_init_wids(info);
595         leo_init_hw(info);
596
597         leo_blank(FB_BLANK_UNBLANK, info);
598
599         if (fb_alloc_cmap(&info->cmap, 256, 0))
600                 goto out_unmap_regs;
601
602         leo_init_fix(info, dp);
603
604         err = register_framebuffer(info);
605         if (err < 0)
606                 goto out_dealloc_cmap;
607
608         dev_set_drvdata(&op->dev, info);
609
610         printk(KERN_INFO "%s: leo at %lx:%lx\n",
611                dp->full_name,
612                par->which_io, par->physbase);
613
614         return 0;
615
616 out_dealloc_cmap:
617         fb_dealloc_cmap(&info->cmap);
618
619 out_unmap_regs:
620         leo_unmap_regs(op, info, par);
621         framebuffer_release(info);
622
623 out_err:
624         return err;
625 }
626
627 static int __devexit leo_remove(struct of_device *op)
628 {
629         struct fb_info *info = dev_get_drvdata(&op->dev);
630         struct leo_par *par = info->par;
631
632         unregister_framebuffer(info);
633         fb_dealloc_cmap(&info->cmap);
634
635         leo_unmap_regs(op, info, par);
636
637         framebuffer_release(info);
638
639         dev_set_drvdata(&op->dev, NULL);
640
641         return 0;
642 }
643
644 static struct of_device_id leo_match[] = {
645         {
646                 .name = "SUNW,leo",
647         },
648         {},
649 };
650 MODULE_DEVICE_TABLE(of, leo_match);
651
652 static struct of_platform_driver leo_driver = {
653         .name           = "leo",
654         .match_table    = leo_match,
655         .probe          = leo_probe,
656         .remove         = __devexit_p(leo_remove),
657 };
658
659 static int __init leo_init(void)
660 {
661         if (fb_get_options("leofb", NULL))
662                 return -ENODEV;
663
664         return of_register_driver(&leo_driver, &of_bus_type);
665 }
666
667 static void __exit leo_exit(void)
668 {
669         of_unregister_driver(&leo_driver);
670 }
671
672 module_init(leo_init);
673 module_exit(leo_exit);
674
675 MODULE_DESCRIPTION("framebuffer driver for LEO chipsets");
676 MODULE_AUTHOR("David S. Miller <davem@davemloft.net>");
677 MODULE_VERSION("2.0");
678 MODULE_LICENSE("GPL");