blob: 129ab19732c7a1cbc98680c62044bd265a11be99 [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
David Herrmann498f6d32013-10-02 16:58:38 +020069static void simplefb_destroy(struct fb_info *info)
70{
71 if (info->screen_base)
72 iounmap(info->screen_base);
73}
74
Stephen Warren26549c82013-05-24 15:55:13 -070075static struct fb_ops simplefb_ops = {
76 .owner = THIS_MODULE,
David Herrmann498f6d32013-10-02 16:58:38 +020077 .fb_destroy = simplefb_destroy,
Stephen Warren26549c82013-05-24 15:55:13 -070078 .fb_setcolreg = simplefb_setcolreg,
79 .fb_fillrect = cfb_fillrect,
80 .fb_copyarea = cfb_copyarea,
81 .fb_imageblit = cfb_imageblit,
82};
83
David Herrmann5ef76da2013-08-02 14:05:20 +020084static struct simplefb_format simplefb_formats[] = SIMPLEFB_FORMATS;
Stephen Warren26549c82013-05-24 15:55:13 -070085
86struct simplefb_params {
87 u32 width;
88 u32 height;
89 u32 stride;
90 struct simplefb_format *format;
91};
92
93static 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
David Herrmann5ef76da2013-08-02 14:05:20 +0200139static int simplefb_parse_pd(struct platform_device *pdev,
140 struct simplefb_params *params)
141{
Jingoo Han129f1be2013-09-17 14:11:04 +0900142 struct simplefb_platform_data *pd = dev_get_platdata(&pdev->dev);
David Herrmann5ef76da2013-08-02 14:05:20 +0200143 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
Stephen Warren26549c82013-05-24 15:55:13 -0700166static 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
David Herrmann5ef76da2013-08-02 14:05:20 +0200176 ret = -ENODEV;
Jingoo Han129f1be2013-09-17 14:11:04 +0900177 if (dev_get_platdata(&pdev->dev))
David Herrmann5ef76da2013-08-02 14:05:20 +0200178 ret = simplefb_parse_pd(pdev, &params);
179 else if (pdev->dev.of_node)
180 ret = simplefb_parse_dt(pdev, &params);
181
Stephen Warren26549c82013-05-24 15:55:13 -0700182 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
David Herrmanndf0960a2013-08-02 14:05:21 +0200212 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
Stephen Warren26549c82013-05-24 15:55:13 -0700220 info->fbops = &simplefb_ops;
David Herrmanndf0960a2013-08-02 14:05:21 +0200221 info->flags = FBINFO_DEFAULT | FBINFO_MISC_FIRMWARE;
David Herrmann498f6d32013-10-02 16:58:38 +0200222 info->screen_base = ioremap(info->fix.smem_start,
223 info->fix.smem_len);
Stephen Warren26549c82013-05-24 15:55:13 -0700224 if (!info->screen_base) {
225 framebuffer_release(info);
226 return -ENODEV;
227 }
228 info->pseudo_palette = (void *)(info + 1);
229
Tom Gundersen9f192a92013-09-07 16:08:35 +0200230 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
Stephen Warren26549c82013-05-24 15:55:13 -0700238 ret = register_framebuffer(info);
239 if (ret < 0) {
240 dev_err(&pdev->dev, "Unable to register simplefb: %d\n", ret);
David Herrmann498f6d32013-10-02 16:58:38 +0200241 iounmap(info->screen_base);
Stephen Warren26549c82013-05-24 15:55:13 -0700242 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
251static 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
261static const struct of_device_id simplefb_of_match[] = {
262 { .compatible = "simple-framebuffer", },
263 { },
264};
265MODULE_DEVICE_TABLE(of, simplefb_of_match);
266
267static 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};
276module_platform_driver(simplefb_driver);
277
278MODULE_AUTHOR("Stephen Warren <swarren@wwwdotorg.org>");
279MODULE_DESCRIPTION("Simple framebuffer driver");
280MODULE_LICENSE("GPL v2");