]> Pileus Git - ~andy/linux/blob - drivers/video/simplefb.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc
[~andy/linux] / drivers / video / simplefb.c
1 /*
2  * Simplest possible simple frame-buffer driver, as a platform device
3  *
4  * Copyright (c) 2013, Stephen Warren
5  *
6  * Based on q40fb.c, which was:
7  * Copyright (C) 2001 Richard Zidlicky <rz@linux-m68k.org>
8  *
9  * Also based on offb.c, which was:
10  * Copyright (C) 1997 Geert Uytterhoeven
11  * Copyright (C) 1996 Paul Mackerras
12  *
13  * This program is free software; you can redistribute it and/or modify it
14  * under the terms and conditions of the GNU General Public License,
15  * version 2, as published by the Free Software Foundation.
16  *
17  * This program is distributed in the hope it will be useful, but WITHOUT
18  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
20  * more details.
21  */
22
23 #include <linux/errno.h>
24 #include <linux/fb.h>
25 #include <linux/io.h>
26 #include <linux/module.h>
27 #include <linux/platform_data/simplefb.h>
28 #include <linux/platform_device.h>
29
30 static struct fb_fix_screeninfo simplefb_fix = {
31         .id             = "simple",
32         .type           = FB_TYPE_PACKED_PIXELS,
33         .visual         = FB_VISUAL_TRUECOLOR,
34         .accel          = FB_ACCEL_NONE,
35 };
36
37 static struct fb_var_screeninfo simplefb_var = {
38         .height         = -1,
39         .width          = -1,
40         .activate       = FB_ACTIVATE_NOW,
41         .vmode          = FB_VMODE_NONINTERLACED,
42 };
43
44 static int simplefb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
45                               u_int transp, struct fb_info *info)
46 {
47         u32 *pal = info->pseudo_palette;
48         u32 cr = red >> (16 - info->var.red.length);
49         u32 cg = green >> (16 - info->var.green.length);
50         u32 cb = blue >> (16 - info->var.blue.length);
51         u32 value;
52
53         if (regno >= 16)
54                 return -EINVAL;
55
56         value = (cr << info->var.red.offset) |
57                 (cg << info->var.green.offset) |
58                 (cb << info->var.blue.offset);
59         if (info->var.transp.length > 0) {
60                 u32 mask = (1 << info->var.transp.length) - 1;
61                 mask <<= info->var.transp.offset;
62                 value |= mask;
63         }
64         pal[regno] = value;
65
66         return 0;
67 }
68
69 static void simplefb_destroy(struct fb_info *info)
70 {
71         if (info->screen_base)
72                 iounmap(info->screen_base);
73 }
74
75 static struct fb_ops simplefb_ops = {
76         .owner          = THIS_MODULE,
77         .fb_destroy     = simplefb_destroy,
78         .fb_setcolreg   = simplefb_setcolreg,
79         .fb_fillrect    = cfb_fillrect,
80         .fb_copyarea    = cfb_copyarea,
81         .fb_imageblit   = cfb_imageblit,
82 };
83
84 static struct simplefb_format simplefb_formats[] = SIMPLEFB_FORMATS;
85
86 struct simplefb_params {
87         u32 width;
88         u32 height;
89         u32 stride;
90         struct simplefb_format *format;
91 };
92
93 static int simplefb_parse_dt(struct platform_device *pdev,
94                            struct simplefb_params *params)
95 {
96         struct device_node *np = pdev->dev.of_node;
97         int ret;
98         const char *format;
99         int i;
100
101         ret = of_property_read_u32(np, "width", &params->width);
102         if (ret) {
103                 dev_err(&pdev->dev, "Can't parse width property\n");
104                 return ret;
105         }
106
107         ret = of_property_read_u32(np, "height", &params->height);
108         if (ret) {
109                 dev_err(&pdev->dev, "Can't parse height property\n");
110                 return ret;
111         }
112
113         ret = of_property_read_u32(np, "stride", &params->stride);
114         if (ret) {
115                 dev_err(&pdev->dev, "Can't parse stride property\n");
116                 return ret;
117         }
118
119         ret = of_property_read_string(np, "format", &format);
120         if (ret) {
121                 dev_err(&pdev->dev, "Can't parse format property\n");
122                 return ret;
123         }
124         params->format = NULL;
125         for (i = 0; i < ARRAY_SIZE(simplefb_formats); i++) {
126                 if (strcmp(format, simplefb_formats[i].name))
127                         continue;
128                 params->format = &simplefb_formats[i];
129                 break;
130         }
131         if (!params->format) {
132                 dev_err(&pdev->dev, "Invalid format value\n");
133                 return -EINVAL;
134         }
135
136         return 0;
137 }
138
139 static int simplefb_parse_pd(struct platform_device *pdev,
140                              struct simplefb_params *params)
141 {
142         struct simplefb_platform_data *pd = dev_get_platdata(&pdev->dev);
143         int i;
144
145         params->width = pd->width;
146         params->height = pd->height;
147         params->stride = pd->stride;
148
149         params->format = NULL;
150         for (i = 0; i < ARRAY_SIZE(simplefb_formats); i++) {
151                 if (strcmp(pd->format, simplefb_formats[i].name))
152                         continue;
153
154                 params->format = &simplefb_formats[i];
155                 break;
156         }
157
158         if (!params->format) {
159                 dev_err(&pdev->dev, "Invalid format value\n");
160                 return -EINVAL;
161         }
162
163         return 0;
164 }
165
166 static int simplefb_probe(struct platform_device *pdev)
167 {
168         int ret;
169         struct simplefb_params params;
170         struct fb_info *info;
171         struct resource *mem;
172
173         if (fb_get_options("simplefb", NULL))
174                 return -ENODEV;
175
176         ret = -ENODEV;
177         if (dev_get_platdata(&pdev->dev))
178                 ret = simplefb_parse_pd(pdev, &params);
179         else if (pdev->dev.of_node)
180                 ret = simplefb_parse_dt(pdev, &params);
181
182         if (ret)
183                 return ret;
184
185         mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
186         if (!mem) {
187                 dev_err(&pdev->dev, "No memory resource\n");
188                 return -EINVAL;
189         }
190
191         info = framebuffer_alloc(sizeof(u32) * 16, &pdev->dev);
192         if (!info)
193                 return -ENOMEM;
194         platform_set_drvdata(pdev, info);
195
196         info->fix = simplefb_fix;
197         info->fix.smem_start = mem->start;
198         info->fix.smem_len = resource_size(mem);
199         info->fix.line_length = params.stride;
200
201         info->var = simplefb_var;
202         info->var.xres = params.width;
203         info->var.yres = params.height;
204         info->var.xres_virtual = params.width;
205         info->var.yres_virtual = params.height;
206         info->var.bits_per_pixel = params.format->bits_per_pixel;
207         info->var.red = params.format->red;
208         info->var.green = params.format->green;
209         info->var.blue = params.format->blue;
210         info->var.transp = params.format->transp;
211
212         info->apertures = alloc_apertures(1);
213         if (!info->apertures) {
214                 framebuffer_release(info);
215                 return -ENOMEM;
216         }
217         info->apertures->ranges[0].base = info->fix.smem_start;
218         info->apertures->ranges[0].size = info->fix.smem_len;
219
220         info->fbops = &simplefb_ops;
221         info->flags = FBINFO_DEFAULT | FBINFO_MISC_FIRMWARE;
222         info->screen_base = ioremap_wc(info->fix.smem_start,
223                                        info->fix.smem_len);
224         if (!info->screen_base) {
225                 framebuffer_release(info);
226                 return -ENODEV;
227         }
228         info->pseudo_palette = (void *)(info + 1);
229
230         dev_info(&pdev->dev, "framebuffer at 0x%lx, 0x%x bytes, mapped to 0x%p\n",
231                              info->fix.smem_start, info->fix.smem_len,
232                              info->screen_base);
233         dev_info(&pdev->dev, "format=%s, mode=%dx%dx%d, linelength=%d\n",
234                              params.format->name,
235                              info->var.xres, info->var.yres,
236                              info->var.bits_per_pixel, info->fix.line_length);
237
238         ret = register_framebuffer(info);
239         if (ret < 0) {
240                 dev_err(&pdev->dev, "Unable to register simplefb: %d\n", ret);
241                 iounmap(info->screen_base);
242                 framebuffer_release(info);
243                 return ret;
244         }
245
246         dev_info(&pdev->dev, "fb%d: simplefb registered!\n", info->node);
247
248         return 0;
249 }
250
251 static int simplefb_remove(struct platform_device *pdev)
252 {
253         struct fb_info *info = platform_get_drvdata(pdev);
254
255         unregister_framebuffer(info);
256         framebuffer_release(info);
257
258         return 0;
259 }
260
261 static const struct of_device_id simplefb_of_match[] = {
262         { .compatible = "simple-framebuffer", },
263         { },
264 };
265 MODULE_DEVICE_TABLE(of, simplefb_of_match);
266
267 static struct platform_driver simplefb_driver = {
268         .driver = {
269                 .name = "simple-framebuffer",
270                 .owner = THIS_MODULE,
271                 .of_match_table = simplefb_of_match,
272         },
273         .probe = simplefb_probe,
274         .remove = simplefb_remove,
275 };
276 module_platform_driver(simplefb_driver);
277
278 MODULE_AUTHOR("Stephen Warren <swarren@wwwdotorg.org>");
279 MODULE_DESCRIPTION("Simple framebuffer driver");
280 MODULE_LICENSE("GPL v2");