[CG6]: code cleanup
[linux-2.6] / drivers / video / cg6.c
1 /* cg6.c: CGSIX (GX, GXplus, TGX) frame buffer driver
2  *
3  * Copyright (C) 2003, 2006 David S. Miller (davem@davemloft.net)
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/of_device.h>
23 #include <asm/fbio.h>
24
25 #include "sbuslib.h"
26
27 /*
28  * Local functions.
29  */
30
31 static int cg6_setcolreg(unsigned, unsigned, unsigned, unsigned,
32                          unsigned, struct fb_info *);
33 static int cg6_blank(int, struct fb_info *);
34
35 static void cg6_imageblit(struct fb_info *, const struct fb_image *);
36 static void cg6_fillrect(struct fb_info *, const struct fb_fillrect *);
37 static int cg6_sync(struct fb_info *);
38 static int cg6_mmap(struct fb_info *, struct vm_area_struct *);
39 static int cg6_ioctl(struct fb_info *, unsigned int, unsigned long);
40
41 /*
42  *  Frame buffer operations
43  */
44
45 static struct fb_ops cg6_ops = {
46         .owner                  = THIS_MODULE,
47         .fb_setcolreg           = cg6_setcolreg,
48         .fb_blank               = cg6_blank,
49         .fb_fillrect            = cg6_fillrect,
50         .fb_copyarea            = cfb_copyarea,
51         .fb_imageblit           = cg6_imageblit,
52         .fb_sync                = cg6_sync,
53         .fb_mmap                = cg6_mmap,
54         .fb_ioctl               = cg6_ioctl,
55 #ifdef CONFIG_COMPAT
56         .fb_compat_ioctl        = sbusfb_compat_ioctl,
57 #endif
58 };
59
60 /* Offset of interesting structures in the OBIO space */
61 /*
62  * Brooktree is the video dac and is funny to program on the cg6.
63  * (it's even funnier on the cg3)
64  * The FBC could be the frame buffer control
65  * The FHC could is the frame buffer hardware control.
66  */
67 #define CG6_ROM_OFFSET                  0x0UL
68 #define CG6_BROOKTREE_OFFSET            0x200000UL
69 #define CG6_DHC_OFFSET                  0x240000UL
70 #define CG6_ALT_OFFSET                  0x280000UL
71 #define CG6_FHC_OFFSET                  0x300000UL
72 #define CG6_THC_OFFSET                  0x301000UL
73 #define CG6_FBC_OFFSET                  0x700000UL
74 #define CG6_TEC_OFFSET                  0x701000UL
75 #define CG6_RAM_OFFSET                  0x800000UL
76
77 /* FHC definitions */
78 #define CG6_FHC_FBID_SHIFT              24
79 #define CG6_FHC_FBID_MASK               255
80 #define CG6_FHC_REV_SHIFT               20
81 #define CG6_FHC_REV_MASK                15
82 #define CG6_FHC_FROP_DISABLE            (1 << 19)
83 #define CG6_FHC_ROW_DISABLE             (1 << 18)
84 #define CG6_FHC_SRC_DISABLE             (1 << 17)
85 #define CG6_FHC_DST_DISABLE             (1 << 16)
86 #define CG6_FHC_RESET                   (1 << 15)
87 #define CG6_FHC_LITTLE_ENDIAN           (1 << 13)
88 #define CG6_FHC_RES_MASK                (3 << 11)
89 #define CG6_FHC_1024                    (0 << 11)
90 #define CG6_FHC_1152                    (1 << 11)
91 #define CG6_FHC_1280                    (2 << 11)
92 #define CG6_FHC_1600                    (3 << 11)
93 #define CG6_FHC_CPU_MASK                (3 << 9)
94 #define CG6_FHC_CPU_SPARC               (0 << 9)
95 #define CG6_FHC_CPU_68020               (1 << 9)
96 #define CG6_FHC_CPU_386                 (2 << 9)
97 #define CG6_FHC_TEST                    (1 << 8)
98 #define CG6_FHC_TEST_X_SHIFT            4
99 #define CG6_FHC_TEST_X_MASK             15
100 #define CG6_FHC_TEST_Y_SHIFT            0
101 #define CG6_FHC_TEST_Y_MASK             15
102
103 /* FBC mode definitions */
104 #define CG6_FBC_BLIT_IGNORE             0x00000000
105 #define CG6_FBC_BLIT_NOSRC              0x00100000
106 #define CG6_FBC_BLIT_SRC                0x00200000
107 #define CG6_FBC_BLIT_ILLEGAL            0x00300000
108 #define CG6_FBC_BLIT_MASK               0x00300000
109
110 #define CG6_FBC_VBLANK                  0x00080000
111
112 #define CG6_FBC_MODE_IGNORE             0x00000000
113 #define CG6_FBC_MODE_COLOR8             0x00020000
114 #define CG6_FBC_MODE_COLOR1             0x00040000
115 #define CG6_FBC_MODE_HRMONO             0x00060000
116 #define CG6_FBC_MODE_MASK               0x00060000
117
118 #define CG6_FBC_DRAW_IGNORE             0x00000000
119 #define CG6_FBC_DRAW_RENDER             0x00008000
120 #define CG6_FBC_DRAW_PICK               0x00010000
121 #define CG6_FBC_DRAW_ILLEGAL            0x00018000
122 #define CG6_FBC_DRAW_MASK               0x00018000
123
124 #define CG6_FBC_BWRITE0_IGNORE          0x00000000
125 #define CG6_FBC_BWRITE0_ENABLE          0x00002000
126 #define CG6_FBC_BWRITE0_DISABLE         0x00004000
127 #define CG6_FBC_BWRITE0_ILLEGAL         0x00006000
128 #define CG6_FBC_BWRITE0_MASK            0x00006000
129
130 #define CG6_FBC_BWRITE1_IGNORE          0x00000000
131 #define CG6_FBC_BWRITE1_ENABLE          0x00000800
132 #define CG6_FBC_BWRITE1_DISABLE         0x00001000
133 #define CG6_FBC_BWRITE1_ILLEGAL         0x00001800
134 #define CG6_FBC_BWRITE1_MASK            0x00001800
135
136 #define CG6_FBC_BREAD_IGNORE            0x00000000
137 #define CG6_FBC_BREAD_0                 0x00000200
138 #define CG6_FBC_BREAD_1                 0x00000400
139 #define CG6_FBC_BREAD_ILLEGAL           0x00000600
140 #define CG6_FBC_BREAD_MASK              0x00000600
141
142 #define CG6_FBC_BDISP_IGNORE            0x00000000
143 #define CG6_FBC_BDISP_0                 0x00000080
144 #define CG6_FBC_BDISP_1                 0x00000100
145 #define CG6_FBC_BDISP_ILLEGAL           0x00000180
146 #define CG6_FBC_BDISP_MASK              0x00000180
147
148 #define CG6_FBC_INDEX_MOD               0x00000040
149 #define CG6_FBC_INDEX_MASK              0x00000030
150
151 /* THC definitions */
152 #define CG6_THC_MISC_REV_SHIFT          16
153 #define CG6_THC_MISC_REV_MASK           15
154 #define CG6_THC_MISC_RESET              (1 << 12)
155 #define CG6_THC_MISC_VIDEO              (1 << 10)
156 #define CG6_THC_MISC_SYNC               (1 << 9)
157 #define CG6_THC_MISC_VSYNC              (1 << 8)
158 #define CG6_THC_MISC_SYNC_ENAB          (1 << 7)
159 #define CG6_THC_MISC_CURS_RES           (1 << 6)
160 #define CG6_THC_MISC_INT_ENAB           (1 << 5)
161 #define CG6_THC_MISC_INT                (1 << 4)
162 #define CG6_THC_MISC_INIT               0x9f
163
164 /* The contents are unknown */
165 struct cg6_tec {
166         int tec_matrix;
167         int tec_clip;
168         int tec_vdc;
169 };
170
171 struct cg6_thc {
172         u32     thc_pad0[512];
173         u32     thc_hs;         /* hsync timing */
174         u32     thc_hsdvs;
175         u32     thc_hd;
176         u32     thc_vs;         /* vsync timing */
177         u32     thc_vd;
178         u32     thc_refresh;
179         u32     thc_misc;
180         u32     thc_pad1[56];
181         u32     thc_cursxy;     /* cursor x,y position (16 bits each) */
182         u32     thc_cursmask[32];       /* cursor mask bits */
183         u32     thc_cursbits[32];       /* what to show where mask enabled */
184 };
185
186 struct cg6_fbc {
187         u32     xxx0[1];
188         u32     mode;
189         u32     clip;
190         u32     xxx1[1];
191         u32     s;
192         u32     draw;
193         u32     blit;
194         u32     font;
195         u32     xxx2[24];
196         u32     x0, y0, z0, color0;
197         u32     x1, y1, z1, color1;
198         u32     x2, y2, z2, color2;
199         u32     x3, y3, z3, color3;
200         u32     offx, offy;
201         u32     xxx3[2];
202         u32     incx, incy;
203         u32     xxx4[2];
204         u32     clipminx, clipminy;
205         u32     xxx5[2];
206         u32     clipmaxx, clipmaxy;
207         u32     xxx6[2];
208         u32     fg;
209         u32     bg;
210         u32     alu;
211         u32     pm;
212         u32     pixelm;
213         u32     xxx7[2];
214         u32     patalign;
215         u32     pattern[8];
216         u32     xxx8[432];
217         u32     apointx, apointy, apointz;
218         u32     xxx9[1];
219         u32     rpointx, rpointy, rpointz;
220         u32     xxx10[5];
221         u32     pointr, pointg, pointb, pointa;
222         u32     alinex, aliney, alinez;
223         u32     xxx11[1];
224         u32     rlinex, rliney, rlinez;
225         u32     xxx12[5];
226         u32     liner, lineg, lineb, linea;
227         u32     atrix, atriy, atriz;
228         u32     xxx13[1];
229         u32     rtrix, rtriy, rtriz;
230         u32     xxx14[5];
231         u32     trir, trig, trib, tria;
232         u32     aquadx, aquady, aquadz;
233         u32     xxx15[1];
234         u32     rquadx, rquady, rquadz;
235         u32     xxx16[5];
236         u32     quadr, quadg, quadb, quada;
237         u32     arectx, arecty, arectz;
238         u32     xxx17[1];
239         u32     rrectx, rrecty, rrectz;
240         u32     xxx18[5];
241         u32     rectr, rectg, rectb, recta;
242 };
243
244 struct bt_regs {
245         u32     addr;
246         u32     color_map;
247         u32     control;
248         u32     cursor;
249 };
250
251 struct cg6_par {
252         spinlock_t              lock;
253         struct bt_regs          __iomem *bt;
254         struct cg6_fbc          __iomem *fbc;
255         struct cg6_thc          __iomem *thc;
256         struct cg6_tec          __iomem *tec;
257         u32                     __iomem *fhc;
258
259         u32                     flags;
260 #define CG6_FLAG_BLANKED        0x00000001
261
262         unsigned long           physbase;
263         unsigned long           which_io;
264         unsigned long           fbsize;
265 };
266
267 static int cg6_sync(struct fb_info *info)
268 {
269         struct cg6_par *par = (struct cg6_par *)info->par;
270         struct cg6_fbc __iomem *fbc = par->fbc;
271         int limit = 10000;
272
273         do {
274                 if (!(sbus_readl(&fbc->s) & 0x10000000))
275                         break;
276                 udelay(10);
277         } while (--limit > 0);
278
279         return 0;
280 }
281
282 /**
283  *      cg6_fillrect -  Draws a rectangle on the screen.
284  *
285  *      @info: frame buffer structure that represents a single frame buffer
286  *      @rect: structure defining the rectagle and operation.
287  */
288 static void cg6_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
289 {
290         struct cg6_par *par = (struct cg6_par *)info->par;
291         struct cg6_fbc __iomem *fbc = par->fbc;
292         unsigned long flags;
293         s32 val;
294
295         /* XXX doesn't handle ROP_XOR */
296
297         spin_lock_irqsave(&par->lock, flags);
298         cg6_sync(info);
299         sbus_writel(rect->color, &fbc->fg);
300         sbus_writel(~(u32)0, &fbc->pixelm);
301         sbus_writel(0xea80ff00, &fbc->alu);
302         sbus_writel(0, &fbc->s);
303         sbus_writel(0, &fbc->clip);
304         sbus_writel(~(u32)0, &fbc->pm);
305         sbus_writel(rect->dy, &fbc->arecty);
306         sbus_writel(rect->dx, &fbc->arectx);
307         sbus_writel(rect->dy + rect->height, &fbc->arecty);
308         sbus_writel(rect->dx + rect->width, &fbc->arectx);
309         do {
310                 val = sbus_readl(&fbc->draw);
311         } while (val < 0 && (val & 0x20000000));
312         spin_unlock_irqrestore(&par->lock, flags);
313 }
314
315 /**
316  *      cg6_imageblit - Copies a image from system memory to the screen.
317  *
318  *      @info: frame buffer structure that represents a single frame buffer
319  *      @image: structure defining the image.
320  */
321 static void cg6_imageblit(struct fb_info *info, const struct fb_image *image)
322 {
323         struct cg6_par *par = (struct cg6_par *)info->par;
324         struct cg6_fbc __iomem *fbc = par->fbc;
325         const u8 *data = image->data;
326         unsigned long flags;
327         u32 x, y;
328         int i, width;
329
330         if (image->depth > 1) {
331                 cfb_imageblit(info, image);
332                 return;
333         }
334
335         spin_lock_irqsave(&par->lock, flags);
336
337         cg6_sync(info);
338
339         sbus_writel(image->fg_color, &fbc->fg);
340         sbus_writel(image->bg_color, &fbc->bg);
341         sbus_writel(0x140000, &fbc->mode);
342         sbus_writel(0xe880fc30, &fbc->alu);
343         sbus_writel(~(u32)0, &fbc->pixelm);
344         sbus_writel(0, &fbc->s);
345         sbus_writel(0, &fbc->clip);
346         sbus_writel(0xff, &fbc->pm);
347         sbus_writel(32, &fbc->incx);
348         sbus_writel(0, &fbc->incy);
349
350         x = image->dx;
351         y = image->dy;
352         for (i = 0; i < image->height; i++) {
353                 width = image->width;
354
355                 while (width >= 32) {
356                         u32 val;
357
358                         sbus_writel(y, &fbc->y0);
359                         sbus_writel(x, &fbc->x0);
360                         sbus_writel(x + 32 - 1, &fbc->x1);
361
362                         val = ((u32)data[0] << 24) |
363                               ((u32)data[1] << 16) |
364                               ((u32)data[2] <<  8) |
365                               ((u32)data[3] <<  0);
366                         sbus_writel(val, &fbc->font);
367
368                         data += 4;
369                         x += 32;
370                         width -= 32;
371                 }
372                 if (width) {
373                         u32 val;
374
375                         sbus_writel(y, &fbc->y0);
376                         sbus_writel(x, &fbc->x0);
377                         sbus_writel(x + width - 1, &fbc->x1);
378                         if (width <= 8) {
379                                 val = (u32) data[0] << 24;
380                                 data += 1;
381                         } else if (width <= 16) {
382                                 val = ((u32) data[0] << 24) |
383                                       ((u32) data[1] << 16);
384                                 data += 2;
385                         } else {
386                                 val = ((u32) data[0] << 24) |
387                                       ((u32) data[1] << 16) |
388                                       ((u32) data[2] <<  8);
389                                 data += 3;
390                         }
391                         sbus_writel(val, &fbc->font);
392                 }
393
394                 y += 1;
395                 x = image->dx;
396         }
397
398         spin_unlock_irqrestore(&par->lock, flags);
399 }
400
401 /**
402  *      cg6_setcolreg - Sets a color register.
403  *
404  *      @regno: boolean, 0 copy local, 1 get_user() function
405  *      @red: frame buffer colormap structure
406  *      @green: The green value which can be up to 16 bits wide
407  *      @blue:  The blue value which can be up to 16 bits wide.
408  *      @transp: If supported the alpha value which can be up to 16 bits wide.
409  *      @info: frame buffer info structure
410  */
411 static int cg6_setcolreg(unsigned regno,
412                          unsigned red, unsigned green, unsigned blue,
413                          unsigned transp, struct fb_info *info)
414 {
415         struct cg6_par *par = (struct cg6_par *)info->par;
416         struct bt_regs __iomem *bt = par->bt;
417         unsigned long flags;
418
419         if (regno >= 256)
420                 return 1;
421
422         red >>= 8;
423         green >>= 8;
424         blue >>= 8;
425
426         spin_lock_irqsave(&par->lock, flags);
427
428         sbus_writel((u32)regno << 24, &bt->addr);
429         sbus_writel((u32)red << 24, &bt->color_map);
430         sbus_writel((u32)green << 24, &bt->color_map);
431         sbus_writel((u32)blue << 24, &bt->color_map);
432
433         spin_unlock_irqrestore(&par->lock, flags);
434
435         return 0;
436 }
437
438 /**
439  *      cg6_blank - Blanks the display.
440  *
441  *      @blank_mode: the blank mode we want.
442  *      @info: frame buffer structure that represents a single frame buffer
443  */
444 static int cg6_blank(int blank, struct fb_info *info)
445 {
446         struct cg6_par *par = (struct cg6_par *)info->par;
447         struct cg6_thc __iomem *thc = par->thc;
448         unsigned long flags;
449         u32 val;
450
451         spin_lock_irqsave(&par->lock, flags);
452         val = sbus_readl(&thc->thc_misc);
453
454         switch (blank) {
455         case FB_BLANK_UNBLANK: /* Unblanking */
456                 val |= CG6_THC_MISC_VIDEO;
457                 par->flags &= ~CG6_FLAG_BLANKED;
458                 break;
459
460         case FB_BLANK_NORMAL: /* Normal blanking */
461         case FB_BLANK_VSYNC_SUSPEND: /* VESA blank (vsync off) */
462         case FB_BLANK_HSYNC_SUSPEND: /* VESA blank (hsync off) */
463         case FB_BLANK_POWERDOWN: /* Poweroff */
464                 val &= ~CG6_THC_MISC_VIDEO;
465                 par->flags |= CG6_FLAG_BLANKED;
466                 break;
467         }
468
469         sbus_writel(val, &thc->thc_misc);
470         spin_unlock_irqrestore(&par->lock, flags);
471
472         return 0;
473 }
474
475 static struct sbus_mmap_map cg6_mmap_map[] = {
476         {
477                 .voff   = CG6_FBC,
478                 .poff   = CG6_FBC_OFFSET,
479                 .size   = PAGE_SIZE
480         },
481         {
482                 .voff   = CG6_TEC,
483                 .poff   = CG6_TEC_OFFSET,
484                 .size   = PAGE_SIZE
485         },
486         {
487                 .voff   = CG6_BTREGS,
488                 .poff   = CG6_BROOKTREE_OFFSET,
489                 .size   = PAGE_SIZE
490         },
491         {
492                 .voff   = CG6_FHC,
493                 .poff   = CG6_FHC_OFFSET,
494                 .size   = PAGE_SIZE
495         },
496         {
497                 .voff   = CG6_THC,
498                 .poff   = CG6_THC_OFFSET,
499                 .size   = PAGE_SIZE
500         },
501         {
502                 .voff   = CG6_ROM,
503                 .poff   = CG6_ROM_OFFSET,
504                 .size   = 0x10000
505         },
506         {
507                 .voff   = CG6_RAM,
508                 .poff   = CG6_RAM_OFFSET,
509                 .size   = SBUS_MMAP_FBSIZE(1)
510         },
511         {
512                 .voff   = CG6_DHC,
513                 .poff   = CG6_DHC_OFFSET,
514                 .size   = 0x40000
515         },
516         { .size = 0 }
517 };
518
519 static int cg6_mmap(struct fb_info *info, struct vm_area_struct *vma)
520 {
521         struct cg6_par *par = (struct cg6_par *)info->par;
522
523         return sbusfb_mmap_helper(cg6_mmap_map,
524                                   par->physbase, par->fbsize,
525                                   par->which_io, vma);
526 }
527
528 static int cg6_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
529 {
530         struct cg6_par *par = (struct cg6_par *)info->par;
531
532         return sbusfb_ioctl_helper(cmd, arg, info,
533                                    FBTYPE_SUNFAST_COLOR, 8, par->fbsize);
534 }
535
536 /*
537  *  Initialisation
538  */
539
540 static void __devinit cg6_init_fix(struct fb_info *info, int linebytes)
541 {
542         struct cg6_par *par = (struct cg6_par *)info->par;
543         const char *cg6_cpu_name, *cg6_card_name;
544         u32 conf;
545
546         conf = sbus_readl(par->fhc);
547         switch (conf & CG6_FHC_CPU_MASK) {
548         case CG6_FHC_CPU_SPARC:
549                 cg6_cpu_name = "sparc";
550                 break;
551         case CG6_FHC_CPU_68020:
552                 cg6_cpu_name = "68020";
553                 break;
554         default:
555                 cg6_cpu_name = "i386";
556                 break;
557         };
558         if (((conf >> CG6_FHC_REV_SHIFT) & CG6_FHC_REV_MASK) >= 11) {
559                 if (par->fbsize <= 0x100000)
560                         cg6_card_name = "TGX";
561                 else
562                         cg6_card_name = "TGX+";
563         } else {
564                 if (par->fbsize <= 0x100000)
565                         cg6_card_name = "GX";
566                 else
567                         cg6_card_name = "GX+";
568         }
569
570         sprintf(info->fix.id, "%s %s", cg6_card_name, cg6_cpu_name);
571         info->fix.id[sizeof(info->fix.id) - 1] = 0;
572
573         info->fix.type = FB_TYPE_PACKED_PIXELS;
574         info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
575
576         info->fix.line_length = linebytes;
577
578         info->fix.accel = FB_ACCEL_SUN_CGSIX;
579 }
580
581 /* Initialize Brooktree DAC */
582 static void __devinit cg6_bt_init(struct cg6_par *par)
583 {
584         struct bt_regs __iomem *bt = par->bt;
585
586         sbus_writel(0x04 << 24, &bt->addr);      /* color planes */
587         sbus_writel(0xff << 24, &bt->control);
588         sbus_writel(0x05 << 24, &bt->addr);
589         sbus_writel(0x00 << 24, &bt->control);
590         sbus_writel(0x06 << 24, &bt->addr);      /* overlay plane */
591         sbus_writel(0x73 << 24, &bt->control);
592         sbus_writel(0x07 << 24, &bt->addr);
593         sbus_writel(0x00 << 24, &bt->control);
594 }
595
596 static void __devinit cg6_chip_init(struct fb_info *info)
597 {
598         struct cg6_par *par = (struct cg6_par *)info->par;
599         struct cg6_tec __iomem *tec = par->tec;
600         struct cg6_fbc __iomem *fbc = par->fbc;
601         u32 rev, conf, mode;
602         int i;
603
604         /* Turn off stuff in the Transform Engine. */
605         sbus_writel(0, &tec->tec_matrix);
606         sbus_writel(0, &tec->tec_clip);
607         sbus_writel(0, &tec->tec_vdc);
608
609         /* Take care of bugs in old revisions. */
610         rev = (sbus_readl(par->fhc) >> CG6_FHC_REV_SHIFT) & CG6_FHC_REV_MASK;
611         if (rev < 5) {
612                 conf = (sbus_readl(par->fhc) & CG6_FHC_RES_MASK) |
613                         CG6_FHC_CPU_68020 | CG6_FHC_TEST |
614                         (11 << CG6_FHC_TEST_X_SHIFT) |
615                         (11 << CG6_FHC_TEST_Y_SHIFT);
616                 if (rev < 2)
617                         conf |= CG6_FHC_DST_DISABLE;
618                 sbus_writel(conf, par->fhc);
619         }
620
621         /* Set things in the FBC. Bad things appear to happen if we do
622          * back to back store/loads on the mode register, so copy it
623          * out instead. */
624         mode = sbus_readl(&fbc->mode);
625         do {
626                 i = sbus_readl(&fbc->s);
627         } while (i & 0x10000000);
628         mode &= ~(CG6_FBC_BLIT_MASK | CG6_FBC_MODE_MASK |
629                   CG6_FBC_DRAW_MASK | CG6_FBC_BWRITE0_MASK |
630                   CG6_FBC_BWRITE1_MASK | CG6_FBC_BREAD_MASK |
631                   CG6_FBC_BDISP_MASK);
632         mode |= (CG6_FBC_BLIT_SRC | CG6_FBC_MODE_COLOR8 |
633                  CG6_FBC_DRAW_RENDER | CG6_FBC_BWRITE0_ENABLE |
634                  CG6_FBC_BWRITE1_DISABLE | CG6_FBC_BREAD_0 |
635                  CG6_FBC_BDISP_0);
636         sbus_writel(mode, &fbc->mode);
637
638         sbus_writel(0, &fbc->clip);
639         sbus_writel(0, &fbc->offx);
640         sbus_writel(0, &fbc->offy);
641         sbus_writel(0, &fbc->clipminx);
642         sbus_writel(0, &fbc->clipminy);
643         sbus_writel(info->var.xres - 1, &fbc->clipmaxx);
644         sbus_writel(info->var.yres - 1, &fbc->clipmaxy);
645 }
646
647 static void cg6_unmap_regs(struct of_device *op, struct fb_info *info,
648                            struct cg6_par *par)
649 {
650         if (par->fbc)
651                 of_iounmap(&op->resource[0], par->fbc, 4096);
652         if (par->tec)
653                 of_iounmap(&op->resource[0], par->tec, sizeof(struct cg6_tec));
654         if (par->thc)
655                 of_iounmap(&op->resource[0], par->thc, sizeof(struct cg6_thc));
656         if (par->bt)
657                 of_iounmap(&op->resource[0], par->bt, sizeof(struct bt_regs));
658         if (par->fhc)
659                 of_iounmap(&op->resource[0], par->fhc, sizeof(u32));
660
661         if (info->screen_base)
662                 of_iounmap(&op->resource[0], info->screen_base, par->fbsize);
663 }
664
665 static int __devinit cg6_probe(struct of_device *op,
666                                 const struct of_device_id *match)
667 {
668         struct device_node *dp = op->node;
669         struct fb_info *info;
670         struct cg6_par *par;
671         int linebytes, err;
672         int dblbuf;
673
674         info = framebuffer_alloc(sizeof(struct cg6_par), &op->dev);
675
676         err = -ENOMEM;
677         if (!info)
678                 goto out_err;
679         par = info->par;
680
681         spin_lock_init(&par->lock);
682
683         par->physbase = op->resource[0].start;
684         par->which_io = op->resource[0].flags & IORESOURCE_BITS;
685
686         sbusfb_fill_var(&info->var, dp->node, 8);
687         info->var.red.length = 8;
688         info->var.green.length = 8;
689         info->var.blue.length = 8;
690
691         linebytes = of_getintprop_default(dp, "linebytes",
692                                           info->var.xres);
693         par->fbsize = PAGE_ALIGN(linebytes * info->var.yres);
694
695         dblbuf = of_getintprop_default(dp, "dblbuf", 0);
696         if (dblbuf)
697                 par->fbsize *= 4;
698
699         par->fbc = of_ioremap(&op->resource[0], CG6_FBC_OFFSET,
700                                 4096, "cgsix fbc");
701         par->tec = of_ioremap(&op->resource[0], CG6_TEC_OFFSET,
702                                 sizeof(struct cg6_tec), "cgsix tec");
703         par->thc = of_ioremap(&op->resource[0], CG6_THC_OFFSET,
704                                 sizeof(struct cg6_thc), "cgsix thc");
705         par->bt = of_ioremap(&op->resource[0], CG6_BROOKTREE_OFFSET,
706                                 sizeof(struct bt_regs), "cgsix dac");
707         par->fhc = of_ioremap(&op->resource[0], CG6_FHC_OFFSET,
708                                 sizeof(u32), "cgsix fhc");
709
710         info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_IMAGEBLIT |
711                         FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT;
712         info->fbops = &cg6_ops;
713
714         info->screen_base = of_ioremap(&op->resource[0], CG6_RAM_OFFSET,
715                                         par->fbsize, "cgsix ram");
716         if (!par->fbc || !par->tec || !par->thc ||
717             !par->bt || !par->fhc || !info->screen_base)
718                 goto out_unmap_regs;
719
720         info->var.accel_flags = FB_ACCELF_TEXT;
721
722         cg6_bt_init(par);
723         cg6_chip_init(info);
724         cg6_blank(0, info);
725
726         if (fb_alloc_cmap(&info->cmap, 256, 0))
727                 goto out_unmap_regs;
728
729         fb_set_cmap(&info->cmap, info);
730         cg6_init_fix(info, linebytes);
731
732         err = register_framebuffer(info);
733         if (err < 0)
734                 goto out_dealloc_cmap;
735
736         dev_set_drvdata(&op->dev, info);
737
738         printk("%s: CGsix [%s] at %lx:%lx\n",
739                dp->full_name, info->fix.id,
740                par->which_io, par->physbase);
741
742         return 0;
743
744 out_dealloc_cmap:
745         fb_dealloc_cmap(&info->cmap);
746
747 out_unmap_regs:
748         cg6_unmap_regs(op, info, par);
749
750 out_err:
751         return err;
752 }
753
754 static int __devexit cg6_remove(struct of_device *op)
755 {
756         struct fb_info *info = dev_get_drvdata(&op->dev);
757         struct cg6_par *par = info->par;
758
759         unregister_framebuffer(info);
760         fb_dealloc_cmap(&info->cmap);
761
762         cg6_unmap_regs(op, info, par);
763
764         framebuffer_release(info);
765
766         dev_set_drvdata(&op->dev, NULL);
767
768         return 0;
769 }
770
771 static struct of_device_id cg6_match[] = {
772         {
773                 .name = "cgsix",
774         },
775         {
776                 .name = "cgthree+",
777         },
778         {},
779 };
780 MODULE_DEVICE_TABLE(of, cg6_match);
781
782 static struct of_platform_driver cg6_driver = {
783         .name           = "cg6",
784         .match_table    = cg6_match,
785         .probe          = cg6_probe,
786         .remove         = __devexit_p(cg6_remove),
787 };
788
789 static int __init cg6_init(void)
790 {
791         if (fb_get_options("cg6fb", NULL))
792                 return -ENODEV;
793
794         return of_register_driver(&cg6_driver, &of_bus_type);
795 }
796
797 static void __exit cg6_exit(void)
798 {
799         of_unregister_driver(&cg6_driver);
800 }
801
802 module_init(cg6_init);
803 module_exit(cg6_exit);
804
805 MODULE_DESCRIPTION("framebuffer driver for CGsix chipsets");
806 MODULE_AUTHOR("David S. Miller <davem@davemloft.net>");
807 MODULE_VERSION("2.0");
808 MODULE_LICENSE("GPL");