xref: /linux/drivers/video/fbdev/simplefb.c (revision 3932b9ca55b0be314a36d3e84faff3e823c081f5)
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");
281