blob: e2e9e3e61b72ad5dc72f0cd3cde1aeb1c36a2b2a [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>
27#include <linux/platform_device.h>
28
29static 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
36static struct fb_var_screeninfo simplefb_var = {
37 .height = -1,
38 .width = -1,
39 .activate = FB_ACTIVATE_NOW,
40 .vmode = FB_VMODE_NONINTERLACED,
41};
42
43static 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
68static 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
76struct 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
85static struct simplefb_format simplefb_formats[] = {
86 { "r5g6b5", 16, {11, 5}, {5, 6}, {0, 5}, {0, 0} },
87};
88
89struct simplefb_params {
90 u32 width;
91 u32 height;
92 u32 stride;
93 struct simplefb_format *format;
94};
95
96static 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
142static 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
205static 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
215static const struct of_device_id simplefb_of_match[] = {
216 { .compatible = "simple-framebuffer", },
217 { },
218};
219MODULE_DEVICE_TABLE(of, simplefb_of_match);
220
221static 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};
230module_platform_driver(simplefb_driver);
231
232MODULE_AUTHOR("Stephen Warren <swarren@wwwdotorg.org>");
233MODULE_DESCRIPTION("Simple framebuffer driver");
234MODULE_LICENSE("GPL v2");