1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Simplest possible simple frame-buffer driver, as a platform device
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (c) 2013, Stephen Warren
6*4882a593Smuzhiyun *
7*4882a593Smuzhiyun * Based on q40fb.c, which was:
8*4882a593Smuzhiyun * Copyright (C) 2001 Richard Zidlicky <rz@linux-m68k.org>
9*4882a593Smuzhiyun *
10*4882a593Smuzhiyun * Also based on offb.c, which was:
11*4882a593Smuzhiyun * Copyright (C) 1997 Geert Uytterhoeven
12*4882a593Smuzhiyun * Copyright (C) 1996 Paul Mackerras
13*4882a593Smuzhiyun */
14*4882a593Smuzhiyun
15*4882a593Smuzhiyun #include <linux/errno.h>
16*4882a593Smuzhiyun #include <linux/fb.h>
17*4882a593Smuzhiyun #include <linux/io.h>
18*4882a593Smuzhiyun #include <linux/module.h>
19*4882a593Smuzhiyun #include <linux/platform_data/simplefb.h>
20*4882a593Smuzhiyun #include <linux/platform_device.h>
21*4882a593Smuzhiyun #include <linux/clk.h>
22*4882a593Smuzhiyun #include <linux/of.h>
23*4882a593Smuzhiyun #include <linux/of_clk.h>
24*4882a593Smuzhiyun #include <linux/of_platform.h>
25*4882a593Smuzhiyun #include <linux/parser.h>
26*4882a593Smuzhiyun #include <linux/regulator/consumer.h>
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun static const struct fb_fix_screeninfo simplefb_fix = {
29*4882a593Smuzhiyun .id = "simple",
30*4882a593Smuzhiyun .type = FB_TYPE_PACKED_PIXELS,
31*4882a593Smuzhiyun .visual = FB_VISUAL_TRUECOLOR,
32*4882a593Smuzhiyun .accel = FB_ACCEL_NONE,
33*4882a593Smuzhiyun };
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun static const struct fb_var_screeninfo simplefb_var = {
36*4882a593Smuzhiyun .height = -1,
37*4882a593Smuzhiyun .width = -1,
38*4882a593Smuzhiyun .activate = FB_ACTIVATE_NOW,
39*4882a593Smuzhiyun .vmode = FB_VMODE_NONINTERLACED,
40*4882a593Smuzhiyun };
41*4882a593Smuzhiyun
42*4882a593Smuzhiyun #define PSEUDO_PALETTE_SIZE 16
43*4882a593Smuzhiyun
simplefb_setcolreg(u_int regno,u_int red,u_int green,u_int blue,u_int transp,struct fb_info * info)44*4882a593Smuzhiyun static int simplefb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
45*4882a593Smuzhiyun u_int transp, struct fb_info *info)
46*4882a593Smuzhiyun {
47*4882a593Smuzhiyun u32 *pal = info->pseudo_palette;
48*4882a593Smuzhiyun u32 cr = red >> (16 - info->var.red.length);
49*4882a593Smuzhiyun u32 cg = green >> (16 - info->var.green.length);
50*4882a593Smuzhiyun u32 cb = blue >> (16 - info->var.blue.length);
51*4882a593Smuzhiyun u32 value;
52*4882a593Smuzhiyun
53*4882a593Smuzhiyun if (regno >= PSEUDO_PALETTE_SIZE)
54*4882a593Smuzhiyun return -EINVAL;
55*4882a593Smuzhiyun
56*4882a593Smuzhiyun value = (cr << info->var.red.offset) |
57*4882a593Smuzhiyun (cg << info->var.green.offset) |
58*4882a593Smuzhiyun (cb << info->var.blue.offset);
59*4882a593Smuzhiyun if (info->var.transp.length > 0) {
60*4882a593Smuzhiyun u32 mask = (1 << info->var.transp.length) - 1;
61*4882a593Smuzhiyun mask <<= info->var.transp.offset;
62*4882a593Smuzhiyun value |= mask;
63*4882a593Smuzhiyun }
64*4882a593Smuzhiyun pal[regno] = value;
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun return 0;
67*4882a593Smuzhiyun }
68*4882a593Smuzhiyun
69*4882a593Smuzhiyun struct simplefb_par;
70*4882a593Smuzhiyun static void simplefb_clocks_destroy(struct simplefb_par *par);
71*4882a593Smuzhiyun static void simplefb_regulators_destroy(struct simplefb_par *par);
72*4882a593Smuzhiyun
simplefb_destroy(struct fb_info * info)73*4882a593Smuzhiyun static void simplefb_destroy(struct fb_info *info)
74*4882a593Smuzhiyun {
75*4882a593Smuzhiyun simplefb_regulators_destroy(info->par);
76*4882a593Smuzhiyun simplefb_clocks_destroy(info->par);
77*4882a593Smuzhiyun if (info->screen_base)
78*4882a593Smuzhiyun iounmap(info->screen_base);
79*4882a593Smuzhiyun }
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun static const struct fb_ops simplefb_ops = {
82*4882a593Smuzhiyun .owner = THIS_MODULE,
83*4882a593Smuzhiyun .fb_destroy = simplefb_destroy,
84*4882a593Smuzhiyun .fb_setcolreg = simplefb_setcolreg,
85*4882a593Smuzhiyun .fb_fillrect = cfb_fillrect,
86*4882a593Smuzhiyun .fb_copyarea = cfb_copyarea,
87*4882a593Smuzhiyun .fb_imageblit = cfb_imageblit,
88*4882a593Smuzhiyun };
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun static struct simplefb_format simplefb_formats[] = SIMPLEFB_FORMATS;
91*4882a593Smuzhiyun
92*4882a593Smuzhiyun struct simplefb_params {
93*4882a593Smuzhiyun u32 width;
94*4882a593Smuzhiyun u32 height;
95*4882a593Smuzhiyun u32 stride;
96*4882a593Smuzhiyun struct simplefb_format *format;
97*4882a593Smuzhiyun };
98*4882a593Smuzhiyun
simplefb_parse_dt(struct platform_device * pdev,struct simplefb_params * params)99*4882a593Smuzhiyun static int simplefb_parse_dt(struct platform_device *pdev,
100*4882a593Smuzhiyun struct simplefb_params *params)
101*4882a593Smuzhiyun {
102*4882a593Smuzhiyun struct device_node *np = pdev->dev.of_node;
103*4882a593Smuzhiyun int ret;
104*4882a593Smuzhiyun const char *format;
105*4882a593Smuzhiyun int i;
106*4882a593Smuzhiyun
107*4882a593Smuzhiyun ret = of_property_read_u32(np, "width", ¶ms->width);
108*4882a593Smuzhiyun if (ret) {
109*4882a593Smuzhiyun dev_err(&pdev->dev, "Can't parse width property\n");
110*4882a593Smuzhiyun return ret;
111*4882a593Smuzhiyun }
112*4882a593Smuzhiyun
113*4882a593Smuzhiyun ret = of_property_read_u32(np, "height", ¶ms->height);
114*4882a593Smuzhiyun if (ret) {
115*4882a593Smuzhiyun dev_err(&pdev->dev, "Can't parse height property\n");
116*4882a593Smuzhiyun return ret;
117*4882a593Smuzhiyun }
118*4882a593Smuzhiyun
119*4882a593Smuzhiyun ret = of_property_read_u32(np, "stride", ¶ms->stride);
120*4882a593Smuzhiyun if (ret) {
121*4882a593Smuzhiyun dev_err(&pdev->dev, "Can't parse stride property\n");
122*4882a593Smuzhiyun return ret;
123*4882a593Smuzhiyun }
124*4882a593Smuzhiyun
125*4882a593Smuzhiyun ret = of_property_read_string(np, "format", &format);
126*4882a593Smuzhiyun if (ret) {
127*4882a593Smuzhiyun dev_err(&pdev->dev, "Can't parse format property\n");
128*4882a593Smuzhiyun return ret;
129*4882a593Smuzhiyun }
130*4882a593Smuzhiyun params->format = NULL;
131*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(simplefb_formats); i++) {
132*4882a593Smuzhiyun if (strcmp(format, simplefb_formats[i].name))
133*4882a593Smuzhiyun continue;
134*4882a593Smuzhiyun params->format = &simplefb_formats[i];
135*4882a593Smuzhiyun break;
136*4882a593Smuzhiyun }
137*4882a593Smuzhiyun if (!params->format) {
138*4882a593Smuzhiyun dev_err(&pdev->dev, "Invalid format value\n");
139*4882a593Smuzhiyun return -EINVAL;
140*4882a593Smuzhiyun }
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun return 0;
143*4882a593Smuzhiyun }
144*4882a593Smuzhiyun
simplefb_parse_pd(struct platform_device * pdev,struct simplefb_params * params)145*4882a593Smuzhiyun static int simplefb_parse_pd(struct platform_device *pdev,
146*4882a593Smuzhiyun struct simplefb_params *params)
147*4882a593Smuzhiyun {
148*4882a593Smuzhiyun struct simplefb_platform_data *pd = dev_get_platdata(&pdev->dev);
149*4882a593Smuzhiyun int i;
150*4882a593Smuzhiyun
151*4882a593Smuzhiyun params->width = pd->width;
152*4882a593Smuzhiyun params->height = pd->height;
153*4882a593Smuzhiyun params->stride = pd->stride;
154*4882a593Smuzhiyun
155*4882a593Smuzhiyun params->format = NULL;
156*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(simplefb_formats); i++) {
157*4882a593Smuzhiyun if (strcmp(pd->format, simplefb_formats[i].name))
158*4882a593Smuzhiyun continue;
159*4882a593Smuzhiyun
160*4882a593Smuzhiyun params->format = &simplefb_formats[i];
161*4882a593Smuzhiyun break;
162*4882a593Smuzhiyun }
163*4882a593Smuzhiyun
164*4882a593Smuzhiyun if (!params->format) {
165*4882a593Smuzhiyun dev_err(&pdev->dev, "Invalid format value\n");
166*4882a593Smuzhiyun return -EINVAL;
167*4882a593Smuzhiyun }
168*4882a593Smuzhiyun
169*4882a593Smuzhiyun return 0;
170*4882a593Smuzhiyun }
171*4882a593Smuzhiyun
172*4882a593Smuzhiyun struct simplefb_par {
173*4882a593Smuzhiyun u32 palette[PSEUDO_PALETTE_SIZE];
174*4882a593Smuzhiyun #if defined CONFIG_OF && defined CONFIG_COMMON_CLK
175*4882a593Smuzhiyun bool clks_enabled;
176*4882a593Smuzhiyun unsigned int clk_count;
177*4882a593Smuzhiyun struct clk **clks;
178*4882a593Smuzhiyun #endif
179*4882a593Smuzhiyun #if defined CONFIG_OF && defined CONFIG_REGULATOR
180*4882a593Smuzhiyun bool regulators_enabled;
181*4882a593Smuzhiyun u32 regulator_count;
182*4882a593Smuzhiyun struct regulator **regulators;
183*4882a593Smuzhiyun #endif
184*4882a593Smuzhiyun };
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun #if defined CONFIG_OF && defined CONFIG_COMMON_CLK
187*4882a593Smuzhiyun /*
188*4882a593Smuzhiyun * Clock handling code.
189*4882a593Smuzhiyun *
190*4882a593Smuzhiyun * Here we handle the clocks property of our "simple-framebuffer" dt node.
191*4882a593Smuzhiyun * This is necessary so that we can make sure that any clocks needed by
192*4882a593Smuzhiyun * the display engine that the bootloader set up for us (and for which it
193*4882a593Smuzhiyun * provided a simplefb dt node), stay up, for the life of the simplefb
194*4882a593Smuzhiyun * driver.
195*4882a593Smuzhiyun *
196*4882a593Smuzhiyun * When the driver unloads, we cleanly disable, and then release the clocks.
197*4882a593Smuzhiyun *
198*4882a593Smuzhiyun * We only complain about errors here, no action is taken as the most likely
199*4882a593Smuzhiyun * error can only happen due to a mismatch between the bootloader which set
200*4882a593Smuzhiyun * up simplefb, and the clock definitions in the device tree. Chances are
201*4882a593Smuzhiyun * that there are no adverse effects, and if there are, a clean teardown of
202*4882a593Smuzhiyun * the fb probe will not help us much either. So just complain and carry on,
203*4882a593Smuzhiyun * and hope that the user actually gets a working fb at the end of things.
204*4882a593Smuzhiyun */
simplefb_clocks_get(struct simplefb_par * par,struct platform_device * pdev)205*4882a593Smuzhiyun static int simplefb_clocks_get(struct simplefb_par *par,
206*4882a593Smuzhiyun struct platform_device *pdev)
207*4882a593Smuzhiyun {
208*4882a593Smuzhiyun struct device_node *np = pdev->dev.of_node;
209*4882a593Smuzhiyun struct clk *clock;
210*4882a593Smuzhiyun int i;
211*4882a593Smuzhiyun
212*4882a593Smuzhiyun if (dev_get_platdata(&pdev->dev) || !np)
213*4882a593Smuzhiyun return 0;
214*4882a593Smuzhiyun
215*4882a593Smuzhiyun par->clk_count = of_clk_get_parent_count(np);
216*4882a593Smuzhiyun if (!par->clk_count)
217*4882a593Smuzhiyun return 0;
218*4882a593Smuzhiyun
219*4882a593Smuzhiyun par->clks = kcalloc(par->clk_count, sizeof(struct clk *), GFP_KERNEL);
220*4882a593Smuzhiyun if (!par->clks)
221*4882a593Smuzhiyun return -ENOMEM;
222*4882a593Smuzhiyun
223*4882a593Smuzhiyun for (i = 0; i < par->clk_count; i++) {
224*4882a593Smuzhiyun clock = of_clk_get(np, i);
225*4882a593Smuzhiyun if (IS_ERR(clock)) {
226*4882a593Smuzhiyun if (PTR_ERR(clock) == -EPROBE_DEFER) {
227*4882a593Smuzhiyun while (--i >= 0) {
228*4882a593Smuzhiyun if (par->clks[i])
229*4882a593Smuzhiyun clk_put(par->clks[i]);
230*4882a593Smuzhiyun }
231*4882a593Smuzhiyun kfree(par->clks);
232*4882a593Smuzhiyun return -EPROBE_DEFER;
233*4882a593Smuzhiyun }
234*4882a593Smuzhiyun dev_err(&pdev->dev, "%s: clock %d not found: %ld\n",
235*4882a593Smuzhiyun __func__, i, PTR_ERR(clock));
236*4882a593Smuzhiyun continue;
237*4882a593Smuzhiyun }
238*4882a593Smuzhiyun par->clks[i] = clock;
239*4882a593Smuzhiyun }
240*4882a593Smuzhiyun
241*4882a593Smuzhiyun return 0;
242*4882a593Smuzhiyun }
243*4882a593Smuzhiyun
simplefb_clocks_enable(struct simplefb_par * par,struct platform_device * pdev)244*4882a593Smuzhiyun static void simplefb_clocks_enable(struct simplefb_par *par,
245*4882a593Smuzhiyun struct platform_device *pdev)
246*4882a593Smuzhiyun {
247*4882a593Smuzhiyun int i, ret;
248*4882a593Smuzhiyun
249*4882a593Smuzhiyun for (i = 0; i < par->clk_count; i++) {
250*4882a593Smuzhiyun if (par->clks[i]) {
251*4882a593Smuzhiyun ret = clk_prepare_enable(par->clks[i]);
252*4882a593Smuzhiyun if (ret) {
253*4882a593Smuzhiyun dev_err(&pdev->dev,
254*4882a593Smuzhiyun "%s: failed to enable clock %d: %d\n",
255*4882a593Smuzhiyun __func__, i, ret);
256*4882a593Smuzhiyun clk_put(par->clks[i]);
257*4882a593Smuzhiyun par->clks[i] = NULL;
258*4882a593Smuzhiyun }
259*4882a593Smuzhiyun }
260*4882a593Smuzhiyun }
261*4882a593Smuzhiyun par->clks_enabled = true;
262*4882a593Smuzhiyun }
263*4882a593Smuzhiyun
simplefb_clocks_destroy(struct simplefb_par * par)264*4882a593Smuzhiyun static void simplefb_clocks_destroy(struct simplefb_par *par)
265*4882a593Smuzhiyun {
266*4882a593Smuzhiyun int i;
267*4882a593Smuzhiyun
268*4882a593Smuzhiyun if (!par->clks)
269*4882a593Smuzhiyun return;
270*4882a593Smuzhiyun
271*4882a593Smuzhiyun for (i = 0; i < par->clk_count; i++) {
272*4882a593Smuzhiyun if (par->clks[i]) {
273*4882a593Smuzhiyun if (par->clks_enabled)
274*4882a593Smuzhiyun clk_disable_unprepare(par->clks[i]);
275*4882a593Smuzhiyun clk_put(par->clks[i]);
276*4882a593Smuzhiyun }
277*4882a593Smuzhiyun }
278*4882a593Smuzhiyun
279*4882a593Smuzhiyun kfree(par->clks);
280*4882a593Smuzhiyun }
281*4882a593Smuzhiyun #else
simplefb_clocks_get(struct simplefb_par * par,struct platform_device * pdev)282*4882a593Smuzhiyun static int simplefb_clocks_get(struct simplefb_par *par,
283*4882a593Smuzhiyun struct platform_device *pdev) { return 0; }
simplefb_clocks_enable(struct simplefb_par * par,struct platform_device * pdev)284*4882a593Smuzhiyun static void simplefb_clocks_enable(struct simplefb_par *par,
285*4882a593Smuzhiyun struct platform_device *pdev) { }
simplefb_clocks_destroy(struct simplefb_par * par)286*4882a593Smuzhiyun static void simplefb_clocks_destroy(struct simplefb_par *par) { }
287*4882a593Smuzhiyun #endif
288*4882a593Smuzhiyun
289*4882a593Smuzhiyun #if defined CONFIG_OF && defined CONFIG_REGULATOR
290*4882a593Smuzhiyun
291*4882a593Smuzhiyun #define SUPPLY_SUFFIX "-supply"
292*4882a593Smuzhiyun
293*4882a593Smuzhiyun /*
294*4882a593Smuzhiyun * Regulator handling code.
295*4882a593Smuzhiyun *
296*4882a593Smuzhiyun * Here we handle the num-supplies and vin*-supply properties of our
297*4882a593Smuzhiyun * "simple-framebuffer" dt node. This is necessary so that we can make sure
298*4882a593Smuzhiyun * that any regulators needed by the display hardware that the bootloader
299*4882a593Smuzhiyun * set up for us (and for which it provided a simplefb dt node), stay up,
300*4882a593Smuzhiyun * for the life of the simplefb driver.
301*4882a593Smuzhiyun *
302*4882a593Smuzhiyun * When the driver unloads, we cleanly disable, and then release the
303*4882a593Smuzhiyun * regulators.
304*4882a593Smuzhiyun *
305*4882a593Smuzhiyun * We only complain about errors here, no action is taken as the most likely
306*4882a593Smuzhiyun * error can only happen due to a mismatch between the bootloader which set
307*4882a593Smuzhiyun * up simplefb, and the regulator definitions in the device tree. Chances are
308*4882a593Smuzhiyun * that there are no adverse effects, and if there are, a clean teardown of
309*4882a593Smuzhiyun * the fb probe will not help us much either. So just complain and carry on,
310*4882a593Smuzhiyun * and hope that the user actually gets a working fb at the end of things.
311*4882a593Smuzhiyun */
simplefb_regulators_get(struct simplefb_par * par,struct platform_device * pdev)312*4882a593Smuzhiyun static int simplefb_regulators_get(struct simplefb_par *par,
313*4882a593Smuzhiyun struct platform_device *pdev)
314*4882a593Smuzhiyun {
315*4882a593Smuzhiyun struct device_node *np = pdev->dev.of_node;
316*4882a593Smuzhiyun struct property *prop;
317*4882a593Smuzhiyun struct regulator *regulator;
318*4882a593Smuzhiyun const char *p;
319*4882a593Smuzhiyun int count = 0, i = 0;
320*4882a593Smuzhiyun
321*4882a593Smuzhiyun if (dev_get_platdata(&pdev->dev) || !np)
322*4882a593Smuzhiyun return 0;
323*4882a593Smuzhiyun
324*4882a593Smuzhiyun /* Count the number of regulator supplies */
325*4882a593Smuzhiyun for_each_property_of_node(np, prop) {
326*4882a593Smuzhiyun p = strstr(prop->name, SUPPLY_SUFFIX);
327*4882a593Smuzhiyun if (p && p != prop->name)
328*4882a593Smuzhiyun count++;
329*4882a593Smuzhiyun }
330*4882a593Smuzhiyun
331*4882a593Smuzhiyun if (!count)
332*4882a593Smuzhiyun return 0;
333*4882a593Smuzhiyun
334*4882a593Smuzhiyun par->regulators = devm_kcalloc(&pdev->dev, count,
335*4882a593Smuzhiyun sizeof(struct regulator *), GFP_KERNEL);
336*4882a593Smuzhiyun if (!par->regulators)
337*4882a593Smuzhiyun return -ENOMEM;
338*4882a593Smuzhiyun
339*4882a593Smuzhiyun /* Get all the regulators */
340*4882a593Smuzhiyun for_each_property_of_node(np, prop) {
341*4882a593Smuzhiyun char name[32]; /* 32 is max size of property name */
342*4882a593Smuzhiyun
343*4882a593Smuzhiyun p = strstr(prop->name, SUPPLY_SUFFIX);
344*4882a593Smuzhiyun if (!p || p == prop->name)
345*4882a593Smuzhiyun continue;
346*4882a593Smuzhiyun
347*4882a593Smuzhiyun strlcpy(name, prop->name,
348*4882a593Smuzhiyun strlen(prop->name) - strlen(SUPPLY_SUFFIX) + 1);
349*4882a593Smuzhiyun regulator = devm_regulator_get_optional(&pdev->dev, name);
350*4882a593Smuzhiyun if (IS_ERR(regulator)) {
351*4882a593Smuzhiyun if (PTR_ERR(regulator) == -EPROBE_DEFER)
352*4882a593Smuzhiyun return -EPROBE_DEFER;
353*4882a593Smuzhiyun dev_err(&pdev->dev, "regulator %s not found: %ld\n",
354*4882a593Smuzhiyun name, PTR_ERR(regulator));
355*4882a593Smuzhiyun continue;
356*4882a593Smuzhiyun }
357*4882a593Smuzhiyun par->regulators[i++] = regulator;
358*4882a593Smuzhiyun }
359*4882a593Smuzhiyun par->regulator_count = i;
360*4882a593Smuzhiyun
361*4882a593Smuzhiyun return 0;
362*4882a593Smuzhiyun }
363*4882a593Smuzhiyun
simplefb_regulators_enable(struct simplefb_par * par,struct platform_device * pdev)364*4882a593Smuzhiyun static void simplefb_regulators_enable(struct simplefb_par *par,
365*4882a593Smuzhiyun struct platform_device *pdev)
366*4882a593Smuzhiyun {
367*4882a593Smuzhiyun int i, ret;
368*4882a593Smuzhiyun
369*4882a593Smuzhiyun /* Enable all the regulators */
370*4882a593Smuzhiyun for (i = 0; i < par->regulator_count; i++) {
371*4882a593Smuzhiyun ret = regulator_enable(par->regulators[i]);
372*4882a593Smuzhiyun if (ret) {
373*4882a593Smuzhiyun dev_err(&pdev->dev,
374*4882a593Smuzhiyun "failed to enable regulator %d: %d\n",
375*4882a593Smuzhiyun i, ret);
376*4882a593Smuzhiyun devm_regulator_put(par->regulators[i]);
377*4882a593Smuzhiyun par->regulators[i] = NULL;
378*4882a593Smuzhiyun }
379*4882a593Smuzhiyun }
380*4882a593Smuzhiyun par->regulators_enabled = true;
381*4882a593Smuzhiyun }
382*4882a593Smuzhiyun
simplefb_regulators_destroy(struct simplefb_par * par)383*4882a593Smuzhiyun static void simplefb_regulators_destroy(struct simplefb_par *par)
384*4882a593Smuzhiyun {
385*4882a593Smuzhiyun int i;
386*4882a593Smuzhiyun
387*4882a593Smuzhiyun if (!par->regulators || !par->regulators_enabled)
388*4882a593Smuzhiyun return;
389*4882a593Smuzhiyun
390*4882a593Smuzhiyun for (i = 0; i < par->regulator_count; i++)
391*4882a593Smuzhiyun if (par->regulators[i])
392*4882a593Smuzhiyun regulator_disable(par->regulators[i]);
393*4882a593Smuzhiyun }
394*4882a593Smuzhiyun #else
simplefb_regulators_get(struct simplefb_par * par,struct platform_device * pdev)395*4882a593Smuzhiyun static int simplefb_regulators_get(struct simplefb_par *par,
396*4882a593Smuzhiyun struct platform_device *pdev) { return 0; }
simplefb_regulators_enable(struct simplefb_par * par,struct platform_device * pdev)397*4882a593Smuzhiyun static void simplefb_regulators_enable(struct simplefb_par *par,
398*4882a593Smuzhiyun struct platform_device *pdev) { }
simplefb_regulators_destroy(struct simplefb_par * par)399*4882a593Smuzhiyun static void simplefb_regulators_destroy(struct simplefb_par *par) { }
400*4882a593Smuzhiyun #endif
401*4882a593Smuzhiyun
simplefb_probe(struct platform_device * pdev)402*4882a593Smuzhiyun static int simplefb_probe(struct platform_device *pdev)
403*4882a593Smuzhiyun {
404*4882a593Smuzhiyun int ret;
405*4882a593Smuzhiyun struct simplefb_params params;
406*4882a593Smuzhiyun struct fb_info *info;
407*4882a593Smuzhiyun struct simplefb_par *par;
408*4882a593Smuzhiyun struct resource *mem;
409*4882a593Smuzhiyun
410*4882a593Smuzhiyun if (fb_get_options("simplefb", NULL))
411*4882a593Smuzhiyun return -ENODEV;
412*4882a593Smuzhiyun
413*4882a593Smuzhiyun ret = -ENODEV;
414*4882a593Smuzhiyun if (dev_get_platdata(&pdev->dev))
415*4882a593Smuzhiyun ret = simplefb_parse_pd(pdev, ¶ms);
416*4882a593Smuzhiyun else if (pdev->dev.of_node)
417*4882a593Smuzhiyun ret = simplefb_parse_dt(pdev, ¶ms);
418*4882a593Smuzhiyun
419*4882a593Smuzhiyun if (ret)
420*4882a593Smuzhiyun return ret;
421*4882a593Smuzhiyun
422*4882a593Smuzhiyun mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
423*4882a593Smuzhiyun if (!mem) {
424*4882a593Smuzhiyun dev_err(&pdev->dev, "No memory resource\n");
425*4882a593Smuzhiyun return -EINVAL;
426*4882a593Smuzhiyun }
427*4882a593Smuzhiyun
428*4882a593Smuzhiyun info = framebuffer_alloc(sizeof(struct simplefb_par), &pdev->dev);
429*4882a593Smuzhiyun if (!info)
430*4882a593Smuzhiyun return -ENOMEM;
431*4882a593Smuzhiyun platform_set_drvdata(pdev, info);
432*4882a593Smuzhiyun
433*4882a593Smuzhiyun par = info->par;
434*4882a593Smuzhiyun
435*4882a593Smuzhiyun info->fix = simplefb_fix;
436*4882a593Smuzhiyun info->fix.smem_start = mem->start;
437*4882a593Smuzhiyun info->fix.smem_len = resource_size(mem);
438*4882a593Smuzhiyun info->fix.line_length = params.stride;
439*4882a593Smuzhiyun
440*4882a593Smuzhiyun info->var = simplefb_var;
441*4882a593Smuzhiyun info->var.xres = params.width;
442*4882a593Smuzhiyun info->var.yres = params.height;
443*4882a593Smuzhiyun info->var.xres_virtual = params.width;
444*4882a593Smuzhiyun info->var.yres_virtual = params.height;
445*4882a593Smuzhiyun info->var.bits_per_pixel = params.format->bits_per_pixel;
446*4882a593Smuzhiyun info->var.red = params.format->red;
447*4882a593Smuzhiyun info->var.green = params.format->green;
448*4882a593Smuzhiyun info->var.blue = params.format->blue;
449*4882a593Smuzhiyun info->var.transp = params.format->transp;
450*4882a593Smuzhiyun
451*4882a593Smuzhiyun info->apertures = alloc_apertures(1);
452*4882a593Smuzhiyun if (!info->apertures) {
453*4882a593Smuzhiyun ret = -ENOMEM;
454*4882a593Smuzhiyun goto error_fb_release;
455*4882a593Smuzhiyun }
456*4882a593Smuzhiyun info->apertures->ranges[0].base = info->fix.smem_start;
457*4882a593Smuzhiyun info->apertures->ranges[0].size = info->fix.smem_len;
458*4882a593Smuzhiyun
459*4882a593Smuzhiyun info->fbops = &simplefb_ops;
460*4882a593Smuzhiyun info->flags = FBINFO_DEFAULT | FBINFO_MISC_FIRMWARE;
461*4882a593Smuzhiyun info->screen_base = ioremap_wc(info->fix.smem_start,
462*4882a593Smuzhiyun info->fix.smem_len);
463*4882a593Smuzhiyun if (!info->screen_base) {
464*4882a593Smuzhiyun ret = -ENOMEM;
465*4882a593Smuzhiyun goto error_fb_release;
466*4882a593Smuzhiyun }
467*4882a593Smuzhiyun info->pseudo_palette = par->palette;
468*4882a593Smuzhiyun
469*4882a593Smuzhiyun ret = simplefb_clocks_get(par, pdev);
470*4882a593Smuzhiyun if (ret < 0)
471*4882a593Smuzhiyun goto error_unmap;
472*4882a593Smuzhiyun
473*4882a593Smuzhiyun ret = simplefb_regulators_get(par, pdev);
474*4882a593Smuzhiyun if (ret < 0)
475*4882a593Smuzhiyun goto error_clocks;
476*4882a593Smuzhiyun
477*4882a593Smuzhiyun simplefb_clocks_enable(par, pdev);
478*4882a593Smuzhiyun simplefb_regulators_enable(par, pdev);
479*4882a593Smuzhiyun
480*4882a593Smuzhiyun dev_info(&pdev->dev, "framebuffer at 0x%lx, 0x%x bytes, mapped to 0x%p\n",
481*4882a593Smuzhiyun info->fix.smem_start, info->fix.smem_len,
482*4882a593Smuzhiyun info->screen_base);
483*4882a593Smuzhiyun dev_info(&pdev->dev, "format=%s, mode=%dx%dx%d, linelength=%d\n",
484*4882a593Smuzhiyun params.format->name,
485*4882a593Smuzhiyun info->var.xres, info->var.yres,
486*4882a593Smuzhiyun info->var.bits_per_pixel, info->fix.line_length);
487*4882a593Smuzhiyun
488*4882a593Smuzhiyun ret = register_framebuffer(info);
489*4882a593Smuzhiyun if (ret < 0) {
490*4882a593Smuzhiyun dev_err(&pdev->dev, "Unable to register simplefb: %d\n", ret);
491*4882a593Smuzhiyun goto error_regulators;
492*4882a593Smuzhiyun }
493*4882a593Smuzhiyun
494*4882a593Smuzhiyun dev_info(&pdev->dev, "fb%d: simplefb registered!\n", info->node);
495*4882a593Smuzhiyun
496*4882a593Smuzhiyun return 0;
497*4882a593Smuzhiyun
498*4882a593Smuzhiyun error_regulators:
499*4882a593Smuzhiyun simplefb_regulators_destroy(par);
500*4882a593Smuzhiyun error_clocks:
501*4882a593Smuzhiyun simplefb_clocks_destroy(par);
502*4882a593Smuzhiyun error_unmap:
503*4882a593Smuzhiyun iounmap(info->screen_base);
504*4882a593Smuzhiyun error_fb_release:
505*4882a593Smuzhiyun framebuffer_release(info);
506*4882a593Smuzhiyun return ret;
507*4882a593Smuzhiyun }
508*4882a593Smuzhiyun
simplefb_remove(struct platform_device * pdev)509*4882a593Smuzhiyun static int simplefb_remove(struct platform_device *pdev)
510*4882a593Smuzhiyun {
511*4882a593Smuzhiyun struct fb_info *info = platform_get_drvdata(pdev);
512*4882a593Smuzhiyun
513*4882a593Smuzhiyun unregister_framebuffer(info);
514*4882a593Smuzhiyun framebuffer_release(info);
515*4882a593Smuzhiyun
516*4882a593Smuzhiyun return 0;
517*4882a593Smuzhiyun }
518*4882a593Smuzhiyun
519*4882a593Smuzhiyun static const struct of_device_id simplefb_of_match[] = {
520*4882a593Smuzhiyun { .compatible = "simple-framebuffer", },
521*4882a593Smuzhiyun { },
522*4882a593Smuzhiyun };
523*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, simplefb_of_match);
524*4882a593Smuzhiyun
525*4882a593Smuzhiyun static struct platform_driver simplefb_driver = {
526*4882a593Smuzhiyun .driver = {
527*4882a593Smuzhiyun .name = "simple-framebuffer",
528*4882a593Smuzhiyun .of_match_table = simplefb_of_match,
529*4882a593Smuzhiyun },
530*4882a593Smuzhiyun .probe = simplefb_probe,
531*4882a593Smuzhiyun .remove = simplefb_remove,
532*4882a593Smuzhiyun };
533*4882a593Smuzhiyun
simplefb_init(void)534*4882a593Smuzhiyun static int __init simplefb_init(void)
535*4882a593Smuzhiyun {
536*4882a593Smuzhiyun int ret;
537*4882a593Smuzhiyun struct device_node *np;
538*4882a593Smuzhiyun
539*4882a593Smuzhiyun ret = platform_driver_register(&simplefb_driver);
540*4882a593Smuzhiyun if (ret)
541*4882a593Smuzhiyun return ret;
542*4882a593Smuzhiyun
543*4882a593Smuzhiyun if (IS_ENABLED(CONFIG_OF_ADDRESS) && of_chosen) {
544*4882a593Smuzhiyun for_each_child_of_node(of_chosen, np) {
545*4882a593Smuzhiyun if (of_device_is_compatible(np, "simple-framebuffer"))
546*4882a593Smuzhiyun of_platform_device_create(np, NULL, NULL);
547*4882a593Smuzhiyun }
548*4882a593Smuzhiyun }
549*4882a593Smuzhiyun
550*4882a593Smuzhiyun return 0;
551*4882a593Smuzhiyun }
552*4882a593Smuzhiyun
553*4882a593Smuzhiyun fs_initcall(simplefb_init);
554*4882a593Smuzhiyun
555*4882a593Smuzhiyun MODULE_AUTHOR("Stephen Warren <swarren@wwwdotorg.org>");
556*4882a593Smuzhiyun MODULE_DESCRIPTION("Simple framebuffer driver");
557*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
558