Linux-2.6.12-rc2
[safe/jmp/linux-2.6] / drivers / video / tcx.c
1 /* tcx.c: TCX frame buffer driver
2  *
3  * Copyright (C) 2003 David S. Miller (davem@redhat.com)
4  * Copyright (C) 1996,1998 Jakub Jelinek (jj@ultra.linux.cz)
5  * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
6  * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
7  *
8  * Driver layout based loosely on tgafb.c, see that file for credits.
9  */
10
11 #include <linux/module.h>
12 #include <linux/kernel.h>
13 #include <linux/errno.h>
14 #include <linux/string.h>
15 #include <linux/slab.h>
16 #include <linux/delay.h>
17 #include <linux/init.h>
18 #include <linux/fb.h>
19 #include <linux/mm.h>
20
21 #include <asm/io.h>
22 #include <asm/sbus.h>
23 #include <asm/oplib.h>
24 #include <asm/fbio.h>
25
26 #include "sbuslib.h"
27
28 /*
29  * Local functions.
30  */
31
32 static int tcx_setcolreg(unsigned, unsigned, unsigned, unsigned,
33                          unsigned, struct fb_info *);
34 static int tcx_blank(int, struct fb_info *);
35
36 static int tcx_mmap(struct fb_info *, struct file *, struct vm_area_struct *);
37 static int tcx_ioctl(struct inode *, struct file *, unsigned int,
38                      unsigned long, struct fb_info *);
39
40 /*
41  *  Frame buffer operations
42  */
43
44 static struct fb_ops tcx_ops = {
45         .owner                  = THIS_MODULE,
46         .fb_setcolreg           = tcx_setcolreg,
47         .fb_blank               = tcx_blank,
48         .fb_fillrect            = cfb_fillrect,
49         .fb_copyarea            = cfb_copyarea,
50         .fb_imageblit           = cfb_imageblit,
51         .fb_mmap                = tcx_mmap,
52         .fb_ioctl               = tcx_ioctl,
53         .fb_cursor              = soft_cursor,
54 };
55
56 /* THC definitions */
57 #define TCX_THC_MISC_REV_SHIFT       16
58 #define TCX_THC_MISC_REV_MASK        15
59 #define TCX_THC_MISC_VSYNC_DIS       (1 << 25)
60 #define TCX_THC_MISC_HSYNC_DIS       (1 << 24)
61 #define TCX_THC_MISC_RESET           (1 << 12)
62 #define TCX_THC_MISC_VIDEO           (1 << 10)
63 #define TCX_THC_MISC_SYNC            (1 << 9)
64 #define TCX_THC_MISC_VSYNC           (1 << 8)
65 #define TCX_THC_MISC_SYNC_ENAB       (1 << 7)
66 #define TCX_THC_MISC_CURS_RES        (1 << 6)
67 #define TCX_THC_MISC_INT_ENAB        (1 << 5)
68 #define TCX_THC_MISC_INT             (1 << 4)
69 #define TCX_THC_MISC_INIT            0x9f
70 #define TCX_THC_REV_REV_SHIFT        20
71 #define TCX_THC_REV_REV_MASK         15
72 #define TCX_THC_REV_MINREV_SHIFT     28
73 #define TCX_THC_REV_MINREV_MASK      15
74
75 /* The contents are unknown */
76 struct tcx_tec {
77         volatile u32 tec_matrix;
78         volatile u32 tec_clip;
79         volatile u32 tec_vdc;
80 };
81
82 struct tcx_thc {
83         volatile u32 thc_rev;
84         u32 thc_pad0[511];
85         volatile u32 thc_hs;            /* hsync timing */
86         volatile u32 thc_hsdvs;
87         volatile u32 thc_hd;
88         volatile u32 thc_vs;            /* vsync timing */
89         volatile u32 thc_vd;
90         volatile u32 thc_refresh;
91         volatile u32 thc_misc;
92         u32 thc_pad1[56];
93         volatile u32 thc_cursxy;        /* cursor x,y position (16 bits each) */
94         volatile u32 thc_cursmask[32];  /* cursor mask bits */
95         volatile u32 thc_cursbits[32];  /* what to show where mask enabled */
96 };
97
98 struct bt_regs {
99         volatile u32 addr;
100         volatile u32 color_map;
101         volatile u32 control;
102         volatile u32 cursor;
103 };
104
105 #define TCX_MMAP_ENTRIES 14
106
107 struct tcx_par {
108         spinlock_t              lock;
109         struct bt_regs          __iomem *bt;
110         struct tcx_thc          __iomem *thc;
111         struct tcx_tec          __iomem *tec;
112         volatile u32            __iomem *cplane;
113
114         u32                     flags;
115 #define TCX_FLAG_BLANKED        0x00000001
116
117         unsigned long           physbase;
118         unsigned long           fbsize;
119
120         struct sbus_mmap_map    mmap_map[TCX_MMAP_ENTRIES];
121         int                     lowdepth;
122
123         struct sbus_dev         *sdev;
124         struct list_head        list;
125 };
126
127 /* Reset control plane so that WID is 8-bit plane. */
128 static void __tcx_set_control_plane (struct tcx_par *par)
129 {
130         volatile u32 __iomem *p, *pend;
131         
132         if (par->lowdepth)
133                 return;
134
135         p = par->cplane;
136         if (p == NULL)
137                 return;
138         for (pend = p + par->fbsize; p < pend; p++) {
139                 u32 tmp = sbus_readl(p);
140
141                 tmp &= 0xffffff;
142                 sbus_writel(tmp, p);
143         }
144 }
145                                                 
146 static void tcx_reset (struct fb_info *info)
147 {
148         struct tcx_par *par = (struct tcx_par *) info->par;
149         unsigned long flags;
150
151         spin_lock_irqsave(&par->lock, flags);
152         __tcx_set_control_plane(par);
153         spin_unlock_irqrestore(&par->lock, flags);
154 }
155
156 /**
157  *      tcx_setcolreg - Optional function. Sets a color register.
158  *      @regno: boolean, 0 copy local, 1 get_user() function
159  *      @red: frame buffer colormap structure
160  *      @green: The green value which can be up to 16 bits wide
161  *      @blue:  The blue value which can be up to 16 bits wide.
162  *      @transp: If supported the alpha value which can be up to 16 bits wide.
163  *      @info: frame buffer info structure
164  */
165 static int tcx_setcolreg(unsigned regno,
166                          unsigned red, unsigned green, unsigned blue,
167                          unsigned transp, struct fb_info *info)
168 {
169         struct tcx_par *par = (struct tcx_par *) info->par;
170         struct bt_regs __iomem *bt = par->bt;
171         unsigned long flags;
172
173         if (regno >= 256)
174                 return 1;
175
176         red >>= 8;
177         green >>= 8;
178         blue >>= 8;
179
180         spin_lock_irqsave(&par->lock, flags);
181
182         sbus_writel(regno << 24, &bt->addr);
183         sbus_writel(red << 24, &bt->color_map);
184         sbus_writel(green << 24, &bt->color_map);
185         sbus_writel(blue << 24, &bt->color_map);
186
187         spin_unlock_irqrestore(&par->lock, flags);
188
189         return 0;
190 }
191
192 /**
193  *      tcx_blank - Optional function.  Blanks the display.
194  *      @blank_mode: the blank mode we want.
195  *      @info: frame buffer structure that represents a single frame buffer
196  */
197 static int
198 tcx_blank(int blank, struct fb_info *info)
199 {
200         struct tcx_par *par = (struct tcx_par *) info->par;
201         struct tcx_thc __iomem *thc = par->thc;
202         unsigned long flags;
203         u32 val;
204
205         spin_lock_irqsave(&par->lock, flags);
206
207         val = sbus_readl(&thc->thc_misc);
208
209         switch (blank) {
210         case FB_BLANK_UNBLANK: /* Unblanking */
211                 val &= ~(TCX_THC_MISC_VSYNC_DIS |
212                          TCX_THC_MISC_HSYNC_DIS);
213                 val |= TCX_THC_MISC_VIDEO;
214                 par->flags &= ~TCX_FLAG_BLANKED;
215                 break;
216
217         case FB_BLANK_NORMAL: /* Normal blanking */
218                 val &= ~TCX_THC_MISC_VIDEO;
219                 par->flags |= TCX_FLAG_BLANKED;
220                 break;
221
222         case FB_BLANK_VSYNC_SUSPEND: /* VESA blank (vsync off) */
223                 val |= TCX_THC_MISC_VSYNC_DIS;
224                 break;
225         case FB_BLANK_HSYNC_SUSPEND: /* VESA blank (hsync off) */
226                 val |= TCX_THC_MISC_HSYNC_DIS;
227                 break;
228
229         case FB_BLANK_POWERDOWN: /* Poweroff */
230                 break;
231         };
232
233         sbus_writel(val, &thc->thc_misc);
234
235         spin_unlock_irqrestore(&par->lock, flags);
236
237         return 0;
238 }
239
240 static struct sbus_mmap_map __tcx_mmap_map[TCX_MMAP_ENTRIES] = {
241         {
242                 .voff   = TCX_RAM8BIT,
243                 .size   = SBUS_MMAP_FBSIZE(1)
244         },
245         {
246                 .voff   = TCX_RAM24BIT,
247                 .size   = SBUS_MMAP_FBSIZE(4)
248         },
249         {
250                 .voff   = TCX_UNK3,
251                 .size   = SBUS_MMAP_FBSIZE(8)
252         },
253         {
254                 .voff   = TCX_UNK4,
255                 .size   = SBUS_MMAP_FBSIZE(8)
256         },
257         {
258                 .voff   = TCX_CONTROLPLANE,
259                 .size   = SBUS_MMAP_FBSIZE(4)
260         },
261         {
262                 .voff   = TCX_UNK6,
263                 .size   = SBUS_MMAP_FBSIZE(8)
264         },
265         {
266                 .voff   = TCX_UNK7,
267                 .size   = SBUS_MMAP_FBSIZE(8)
268         },
269         {
270                 .voff   = TCX_TEC,
271                 .size   = PAGE_SIZE
272         },
273         {
274                 .voff   = TCX_BTREGS,
275                 .size   = PAGE_SIZE
276         },
277         {
278                 .voff   = TCX_THC,
279                 .size   = PAGE_SIZE
280         },
281         {
282                 .voff   = TCX_DHC,
283                 .size   = PAGE_SIZE
284         },
285         {
286                 .voff   = TCX_ALT,
287                 .size   = PAGE_SIZE
288         },
289         {
290                 .voff   = TCX_UNK2,
291                 .size   = 0x20000
292         },
293         { .size = 0 }
294 };
295
296 static int tcx_mmap(struct fb_info *info, struct file *file, struct vm_area_struct *vma)
297 {
298         struct tcx_par *par = (struct tcx_par *)info->par;
299
300         return sbusfb_mmap_helper(par->mmap_map,
301                                   par->physbase, par->fbsize,
302                                   par->sdev->reg_addrs[0].which_io,
303                                   vma);
304 }
305
306 static int tcx_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
307                      unsigned long arg, struct fb_info *info)
308 {
309         struct tcx_par *par = (struct tcx_par *) info->par;
310
311         return sbusfb_ioctl_helper(cmd, arg, info,
312                                    FBTYPE_TCXCOLOR,
313                                    (par->lowdepth ? 8 : 24),
314                                    par->fbsize);
315 }
316
317 /*
318  *  Initialisation
319  */
320
321 static void
322 tcx_init_fix(struct fb_info *info, int linebytes)
323 {
324         struct tcx_par *par = (struct tcx_par *)info->par;
325         const char *tcx_name;
326
327         if (par->lowdepth)
328                 tcx_name = "TCX8";
329         else
330                 tcx_name = "TCX24";
331
332         strlcpy(info->fix.id, tcx_name, sizeof(info->fix.id));
333
334         info->fix.type = FB_TYPE_PACKED_PIXELS;
335         info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
336
337         info->fix.line_length = linebytes;
338
339         info->fix.accel = FB_ACCEL_SUN_TCX;
340 }
341
342 struct all_info {
343         struct fb_info info;
344         struct tcx_par par;
345         struct list_head list;
346 };
347 static LIST_HEAD(tcx_list);
348
349 static void tcx_init_one(struct sbus_dev *sdev)
350 {
351         struct all_info *all;
352         int linebytes, i;
353
354         all = kmalloc(sizeof(*all), GFP_KERNEL);
355         if (!all) {
356                 printk(KERN_ERR "tcx: Cannot allocate memory.\n");
357                 return;
358         }
359         memset(all, 0, sizeof(*all));
360
361         INIT_LIST_HEAD(&all->list);
362
363         spin_lock_init(&all->par.lock);
364         all->par.sdev = sdev;
365
366         all->par.lowdepth = prom_getbool(sdev->prom_node, "tcx-8-bit");
367
368         sbusfb_fill_var(&all->info.var, sdev->prom_node, 8);
369
370         linebytes = prom_getintdefault(sdev->prom_node, "linebytes",
371                                        all->info.var.xres);
372         all->par.fbsize = PAGE_ALIGN(linebytes * all->info.var.yres);
373
374         all->par.tec = sbus_ioremap(&sdev->resource[7], 0,
375                              sizeof(struct tcx_tec), "tcx tec");
376         all->par.thc = sbus_ioremap(&sdev->resource[9], 0,
377                              sizeof(struct tcx_thc), "tcx thc");
378         all->par.bt = sbus_ioremap(&sdev->resource[8], 0,
379                              sizeof(struct bt_regs), "tcx dac");
380         memcpy(&all->par.mmap_map, &__tcx_mmap_map, sizeof(all->par.mmap_map));
381         if (!all->par.lowdepth) {
382                 all->par.cplane = sbus_ioremap(&sdev->resource[4], 0,
383                                      all->par.fbsize * sizeof(u32), "tcx cplane");
384         } else {
385                 all->par.mmap_map[1].size = SBUS_MMAP_EMPTY;
386                 all->par.mmap_map[4].size = SBUS_MMAP_EMPTY;
387                 all->par.mmap_map[5].size = SBUS_MMAP_EMPTY;
388                 all->par.mmap_map[6].size = SBUS_MMAP_EMPTY;
389         }
390
391         all->par.physbase = 0;
392         for (i = 0; i < TCX_MMAP_ENTRIES; i++) {
393                 int j;
394
395                 switch (i) {
396                 case 10:
397                         j = 12;
398                         break;
399
400                 case 11: case 12:
401                         j = i - 1;
402                         break;
403
404                 default:
405                         j = i;
406                         break;
407                 };
408                 all->par.mmap_map[i].poff = sdev->reg_addrs[j].phys_addr;
409         }
410
411         all->info.flags = FBINFO_DEFAULT;
412         all->info.fbops = &tcx_ops;
413 #ifdef CONFIG_SPARC32
414         all->info.screen_base = (char __iomem *)
415                 prom_getintdefault(sdev->prom_node, "address", 0);
416 #endif
417         if (!all->info.screen_base)
418                 all->info.screen_base = sbus_ioremap(&sdev->resource[0], 0,
419                                      all->par.fbsize, "tcx ram");
420         all->info.par = &all->par;
421
422         /* Initialize brooktree DAC. */
423         sbus_writel(0x04 << 24, &all->par.bt->addr);         /* color planes */
424         sbus_writel(0xff << 24, &all->par.bt->control);
425         sbus_writel(0x05 << 24, &all->par.bt->addr);
426         sbus_writel(0x00 << 24, &all->par.bt->control);
427         sbus_writel(0x06 << 24, &all->par.bt->addr);         /* overlay plane */
428         sbus_writel(0x73 << 24, &all->par.bt->control);
429         sbus_writel(0x07 << 24, &all->par.bt->addr);
430         sbus_writel(0x00 << 24, &all->par.bt->control);
431
432         tcx_reset(&all->info);
433
434         tcx_blank(0, &all->info);
435
436         if (fb_alloc_cmap(&all->info.cmap, 256, 0)) {
437                 printk(KERN_ERR "tcx: Could not allocate color map.\n");
438                 kfree(all);
439                 return;
440         }
441
442         tcx_init_fix(&all->info, linebytes);
443
444         if (register_framebuffer(&all->info) < 0) {
445                 printk(KERN_ERR "tcx: Could not register framebuffer.\n");
446                 fb_dealloc_cmap(&all->info.cmap);
447                 kfree(all);
448                 return;
449         }
450
451         list_add(&all->list, &tcx_list);
452
453         printk("tcx: %s at %lx:%lx, %s\n",
454                sdev->prom_name,
455                (long) sdev->reg_addrs[0].which_io,
456                (long) sdev->reg_addrs[0].phys_addr,
457                all->par.lowdepth ? "8-bit only" : "24-bit depth");
458 }
459
460 int __init tcx_init(void)
461 {
462         struct sbus_bus *sbus;
463         struct sbus_dev *sdev;
464
465         if (fb_get_options("tcxfb", NULL))
466                 return -ENODEV;
467
468         for_all_sbusdev(sdev, sbus) {
469                 if (!strcmp(sdev->prom_name, "tcx"))
470                         tcx_init_one(sdev);
471         }
472
473         return 0;
474 }
475
476 void __exit tcx_exit(void)
477 {
478         struct list_head *pos, *tmp;
479
480         list_for_each_safe(pos, tmp, &tcx_list) {
481                 struct all_info *all = list_entry(pos, typeof(*all), list);
482
483                 unregister_framebuffer(&all->info);
484                 fb_dealloc_cmap(&all->info.cmap);
485                 kfree(all);
486         }
487 }
488
489 int __init
490 tcx_setup(char *arg)
491 {
492         /* No cmdline options yet... */
493         return 0;
494 }
495
496 module_init(tcx_init);
497
498 #ifdef MODULE
499 module_exit(tcx_exit);
500 #endif
501
502 MODULE_DESCRIPTION("framebuffer driver for TCX chipsets");
503 MODULE_AUTHOR("David S. Miller <davem@redhat.com>");
504 MODULE_LICENSE("GPL");