1 | /* sunxvr1000.c: Sun XVR-1000 fb driver for sparc64 systems |
2 | * |
3 | * License: GPL |
4 | * |
5 | * Copyright (C) 2010 David S. Miller (davem@davemloft.net) |
6 | */ |
7 | |
8 | #include <linux/kernel.h> |
9 | #include <linux/fb.h> |
10 | #include <linux/init.h> |
11 | #include <linux/of.h> |
12 | #include <linux/platform_device.h> |
13 | |
14 | struct gfb_info { |
15 | struct fb_info *info; |
16 | |
17 | char __iomem *fb_base; |
18 | unsigned long fb_base_phys; |
19 | |
20 | struct device_node *of_node; |
21 | |
22 | unsigned int width; |
23 | unsigned int height; |
24 | unsigned int depth; |
25 | unsigned int fb_size; |
26 | |
27 | u32 pseudo_palette[16]; |
28 | }; |
29 | |
30 | static int gfb_get_props(struct gfb_info *gp) |
31 | { |
32 | gp->width = of_getintprop_default(gp->of_node, "width" , 0); |
33 | gp->height = of_getintprop_default(gp->of_node, "height" , 0); |
34 | gp->depth = of_getintprop_default(gp->of_node, "depth" , 32); |
35 | |
36 | if (!gp->width || !gp->height) { |
37 | printk(KERN_ERR "gfb: Critical properties missing for %pOF\n" , |
38 | gp->of_node); |
39 | return -EINVAL; |
40 | } |
41 | |
42 | return 0; |
43 | } |
44 | |
45 | static int gfb_setcolreg(unsigned regno, |
46 | unsigned red, unsigned green, unsigned blue, |
47 | unsigned transp, struct fb_info *info) |
48 | { |
49 | u32 value; |
50 | |
51 | if (regno < 16) { |
52 | red >>= 8; |
53 | green >>= 8; |
54 | blue >>= 8; |
55 | |
56 | value = (blue << 16) | (green << 8) | red; |
57 | ((u32 *)info->pseudo_palette)[regno] = value; |
58 | } |
59 | |
60 | return 0; |
61 | } |
62 | |
63 | static const struct fb_ops gfb_ops = { |
64 | .owner = THIS_MODULE, |
65 | FB_DEFAULT_IOMEM_OPS, |
66 | .fb_setcolreg = gfb_setcolreg, |
67 | }; |
68 | |
69 | static int 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->fbops = &gfb_ops; |
75 | info->screen_base = gp->fb_base; |
76 | info->screen_size = gp->fb_size; |
77 | |
78 | info->pseudo_palette = gp->pseudo_palette; |
79 | |
80 | /* Fill fix common fields */ |
81 | strscpy(info->fix.id, "gfb" , sizeof(info->fix.id)); |
82 | info->fix.smem_start = gp->fb_base_phys; |
83 | info->fix.smem_len = gp->fb_size; |
84 | info->fix.type = FB_TYPE_PACKED_PIXELS; |
85 | if (gp->depth == 32 || gp->depth == 24) |
86 | info->fix.visual = FB_VISUAL_TRUECOLOR; |
87 | else |
88 | info->fix.visual = FB_VISUAL_PSEUDOCOLOR; |
89 | |
90 | var->xres = gp->width; |
91 | var->yres = gp->height; |
92 | var->xres_virtual = var->xres; |
93 | var->yres_virtual = var->yres; |
94 | var->bits_per_pixel = gp->depth; |
95 | |
96 | var->red.offset = 0; |
97 | var->red.length = 8; |
98 | var->green.offset = 8; |
99 | var->green.length = 8; |
100 | var->blue.offset = 16; |
101 | var->blue.length = 8; |
102 | var->transp.offset = 0; |
103 | var->transp.length = 0; |
104 | |
105 | if (fb_alloc_cmap(cmap: &info->cmap, len: 256, transp: 0)) { |
106 | printk(KERN_ERR "gfb: Cannot allocate color map.\n" ); |
107 | return -ENOMEM; |
108 | } |
109 | |
110 | return 0; |
111 | } |
112 | |
113 | static int gfb_probe(struct platform_device *op) |
114 | { |
115 | struct device_node *dp = op->dev.of_node; |
116 | struct fb_info *info; |
117 | struct gfb_info *gp; |
118 | int err; |
119 | |
120 | info = framebuffer_alloc(size: sizeof(struct gfb_info), dev: &op->dev); |
121 | if (!info) { |
122 | err = -ENOMEM; |
123 | goto err_out; |
124 | } |
125 | |
126 | gp = info->par; |
127 | gp->info = info; |
128 | gp->of_node = dp; |
129 | |
130 | gp->fb_base_phys = op->resource[6].start; |
131 | |
132 | err = gfb_get_props(gp); |
133 | if (err) |
134 | goto err_release_fb; |
135 | |
136 | /* Framebuffer length is the same regardless of resolution. */ |
137 | info->fix.line_length = 16384; |
138 | gp->fb_size = info->fix.line_length * gp->height; |
139 | |
140 | gp->fb_base = of_ioremap(&op->resource[6], 0, |
141 | gp->fb_size, "gfb fb" ); |
142 | if (!gp->fb_base) { |
143 | err = -ENOMEM; |
144 | goto err_release_fb; |
145 | } |
146 | |
147 | err = gfb_set_fbinfo(gp); |
148 | if (err) |
149 | goto err_unmap_fb; |
150 | |
151 | printk("gfb: Found device at %pOF\n" , dp); |
152 | |
153 | err = register_framebuffer(fb_info: info); |
154 | if (err < 0) { |
155 | printk(KERN_ERR "gfb: Could not register framebuffer %pOF\n" , |
156 | dp); |
157 | goto err_unmap_fb; |
158 | } |
159 | |
160 | dev_set_drvdata(dev: &op->dev, data: info); |
161 | |
162 | return 0; |
163 | |
164 | err_unmap_fb: |
165 | of_iounmap(&op->resource[6], gp->fb_base, gp->fb_size); |
166 | |
167 | err_release_fb: |
168 | framebuffer_release(info); |
169 | |
170 | err_out: |
171 | return err; |
172 | } |
173 | |
174 | static const struct of_device_id gfb_match[] = { |
175 | { |
176 | .name = "SUNW,gfb" , |
177 | }, |
178 | {}, |
179 | }; |
180 | |
181 | static struct platform_driver gfb_driver = { |
182 | .probe = gfb_probe, |
183 | .driver = { |
184 | .name = "gfb" , |
185 | .of_match_table = gfb_match, |
186 | .suppress_bind_attrs = true, |
187 | }, |
188 | }; |
189 | |
190 | static int __init gfb_init(void) |
191 | { |
192 | if (fb_get_options(name: "gfb" , NULL)) |
193 | return -ENODEV; |
194 | |
195 | return platform_driver_register(&gfb_driver); |
196 | } |
197 | device_initcall(gfb_init); |
198 | |