projects/linux·blob master · refs

250 lines · 5.2 KiB

1/* sunxvr2500.c: Sun 3DLABS XVR-2500 et al. fb driver for sparc64 systems
2 *
3 * License: GPL
4 *
5 * Copyright (C) 2007 David S. Miller ([email protected])
6 */
7
8#include <linux/aperture.h>
9#include <linux/kernel.h>
10#include <linux/fb.h>
11#include <linux/pci.h>
12#include <linux/init.h>
13#include <linux/of.h>
14
15#include <asm/io.h>
16
17struct s3d_info {
18 struct fb_info *info;
19 struct pci_dev *pdev;
20
21 char __iomem *fb_base;
22 unsigned long fb_base_phys;
23
24 struct device_node *of_node;
25
26 unsigned int width;
27 unsigned int height;
28 unsigned int depth;
29 unsigned int fb_size;
30
31 u32 pseudo_palette[16];
32};
33
34static int s3d_get_props(struct s3d_info *sp)
35{
36 sp->width = of_getintprop_default(sp->of_node, "width", 0);
37 sp->height = of_getintprop_default(sp->of_node, "height", 0);
38 sp->depth = of_getintprop_default(sp->of_node, "depth", 8);
39
40 if (!sp->width || !sp->height) {
41 pci_err(sp->pdev, "Critical properties missing\n");
42 return -EINVAL;
43 }
44
45 return 0;
46}
47
48static int s3d_setcolreg(unsigned regno,
49 unsigned red, unsigned green, unsigned blue,
50 unsigned transp, struct fb_info *info)
51{
52 u32 value;
53
54 if (regno < 16) {
55 red >>= 8;
56 green >>= 8;
57 blue >>= 8;
58
59 value = (blue << 24) | (green << 16) | (red << 8);
60 ((u32 *)info->pseudo_palette)[regno] = value;
61 }
62
63 return 0;
64}
65
66static const struct fb_ops s3d_ops = {
67 .owner = THIS_MODULE,
68 FB_DEFAULT_IOMEM_OPS,
69 .fb_setcolreg = s3d_setcolreg,
70};
71
72static int s3d_set_fbinfo(struct s3d_info *sp)
73{
74 struct fb_info *info = sp->info;
75 struct fb_var_screeninfo *var = &info->var;
76
77 info->fbops = &s3d_ops;
78 info->screen_base = sp->fb_base;
79 info->screen_size = sp->fb_size;
80
81 info->pseudo_palette = sp->pseudo_palette;
82
83 /* Fill fix common fields */
84 strscpy(info->fix.id, "s3d", sizeof(info->fix.id));
85 info->fix.smem_start = sp->fb_base_phys;
86 info->fix.smem_len = sp->fb_size;
87 info->fix.type = FB_TYPE_PACKED_PIXELS;
88 if (sp->depth == 32 || sp->depth == 24)
89 info->fix.visual = FB_VISUAL_TRUECOLOR;
90 else
91 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
92
93 var->xres = sp->width;
94 var->yres = sp->height;
95 var->xres_virtual = var->xres;
96 var->yres_virtual = var->yres;
97 var->bits_per_pixel = sp->depth;
98
99 var->red.offset = 8;
100 var->red.length = 8;
101 var->green.offset = 16;
102 var->green.length = 8;
103 var->blue.offset = 24;
104 var->blue.length = 8;
105 var->transp.offset = 0;
106 var->transp.length = 0;
107
108 if (fb_alloc_cmap(&info->cmap, 256, 0)) {
109 pci_err(sp->pdev, "Cannot allocate color map\n");
110 return -ENOMEM;
111 }
112
113 return 0;
114}
115
116static int s3d_pci_register(struct pci_dev *pdev,
117 const struct pci_device_id *ent)
118{
119 struct fb_info *info;
120 struct s3d_info *sp;
121 int err;
122
123 err = aperture_remove_conflicting_pci_devices(pdev, "s3dfb");
124 if (err)
125 return err;
126
127 err = pci_enable_device(pdev);
128 if (err < 0) {
129 pci_err(pdev, "Cannot enable PCI device\n");
130 goto err_out;
131 }
132
133 info = framebuffer_alloc(sizeof(struct s3d_info), &pdev->dev);
134 if (!info) {
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 pci_err(pdev, "Cannot find OF node\n");
145 err = -ENODEV;
146 goto err_release_fb;
147 }
148
149 sp->fb_base_phys = pci_resource_start (pdev, 1);
150
151 err = pci_request_region(pdev, 1, "s3d framebuffer");
152 if (err < 0) {
153 pci_err(pdev, "Cannot request region 1\n");
154 goto err_release_fb;
155 }
156
157 err = s3d_get_props(sp);
158 if (err)
159 goto err_release_pci;
160
161 /* XXX 'linebytes' is often wrong, it is equal to the width
162 * XXX with depth of 32 on my XVR-2500 which is clearly not
163 * XXX right. So we don't try to use it.
164 */
165 switch (sp->depth) {
166 case 8:
167 info->fix.line_length = sp->width;
168 break;
169 case 16:
170 info->fix.line_length = sp->width * 2;
171 break;
172 case 24:
173 info->fix.line_length = sp->width * 3;
174 break;
175 case 32:
176 info->fix.line_length = sp->width * 4;
177 break;
178 }
179 sp->fb_size = info->fix.line_length * sp->height;
180
181 sp->fb_base = ioremap(sp->fb_base_phys, sp->fb_size);
182 if (!sp->fb_base) {
183 err = -ENOMEM;
184 goto err_release_pci;
185 }
186
187 err = s3d_set_fbinfo(sp);
188 if (err)
189 goto err_unmap_fb;
190
191 pci_set_drvdata(pdev, info);
192
193 pci_info(pdev, "Found device\n");
194
195 err = register_framebuffer(info);
196 if (err < 0) {
197 pci_err(pdev, "Could not register framebuffer\n");
198 goto err_unmap_fb;
199 }
200
201 return 0;
202
203err_unmap_fb:
204 iounmap(sp->fb_base);
205
206err_release_pci:
207 pci_release_region(pdev, 1);
208
209err_release_fb:
210 framebuffer_release(info);
211
212err_disable:
213 pci_disable_device(pdev);
214
215err_out:
216 return err;
217}
218
219static const struct pci_device_id s3d_pci_table[] = {
220 { PCI_DEVICE(PCI_VENDOR_ID_3DLABS, 0x002c), },
221 { PCI_DEVICE(PCI_VENDOR_ID_3DLABS, 0x002d), },
222 { PCI_DEVICE(PCI_VENDOR_ID_3DLABS, 0x002e), },
223 { PCI_DEVICE(PCI_VENDOR_ID_3DLABS, 0x002f), },
224 { PCI_DEVICE(PCI_VENDOR_ID_3DLABS, 0x0030), },
225 { PCI_DEVICE(PCI_VENDOR_ID_3DLABS, 0x0031), },
226 { PCI_DEVICE(PCI_VENDOR_ID_3DLABS, 0x0032), },
227 { PCI_DEVICE(PCI_VENDOR_ID_3DLABS, 0x0033), },
228 { 0, }
229};
230
231static struct pci_driver s3d_driver = {
232 .driver = {
233 .suppress_bind_attrs = true,
234 },
235 .name = "s3d",
236 .id_table = s3d_pci_table,
237 .probe = s3d_pci_register,
238};
239
240static int __init s3d_init(void)
241{
242 if (fb_modesetting_disabled("s3d"))
243 return -ENODEV;
244
245 if (fb_get_options("s3d", NULL))
246 return -ENODEV;
247
248 return pci_register_driver(&s3d_driver);
249}
250device_initcall(s3d_init);