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