sparc: Fix SA_ONSTACK signal handling.
[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
6c8f5b90 318static int __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 {
6c8f5b90
DM
335 printk(KERN_ERR "cgthree: can't handle SR %02x\n",
336 status);
337 return -EINVAL;
1da177e4
LT
338 }
339 }
340
341 for (p = cg3_regvals[type]; *p; p += 2) {
342 u8 __iomem *regp = &((u8 __iomem *)par->regs)[p[0]];
343 sbus_writeb(p[1], regp);
344 }
345 for (p = cg3_dacvals; *p; p += 2) {
50312ce9 346 u8 __iomem *regp;
1da177e4 347
50312ce9 348 regp = (u8 __iomem *)&par->regs->cmap.addr;
1da177e4 349 sbus_writeb(p[0], regp);
50312ce9 350 regp = (u8 __iomem *)&par->regs->cmap.control;
1da177e4
LT
351 sbus_writeb(p[1], regp);
352 }
6c8f5b90 353 return 0;
1da177e4
LT
354}
355
c7f439b9
DM
356static int __devinit cg3_probe(struct of_device *op,
357 const struct of_device_id *match)
1da177e4 358{
50312ce9 359 struct device_node *dp = op->node;
c7f439b9
DM
360 struct fb_info *info;
361 struct cg3_par *par;
50312ce9 362 int linebytes, err;
1da177e4 363
c7f439b9 364 info = framebuffer_alloc(sizeof(struct cg3_par), &op->dev);
1da177e4 365
c7f439b9
DM
366 err = -ENOMEM;
367 if (!info)
368 goto out_err;
369 par = info->par;
1da177e4 370
c7f439b9 371 spin_lock_init(&par->lock);
1da177e4 372
c7f439b9
DM
373 par->physbase = op->resource[0].start;
374 par->which_io = op->resource[0].flags & IORESOURCE_BITS;
375
376 sbusfb_fill_var(&info->var, dp->node, 8);
377 info->var.red.length = 8;
378 info->var.green.length = 8;
379 info->var.blue.length = 8;
50312ce9 380 if (!strcmp(dp->name, "cgRDI"))
c7f439b9
DM
381 par->flags |= CG3_FLAG_RDI;
382 if (par->flags & CG3_FLAG_RDI)
383 cg3_rdi_maybe_fixup_var(&info->var, dp);
1da177e4 384
50312ce9 385 linebytes = of_getintprop_default(dp, "linebytes",
c7f439b9
DM
386 info->var.xres);
387 par->fbsize = PAGE_ALIGN(linebytes * info->var.yres);
1da177e4 388
c7f439b9
DM
389 par->regs = of_ioremap(&op->resource[0], CG3_REGS_OFFSET,
390 sizeof(struct cg3_regs), "cg3 regs");
391 if (!par->regs)
392 goto out_release_fb;
1da177e4 393
c7f439b9
DM
394 info->flags = FBINFO_DEFAULT;
395 info->fbops = &cg3_ops;
396 info->screen_base = of_ioremap(&op->resource[0], CG3_RAM_OFFSET,
397 par->fbsize, "cg3 ram");
398 if (!info->screen_base)
399 goto out_unmap_regs;
1da177e4 400
59f7137a 401 cg3_blank(FB_BLANK_UNBLANK, info);
1da177e4 402
6c8f5b90
DM
403 if (!of_find_property(dp, "width", NULL)) {
404 err = cg3_do_default_mode(par);
405 if (err)
406 goto out_unmap_screen;
407 }
c7f439b9
DM
408
409 if (fb_alloc_cmap(&info->cmap, 256, 0))
410 goto out_unmap_screen;
411
412 fb_set_cmap(&info->cmap, info);
1da177e4 413
c7f439b9
DM
414 cg3_init_fix(info, linebytes, dp);
415
416 err = register_framebuffer(info);
417 if (err < 0)
418 goto out_dealloc_cmap;
419
420 dev_set_drvdata(&op->dev, info);
50312ce9 421
194f1a68 422 printk(KERN_INFO "%s: cg3 at %lx:%lx\n",
c7f439b9 423 dp->full_name, par->which_io, par->physbase);
1da177e4 424
50312ce9 425 return 0;
1da177e4 426
c7f439b9
DM
427out_dealloc_cmap:
428 fb_dealloc_cmap(&info->cmap);
429
430out_unmap_screen:
431 of_iounmap(&op->resource[0], info->screen_base, par->fbsize);
432
433out_unmap_regs:
434 of_iounmap(&op->resource[0], par->regs, sizeof(struct cg3_regs));
435
436out_release_fb:
437 framebuffer_release(info);
1da177e4 438
c7f439b9
DM
439out_err:
440 return err;
50312ce9 441}
1da177e4 442
e3a411a3 443static int __devexit cg3_remove(struct of_device *op)
50312ce9 444{
c7f439b9
DM
445 struct fb_info *info = dev_get_drvdata(&op->dev);
446 struct cg3_par *par = info->par;
50312ce9 447
c7f439b9
DM
448 unregister_framebuffer(info);
449 fb_dealloc_cmap(&info->cmap);
50312ce9 450
c7f439b9
DM
451 of_iounmap(&op->resource[0], par->regs, sizeof(struct cg3_regs));
452 of_iounmap(&op->resource[0], info->screen_base, par->fbsize);
50312ce9 453
c7f439b9 454 framebuffer_release(info);
50312ce9 455
e3a411a3 456 dev_set_drvdata(&op->dev, NULL);
1da177e4
LT
457
458 return 0;
459}
460
50312ce9
DM
461static struct of_device_id cg3_match[] = {
462 {
463 .name = "cgthree",
464 },
465 {
466 .name = "cgRDI",
467 },
468 {},
469};
470MODULE_DEVICE_TABLE(of, cg3_match);
1da177e4 471
50312ce9
DM
472static struct of_platform_driver cg3_driver = {
473 .name = "cg3",
474 .match_table = cg3_match,
475 .probe = cg3_probe,
476 .remove = __devexit_p(cg3_remove),
477};
1da177e4 478
50312ce9
DM
479static int __init cg3_init(void)
480{
481 if (fb_get_options("cg3fb", NULL))
482 return -ENODEV;
483
484 return of_register_driver(&cg3_driver, &of_bus_type);
1da177e4
LT
485}
486
50312ce9 487static void __exit cg3_exit(void)
1da177e4 488{
50312ce9 489 of_unregister_driver(&cg3_driver);
1da177e4
LT
490}
491
492module_init(cg3_init);
1da177e4 493module_exit(cg3_exit);
1da177e4
LT
494
495MODULE_DESCRIPTION("framebuffer driver for CGthree chipsets");
50312ce9
DM
496MODULE_AUTHOR("David S. Miller <davem@davemloft.net>");
497MODULE_VERSION("2.0");
1da177e4 498MODULE_LICENSE("GPL");
This page took 0.373183 seconds and 5 git commands to generate.