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> 28*92a511a5SThierry 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 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 82*92a511a5SThierry Reding #if defined CONFIG_OF && defined CONFIG_PM_GENERIC_DOMAINS 83*92a511a5SThierry Reding unsigned int num_genpds; 84*92a511a5SThierry Reding struct device **genpds; 85*92a511a5SThierry Reding struct device_link **genpd_links; 86*92a511a5SThierry 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 */ 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 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", ¶ms->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", ¶ms->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", ¶ms->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, ¶ms->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(¶ms->memory, 0, sizeof(params->memory)); 1928ddfc01aSThierry Reding } 1938ddfc01aSThierry Reding 194f7018c21STomi Valkeinen return 0; 195f7018c21STomi Valkeinen } 196f7018c21STomi Valkeinen 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(¶ms->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 */ 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 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 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 321a3accfd7SHans de Goede static int simplefb_clocks_get(struct simplefb_par *par, 322fc219bfdSLuc Verhaegen struct platform_device *pdev) { return 0; } 323a3accfd7SHans de Goede static void simplefb_clocks_enable(struct simplefb_par *par, 324a3accfd7SHans de Goede struct platform_device *pdev) { } 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 */ 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 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 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 434a3accfd7SHans de Goede static int simplefb_regulators_get(struct simplefb_par *par, 435814740e7SChen-Yu Tsai struct platform_device *pdev) { return 0; } 436a3accfd7SHans de Goede static void simplefb_regulators_enable(struct simplefb_par *par, 437a3accfd7SHans de Goede struct platform_device *pdev) { } 438814740e7SChen-Yu Tsai static void simplefb_regulators_destroy(struct simplefb_par *par) { } 439814740e7SChen-Yu Tsai #endif 440814740e7SChen-Yu Tsai 441*92a511a5SThierry Reding #if defined CONFIG_OF && defined CONFIG_PM_GENERIC_DOMAINS 442*92a511a5SThierry Reding static void simplefb_detach_genpds(void *res) 443*92a511a5SThierry Reding { 444*92a511a5SThierry Reding struct simplefb_par *par = res; 445*92a511a5SThierry Reding unsigned int i = par->num_genpds; 446*92a511a5SThierry Reding 447*92a511a5SThierry Reding if (par->num_genpds <= 1) 448*92a511a5SThierry Reding return; 449*92a511a5SThierry Reding 450*92a511a5SThierry Reding while (i--) { 451*92a511a5SThierry Reding if (par->genpd_links[i]) 452*92a511a5SThierry Reding device_link_del(par->genpd_links[i]); 453*92a511a5SThierry Reding 454*92a511a5SThierry Reding if (!IS_ERR_OR_NULL(par->genpds[i])) 455*92a511a5SThierry Reding dev_pm_domain_detach(par->genpds[i], true); 456*92a511a5SThierry Reding } 457*92a511a5SThierry Reding } 458*92a511a5SThierry Reding 459*92a511a5SThierry Reding static int simplefb_attach_genpds(struct simplefb_par *par, 460*92a511a5SThierry Reding struct platform_device *pdev) 461*92a511a5SThierry Reding { 462*92a511a5SThierry Reding struct device *dev = &pdev->dev; 463*92a511a5SThierry Reding unsigned int i; 464*92a511a5SThierry Reding int err; 465*92a511a5SThierry Reding 466*92a511a5SThierry Reding err = of_count_phandle_with_args(dev->of_node, "power-domains", 467*92a511a5SThierry Reding "#power-domain-cells"); 468*92a511a5SThierry Reding if (err < 0) { 469*92a511a5SThierry Reding dev_info(dev, "failed to parse power-domains: %d\n", err); 470*92a511a5SThierry Reding return err; 471*92a511a5SThierry Reding } 472*92a511a5SThierry Reding 473*92a511a5SThierry Reding par->num_genpds = err; 474*92a511a5SThierry Reding 475*92a511a5SThierry Reding /* 476*92a511a5SThierry Reding * Single power-domain devices are handled by the driver core, so 477*92a511a5SThierry Reding * nothing to do here. 478*92a511a5SThierry Reding */ 479*92a511a5SThierry Reding if (par->num_genpds <= 1) 480*92a511a5SThierry Reding return 0; 481*92a511a5SThierry Reding 482*92a511a5SThierry Reding par->genpds = devm_kcalloc(dev, par->num_genpds, sizeof(*par->genpds), 483*92a511a5SThierry Reding GFP_KERNEL); 484*92a511a5SThierry Reding if (!par->genpds) 485*92a511a5SThierry Reding return -ENOMEM; 486*92a511a5SThierry Reding 487*92a511a5SThierry Reding par->genpd_links = devm_kcalloc(dev, par->num_genpds, 488*92a511a5SThierry Reding sizeof(*par->genpd_links), 489*92a511a5SThierry Reding GFP_KERNEL); 490*92a511a5SThierry Reding if (!par->genpd_links) 491*92a511a5SThierry Reding return -ENOMEM; 492*92a511a5SThierry Reding 493*92a511a5SThierry Reding for (i = 0; i < par->num_genpds; i++) { 494*92a511a5SThierry Reding par->genpds[i] = dev_pm_domain_attach_by_id(dev, i); 495*92a511a5SThierry Reding if (IS_ERR(par->genpds[i])) { 496*92a511a5SThierry Reding err = PTR_ERR(par->genpds[i]); 497*92a511a5SThierry Reding if (err == -EPROBE_DEFER) { 498*92a511a5SThierry Reding simplefb_detach_genpds(par); 499*92a511a5SThierry Reding return err; 500*92a511a5SThierry Reding } 501*92a511a5SThierry Reding 502*92a511a5SThierry Reding dev_warn(dev, "failed to attach domain %u: %d\n", i, err); 503*92a511a5SThierry Reding continue; 504*92a511a5SThierry Reding } 505*92a511a5SThierry Reding 506*92a511a5SThierry Reding par->genpd_links[i] = device_link_add(dev, par->genpds[i], 507*92a511a5SThierry Reding DL_FLAG_STATELESS | 508*92a511a5SThierry Reding DL_FLAG_PM_RUNTIME | 509*92a511a5SThierry Reding DL_FLAG_RPM_ACTIVE); 510*92a511a5SThierry Reding if (!par->genpd_links[i]) 511*92a511a5SThierry Reding dev_warn(dev, "failed to link power-domain %u\n", i); 512*92a511a5SThierry Reding } 513*92a511a5SThierry Reding 514*92a511a5SThierry Reding return devm_add_action_or_reset(dev, simplefb_detach_genpds, par); 515*92a511a5SThierry Reding } 516*92a511a5SThierry Reding #else 517*92a511a5SThierry Reding static int simplefb_attach_genpds(struct simplefb_par *par, 518*92a511a5SThierry Reding struct platform_device *pdev) 519*92a511a5SThierry Reding { 520*92a511a5SThierry Reding return 0; 521*92a511a5SThierry Reding } 522*92a511a5SThierry Reding #endif 523*92a511a5SThierry Reding 524f7018c21STomi Valkeinen static int simplefb_probe(struct platform_device *pdev) 525f7018c21STomi Valkeinen { 526f7018c21STomi Valkeinen int ret; 527f7018c21STomi Valkeinen struct simplefb_params params; 528f7018c21STomi Valkeinen struct fb_info *info; 5291270be4aSLuc Verhaegen struct simplefb_par *par; 530748bd587SThomas Zimmermann struct resource *res, *mem; 531f7018c21STomi Valkeinen 532f7018c21STomi Valkeinen if (fb_get_options("simplefb", NULL)) 533f7018c21STomi Valkeinen return -ENODEV; 534f7018c21STomi Valkeinen 535f7018c21STomi Valkeinen ret = -ENODEV; 536f7018c21STomi Valkeinen if (dev_get_platdata(&pdev->dev)) 537f7018c21STomi Valkeinen ret = simplefb_parse_pd(pdev, ¶ms); 538f7018c21STomi Valkeinen else if (pdev->dev.of_node) 539f7018c21STomi Valkeinen ret = simplefb_parse_dt(pdev, ¶ms); 540f7018c21STomi Valkeinen 541f7018c21STomi Valkeinen if (ret) 542f7018c21STomi Valkeinen return ret; 543f7018c21STomi Valkeinen 5448ddfc01aSThierry Reding if (params.memory.start == 0 && params.memory.end == 0) { 545748bd587SThomas Zimmermann res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 546748bd587SThomas Zimmermann if (!res) { 547f7018c21STomi Valkeinen dev_err(&pdev->dev, "No memory resource\n"); 548f7018c21STomi Valkeinen return -EINVAL; 549f7018c21STomi Valkeinen } 5508ddfc01aSThierry Reding } else { 5518ddfc01aSThierry Reding res = ¶ms.memory; 5528ddfc01aSThierry Reding } 553f7018c21STomi Valkeinen 554748bd587SThomas Zimmermann mem = request_mem_region(res->start, resource_size(res), "simplefb"); 555748bd587SThomas Zimmermann if (!mem) { 556748bd587SThomas Zimmermann /* 557748bd587SThomas Zimmermann * We cannot make this fatal. Sometimes this comes from magic 558748bd587SThomas Zimmermann * spaces our resource handlers simply don't know about. Use 559748bd587SThomas Zimmermann * the I/O-memory resource as-is and try to map that instead. 560748bd587SThomas Zimmermann */ 561748bd587SThomas Zimmermann dev_warn(&pdev->dev, "simplefb: cannot reserve video memory at %pR\n", res); 562748bd587SThomas Zimmermann mem = res; 563748bd587SThomas Zimmermann } 564748bd587SThomas Zimmermann 5651270be4aSLuc Verhaegen info = framebuffer_alloc(sizeof(struct simplefb_par), &pdev->dev); 566748bd587SThomas Zimmermann if (!info) { 567748bd587SThomas Zimmermann ret = -ENOMEM; 568748bd587SThomas Zimmermann goto error_release_mem_region; 569748bd587SThomas Zimmermann } 570f7018c21STomi Valkeinen platform_set_drvdata(pdev, info); 571f7018c21STomi Valkeinen 5721270be4aSLuc Verhaegen par = info->par; 5731270be4aSLuc Verhaegen 574f7018c21STomi Valkeinen info->fix = simplefb_fix; 575f7018c21STomi Valkeinen info->fix.smem_start = mem->start; 576f7018c21STomi Valkeinen info->fix.smem_len = resource_size(mem); 577f7018c21STomi Valkeinen info->fix.line_length = params.stride; 578f7018c21STomi Valkeinen 579f7018c21STomi Valkeinen info->var = simplefb_var; 580f7018c21STomi Valkeinen info->var.xres = params.width; 581f7018c21STomi Valkeinen info->var.yres = params.height; 582f7018c21STomi Valkeinen info->var.xres_virtual = params.width; 583f7018c21STomi Valkeinen info->var.yres_virtual = params.height; 584f7018c21STomi Valkeinen info->var.bits_per_pixel = params.format->bits_per_pixel; 585f7018c21STomi Valkeinen info->var.red = params.format->red; 586f7018c21STomi Valkeinen info->var.green = params.format->green; 587f7018c21STomi Valkeinen info->var.blue = params.format->blue; 588f7018c21STomi Valkeinen info->var.transp = params.format->transp; 589f7018c21STomi Valkeinen 590d9702b2aSThomas Zimmermann par->base = info->fix.smem_start; 591d9702b2aSThomas Zimmermann par->size = info->fix.smem_len; 592f7018c21STomi Valkeinen 593f7018c21STomi Valkeinen info->fbops = &simplefb_ops; 594f7018c21STomi Valkeinen info->screen_base = ioremap_wc(info->fix.smem_start, 595f7018c21STomi Valkeinen info->fix.smem_len); 596f7018c21STomi Valkeinen if (!info->screen_base) { 597bf2fda15SLuc Verhaegen ret = -ENOMEM; 598bf2fda15SLuc Verhaegen goto error_fb_release; 599f7018c21STomi Valkeinen } 6001270be4aSLuc Verhaegen info->pseudo_palette = par->palette; 601f7018c21STomi Valkeinen 602a3accfd7SHans de Goede ret = simplefb_clocks_get(par, pdev); 603fc219bfdSLuc Verhaegen if (ret < 0) 604fc219bfdSLuc Verhaegen goto error_unmap; 605fc219bfdSLuc Verhaegen 606a3accfd7SHans de Goede ret = simplefb_regulators_get(par, pdev); 607814740e7SChen-Yu Tsai if (ret < 0) 608814740e7SChen-Yu Tsai goto error_clocks; 609814740e7SChen-Yu Tsai 610*92a511a5SThierry Reding ret = simplefb_attach_genpds(par, pdev); 611*92a511a5SThierry Reding if (ret < 0) 612*92a511a5SThierry Reding goto error_regulators; 613*92a511a5SThierry Reding 614a3accfd7SHans de Goede simplefb_clocks_enable(par, pdev); 615a3accfd7SHans de Goede simplefb_regulators_enable(par, pdev); 616a3accfd7SHans de Goede 6175269a618SPeter Robinson dev_info(&pdev->dev, "framebuffer at 0x%lx, 0x%x bytes\n", 6185269a618SPeter Robinson info->fix.smem_start, info->fix.smem_len); 619f7018c21STomi Valkeinen dev_info(&pdev->dev, "format=%s, mode=%dx%dx%d, linelength=%d\n", 620f7018c21STomi Valkeinen params.format->name, 621f7018c21STomi Valkeinen info->var.xres, info->var.yres, 622f7018c21STomi Valkeinen info->var.bits_per_pixel, info->fix.line_length); 623f7018c21STomi Valkeinen 624748bd587SThomas Zimmermann if (mem != res) 625748bd587SThomas Zimmermann par->mem = mem; /* release in clean-up handler */ 626748bd587SThomas Zimmermann 627d9702b2aSThomas Zimmermann ret = devm_aperture_acquire_for_platform_device(pdev, par->base, par->size); 628d9702b2aSThomas Zimmermann if (ret) { 629d9702b2aSThomas Zimmermann dev_err(&pdev->dev, "Unable to acquire aperture: %d\n", ret); 630d9702b2aSThomas Zimmermann goto error_regulators; 631d9702b2aSThomas Zimmermann } 632f7018c21STomi Valkeinen ret = register_framebuffer(info); 633f7018c21STomi Valkeinen if (ret < 0) { 634f7018c21STomi Valkeinen dev_err(&pdev->dev, "Unable to register simplefb: %d\n", ret); 635814740e7SChen-Yu Tsai goto error_regulators; 636f7018c21STomi Valkeinen } 637f7018c21STomi Valkeinen 638f7018c21STomi Valkeinen dev_info(&pdev->dev, "fb%d: simplefb registered!\n", info->node); 639f7018c21STomi Valkeinen 640f7018c21STomi Valkeinen return 0; 641bf2fda15SLuc Verhaegen 642814740e7SChen-Yu Tsai error_regulators: 643814740e7SChen-Yu Tsai simplefb_regulators_destroy(par); 644fc219bfdSLuc Verhaegen error_clocks: 645fc219bfdSLuc Verhaegen simplefb_clocks_destroy(par); 646bf2fda15SLuc Verhaegen error_unmap: 647bf2fda15SLuc Verhaegen iounmap(info->screen_base); 648bf2fda15SLuc Verhaegen error_fb_release: 649bf2fda15SLuc Verhaegen framebuffer_release(info); 650748bd587SThomas Zimmermann error_release_mem_region: 651748bd587SThomas Zimmermann if (mem != res) 652748bd587SThomas Zimmermann release_mem_region(mem->start, resource_size(mem)); 653bf2fda15SLuc Verhaegen return ret; 654f7018c21STomi Valkeinen } 655f7018c21STomi Valkeinen 6564ba34066SUwe Kleine-König static void simplefb_remove(struct platform_device *pdev) 657f7018c21STomi Valkeinen { 658f7018c21STomi Valkeinen struct fb_info *info = platform_get_drvdata(pdev); 659f7018c21STomi Valkeinen 660666b90b3SJavier Martinez Canillas /* simplefb_destroy takes care of info cleanup */ 661f7018c21STomi Valkeinen unregister_framebuffer(info); 662f7018c21STomi Valkeinen } 663f7018c21STomi Valkeinen 664f7018c21STomi Valkeinen static const struct of_device_id simplefb_of_match[] = { 665f7018c21STomi Valkeinen { .compatible = "simple-framebuffer", }, 666f7018c21STomi Valkeinen { }, 667f7018c21STomi Valkeinen }; 668f7018c21STomi Valkeinen MODULE_DEVICE_TABLE(of, simplefb_of_match); 669f7018c21STomi Valkeinen 670f7018c21STomi Valkeinen static struct platform_driver simplefb_driver = { 671f7018c21STomi Valkeinen .driver = { 672f7018c21STomi Valkeinen .name = "simple-framebuffer", 673f7018c21STomi Valkeinen .of_match_table = simplefb_of_match, 674f7018c21STomi Valkeinen }, 675f7018c21STomi Valkeinen .probe = simplefb_probe, 6764ba34066SUwe Kleine-König .remove_new = simplefb_remove, 677f7018c21STomi Valkeinen }; 67889c95001SHans de Goede 6792f92ea21SHector Martin module_platform_driver(simplefb_driver); 680f7018c21STomi Valkeinen 681f7018c21STomi Valkeinen MODULE_AUTHOR("Stephen Warren <[email protected]>"); 682f7018c21STomi Valkeinen MODULE_DESCRIPTION("Simple framebuffer driver"); 683f7018c21STomi Valkeinen MODULE_LICENSE("GPL v2"); 684