[SPARC64]: Handle mostek clock type in mini_rtc driver.
[deliverable/linux.git] / drivers / video / cg3.c
CommitLineData
1da177e4
LT
1/* cg3.c: CGTHREE frame buffer driver
2 *
50312ce9 3 * Copyright (C) 2003, 2006 David S. Miller (davem@davemloft.net)
1da177e4
LT
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) 1997 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>
1da177e4 22#include <asm/oplib.h>
50312ce9
DM
23#include <asm/prom.h>
24#include <asm/of_device.h>
1da177e4
LT
25#include <asm/fbio.h>
26
27#include "sbuslib.h"
28
29/*
30 * Local functions.
31 */
32
33static int cg3_setcolreg(unsigned, unsigned, unsigned, unsigned,
34 unsigned, struct fb_info *);
35static int cg3_blank(int, struct fb_info *);
36
216d526c 37static int cg3_mmap(struct fb_info *, struct vm_area_struct *);
67a6680d 38static int cg3_ioctl(struct fb_info *, unsigned int, unsigned long);
1da177e4
LT
39
40/*
41 * Frame buffer operations
42 */
43
44static struct fb_ops cg3_ops = {
45 .owner = THIS_MODULE,
46 .fb_setcolreg = cg3_setcolreg,
47 .fb_blank = cg3_blank,
48 .fb_fillrect = cfb_fillrect,
49 .fb_copyarea = cfb_copyarea,
50 .fb_imageblit = cfb_imageblit,
51 .fb_mmap = cg3_mmap,
52 .fb_ioctl = cg3_ioctl,
9ffb83bc
CH
53#ifdef CONFIG_COMPAT
54 .fb_compat_ioctl = sbusfb_compat_ioctl,
55#endif
1da177e4
LT
56};
57
58
59/* Control Register Constants */
60#define CG3_CR_ENABLE_INTS 0x80
61#define CG3_CR_ENABLE_VIDEO 0x40
62#define CG3_CR_ENABLE_TIMING 0x20
63#define CG3_CR_ENABLE_CURCMP 0x10
64#define CG3_CR_XTAL_MASK 0x0c
65#define CG3_CR_DIVISOR_MASK 0x03
66
67/* Status Register Constants */
68#define CG3_SR_PENDING_INT 0x80
69#define CG3_SR_RES_MASK 0x70
70#define CG3_SR_1152_900_76_A 0x40
71#define CG3_SR_1152_900_76_B 0x60
72#define CG3_SR_ID_MASK 0x0f
73#define CG3_SR_ID_COLOR 0x01
74#define CG3_SR_ID_MONO 0x02
75#define CG3_SR_ID_MONO_ECL 0x03
76
77enum cg3_type {
78 CG3_AT_66HZ = 0,
79 CG3_AT_76HZ,
80 CG3_RDI
81};
82
83struct bt_regs {
50312ce9
DM
84 u32 addr;
85 u32 color_map;
86 u32 control;
87 u32 cursor;
1da177e4
LT
88};
89
90struct cg3_regs {
91 struct bt_regs cmap;
50312ce9
DM
92 u8 control;
93 u8 status;
94 u8 cursor_start;
95 u8 cursor_end;
96 u8 h_blank_start;
97 u8 h_blank_end;
98 u8 h_sync_start;
99 u8 h_sync_end;
100 u8 comp_sync_end;
101 u8 v_blank_start_high;
102 u8 v_blank_start_low;
103 u8 v_blank_end;
104 u8 v_sync_start;
105 u8 v_sync_end;
106 u8 xfer_holdoff_start;
107 u8 xfer_holdoff_end;
1da177e4
LT
108};
109
110/* Offset of interesting structures in the OBIO space */
111#define CG3_REGS_OFFSET 0x400000UL
112#define CG3_RAM_OFFSET 0x800000UL
113
114struct cg3_par {
115 spinlock_t lock;
116 struct cg3_regs __iomem *regs;
117 u32 sw_cmap[((256 * 3) + 3) / 4];
118
119 u32 flags;
120#define CG3_FLAG_BLANKED 0x00000001
121#define CG3_FLAG_RDI 0x00000002
122
123 unsigned long physbase;
50312ce9 124 unsigned long which_io;
1da177e4 125 unsigned long fbsize;
1da177e4
LT
126};
127
128/**
129 * cg3_setcolreg - Optional function. Sets a color register.
130 * @regno: boolean, 0 copy local, 1 get_user() function
131 * @red: frame buffer colormap structure
132 * @green: The green value which can be up to 16 bits wide
133 * @blue: The blue value which can be up to 16 bits wide.
134 * @transp: If supported the alpha value which can be up to 16 bits wide.
135 * @info: frame buffer info structure
136 *
137 * The cg3 palette is loaded with 4 color values at each time
138 * so you end up with: (rgb)(r), (gb)(rg), (b)(rgb), and so on.
139 * We keep a sw copy of the hw cmap to assist us in this esoteric
140 * loading procedure.
141 */
142static int cg3_setcolreg(unsigned regno,
143 unsigned red, unsigned green, unsigned blue,
144 unsigned transp, struct fb_info *info)
145{
146 struct cg3_par *par = (struct cg3_par *) info->par;
147 struct bt_regs __iomem *bt = &par->regs->cmap;
148 unsigned long flags;
149 u32 *p32;
150 u8 *p8;
151 int count;
152
153 if (regno >= 256)
154 return 1;
155
156 red >>= 8;
157 green >>= 8;
158 blue >>= 8;
159
160 spin_lock_irqsave(&par->lock, flags);
161
162 p8 = (u8 *)par->sw_cmap + (regno * 3);
163 p8[0] = red;
164 p8[1] = green;
165 p8[2] = blue;
166
167#define D4M3(x) ((((x)>>2)<<1) + ((x)>>2)) /* (x/4)*3 */
168#define D4M4(x) ((x)&~0x3) /* (x/4)*4 */
169
170 count = 3;
171 p32 = &par->sw_cmap[D4M3(regno)];
172 sbus_writel(D4M4(regno), &bt->addr);
173 while (count--)
174 sbus_writel(*p32++, &bt->color_map);
175
176#undef D4M3
177#undef D4M4
178
179 spin_unlock_irqrestore(&par->lock, flags);
180
181 return 0;
182}
183
184/**
185 * cg3_blank - Optional function. Blanks the display.
186 * @blank_mode: the blank mode we want.
187 * @info: frame buffer structure that represents a single frame buffer
188 */
a7177514 189static int cg3_blank(int blank, struct fb_info *info)
1da177e4
LT
190{
191 struct cg3_par *par = (struct cg3_par *) info->par;
192 struct cg3_regs __iomem *regs = par->regs;
193 unsigned long flags;
194 u8 val;
195
196 spin_lock_irqsave(&par->lock, flags);
197
198 switch (blank) {
199 case FB_BLANK_UNBLANK: /* Unblanking */
200 val = sbus_readb(&regs->control);
201 val |= CG3_CR_ENABLE_VIDEO;
202 sbus_writeb(val, &regs->control);
203 par->flags &= ~CG3_FLAG_BLANKED;
204 break;
205
206 case FB_BLANK_NORMAL: /* Normal blanking */
207 case FB_BLANK_VSYNC_SUSPEND: /* VESA blank (vsync off) */
208 case FB_BLANK_HSYNC_SUSPEND: /* VESA blank (hsync off) */
209 case FB_BLANK_POWERDOWN: /* Poweroff */
210 val = sbus_readb(&regs->control);
211 val &= ~CG3_CR_ENABLE_VIDEO;
212 sbus_writeb(val, &regs->control);
213 par->flags |= CG3_FLAG_BLANKED;
214 break;
215 }
216
217 spin_unlock_irqrestore(&par->lock, flags);
218
219 return 0;
220}
221
222static struct sbus_mmap_map cg3_mmap_map[] = {
223 {
224 .voff = CG3_MMAP_OFFSET,
225 .poff = CG3_RAM_OFFSET,
226 .size = SBUS_MMAP_FBSIZE(1)
227 },
228 { .size = 0 }
229};
230
216d526c 231static int cg3_mmap(struct fb_info *info, struct vm_area_struct *vma)
1da177e4
LT
232{
233 struct cg3_par *par = (struct cg3_par *)info->par;
234
235 return sbusfb_mmap_helper(cg3_mmap_map,
236 par->physbase, par->fbsize,
50312ce9 237 par->which_io,
1da177e4
LT
238 vma);
239}
240
67a6680d 241static int cg3_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
1da177e4
LT
242{
243 struct cg3_par *par = (struct cg3_par *) info->par;
244
245 return sbusfb_ioctl_helper(cmd, arg, info,
246 FBTYPE_SUN3COLOR, 8, par->fbsize);
247}
248
249/*
250 * Initialisation
251 */
252
a7177514
RR
253static void __devinit cg3_init_fix(struct fb_info *info, int linebytes,
254 struct device_node *dp)
1da177e4 255{
50312ce9 256 strlcpy(info->fix.id, dp->name, sizeof(info->fix.id));
1da177e4
LT
257
258 info->fix.type = FB_TYPE_PACKED_PIXELS;
259 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
260
261 info->fix.line_length = linebytes;
262
263 info->fix.accel = FB_ACCEL_SUN_CGTHREE;
264}
265
a7177514
RR
266static void __devinit cg3_rdi_maybe_fixup_var(struct fb_var_screeninfo *var,
267 struct device_node *dp)
1da177e4 268{
ccf0dec6 269 const char *params;
1da177e4
LT
270 char *p;
271 int ww, hh;
272
50312ce9
DM
273 params = of_get_property(dp, "params", NULL);
274 if (params) {
275 ww = simple_strtoul(params, &p, 10);
1da177e4
LT
276 if (ww && *p == 'x') {
277 hh = simple_strtoul(p + 1, &p, 10);
278 if (hh && *p == '-') {
279 if (var->xres != ww ||
280 var->yres != hh) {
281 var->xres = var->xres_virtual = ww;
282 var->yres = var->yres_virtual = hh;
283 }
284 }
285 }
286 }
287}
288
a7177514 289static u8 cg3regvals_66hz[] __devinitdata = { /* 1152 x 900, 66 Hz */
1da177e4
LT
290 0x14, 0xbb, 0x15, 0x2b, 0x16, 0x04, 0x17, 0x14,
291 0x18, 0xae, 0x19, 0x03, 0x1a, 0xa8, 0x1b, 0x24,
292 0x1c, 0x01, 0x1d, 0x05, 0x1e, 0xff, 0x1f, 0x01,
293 0x10, 0x20, 0
294};
295
a7177514 296static u8 cg3regvals_76hz[] __devinitdata = { /* 1152 x 900, 76 Hz */
1da177e4
LT
297 0x14, 0xb7, 0x15, 0x27, 0x16, 0x03, 0x17, 0x0f,
298 0x18, 0xae, 0x19, 0x03, 0x1a, 0xae, 0x1b, 0x2a,
299 0x1c, 0x01, 0x1d, 0x09, 0x1e, 0xff, 0x1f, 0x01,
300 0x10, 0x24, 0
301};
302
a7177514 303static u8 cg3regvals_rdi[] __devinitdata = { /* 640 x 480, cgRDI */
1da177e4
LT
304 0x14, 0x70, 0x15, 0x20, 0x16, 0x08, 0x17, 0x10,
305 0x18, 0x06, 0x19, 0x02, 0x1a, 0x31, 0x1b, 0x51,
306 0x1c, 0x06, 0x1d, 0x0c, 0x1e, 0xff, 0x1f, 0x01,
307 0x10, 0x22, 0
308};
309
a7177514 310static u8 *cg3_regvals[] __devinitdata = {
1da177e4
LT
311 cg3regvals_66hz, cg3regvals_76hz, cg3regvals_rdi
312};
313
a7177514 314static u_char cg3_dacvals[] __devinitdata = {
1da177e4
LT
315 4, 0xff, 5, 0x00, 6, 0x70, 7, 0x00, 0
316};
317
a7177514 318static void __devinit cg3_do_default_mode(struct cg3_par *par)
1da177e4
LT
319{
320 enum cg3_type type;
321 u8 *p;
322
323 if (par->flags & CG3_FLAG_RDI)
324 type = CG3_RDI;
325 else {
326 u8 status = sbus_readb(&par->regs->status), mon;
327 if ((status & CG3_SR_ID_MASK) == CG3_SR_ID_COLOR) {
328 mon = status & CG3_SR_RES_MASK;
329 if (mon == CG3_SR_1152_900_76_A ||
330 mon == CG3_SR_1152_900_76_B)
331 type = CG3_AT_76HZ;
332 else
333 type = CG3_AT_66HZ;
334 } else {
335 prom_printf("cgthree: can't handle SR %02x\n",
336 status);
337 prom_halt();
338 return;
339 }
340 }
341
342 for (p = cg3_regvals[type]; *p; p += 2) {
343 u8 __iomem *regp = &((u8 __iomem *)par->regs)[p[0]];
344 sbus_writeb(p[1], regp);
345 }
346 for (p = cg3_dacvals; *p; p += 2) {
50312ce9 347 u8 __iomem *regp;
1da177e4 348
50312ce9 349 regp = (u8 __iomem *)&par->regs->cmap.addr;
1da177e4 350 sbus_writeb(p[0], regp);
50312ce9 351 regp = (u8 __iomem *)&par->regs->cmap.control;
1da177e4
LT
352 sbus_writeb(p[1], regp);
353 }
354}
355
356struct all_info {
357 struct fb_info info;
358 struct cg3_par par;
1da177e4 359};
1da177e4 360
50312ce9 361static int __devinit cg3_init_one(struct of_device *op)
1da177e4 362{
50312ce9 363 struct device_node *dp = op->node;
1da177e4 364 struct all_info *all;
50312ce9 365 int linebytes, err;
1da177e4 366
50312ce9
DM
367 all = kzalloc(sizeof(*all), GFP_KERNEL);
368 if (!all)
369 return -ENOMEM;
1da177e4
LT
370
371 spin_lock_init(&all->par.lock);
1da177e4 372
50312ce9
DM
373 all->par.physbase = op->resource[0].start;
374 all->par.which_io = op->resource[0].flags & IORESOURCE_BITS;
1da177e4 375
50312ce9 376 sbusfb_fill_var(&all->info.var, dp->node, 8);
1da177e4
LT
377 all->info.var.red.length = 8;
378 all->info.var.green.length = 8;
379 all->info.var.blue.length = 8;
50312ce9 380 if (!strcmp(dp->name, "cgRDI"))
1da177e4
LT
381 all->par.flags |= CG3_FLAG_RDI;
382 if (all->par.flags & CG3_FLAG_RDI)
50312ce9 383 cg3_rdi_maybe_fixup_var(&all->info.var, dp);
1da177e4 384
50312ce9
DM
385 linebytes = of_getintprop_default(dp, "linebytes",
386 all->info.var.xres);
1da177e4
LT
387 all->par.fbsize = PAGE_ALIGN(linebytes * all->info.var.yres);
388
50312ce9
DM
389 all->par.regs = of_ioremap(&op->resource[0], CG3_REGS_OFFSET,
390 sizeof(struct cg3_regs), "cg3 regs");
1da177e4
LT
391
392 all->info.flags = FBINFO_DEFAULT;
393 all->info.fbops = &cg3_ops;
50312ce9
DM
394 all->info.screen_base =
395 of_ioremap(&op->resource[0], CG3_RAM_OFFSET,
396 all->par.fbsize, "cg3 ram");
1da177e4
LT
397 all->info.par = &all->par;
398
399 cg3_blank(0, &all->info);
400
50312ce9 401 if (!of_find_property(dp, "width", NULL))
1da177e4
LT
402 cg3_do_default_mode(&all->par);
403
404 if (fb_alloc_cmap(&all->info.cmap, 256, 0)) {
e3a411a3
DM
405 of_iounmap(&op->resource[0],
406 all->par.regs, sizeof(struct cg3_regs));
407 of_iounmap(&op->resource[0],
408 all->info.screen_base, all->par.fbsize);
1da177e4 409 kfree(all);
50312ce9 410 return -ENOMEM;
1da177e4
LT
411 }
412 fb_set_cmap(&all->info.cmap, &all->info);
413
50312ce9 414 cg3_init_fix(&all->info, linebytes, dp);
1da177e4 415
50312ce9
DM
416 err = register_framebuffer(&all->info);
417 if (err < 0) {
1da177e4 418 fb_dealloc_cmap(&all->info.cmap);
e3a411a3
DM
419 of_iounmap(&op->resource[0],
420 all->par.regs, sizeof(struct cg3_regs));
421 of_iounmap(&op->resource[0],
422 all->info.screen_base, all->par.fbsize);
1da177e4 423 kfree(all);
50312ce9 424 return err;
1da177e4
LT
425 }
426
50312ce9
DM
427 dev_set_drvdata(&op->dev, all);
428
429 printk("%s: cg3 at %lx:%lx\n",
430 dp->full_name, all->par.which_io, all->par.physbase);
1da177e4 431
50312ce9 432 return 0;
1da177e4
LT
433}
434
a7177514
RR
435static int __devinit cg3_probe(struct of_device *dev,
436 const struct of_device_id *match)
1da177e4 437{
50312ce9 438 struct of_device *op = to_of_device(&dev->dev);
1da177e4 439
50312ce9
DM
440 return cg3_init_one(op);
441}
1da177e4 442
e3a411a3 443static int __devexit cg3_remove(struct of_device *op)
50312ce9 444{
e3a411a3 445 struct all_info *all = dev_get_drvdata(&op->dev);
50312ce9
DM
446
447 unregister_framebuffer(&all->info);
448 fb_dealloc_cmap(&all->info.cmap);
449
e3a411a3
DM
450 of_iounmap(&op->resource[0], all->par.regs, sizeof(struct cg3_regs));
451 of_iounmap(&op->resource[0], all->info.screen_base, all->par.fbsize);
50312ce9
DM
452
453 kfree(all);
454
e3a411a3 455 dev_set_drvdata(&op->dev, NULL);
1da177e4
LT
456
457 return 0;
458}
459
50312ce9
DM
460static struct of_device_id cg3_match[] = {
461 {
462 .name = "cgthree",
463 },
464 {
465 .name = "cgRDI",
466 },
467 {},
468};
469MODULE_DEVICE_TABLE(of, cg3_match);
1da177e4 470
50312ce9
DM
471static struct of_platform_driver cg3_driver = {
472 .name = "cg3",
473 .match_table = cg3_match,
474 .probe = cg3_probe,
475 .remove = __devexit_p(cg3_remove),
476};
1da177e4 477
50312ce9
DM
478static int __init cg3_init(void)
479{
480 if (fb_get_options("cg3fb", NULL))
481 return -ENODEV;
482
483 return of_register_driver(&cg3_driver, &of_bus_type);
1da177e4
LT
484}
485
50312ce9 486static void __exit cg3_exit(void)
1da177e4 487{
50312ce9 488 of_unregister_driver(&cg3_driver);
1da177e4
LT
489}
490
491module_init(cg3_init);
1da177e4 492module_exit(cg3_exit);
1da177e4
LT
493
494MODULE_DESCRIPTION("framebuffer driver for CGthree chipsets");
50312ce9
DM
495MODULE_AUTHOR("David S. Miller <davem@davemloft.net>");
496MODULE_VERSION("2.0");
1da177e4 497MODULE_LICENSE("GPL");
This page took 0.360551 seconds and 5 git commands to generate.