Commit | Line | Data |
---|---|---|
2025cf9e | 1 | // SPDX-License-Identifier: GPL-2.0-only |
26549c8d SW |
2 | /* |
3 | * Simplest possible simple frame-buffer driver, as a platform device | |
4 | * | |
5 | * Copyright (c) 2013, Stephen Warren | |
6 | * | |
7 | * Based on q40fb.c, which was: | |
8 | * Copyright (C) 2001 Richard Zidlicky <rz@linux-m68k.org> | |
9 | * | |
10 | * Also based on offb.c, which was: | |
11 | * Copyright (C) 1997 Geert Uytterhoeven | |
12 | * Copyright (C) 1996 Paul Mackerras | |
26549c8d SW |
13 | */ |
14 | ||
d9702b2a | 15 | #include <linux/aperture.h> |
26549c8d SW |
16 | #include <linux/errno.h> |
17 | #include <linux/fb.h> | |
18 | #include <linux/io.h> | |
19 | #include <linux/module.h> | |
5ef76da6 | 20 | #include <linux/platform_data/simplefb.h> |
26549c8d | 21 | #include <linux/platform_device.h> |
d49c6993 | 22 | #include <linux/clk.h> |
814740e7 | 23 | #include <linux/of.h> |
8ddfc01a | 24 | #include <linux/of_address.h> |
db3b4fd1 | 25 | #include <linux/of_clk.h> |
89c95001 | 26 | #include <linux/of_platform.h> |
814740e7 | 27 | #include <linux/parser.h> |
92a511a5 | 28 | #include <linux/pm_domain.h> |
814740e7 | 29 | #include <linux/regulator/consumer.h> |
26549c8d | 30 | |
ca9384c5 | 31 | static const struct fb_fix_screeninfo simplefb_fix = { |
26549c8d SW |
32 | .id = "simple", |
33 | .type = FB_TYPE_PACKED_PIXELS, | |
34 | .visual = FB_VISUAL_TRUECOLOR, | |
35 | .accel = FB_ACCEL_NONE, | |
36 | }; | |
37 | ||
ca9384c5 | 38 | static const struct fb_var_screeninfo simplefb_var = { |
26549c8d SW |
39 | .height = -1, |
40 | .width = -1, | |
41 | .activate = FB_ACTIVATE_NOW, | |
42 | .vmode = FB_VMODE_NONINTERLACED, | |
43 | }; | |
44 | ||
1270be4a LV |
45 | #define PSEUDO_PALETTE_SIZE 16 |
46 | ||
26549c8d SW |
47 | static int simplefb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, |
48 | u_int transp, struct fb_info *info) | |
49 | { | |
50 | u32 *pal = info->pseudo_palette; | |
51 | u32 cr = red >> (16 - info->var.red.length); | |
52 | u32 cg = green >> (16 - info->var.green.length); | |
53 | u32 cb = blue >> (16 - info->var.blue.length); | |
54 | u32 value; | |
55 | ||
1270be4a | 56 | if (regno >= PSEUDO_PALETTE_SIZE) |
26549c8d SW |
57 | return -EINVAL; |
58 | ||
59 | value = (cr << info->var.red.offset) | | |
60 | (cg << info->var.green.offset) | | |
61 | (cb << info->var.blue.offset); | |
62 | if (info->var.transp.length > 0) { | |
63 | u32 mask = (1 << info->var.transp.length) - 1; | |
64 | mask <<= info->var.transp.offset; | |
65 | value |= mask; | |
66 | } | |
67 | pal[regno] = value; | |
68 | ||
69 | return 0; | |
70 | } | |
71 | ||
748bd587 TZ |
72 | struct simplefb_par { |
73 | u32 palette[PSEUDO_PALETTE_SIZE]; | |
d9702b2a TZ |
74 | resource_size_t base; |
75 | resource_size_t size; | |
748bd587 TZ |
76 | struct resource *mem; |
77 | #if defined CONFIG_OF && defined CONFIG_COMMON_CLK | |
78 | bool clks_enabled; | |
79 | unsigned int clk_count; | |
80 | struct clk **clks; | |
81 | #endif | |
92a511a5 TR |
82 | #if defined CONFIG_OF && defined CONFIG_PM_GENERIC_DOMAINS |
83 | unsigned int num_genpds; | |
84 | struct device **genpds; | |
85 | struct device_link **genpd_links; | |
86 | #endif | |
748bd587 TZ |
87 | #if defined CONFIG_OF && defined CONFIG_REGULATOR |
88 | bool regulators_enabled; | |
89 | u32 regulator_count; | |
90 | struct regulator **regulators; | |
91 | #endif | |
92 | }; | |
93 | ||
7c9806e8 CYT |
94 | static void simplefb_clocks_destroy(struct simplefb_par *par); |
95 | static void simplefb_regulators_destroy(struct simplefb_par *par); | |
96 | ||
666b90b3 JMC |
97 | /* |
98 | * fb_ops.fb_destroy is called by the last put_fb_info() call at the end | |
99 | * of unregister_framebuffer() or fb_release(). Do any cleanup here. | |
100 | */ | |
498f6d36 DH |
101 | static void simplefb_destroy(struct fb_info *info) |
102 | { | |
748bd587 TZ |
103 | struct simplefb_par *par = info->par; |
104 | struct resource *mem = par->mem; | |
105 | ||
7c9806e8 CYT |
106 | simplefb_regulators_destroy(info->par); |
107 | simplefb_clocks_destroy(info->par); | |
498f6d36 DH |
108 | if (info->screen_base) |
109 | iounmap(info->screen_base); | |
748bd587 | 110 | |
666b90b3 JMC |
111 | framebuffer_release(info); |
112 | ||
748bd587 TZ |
113 | if (mem) |
114 | release_mem_region(mem->start, resource_size(mem)); | |
498f6d36 DH |
115 | } |
116 | ||
8a48ac33 | 117 | static const struct fb_ops simplefb_ops = { |
26549c8d | 118 | .owner = THIS_MODULE, |
367984eb | 119 | FB_DEFAULT_IOMEM_OPS, |
498f6d36 | 120 | .fb_destroy = simplefb_destroy, |
26549c8d | 121 | .fb_setcolreg = simplefb_setcolreg, |
26549c8d SW |
122 | }; |
123 | ||
5ef76da6 | 124 | static struct simplefb_format simplefb_formats[] = SIMPLEFB_FORMATS; |
26549c8d SW |
125 | |
126 | struct simplefb_params { | |
127 | u32 width; | |
128 | u32 height; | |
129 | u32 stride; | |
130 | struct simplefb_format *format; | |
8ddfc01a | 131 | struct resource memory; |
26549c8d SW |
132 | }; |
133 | ||
134 | static int simplefb_parse_dt(struct platform_device *pdev, | |
135 | struct simplefb_params *params) | |
136 | { | |
8ddfc01a | 137 | struct device_node *np = pdev->dev.of_node, *mem; |
26549c8d SW |
138 | int ret; |
139 | const char *format; | |
140 | int i; | |
141 | ||
142 | ret = of_property_read_u32(np, "width", ¶ms->width); | |
143 | if (ret) { | |
144 | dev_err(&pdev->dev, "Can't parse width property\n"); | |
145 | return ret; | |
146 | } | |
147 | ||
148 | ret = of_property_read_u32(np, "height", ¶ms->height); | |
149 | if (ret) { | |
150 | dev_err(&pdev->dev, "Can't parse height property\n"); | |
151 | return ret; | |
152 | } | |
153 | ||
154 | ret = of_property_read_u32(np, "stride", ¶ms->stride); | |
155 | if (ret) { | |
156 | dev_err(&pdev->dev, "Can't parse stride property\n"); | |
157 | return ret; | |
158 | } | |
159 | ||
160 | ret = of_property_read_string(np, "format", &format); | |
161 | if (ret) { | |
162 | dev_err(&pdev->dev, "Can't parse format property\n"); | |
163 | return ret; | |
164 | } | |
165 | params->format = NULL; | |
166 | for (i = 0; i < ARRAY_SIZE(simplefb_formats); i++) { | |
167 | if (strcmp(format, simplefb_formats[i].name)) | |
168 | continue; | |
169 | params->format = &simplefb_formats[i]; | |
170 | break; | |
171 | } | |
172 | if (!params->format) { | |
173 | dev_err(&pdev->dev, "Invalid format value\n"); | |
174 | return -EINVAL; | |
175 | } | |
176 | ||
8ddfc01a TR |
177 | mem = of_parse_phandle(np, "memory-region", 0); |
178 | if (mem) { | |
179 | ret = of_address_to_resource(mem, 0, ¶ms->memory); | |
180 | if (ret < 0) { | |
181 | dev_err(&pdev->dev, "failed to parse memory-region\n"); | |
182 | of_node_put(mem); | |
183 | return ret; | |
184 | } | |
185 | ||
186 | if (of_property_present(np, "reg")) | |
187 | dev_warn(&pdev->dev, "preferring \"memory-region\" over \"reg\" property\n"); | |
188 | ||
189 | of_node_put(mem); | |
190 | } else { | |
191 | memset(¶ms->memory, 0, sizeof(params->memory)); | |
192 | } | |
193 | ||
26549c8d SW |
194 | return 0; |
195 | } | |
196 | ||
5ef76da6 DH |
197 | static int simplefb_parse_pd(struct platform_device *pdev, |
198 | struct simplefb_params *params) | |
199 | { | |
129f1be4 | 200 | struct simplefb_platform_data *pd = dev_get_platdata(&pdev->dev); |
5ef76da6 DH |
201 | int i; |
202 | ||
203 | params->width = pd->width; | |
204 | params->height = pd->height; | |
205 | params->stride = pd->stride; | |
206 | ||
207 | params->format = NULL; | |
208 | for (i = 0; i < ARRAY_SIZE(simplefb_formats); i++) { | |
209 | if (strcmp(pd->format, simplefb_formats[i].name)) | |
210 | continue; | |
211 | ||
212 | params->format = &simplefb_formats[i]; | |
213 | break; | |
214 | } | |
215 | ||
216 | if (!params->format) { | |
217 | dev_err(&pdev->dev, "Invalid format value\n"); | |
218 | return -EINVAL; | |
219 | } | |
220 | ||
8ddfc01a TR |
221 | memset(¶ms->memory, 0, sizeof(params->memory)); |
222 | ||
5ef76da6 DH |
223 | return 0; |
224 | } | |
225 | ||
8284731e | 226 | #if defined CONFIG_OF && defined CONFIG_COMMON_CLK |
fc219bfd LV |
227 | /* |
228 | * Clock handling code. | |
229 | * | |
230 | * Here we handle the clocks property of our "simple-framebuffer" dt node. | |
231 | * This is necessary so that we can make sure that any clocks needed by | |
232 | * the display engine that the bootloader set up for us (and for which it | |
233 | * provided a simplefb dt node), stay up, for the life of the simplefb | |
234 | * driver. | |
235 | * | |
236 | * When the driver unloads, we cleanly disable, and then release the clocks. | |
237 | * | |
238 | * We only complain about errors here, no action is taken as the most likely | |
239 | * error can only happen due to a mismatch between the bootloader which set | |
240 | * up simplefb, and the clock definitions in the device tree. Chances are | |
241 | * that there are no adverse effects, and if there are, a clean teardown of | |
242 | * the fb probe will not help us much either. So just complain and carry on, | |
243 | * and hope that the user actually gets a working fb at the end of things. | |
244 | */ | |
a3accfd7 HG |
245 | static int simplefb_clocks_get(struct simplefb_par *par, |
246 | struct platform_device *pdev) | |
fc219bfd LV |
247 | { |
248 | struct device_node *np = pdev->dev.of_node; | |
249 | struct clk *clock; | |
a3accfd7 | 250 | int i; |
fc219bfd LV |
251 | |
252 | if (dev_get_platdata(&pdev->dev) || !np) | |
253 | return 0; | |
254 | ||
255 | par->clk_count = of_clk_get_parent_count(np); | |
2cd82d06 | 256 | if (!par->clk_count) |
fc219bfd LV |
257 | return 0; |
258 | ||
259 | par->clks = kcalloc(par->clk_count, sizeof(struct clk *), GFP_KERNEL); | |
260 | if (!par->clks) | |
261 | return -ENOMEM; | |
262 | ||
263 | for (i = 0; i < par->clk_count; i++) { | |
264 | clock = of_clk_get(np, i); | |
265 | if (IS_ERR(clock)) { | |
266 | if (PTR_ERR(clock) == -EPROBE_DEFER) { | |
267 | while (--i >= 0) { | |
5491424d | 268 | clk_put(par->clks[i]); |
fc219bfd LV |
269 | } |
270 | kfree(par->clks); | |
271 | return -EPROBE_DEFER; | |
272 | } | |
273 | dev_err(&pdev->dev, "%s: clock %d not found: %ld\n", | |
274 | __func__, i, PTR_ERR(clock)); | |
275 | continue; | |
276 | } | |
277 | par->clks[i] = clock; | |
278 | } | |
279 | ||
a3accfd7 HG |
280 | return 0; |
281 | } | |
282 | ||
283 | static void simplefb_clocks_enable(struct simplefb_par *par, | |
284 | struct platform_device *pdev) | |
285 | { | |
286 | int i, ret; | |
287 | ||
fc219bfd LV |
288 | for (i = 0; i < par->clk_count; i++) { |
289 | if (par->clks[i]) { | |
290 | ret = clk_prepare_enable(par->clks[i]); | |
291 | if (ret) { | |
292 | dev_err(&pdev->dev, | |
293 | "%s: failed to enable clock %d: %d\n", | |
294 | __func__, i, ret); | |
295 | clk_put(par->clks[i]); | |
296 | par->clks[i] = NULL; | |
297 | } | |
298 | } | |
299 | } | |
a3accfd7 | 300 | par->clks_enabled = true; |
fc219bfd LV |
301 | } |
302 | ||
303 | static void simplefb_clocks_destroy(struct simplefb_par *par) | |
304 | { | |
305 | int i; | |
306 | ||
307 | if (!par->clks) | |
308 | return; | |
309 | ||
310 | for (i = 0; i < par->clk_count; i++) { | |
311 | if (par->clks[i]) { | |
a3accfd7 HG |
312 | if (par->clks_enabled) |
313 | clk_disable_unprepare(par->clks[i]); | |
fc219bfd LV |
314 | clk_put(par->clks[i]); |
315 | } | |
316 | } | |
317 | ||
318 | kfree(par->clks); | |
319 | } | |
320 | #else | |
a3accfd7 | 321 | static int simplefb_clocks_get(struct simplefb_par *par, |
fc219bfd | 322 | struct platform_device *pdev) { return 0; } |
a3accfd7 HG |
323 | static void simplefb_clocks_enable(struct simplefb_par *par, |
324 | struct platform_device *pdev) { } | |
fc219bfd LV |
325 | static void simplefb_clocks_destroy(struct simplefb_par *par) { } |
326 | #endif | |
327 | ||
814740e7 CYT |
328 | #if defined CONFIG_OF && defined CONFIG_REGULATOR |
329 | ||
330 | #define SUPPLY_SUFFIX "-supply" | |
331 | ||
332 | /* | |
333 | * Regulator handling code. | |
334 | * | |
335 | * Here we handle the num-supplies and vin*-supply properties of our | |
336 | * "simple-framebuffer" dt node. This is necessary so that we can make sure | |
337 | * that any regulators needed by the display hardware that the bootloader | |
338 | * set up for us (and for which it provided a simplefb dt node), stay up, | |
339 | * for the life of the simplefb driver. | |
340 | * | |
341 | * When the driver unloads, we cleanly disable, and then release the | |
342 | * regulators. | |
343 | * | |
344 | * We only complain about errors here, no action is taken as the most likely | |
345 | * error can only happen due to a mismatch between the bootloader which set | |
346 | * up simplefb, and the regulator definitions in the device tree. Chances are | |
347 | * that there are no adverse effects, and if there are, a clean teardown of | |
348 | * the fb probe will not help us much either. So just complain and carry on, | |
349 | * and hope that the user actually gets a working fb at the end of things. | |
350 | */ | |
a3accfd7 HG |
351 | static int simplefb_regulators_get(struct simplefb_par *par, |
352 | struct platform_device *pdev) | |
814740e7 CYT |
353 | { |
354 | struct device_node *np = pdev->dev.of_node; | |
355 | struct property *prop; | |
356 | struct regulator *regulator; | |
357 | const char *p; | |
a3accfd7 | 358 | int count = 0, i = 0; |
814740e7 CYT |
359 | |
360 | if (dev_get_platdata(&pdev->dev) || !np) | |
361 | return 0; | |
362 | ||
363 | /* Count the number of regulator supplies */ | |
364 | for_each_property_of_node(np, prop) { | |
365 | p = strstr(prop->name, SUPPLY_SUFFIX); | |
366 | if (p && p != prop->name) | |
367 | count++; | |
368 | } | |
369 | ||
370 | if (!count) | |
371 | return 0; | |
372 | ||
373 | par->regulators = devm_kcalloc(&pdev->dev, count, | |
374 | sizeof(struct regulator *), GFP_KERNEL); | |
375 | if (!par->regulators) | |
376 | return -ENOMEM; | |
377 | ||
378 | /* Get all the regulators */ | |
379 | for_each_property_of_node(np, prop) { | |
380 | char name[32]; /* 32 is max size of property name */ | |
381 | ||
382 | p = strstr(prop->name, SUPPLY_SUFFIX); | |
383 | if (!p || p == prop->name) | |
384 | continue; | |
385 | ||
8d026858 | 386 | strscpy(name, prop->name, |
814740e7 CYT |
387 | strlen(prop->name) - strlen(SUPPLY_SUFFIX) + 1); |
388 | regulator = devm_regulator_get_optional(&pdev->dev, name); | |
389 | if (IS_ERR(regulator)) { | |
390 | if (PTR_ERR(regulator) == -EPROBE_DEFER) | |
391 | return -EPROBE_DEFER; | |
392 | dev_err(&pdev->dev, "regulator %s not found: %ld\n", | |
393 | name, PTR_ERR(regulator)); | |
394 | continue; | |
395 | } | |
396 | par->regulators[i++] = regulator; | |
397 | } | |
398 | par->regulator_count = i; | |
399 | ||
a3accfd7 HG |
400 | return 0; |
401 | } | |
402 | ||
403 | static void simplefb_regulators_enable(struct simplefb_par *par, | |
404 | struct platform_device *pdev) | |
405 | { | |
406 | int i, ret; | |
407 | ||
814740e7 CYT |
408 | /* Enable all the regulators */ |
409 | for (i = 0; i < par->regulator_count; i++) { | |
410 | ret = regulator_enable(par->regulators[i]); | |
411 | if (ret) { | |
412 | dev_err(&pdev->dev, | |
413 | "failed to enable regulator %d: %d\n", | |
414 | i, ret); | |
415 | devm_regulator_put(par->regulators[i]); | |
416 | par->regulators[i] = NULL; | |
417 | } | |
418 | } | |
a3accfd7 | 419 | par->regulators_enabled = true; |
814740e7 CYT |
420 | } |
421 | ||
422 | static void simplefb_regulators_destroy(struct simplefb_par *par) | |
423 | { | |
424 | int i; | |
425 | ||
a3accfd7 | 426 | if (!par->regulators || !par->regulators_enabled) |
814740e7 CYT |
427 | return; |
428 | ||
429 | for (i = 0; i < par->regulator_count; i++) | |
430 | if (par->regulators[i]) | |
431 | regulator_disable(par->regulators[i]); | |
432 | } | |
433 | #else | |
a3accfd7 | 434 | static int simplefb_regulators_get(struct simplefb_par *par, |
814740e7 | 435 | struct platform_device *pdev) { return 0; } |
a3accfd7 HG |
436 | static void simplefb_regulators_enable(struct simplefb_par *par, |
437 | struct platform_device *pdev) { } | |
814740e7 CYT |
438 | static void simplefb_regulators_destroy(struct simplefb_par *par) { } |
439 | #endif | |
440 | ||
92a511a5 TR |
441 | #if defined CONFIG_OF && defined CONFIG_PM_GENERIC_DOMAINS |
442 | static void simplefb_detach_genpds(void *res) | |
443 | { | |
444 | struct simplefb_par *par = res; | |
445 | unsigned int i = par->num_genpds; | |
446 | ||
447 | if (par->num_genpds <= 1) | |
448 | return; | |
449 | ||
450 | while (i--) { | |
451 | if (par->genpd_links[i]) | |
452 | device_link_del(par->genpd_links[i]); | |
453 | ||
454 | if (!IS_ERR_OR_NULL(par->genpds[i])) | |
455 | dev_pm_domain_detach(par->genpds[i], true); | |
456 | } | |
457 | } | |
458 | ||
459 | static int simplefb_attach_genpds(struct simplefb_par *par, | |
460 | struct platform_device *pdev) | |
461 | { | |
462 | struct device *dev = &pdev->dev; | |
463 | unsigned int i; | |
464 | int err; | |
465 | ||
466 | err = of_count_phandle_with_args(dev->of_node, "power-domains", | |
467 | "#power-domain-cells"); | |
468 | if (err < 0) { | |
9c689432 RA |
469 | /* Nothing wrong if optional PDs are missing */ |
470 | if (err == -ENOENT) | |
471 | return 0; | |
472 | ||
4350aa21 | 473 | dev_err(dev, "failed to parse power-domains: %d\n", err); |
92a511a5 TR |
474 | return err; |
475 | } | |
476 | ||
477 | par->num_genpds = err; | |
478 | ||
479 | /* | |
480 | * Single power-domain devices are handled by the driver core, so | |
481 | * nothing to do here. | |
482 | */ | |
483 | if (par->num_genpds <= 1) | |
484 | return 0; | |
485 | ||
486 | par->genpds = devm_kcalloc(dev, par->num_genpds, sizeof(*par->genpds), | |
487 | GFP_KERNEL); | |
488 | if (!par->genpds) | |
489 | return -ENOMEM; | |
490 | ||
491 | par->genpd_links = devm_kcalloc(dev, par->num_genpds, | |
492 | sizeof(*par->genpd_links), | |
493 | GFP_KERNEL); | |
494 | if (!par->genpd_links) | |
495 | return -ENOMEM; | |
496 | ||
497 | for (i = 0; i < par->num_genpds; i++) { | |
498 | par->genpds[i] = dev_pm_domain_attach_by_id(dev, i); | |
499 | if (IS_ERR(par->genpds[i])) { | |
500 | err = PTR_ERR(par->genpds[i]); | |
501 | if (err == -EPROBE_DEFER) { | |
502 | simplefb_detach_genpds(par); | |
503 | return err; | |
504 | } | |
505 | ||
506 | dev_warn(dev, "failed to attach domain %u: %d\n", i, err); | |
507 | continue; | |
508 | } | |
509 | ||
510 | par->genpd_links[i] = device_link_add(dev, par->genpds[i], | |
511 | DL_FLAG_STATELESS | | |
512 | DL_FLAG_PM_RUNTIME | | |
513 | DL_FLAG_RPM_ACTIVE); | |
514 | if (!par->genpd_links[i]) | |
515 | dev_warn(dev, "failed to link power-domain %u\n", i); | |
516 | } | |
517 | ||
518 | return devm_add_action_or_reset(dev, simplefb_detach_genpds, par); | |
519 | } | |
520 | #else | |
521 | static int simplefb_attach_genpds(struct simplefb_par *par, | |
522 | struct platform_device *pdev) | |
523 | { | |
524 | return 0; | |
525 | } | |
526 | #endif | |
527 | ||
26549c8d SW |
528 | static int simplefb_probe(struct platform_device *pdev) |
529 | { | |
530 | int ret; | |
531 | struct simplefb_params params; | |
532 | struct fb_info *info; | |
1270be4a | 533 | struct simplefb_par *par; |
748bd587 | 534 | struct resource *res, *mem; |
26549c8d SW |
535 | |
536 | if (fb_get_options("simplefb", NULL)) | |
537 | return -ENODEV; | |
538 | ||
5ef76da6 | 539 | ret = -ENODEV; |
129f1be4 | 540 | if (dev_get_platdata(&pdev->dev)) |
5ef76da6 DH |
541 | ret = simplefb_parse_pd(pdev, ¶ms); |
542 | else if (pdev->dev.of_node) | |
543 | ret = simplefb_parse_dt(pdev, ¶ms); | |
544 | ||
26549c8d SW |
545 | if (ret) |
546 | return ret; | |
547 | ||
8ddfc01a TR |
548 | if (params.memory.start == 0 && params.memory.end == 0) { |
549 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
550 | if (!res) { | |
551 | dev_err(&pdev->dev, "No memory resource\n"); | |
552 | return -EINVAL; | |
553 | } | |
554 | } else { | |
555 | res = ¶ms.memory; | |
26549c8d SW |
556 | } |
557 | ||
748bd587 TZ |
558 | mem = request_mem_region(res->start, resource_size(res), "simplefb"); |
559 | if (!mem) { | |
560 | /* | |
561 | * We cannot make this fatal. Sometimes this comes from magic | |
562 | * spaces our resource handlers simply don't know about. Use | |
563 | * the I/O-memory resource as-is and try to map that instead. | |
564 | */ | |
565 | dev_warn(&pdev->dev, "simplefb: cannot reserve video memory at %pR\n", res); | |
566 | mem = res; | |
567 | } | |
568 | ||
1270be4a | 569 | info = framebuffer_alloc(sizeof(struct simplefb_par), &pdev->dev); |
748bd587 TZ |
570 | if (!info) { |
571 | ret = -ENOMEM; | |
572 | goto error_release_mem_region; | |
573 | } | |
26549c8d SW |
574 | platform_set_drvdata(pdev, info); |
575 | ||
1270be4a LV |
576 | par = info->par; |
577 | ||
26549c8d SW |
578 | info->fix = simplefb_fix; |
579 | info->fix.smem_start = mem->start; | |
580 | info->fix.smem_len = resource_size(mem); | |
581 | info->fix.line_length = params.stride; | |
582 | ||
583 | info->var = simplefb_var; | |
584 | info->var.xres = params.width; | |
585 | info->var.yres = params.height; | |
586 | info->var.xres_virtual = params.width; | |
587 | info->var.yres_virtual = params.height; | |
588 | info->var.bits_per_pixel = params.format->bits_per_pixel; | |
589 | info->var.red = params.format->red; | |
590 | info->var.green = params.format->green; | |
591 | info->var.blue = params.format->blue; | |
592 | info->var.transp = params.format->transp; | |
593 | ||
d9702b2a TZ |
594 | par->base = info->fix.smem_start; |
595 | par->size = info->fix.smem_len; | |
df0960ab | 596 | |
26549c8d | 597 | info->fbops = &simplefb_ops; |
9e210be6 DH |
598 | info->screen_base = ioremap_wc(info->fix.smem_start, |
599 | info->fix.smem_len); | |
26549c8d | 600 | if (!info->screen_base) { |
bf2fda15 LV |
601 | ret = -ENOMEM; |
602 | goto error_fb_release; | |
26549c8d | 603 | } |
1270be4a | 604 | info->pseudo_palette = par->palette; |
26549c8d | 605 | |
a3accfd7 | 606 | ret = simplefb_clocks_get(par, pdev); |
fc219bfd LV |
607 | if (ret < 0) |
608 | goto error_unmap; | |
609 | ||
a3accfd7 | 610 | ret = simplefb_regulators_get(par, pdev); |
814740e7 CYT |
611 | if (ret < 0) |
612 | goto error_clocks; | |
613 | ||
92a511a5 TR |
614 | ret = simplefb_attach_genpds(par, pdev); |
615 | if (ret < 0) | |
616 | goto error_regulators; | |
617 | ||
a3accfd7 HG |
618 | simplefb_clocks_enable(par, pdev); |
619 | simplefb_regulators_enable(par, pdev); | |
620 | ||
5269a618 PR |
621 | dev_info(&pdev->dev, "framebuffer at 0x%lx, 0x%x bytes\n", |
622 | info->fix.smem_start, info->fix.smem_len); | |
9f192a92 TG |
623 | dev_info(&pdev->dev, "format=%s, mode=%dx%dx%d, linelength=%d\n", |
624 | params.format->name, | |
625 | info->var.xres, info->var.yres, | |
626 | info->var.bits_per_pixel, info->fix.line_length); | |
627 | ||
748bd587 TZ |
628 | if (mem != res) |
629 | par->mem = mem; /* release in clean-up handler */ | |
630 | ||
d9702b2a TZ |
631 | ret = devm_aperture_acquire_for_platform_device(pdev, par->base, par->size); |
632 | if (ret) { | |
633 | dev_err(&pdev->dev, "Unable to acquire aperture: %d\n", ret); | |
634 | goto error_regulators; | |
635 | } | |
26549c8d SW |
636 | ret = register_framebuffer(info); |
637 | if (ret < 0) { | |
638 | dev_err(&pdev->dev, "Unable to register simplefb: %d\n", ret); | |
814740e7 | 639 | goto error_regulators; |
26549c8d SW |
640 | } |
641 | ||
642 | dev_info(&pdev->dev, "fb%d: simplefb registered!\n", info->node); | |
643 | ||
644 | return 0; | |
bf2fda15 | 645 | |
814740e7 CYT |
646 | error_regulators: |
647 | simplefb_regulators_destroy(par); | |
fc219bfd LV |
648 | error_clocks: |
649 | simplefb_clocks_destroy(par); | |
bf2fda15 LV |
650 | error_unmap: |
651 | iounmap(info->screen_base); | |
652 | error_fb_release: | |
653 | framebuffer_release(info); | |
748bd587 TZ |
654 | error_release_mem_region: |
655 | if (mem != res) | |
656 | release_mem_region(mem->start, resource_size(mem)); | |
bf2fda15 | 657 | return ret; |
26549c8d SW |
658 | } |
659 | ||
4ba34066 | 660 | static void simplefb_remove(struct platform_device *pdev) |
26549c8d SW |
661 | { |
662 | struct fb_info *info = platform_get_drvdata(pdev); | |
663 | ||
666b90b3 | 664 | /* simplefb_destroy takes care of info cleanup */ |
26549c8d | 665 | unregister_framebuffer(info); |
26549c8d SW |
666 | } |
667 | ||
668 | static const struct of_device_id simplefb_of_match[] = { | |
669 | { .compatible = "simple-framebuffer", }, | |
670 | { }, | |
671 | }; | |
672 | MODULE_DEVICE_TABLE(of, simplefb_of_match); | |
673 | ||
674 | static struct platform_driver simplefb_driver = { | |
675 | .driver = { | |
676 | .name = "simple-framebuffer", | |
26549c8d SW |
677 | .of_match_table = simplefb_of_match, |
678 | }, | |
679 | .probe = simplefb_probe, | |
4ba34066 | 680 | .remove_new = simplefb_remove, |
26549c8d | 681 | }; |
89c95001 | 682 | |
2f92ea21 | 683 | module_platform_driver(simplefb_driver); |
26549c8d SW |
684 | |
685 | MODULE_AUTHOR("Stephen Warren <swarren@wwwdotorg.org>"); | |
686 | MODULE_DESCRIPTION("Simple framebuffer driver"); | |
687 | MODULE_LICENSE("GPL v2"); |