xref: /linux-6.15/drivers/video/fbdev/simplefb.c (revision 01ecc142)
12025cf9eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2f7018c21STomi Valkeinen /*
3f7018c21STomi Valkeinen  * Simplest possible simple frame-buffer driver, as a platform device
4f7018c21STomi Valkeinen  *
5f7018c21STomi Valkeinen  * Copyright (c) 2013, Stephen Warren
6f7018c21STomi Valkeinen  *
7f7018c21STomi Valkeinen  * Based on q40fb.c, which was:
8f7018c21STomi Valkeinen  * Copyright (C) 2001 Richard Zidlicky <[email protected]>
9f7018c21STomi Valkeinen  *
10f7018c21STomi Valkeinen  * Also based on offb.c, which was:
11f7018c21STomi Valkeinen  * Copyright (C) 1997 Geert Uytterhoeven
12f7018c21STomi Valkeinen  * Copyright (C) 1996 Paul Mackerras
13f7018c21STomi Valkeinen  */
14f7018c21STomi Valkeinen 
15d9702b2aSThomas Zimmermann #include <linux/aperture.h>
16f7018c21STomi Valkeinen #include <linux/errno.h>
17f7018c21STomi Valkeinen #include <linux/fb.h>
18f7018c21STomi Valkeinen #include <linux/io.h>
19f7018c21STomi Valkeinen #include <linux/module.h>
20f7018c21STomi Valkeinen #include <linux/platform_data/simplefb.h>
21f7018c21STomi Valkeinen #include <linux/platform_device.h>
22d49c6993SStephen Boyd #include <linux/clk.h>
23814740e7SChen-Yu Tsai #include <linux/of.h>
248ddfc01aSThierry Reding #include <linux/of_address.h>
25db3b4fd1SGeert Uytterhoeven #include <linux/of_clk.h>
2689c95001SHans de Goede #include <linux/of_platform.h>
27814740e7SChen-Yu Tsai #include <linux/parser.h>
2892a511a5SThierry Reding #include <linux/pm_domain.h>
29814740e7SChen-Yu Tsai #include <linux/regulator/consumer.h>
30f7018c21STomi Valkeinen 
31ca9384c5SJulia Lawall static const struct fb_fix_screeninfo simplefb_fix = {
32f7018c21STomi Valkeinen 	.id		= "simple",
33f7018c21STomi Valkeinen 	.type		= FB_TYPE_PACKED_PIXELS,
34f7018c21STomi Valkeinen 	.visual		= FB_VISUAL_TRUECOLOR,
35f7018c21STomi Valkeinen 	.accel		= FB_ACCEL_NONE,
36f7018c21STomi Valkeinen };
37f7018c21STomi Valkeinen 
38ca9384c5SJulia Lawall static const struct fb_var_screeninfo simplefb_var = {
39f7018c21STomi Valkeinen 	.height		= -1,
40f7018c21STomi Valkeinen 	.width		= -1,
41f7018c21STomi Valkeinen 	.activate	= FB_ACTIVATE_NOW,
42f7018c21STomi Valkeinen 	.vmode		= FB_VMODE_NONINTERLACED,
43f7018c21STomi Valkeinen };
44f7018c21STomi Valkeinen 
451270be4aSLuc Verhaegen #define PSEUDO_PALETTE_SIZE 16
461270be4aSLuc Verhaegen 
simplefb_setcolreg(u_int regno,u_int red,u_int green,u_int blue,u_int transp,struct fb_info * info)47f7018c21STomi Valkeinen static int simplefb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
48f7018c21STomi Valkeinen 			      u_int transp, struct fb_info *info)
49f7018c21STomi Valkeinen {
50f7018c21STomi Valkeinen 	u32 *pal = info->pseudo_palette;
51f7018c21STomi Valkeinen 	u32 cr = red >> (16 - info->var.red.length);
52f7018c21STomi Valkeinen 	u32 cg = green >> (16 - info->var.green.length);
53f7018c21STomi Valkeinen 	u32 cb = blue >> (16 - info->var.blue.length);
54f7018c21STomi Valkeinen 	u32 value;
55f7018c21STomi Valkeinen 
561270be4aSLuc Verhaegen 	if (regno >= PSEUDO_PALETTE_SIZE)
57f7018c21STomi Valkeinen 		return -EINVAL;
58f7018c21STomi Valkeinen 
59f7018c21STomi Valkeinen 	value = (cr << info->var.red.offset) |
60f7018c21STomi Valkeinen 		(cg << info->var.green.offset) |
61f7018c21STomi Valkeinen 		(cb << info->var.blue.offset);
62f7018c21STomi Valkeinen 	if (info->var.transp.length > 0) {
63f7018c21STomi Valkeinen 		u32 mask = (1 << info->var.transp.length) - 1;
64f7018c21STomi Valkeinen 		mask <<= info->var.transp.offset;
65f7018c21STomi Valkeinen 		value |= mask;
66f7018c21STomi Valkeinen 	}
67f7018c21STomi Valkeinen 	pal[regno] = value;
68f7018c21STomi Valkeinen 
69f7018c21STomi Valkeinen 	return 0;
70f7018c21STomi Valkeinen }
71f7018c21STomi Valkeinen 
72748bd587SThomas Zimmermann struct simplefb_par {
73748bd587SThomas Zimmermann 	u32 palette[PSEUDO_PALETTE_SIZE];
74d9702b2aSThomas Zimmermann 	resource_size_t base;
75d9702b2aSThomas Zimmermann 	resource_size_t size;
76748bd587SThomas Zimmermann 	struct resource *mem;
77748bd587SThomas Zimmermann #if defined CONFIG_OF && defined CONFIG_COMMON_CLK
78748bd587SThomas Zimmermann 	bool clks_enabled;
79748bd587SThomas Zimmermann 	unsigned int clk_count;
80748bd587SThomas Zimmermann 	struct clk **clks;
81748bd587SThomas Zimmermann #endif
8292a511a5SThierry Reding #if defined CONFIG_OF && defined CONFIG_PM_GENERIC_DOMAINS
8392a511a5SThierry Reding 	unsigned int num_genpds;
8492a511a5SThierry Reding 	struct device **genpds;
8592a511a5SThierry Reding 	struct device_link **genpd_links;
8692a511a5SThierry Reding #endif
87748bd587SThomas Zimmermann #if defined CONFIG_OF && defined CONFIG_REGULATOR
88748bd587SThomas Zimmermann 	bool regulators_enabled;
89748bd587SThomas Zimmermann 	u32 regulator_count;
90748bd587SThomas Zimmermann 	struct regulator **regulators;
91748bd587SThomas Zimmermann #endif
92748bd587SThomas Zimmermann };
93748bd587SThomas Zimmermann 
947c9806e8SChen-Yu Tsai static void simplefb_clocks_destroy(struct simplefb_par *par);
957c9806e8SChen-Yu Tsai static void simplefb_regulators_destroy(struct simplefb_par *par);
967c9806e8SChen-Yu Tsai 
97666b90b3SJavier Martinez Canillas /*
98666b90b3SJavier Martinez Canillas  * fb_ops.fb_destroy is called by the last put_fb_info() call at the end
99666b90b3SJavier Martinez Canillas  * of unregister_framebuffer() or fb_release(). Do any cleanup here.
100666b90b3SJavier Martinez Canillas  */
simplefb_destroy(struct fb_info * info)101f7018c21STomi Valkeinen static void simplefb_destroy(struct fb_info *info)
102f7018c21STomi Valkeinen {
103748bd587SThomas Zimmermann 	struct simplefb_par *par = info->par;
104748bd587SThomas Zimmermann 	struct resource *mem = par->mem;
105748bd587SThomas Zimmermann 
1067c9806e8SChen-Yu Tsai 	simplefb_regulators_destroy(info->par);
1077c9806e8SChen-Yu Tsai 	simplefb_clocks_destroy(info->par);
108f7018c21STomi Valkeinen 	if (info->screen_base)
109f7018c21STomi Valkeinen 		iounmap(info->screen_base);
110748bd587SThomas Zimmermann 
111666b90b3SJavier Martinez Canillas 	framebuffer_release(info);
112666b90b3SJavier Martinez Canillas 
113748bd587SThomas Zimmermann 	if (mem)
114748bd587SThomas Zimmermann 		release_mem_region(mem->start, resource_size(mem));
115f7018c21STomi Valkeinen }
116f7018c21STomi Valkeinen 
1178a48ac33SJani Nikula static const struct fb_ops simplefb_ops = {
118f7018c21STomi Valkeinen 	.owner		= THIS_MODULE,
119367984ebSThomas Zimmermann 	FB_DEFAULT_IOMEM_OPS,
120f7018c21STomi Valkeinen 	.fb_destroy	= simplefb_destroy,
121f7018c21STomi Valkeinen 	.fb_setcolreg	= simplefb_setcolreg,
122f7018c21STomi Valkeinen };
123f7018c21STomi Valkeinen 
124f7018c21STomi Valkeinen static struct simplefb_format simplefb_formats[] = SIMPLEFB_FORMATS;
125f7018c21STomi Valkeinen 
126f7018c21STomi Valkeinen struct simplefb_params {
127f7018c21STomi Valkeinen 	u32 width;
128f7018c21STomi Valkeinen 	u32 height;
129f7018c21STomi Valkeinen 	u32 stride;
130f7018c21STomi Valkeinen 	struct simplefb_format *format;
1318ddfc01aSThierry Reding 	struct resource memory;
132f7018c21STomi Valkeinen };
133f7018c21STomi Valkeinen 
simplefb_parse_dt(struct platform_device * pdev,struct simplefb_params * params)134f7018c21STomi Valkeinen static int simplefb_parse_dt(struct platform_device *pdev,
135f7018c21STomi Valkeinen 			   struct simplefb_params *params)
136f7018c21STomi Valkeinen {
1378ddfc01aSThierry Reding 	struct device_node *np = pdev->dev.of_node, *mem;
138f7018c21STomi Valkeinen 	int ret;
139f7018c21STomi Valkeinen 	const char *format;
140f7018c21STomi Valkeinen 	int i;
141f7018c21STomi Valkeinen 
142f7018c21STomi Valkeinen 	ret = of_property_read_u32(np, "width", &params->width);
143f7018c21STomi Valkeinen 	if (ret) {
144f7018c21STomi Valkeinen 		dev_err(&pdev->dev, "Can't parse width property\n");
145f7018c21STomi Valkeinen 		return ret;
146f7018c21STomi Valkeinen 	}
147f7018c21STomi Valkeinen 
148f7018c21STomi Valkeinen 	ret = of_property_read_u32(np, "height", &params->height);
149f7018c21STomi Valkeinen 	if (ret) {
150f7018c21STomi Valkeinen 		dev_err(&pdev->dev, "Can't parse height property\n");
151f7018c21STomi Valkeinen 		return ret;
152f7018c21STomi Valkeinen 	}
153f7018c21STomi Valkeinen 
154f7018c21STomi Valkeinen 	ret = of_property_read_u32(np, "stride", &params->stride);
155f7018c21STomi Valkeinen 	if (ret) {
156f7018c21STomi Valkeinen 		dev_err(&pdev->dev, "Can't parse stride property\n");
157f7018c21STomi Valkeinen 		return ret;
158f7018c21STomi Valkeinen 	}
159f7018c21STomi Valkeinen 
160f7018c21STomi Valkeinen 	ret = of_property_read_string(np, "format", &format);
161f7018c21STomi Valkeinen 	if (ret) {
162f7018c21STomi Valkeinen 		dev_err(&pdev->dev, "Can't parse format property\n");
163f7018c21STomi Valkeinen 		return ret;
164f7018c21STomi Valkeinen 	}
165f7018c21STomi Valkeinen 	params->format = NULL;
166f7018c21STomi Valkeinen 	for (i = 0; i < ARRAY_SIZE(simplefb_formats); i++) {
167f7018c21STomi Valkeinen 		if (strcmp(format, simplefb_formats[i].name))
168f7018c21STomi Valkeinen 			continue;
169f7018c21STomi Valkeinen 		params->format = &simplefb_formats[i];
170f7018c21STomi Valkeinen 		break;
171f7018c21STomi Valkeinen 	}
172f7018c21STomi Valkeinen 	if (!params->format) {
173f7018c21STomi Valkeinen 		dev_err(&pdev->dev, "Invalid format value\n");
174f7018c21STomi Valkeinen 		return -EINVAL;
175f7018c21STomi Valkeinen 	}
176f7018c21STomi Valkeinen 
1778ddfc01aSThierry Reding 	mem = of_parse_phandle(np, "memory-region", 0);
1788ddfc01aSThierry Reding 	if (mem) {
1798ddfc01aSThierry Reding 		ret = of_address_to_resource(mem, 0, &params->memory);
1808ddfc01aSThierry Reding 		if (ret < 0) {
1818ddfc01aSThierry Reding 			dev_err(&pdev->dev, "failed to parse memory-region\n");
1828ddfc01aSThierry Reding 			of_node_put(mem);
1838ddfc01aSThierry Reding 			return ret;
1848ddfc01aSThierry Reding 		}
1858ddfc01aSThierry Reding 
1868ddfc01aSThierry Reding 		if (of_property_present(np, "reg"))
1878ddfc01aSThierry Reding 			dev_warn(&pdev->dev, "preferring \"memory-region\" over \"reg\" property\n");
1888ddfc01aSThierry Reding 
1898ddfc01aSThierry Reding 		of_node_put(mem);
1908ddfc01aSThierry Reding 	} else {
1918ddfc01aSThierry Reding 		memset(&params->memory, 0, sizeof(params->memory));
1928ddfc01aSThierry Reding 	}
1938ddfc01aSThierry Reding 
194f7018c21STomi Valkeinen 	return 0;
195f7018c21STomi Valkeinen }
196f7018c21STomi Valkeinen 
simplefb_parse_pd(struct platform_device * pdev,struct simplefb_params * params)197f7018c21STomi Valkeinen static int simplefb_parse_pd(struct platform_device *pdev,
198f7018c21STomi Valkeinen 			     struct simplefb_params *params)
199f7018c21STomi Valkeinen {
200f7018c21STomi Valkeinen 	struct simplefb_platform_data *pd = dev_get_platdata(&pdev->dev);
201f7018c21STomi Valkeinen 	int i;
202f7018c21STomi Valkeinen 
203f7018c21STomi Valkeinen 	params->width = pd->width;
204f7018c21STomi Valkeinen 	params->height = pd->height;
205f7018c21STomi Valkeinen 	params->stride = pd->stride;
206f7018c21STomi Valkeinen 
207f7018c21STomi Valkeinen 	params->format = NULL;
208f7018c21STomi Valkeinen 	for (i = 0; i < ARRAY_SIZE(simplefb_formats); i++) {
209f7018c21STomi Valkeinen 		if (strcmp(pd->format, simplefb_formats[i].name))
210f7018c21STomi Valkeinen 			continue;
211f7018c21STomi Valkeinen 
212f7018c21STomi Valkeinen 		params->format = &simplefb_formats[i];
213f7018c21STomi Valkeinen 		break;
214f7018c21STomi Valkeinen 	}
215f7018c21STomi Valkeinen 
216f7018c21STomi Valkeinen 	if (!params->format) {
217f7018c21STomi Valkeinen 		dev_err(&pdev->dev, "Invalid format value\n");
218f7018c21STomi Valkeinen 		return -EINVAL;
219f7018c21STomi Valkeinen 	}
220f7018c21STomi Valkeinen 
2218ddfc01aSThierry Reding 	memset(&params->memory, 0, sizeof(params->memory));
2228ddfc01aSThierry Reding 
223f7018c21STomi Valkeinen 	return 0;
224f7018c21STomi Valkeinen }
225f7018c21STomi Valkeinen 
2268284731eSHans de Goede #if defined CONFIG_OF && defined CONFIG_COMMON_CLK
227fc219bfdSLuc Verhaegen /*
228fc219bfdSLuc Verhaegen  * Clock handling code.
229fc219bfdSLuc Verhaegen  *
230fc219bfdSLuc Verhaegen  * Here we handle the clocks property of our "simple-framebuffer" dt node.
231fc219bfdSLuc Verhaegen  * This is necessary so that we can make sure that any clocks needed by
232fc219bfdSLuc Verhaegen  * the display engine that the bootloader set up for us (and for which it
233fc219bfdSLuc Verhaegen  * provided a simplefb dt node), stay up, for the life of the simplefb
234fc219bfdSLuc Verhaegen  * driver.
235fc219bfdSLuc Verhaegen  *
236fc219bfdSLuc Verhaegen  * When the driver unloads, we cleanly disable, and then release the clocks.
237fc219bfdSLuc Verhaegen  *
238fc219bfdSLuc Verhaegen  * We only complain about errors here, no action is taken as the most likely
239fc219bfdSLuc Verhaegen  * error can only happen due to a mismatch between the bootloader which set
240fc219bfdSLuc Verhaegen  * up simplefb, and the clock definitions in the device tree. Chances are
241fc219bfdSLuc Verhaegen  * that there are no adverse effects, and if there are, a clean teardown of
242fc219bfdSLuc Verhaegen  * the fb probe will not help us much either. So just complain and carry on,
243fc219bfdSLuc Verhaegen  * and hope that the user actually gets a working fb at the end of things.
244fc219bfdSLuc Verhaegen  */
simplefb_clocks_get(struct simplefb_par * par,struct platform_device * pdev)245a3accfd7SHans de Goede static int simplefb_clocks_get(struct simplefb_par *par,
246fc219bfdSLuc Verhaegen 			       struct platform_device *pdev)
247fc219bfdSLuc Verhaegen {
248fc219bfdSLuc Verhaegen 	struct device_node *np = pdev->dev.of_node;
249fc219bfdSLuc Verhaegen 	struct clk *clock;
250a3accfd7SHans de Goede 	int i;
251fc219bfdSLuc Verhaegen 
252fc219bfdSLuc Verhaegen 	if (dev_get_platdata(&pdev->dev) || !np)
253fc219bfdSLuc Verhaegen 		return 0;
254fc219bfdSLuc Verhaegen 
255fc219bfdSLuc Verhaegen 	par->clk_count = of_clk_get_parent_count(np);
2562cd82d06SStephen Boyd 	if (!par->clk_count)
257fc219bfdSLuc Verhaegen 		return 0;
258fc219bfdSLuc Verhaegen 
259fc219bfdSLuc Verhaegen 	par->clks = kcalloc(par->clk_count, sizeof(struct clk *), GFP_KERNEL);
260fc219bfdSLuc Verhaegen 	if (!par->clks)
261fc219bfdSLuc Verhaegen 		return -ENOMEM;
262fc219bfdSLuc Verhaegen 
263fc219bfdSLuc Verhaegen 	for (i = 0; i < par->clk_count; i++) {
264fc219bfdSLuc Verhaegen 		clock = of_clk_get(np, i);
265fc219bfdSLuc Verhaegen 		if (IS_ERR(clock)) {
266fc219bfdSLuc Verhaegen 			if (PTR_ERR(clock) == -EPROBE_DEFER) {
267fc219bfdSLuc Verhaegen 				while (--i >= 0) {
268fc219bfdSLuc Verhaegen 					clk_put(par->clks[i]);
269fc219bfdSLuc Verhaegen 				}
270fc219bfdSLuc Verhaegen 				kfree(par->clks);
271fc219bfdSLuc Verhaegen 				return -EPROBE_DEFER;
272fc219bfdSLuc Verhaegen 			}
273fc219bfdSLuc Verhaegen 			dev_err(&pdev->dev, "%s: clock %d not found: %ld\n",
274fc219bfdSLuc Verhaegen 				__func__, i, PTR_ERR(clock));
275fc219bfdSLuc Verhaegen 			continue;
276fc219bfdSLuc Verhaegen 		}
277fc219bfdSLuc Verhaegen 		par->clks[i] = clock;
278fc219bfdSLuc Verhaegen 	}
279fc219bfdSLuc Verhaegen 
280a3accfd7SHans de Goede 	return 0;
281a3accfd7SHans de Goede }
282a3accfd7SHans de Goede 
simplefb_clocks_enable(struct simplefb_par * par,struct platform_device * pdev)283a3accfd7SHans de Goede static void simplefb_clocks_enable(struct simplefb_par *par,
284a3accfd7SHans de Goede 				   struct platform_device *pdev)
285a3accfd7SHans de Goede {
286a3accfd7SHans de Goede 	int i, ret;
287a3accfd7SHans de Goede 
288fc219bfdSLuc Verhaegen 	for (i = 0; i < par->clk_count; i++) {
289fc219bfdSLuc Verhaegen 		if (par->clks[i]) {
290fc219bfdSLuc Verhaegen 			ret = clk_prepare_enable(par->clks[i]);
291fc219bfdSLuc Verhaegen 			if (ret) {
292fc219bfdSLuc Verhaegen 				dev_err(&pdev->dev,
293fc219bfdSLuc Verhaegen 					"%s: failed to enable clock %d: %d\n",
294fc219bfdSLuc Verhaegen 					__func__, i, ret);
295fc219bfdSLuc Verhaegen 				clk_put(par->clks[i]);
296fc219bfdSLuc Verhaegen 				par->clks[i] = NULL;
297fc219bfdSLuc Verhaegen 			}
298fc219bfdSLuc Verhaegen 		}
299fc219bfdSLuc Verhaegen 	}
300a3accfd7SHans de Goede 	par->clks_enabled = true;
301fc219bfdSLuc Verhaegen }
302fc219bfdSLuc Verhaegen 
simplefb_clocks_destroy(struct simplefb_par * par)303fc219bfdSLuc Verhaegen static void simplefb_clocks_destroy(struct simplefb_par *par)
304fc219bfdSLuc Verhaegen {
305fc219bfdSLuc Verhaegen 	int i;
306fc219bfdSLuc Verhaegen 
307fc219bfdSLuc Verhaegen 	if (!par->clks)
308fc219bfdSLuc Verhaegen 		return;
309fc219bfdSLuc Verhaegen 
310fc219bfdSLuc Verhaegen 	for (i = 0; i < par->clk_count; i++) {
311fc219bfdSLuc Verhaegen 		if (par->clks[i]) {
312a3accfd7SHans de Goede 			if (par->clks_enabled)
313fc219bfdSLuc Verhaegen 				clk_disable_unprepare(par->clks[i]);
314fc219bfdSLuc Verhaegen 			clk_put(par->clks[i]);
315fc219bfdSLuc Verhaegen 		}
316fc219bfdSLuc Verhaegen 	}
317fc219bfdSLuc Verhaegen 
318fc219bfdSLuc Verhaegen 	kfree(par->clks);
319fc219bfdSLuc Verhaegen }
320fc219bfdSLuc Verhaegen #else
simplefb_clocks_get(struct simplefb_par * par,struct platform_device * pdev)321a3accfd7SHans de Goede static int simplefb_clocks_get(struct simplefb_par *par,
322fc219bfdSLuc Verhaegen 	struct platform_device *pdev) { return 0; }
simplefb_clocks_enable(struct simplefb_par * par,struct platform_device * pdev)323a3accfd7SHans de Goede static void simplefb_clocks_enable(struct simplefb_par *par,
324a3accfd7SHans de Goede 	struct platform_device *pdev) { }
simplefb_clocks_destroy(struct simplefb_par * par)325fc219bfdSLuc Verhaegen static void simplefb_clocks_destroy(struct simplefb_par *par) { }
326fc219bfdSLuc Verhaegen #endif
327fc219bfdSLuc Verhaegen 
328814740e7SChen-Yu Tsai #if defined CONFIG_OF && defined CONFIG_REGULATOR
329814740e7SChen-Yu Tsai 
330814740e7SChen-Yu Tsai #define SUPPLY_SUFFIX "-supply"
331814740e7SChen-Yu Tsai 
332814740e7SChen-Yu Tsai /*
333814740e7SChen-Yu Tsai  * Regulator handling code.
334814740e7SChen-Yu Tsai  *
335814740e7SChen-Yu Tsai  * Here we handle the num-supplies and vin*-supply properties of our
336814740e7SChen-Yu Tsai  * "simple-framebuffer" dt node. This is necessary so that we can make sure
337814740e7SChen-Yu Tsai  * that any regulators needed by the display hardware that the bootloader
338814740e7SChen-Yu Tsai  * set up for us (and for which it provided a simplefb dt node), stay up,
339814740e7SChen-Yu Tsai  * for the life of the simplefb driver.
340814740e7SChen-Yu Tsai  *
341814740e7SChen-Yu Tsai  * When the driver unloads, we cleanly disable, and then release the
342814740e7SChen-Yu Tsai  * regulators.
343814740e7SChen-Yu Tsai  *
344814740e7SChen-Yu Tsai  * We only complain about errors here, no action is taken as the most likely
345814740e7SChen-Yu Tsai  * error can only happen due to a mismatch between the bootloader which set
346814740e7SChen-Yu Tsai  * up simplefb, and the regulator definitions in the device tree. Chances are
347814740e7SChen-Yu Tsai  * that there are no adverse effects, and if there are, a clean teardown of
348814740e7SChen-Yu Tsai  * the fb probe will not help us much either. So just complain and carry on,
349814740e7SChen-Yu Tsai  * and hope that the user actually gets a working fb at the end of things.
350814740e7SChen-Yu Tsai  */
simplefb_regulators_get(struct simplefb_par * par,struct platform_device * pdev)351a3accfd7SHans de Goede static int simplefb_regulators_get(struct simplefb_par *par,
352814740e7SChen-Yu Tsai 				   struct platform_device *pdev)
353814740e7SChen-Yu Tsai {
354814740e7SChen-Yu Tsai 	struct device_node *np = pdev->dev.of_node;
355814740e7SChen-Yu Tsai 	struct property *prop;
356814740e7SChen-Yu Tsai 	struct regulator *regulator;
357814740e7SChen-Yu Tsai 	const char *p;
358a3accfd7SHans de Goede 	int count = 0, i = 0;
359814740e7SChen-Yu Tsai 
360814740e7SChen-Yu Tsai 	if (dev_get_platdata(&pdev->dev) || !np)
361814740e7SChen-Yu Tsai 		return 0;
362814740e7SChen-Yu Tsai 
363814740e7SChen-Yu Tsai 	/* Count the number of regulator supplies */
364814740e7SChen-Yu Tsai 	for_each_property_of_node(np, prop) {
365814740e7SChen-Yu Tsai 		p = strstr(prop->name, SUPPLY_SUFFIX);
366814740e7SChen-Yu Tsai 		if (p && p != prop->name)
367814740e7SChen-Yu Tsai 			count++;
368814740e7SChen-Yu Tsai 	}
369814740e7SChen-Yu Tsai 
370814740e7SChen-Yu Tsai 	if (!count)
371814740e7SChen-Yu Tsai 		return 0;
372814740e7SChen-Yu Tsai 
373814740e7SChen-Yu Tsai 	par->regulators = devm_kcalloc(&pdev->dev, count,
374814740e7SChen-Yu Tsai 				       sizeof(struct regulator *), GFP_KERNEL);
375814740e7SChen-Yu Tsai 	if (!par->regulators)
376814740e7SChen-Yu Tsai 		return -ENOMEM;
377814740e7SChen-Yu Tsai 
378814740e7SChen-Yu Tsai 	/* Get all the regulators */
379814740e7SChen-Yu Tsai 	for_each_property_of_node(np, prop) {
380814740e7SChen-Yu Tsai 		char name[32]; /* 32 is max size of property name */
381814740e7SChen-Yu Tsai 
382814740e7SChen-Yu Tsai 		p = strstr(prop->name, SUPPLY_SUFFIX);
383814740e7SChen-Yu Tsai 		if (!p || p == prop->name)
384814740e7SChen-Yu Tsai 			continue;
385814740e7SChen-Yu Tsai 
3868d026858SWolfram Sang 		strscpy(name, prop->name,
387814740e7SChen-Yu Tsai 			strlen(prop->name) - strlen(SUPPLY_SUFFIX) + 1);
388814740e7SChen-Yu Tsai 		regulator = devm_regulator_get_optional(&pdev->dev, name);
389814740e7SChen-Yu Tsai 		if (IS_ERR(regulator)) {
390814740e7SChen-Yu Tsai 			if (PTR_ERR(regulator) == -EPROBE_DEFER)
391814740e7SChen-Yu Tsai 				return -EPROBE_DEFER;
392814740e7SChen-Yu Tsai 			dev_err(&pdev->dev, "regulator %s not found: %ld\n",
393814740e7SChen-Yu Tsai 				name, PTR_ERR(regulator));
394814740e7SChen-Yu Tsai 			continue;
395814740e7SChen-Yu Tsai 		}
396814740e7SChen-Yu Tsai 		par->regulators[i++] = regulator;
397814740e7SChen-Yu Tsai 	}
398814740e7SChen-Yu Tsai 	par->regulator_count = i;
399814740e7SChen-Yu Tsai 
400a3accfd7SHans de Goede 	return 0;
401a3accfd7SHans de Goede }
402a3accfd7SHans de Goede 
simplefb_regulators_enable(struct simplefb_par * par,struct platform_device * pdev)403a3accfd7SHans de Goede static void simplefb_regulators_enable(struct simplefb_par *par,
404a3accfd7SHans de Goede 				       struct platform_device *pdev)
405a3accfd7SHans de Goede {
406a3accfd7SHans de Goede 	int i, ret;
407a3accfd7SHans de Goede 
408814740e7SChen-Yu Tsai 	/* Enable all the regulators */
409814740e7SChen-Yu Tsai 	for (i = 0; i < par->regulator_count; i++) {
410814740e7SChen-Yu Tsai 		ret = regulator_enable(par->regulators[i]);
411814740e7SChen-Yu Tsai 		if (ret) {
412814740e7SChen-Yu Tsai 			dev_err(&pdev->dev,
413814740e7SChen-Yu Tsai 				"failed to enable regulator %d: %d\n",
414814740e7SChen-Yu Tsai 				i, ret);
415814740e7SChen-Yu Tsai 			devm_regulator_put(par->regulators[i]);
416814740e7SChen-Yu Tsai 			par->regulators[i] = NULL;
417814740e7SChen-Yu Tsai 		}
418814740e7SChen-Yu Tsai 	}
419a3accfd7SHans de Goede 	par->regulators_enabled = true;
420814740e7SChen-Yu Tsai }
421814740e7SChen-Yu Tsai 
simplefb_regulators_destroy(struct simplefb_par * par)422814740e7SChen-Yu Tsai static void simplefb_regulators_destroy(struct simplefb_par *par)
423814740e7SChen-Yu Tsai {
424814740e7SChen-Yu Tsai 	int i;
425814740e7SChen-Yu Tsai 
426a3accfd7SHans de Goede 	if (!par->regulators || !par->regulators_enabled)
427814740e7SChen-Yu Tsai 		return;
428814740e7SChen-Yu Tsai 
429814740e7SChen-Yu Tsai 	for (i = 0; i < par->regulator_count; i++)
430814740e7SChen-Yu Tsai 		if (par->regulators[i])
431814740e7SChen-Yu Tsai 			regulator_disable(par->regulators[i]);
432814740e7SChen-Yu Tsai }
433814740e7SChen-Yu Tsai #else
simplefb_regulators_get(struct simplefb_par * par,struct platform_device * pdev)434a3accfd7SHans de Goede static int simplefb_regulators_get(struct simplefb_par *par,
435814740e7SChen-Yu Tsai 	struct platform_device *pdev) { return 0; }
simplefb_regulators_enable(struct simplefb_par * par,struct platform_device * pdev)436a3accfd7SHans de Goede static void simplefb_regulators_enable(struct simplefb_par *par,
437a3accfd7SHans de Goede 	struct platform_device *pdev) { }
simplefb_regulators_destroy(struct simplefb_par * par)438814740e7SChen-Yu Tsai static void simplefb_regulators_destroy(struct simplefb_par *par) { }
439814740e7SChen-Yu Tsai #endif
440814740e7SChen-Yu Tsai 
44192a511a5SThierry Reding #if defined CONFIG_OF && defined CONFIG_PM_GENERIC_DOMAINS
simplefb_detach_genpds(void * res)44292a511a5SThierry Reding static void simplefb_detach_genpds(void *res)
44392a511a5SThierry Reding {
44492a511a5SThierry Reding 	struct simplefb_par *par = res;
44592a511a5SThierry Reding 	unsigned int i = par->num_genpds;
44692a511a5SThierry Reding 
44792a511a5SThierry Reding 	if (par->num_genpds <= 1)
44892a511a5SThierry Reding 		return;
44992a511a5SThierry Reding 
45092a511a5SThierry Reding 	while (i--) {
45192a511a5SThierry Reding 		if (par->genpd_links[i])
45292a511a5SThierry Reding 			device_link_del(par->genpd_links[i]);
45392a511a5SThierry Reding 
45492a511a5SThierry Reding 		if (!IS_ERR_OR_NULL(par->genpds[i]))
45592a511a5SThierry Reding 			dev_pm_domain_detach(par->genpds[i], true);
45692a511a5SThierry Reding 	}
45792a511a5SThierry Reding }
45892a511a5SThierry Reding 
simplefb_attach_genpds(struct simplefb_par * par,struct platform_device * pdev)45992a511a5SThierry Reding static int simplefb_attach_genpds(struct simplefb_par *par,
46092a511a5SThierry Reding 				  struct platform_device *pdev)
46192a511a5SThierry Reding {
46292a511a5SThierry Reding 	struct device *dev = &pdev->dev;
46392a511a5SThierry Reding 	unsigned int i;
46492a511a5SThierry Reding 	int err;
46592a511a5SThierry Reding 
46692a511a5SThierry Reding 	err = of_count_phandle_with_args(dev->of_node, "power-domains",
46792a511a5SThierry Reding 					 "#power-domain-cells");
46892a511a5SThierry Reding 	if (err < 0) {
4699c689432SRichard Acayan 		/* Nothing wrong if optional PDs are missing */
4709c689432SRichard Acayan 		if (err == -ENOENT)
4719c689432SRichard Acayan 			return 0;
4729c689432SRichard Acayan 
4734350aa21SBrian Masney 		dev_err(dev, "failed to parse power-domains: %d\n", err);
47492a511a5SThierry Reding 		return err;
47592a511a5SThierry Reding 	}
47692a511a5SThierry Reding 
47792a511a5SThierry Reding 	par->num_genpds = err;
47892a511a5SThierry Reding 
47992a511a5SThierry Reding 	/*
48092a511a5SThierry Reding 	 * Single power-domain devices are handled by the driver core, so
48192a511a5SThierry Reding 	 * nothing to do here.
48292a511a5SThierry Reding 	 */
48392a511a5SThierry Reding 	if (par->num_genpds <= 1)
48492a511a5SThierry Reding 		return 0;
48592a511a5SThierry Reding 
48692a511a5SThierry Reding 	par->genpds = devm_kcalloc(dev, par->num_genpds, sizeof(*par->genpds),
48792a511a5SThierry Reding 				   GFP_KERNEL);
48892a511a5SThierry Reding 	if (!par->genpds)
48992a511a5SThierry Reding 		return -ENOMEM;
49092a511a5SThierry Reding 
49192a511a5SThierry Reding 	par->genpd_links = devm_kcalloc(dev, par->num_genpds,
49292a511a5SThierry Reding 					sizeof(*par->genpd_links),
49392a511a5SThierry Reding 					GFP_KERNEL);
49492a511a5SThierry Reding 	if (!par->genpd_links)
49592a511a5SThierry Reding 		return -ENOMEM;
49692a511a5SThierry Reding 
49792a511a5SThierry Reding 	for (i = 0; i < par->num_genpds; i++) {
49892a511a5SThierry Reding 		par->genpds[i] = dev_pm_domain_attach_by_id(dev, i);
49992a511a5SThierry Reding 		if (IS_ERR(par->genpds[i])) {
50092a511a5SThierry Reding 			err = PTR_ERR(par->genpds[i]);
50192a511a5SThierry Reding 			if (err == -EPROBE_DEFER) {
50292a511a5SThierry Reding 				simplefb_detach_genpds(par);
50392a511a5SThierry Reding 				return err;
50492a511a5SThierry Reding 			}
50592a511a5SThierry Reding 
50692a511a5SThierry Reding 			dev_warn(dev, "failed to attach domain %u: %d\n", i, err);
50792a511a5SThierry Reding 			continue;
50892a511a5SThierry Reding 		}
50992a511a5SThierry Reding 
51092a511a5SThierry Reding 		par->genpd_links[i] = device_link_add(dev, par->genpds[i],
51192a511a5SThierry Reding 						      DL_FLAG_STATELESS |
51292a511a5SThierry Reding 						      DL_FLAG_PM_RUNTIME |
51392a511a5SThierry Reding 						      DL_FLAG_RPM_ACTIVE);
51492a511a5SThierry Reding 		if (!par->genpd_links[i])
51592a511a5SThierry Reding 			dev_warn(dev, "failed to link power-domain %u\n", i);
51692a511a5SThierry Reding 	}
51792a511a5SThierry Reding 
51892a511a5SThierry Reding 	return devm_add_action_or_reset(dev, simplefb_detach_genpds, par);
51992a511a5SThierry Reding }
52092a511a5SThierry Reding #else
simplefb_attach_genpds(struct simplefb_par * par,struct platform_device * pdev)52192a511a5SThierry Reding static int simplefb_attach_genpds(struct simplefb_par *par,
52292a511a5SThierry Reding 				  struct platform_device *pdev)
52392a511a5SThierry Reding {
52492a511a5SThierry Reding 	return 0;
52592a511a5SThierry Reding }
52692a511a5SThierry Reding #endif
52792a511a5SThierry Reding 
simplefb_probe(struct platform_device * pdev)528f7018c21STomi Valkeinen static int simplefb_probe(struct platform_device *pdev)
529f7018c21STomi Valkeinen {
530f7018c21STomi Valkeinen 	int ret;
531f7018c21STomi Valkeinen 	struct simplefb_params params;
532f7018c21STomi Valkeinen 	struct fb_info *info;
5331270be4aSLuc Verhaegen 	struct simplefb_par *par;
534748bd587SThomas Zimmermann 	struct resource *res, *mem;
535f7018c21STomi Valkeinen 
536f7018c21STomi Valkeinen 	if (fb_get_options("simplefb", NULL))
537f7018c21STomi Valkeinen 		return -ENODEV;
538f7018c21STomi Valkeinen 
539f7018c21STomi Valkeinen 	ret = -ENODEV;
540f7018c21STomi Valkeinen 	if (dev_get_platdata(&pdev->dev))
541f7018c21STomi Valkeinen 		ret = simplefb_parse_pd(pdev, &params);
542f7018c21STomi Valkeinen 	else if (pdev->dev.of_node)
543f7018c21STomi Valkeinen 		ret = simplefb_parse_dt(pdev, &params);
544f7018c21STomi Valkeinen 
545f7018c21STomi Valkeinen 	if (ret)
546f7018c21STomi Valkeinen 		return ret;
547f7018c21STomi Valkeinen 
5488ddfc01aSThierry Reding 	if (params.memory.start == 0 && params.memory.end == 0) {
549748bd587SThomas Zimmermann 		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
550748bd587SThomas Zimmermann 		if (!res) {
551f7018c21STomi Valkeinen 			dev_err(&pdev->dev, "No memory resource\n");
552f7018c21STomi Valkeinen 			return -EINVAL;
553f7018c21STomi Valkeinen 		}
5548ddfc01aSThierry Reding 	} else {
5558ddfc01aSThierry Reding 		res = &params.memory;
5568ddfc01aSThierry Reding 	}
557f7018c21STomi Valkeinen 
558748bd587SThomas Zimmermann 	mem = request_mem_region(res->start, resource_size(res), "simplefb");
559748bd587SThomas Zimmermann 	if (!mem) {
560748bd587SThomas Zimmermann 		/*
561748bd587SThomas Zimmermann 		 * We cannot make this fatal. Sometimes this comes from magic
562748bd587SThomas Zimmermann 		 * spaces our resource handlers simply don't know about. Use
563748bd587SThomas Zimmermann 		 * the I/O-memory resource as-is and try to map that instead.
564748bd587SThomas Zimmermann 		 */
565748bd587SThomas Zimmermann 		dev_warn(&pdev->dev, "simplefb: cannot reserve video memory at %pR\n", res);
566748bd587SThomas Zimmermann 		mem = res;
567748bd587SThomas Zimmermann 	}
568748bd587SThomas Zimmermann 
5691270be4aSLuc Verhaegen 	info = framebuffer_alloc(sizeof(struct simplefb_par), &pdev->dev);
570748bd587SThomas Zimmermann 	if (!info) {
571748bd587SThomas Zimmermann 		ret = -ENOMEM;
572748bd587SThomas Zimmermann 		goto error_release_mem_region;
573748bd587SThomas Zimmermann 	}
574f7018c21STomi Valkeinen 	platform_set_drvdata(pdev, info);
575f7018c21STomi Valkeinen 
5761270be4aSLuc Verhaegen 	par = info->par;
5771270be4aSLuc Verhaegen 
578f7018c21STomi Valkeinen 	info->fix = simplefb_fix;
579f7018c21STomi Valkeinen 	info->fix.smem_start = mem->start;
580f7018c21STomi Valkeinen 	info->fix.smem_len = resource_size(mem);
581f7018c21STomi Valkeinen 	info->fix.line_length = params.stride;
582f7018c21STomi Valkeinen 
583f7018c21STomi Valkeinen 	info->var = simplefb_var;
584f7018c21STomi Valkeinen 	info->var.xres = params.width;
585f7018c21STomi Valkeinen 	info->var.yres = params.height;
586f7018c21STomi Valkeinen 	info->var.xres_virtual = params.width;
587f7018c21STomi Valkeinen 	info->var.yres_virtual = params.height;
588f7018c21STomi Valkeinen 	info->var.bits_per_pixel = params.format->bits_per_pixel;
589f7018c21STomi Valkeinen 	info->var.red = params.format->red;
590f7018c21STomi Valkeinen 	info->var.green = params.format->green;
591f7018c21STomi Valkeinen 	info->var.blue = params.format->blue;
592f7018c21STomi Valkeinen 	info->var.transp = params.format->transp;
593f7018c21STomi Valkeinen 
594d9702b2aSThomas Zimmermann 	par->base = info->fix.smem_start;
595d9702b2aSThomas Zimmermann 	par->size = info->fix.smem_len;
596f7018c21STomi Valkeinen 
597f7018c21STomi Valkeinen 	info->fbops = &simplefb_ops;
598f7018c21STomi Valkeinen 	info->screen_base = ioremap_wc(info->fix.smem_start,
599f7018c21STomi Valkeinen 				       info->fix.smem_len);
600f7018c21STomi Valkeinen 	if (!info->screen_base) {
601bf2fda15SLuc Verhaegen 		ret = -ENOMEM;
602bf2fda15SLuc Verhaegen 		goto error_fb_release;
603f7018c21STomi Valkeinen 	}
6041270be4aSLuc Verhaegen 	info->pseudo_palette = par->palette;
605f7018c21STomi Valkeinen 
606a3accfd7SHans de Goede 	ret = simplefb_clocks_get(par, pdev);
607fc219bfdSLuc Verhaegen 	if (ret < 0)
608fc219bfdSLuc Verhaegen 		goto error_unmap;
609fc219bfdSLuc Verhaegen 
610a3accfd7SHans de Goede 	ret = simplefb_regulators_get(par, pdev);
611814740e7SChen-Yu Tsai 	if (ret < 0)
612814740e7SChen-Yu Tsai 		goto error_clocks;
613814740e7SChen-Yu Tsai 
61492a511a5SThierry Reding 	ret = simplefb_attach_genpds(par, pdev);
61592a511a5SThierry Reding 	if (ret < 0)
61692a511a5SThierry Reding 		goto error_regulators;
61792a511a5SThierry Reding 
618a3accfd7SHans de Goede 	simplefb_clocks_enable(par, pdev);
619a3accfd7SHans de Goede 	simplefb_regulators_enable(par, pdev);
620a3accfd7SHans de Goede 
6215269a618SPeter Robinson 	dev_info(&pdev->dev, "framebuffer at 0x%lx, 0x%x bytes\n",
6225269a618SPeter Robinson 			     info->fix.smem_start, info->fix.smem_len);
623f7018c21STomi Valkeinen 	dev_info(&pdev->dev, "format=%s, mode=%dx%dx%d, linelength=%d\n",
624f7018c21STomi Valkeinen 			     params.format->name,
625f7018c21STomi Valkeinen 			     info->var.xres, info->var.yres,
626f7018c21STomi Valkeinen 			     info->var.bits_per_pixel, info->fix.line_length);
627f7018c21STomi Valkeinen 
628748bd587SThomas Zimmermann 	if (mem != res)
629748bd587SThomas Zimmermann 		par->mem = mem; /* release in clean-up handler */
630748bd587SThomas Zimmermann 
631d9702b2aSThomas Zimmermann 	ret = devm_aperture_acquire_for_platform_device(pdev, par->base, par->size);
632d9702b2aSThomas Zimmermann 	if (ret) {
633d9702b2aSThomas Zimmermann 		dev_err(&pdev->dev, "Unable to acquire aperture: %d\n", ret);
634d9702b2aSThomas Zimmermann 		goto error_regulators;
635d9702b2aSThomas Zimmermann 	}
636f7018c21STomi Valkeinen 	ret = register_framebuffer(info);
637f7018c21STomi Valkeinen 	if (ret < 0) {
638f7018c21STomi Valkeinen 		dev_err(&pdev->dev, "Unable to register simplefb: %d\n", ret);
639814740e7SChen-Yu Tsai 		goto error_regulators;
640f7018c21STomi Valkeinen 	}
641f7018c21STomi Valkeinen 
642f7018c21STomi Valkeinen 	dev_info(&pdev->dev, "fb%d: simplefb registered!\n", info->node);
643f7018c21STomi Valkeinen 
644f7018c21STomi Valkeinen 	return 0;
645bf2fda15SLuc Verhaegen 
646814740e7SChen-Yu Tsai error_regulators:
647814740e7SChen-Yu Tsai 	simplefb_regulators_destroy(par);
648fc219bfdSLuc Verhaegen error_clocks:
649fc219bfdSLuc Verhaegen 	simplefb_clocks_destroy(par);
650bf2fda15SLuc Verhaegen error_unmap:
651bf2fda15SLuc Verhaegen 	iounmap(info->screen_base);
652bf2fda15SLuc Verhaegen error_fb_release:
653bf2fda15SLuc Verhaegen 	framebuffer_release(info);
654748bd587SThomas Zimmermann error_release_mem_region:
655748bd587SThomas Zimmermann 	if (mem != res)
656748bd587SThomas Zimmermann 		release_mem_region(mem->start, resource_size(mem));
657bf2fda15SLuc Verhaegen 	return ret;
658f7018c21STomi Valkeinen }
659f7018c21STomi Valkeinen 
simplefb_remove(struct platform_device * pdev)6604ba34066SUwe Kleine-König static void simplefb_remove(struct platform_device *pdev)
661f7018c21STomi Valkeinen {
662f7018c21STomi Valkeinen 	struct fb_info *info = platform_get_drvdata(pdev);
663f7018c21STomi Valkeinen 
664666b90b3SJavier Martinez Canillas 	/* simplefb_destroy takes care of info cleanup */
665f7018c21STomi Valkeinen 	unregister_framebuffer(info);
666f7018c21STomi Valkeinen }
667f7018c21STomi Valkeinen 
668f7018c21STomi Valkeinen static const struct of_device_id simplefb_of_match[] = {
669f7018c21STomi Valkeinen 	{ .compatible = "simple-framebuffer", },
670f7018c21STomi Valkeinen 	{ },
671f7018c21STomi Valkeinen };
672f7018c21STomi Valkeinen MODULE_DEVICE_TABLE(of, simplefb_of_match);
673f7018c21STomi Valkeinen 
674f7018c21STomi Valkeinen static struct platform_driver simplefb_driver = {
675f7018c21STomi Valkeinen 	.driver = {
676f7018c21STomi Valkeinen 		.name = "simple-framebuffer",
677f7018c21STomi Valkeinen 		.of_match_table = simplefb_of_match,
678f7018c21STomi Valkeinen 	},
679f7018c21STomi Valkeinen 	.probe = simplefb_probe,
680*01ecc142SUwe Kleine-König 	.remove = simplefb_remove,
681f7018c21STomi Valkeinen };
68289c95001SHans de Goede 
6832f92ea21SHector Martin module_platform_driver(simplefb_driver);
684f7018c21STomi Valkeinen 
685f7018c21STomi Valkeinen MODULE_AUTHOR("Stephen Warren <[email protected]>");
686f7018c21STomi Valkeinen MODULE_DESCRIPTION("Simple framebuffer driver");
687f7018c21STomi Valkeinen MODULE_LICENSE("GPL v2");
688