// SPDX-License-Identifier: GPL-2.0-only /* * Simplest possible simple frame-buffer driver, as a platform device * * Copyright (c) 2013, Stephen Warren * * Based on q40fb.c, which was: * Copyright (C) 2001 Richard Zidlicky <rz@linux-m68k.org> * * Also based on offb.c, which was: * Copyright (C) 1997 Geert Uytterhoeven * Copyright (C) 1996 Paul Mackerras
*/
/* * fb_ops.fb_destroy is called by the last put_fb_info() call at the end * of unregister_framebuffer() or fb_release(). Do any cleanup here.
*/ staticvoid simplefb_destroy(struct fb_info *info)
{ struct simplefb_par *par = info->par; struct resource *mem = par->mem;
simplefb_regulators_destroy(info->par);
simplefb_clocks_destroy(info->par);
simplefb_detach_genpds(info->par); if (info->screen_base)
iounmap(info->screen_base);
framebuffer_release(info);
if (mem)
release_mem_region(mem->start, resource_size(mem));
}
#ifdefined CONFIG_OF && defined CONFIG_COMMON_CLK /* * Clock handling code. * * Here we handle the clocks property of our "simple-framebuffer" dt node. * This is necessary so that we can make sure that any clocks needed by * the display engine that the bootloader set up for us (and for which it * provided a simplefb dt node), stay up, for the life of the simplefb * driver. * * When the driver unloads, we cleanly disable, and then release the clocks. * * We only complain about errors here, no action is taken as the most likely * error can only happen due to a mismatch between the bootloader which set * up simplefb, and the clock definitions in the device tree. Chances are * that there are no adverse effects, and if there are, a clean teardown of * the fb probe will not help us much either. So just complain and carry on, * and hope that the user actually gets a working fb at the end of things.
*/ staticint simplefb_clocks_get(struct simplefb_par *par, struct platform_device *pdev)
{ struct device_node *np = pdev->dev.of_node; struct clk *clock; int i;
if (dev_get_platdata(&pdev->dev) || !np) return 0;
par->clk_count = of_clk_get_parent_count(np); if (!par->clk_count) return 0;
for (i = 0; i < par->clk_count; i++) {
clock = of_clk_get(np, i); if (IS_ERR(clock)) { if (PTR_ERR(clock) == -EPROBE_DEFER) { while (--i >= 0) {
clk_put(par->clks[i]);
}
kfree(par->clks); return -EPROBE_DEFER;
}
dev_err(&pdev->dev, "%s: clock %d not found: %ld\n",
__func__, i, PTR_ERR(clock)); continue;
}
par->clks[i] = clock;
}
return 0;
}
staticvoid simplefb_clocks_enable(struct simplefb_par *par, struct platform_device *pdev)
{ int i, ret;
for (i = 0; i < par->clk_count; i++) { if (par->clks[i]) {
ret = clk_prepare_enable(par->clks[i]); if (ret) {
dev_err(&pdev->dev, "%s: failed to enable clock %d: %d\n",
__func__, i, ret);
clk_put(par->clks[i]);
par->clks[i] = NULL;
}
}
}
par->clks_enabled = true;
}
staticvoid simplefb_clocks_destroy(struct simplefb_par *par)
{ int i;
if (!par->clks) return;
for (i = 0; i < par->clk_count; i++) { if (par->clks[i]) { if (par->clks_enabled)
clk_disable_unprepare(par->clks[i]);
clk_put(par->clks[i]);
}
}
/* * Regulator handling code. * * Here we handle the num-supplies and vin*-supply properties of our * "simple-framebuffer" dt node. This is necessary so that we can make sure * that any regulators needed by the display hardware that the bootloader * set up for us (and for which it provided a simplefb dt node), stay up, * for the life of the simplefb driver. * * When the driver unloads, we cleanly disable, and then release the * regulators. * * We only complain about errors here, no action is taken as the most likely * error can only happen due to a mismatch between the bootloader which set * up simplefb, and the regulator definitions in the device tree. Chances are * that there are no adverse effects, and if there are, a clean teardown of * the fb probe will not help us much either. So just complain and carry on, * and hope that the user actually gets a working fb at the end of things.
*/ staticint simplefb_regulators_get(struct simplefb_par *par, struct platform_device *pdev)
{ struct device_node *np = pdev->dev.of_node; struct property *prop; struct regulator *regulator; constchar *p; int count = 0, i = 0;
if (dev_get_platdata(&pdev->dev) || !np) return 0;
/* Count the number of regulator supplies */
for_each_property_of_node(np, prop) {
p = strstr(prop->name, SUPPLY_SUFFIX); if (p && p != prop->name)
count++;
}
staticvoid simplefb_regulators_enable(struct simplefb_par *par, struct platform_device *pdev)
{ int i, ret;
/* Enable all the regulators */ for (i = 0; i < par->regulator_count; i++) {
ret = regulator_enable(par->regulators[i]); if (ret) {
dev_err(&pdev->dev, "failed to enable regulator %d: %d\n",
i, ret);
devm_regulator_put(par->regulators[i]);
par->regulators[i] = NULL;
}
}
par->regulators_enabled = true;
}
staticvoid simplefb_regulators_destroy(struct simplefb_par *par)
{ int i;
if (!par->regulators || !par->regulators_enabled) return;
for (i = 0; i < par->regulator_count; i++) if (par->regulators[i])
regulator_disable(par->regulators[i]);
} #else staticint simplefb_regulators_get(struct simplefb_par *par, struct platform_device *pdev) { return 0; } staticvoid simplefb_regulators_enable(struct simplefb_par *par, struct platform_device *pdev) { } staticvoid simplefb_regulators_destroy(struct simplefb_par *par) { } #endif
#ifdefined CONFIG_OF && defined CONFIG_PM_GENERIC_DOMAINS staticvoid simplefb_detach_genpds(void *res)
{ struct simplefb_par *par = res; unsignedint i = par->num_genpds;
if (par->num_genpds <= 1) return;
while (i--) { if (par->genpd_links[i])
device_link_del(par->genpd_links[i]);
if (!IS_ERR_OR_NULL(par->genpds[i]))
dev_pm_domain_detach(par->genpds[i], true);
}
par->num_genpds = 0;
}
staticint simplefb_attach_genpds(struct simplefb_par *par, struct platform_device *pdev)
{ struct device *dev = &pdev->dev; unsignedint i, num_genpds; int err;
err = of_count_phandle_with_args(dev->of_node, "power-domains", "#power-domain-cells"); if (err < 0) { /* Nothing wrong if optional PDs are missing */ if (err == -ENOENT) return 0;
dev_err(dev, "failed to parse power-domains: %d\n", err); return err;
}
num_genpds = err;
/* * Single power-domain devices are handled by the driver core, so * nothing to do here.
*/ if (num_genpds <= 1) {
par->num_genpds = num_genpds; return 0;
}
par->genpds = devm_kcalloc(dev, num_genpds, sizeof(*par->genpds),
GFP_KERNEL); if (!par->genpds) return -ENOMEM;
par->genpd_links = devm_kcalloc(dev, num_genpds, sizeof(*par->genpd_links),
GFP_KERNEL); if (!par->genpd_links) return -ENOMEM;
/* * Set par->num_genpds only after genpds and genpd_links are allocated * to exit early from simplefb_detach_genpds() without full * initialisation.
*/
par->num_genpds = num_genpds;
for (i = 0; i < par->num_genpds; i++) {
par->genpds[i] = dev_pm_domain_attach_by_id(dev, i); if (IS_ERR(par->genpds[i])) {
err = PTR_ERR(par->genpds[i]); if (err == -EPROBE_DEFER) {
simplefb_detach_genpds(par); return err;
}
dev_warn(dev, "failed to attach domain %u: %d\n", i, err); continue;
}
par->genpd_links[i] = device_link_add(dev, par->genpds[i],
DL_FLAG_STATELESS |
DL_FLAG_PM_RUNTIME |
DL_FLAG_RPM_ACTIVE); if (!par->genpd_links[i])
dev_warn(dev, "failed to link power-domain %u\n", i);
}
if (fb_get_options("simplefb", NULL)) return -ENODEV;
ret = -ENODEV; if (dev_get_platdata(&pdev->dev))
ret = simplefb_parse_pd(pdev, ¶ms); elseif (pdev->dev.of_node)
ret = simplefb_parse_dt(pdev, ¶ms);
if (ret) return ret;
if (params.memory.start == 0 && params.memory.end == 0) {
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) {
dev_err(&pdev->dev, "No memory resource\n"); return -EINVAL;
}
} else {
res = ¶ms.memory;
}
mem = request_mem_region(res->start, resource_size(res), "simplefb"); if (!mem) { /* * We cannot make this fatal. Sometimes this comes from magic * spaces our resource handlers simply don't know about. Use * the I/O-memory resource as-is and try to map that instead.
*/
dev_warn(&pdev->dev, "simplefb: cannot reserve video memory at %pR\n", res);
mem = res;
}
info = framebuffer_alloc(sizeof(struct simplefb_par), &pdev->dev); if (!info) {
ret = -ENOMEM; goto error_release_mem_region;
}
platform_set_drvdata(pdev, info);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.