18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Simplest possible simple frame-buffer driver, as a platform device 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2013, Stephen Warren 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Based on q40fb.c, which was: 88c2ecf20Sopenharmony_ci * Copyright (C) 2001 Richard Zidlicky <rz@linux-m68k.org> 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Also based on offb.c, which was: 118c2ecf20Sopenharmony_ci * Copyright (C) 1997 Geert Uytterhoeven 128c2ecf20Sopenharmony_ci * Copyright (C) 1996 Paul Mackerras 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/errno.h> 168c2ecf20Sopenharmony_ci#include <linux/fb.h> 178c2ecf20Sopenharmony_ci#include <linux/io.h> 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/platform_data/simplefb.h> 208c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 218c2ecf20Sopenharmony_ci#include <linux/clk.h> 228c2ecf20Sopenharmony_ci#include <linux/of.h> 238c2ecf20Sopenharmony_ci#include <linux/of_clk.h> 248c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 258c2ecf20Sopenharmony_ci#include <linux/parser.h> 268c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic const struct fb_fix_screeninfo simplefb_fix = { 298c2ecf20Sopenharmony_ci .id = "simple", 308c2ecf20Sopenharmony_ci .type = FB_TYPE_PACKED_PIXELS, 318c2ecf20Sopenharmony_ci .visual = FB_VISUAL_TRUECOLOR, 328c2ecf20Sopenharmony_ci .accel = FB_ACCEL_NONE, 338c2ecf20Sopenharmony_ci}; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic const struct fb_var_screeninfo simplefb_var = { 368c2ecf20Sopenharmony_ci .height = -1, 378c2ecf20Sopenharmony_ci .width = -1, 388c2ecf20Sopenharmony_ci .activate = FB_ACTIVATE_NOW, 398c2ecf20Sopenharmony_ci .vmode = FB_VMODE_NONINTERLACED, 408c2ecf20Sopenharmony_ci}; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define PSEUDO_PALETTE_SIZE 16 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic int simplefb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, 458c2ecf20Sopenharmony_ci u_int transp, struct fb_info *info) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci u32 *pal = info->pseudo_palette; 488c2ecf20Sopenharmony_ci u32 cr = red >> (16 - info->var.red.length); 498c2ecf20Sopenharmony_ci u32 cg = green >> (16 - info->var.green.length); 508c2ecf20Sopenharmony_ci u32 cb = blue >> (16 - info->var.blue.length); 518c2ecf20Sopenharmony_ci u32 value; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci if (regno >= PSEUDO_PALETTE_SIZE) 548c2ecf20Sopenharmony_ci return -EINVAL; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci value = (cr << info->var.red.offset) | 578c2ecf20Sopenharmony_ci (cg << info->var.green.offset) | 588c2ecf20Sopenharmony_ci (cb << info->var.blue.offset); 598c2ecf20Sopenharmony_ci if (info->var.transp.length > 0) { 608c2ecf20Sopenharmony_ci u32 mask = (1 << info->var.transp.length) - 1; 618c2ecf20Sopenharmony_ci mask <<= info->var.transp.offset; 628c2ecf20Sopenharmony_ci value |= mask; 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci pal[regno] = value; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci return 0; 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistruct simplefb_par; 708c2ecf20Sopenharmony_cistatic void simplefb_clocks_destroy(struct simplefb_par *par); 718c2ecf20Sopenharmony_cistatic void simplefb_regulators_destroy(struct simplefb_par *par); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic void simplefb_destroy(struct fb_info *info) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci simplefb_regulators_destroy(info->par); 768c2ecf20Sopenharmony_ci simplefb_clocks_destroy(info->par); 778c2ecf20Sopenharmony_ci if (info->screen_base) 788c2ecf20Sopenharmony_ci iounmap(info->screen_base); 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic const struct fb_ops simplefb_ops = { 828c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 838c2ecf20Sopenharmony_ci .fb_destroy = simplefb_destroy, 848c2ecf20Sopenharmony_ci .fb_setcolreg = simplefb_setcolreg, 858c2ecf20Sopenharmony_ci .fb_fillrect = cfb_fillrect, 868c2ecf20Sopenharmony_ci .fb_copyarea = cfb_copyarea, 878c2ecf20Sopenharmony_ci .fb_imageblit = cfb_imageblit, 888c2ecf20Sopenharmony_ci}; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic struct simplefb_format simplefb_formats[] = SIMPLEFB_FORMATS; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistruct simplefb_params { 938c2ecf20Sopenharmony_ci u32 width; 948c2ecf20Sopenharmony_ci u32 height; 958c2ecf20Sopenharmony_ci u32 stride; 968c2ecf20Sopenharmony_ci struct simplefb_format *format; 978c2ecf20Sopenharmony_ci}; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic int simplefb_parse_dt(struct platform_device *pdev, 1008c2ecf20Sopenharmony_ci struct simplefb_params *params) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 1038c2ecf20Sopenharmony_ci int ret; 1048c2ecf20Sopenharmony_ci const char *format; 1058c2ecf20Sopenharmony_ci int i; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "width", ¶ms->width); 1088c2ecf20Sopenharmony_ci if (ret) { 1098c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Can't parse width property\n"); 1108c2ecf20Sopenharmony_ci return ret; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "height", ¶ms->height); 1148c2ecf20Sopenharmony_ci if (ret) { 1158c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Can't parse height property\n"); 1168c2ecf20Sopenharmony_ci return ret; 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "stride", ¶ms->stride); 1208c2ecf20Sopenharmony_ci if (ret) { 1218c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Can't parse stride property\n"); 1228c2ecf20Sopenharmony_ci return ret; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci ret = of_property_read_string(np, "format", &format); 1268c2ecf20Sopenharmony_ci if (ret) { 1278c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Can't parse format property\n"); 1288c2ecf20Sopenharmony_ci return ret; 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci params->format = NULL; 1318c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(simplefb_formats); i++) { 1328c2ecf20Sopenharmony_ci if (strcmp(format, simplefb_formats[i].name)) 1338c2ecf20Sopenharmony_ci continue; 1348c2ecf20Sopenharmony_ci params->format = &simplefb_formats[i]; 1358c2ecf20Sopenharmony_ci break; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci if (!params->format) { 1388c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Invalid format value\n"); 1398c2ecf20Sopenharmony_ci return -EINVAL; 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci return 0; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic int simplefb_parse_pd(struct platform_device *pdev, 1468c2ecf20Sopenharmony_ci struct simplefb_params *params) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci struct simplefb_platform_data *pd = dev_get_platdata(&pdev->dev); 1498c2ecf20Sopenharmony_ci int i; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci params->width = pd->width; 1528c2ecf20Sopenharmony_ci params->height = pd->height; 1538c2ecf20Sopenharmony_ci params->stride = pd->stride; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci params->format = NULL; 1568c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(simplefb_formats); i++) { 1578c2ecf20Sopenharmony_ci if (strcmp(pd->format, simplefb_formats[i].name)) 1588c2ecf20Sopenharmony_ci continue; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci params->format = &simplefb_formats[i]; 1618c2ecf20Sopenharmony_ci break; 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci if (!params->format) { 1658c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Invalid format value\n"); 1668c2ecf20Sopenharmony_ci return -EINVAL; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci return 0; 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistruct simplefb_par { 1738c2ecf20Sopenharmony_ci u32 palette[PSEUDO_PALETTE_SIZE]; 1748c2ecf20Sopenharmony_ci#if defined CONFIG_OF && defined CONFIG_COMMON_CLK 1758c2ecf20Sopenharmony_ci bool clks_enabled; 1768c2ecf20Sopenharmony_ci unsigned int clk_count; 1778c2ecf20Sopenharmony_ci struct clk **clks; 1788c2ecf20Sopenharmony_ci#endif 1798c2ecf20Sopenharmony_ci#if defined CONFIG_OF && defined CONFIG_REGULATOR 1808c2ecf20Sopenharmony_ci bool regulators_enabled; 1818c2ecf20Sopenharmony_ci u32 regulator_count; 1828c2ecf20Sopenharmony_ci struct regulator **regulators; 1838c2ecf20Sopenharmony_ci#endif 1848c2ecf20Sopenharmony_ci}; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci#if defined CONFIG_OF && defined CONFIG_COMMON_CLK 1878c2ecf20Sopenharmony_ci/* 1888c2ecf20Sopenharmony_ci * Clock handling code. 1898c2ecf20Sopenharmony_ci * 1908c2ecf20Sopenharmony_ci * Here we handle the clocks property of our "simple-framebuffer" dt node. 1918c2ecf20Sopenharmony_ci * This is necessary so that we can make sure that any clocks needed by 1928c2ecf20Sopenharmony_ci * the display engine that the bootloader set up for us (and for which it 1938c2ecf20Sopenharmony_ci * provided a simplefb dt node), stay up, for the life of the simplefb 1948c2ecf20Sopenharmony_ci * driver. 1958c2ecf20Sopenharmony_ci * 1968c2ecf20Sopenharmony_ci * When the driver unloads, we cleanly disable, and then release the clocks. 1978c2ecf20Sopenharmony_ci * 1988c2ecf20Sopenharmony_ci * We only complain about errors here, no action is taken as the most likely 1998c2ecf20Sopenharmony_ci * error can only happen due to a mismatch between the bootloader which set 2008c2ecf20Sopenharmony_ci * up simplefb, and the clock definitions in the device tree. Chances are 2018c2ecf20Sopenharmony_ci * that there are no adverse effects, and if there are, a clean teardown of 2028c2ecf20Sopenharmony_ci * the fb probe will not help us much either. So just complain and carry on, 2038c2ecf20Sopenharmony_ci * and hope that the user actually gets a working fb at the end of things. 2048c2ecf20Sopenharmony_ci */ 2058c2ecf20Sopenharmony_cistatic int simplefb_clocks_get(struct simplefb_par *par, 2068c2ecf20Sopenharmony_ci struct platform_device *pdev) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 2098c2ecf20Sopenharmony_ci struct clk *clock; 2108c2ecf20Sopenharmony_ci int i; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (dev_get_platdata(&pdev->dev) || !np) 2138c2ecf20Sopenharmony_ci return 0; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci par->clk_count = of_clk_get_parent_count(np); 2168c2ecf20Sopenharmony_ci if (!par->clk_count) 2178c2ecf20Sopenharmony_ci return 0; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci par->clks = kcalloc(par->clk_count, sizeof(struct clk *), GFP_KERNEL); 2208c2ecf20Sopenharmony_ci if (!par->clks) 2218c2ecf20Sopenharmony_ci return -ENOMEM; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci for (i = 0; i < par->clk_count; i++) { 2248c2ecf20Sopenharmony_ci clock = of_clk_get(np, i); 2258c2ecf20Sopenharmony_ci if (IS_ERR(clock)) { 2268c2ecf20Sopenharmony_ci if (PTR_ERR(clock) == -EPROBE_DEFER) { 2278c2ecf20Sopenharmony_ci while (--i >= 0) { 2288c2ecf20Sopenharmony_ci if (par->clks[i]) 2298c2ecf20Sopenharmony_ci clk_put(par->clks[i]); 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci kfree(par->clks); 2328c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "%s: clock %d not found: %ld\n", 2358c2ecf20Sopenharmony_ci __func__, i, PTR_ERR(clock)); 2368c2ecf20Sopenharmony_ci continue; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci par->clks[i] = clock; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci return 0; 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistatic void simplefb_clocks_enable(struct simplefb_par *par, 2458c2ecf20Sopenharmony_ci struct platform_device *pdev) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci int i, ret; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci for (i = 0; i < par->clk_count; i++) { 2508c2ecf20Sopenharmony_ci if (par->clks[i]) { 2518c2ecf20Sopenharmony_ci ret = clk_prepare_enable(par->clks[i]); 2528c2ecf20Sopenharmony_ci if (ret) { 2538c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 2548c2ecf20Sopenharmony_ci "%s: failed to enable clock %d: %d\n", 2558c2ecf20Sopenharmony_ci __func__, i, ret); 2568c2ecf20Sopenharmony_ci clk_put(par->clks[i]); 2578c2ecf20Sopenharmony_ci par->clks[i] = NULL; 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci par->clks_enabled = true; 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cistatic void simplefb_clocks_destroy(struct simplefb_par *par) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci int i; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci if (!par->clks) 2698c2ecf20Sopenharmony_ci return; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci for (i = 0; i < par->clk_count; i++) { 2728c2ecf20Sopenharmony_ci if (par->clks[i]) { 2738c2ecf20Sopenharmony_ci if (par->clks_enabled) 2748c2ecf20Sopenharmony_ci clk_disable_unprepare(par->clks[i]); 2758c2ecf20Sopenharmony_ci clk_put(par->clks[i]); 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci kfree(par->clks); 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ci#else 2828c2ecf20Sopenharmony_cistatic int simplefb_clocks_get(struct simplefb_par *par, 2838c2ecf20Sopenharmony_ci struct platform_device *pdev) { return 0; } 2848c2ecf20Sopenharmony_cistatic void simplefb_clocks_enable(struct simplefb_par *par, 2858c2ecf20Sopenharmony_ci struct platform_device *pdev) { } 2868c2ecf20Sopenharmony_cistatic void simplefb_clocks_destroy(struct simplefb_par *par) { } 2878c2ecf20Sopenharmony_ci#endif 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci#if defined CONFIG_OF && defined CONFIG_REGULATOR 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci#define SUPPLY_SUFFIX "-supply" 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci/* 2948c2ecf20Sopenharmony_ci * Regulator handling code. 2958c2ecf20Sopenharmony_ci * 2968c2ecf20Sopenharmony_ci * Here we handle the num-supplies and vin*-supply properties of our 2978c2ecf20Sopenharmony_ci * "simple-framebuffer" dt node. This is necessary so that we can make sure 2988c2ecf20Sopenharmony_ci * that any regulators needed by the display hardware that the bootloader 2998c2ecf20Sopenharmony_ci * set up for us (and for which it provided a simplefb dt node), stay up, 3008c2ecf20Sopenharmony_ci * for the life of the simplefb driver. 3018c2ecf20Sopenharmony_ci * 3028c2ecf20Sopenharmony_ci * When the driver unloads, we cleanly disable, and then release the 3038c2ecf20Sopenharmony_ci * regulators. 3048c2ecf20Sopenharmony_ci * 3058c2ecf20Sopenharmony_ci * We only complain about errors here, no action is taken as the most likely 3068c2ecf20Sopenharmony_ci * error can only happen due to a mismatch between the bootloader which set 3078c2ecf20Sopenharmony_ci * up simplefb, and the regulator definitions in the device tree. Chances are 3088c2ecf20Sopenharmony_ci * that there are no adverse effects, and if there are, a clean teardown of 3098c2ecf20Sopenharmony_ci * the fb probe will not help us much either. So just complain and carry on, 3108c2ecf20Sopenharmony_ci * and hope that the user actually gets a working fb at the end of things. 3118c2ecf20Sopenharmony_ci */ 3128c2ecf20Sopenharmony_cistatic int simplefb_regulators_get(struct simplefb_par *par, 3138c2ecf20Sopenharmony_ci struct platform_device *pdev) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 3168c2ecf20Sopenharmony_ci struct property *prop; 3178c2ecf20Sopenharmony_ci struct regulator *regulator; 3188c2ecf20Sopenharmony_ci const char *p; 3198c2ecf20Sopenharmony_ci int count = 0, i = 0; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci if (dev_get_platdata(&pdev->dev) || !np) 3228c2ecf20Sopenharmony_ci return 0; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci /* Count the number of regulator supplies */ 3258c2ecf20Sopenharmony_ci for_each_property_of_node(np, prop) { 3268c2ecf20Sopenharmony_ci p = strstr(prop->name, SUPPLY_SUFFIX); 3278c2ecf20Sopenharmony_ci if (p && p != prop->name) 3288c2ecf20Sopenharmony_ci count++; 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci if (!count) 3328c2ecf20Sopenharmony_ci return 0; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci par->regulators = devm_kcalloc(&pdev->dev, count, 3358c2ecf20Sopenharmony_ci sizeof(struct regulator *), GFP_KERNEL); 3368c2ecf20Sopenharmony_ci if (!par->regulators) 3378c2ecf20Sopenharmony_ci return -ENOMEM; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci /* Get all the regulators */ 3408c2ecf20Sopenharmony_ci for_each_property_of_node(np, prop) { 3418c2ecf20Sopenharmony_ci char name[32]; /* 32 is max size of property name */ 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci p = strstr(prop->name, SUPPLY_SUFFIX); 3448c2ecf20Sopenharmony_ci if (!p || p == prop->name) 3458c2ecf20Sopenharmony_ci continue; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci strlcpy(name, prop->name, 3488c2ecf20Sopenharmony_ci strlen(prop->name) - strlen(SUPPLY_SUFFIX) + 1); 3498c2ecf20Sopenharmony_ci regulator = devm_regulator_get_optional(&pdev->dev, name); 3508c2ecf20Sopenharmony_ci if (IS_ERR(regulator)) { 3518c2ecf20Sopenharmony_ci if (PTR_ERR(regulator) == -EPROBE_DEFER) 3528c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 3538c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "regulator %s not found: %ld\n", 3548c2ecf20Sopenharmony_ci name, PTR_ERR(regulator)); 3558c2ecf20Sopenharmony_ci continue; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci par->regulators[i++] = regulator; 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci par->regulator_count = i; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci return 0; 3628c2ecf20Sopenharmony_ci} 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_cistatic void simplefb_regulators_enable(struct simplefb_par *par, 3658c2ecf20Sopenharmony_ci struct platform_device *pdev) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci int i, ret; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci /* Enable all the regulators */ 3708c2ecf20Sopenharmony_ci for (i = 0; i < par->regulator_count; i++) { 3718c2ecf20Sopenharmony_ci ret = regulator_enable(par->regulators[i]); 3728c2ecf20Sopenharmony_ci if (ret) { 3738c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 3748c2ecf20Sopenharmony_ci "failed to enable regulator %d: %d\n", 3758c2ecf20Sopenharmony_ci i, ret); 3768c2ecf20Sopenharmony_ci devm_regulator_put(par->regulators[i]); 3778c2ecf20Sopenharmony_ci par->regulators[i] = NULL; 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci par->regulators_enabled = true; 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistatic void simplefb_regulators_destroy(struct simplefb_par *par) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci int i; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci if (!par->regulators || !par->regulators_enabled) 3888c2ecf20Sopenharmony_ci return; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci for (i = 0; i < par->regulator_count; i++) 3918c2ecf20Sopenharmony_ci if (par->regulators[i]) 3928c2ecf20Sopenharmony_ci regulator_disable(par->regulators[i]); 3938c2ecf20Sopenharmony_ci} 3948c2ecf20Sopenharmony_ci#else 3958c2ecf20Sopenharmony_cistatic int simplefb_regulators_get(struct simplefb_par *par, 3968c2ecf20Sopenharmony_ci struct platform_device *pdev) { return 0; } 3978c2ecf20Sopenharmony_cistatic void simplefb_regulators_enable(struct simplefb_par *par, 3988c2ecf20Sopenharmony_ci struct platform_device *pdev) { } 3998c2ecf20Sopenharmony_cistatic void simplefb_regulators_destroy(struct simplefb_par *par) { } 4008c2ecf20Sopenharmony_ci#endif 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_cistatic int simplefb_probe(struct platform_device *pdev) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci int ret; 4058c2ecf20Sopenharmony_ci struct simplefb_params params; 4068c2ecf20Sopenharmony_ci struct fb_info *info; 4078c2ecf20Sopenharmony_ci struct simplefb_par *par; 4088c2ecf20Sopenharmony_ci struct resource *mem; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci if (fb_get_options("simplefb", NULL)) 4118c2ecf20Sopenharmony_ci return -ENODEV; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci ret = -ENODEV; 4148c2ecf20Sopenharmony_ci if (dev_get_platdata(&pdev->dev)) 4158c2ecf20Sopenharmony_ci ret = simplefb_parse_pd(pdev, ¶ms); 4168c2ecf20Sopenharmony_ci else if (pdev->dev.of_node) 4178c2ecf20Sopenharmony_ci ret = simplefb_parse_dt(pdev, ¶ms); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci if (ret) 4208c2ecf20Sopenharmony_ci return ret; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 4238c2ecf20Sopenharmony_ci if (!mem) { 4248c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "No memory resource\n"); 4258c2ecf20Sopenharmony_ci return -EINVAL; 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci info = framebuffer_alloc(sizeof(struct simplefb_par), &pdev->dev); 4298c2ecf20Sopenharmony_ci if (!info) 4308c2ecf20Sopenharmony_ci return -ENOMEM; 4318c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, info); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci par = info->par; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci info->fix = simplefb_fix; 4368c2ecf20Sopenharmony_ci info->fix.smem_start = mem->start; 4378c2ecf20Sopenharmony_ci info->fix.smem_len = resource_size(mem); 4388c2ecf20Sopenharmony_ci info->fix.line_length = params.stride; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci info->var = simplefb_var; 4418c2ecf20Sopenharmony_ci info->var.xres = params.width; 4428c2ecf20Sopenharmony_ci info->var.yres = params.height; 4438c2ecf20Sopenharmony_ci info->var.xres_virtual = params.width; 4448c2ecf20Sopenharmony_ci info->var.yres_virtual = params.height; 4458c2ecf20Sopenharmony_ci info->var.bits_per_pixel = params.format->bits_per_pixel; 4468c2ecf20Sopenharmony_ci info->var.red = params.format->red; 4478c2ecf20Sopenharmony_ci info->var.green = params.format->green; 4488c2ecf20Sopenharmony_ci info->var.blue = params.format->blue; 4498c2ecf20Sopenharmony_ci info->var.transp = params.format->transp; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci info->apertures = alloc_apertures(1); 4528c2ecf20Sopenharmony_ci if (!info->apertures) { 4538c2ecf20Sopenharmony_ci ret = -ENOMEM; 4548c2ecf20Sopenharmony_ci goto error_fb_release; 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci info->apertures->ranges[0].base = info->fix.smem_start; 4578c2ecf20Sopenharmony_ci info->apertures->ranges[0].size = info->fix.smem_len; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci info->fbops = &simplefb_ops; 4608c2ecf20Sopenharmony_ci info->flags = FBINFO_DEFAULT | FBINFO_MISC_FIRMWARE; 4618c2ecf20Sopenharmony_ci info->screen_base = ioremap_wc(info->fix.smem_start, 4628c2ecf20Sopenharmony_ci info->fix.smem_len); 4638c2ecf20Sopenharmony_ci if (!info->screen_base) { 4648c2ecf20Sopenharmony_ci ret = -ENOMEM; 4658c2ecf20Sopenharmony_ci goto error_fb_release; 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci info->pseudo_palette = par->palette; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci ret = simplefb_clocks_get(par, pdev); 4708c2ecf20Sopenharmony_ci if (ret < 0) 4718c2ecf20Sopenharmony_ci goto error_unmap; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci ret = simplefb_regulators_get(par, pdev); 4748c2ecf20Sopenharmony_ci if (ret < 0) 4758c2ecf20Sopenharmony_ci goto error_clocks; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci simplefb_clocks_enable(par, pdev); 4788c2ecf20Sopenharmony_ci simplefb_regulators_enable(par, pdev); 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "framebuffer at 0x%lx, 0x%x bytes, mapped to 0x%p\n", 4818c2ecf20Sopenharmony_ci info->fix.smem_start, info->fix.smem_len, 4828c2ecf20Sopenharmony_ci info->screen_base); 4838c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "format=%s, mode=%dx%dx%d, linelength=%d\n", 4848c2ecf20Sopenharmony_ci params.format->name, 4858c2ecf20Sopenharmony_ci info->var.xres, info->var.yres, 4868c2ecf20Sopenharmony_ci info->var.bits_per_pixel, info->fix.line_length); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci ret = register_framebuffer(info); 4898c2ecf20Sopenharmony_ci if (ret < 0) { 4908c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Unable to register simplefb: %d\n", ret); 4918c2ecf20Sopenharmony_ci goto error_regulators; 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "fb%d: simplefb registered!\n", info->node); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci return 0; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_cierror_regulators: 4998c2ecf20Sopenharmony_ci simplefb_regulators_destroy(par); 5008c2ecf20Sopenharmony_cierror_clocks: 5018c2ecf20Sopenharmony_ci simplefb_clocks_destroy(par); 5028c2ecf20Sopenharmony_cierror_unmap: 5038c2ecf20Sopenharmony_ci iounmap(info->screen_base); 5048c2ecf20Sopenharmony_cierror_fb_release: 5058c2ecf20Sopenharmony_ci framebuffer_release(info); 5068c2ecf20Sopenharmony_ci return ret; 5078c2ecf20Sopenharmony_ci} 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_cistatic int simplefb_remove(struct platform_device *pdev) 5108c2ecf20Sopenharmony_ci{ 5118c2ecf20Sopenharmony_ci struct fb_info *info = platform_get_drvdata(pdev); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci unregister_framebuffer(info); 5148c2ecf20Sopenharmony_ci framebuffer_release(info); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci return 0; 5178c2ecf20Sopenharmony_ci} 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_cistatic const struct of_device_id simplefb_of_match[] = { 5208c2ecf20Sopenharmony_ci { .compatible = "simple-framebuffer", }, 5218c2ecf20Sopenharmony_ci { }, 5228c2ecf20Sopenharmony_ci}; 5238c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, simplefb_of_match); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_cistatic struct platform_driver simplefb_driver = { 5268c2ecf20Sopenharmony_ci .driver = { 5278c2ecf20Sopenharmony_ci .name = "simple-framebuffer", 5288c2ecf20Sopenharmony_ci .of_match_table = simplefb_of_match, 5298c2ecf20Sopenharmony_ci }, 5308c2ecf20Sopenharmony_ci .probe = simplefb_probe, 5318c2ecf20Sopenharmony_ci .remove = simplefb_remove, 5328c2ecf20Sopenharmony_ci}; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_cistatic int __init simplefb_init(void) 5358c2ecf20Sopenharmony_ci{ 5368c2ecf20Sopenharmony_ci int ret; 5378c2ecf20Sopenharmony_ci struct device_node *np; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci ret = platform_driver_register(&simplefb_driver); 5408c2ecf20Sopenharmony_ci if (ret) 5418c2ecf20Sopenharmony_ci return ret; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_OF_ADDRESS) && of_chosen) { 5448c2ecf20Sopenharmony_ci for_each_child_of_node(of_chosen, np) { 5458c2ecf20Sopenharmony_ci if (of_device_is_compatible(np, "simple-framebuffer")) 5468c2ecf20Sopenharmony_ci of_platform_device_create(np, NULL, NULL); 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci } 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci return 0; 5518c2ecf20Sopenharmony_ci} 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_cifs_initcall(simplefb_init); 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ciMODULE_AUTHOR("Stephen Warren <swarren@wwwdotorg.org>"); 5568c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Simple framebuffer driver"); 5578c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 558