Merge remote-tracking branch 'regmap/fix/cache' into regmap-linus
[firefly-linux-kernel-4.4.55.git] / 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_device.h>
28
29 static struct fb_fix_screeninfo simplefb_fix = {
30         .id             = "simple",
31         .type           = FB_TYPE_PACKED_PIXELS,
32         .visual         = FB_VISUAL_TRUECOLOR,
33         .accel          = FB_ACCEL_NONE,
34 };
35
36 static struct fb_var_screeninfo simplefb_var = {
37         .height         = -1,
38         .width          = -1,
39         .activate       = FB_ACTIVATE_NOW,
40         .vmode          = FB_VMODE_NONINTERLACED,
41 };
42
43 static int simplefb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
44                               u_int transp, struct fb_info *info)
45 {
46         u32 *pal = info->pseudo_palette;
47         u32 cr = red >> (16 - info->var.red.length);
48         u32 cg = green >> (16 - info->var.green.length);
49         u32 cb = blue >> (16 - info->var.blue.length);
50         u32 value;
51
52         if (regno >= 16)
53                 return -EINVAL;
54
55         value = (cr << info->var.red.offset) |
56                 (cg << info->var.green.offset) |
57                 (cb << info->var.blue.offset);
58         if (info->var.transp.length > 0) {
59                 u32 mask = (1 << info->var.transp.length) - 1;
60                 mask <<= info->var.transp.offset;
61                 value |= mask;
62         }
63         pal[regno] = value;
64
65         return 0;
66 }
67
68 static struct fb_ops simplefb_ops = {
69         .owner          = THIS_MODULE,
70         .fb_setcolreg   = simplefb_setcolreg,
71         .fb_fillrect    = cfb_fillrect,
72         .fb_copyarea    = cfb_copyarea,
73         .fb_imageblit   = cfb_imageblit,
74 };
75
76 struct simplefb_format {
77         const char *name;
78         u32 bits_per_pixel;
79         struct fb_bitfield red;
80         struct fb_bitfield green;
81         struct fb_bitfield blue;
82         struct fb_bitfield transp;
83 };
84
85 static struct simplefb_format simplefb_formats[] = {
86         { "r5g6b5", 16, {11, 5}, {5, 6}, {0, 5}, {0, 0} },
87 };
88
89 struct simplefb_params {
90         u32 width;
91         u32 height;
92         u32 stride;
93         struct simplefb_format *format;
94 };
95
96 static int simplefb_parse_dt(struct platform_device *pdev,
97                            struct simplefb_params *params)
98 {
99         struct device_node *np = pdev->dev.of_node;
100         int ret;
101         const char *format;
102         int i;
103
104         ret = of_property_read_u32(np, "width", &params->width);
105         if (ret) {
106                 dev_err(&pdev->dev, "Can't parse width property\n");
107                 return ret;
108         }
109
110         ret = of_property_read_u32(np, "height", &params->height);
111         if (ret) {
112                 dev_err(&pdev->dev, "Can't parse height property\n");
113                 return ret;
114         }
115
116         ret = of_property_read_u32(np, "stride", &params->stride);
117         if (ret) {
118                 dev_err(&pdev->dev, "Can't parse stride property\n");
119                 return ret;
120         }
121
122         ret = of_property_read_string(np, "format", &format);
123         if (ret) {
124                 dev_err(&pdev->dev, "Can't parse format property\n");
125                 return ret;
126         }
127         params->format = NULL;
128         for (i = 0; i < ARRAY_SIZE(simplefb_formats); i++) {
129                 if (strcmp(format, simplefb_formats[i].name))
130                         continue;
131                 params->format = &simplefb_formats[i];
132                 break;
133         }
134         if (!params->format) {
135                 dev_err(&pdev->dev, "Invalid format value\n");
136                 return -EINVAL;
137         }
138
139         return 0;
140 }
141
142 static int simplefb_probe(struct platform_device *pdev)
143 {
144         int ret;
145         struct simplefb_params params;
146         struct fb_info *info;
147         struct resource *mem;
148
149         if (fb_get_options("simplefb", NULL))
150                 return -ENODEV;
151
152         ret = simplefb_parse_dt(pdev, &params);
153         if (ret)
154                 return ret;
155
156         mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
157         if (!mem) {
158                 dev_err(&pdev->dev, "No memory resource\n");
159                 return -EINVAL;
160         }
161
162         info = framebuffer_alloc(sizeof(u32) * 16, &pdev->dev);
163         if (!info)
164                 return -ENOMEM;
165         platform_set_drvdata(pdev, info);
166
167         info->fix = simplefb_fix;
168         info->fix.smem_start = mem->start;
169         info->fix.smem_len = resource_size(mem);
170         info->fix.line_length = params.stride;
171
172         info->var = simplefb_var;
173         info->var.xres = params.width;
174         info->var.yres = params.height;
175         info->var.xres_virtual = params.width;
176         info->var.yres_virtual = params.height;
177         info->var.bits_per_pixel = params.format->bits_per_pixel;
178         info->var.red = params.format->red;
179         info->var.green = params.format->green;
180         info->var.blue = params.format->blue;
181         info->var.transp = params.format->transp;
182
183         info->fbops = &simplefb_ops;
184         info->flags = FBINFO_DEFAULT;
185         info->screen_base = devm_ioremap(&pdev->dev, info->fix.smem_start,
186                                          info->fix.smem_len);
187         if (!info->screen_base) {
188                 framebuffer_release(info);
189                 return -ENODEV;
190         }
191         info->pseudo_palette = (void *)(info + 1);
192
193         ret = register_framebuffer(info);
194         if (ret < 0) {
195                 dev_err(&pdev->dev, "Unable to register simplefb: %d\n", ret);
196                 framebuffer_release(info);
197                 return ret;
198         }
199
200         dev_info(&pdev->dev, "fb%d: simplefb registered!\n", info->node);
201
202         return 0;
203 }
204
205 static int simplefb_remove(struct platform_device *pdev)
206 {
207         struct fb_info *info = platform_get_drvdata(pdev);
208
209         unregister_framebuffer(info);
210         framebuffer_release(info);
211
212         return 0;
213 }
214
215 static const struct of_device_id simplefb_of_match[] = {
216         { .compatible = "simple-framebuffer", },
217         { },
218 };
219 MODULE_DEVICE_TABLE(of, simplefb_of_match);
220
221 static struct platform_driver simplefb_driver = {
222         .driver = {
223                 .name = "simple-framebuffer",
224                 .owner = THIS_MODULE,
225                 .of_match_table = simplefb_of_match,
226         },
227         .probe = simplefb_probe,
228         .remove = simplefb_remove,
229 };
230 module_platform_driver(simplefb_driver);
231
232 MODULE_AUTHOR("Stephen Warren <swarren@wwwdotorg.org>");
233 MODULE_DESCRIPTION("Simple framebuffer driver");
234 MODULE_LICENSE("GPL v2");