diff options
Diffstat (limited to 'drivers/video/sunxvr1000.c')
-rw-r--r-- | drivers/video/sunxvr1000.c | 227 |
1 files changed, 227 insertions, 0 deletions
diff --git a/drivers/video/sunxvr1000.c b/drivers/video/sunxvr1000.c new file mode 100644 index 000000000000..23e69e834a18 --- /dev/null +++ b/drivers/video/sunxvr1000.c | |||
@@ -0,0 +1,227 @@ | |||
1 | /* sunxvr1000.c: Sun XVR-1000 driver for sparc64 systems | ||
2 | * | ||
3 | * Copyright (C) 2010 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/init.h> | ||
10 | #include <linux/of_device.h> | ||
11 | |||
12 | struct gfb_info { | ||
13 | struct fb_info *info; | ||
14 | |||
15 | char __iomem *fb_base; | ||
16 | unsigned long fb_base_phys; | ||
17 | |||
18 | struct device_node *of_node; | ||
19 | |||
20 | unsigned int width; | ||
21 | unsigned int height; | ||
22 | unsigned int depth; | ||
23 | unsigned int fb_size; | ||
24 | |||
25 | u32 pseudo_palette[16]; | ||
26 | }; | ||
27 | |||
28 | static int __devinit gfb_get_props(struct gfb_info *gp) | ||
29 | { | ||
30 | gp->width = of_getintprop_default(gp->of_node, "width", 0); | ||
31 | gp->height = of_getintprop_default(gp->of_node, "height", 0); | ||
32 | gp->depth = of_getintprop_default(gp->of_node, "depth", 32); | ||
33 | |||
34 | if (!gp->width || !gp->height) { | ||
35 | printk(KERN_ERR "gfb: Critical properties missing for %s\n", | ||
36 | gp->of_node->full_name); | ||
37 | return -EINVAL; | ||
38 | } | ||
39 | |||
40 | return 0; | ||
41 | } | ||
42 | |||
43 | static int gfb_setcolreg(unsigned regno, | ||
44 | unsigned red, unsigned green, unsigned blue, | ||
45 | unsigned transp, struct fb_info *info) | ||
46 | { | ||
47 | u32 value; | ||
48 | |||
49 | if (regno < 16) { | ||
50 | red >>= 8; | ||
51 | green >>= 8; | ||
52 | blue >>= 8; | ||
53 | |||
54 | value = (blue << 16) | (green << 8) | red; | ||
55 | ((u32 *)info->pseudo_palette)[regno] = value; | ||
56 | } | ||
57 | |||
58 | return 0; | ||
59 | } | ||
60 | |||
61 | static struct fb_ops gfb_ops = { | ||
62 | .owner = THIS_MODULE, | ||
63 | .fb_setcolreg = gfb_setcolreg, | ||
64 | .fb_fillrect = cfb_fillrect, | ||
65 | .fb_copyarea = cfb_copyarea, | ||
66 | .fb_imageblit = cfb_imageblit, | ||
67 | }; | ||
68 | |||
69 | static int __devinit gfb_set_fbinfo(struct gfb_info *gp) | ||
70 | { | ||
71 | struct fb_info *info = gp->info; | ||
72 | struct fb_var_screeninfo *var = &info->var; | ||
73 | |||
74 | info->flags = FBINFO_DEFAULT; | ||
75 | info->fbops = &gfb_ops; | ||
76 | info->screen_base = gp->fb_base; | ||
77 | info->screen_size = gp->fb_size; | ||
78 | |||
79 | info->pseudo_palette = gp->pseudo_palette; | ||
80 | |||
81 | /* Fill fix common fields */ | ||
82 | strlcpy(info->fix.id, "gfb", sizeof(info->fix.id)); | ||
83 | info->fix.smem_start = gp->fb_base_phys; | ||
84 | info->fix.smem_len = gp->fb_size; | ||
85 | info->fix.type = FB_TYPE_PACKED_PIXELS; | ||
86 | if (gp->depth == 32 || gp->depth == 24) | ||
87 | info->fix.visual = FB_VISUAL_TRUECOLOR; | ||
88 | else | ||
89 | info->fix.visual = FB_VISUAL_PSEUDOCOLOR; | ||
90 | |||
91 | var->xres = gp->width; | ||
92 | var->yres = gp->height; | ||
93 | var->xres_virtual = var->xres; | ||
94 | var->yres_virtual = var->yres; | ||
95 | var->bits_per_pixel = gp->depth; | ||
96 | |||
97 | var->red.offset = 0; | ||
98 | var->red.length = 8; | ||
99 | var->green.offset = 8; | ||
100 | var->green.length = 8; | ||
101 | var->blue.offset = 16; | ||
102 | var->blue.length = 8; | ||
103 | var->transp.offset = 0; | ||
104 | var->transp.length = 0; | ||
105 | |||
106 | if (fb_alloc_cmap(&info->cmap, 256, 0)) { | ||
107 | printk(KERN_ERR "gfb: Cannot allocate color map.\n"); | ||
108 | return -ENOMEM; | ||
109 | } | ||
110 | |||
111 | return 0; | ||
112 | } | ||
113 | |||
114 | static int __devinit gfb_probe(struct of_device *op, | ||
115 | const struct of_device_id *match) | ||
116 | { | ||
117 | struct device_node *dp = op->node; | ||
118 | struct fb_info *info; | ||
119 | struct gfb_info *gp; | ||
120 | int err; | ||
121 | |||
122 | info = framebuffer_alloc(sizeof(struct gfb_info), &op->dev); | ||
123 | if (!info) { | ||
124 | printk(KERN_ERR "gfb: Cannot allocate fb_info\n"); | ||
125 | err = -ENOMEM; | ||
126 | goto err_out; | ||
127 | } | ||
128 | |||
129 | gp = info->par; | ||
130 | gp->info = info; | ||
131 | gp->of_node = dp; | ||
132 | |||
133 | gp->fb_base_phys = op->resource[6].start; | ||
134 | |||
135 | err = gfb_get_props(gp); | ||
136 | if (err) | ||
137 | goto err_release_fb; | ||
138 | |||
139 | /* Framebuffer length is the same regardless of resolution. */ | ||
140 | info->fix.line_length = 16384; | ||
141 | gp->fb_size = info->fix.line_length * gp->height; | ||
142 | |||
143 | gp->fb_base = of_ioremap(&op->resource[6], 0, | ||
144 | gp->fb_size, "gfb fb"); | ||
145 | if (!gp->fb_base) | ||
146 | goto err_release_fb; | ||
147 | |||
148 | err = gfb_set_fbinfo(gp); | ||
149 | if (err) | ||
150 | goto err_unmap_fb; | ||
151 | |||
152 | printk("gfb: Found device at %s\n", dp->full_name); | ||
153 | |||
154 | err = register_framebuffer(info); | ||
155 | if (err < 0) { | ||
156 | printk(KERN_ERR "gfb: Could not register framebuffer %s\n", | ||
157 | dp->full_name); | ||
158 | goto err_unmap_fb; | ||
159 | } | ||
160 | |||
161 | dev_set_drvdata(&op->dev, info); | ||
162 | |||
163 | return 0; | ||
164 | |||
165 | err_unmap_fb: | ||
166 | of_iounmap(&op->resource[6], gp->fb_base, gp->fb_size); | ||
167 | |||
168 | err_release_fb: | ||
169 | framebuffer_release(info); | ||
170 | |||
171 | err_out: | ||
172 | return err; | ||
173 | } | ||
174 | |||
175 | static int __devexit gfb_remove(struct of_device *op) | ||
176 | { | ||
177 | struct fb_info *info = dev_get_drvdata(&op->dev); | ||
178 | struct gfb_info *gp = info->par; | ||
179 | |||
180 | unregister_framebuffer(info); | ||
181 | |||
182 | iounmap(gp->fb_base); | ||
183 | |||
184 | of_iounmap(&op->resource[6], gp->fb_base, gp->fb_size); | ||
185 | |||
186 | framebuffer_release(info); | ||
187 | |||
188 | dev_set_drvdata(&op->dev, NULL); | ||
189 | |||
190 | return 0; | ||
191 | } | ||
192 | |||
193 | static const struct of_device_id gfb_match[] = { | ||
194 | { | ||
195 | .name = "SUNW,gfb", | ||
196 | }, | ||
197 | {}, | ||
198 | }; | ||
199 | MODULE_DEVICE_TABLE(of, ffb_match); | ||
200 | |||
201 | static struct of_platform_driver gfb_driver = { | ||
202 | .name = "gfb", | ||
203 | .match_table = gfb_match, | ||
204 | .probe = gfb_probe, | ||
205 | .remove = __devexit_p(gfb_remove), | ||
206 | }; | ||
207 | |||
208 | static int __init gfb_init(void) | ||
209 | { | ||
210 | if (fb_get_options("gfb", NULL)) | ||
211 | return -ENODEV; | ||
212 | |||
213 | return of_register_driver(&gfb_driver, &of_bus_type); | ||
214 | } | ||
215 | |||
216 | static void __exit gfb_exit(void) | ||
217 | { | ||
218 | of_unregister_driver(&gfb_driver); | ||
219 | } | ||
220 | |||
221 | module_init(gfb_init); | ||
222 | module_exit(gfb_exit); | ||
223 | |||
224 | MODULE_DESCRIPTION("framebuffer driver for Sun XVR-1000 graphics"); | ||
225 | MODULE_AUTHOR("David S. Miller <davem@davemloft.net>"); | ||
226 | MODULE_VERSION("1.0"); | ||
227 | MODULE_LICENSE("GPL"); | ||