blob: ad46d2dd55a2d248d46fea9402912955d01fa1f4 [file] [log] [blame]
Stephen Warren26549c82013-05-24 15:55:13 -07001/*
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>
David Herrmann5ef76da2013-08-02 14:05:20 +020027#include <linux/platform_data/simplefb.h>
Stephen Warren26549c82013-05-24 15:55:13 -070028#include <linux/platform_device.h>
29
30static 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
37static struct fb_var_screeninfo simplefb_var = {
38 .height = -1,
39 .width = -1,
40 .activate = FB_ACTIVATE_NOW,
41 .vmode = FB_VMODE_NONINTERLACED,
42};
43
44static 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
69static struct fb_ops simplefb_ops = {
70 .owner = THIS_MODULE,
71 .fb_setcolreg = simplefb_setcolreg,
72 .fb_fillrect = cfb_fillrect,
73 .fb_copyarea = cfb_copyarea,
74 .fb_imageblit = cfb_imageblit,
75};
76
David Herrmann5ef76da2013-08-02 14:05:20 +020077static struct simplefb_format simplefb_formats[] = SIMPLEFB_FORMATS;
Stephen Warren26549c82013-05-24 15:55:13 -070078
79struct simplefb_params {
80 u32 width;
81 u32 height;
82 u32 stride;
83 struct simplefb_format *format;
84};
85
86static int simplefb_parse_dt(struct platform_device *pdev,
87 struct simplefb_params *params)
88{
89 struct device_node *np = pdev->dev.of_node;
90 int ret;
91 const char *format;
92 int i;
93
94 ret = of_property_read_u32(np, "width", &params->width);
95 if (ret) {
96 dev_err(&pdev->dev, "Can't parse width property\n");
97 return ret;
98 }
99
100 ret = of_property_read_u32(np, "height", &params->height);
101 if (ret) {
102 dev_err(&pdev->dev, "Can't parse height property\n");
103 return ret;
104 }
105
106 ret = of_property_read_u32(np, "stride", &params->stride);
107 if (ret) {
108 dev_err(&pdev->dev, "Can't parse stride property\n");
109 return ret;
110 }
111
112 ret = of_property_read_string(np, "format", &format);
113 if (ret) {
114 dev_err(&pdev->dev, "Can't parse format property\n");
115 return ret;
116 }
117 params->format = NULL;
118 for (i = 0; i < ARRAY_SIZE(simplefb_formats); i++) {
119 if (strcmp(format, simplefb_formats[i].name))
120 continue;
121 params->format = &simplefb_formats[i];
122 break;
123 }
124 if (!params->format) {
125 dev_err(&pdev->dev, "Invalid format value\n");
126 return -EINVAL;
127 }
128
129 return 0;
130}
131
David Herrmann5ef76da2013-08-02 14:05:20 +0200132static int simplefb_parse_pd(struct platform_device *pdev,
133 struct simplefb_params *params)
134{
Jingoo Han129f1be2013-09-17 14:11:04 +0900135 struct simplefb_platform_data *pd = dev_get_platdata(&pdev->dev);
David Herrmann5ef76da2013-08-02 14:05:20 +0200136 int i;
137
138 params->width = pd->width;
139 params->height = pd->height;
140 params->stride = pd->stride;
141
142 params->format = NULL;
143 for (i = 0; i < ARRAY_SIZE(simplefb_formats); i++) {
144 if (strcmp(pd->format, simplefb_formats[i].name))
145 continue;
146
147 params->format = &simplefb_formats[i];
148 break;
149 }
150
151 if (!params->format) {
152 dev_err(&pdev->dev, "Invalid format value\n");
153 return -EINVAL;
154 }
155
156 return 0;
157}
158
Stephen Warren26549c82013-05-24 15:55:13 -0700159static int simplefb_probe(struct platform_device *pdev)
160{
161 int ret;
162 struct simplefb_params params;
163 struct fb_info *info;
164 struct resource *mem;
165
166 if (fb_get_options("simplefb", NULL))
167 return -ENODEV;
168
David Herrmann5ef76da2013-08-02 14:05:20 +0200169 ret = -ENODEV;
Jingoo Han129f1be2013-09-17 14:11:04 +0900170 if (dev_get_platdata(&pdev->dev))
David Herrmann5ef76da2013-08-02 14:05:20 +0200171 ret = simplefb_parse_pd(pdev, &params);
172 else if (pdev->dev.of_node)
173 ret = simplefb_parse_dt(pdev, &params);
174
Stephen Warren26549c82013-05-24 15:55:13 -0700175 if (ret)
176 return ret;
177
178 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
179 if (!mem) {
180 dev_err(&pdev->dev, "No memory resource\n");
181 return -EINVAL;
182 }
183
184 info = framebuffer_alloc(sizeof(u32) * 16, &pdev->dev);
185 if (!info)
186 return -ENOMEM;
187 platform_set_drvdata(pdev, info);
188
189 info->fix = simplefb_fix;
190 info->fix.smem_start = mem->start;
191 info->fix.smem_len = resource_size(mem);
192 info->fix.line_length = params.stride;
193
194 info->var = simplefb_var;
195 info->var.xres = params.width;
196 info->var.yres = params.height;
197 info->var.xres_virtual = params.width;
198 info->var.yres_virtual = params.height;
199 info->var.bits_per_pixel = params.format->bits_per_pixel;
200 info->var.red = params.format->red;
201 info->var.green = params.format->green;
202 info->var.blue = params.format->blue;
203 info->var.transp = params.format->transp;
204
David Herrmanndf0960a2013-08-02 14:05:21 +0200205 info->apertures = alloc_apertures(1);
206 if (!info->apertures) {
207 framebuffer_release(info);
208 return -ENOMEM;
209 }
210 info->apertures->ranges[0].base = info->fix.smem_start;
211 info->apertures->ranges[0].size = info->fix.smem_len;
212
Stephen Warren26549c82013-05-24 15:55:13 -0700213 info->fbops = &simplefb_ops;
David Herrmanndf0960a2013-08-02 14:05:21 +0200214 info->flags = FBINFO_DEFAULT | FBINFO_MISC_FIRMWARE;
Stephen Warren26549c82013-05-24 15:55:13 -0700215 info->screen_base = devm_ioremap(&pdev->dev, info->fix.smem_start,
216 info->fix.smem_len);
217 if (!info->screen_base) {
218 framebuffer_release(info);
219 return -ENODEV;
220 }
221 info->pseudo_palette = (void *)(info + 1);
222
223 ret = register_framebuffer(info);
224 if (ret < 0) {
225 dev_err(&pdev->dev, "Unable to register simplefb: %d\n", ret);
226 framebuffer_release(info);
227 return ret;
228 }
229
230 dev_info(&pdev->dev, "fb%d: simplefb registered!\n", info->node);
231
232 return 0;
233}
234
235static int simplefb_remove(struct platform_device *pdev)
236{
237 struct fb_info *info = platform_get_drvdata(pdev);
238
239 unregister_framebuffer(info);
240 framebuffer_release(info);
241
242 return 0;
243}
244
245static const struct of_device_id simplefb_of_match[] = {
246 { .compatible = "simple-framebuffer", },
247 { },
248};
249MODULE_DEVICE_TABLE(of, simplefb_of_match);
250
251static struct platform_driver simplefb_driver = {
252 .driver = {
253 .name = "simple-framebuffer",
254 .owner = THIS_MODULE,
255 .of_match_table = simplefb_of_match,
256 },
257 .probe = simplefb_probe,
258 .remove = simplefb_remove,
259};
260module_platform_driver(simplefb_driver);
261
262MODULE_AUTHOR("Stephen Warren <swarren@wwwdotorg.org>");
263MODULE_DESCRIPTION("Simple framebuffer driver");
264MODULE_LICENSE("GPL v2");