diff options
Diffstat (limited to 'drivers/video/fbdev/sunxvr2500.c')
-rw-r--r-- | drivers/video/fbdev/sunxvr2500.c | 276 |
1 files changed, 276 insertions, 0 deletions
diff --git a/drivers/video/fbdev/sunxvr2500.c b/drivers/video/fbdev/sunxvr2500.c new file mode 100644 index 000000000000..843b6bab0483 --- /dev/null +++ b/drivers/video/fbdev/sunxvr2500.c | |||
@@ -0,0 +1,276 @@ | |||
1 | /* s3d.c: Sun 3DLABS XVR-2500 et al. driver for sparc64 systems | ||
2 | * | ||
3 | * Copyright (C) 2007 David S. Miller (davem@davemloft.net) | ||
4 | */ | ||
5 | |||
6 | #include <linux/module.h> | ||
7 | #include <linux/kernel.h> | ||
8 | #include <linux/fb.h> | ||
9 | #include <linux/pci.h> | ||
10 | #include <linux/init.h> | ||
11 | #include <linux/of_device.h> | ||
12 | |||
13 | #include <asm/io.h> | ||
14 | |||
15 | struct s3d_info { | ||
16 | struct fb_info *info; | ||
17 | struct pci_dev *pdev; | ||
18 | |||
19 | char __iomem *fb_base; | ||
20 | unsigned long fb_base_phys; | ||
21 | |||
22 | struct device_node *of_node; | ||
23 | |||
24 | unsigned int width; | ||
25 | unsigned int height; | ||
26 | unsigned int depth; | ||
27 | unsigned int fb_size; | ||
28 | |||
29 | u32 pseudo_palette[16]; | ||
30 | }; | ||
31 | |||
32 | static int s3d_get_props(struct s3d_info *sp) | ||
33 | { | ||
34 | sp->width = of_getintprop_default(sp->of_node, "width", 0); | ||
35 | sp->height = of_getintprop_default(sp->of_node, "height", 0); | ||
36 | sp->depth = of_getintprop_default(sp->of_node, "depth", 8); | ||
37 | |||
38 | if (!sp->width || !sp->height) { | ||
39 | printk(KERN_ERR "s3d: Critical properties missing for %s\n", | ||
40 | pci_name(sp->pdev)); | ||
41 | return -EINVAL; | ||
42 | } | ||
43 | |||
44 | return 0; | ||
45 | } | ||
46 | |||
47 | static int s3d_setcolreg(unsigned regno, | ||
48 | unsigned red, unsigned green, unsigned blue, | ||
49 | unsigned transp, struct fb_info *info) | ||
50 | { | ||
51 | u32 value; | ||
52 | |||
53 | if (regno < 16) { | ||
54 | red >>= 8; | ||
55 | green >>= 8; | ||
56 | blue >>= 8; | ||
57 | |||
58 | value = (blue << 24) | (green << 16) | (red << 8); | ||
59 | ((u32 *)info->pseudo_palette)[regno] = value; | ||
60 | } | ||
61 | |||
62 | return 0; | ||
63 | } | ||
64 | |||
65 | static struct fb_ops s3d_ops = { | ||
66 | .owner = THIS_MODULE, | ||
67 | .fb_setcolreg = s3d_setcolreg, | ||
68 | .fb_fillrect = cfb_fillrect, | ||
69 | .fb_copyarea = cfb_copyarea, | ||
70 | .fb_imageblit = cfb_imageblit, | ||
71 | }; | ||
72 | |||
73 | static int s3d_set_fbinfo(struct s3d_info *sp) | ||
74 | { | ||
75 | struct fb_info *info = sp->info; | ||
76 | struct fb_var_screeninfo *var = &info->var; | ||
77 | |||
78 | info->flags = FBINFO_DEFAULT; | ||
79 | info->fbops = &s3d_ops; | ||
80 | info->screen_base = sp->fb_base; | ||
81 | info->screen_size = sp->fb_size; | ||
82 | |||
83 | info->pseudo_palette = sp->pseudo_palette; | ||
84 | |||
85 | /* Fill fix common fields */ | ||
86 | strlcpy(info->fix.id, "s3d", sizeof(info->fix.id)); | ||
87 | info->fix.smem_start = sp->fb_base_phys; | ||
88 | info->fix.smem_len = sp->fb_size; | ||
89 | info->fix.type = FB_TYPE_PACKED_PIXELS; | ||
90 | if (sp->depth == 32 || sp->depth == 24) | ||
91 | info->fix.visual = FB_VISUAL_TRUECOLOR; | ||
92 | else | ||
93 | info->fix.visual = FB_VISUAL_PSEUDOCOLOR; | ||
94 | |||
95 | var->xres = sp->width; | ||
96 | var->yres = sp->height; | ||
97 | var->xres_virtual = var->xres; | ||
98 | var->yres_virtual = var->yres; | ||
99 | var->bits_per_pixel = sp->depth; | ||
100 | |||
101 | var->red.offset = 8; | ||
102 | var->red.length = 8; | ||
103 | var->green.offset = 16; | ||
104 | var->green.length = 8; | ||
105 | var->blue.offset = 24; | ||
106 | var->blue.length = 8; | ||
107 | var->transp.offset = 0; | ||
108 | var->transp.length = 0; | ||
109 | |||
110 | if (fb_alloc_cmap(&info->cmap, 256, 0)) { | ||
111 | printk(KERN_ERR "s3d: Cannot allocate color map.\n"); | ||
112 | return -ENOMEM; | ||
113 | } | ||
114 | |||
115 | return 0; | ||
116 | } | ||
117 | |||
118 | static int s3d_pci_register(struct pci_dev *pdev, | ||
119 | const struct pci_device_id *ent) | ||
120 | { | ||
121 | struct fb_info *info; | ||
122 | struct s3d_info *sp; | ||
123 | int err; | ||
124 | |||
125 | err = pci_enable_device(pdev); | ||
126 | if (err < 0) { | ||
127 | printk(KERN_ERR "s3d: Cannot enable PCI device %s\n", | ||
128 | pci_name(pdev)); | ||
129 | goto err_out; | ||
130 | } | ||
131 | |||
132 | info = framebuffer_alloc(sizeof(struct s3d_info), &pdev->dev); | ||
133 | if (!info) { | ||
134 | printk(KERN_ERR "s3d: Cannot allocate fb_info\n"); | ||
135 | err = -ENOMEM; | ||
136 | goto err_disable; | ||
137 | } | ||
138 | |||
139 | sp = info->par; | ||
140 | sp->info = info; | ||
141 | sp->pdev = pdev; | ||
142 | sp->of_node = pci_device_to_OF_node(pdev); | ||
143 | if (!sp->of_node) { | ||
144 | printk(KERN_ERR "s3d: Cannot find OF node of %s\n", | ||
145 | pci_name(pdev)); | ||
146 | err = -ENODEV; | ||
147 | goto err_release_fb; | ||
148 | } | ||
149 | |||
150 | sp->fb_base_phys = pci_resource_start (pdev, 1); | ||
151 | |||
152 | err = pci_request_region(pdev, 1, "s3d framebuffer"); | ||
153 | if (err < 0) { | ||
154 | printk("s3d: Cannot request region 1 for %s\n", | ||
155 | pci_name(pdev)); | ||
156 | goto err_release_fb; | ||
157 | } | ||
158 | |||
159 | err = s3d_get_props(sp); | ||
160 | if (err) | ||
161 | goto err_release_pci; | ||
162 | |||
163 | /* XXX 'linebytes' is often wrong, it is equal to the width | ||
164 | * XXX with depth of 32 on my XVR-2500 which is clearly not | ||
165 | * XXX right. So we don't try to use it. | ||
166 | */ | ||
167 | switch (sp->depth) { | ||
168 | case 8: | ||
169 | info->fix.line_length = sp->width; | ||
170 | break; | ||
171 | case 16: | ||
172 | info->fix.line_length = sp->width * 2; | ||
173 | break; | ||
174 | case 24: | ||
175 | info->fix.line_length = sp->width * 3; | ||
176 | break; | ||
177 | case 32: | ||
178 | info->fix.line_length = sp->width * 4; | ||
179 | break; | ||
180 | } | ||
181 | sp->fb_size = info->fix.line_length * sp->height; | ||
182 | |||
183 | sp->fb_base = ioremap(sp->fb_base_phys, sp->fb_size); | ||
184 | if (!sp->fb_base) { | ||
185 | err = -ENOMEM; | ||
186 | goto err_release_pci; | ||
187 | } | ||
188 | |||
189 | err = s3d_set_fbinfo(sp); | ||
190 | if (err) | ||
191 | goto err_unmap_fb; | ||
192 | |||
193 | pci_set_drvdata(pdev, info); | ||
194 | |||
195 | printk("s3d: Found device at %s\n", pci_name(pdev)); | ||
196 | |||
197 | err = register_framebuffer(info); | ||
198 | if (err < 0) { | ||
199 | printk(KERN_ERR "s3d: Could not register framebuffer %s\n", | ||
200 | pci_name(pdev)); | ||
201 | goto err_unmap_fb; | ||
202 | } | ||
203 | |||
204 | return 0; | ||
205 | |||
206 | err_unmap_fb: | ||
207 | iounmap(sp->fb_base); | ||
208 | |||
209 | err_release_pci: | ||
210 | pci_release_region(pdev, 1); | ||
211 | |||
212 | err_release_fb: | ||
213 | framebuffer_release(info); | ||
214 | |||
215 | err_disable: | ||
216 | pci_disable_device(pdev); | ||
217 | |||
218 | err_out: | ||
219 | return err; | ||
220 | } | ||
221 | |||
222 | static void s3d_pci_unregister(struct pci_dev *pdev) | ||
223 | { | ||
224 | struct fb_info *info = pci_get_drvdata(pdev); | ||
225 | struct s3d_info *sp = info->par; | ||
226 | |||
227 | unregister_framebuffer(info); | ||
228 | |||
229 | iounmap(sp->fb_base); | ||
230 | |||
231 | pci_release_region(pdev, 1); | ||
232 | |||
233 | framebuffer_release(info); | ||
234 | |||
235 | pci_disable_device(pdev); | ||
236 | } | ||
237 | |||
238 | static struct pci_device_id s3d_pci_table[] = { | ||
239 | { PCI_DEVICE(PCI_VENDOR_ID_3DLABS, 0x002c), }, | ||
240 | { PCI_DEVICE(PCI_VENDOR_ID_3DLABS, 0x002d), }, | ||
241 | { PCI_DEVICE(PCI_VENDOR_ID_3DLABS, 0x002e), }, | ||
242 | { PCI_DEVICE(PCI_VENDOR_ID_3DLABS, 0x002f), }, | ||
243 | { PCI_DEVICE(PCI_VENDOR_ID_3DLABS, 0x0030), }, | ||
244 | { PCI_DEVICE(PCI_VENDOR_ID_3DLABS, 0x0031), }, | ||
245 | { PCI_DEVICE(PCI_VENDOR_ID_3DLABS, 0x0032), }, | ||
246 | { PCI_DEVICE(PCI_VENDOR_ID_3DLABS, 0x0033), }, | ||
247 | { 0, } | ||
248 | }; | ||
249 | |||
250 | static struct pci_driver s3d_driver = { | ||
251 | .name = "s3d", | ||
252 | .id_table = s3d_pci_table, | ||
253 | .probe = s3d_pci_register, | ||
254 | .remove = s3d_pci_unregister, | ||
255 | }; | ||
256 | |||
257 | static int __init s3d_init(void) | ||
258 | { | ||
259 | if (fb_get_options("s3d", NULL)) | ||
260 | return -ENODEV; | ||
261 | |||
262 | return pci_register_driver(&s3d_driver); | ||
263 | } | ||
264 | |||
265 | static void __exit s3d_exit(void) | ||
266 | { | ||
267 | pci_unregister_driver(&s3d_driver); | ||
268 | } | ||
269 | |||
270 | module_init(s3d_init); | ||
271 | module_exit(s3d_exit); | ||
272 | |||
273 | MODULE_DESCRIPTION("framebuffer driver for Sun XVR-2500 graphics"); | ||
274 | MODULE_AUTHOR("David S. Miller <davem@davemloft.net>"); | ||
275 | MODULE_VERSION("1.0"); | ||
276 | MODULE_LICENSE("GPL"); | ||