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> 24db3b4fd1SGeert Uytterhoeven #include <linux/of_clk.h> 2589c95001SHans de Goede #include <linux/of_platform.h> 26814740e7SChen-Yu Tsai #include <linux/parser.h> 27814740e7SChen-Yu Tsai #include <linux/regulator/consumer.h> 28f7018c21STomi Valkeinen 29ca9384c5SJulia Lawall static const struct fb_fix_screeninfo simplefb_fix = { 30f7018c21STomi Valkeinen .id = "simple", 31f7018c21STomi Valkeinen .type = FB_TYPE_PACKED_PIXELS, 32f7018c21STomi Valkeinen .visual = FB_VISUAL_TRUECOLOR, 33f7018c21STomi Valkeinen .accel = FB_ACCEL_NONE, 34f7018c21STomi Valkeinen }; 35f7018c21STomi Valkeinen 36ca9384c5SJulia Lawall static const struct fb_var_screeninfo simplefb_var = { 37f7018c21STomi Valkeinen .height = -1, 38f7018c21STomi Valkeinen .width = -1, 39f7018c21STomi Valkeinen .activate = FB_ACTIVATE_NOW, 40f7018c21STomi Valkeinen .vmode = FB_VMODE_NONINTERLACED, 41f7018c21STomi Valkeinen }; 42f7018c21STomi Valkeinen 431270be4aSLuc Verhaegen #define PSEUDO_PALETTE_SIZE 16 441270be4aSLuc Verhaegen 45f7018c21STomi Valkeinen static int simplefb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, 46f7018c21STomi Valkeinen u_int transp, struct fb_info *info) 47f7018c21STomi Valkeinen { 48f7018c21STomi Valkeinen u32 *pal = info->pseudo_palette; 49f7018c21STomi Valkeinen u32 cr = red >> (16 - info->var.red.length); 50f7018c21STomi Valkeinen u32 cg = green >> (16 - info->var.green.length); 51f7018c21STomi Valkeinen u32 cb = blue >> (16 - info->var.blue.length); 52f7018c21STomi Valkeinen u32 value; 53f7018c21STomi Valkeinen 541270be4aSLuc Verhaegen if (regno >= PSEUDO_PALETTE_SIZE) 55f7018c21STomi Valkeinen return -EINVAL; 56f7018c21STomi Valkeinen 57f7018c21STomi Valkeinen value = (cr << info->var.red.offset) | 58f7018c21STomi Valkeinen (cg << info->var.green.offset) | 59f7018c21STomi Valkeinen (cb << info->var.blue.offset); 60f7018c21STomi Valkeinen if (info->var.transp.length > 0) { 61f7018c21STomi Valkeinen u32 mask = (1 << info->var.transp.length) - 1; 62f7018c21STomi Valkeinen mask <<= info->var.transp.offset; 63f7018c21STomi Valkeinen value |= mask; 64f7018c21STomi Valkeinen } 65f7018c21STomi Valkeinen pal[regno] = value; 66f7018c21STomi Valkeinen 67f7018c21STomi Valkeinen return 0; 68f7018c21STomi Valkeinen } 69f7018c21STomi Valkeinen 70748bd587SThomas Zimmermann struct simplefb_par { 71748bd587SThomas Zimmermann u32 palette[PSEUDO_PALETTE_SIZE]; 72d9702b2aSThomas Zimmermann resource_size_t base; 73d9702b2aSThomas Zimmermann resource_size_t size; 74748bd587SThomas Zimmermann struct resource *mem; 75748bd587SThomas Zimmermann #if defined CONFIG_OF && defined CONFIG_COMMON_CLK 76748bd587SThomas Zimmermann bool clks_enabled; 77748bd587SThomas Zimmermann unsigned int clk_count; 78748bd587SThomas Zimmermann struct clk **clks; 79748bd587SThomas Zimmermann #endif 80748bd587SThomas Zimmermann #if defined CONFIG_OF && defined CONFIG_REGULATOR 81748bd587SThomas Zimmermann bool regulators_enabled; 82748bd587SThomas Zimmermann u32 regulator_count; 83748bd587SThomas Zimmermann struct regulator **regulators; 84748bd587SThomas Zimmermann #endif 85748bd587SThomas Zimmermann }; 86748bd587SThomas Zimmermann 877c9806e8SChen-Yu Tsai static void simplefb_clocks_destroy(struct simplefb_par *par); 887c9806e8SChen-Yu Tsai static void simplefb_regulators_destroy(struct simplefb_par *par); 897c9806e8SChen-Yu Tsai 90666b90b3SJavier Martinez Canillas /* 91666b90b3SJavier Martinez Canillas * fb_ops.fb_destroy is called by the last put_fb_info() call at the end 92666b90b3SJavier Martinez Canillas * of unregister_framebuffer() or fb_release(). Do any cleanup here. 93666b90b3SJavier Martinez Canillas */ 94f7018c21STomi Valkeinen static void simplefb_destroy(struct fb_info *info) 95f7018c21STomi Valkeinen { 96748bd587SThomas Zimmermann struct simplefb_par *par = info->par; 97748bd587SThomas Zimmermann struct resource *mem = par->mem; 98748bd587SThomas Zimmermann 997c9806e8SChen-Yu Tsai simplefb_regulators_destroy(info->par); 1007c9806e8SChen-Yu Tsai simplefb_clocks_destroy(info->par); 101f7018c21STomi Valkeinen if (info->screen_base) 102f7018c21STomi Valkeinen iounmap(info->screen_base); 103748bd587SThomas Zimmermann 104666b90b3SJavier Martinez Canillas framebuffer_release(info); 105666b90b3SJavier Martinez Canillas 106748bd587SThomas Zimmermann if (mem) 107748bd587SThomas Zimmermann release_mem_region(mem->start, resource_size(mem)); 108f7018c21STomi Valkeinen } 109f7018c21STomi Valkeinen 1108a48ac33SJani Nikula static const struct fb_ops simplefb_ops = { 111f7018c21STomi Valkeinen .owner = THIS_MODULE, 112f7018c21STomi Valkeinen .fb_destroy = simplefb_destroy, 113f7018c21STomi Valkeinen .fb_setcolreg = simplefb_setcolreg, 114f7018c21STomi Valkeinen .fb_fillrect = cfb_fillrect, 115f7018c21STomi Valkeinen .fb_copyarea = cfb_copyarea, 116f7018c21STomi Valkeinen .fb_imageblit = cfb_imageblit, 117f7018c21STomi Valkeinen }; 118f7018c21STomi Valkeinen 119f7018c21STomi Valkeinen static struct simplefb_format simplefb_formats[] = SIMPLEFB_FORMATS; 120f7018c21STomi Valkeinen 121f7018c21STomi Valkeinen struct simplefb_params { 122f7018c21STomi Valkeinen u32 width; 123f7018c21STomi Valkeinen u32 height; 124f7018c21STomi Valkeinen u32 stride; 125f7018c21STomi Valkeinen struct simplefb_format *format; 126f7018c21STomi Valkeinen }; 127f7018c21STomi Valkeinen 128f7018c21STomi Valkeinen static int simplefb_parse_dt(struct platform_device *pdev, 129f7018c21STomi Valkeinen struct simplefb_params *params) 130f7018c21STomi Valkeinen { 131f7018c21STomi Valkeinen struct device_node *np = pdev->dev.of_node; 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 171f7018c21STomi Valkeinen return 0; 172f7018c21STomi Valkeinen } 173f7018c21STomi Valkeinen 174f7018c21STomi Valkeinen static int simplefb_parse_pd(struct platform_device *pdev, 175f7018c21STomi Valkeinen struct simplefb_params *params) 176f7018c21STomi Valkeinen { 177f7018c21STomi Valkeinen struct simplefb_platform_data *pd = dev_get_platdata(&pdev->dev); 178f7018c21STomi Valkeinen int i; 179f7018c21STomi Valkeinen 180f7018c21STomi Valkeinen params->width = pd->width; 181f7018c21STomi Valkeinen params->height = pd->height; 182f7018c21STomi Valkeinen params->stride = pd->stride; 183f7018c21STomi Valkeinen 184f7018c21STomi Valkeinen params->format = NULL; 185f7018c21STomi Valkeinen for (i = 0; i < ARRAY_SIZE(simplefb_formats); i++) { 186f7018c21STomi Valkeinen if (strcmp(pd->format, simplefb_formats[i].name)) 187f7018c21STomi Valkeinen continue; 188f7018c21STomi Valkeinen 189f7018c21STomi Valkeinen params->format = &simplefb_formats[i]; 190f7018c21STomi Valkeinen break; 191f7018c21STomi Valkeinen } 192f7018c21STomi Valkeinen 193f7018c21STomi Valkeinen if (!params->format) { 194f7018c21STomi Valkeinen dev_err(&pdev->dev, "Invalid format value\n"); 195f7018c21STomi Valkeinen return -EINVAL; 196f7018c21STomi Valkeinen } 197f7018c21STomi Valkeinen 198f7018c21STomi Valkeinen return 0; 199f7018c21STomi Valkeinen } 200f7018c21STomi Valkeinen 2018284731eSHans de Goede #if defined CONFIG_OF && defined CONFIG_COMMON_CLK 202fc219bfdSLuc Verhaegen /* 203fc219bfdSLuc Verhaegen * Clock handling code. 204fc219bfdSLuc Verhaegen * 205fc219bfdSLuc Verhaegen * Here we handle the clocks property of our "simple-framebuffer" dt node. 206fc219bfdSLuc Verhaegen * This is necessary so that we can make sure that any clocks needed by 207fc219bfdSLuc Verhaegen * the display engine that the bootloader set up for us (and for which it 208fc219bfdSLuc Verhaegen * provided a simplefb dt node), stay up, for the life of the simplefb 209fc219bfdSLuc Verhaegen * driver. 210fc219bfdSLuc Verhaegen * 211fc219bfdSLuc Verhaegen * When the driver unloads, we cleanly disable, and then release the clocks. 212fc219bfdSLuc Verhaegen * 213fc219bfdSLuc Verhaegen * We only complain about errors here, no action is taken as the most likely 214fc219bfdSLuc Verhaegen * error can only happen due to a mismatch between the bootloader which set 215fc219bfdSLuc Verhaegen * up simplefb, and the clock definitions in the device tree. Chances are 216fc219bfdSLuc Verhaegen * that there are no adverse effects, and if there are, a clean teardown of 217fc219bfdSLuc Verhaegen * the fb probe will not help us much either. So just complain and carry on, 218fc219bfdSLuc Verhaegen * and hope that the user actually gets a working fb at the end of things. 219fc219bfdSLuc Verhaegen */ 220a3accfd7SHans de Goede static int simplefb_clocks_get(struct simplefb_par *par, 221fc219bfdSLuc Verhaegen struct platform_device *pdev) 222fc219bfdSLuc Verhaegen { 223fc219bfdSLuc Verhaegen struct device_node *np = pdev->dev.of_node; 224fc219bfdSLuc Verhaegen struct clk *clock; 225a3accfd7SHans de Goede int i; 226fc219bfdSLuc Verhaegen 227fc219bfdSLuc Verhaegen if (dev_get_platdata(&pdev->dev) || !np) 228fc219bfdSLuc Verhaegen return 0; 229fc219bfdSLuc Verhaegen 230fc219bfdSLuc Verhaegen par->clk_count = of_clk_get_parent_count(np); 2312cd82d06SStephen Boyd if (!par->clk_count) 232fc219bfdSLuc Verhaegen return 0; 233fc219bfdSLuc Verhaegen 234fc219bfdSLuc Verhaegen par->clks = kcalloc(par->clk_count, sizeof(struct clk *), GFP_KERNEL); 235fc219bfdSLuc Verhaegen if (!par->clks) 236fc219bfdSLuc Verhaegen return -ENOMEM; 237fc219bfdSLuc Verhaegen 238fc219bfdSLuc Verhaegen for (i = 0; i < par->clk_count; i++) { 239fc219bfdSLuc Verhaegen clock = of_clk_get(np, i); 240fc219bfdSLuc Verhaegen if (IS_ERR(clock)) { 241fc219bfdSLuc Verhaegen if (PTR_ERR(clock) == -EPROBE_DEFER) { 242fc219bfdSLuc Verhaegen while (--i >= 0) { 243fc219bfdSLuc Verhaegen clk_put(par->clks[i]); 244fc219bfdSLuc Verhaegen } 245fc219bfdSLuc Verhaegen kfree(par->clks); 246fc219bfdSLuc Verhaegen return -EPROBE_DEFER; 247fc219bfdSLuc Verhaegen } 248fc219bfdSLuc Verhaegen dev_err(&pdev->dev, "%s: clock %d not found: %ld\n", 249fc219bfdSLuc Verhaegen __func__, i, PTR_ERR(clock)); 250fc219bfdSLuc Verhaegen continue; 251fc219bfdSLuc Verhaegen } 252fc219bfdSLuc Verhaegen par->clks[i] = clock; 253fc219bfdSLuc Verhaegen } 254fc219bfdSLuc Verhaegen 255a3accfd7SHans de Goede return 0; 256a3accfd7SHans de Goede } 257a3accfd7SHans de Goede 258a3accfd7SHans de Goede static void simplefb_clocks_enable(struct simplefb_par *par, 259a3accfd7SHans de Goede struct platform_device *pdev) 260a3accfd7SHans de Goede { 261a3accfd7SHans de Goede int i, ret; 262a3accfd7SHans de Goede 263fc219bfdSLuc Verhaegen for (i = 0; i < par->clk_count; i++) { 264fc219bfdSLuc Verhaegen if (par->clks[i]) { 265fc219bfdSLuc Verhaegen ret = clk_prepare_enable(par->clks[i]); 266fc219bfdSLuc Verhaegen if (ret) { 267fc219bfdSLuc Verhaegen dev_err(&pdev->dev, 268fc219bfdSLuc Verhaegen "%s: failed to enable clock %d: %d\n", 269fc219bfdSLuc Verhaegen __func__, i, ret); 270fc219bfdSLuc Verhaegen clk_put(par->clks[i]); 271fc219bfdSLuc Verhaegen par->clks[i] = NULL; 272fc219bfdSLuc Verhaegen } 273fc219bfdSLuc Verhaegen } 274fc219bfdSLuc Verhaegen } 275a3accfd7SHans de Goede par->clks_enabled = true; 276fc219bfdSLuc Verhaegen } 277fc219bfdSLuc Verhaegen 278fc219bfdSLuc Verhaegen static void simplefb_clocks_destroy(struct simplefb_par *par) 279fc219bfdSLuc Verhaegen { 280fc219bfdSLuc Verhaegen int i; 281fc219bfdSLuc Verhaegen 282fc219bfdSLuc Verhaegen if (!par->clks) 283fc219bfdSLuc Verhaegen return; 284fc219bfdSLuc Verhaegen 285fc219bfdSLuc Verhaegen for (i = 0; i < par->clk_count; i++) { 286fc219bfdSLuc Verhaegen if (par->clks[i]) { 287a3accfd7SHans de Goede if (par->clks_enabled) 288fc219bfdSLuc Verhaegen clk_disable_unprepare(par->clks[i]); 289fc219bfdSLuc Verhaegen clk_put(par->clks[i]); 290fc219bfdSLuc Verhaegen } 291fc219bfdSLuc Verhaegen } 292fc219bfdSLuc Verhaegen 293fc219bfdSLuc Verhaegen kfree(par->clks); 294fc219bfdSLuc Verhaegen } 295fc219bfdSLuc Verhaegen #else 296a3accfd7SHans de Goede static int simplefb_clocks_get(struct simplefb_par *par, 297fc219bfdSLuc Verhaegen struct platform_device *pdev) { return 0; } 298a3accfd7SHans de Goede static void simplefb_clocks_enable(struct simplefb_par *par, 299a3accfd7SHans de Goede struct platform_device *pdev) { } 300fc219bfdSLuc Verhaegen static void simplefb_clocks_destroy(struct simplefb_par *par) { } 301fc219bfdSLuc Verhaegen #endif 302fc219bfdSLuc Verhaegen 303814740e7SChen-Yu Tsai #if defined CONFIG_OF && defined CONFIG_REGULATOR 304814740e7SChen-Yu Tsai 305814740e7SChen-Yu Tsai #define SUPPLY_SUFFIX "-supply" 306814740e7SChen-Yu Tsai 307814740e7SChen-Yu Tsai /* 308814740e7SChen-Yu Tsai * Regulator handling code. 309814740e7SChen-Yu Tsai * 310814740e7SChen-Yu Tsai * Here we handle the num-supplies and vin*-supply properties of our 311814740e7SChen-Yu Tsai * "simple-framebuffer" dt node. This is necessary so that we can make sure 312814740e7SChen-Yu Tsai * that any regulators needed by the display hardware that the bootloader 313814740e7SChen-Yu Tsai * set up for us (and for which it provided a simplefb dt node), stay up, 314814740e7SChen-Yu Tsai * for the life of the simplefb driver. 315814740e7SChen-Yu Tsai * 316814740e7SChen-Yu Tsai * When the driver unloads, we cleanly disable, and then release the 317814740e7SChen-Yu Tsai * regulators. 318814740e7SChen-Yu Tsai * 319814740e7SChen-Yu Tsai * We only complain about errors here, no action is taken as the most likely 320814740e7SChen-Yu Tsai * error can only happen due to a mismatch between the bootloader which set 321814740e7SChen-Yu Tsai * up simplefb, and the regulator definitions in the device tree. Chances are 322814740e7SChen-Yu Tsai * that there are no adverse effects, and if there are, a clean teardown of 323814740e7SChen-Yu Tsai * the fb probe will not help us much either. So just complain and carry on, 324814740e7SChen-Yu Tsai * and hope that the user actually gets a working fb at the end of things. 325814740e7SChen-Yu Tsai */ 326a3accfd7SHans de Goede static int simplefb_regulators_get(struct simplefb_par *par, 327814740e7SChen-Yu Tsai struct platform_device *pdev) 328814740e7SChen-Yu Tsai { 329814740e7SChen-Yu Tsai struct device_node *np = pdev->dev.of_node; 330814740e7SChen-Yu Tsai struct property *prop; 331814740e7SChen-Yu Tsai struct regulator *regulator; 332814740e7SChen-Yu Tsai const char *p; 333a3accfd7SHans de Goede int count = 0, i = 0; 334814740e7SChen-Yu Tsai 335814740e7SChen-Yu Tsai if (dev_get_platdata(&pdev->dev) || !np) 336814740e7SChen-Yu Tsai return 0; 337814740e7SChen-Yu Tsai 338814740e7SChen-Yu Tsai /* Count the number of regulator supplies */ 339814740e7SChen-Yu Tsai for_each_property_of_node(np, prop) { 340814740e7SChen-Yu Tsai p = strstr(prop->name, SUPPLY_SUFFIX); 341814740e7SChen-Yu Tsai if (p && p != prop->name) 342814740e7SChen-Yu Tsai count++; 343814740e7SChen-Yu Tsai } 344814740e7SChen-Yu Tsai 345814740e7SChen-Yu Tsai if (!count) 346814740e7SChen-Yu Tsai return 0; 347814740e7SChen-Yu Tsai 348814740e7SChen-Yu Tsai par->regulators = devm_kcalloc(&pdev->dev, count, 349814740e7SChen-Yu Tsai sizeof(struct regulator *), GFP_KERNEL); 350814740e7SChen-Yu Tsai if (!par->regulators) 351814740e7SChen-Yu Tsai return -ENOMEM; 352814740e7SChen-Yu Tsai 353814740e7SChen-Yu Tsai /* Get all the regulators */ 354814740e7SChen-Yu Tsai for_each_property_of_node(np, prop) { 355814740e7SChen-Yu Tsai char name[32]; /* 32 is max size of property name */ 356814740e7SChen-Yu Tsai 357814740e7SChen-Yu Tsai p = strstr(prop->name, SUPPLY_SUFFIX); 358814740e7SChen-Yu Tsai if (!p || p == prop->name) 359814740e7SChen-Yu Tsai continue; 360814740e7SChen-Yu Tsai 3618d026858SWolfram Sang strscpy(name, prop->name, 362814740e7SChen-Yu Tsai strlen(prop->name) - strlen(SUPPLY_SUFFIX) + 1); 363814740e7SChen-Yu Tsai regulator = devm_regulator_get_optional(&pdev->dev, name); 364814740e7SChen-Yu Tsai if (IS_ERR(regulator)) { 365814740e7SChen-Yu Tsai if (PTR_ERR(regulator) == -EPROBE_DEFER) 366814740e7SChen-Yu Tsai return -EPROBE_DEFER; 367814740e7SChen-Yu Tsai dev_err(&pdev->dev, "regulator %s not found: %ld\n", 368814740e7SChen-Yu Tsai name, PTR_ERR(regulator)); 369814740e7SChen-Yu Tsai continue; 370814740e7SChen-Yu Tsai } 371814740e7SChen-Yu Tsai par->regulators[i++] = regulator; 372814740e7SChen-Yu Tsai } 373814740e7SChen-Yu Tsai par->regulator_count = i; 374814740e7SChen-Yu Tsai 375a3accfd7SHans de Goede return 0; 376a3accfd7SHans de Goede } 377a3accfd7SHans de Goede 378a3accfd7SHans de Goede static void simplefb_regulators_enable(struct simplefb_par *par, 379a3accfd7SHans de Goede struct platform_device *pdev) 380a3accfd7SHans de Goede { 381a3accfd7SHans de Goede int i, ret; 382a3accfd7SHans de Goede 383814740e7SChen-Yu Tsai /* Enable all the regulators */ 384814740e7SChen-Yu Tsai for (i = 0; i < par->regulator_count; i++) { 385814740e7SChen-Yu Tsai ret = regulator_enable(par->regulators[i]); 386814740e7SChen-Yu Tsai if (ret) { 387814740e7SChen-Yu Tsai dev_err(&pdev->dev, 388814740e7SChen-Yu Tsai "failed to enable regulator %d: %d\n", 389814740e7SChen-Yu Tsai i, ret); 390814740e7SChen-Yu Tsai devm_regulator_put(par->regulators[i]); 391814740e7SChen-Yu Tsai par->regulators[i] = NULL; 392814740e7SChen-Yu Tsai } 393814740e7SChen-Yu Tsai } 394a3accfd7SHans de Goede par->regulators_enabled = true; 395814740e7SChen-Yu Tsai } 396814740e7SChen-Yu Tsai 397814740e7SChen-Yu Tsai static void simplefb_regulators_destroy(struct simplefb_par *par) 398814740e7SChen-Yu Tsai { 399814740e7SChen-Yu Tsai int i; 400814740e7SChen-Yu Tsai 401a3accfd7SHans de Goede if (!par->regulators || !par->regulators_enabled) 402814740e7SChen-Yu Tsai return; 403814740e7SChen-Yu Tsai 404814740e7SChen-Yu Tsai for (i = 0; i < par->regulator_count; i++) 405814740e7SChen-Yu Tsai if (par->regulators[i]) 406814740e7SChen-Yu Tsai regulator_disable(par->regulators[i]); 407814740e7SChen-Yu Tsai } 408814740e7SChen-Yu Tsai #else 409a3accfd7SHans de Goede static int simplefb_regulators_get(struct simplefb_par *par, 410814740e7SChen-Yu Tsai struct platform_device *pdev) { return 0; } 411a3accfd7SHans de Goede static void simplefb_regulators_enable(struct simplefb_par *par, 412a3accfd7SHans de Goede struct platform_device *pdev) { } 413814740e7SChen-Yu Tsai static void simplefb_regulators_destroy(struct simplefb_par *par) { } 414814740e7SChen-Yu Tsai #endif 415814740e7SChen-Yu Tsai 416f7018c21STomi Valkeinen static int simplefb_probe(struct platform_device *pdev) 417f7018c21STomi Valkeinen { 418f7018c21STomi Valkeinen int ret; 419f7018c21STomi Valkeinen struct simplefb_params params; 420f7018c21STomi Valkeinen struct fb_info *info; 4211270be4aSLuc Verhaegen struct simplefb_par *par; 422748bd587SThomas Zimmermann struct resource *res, *mem; 423f7018c21STomi Valkeinen 424f7018c21STomi Valkeinen if (fb_get_options("simplefb", NULL)) 425f7018c21STomi Valkeinen return -ENODEV; 426f7018c21STomi Valkeinen 427f7018c21STomi Valkeinen ret = -ENODEV; 428f7018c21STomi Valkeinen if (dev_get_platdata(&pdev->dev)) 429f7018c21STomi Valkeinen ret = simplefb_parse_pd(pdev, ¶ms); 430f7018c21STomi Valkeinen else if (pdev->dev.of_node) 431f7018c21STomi Valkeinen ret = simplefb_parse_dt(pdev, ¶ms); 432f7018c21STomi Valkeinen 433f7018c21STomi Valkeinen if (ret) 434f7018c21STomi Valkeinen return ret; 435f7018c21STomi Valkeinen 436748bd587SThomas Zimmermann res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 437748bd587SThomas Zimmermann if (!res) { 438f7018c21STomi Valkeinen dev_err(&pdev->dev, "No memory resource\n"); 439f7018c21STomi Valkeinen return -EINVAL; 440f7018c21STomi Valkeinen } 441f7018c21STomi Valkeinen 442748bd587SThomas Zimmermann mem = request_mem_region(res->start, resource_size(res), "simplefb"); 443748bd587SThomas Zimmermann if (!mem) { 444748bd587SThomas Zimmermann /* 445748bd587SThomas Zimmermann * We cannot make this fatal. Sometimes this comes from magic 446748bd587SThomas Zimmermann * spaces our resource handlers simply don't know about. Use 447748bd587SThomas Zimmermann * the I/O-memory resource as-is and try to map that instead. 448748bd587SThomas Zimmermann */ 449748bd587SThomas Zimmermann dev_warn(&pdev->dev, "simplefb: cannot reserve video memory at %pR\n", res); 450748bd587SThomas Zimmermann mem = res; 451748bd587SThomas Zimmermann } 452748bd587SThomas Zimmermann 4531270be4aSLuc Verhaegen info = framebuffer_alloc(sizeof(struct simplefb_par), &pdev->dev); 454748bd587SThomas Zimmermann if (!info) { 455748bd587SThomas Zimmermann ret = -ENOMEM; 456748bd587SThomas Zimmermann goto error_release_mem_region; 457748bd587SThomas Zimmermann } 458f7018c21STomi Valkeinen platform_set_drvdata(pdev, info); 459f7018c21STomi Valkeinen 4601270be4aSLuc Verhaegen par = info->par; 4611270be4aSLuc Verhaegen 462f7018c21STomi Valkeinen info->fix = simplefb_fix; 463f7018c21STomi Valkeinen info->fix.smem_start = mem->start; 464f7018c21STomi Valkeinen info->fix.smem_len = resource_size(mem); 465f7018c21STomi Valkeinen info->fix.line_length = params.stride; 466f7018c21STomi Valkeinen 467f7018c21STomi Valkeinen info->var = simplefb_var; 468f7018c21STomi Valkeinen info->var.xres = params.width; 469f7018c21STomi Valkeinen info->var.yres = params.height; 470f7018c21STomi Valkeinen info->var.xres_virtual = params.width; 471f7018c21STomi Valkeinen info->var.yres_virtual = params.height; 472f7018c21STomi Valkeinen info->var.bits_per_pixel = params.format->bits_per_pixel; 473f7018c21STomi Valkeinen info->var.red = params.format->red; 474f7018c21STomi Valkeinen info->var.green = params.format->green; 475f7018c21STomi Valkeinen info->var.blue = params.format->blue; 476f7018c21STomi Valkeinen info->var.transp = params.format->transp; 477f7018c21STomi Valkeinen 478d9702b2aSThomas Zimmermann par->base = info->fix.smem_start; 479d9702b2aSThomas Zimmermann par->size = info->fix.smem_len; 480f7018c21STomi Valkeinen 481f7018c21STomi Valkeinen info->fbops = &simplefb_ops; 482d9702b2aSThomas Zimmermann info->flags = FBINFO_DEFAULT; 483f7018c21STomi Valkeinen info->screen_base = ioremap_wc(info->fix.smem_start, 484f7018c21STomi Valkeinen info->fix.smem_len); 485f7018c21STomi Valkeinen if (!info->screen_base) { 486bf2fda15SLuc Verhaegen ret = -ENOMEM; 487bf2fda15SLuc Verhaegen goto error_fb_release; 488f7018c21STomi Valkeinen } 4891270be4aSLuc Verhaegen info->pseudo_palette = par->palette; 490f7018c21STomi Valkeinen 491a3accfd7SHans de Goede ret = simplefb_clocks_get(par, pdev); 492fc219bfdSLuc Verhaegen if (ret < 0) 493fc219bfdSLuc Verhaegen goto error_unmap; 494fc219bfdSLuc Verhaegen 495a3accfd7SHans de Goede ret = simplefb_regulators_get(par, pdev); 496814740e7SChen-Yu Tsai if (ret < 0) 497814740e7SChen-Yu Tsai goto error_clocks; 498814740e7SChen-Yu Tsai 499a3accfd7SHans de Goede simplefb_clocks_enable(par, pdev); 500a3accfd7SHans de Goede simplefb_regulators_enable(par, pdev); 501a3accfd7SHans de Goede 5025269a618SPeter Robinson dev_info(&pdev->dev, "framebuffer at 0x%lx, 0x%x bytes\n", 5035269a618SPeter Robinson info->fix.smem_start, info->fix.smem_len); 504f7018c21STomi Valkeinen dev_info(&pdev->dev, "format=%s, mode=%dx%dx%d, linelength=%d\n", 505f7018c21STomi Valkeinen params.format->name, 506f7018c21STomi Valkeinen info->var.xres, info->var.yres, 507f7018c21STomi Valkeinen info->var.bits_per_pixel, info->fix.line_length); 508f7018c21STomi Valkeinen 509748bd587SThomas Zimmermann if (mem != res) 510748bd587SThomas Zimmermann par->mem = mem; /* release in clean-up handler */ 511748bd587SThomas Zimmermann 512d9702b2aSThomas Zimmermann ret = devm_aperture_acquire_for_platform_device(pdev, par->base, par->size); 513d9702b2aSThomas Zimmermann if (ret) { 514d9702b2aSThomas Zimmermann dev_err(&pdev->dev, "Unable to acquire aperture: %d\n", ret); 515d9702b2aSThomas Zimmermann goto error_regulators; 516d9702b2aSThomas Zimmermann } 517f7018c21STomi Valkeinen ret = register_framebuffer(info); 518f7018c21STomi Valkeinen if (ret < 0) { 519f7018c21STomi Valkeinen dev_err(&pdev->dev, "Unable to register simplefb: %d\n", ret); 520814740e7SChen-Yu Tsai goto error_regulators; 521f7018c21STomi Valkeinen } 522f7018c21STomi Valkeinen 523f7018c21STomi Valkeinen dev_info(&pdev->dev, "fb%d: simplefb registered!\n", info->node); 524f7018c21STomi Valkeinen 525f7018c21STomi Valkeinen return 0; 526bf2fda15SLuc Verhaegen 527814740e7SChen-Yu Tsai error_regulators: 528814740e7SChen-Yu Tsai simplefb_regulators_destroy(par); 529fc219bfdSLuc Verhaegen error_clocks: 530fc219bfdSLuc Verhaegen simplefb_clocks_destroy(par); 531bf2fda15SLuc Verhaegen error_unmap: 532bf2fda15SLuc Verhaegen iounmap(info->screen_base); 533bf2fda15SLuc Verhaegen error_fb_release: 534bf2fda15SLuc Verhaegen framebuffer_release(info); 535748bd587SThomas Zimmermann error_release_mem_region: 536748bd587SThomas Zimmermann if (mem != res) 537748bd587SThomas Zimmermann release_mem_region(mem->start, resource_size(mem)); 538bf2fda15SLuc Verhaegen return ret; 539f7018c21STomi Valkeinen } 540f7018c21STomi Valkeinen 541*4ba34066SUwe Kleine-König static void simplefb_remove(struct platform_device *pdev) 542f7018c21STomi Valkeinen { 543f7018c21STomi Valkeinen struct fb_info *info = platform_get_drvdata(pdev); 544f7018c21STomi Valkeinen 545666b90b3SJavier Martinez Canillas /* simplefb_destroy takes care of info cleanup */ 546f7018c21STomi Valkeinen unregister_framebuffer(info); 547f7018c21STomi Valkeinen } 548f7018c21STomi Valkeinen 549f7018c21STomi Valkeinen static const struct of_device_id simplefb_of_match[] = { 550f7018c21STomi Valkeinen { .compatible = "simple-framebuffer", }, 551f7018c21STomi Valkeinen { }, 552f7018c21STomi Valkeinen }; 553f7018c21STomi Valkeinen MODULE_DEVICE_TABLE(of, simplefb_of_match); 554f7018c21STomi Valkeinen 555f7018c21STomi Valkeinen static struct platform_driver simplefb_driver = { 556f7018c21STomi Valkeinen .driver = { 557f7018c21STomi Valkeinen .name = "simple-framebuffer", 558f7018c21STomi Valkeinen .of_match_table = simplefb_of_match, 559f7018c21STomi Valkeinen }, 560f7018c21STomi Valkeinen .probe = simplefb_probe, 561*4ba34066SUwe Kleine-König .remove_new = simplefb_remove, 562f7018c21STomi Valkeinen }; 56389c95001SHans de Goede 5642f92ea21SHector Martin module_platform_driver(simplefb_driver); 565f7018c21STomi Valkeinen 566f7018c21STomi Valkeinen MODULE_AUTHOR("Stephen Warren <[email protected]>"); 567f7018c21STomi Valkeinen MODULE_DESCRIPTION("Simple framebuffer driver"); 568f7018c21STomi Valkeinen MODULE_LICENSE("GPL v2"); 569