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