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> 24*8ddfc01aSThierry 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> 28814740e7SChen-Yu Tsai #include <linux/regulator/consumer.h> 29f7018c21STomi Valkeinen 30ca9384c5SJulia Lawall static const struct fb_fix_screeninfo simplefb_fix = { 31f7018c21STomi Valkeinen .id = "simple", 32f7018c21STomi Valkeinen .type = FB_TYPE_PACKED_PIXELS, 33f7018c21STomi Valkeinen .visual = FB_VISUAL_TRUECOLOR, 34f7018c21STomi Valkeinen .accel = FB_ACCEL_NONE, 35f7018c21STomi Valkeinen }; 36f7018c21STomi Valkeinen 37ca9384c5SJulia Lawall static const struct fb_var_screeninfo simplefb_var = { 38f7018c21STomi Valkeinen .height = -1, 39f7018c21STomi Valkeinen .width = -1, 40f7018c21STomi Valkeinen .activate = FB_ACTIVATE_NOW, 41f7018c21STomi Valkeinen .vmode = FB_VMODE_NONINTERLACED, 42f7018c21STomi Valkeinen }; 43f7018c21STomi Valkeinen 441270be4aSLuc Verhaegen #define PSEUDO_PALETTE_SIZE 16 451270be4aSLuc Verhaegen 46f7018c21STomi Valkeinen static int simplefb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, 47f7018c21STomi Valkeinen u_int transp, struct fb_info *info) 48f7018c21STomi Valkeinen { 49f7018c21STomi Valkeinen u32 *pal = info->pseudo_palette; 50f7018c21STomi Valkeinen u32 cr = red >> (16 - info->var.red.length); 51f7018c21STomi Valkeinen u32 cg = green >> (16 - info->var.green.length); 52f7018c21STomi Valkeinen u32 cb = blue >> (16 - info->var.blue.length); 53f7018c21STomi Valkeinen u32 value; 54f7018c21STomi Valkeinen 551270be4aSLuc Verhaegen if (regno >= PSEUDO_PALETTE_SIZE) 56f7018c21STomi Valkeinen return -EINVAL; 57f7018c21STomi Valkeinen 58f7018c21STomi Valkeinen value = (cr << info->var.red.offset) | 59f7018c21STomi Valkeinen (cg << info->var.green.offset) | 60f7018c21STomi Valkeinen (cb << info->var.blue.offset); 61f7018c21STomi Valkeinen if (info->var.transp.length > 0) { 62f7018c21STomi Valkeinen u32 mask = (1 << info->var.transp.length) - 1; 63f7018c21STomi Valkeinen mask <<= info->var.transp.offset; 64f7018c21STomi Valkeinen value |= mask; 65f7018c21STomi Valkeinen } 66f7018c21STomi Valkeinen pal[regno] = value; 67f7018c21STomi Valkeinen 68f7018c21STomi Valkeinen return 0; 69f7018c21STomi Valkeinen } 70f7018c21STomi Valkeinen 71748bd587SThomas Zimmermann struct simplefb_par { 72748bd587SThomas Zimmermann u32 palette[PSEUDO_PALETTE_SIZE]; 73d9702b2aSThomas Zimmermann resource_size_t base; 74d9702b2aSThomas Zimmermann resource_size_t size; 75748bd587SThomas Zimmermann struct resource *mem; 76748bd587SThomas Zimmermann #if defined CONFIG_OF && defined CONFIG_COMMON_CLK 77748bd587SThomas Zimmermann bool clks_enabled; 78748bd587SThomas Zimmermann unsigned int clk_count; 79748bd587SThomas Zimmermann struct clk **clks; 80748bd587SThomas Zimmermann #endif 81748bd587SThomas Zimmermann #if defined CONFIG_OF && defined CONFIG_REGULATOR 82748bd587SThomas Zimmermann bool regulators_enabled; 83748bd587SThomas Zimmermann u32 regulator_count; 84748bd587SThomas Zimmermann struct regulator **regulators; 85748bd587SThomas Zimmermann #endif 86748bd587SThomas Zimmermann }; 87748bd587SThomas Zimmermann 887c9806e8SChen-Yu Tsai static void simplefb_clocks_destroy(struct simplefb_par *par); 897c9806e8SChen-Yu Tsai static void simplefb_regulators_destroy(struct simplefb_par *par); 907c9806e8SChen-Yu Tsai 91666b90b3SJavier Martinez Canillas /* 92666b90b3SJavier Martinez Canillas * fb_ops.fb_destroy is called by the last put_fb_info() call at the end 93666b90b3SJavier Martinez Canillas * of unregister_framebuffer() or fb_release(). Do any cleanup here. 94666b90b3SJavier Martinez Canillas */ 95f7018c21STomi Valkeinen static void simplefb_destroy(struct fb_info *info) 96f7018c21STomi Valkeinen { 97748bd587SThomas Zimmermann struct simplefb_par *par = info->par; 98748bd587SThomas Zimmermann struct resource *mem = par->mem; 99748bd587SThomas Zimmermann 1007c9806e8SChen-Yu Tsai simplefb_regulators_destroy(info->par); 1017c9806e8SChen-Yu Tsai simplefb_clocks_destroy(info->par); 102f7018c21STomi Valkeinen if (info->screen_base) 103f7018c21STomi Valkeinen iounmap(info->screen_base); 104748bd587SThomas Zimmermann 105666b90b3SJavier Martinez Canillas framebuffer_release(info); 106666b90b3SJavier Martinez Canillas 107748bd587SThomas Zimmermann if (mem) 108748bd587SThomas Zimmermann release_mem_region(mem->start, resource_size(mem)); 109f7018c21STomi Valkeinen } 110f7018c21STomi Valkeinen 1118a48ac33SJani Nikula static const struct fb_ops simplefb_ops = { 112f7018c21STomi Valkeinen .owner = THIS_MODULE, 113367984ebSThomas Zimmermann FB_DEFAULT_IOMEM_OPS, 114f7018c21STomi Valkeinen .fb_destroy = simplefb_destroy, 115f7018c21STomi Valkeinen .fb_setcolreg = simplefb_setcolreg, 116f7018c21STomi Valkeinen }; 117f7018c21STomi Valkeinen 118f7018c21STomi Valkeinen static struct simplefb_format simplefb_formats[] = SIMPLEFB_FORMATS; 119f7018c21STomi Valkeinen 120f7018c21STomi Valkeinen struct simplefb_params { 121f7018c21STomi Valkeinen u32 width; 122f7018c21STomi Valkeinen u32 height; 123f7018c21STomi Valkeinen u32 stride; 124f7018c21STomi Valkeinen struct simplefb_format *format; 125*8ddfc01aSThierry Reding struct resource memory; 126f7018c21STomi Valkeinen }; 127f7018c21STomi Valkeinen 128f7018c21STomi Valkeinen static int simplefb_parse_dt(struct platform_device *pdev, 129f7018c21STomi Valkeinen struct simplefb_params *params) 130f7018c21STomi Valkeinen { 131*8ddfc01aSThierry Reding struct device_node *np = pdev->dev.of_node, *mem; 132f7018c21STomi Valkeinen int ret; 133f7018c21STomi Valkeinen const char *format; 134f7018c21STomi Valkeinen int i; 135f7018c21STomi Valkeinen 136f7018c21STomi Valkeinen ret = of_property_read_u32(np, "width", ¶ms->width); 137f7018c21STomi Valkeinen if (ret) { 138f7018c21STomi Valkeinen dev_err(&pdev->dev, "Can't parse width property\n"); 139f7018c21STomi Valkeinen return ret; 140f7018c21STomi Valkeinen } 141f7018c21STomi Valkeinen 142f7018c21STomi Valkeinen ret = of_property_read_u32(np, "height", ¶ms->height); 143f7018c21STomi Valkeinen if (ret) { 144f7018c21STomi Valkeinen dev_err(&pdev->dev, "Can't parse height property\n"); 145f7018c21STomi Valkeinen return ret; 146f7018c21STomi Valkeinen } 147f7018c21STomi Valkeinen 148f7018c21STomi Valkeinen ret = of_property_read_u32(np, "stride", ¶ms->stride); 149f7018c21STomi Valkeinen if (ret) { 150f7018c21STomi Valkeinen dev_err(&pdev->dev, "Can't parse stride property\n"); 151f7018c21STomi Valkeinen return ret; 152f7018c21STomi Valkeinen } 153f7018c21STomi Valkeinen 154f7018c21STomi Valkeinen ret = of_property_read_string(np, "format", &format); 155f7018c21STomi Valkeinen if (ret) { 156f7018c21STomi Valkeinen dev_err(&pdev->dev, "Can't parse format property\n"); 157f7018c21STomi Valkeinen return ret; 158f7018c21STomi Valkeinen } 159f7018c21STomi Valkeinen params->format = NULL; 160f7018c21STomi Valkeinen for (i = 0; i < ARRAY_SIZE(simplefb_formats); i++) { 161f7018c21STomi Valkeinen if (strcmp(format, simplefb_formats[i].name)) 162f7018c21STomi Valkeinen continue; 163f7018c21STomi Valkeinen params->format = &simplefb_formats[i]; 164f7018c21STomi Valkeinen break; 165f7018c21STomi Valkeinen } 166f7018c21STomi Valkeinen if (!params->format) { 167f7018c21STomi Valkeinen dev_err(&pdev->dev, "Invalid format value\n"); 168f7018c21STomi Valkeinen return -EINVAL; 169f7018c21STomi Valkeinen } 170f7018c21STomi Valkeinen 171*8ddfc01aSThierry Reding mem = of_parse_phandle(np, "memory-region", 0); 172*8ddfc01aSThierry Reding if (mem) { 173*8ddfc01aSThierry Reding ret = of_address_to_resource(mem, 0, ¶ms->memory); 174*8ddfc01aSThierry Reding if (ret < 0) { 175*8ddfc01aSThierry Reding dev_err(&pdev->dev, "failed to parse memory-region\n"); 176*8ddfc01aSThierry Reding of_node_put(mem); 177*8ddfc01aSThierry Reding return ret; 178*8ddfc01aSThierry Reding } 179*8ddfc01aSThierry Reding 180*8ddfc01aSThierry Reding if (of_property_present(np, "reg")) 181*8ddfc01aSThierry Reding dev_warn(&pdev->dev, "preferring \"memory-region\" over \"reg\" property\n"); 182*8ddfc01aSThierry Reding 183*8ddfc01aSThierry Reding of_node_put(mem); 184*8ddfc01aSThierry Reding } else { 185*8ddfc01aSThierry Reding memset(¶ms->memory, 0, sizeof(params->memory)); 186*8ddfc01aSThierry Reding } 187*8ddfc01aSThierry Reding 188f7018c21STomi Valkeinen return 0; 189f7018c21STomi Valkeinen } 190f7018c21STomi Valkeinen 191f7018c21STomi Valkeinen static int simplefb_parse_pd(struct platform_device *pdev, 192f7018c21STomi Valkeinen struct simplefb_params *params) 193f7018c21STomi Valkeinen { 194f7018c21STomi Valkeinen struct simplefb_platform_data *pd = dev_get_platdata(&pdev->dev); 195f7018c21STomi Valkeinen int i; 196f7018c21STomi Valkeinen 197f7018c21STomi Valkeinen params->width = pd->width; 198f7018c21STomi Valkeinen params->height = pd->height; 199f7018c21STomi Valkeinen params->stride = pd->stride; 200f7018c21STomi Valkeinen 201f7018c21STomi Valkeinen params->format = NULL; 202f7018c21STomi Valkeinen for (i = 0; i < ARRAY_SIZE(simplefb_formats); i++) { 203f7018c21STomi Valkeinen if (strcmp(pd->format, simplefb_formats[i].name)) 204f7018c21STomi Valkeinen continue; 205f7018c21STomi Valkeinen 206f7018c21STomi Valkeinen params->format = &simplefb_formats[i]; 207f7018c21STomi Valkeinen break; 208f7018c21STomi Valkeinen } 209f7018c21STomi Valkeinen 210f7018c21STomi Valkeinen if (!params->format) { 211f7018c21STomi Valkeinen dev_err(&pdev->dev, "Invalid format value\n"); 212f7018c21STomi Valkeinen return -EINVAL; 213f7018c21STomi Valkeinen } 214f7018c21STomi Valkeinen 215*8ddfc01aSThierry Reding memset(¶ms->memory, 0, sizeof(params->memory)); 216*8ddfc01aSThierry Reding 217f7018c21STomi Valkeinen return 0; 218f7018c21STomi Valkeinen } 219f7018c21STomi Valkeinen 2208284731eSHans de Goede #if defined CONFIG_OF && defined CONFIG_COMMON_CLK 221fc219bfdSLuc Verhaegen /* 222fc219bfdSLuc Verhaegen * Clock handling code. 223fc219bfdSLuc Verhaegen * 224fc219bfdSLuc Verhaegen * Here we handle the clocks property of our "simple-framebuffer" dt node. 225fc219bfdSLuc Verhaegen * This is necessary so that we can make sure that any clocks needed by 226fc219bfdSLuc Verhaegen * the display engine that the bootloader set up for us (and for which it 227fc219bfdSLuc Verhaegen * provided a simplefb dt node), stay up, for the life of the simplefb 228fc219bfdSLuc Verhaegen * driver. 229fc219bfdSLuc Verhaegen * 230fc219bfdSLuc Verhaegen * When the driver unloads, we cleanly disable, and then release the clocks. 231fc219bfdSLuc Verhaegen * 232fc219bfdSLuc Verhaegen * We only complain about errors here, no action is taken as the most likely 233fc219bfdSLuc Verhaegen * error can only happen due to a mismatch between the bootloader which set 234fc219bfdSLuc Verhaegen * up simplefb, and the clock definitions in the device tree. Chances are 235fc219bfdSLuc Verhaegen * that there are no adverse effects, and if there are, a clean teardown of 236fc219bfdSLuc Verhaegen * the fb probe will not help us much either. So just complain and carry on, 237fc219bfdSLuc Verhaegen * and hope that the user actually gets a working fb at the end of things. 238fc219bfdSLuc Verhaegen */ 239a3accfd7SHans de Goede static int simplefb_clocks_get(struct simplefb_par *par, 240fc219bfdSLuc Verhaegen struct platform_device *pdev) 241fc219bfdSLuc Verhaegen { 242fc219bfdSLuc Verhaegen struct device_node *np = pdev->dev.of_node; 243fc219bfdSLuc Verhaegen struct clk *clock; 244a3accfd7SHans de Goede int i; 245fc219bfdSLuc Verhaegen 246fc219bfdSLuc Verhaegen if (dev_get_platdata(&pdev->dev) || !np) 247fc219bfdSLuc Verhaegen return 0; 248fc219bfdSLuc Verhaegen 249fc219bfdSLuc Verhaegen par->clk_count = of_clk_get_parent_count(np); 2502cd82d06SStephen Boyd if (!par->clk_count) 251fc219bfdSLuc Verhaegen return 0; 252fc219bfdSLuc Verhaegen 253fc219bfdSLuc Verhaegen par->clks = kcalloc(par->clk_count, sizeof(struct clk *), GFP_KERNEL); 254fc219bfdSLuc Verhaegen if (!par->clks) 255fc219bfdSLuc Verhaegen return -ENOMEM; 256fc219bfdSLuc Verhaegen 257fc219bfdSLuc Verhaegen for (i = 0; i < par->clk_count; i++) { 258fc219bfdSLuc Verhaegen clock = of_clk_get(np, i); 259fc219bfdSLuc Verhaegen if (IS_ERR(clock)) { 260fc219bfdSLuc Verhaegen if (PTR_ERR(clock) == -EPROBE_DEFER) { 261fc219bfdSLuc Verhaegen while (--i >= 0) { 262fc219bfdSLuc Verhaegen clk_put(par->clks[i]); 263fc219bfdSLuc Verhaegen } 264fc219bfdSLuc Verhaegen kfree(par->clks); 265fc219bfdSLuc Verhaegen return -EPROBE_DEFER; 266fc219bfdSLuc Verhaegen } 267fc219bfdSLuc Verhaegen dev_err(&pdev->dev, "%s: clock %d not found: %ld\n", 268fc219bfdSLuc Verhaegen __func__, i, PTR_ERR(clock)); 269fc219bfdSLuc Verhaegen continue; 270fc219bfdSLuc Verhaegen } 271fc219bfdSLuc Verhaegen par->clks[i] = clock; 272fc219bfdSLuc Verhaegen } 273fc219bfdSLuc Verhaegen 274a3accfd7SHans de Goede return 0; 275a3accfd7SHans de Goede } 276a3accfd7SHans de Goede 277a3accfd7SHans de Goede static void simplefb_clocks_enable(struct simplefb_par *par, 278a3accfd7SHans de Goede struct platform_device *pdev) 279a3accfd7SHans de Goede { 280a3accfd7SHans de Goede int i, ret; 281a3accfd7SHans de Goede 282fc219bfdSLuc Verhaegen for (i = 0; i < par->clk_count; i++) { 283fc219bfdSLuc Verhaegen if (par->clks[i]) { 284fc219bfdSLuc Verhaegen ret = clk_prepare_enable(par->clks[i]); 285fc219bfdSLuc Verhaegen if (ret) { 286fc219bfdSLuc Verhaegen dev_err(&pdev->dev, 287fc219bfdSLuc Verhaegen "%s: failed to enable clock %d: %d\n", 288fc219bfdSLuc Verhaegen __func__, i, ret); 289fc219bfdSLuc Verhaegen clk_put(par->clks[i]); 290fc219bfdSLuc Verhaegen par->clks[i] = NULL; 291fc219bfdSLuc Verhaegen } 292fc219bfdSLuc Verhaegen } 293fc219bfdSLuc Verhaegen } 294a3accfd7SHans de Goede par->clks_enabled = true; 295fc219bfdSLuc Verhaegen } 296fc219bfdSLuc Verhaegen 297fc219bfdSLuc Verhaegen static void simplefb_clocks_destroy(struct simplefb_par *par) 298fc219bfdSLuc Verhaegen { 299fc219bfdSLuc Verhaegen int i; 300fc219bfdSLuc Verhaegen 301fc219bfdSLuc Verhaegen if (!par->clks) 302fc219bfdSLuc Verhaegen return; 303fc219bfdSLuc Verhaegen 304fc219bfdSLuc Verhaegen for (i = 0; i < par->clk_count; i++) { 305fc219bfdSLuc Verhaegen if (par->clks[i]) { 306a3accfd7SHans de Goede if (par->clks_enabled) 307fc219bfdSLuc Verhaegen clk_disable_unprepare(par->clks[i]); 308fc219bfdSLuc Verhaegen clk_put(par->clks[i]); 309fc219bfdSLuc Verhaegen } 310fc219bfdSLuc Verhaegen } 311fc219bfdSLuc Verhaegen 312fc219bfdSLuc Verhaegen kfree(par->clks); 313fc219bfdSLuc Verhaegen } 314fc219bfdSLuc Verhaegen #else 315a3accfd7SHans de Goede static int simplefb_clocks_get(struct simplefb_par *par, 316fc219bfdSLuc Verhaegen struct platform_device *pdev) { return 0; } 317a3accfd7SHans de Goede static void simplefb_clocks_enable(struct simplefb_par *par, 318a3accfd7SHans de Goede struct platform_device *pdev) { } 319fc219bfdSLuc Verhaegen static void simplefb_clocks_destroy(struct simplefb_par *par) { } 320fc219bfdSLuc Verhaegen #endif 321fc219bfdSLuc Verhaegen 322814740e7SChen-Yu Tsai #if defined CONFIG_OF && defined CONFIG_REGULATOR 323814740e7SChen-Yu Tsai 324814740e7SChen-Yu Tsai #define SUPPLY_SUFFIX "-supply" 325814740e7SChen-Yu Tsai 326814740e7SChen-Yu Tsai /* 327814740e7SChen-Yu Tsai * Regulator handling code. 328814740e7SChen-Yu Tsai * 329814740e7SChen-Yu Tsai * Here we handle the num-supplies and vin*-supply properties of our 330814740e7SChen-Yu Tsai * "simple-framebuffer" dt node. This is necessary so that we can make sure 331814740e7SChen-Yu Tsai * that any regulators needed by the display hardware that the bootloader 332814740e7SChen-Yu Tsai * set up for us (and for which it provided a simplefb dt node), stay up, 333814740e7SChen-Yu Tsai * for the life of the simplefb driver. 334814740e7SChen-Yu Tsai * 335814740e7SChen-Yu Tsai * When the driver unloads, we cleanly disable, and then release the 336814740e7SChen-Yu Tsai * regulators. 337814740e7SChen-Yu Tsai * 338814740e7SChen-Yu Tsai * We only complain about errors here, no action is taken as the most likely 339814740e7SChen-Yu Tsai * error can only happen due to a mismatch between the bootloader which set 340814740e7SChen-Yu Tsai * up simplefb, and the regulator definitions in the device tree. Chances are 341814740e7SChen-Yu Tsai * that there are no adverse effects, and if there are, a clean teardown of 342814740e7SChen-Yu Tsai * the fb probe will not help us much either. So just complain and carry on, 343814740e7SChen-Yu Tsai * and hope that the user actually gets a working fb at the end of things. 344814740e7SChen-Yu Tsai */ 345a3accfd7SHans de Goede static int simplefb_regulators_get(struct simplefb_par *par, 346814740e7SChen-Yu Tsai struct platform_device *pdev) 347814740e7SChen-Yu Tsai { 348814740e7SChen-Yu Tsai struct device_node *np = pdev->dev.of_node; 349814740e7SChen-Yu Tsai struct property *prop; 350814740e7SChen-Yu Tsai struct regulator *regulator; 351814740e7SChen-Yu Tsai const char *p; 352a3accfd7SHans de Goede int count = 0, i = 0; 353814740e7SChen-Yu Tsai 354814740e7SChen-Yu Tsai if (dev_get_platdata(&pdev->dev) || !np) 355814740e7SChen-Yu Tsai return 0; 356814740e7SChen-Yu Tsai 357814740e7SChen-Yu Tsai /* Count the number of regulator supplies */ 358814740e7SChen-Yu Tsai for_each_property_of_node(np, prop) { 359814740e7SChen-Yu Tsai p = strstr(prop->name, SUPPLY_SUFFIX); 360814740e7SChen-Yu Tsai if (p && p != prop->name) 361814740e7SChen-Yu Tsai count++; 362814740e7SChen-Yu Tsai } 363814740e7SChen-Yu Tsai 364814740e7SChen-Yu Tsai if (!count) 365814740e7SChen-Yu Tsai return 0; 366814740e7SChen-Yu Tsai 367814740e7SChen-Yu Tsai par->regulators = devm_kcalloc(&pdev->dev, count, 368814740e7SChen-Yu Tsai sizeof(struct regulator *), GFP_KERNEL); 369814740e7SChen-Yu Tsai if (!par->regulators) 370814740e7SChen-Yu Tsai return -ENOMEM; 371814740e7SChen-Yu Tsai 372814740e7SChen-Yu Tsai /* Get all the regulators */ 373814740e7SChen-Yu Tsai for_each_property_of_node(np, prop) { 374814740e7SChen-Yu Tsai char name[32]; /* 32 is max size of property name */ 375814740e7SChen-Yu Tsai 376814740e7SChen-Yu Tsai p = strstr(prop->name, SUPPLY_SUFFIX); 377814740e7SChen-Yu Tsai if (!p || p == prop->name) 378814740e7SChen-Yu Tsai continue; 379814740e7SChen-Yu Tsai 3808d026858SWolfram Sang strscpy(name, prop->name, 381814740e7SChen-Yu Tsai strlen(prop->name) - strlen(SUPPLY_SUFFIX) + 1); 382814740e7SChen-Yu Tsai regulator = devm_regulator_get_optional(&pdev->dev, name); 383814740e7SChen-Yu Tsai if (IS_ERR(regulator)) { 384814740e7SChen-Yu Tsai if (PTR_ERR(regulator) == -EPROBE_DEFER) 385814740e7SChen-Yu Tsai return -EPROBE_DEFER; 386814740e7SChen-Yu Tsai dev_err(&pdev->dev, "regulator %s not found: %ld\n", 387814740e7SChen-Yu Tsai name, PTR_ERR(regulator)); 388814740e7SChen-Yu Tsai continue; 389814740e7SChen-Yu Tsai } 390814740e7SChen-Yu Tsai par->regulators[i++] = regulator; 391814740e7SChen-Yu Tsai } 392814740e7SChen-Yu Tsai par->regulator_count = i; 393814740e7SChen-Yu Tsai 394a3accfd7SHans de Goede return 0; 395a3accfd7SHans de Goede } 396a3accfd7SHans de Goede 397a3accfd7SHans de Goede static void simplefb_regulators_enable(struct simplefb_par *par, 398a3accfd7SHans de Goede struct platform_device *pdev) 399a3accfd7SHans de Goede { 400a3accfd7SHans de Goede int i, ret; 401a3accfd7SHans de Goede 402814740e7SChen-Yu Tsai /* Enable all the regulators */ 403814740e7SChen-Yu Tsai for (i = 0; i < par->regulator_count; i++) { 404814740e7SChen-Yu Tsai ret = regulator_enable(par->regulators[i]); 405814740e7SChen-Yu Tsai if (ret) { 406814740e7SChen-Yu Tsai dev_err(&pdev->dev, 407814740e7SChen-Yu Tsai "failed to enable regulator %d: %d\n", 408814740e7SChen-Yu Tsai i, ret); 409814740e7SChen-Yu Tsai devm_regulator_put(par->regulators[i]); 410814740e7SChen-Yu Tsai par->regulators[i] = NULL; 411814740e7SChen-Yu Tsai } 412814740e7SChen-Yu Tsai } 413a3accfd7SHans de Goede par->regulators_enabled = true; 414814740e7SChen-Yu Tsai } 415814740e7SChen-Yu Tsai 416814740e7SChen-Yu Tsai static void simplefb_regulators_destroy(struct simplefb_par *par) 417814740e7SChen-Yu Tsai { 418814740e7SChen-Yu Tsai int i; 419814740e7SChen-Yu Tsai 420a3accfd7SHans de Goede if (!par->regulators || !par->regulators_enabled) 421814740e7SChen-Yu Tsai return; 422814740e7SChen-Yu Tsai 423814740e7SChen-Yu Tsai for (i = 0; i < par->regulator_count; i++) 424814740e7SChen-Yu Tsai if (par->regulators[i]) 425814740e7SChen-Yu Tsai regulator_disable(par->regulators[i]); 426814740e7SChen-Yu Tsai } 427814740e7SChen-Yu Tsai #else 428a3accfd7SHans de Goede static int simplefb_regulators_get(struct simplefb_par *par, 429814740e7SChen-Yu Tsai struct platform_device *pdev) { return 0; } 430a3accfd7SHans de Goede static void simplefb_regulators_enable(struct simplefb_par *par, 431a3accfd7SHans de Goede struct platform_device *pdev) { } 432814740e7SChen-Yu Tsai static void simplefb_regulators_destroy(struct simplefb_par *par) { } 433814740e7SChen-Yu Tsai #endif 434814740e7SChen-Yu Tsai 435f7018c21STomi Valkeinen static int simplefb_probe(struct platform_device *pdev) 436f7018c21STomi Valkeinen { 437f7018c21STomi Valkeinen int ret; 438f7018c21STomi Valkeinen struct simplefb_params params; 439f7018c21STomi Valkeinen struct fb_info *info; 4401270be4aSLuc Verhaegen struct simplefb_par *par; 441748bd587SThomas Zimmermann struct resource *res, *mem; 442f7018c21STomi Valkeinen 443f7018c21STomi Valkeinen if (fb_get_options("simplefb", NULL)) 444f7018c21STomi Valkeinen return -ENODEV; 445f7018c21STomi Valkeinen 446f7018c21STomi Valkeinen ret = -ENODEV; 447f7018c21STomi Valkeinen if (dev_get_platdata(&pdev->dev)) 448f7018c21STomi Valkeinen ret = simplefb_parse_pd(pdev, ¶ms); 449f7018c21STomi Valkeinen else if (pdev->dev.of_node) 450f7018c21STomi Valkeinen ret = simplefb_parse_dt(pdev, ¶ms); 451f7018c21STomi Valkeinen 452f7018c21STomi Valkeinen if (ret) 453f7018c21STomi Valkeinen return ret; 454f7018c21STomi Valkeinen 455*8ddfc01aSThierry Reding if (params.memory.start == 0 && params.memory.end == 0) { 456748bd587SThomas Zimmermann res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 457748bd587SThomas Zimmermann if (!res) { 458f7018c21STomi Valkeinen dev_err(&pdev->dev, "No memory resource\n"); 459f7018c21STomi Valkeinen return -EINVAL; 460f7018c21STomi Valkeinen } 461*8ddfc01aSThierry Reding } else { 462*8ddfc01aSThierry Reding res = ¶ms.memory; 463*8ddfc01aSThierry Reding } 464f7018c21STomi Valkeinen 465748bd587SThomas Zimmermann mem = request_mem_region(res->start, resource_size(res), "simplefb"); 466748bd587SThomas Zimmermann if (!mem) { 467748bd587SThomas Zimmermann /* 468748bd587SThomas Zimmermann * We cannot make this fatal. Sometimes this comes from magic 469748bd587SThomas Zimmermann * spaces our resource handlers simply don't know about. Use 470748bd587SThomas Zimmermann * the I/O-memory resource as-is and try to map that instead. 471748bd587SThomas Zimmermann */ 472748bd587SThomas Zimmermann dev_warn(&pdev->dev, "simplefb: cannot reserve video memory at %pR\n", res); 473748bd587SThomas Zimmermann mem = res; 474748bd587SThomas Zimmermann } 475748bd587SThomas Zimmermann 4761270be4aSLuc Verhaegen info = framebuffer_alloc(sizeof(struct simplefb_par), &pdev->dev); 477748bd587SThomas Zimmermann if (!info) { 478748bd587SThomas Zimmermann ret = -ENOMEM; 479748bd587SThomas Zimmermann goto error_release_mem_region; 480748bd587SThomas Zimmermann } 481f7018c21STomi Valkeinen platform_set_drvdata(pdev, info); 482f7018c21STomi Valkeinen 4831270be4aSLuc Verhaegen par = info->par; 4841270be4aSLuc Verhaegen 485f7018c21STomi Valkeinen info->fix = simplefb_fix; 486f7018c21STomi Valkeinen info->fix.smem_start = mem->start; 487f7018c21STomi Valkeinen info->fix.smem_len = resource_size(mem); 488f7018c21STomi Valkeinen info->fix.line_length = params.stride; 489f7018c21STomi Valkeinen 490f7018c21STomi Valkeinen info->var = simplefb_var; 491f7018c21STomi Valkeinen info->var.xres = params.width; 492f7018c21STomi Valkeinen info->var.yres = params.height; 493f7018c21STomi Valkeinen info->var.xres_virtual = params.width; 494f7018c21STomi Valkeinen info->var.yres_virtual = params.height; 495f7018c21STomi Valkeinen info->var.bits_per_pixel = params.format->bits_per_pixel; 496f7018c21STomi Valkeinen info->var.red = params.format->red; 497f7018c21STomi Valkeinen info->var.green = params.format->green; 498f7018c21STomi Valkeinen info->var.blue = params.format->blue; 499f7018c21STomi Valkeinen info->var.transp = params.format->transp; 500f7018c21STomi Valkeinen 501d9702b2aSThomas Zimmermann par->base = info->fix.smem_start; 502d9702b2aSThomas Zimmermann par->size = info->fix.smem_len; 503f7018c21STomi Valkeinen 504f7018c21STomi Valkeinen info->fbops = &simplefb_ops; 505f7018c21STomi Valkeinen info->screen_base = ioremap_wc(info->fix.smem_start, 506f7018c21STomi Valkeinen info->fix.smem_len); 507f7018c21STomi Valkeinen if (!info->screen_base) { 508bf2fda15SLuc Verhaegen ret = -ENOMEM; 509bf2fda15SLuc Verhaegen goto error_fb_release; 510f7018c21STomi Valkeinen } 5111270be4aSLuc Verhaegen info->pseudo_palette = par->palette; 512f7018c21STomi Valkeinen 513a3accfd7SHans de Goede ret = simplefb_clocks_get(par, pdev); 514fc219bfdSLuc Verhaegen if (ret < 0) 515fc219bfdSLuc Verhaegen goto error_unmap; 516fc219bfdSLuc Verhaegen 517a3accfd7SHans de Goede ret = simplefb_regulators_get(par, pdev); 518814740e7SChen-Yu Tsai if (ret < 0) 519814740e7SChen-Yu Tsai goto error_clocks; 520814740e7SChen-Yu Tsai 521a3accfd7SHans de Goede simplefb_clocks_enable(par, pdev); 522a3accfd7SHans de Goede simplefb_regulators_enable(par, pdev); 523a3accfd7SHans de Goede 5245269a618SPeter Robinson dev_info(&pdev->dev, "framebuffer at 0x%lx, 0x%x bytes\n", 5255269a618SPeter Robinson info->fix.smem_start, info->fix.smem_len); 526f7018c21STomi Valkeinen dev_info(&pdev->dev, "format=%s, mode=%dx%dx%d, linelength=%d\n", 527f7018c21STomi Valkeinen params.format->name, 528f7018c21STomi Valkeinen info->var.xres, info->var.yres, 529f7018c21STomi Valkeinen info->var.bits_per_pixel, info->fix.line_length); 530f7018c21STomi Valkeinen 531748bd587SThomas Zimmermann if (mem != res) 532748bd587SThomas Zimmermann par->mem = mem; /* release in clean-up handler */ 533748bd587SThomas Zimmermann 534d9702b2aSThomas Zimmermann ret = devm_aperture_acquire_for_platform_device(pdev, par->base, par->size); 535d9702b2aSThomas Zimmermann if (ret) { 536d9702b2aSThomas Zimmermann dev_err(&pdev->dev, "Unable to acquire aperture: %d\n", ret); 537d9702b2aSThomas Zimmermann goto error_regulators; 538d9702b2aSThomas Zimmermann } 539f7018c21STomi Valkeinen ret = register_framebuffer(info); 540f7018c21STomi Valkeinen if (ret < 0) { 541f7018c21STomi Valkeinen dev_err(&pdev->dev, "Unable to register simplefb: %d\n", ret); 542814740e7SChen-Yu Tsai goto error_regulators; 543f7018c21STomi Valkeinen } 544f7018c21STomi Valkeinen 545f7018c21STomi Valkeinen dev_info(&pdev->dev, "fb%d: simplefb registered!\n", info->node); 546f7018c21STomi Valkeinen 547f7018c21STomi Valkeinen return 0; 548bf2fda15SLuc Verhaegen 549814740e7SChen-Yu Tsai error_regulators: 550814740e7SChen-Yu Tsai simplefb_regulators_destroy(par); 551fc219bfdSLuc Verhaegen error_clocks: 552fc219bfdSLuc Verhaegen simplefb_clocks_destroy(par); 553bf2fda15SLuc Verhaegen error_unmap: 554bf2fda15SLuc Verhaegen iounmap(info->screen_base); 555bf2fda15SLuc Verhaegen error_fb_release: 556bf2fda15SLuc Verhaegen framebuffer_release(info); 557748bd587SThomas Zimmermann error_release_mem_region: 558748bd587SThomas Zimmermann if (mem != res) 559748bd587SThomas Zimmermann release_mem_region(mem->start, resource_size(mem)); 560bf2fda15SLuc Verhaegen return ret; 561f7018c21STomi Valkeinen } 562f7018c21STomi Valkeinen 5634ba34066SUwe Kleine-König static void simplefb_remove(struct platform_device *pdev) 564f7018c21STomi Valkeinen { 565f7018c21STomi Valkeinen struct fb_info *info = platform_get_drvdata(pdev); 566f7018c21STomi Valkeinen 567666b90b3SJavier Martinez Canillas /* simplefb_destroy takes care of info cleanup */ 568f7018c21STomi Valkeinen unregister_framebuffer(info); 569f7018c21STomi Valkeinen } 570f7018c21STomi Valkeinen 571f7018c21STomi Valkeinen static const struct of_device_id simplefb_of_match[] = { 572f7018c21STomi Valkeinen { .compatible = "simple-framebuffer", }, 573f7018c21STomi Valkeinen { }, 574f7018c21STomi Valkeinen }; 575f7018c21STomi Valkeinen MODULE_DEVICE_TABLE(of, simplefb_of_match); 576f7018c21STomi Valkeinen 577f7018c21STomi Valkeinen static struct platform_driver simplefb_driver = { 578f7018c21STomi Valkeinen .driver = { 579f7018c21STomi Valkeinen .name = "simple-framebuffer", 580f7018c21STomi Valkeinen .of_match_table = simplefb_of_match, 581f7018c21STomi Valkeinen }, 582f7018c21STomi Valkeinen .probe = simplefb_probe, 5834ba34066SUwe Kleine-König .remove_new = simplefb_remove, 584f7018c21STomi Valkeinen }; 58589c95001SHans de Goede 5862f92ea21SHector Martin module_platform_driver(simplefb_driver); 587f7018c21STomi Valkeinen 588f7018c21STomi Valkeinen MODULE_AUTHOR("Stephen Warren <[email protected]>"); 589f7018c21STomi Valkeinen MODULE_DESCRIPTION("Simple framebuffer driver"); 590f7018c21STomi Valkeinen MODULE_LICENSE("GPL v2"); 591