1319251c5SThomas Zimmermann // SPDX-License-Identifier: GPL-2.0-only 2319251c5SThomas Zimmermann 3319251c5SThomas Zimmermann #include <linux/aperture.h> 4319251c5SThomas Zimmermann #include <linux/clk.h> 5319251c5SThomas Zimmermann #include <linux/of_clk.h> 6319251c5SThomas Zimmermann #include <linux/minmax.h> 7319251c5SThomas Zimmermann #include <linux/of_address.h> 8319251c5SThomas Zimmermann #include <linux/platform_data/simplefb.h> 9319251c5SThomas Zimmermann #include <linux/platform_device.h> 10319251c5SThomas Zimmermann #include <linux/pm_domain.h> 11319251c5SThomas Zimmermann #include <linux/regulator/consumer.h> 12319251c5SThomas Zimmermann 13319251c5SThomas Zimmermann #include <drm/clients/drm_client_setup.h> 14319251c5SThomas Zimmermann #include <drm/drm_atomic.h> 15319251c5SThomas Zimmermann #include <drm/drm_atomic_state_helper.h> 16319251c5SThomas Zimmermann #include <drm/drm_connector.h> 17319251c5SThomas Zimmermann #include <drm/drm_damage_helper.h> 18319251c5SThomas Zimmermann #include <drm/drm_device.h> 19319251c5SThomas Zimmermann #include <drm/drm_drv.h> 20319251c5SThomas Zimmermann #include <drm/drm_fbdev_shmem.h> 21319251c5SThomas Zimmermann #include <drm/drm_format_helper.h> 22319251c5SThomas Zimmermann #include <drm/drm_framebuffer.h> 23319251c5SThomas Zimmermann #include <drm/drm_gem_atomic_helper.h> 24319251c5SThomas Zimmermann #include <drm/drm_gem_framebuffer_helper.h> 25319251c5SThomas Zimmermann #include <drm/drm_gem_shmem_helper.h> 26319251c5SThomas Zimmermann #include <drm/drm_managed.h> 27319251c5SThomas Zimmermann #include <drm/drm_modeset_helper_vtables.h> 28319251c5SThomas Zimmermann #include <drm/drm_probe_helper.h> 29319251c5SThomas Zimmermann 30b5626f6fSThomas Zimmermann #include "drm_sysfb_helper.h" 31b5626f6fSThomas Zimmermann 32319251c5SThomas Zimmermann #define DRIVER_NAME "simpledrm" 33319251c5SThomas Zimmermann #define DRIVER_DESC "DRM driver for simple-framebuffer platform devices" 34319251c5SThomas Zimmermann #define DRIVER_MAJOR 1 35319251c5SThomas Zimmermann #define DRIVER_MINOR 0 36319251c5SThomas Zimmermann 37319251c5SThomas Zimmermann /* 38319251c5SThomas Zimmermann * Helpers for simplefb 39319251c5SThomas Zimmermann */ 40319251c5SThomas Zimmermann 41319251c5SThomas Zimmermann static int 42319251c5SThomas Zimmermann simplefb_get_validated_int(struct drm_device *dev, const char *name, 43319251c5SThomas Zimmermann uint32_t value) 44319251c5SThomas Zimmermann { 45*6046b49bSThomas Zimmermann return drm_sysfb_get_validated_int(dev, name, value, INT_MAX); 46319251c5SThomas Zimmermann } 47319251c5SThomas Zimmermann 48319251c5SThomas Zimmermann static int 49319251c5SThomas Zimmermann simplefb_get_validated_int0(struct drm_device *dev, const char *name, 50319251c5SThomas Zimmermann uint32_t value) 51319251c5SThomas Zimmermann { 52*6046b49bSThomas Zimmermann return drm_sysfb_get_validated_int0(dev, name, value, INT_MAX); 53319251c5SThomas Zimmermann } 54319251c5SThomas Zimmermann 55319251c5SThomas Zimmermann static const struct drm_format_info * 56319251c5SThomas Zimmermann simplefb_get_validated_format(struct drm_device *dev, const char *format_name) 57319251c5SThomas Zimmermann { 58319251c5SThomas Zimmermann static const struct simplefb_format formats[] = SIMPLEFB_FORMATS; 59319251c5SThomas Zimmermann const struct simplefb_format *fmt = formats; 60319251c5SThomas Zimmermann const struct simplefb_format *end = fmt + ARRAY_SIZE(formats); 61319251c5SThomas Zimmermann const struct drm_format_info *info; 62319251c5SThomas Zimmermann 63319251c5SThomas Zimmermann if (!format_name) { 64319251c5SThomas Zimmermann drm_err(dev, "simplefb: missing framebuffer format\n"); 65319251c5SThomas Zimmermann return ERR_PTR(-EINVAL); 66319251c5SThomas Zimmermann } 67319251c5SThomas Zimmermann 68319251c5SThomas Zimmermann while (fmt < end) { 69319251c5SThomas Zimmermann if (!strcmp(format_name, fmt->name)) { 70319251c5SThomas Zimmermann info = drm_format_info(fmt->fourcc); 71319251c5SThomas Zimmermann if (!info) 72319251c5SThomas Zimmermann return ERR_PTR(-EINVAL); 73319251c5SThomas Zimmermann return info; 74319251c5SThomas Zimmermann } 75319251c5SThomas Zimmermann ++fmt; 76319251c5SThomas Zimmermann } 77319251c5SThomas Zimmermann 78319251c5SThomas Zimmermann drm_err(dev, "simplefb: unknown framebuffer format %s\n", 79319251c5SThomas Zimmermann format_name); 80319251c5SThomas Zimmermann 81319251c5SThomas Zimmermann return ERR_PTR(-EINVAL); 82319251c5SThomas Zimmermann } 83319251c5SThomas Zimmermann 84319251c5SThomas Zimmermann static int 85319251c5SThomas Zimmermann simplefb_get_width_pd(struct drm_device *dev, 86319251c5SThomas Zimmermann const struct simplefb_platform_data *pd) 87319251c5SThomas Zimmermann { 88319251c5SThomas Zimmermann return simplefb_get_validated_int0(dev, "width", pd->width); 89319251c5SThomas Zimmermann } 90319251c5SThomas Zimmermann 91319251c5SThomas Zimmermann static int 92319251c5SThomas Zimmermann simplefb_get_height_pd(struct drm_device *dev, 93319251c5SThomas Zimmermann const struct simplefb_platform_data *pd) 94319251c5SThomas Zimmermann { 95319251c5SThomas Zimmermann return simplefb_get_validated_int0(dev, "height", pd->height); 96319251c5SThomas Zimmermann } 97319251c5SThomas Zimmermann 98319251c5SThomas Zimmermann static int 99319251c5SThomas Zimmermann simplefb_get_stride_pd(struct drm_device *dev, 100319251c5SThomas Zimmermann const struct simplefb_platform_data *pd) 101319251c5SThomas Zimmermann { 102319251c5SThomas Zimmermann return simplefb_get_validated_int(dev, "stride", pd->stride); 103319251c5SThomas Zimmermann } 104319251c5SThomas Zimmermann 105319251c5SThomas Zimmermann static const struct drm_format_info * 106319251c5SThomas Zimmermann simplefb_get_format_pd(struct drm_device *dev, 107319251c5SThomas Zimmermann const struct simplefb_platform_data *pd) 108319251c5SThomas Zimmermann { 109319251c5SThomas Zimmermann return simplefb_get_validated_format(dev, pd->format); 110319251c5SThomas Zimmermann } 111319251c5SThomas Zimmermann 112319251c5SThomas Zimmermann static int 113319251c5SThomas Zimmermann simplefb_read_u32_of(struct drm_device *dev, struct device_node *of_node, 114319251c5SThomas Zimmermann const char *name, u32 *value) 115319251c5SThomas Zimmermann { 116319251c5SThomas Zimmermann int ret = of_property_read_u32(of_node, name, value); 117319251c5SThomas Zimmermann 118319251c5SThomas Zimmermann if (ret) 119319251c5SThomas Zimmermann drm_err(dev, "simplefb: cannot parse framebuffer %s: error %d\n", 120319251c5SThomas Zimmermann name, ret); 121319251c5SThomas Zimmermann return ret; 122319251c5SThomas Zimmermann } 123319251c5SThomas Zimmermann 124319251c5SThomas Zimmermann static int 125319251c5SThomas Zimmermann simplefb_read_string_of(struct drm_device *dev, struct device_node *of_node, 126319251c5SThomas Zimmermann const char *name, const char **value) 127319251c5SThomas Zimmermann { 128319251c5SThomas Zimmermann int ret = of_property_read_string(of_node, name, value); 129319251c5SThomas Zimmermann 130319251c5SThomas Zimmermann if (ret) 131319251c5SThomas Zimmermann drm_err(dev, "simplefb: cannot parse framebuffer %s: error %d\n", 132319251c5SThomas Zimmermann name, ret); 133319251c5SThomas Zimmermann return ret; 134319251c5SThomas Zimmermann } 135319251c5SThomas Zimmermann 136319251c5SThomas Zimmermann static int 137319251c5SThomas Zimmermann simplefb_get_width_of(struct drm_device *dev, struct device_node *of_node) 138319251c5SThomas Zimmermann { 139319251c5SThomas Zimmermann u32 width; 140319251c5SThomas Zimmermann int ret = simplefb_read_u32_of(dev, of_node, "width", &width); 141319251c5SThomas Zimmermann 142319251c5SThomas Zimmermann if (ret) 143319251c5SThomas Zimmermann return ret; 144319251c5SThomas Zimmermann return simplefb_get_validated_int0(dev, "width", width); 145319251c5SThomas Zimmermann } 146319251c5SThomas Zimmermann 147319251c5SThomas Zimmermann static int 148319251c5SThomas Zimmermann simplefb_get_height_of(struct drm_device *dev, struct device_node *of_node) 149319251c5SThomas Zimmermann { 150319251c5SThomas Zimmermann u32 height; 151319251c5SThomas Zimmermann int ret = simplefb_read_u32_of(dev, of_node, "height", &height); 152319251c5SThomas Zimmermann 153319251c5SThomas Zimmermann if (ret) 154319251c5SThomas Zimmermann return ret; 155319251c5SThomas Zimmermann return simplefb_get_validated_int0(dev, "height", height); 156319251c5SThomas Zimmermann } 157319251c5SThomas Zimmermann 158319251c5SThomas Zimmermann static int 159319251c5SThomas Zimmermann simplefb_get_stride_of(struct drm_device *dev, struct device_node *of_node) 160319251c5SThomas Zimmermann { 161319251c5SThomas Zimmermann u32 stride; 162319251c5SThomas Zimmermann int ret = simplefb_read_u32_of(dev, of_node, "stride", &stride); 163319251c5SThomas Zimmermann 164319251c5SThomas Zimmermann if (ret) 165319251c5SThomas Zimmermann return ret; 166319251c5SThomas Zimmermann return simplefb_get_validated_int(dev, "stride", stride); 167319251c5SThomas Zimmermann } 168319251c5SThomas Zimmermann 169319251c5SThomas Zimmermann static const struct drm_format_info * 170319251c5SThomas Zimmermann simplefb_get_format_of(struct drm_device *dev, struct device_node *of_node) 171319251c5SThomas Zimmermann { 172319251c5SThomas Zimmermann const char *format; 173319251c5SThomas Zimmermann int ret = simplefb_read_string_of(dev, of_node, "format", &format); 174319251c5SThomas Zimmermann 175319251c5SThomas Zimmermann if (ret) 176319251c5SThomas Zimmermann return ERR_PTR(ret); 177319251c5SThomas Zimmermann return simplefb_get_validated_format(dev, format); 178319251c5SThomas Zimmermann } 179319251c5SThomas Zimmermann 180319251c5SThomas Zimmermann static struct resource * 181319251c5SThomas Zimmermann simplefb_get_memory_of(struct drm_device *dev, struct device_node *of_node) 182319251c5SThomas Zimmermann { 183319251c5SThomas Zimmermann struct device_node *np; 184319251c5SThomas Zimmermann struct resource *res; 185319251c5SThomas Zimmermann int err; 186319251c5SThomas Zimmermann 187319251c5SThomas Zimmermann np = of_parse_phandle(of_node, "memory-region", 0); 188319251c5SThomas Zimmermann if (!np) 189319251c5SThomas Zimmermann return NULL; 190319251c5SThomas Zimmermann 191319251c5SThomas Zimmermann res = devm_kzalloc(dev->dev, sizeof(*res), GFP_KERNEL); 192319251c5SThomas Zimmermann if (!res) 193319251c5SThomas Zimmermann return ERR_PTR(-ENOMEM); 194319251c5SThomas Zimmermann 195319251c5SThomas Zimmermann err = of_address_to_resource(np, 0, res); 196319251c5SThomas Zimmermann if (err) 197319251c5SThomas Zimmermann return ERR_PTR(err); 198319251c5SThomas Zimmermann 199319251c5SThomas Zimmermann if (of_property_present(of_node, "reg")) 200319251c5SThomas Zimmermann drm_warn(dev, "preferring \"memory-region\" over \"reg\" property\n"); 201319251c5SThomas Zimmermann 202319251c5SThomas Zimmermann return res; 203319251c5SThomas Zimmermann } 204319251c5SThomas Zimmermann 205319251c5SThomas Zimmermann /* 206319251c5SThomas Zimmermann * Simple Framebuffer device 207319251c5SThomas Zimmermann */ 208319251c5SThomas Zimmermann 209319251c5SThomas Zimmermann struct simpledrm_device { 210b5626f6fSThomas Zimmermann struct drm_sysfb_device sysfb; 211319251c5SThomas Zimmermann 212319251c5SThomas Zimmermann /* clocks */ 213319251c5SThomas Zimmermann #if defined CONFIG_OF && defined CONFIG_COMMON_CLK 214319251c5SThomas Zimmermann unsigned int clk_count; 215319251c5SThomas Zimmermann struct clk **clks; 216319251c5SThomas Zimmermann #endif 217319251c5SThomas Zimmermann /* regulators */ 218319251c5SThomas Zimmermann #if defined CONFIG_OF && defined CONFIG_REGULATOR 219319251c5SThomas Zimmermann unsigned int regulator_count; 220319251c5SThomas Zimmermann struct regulator **regulators; 221319251c5SThomas Zimmermann #endif 222319251c5SThomas Zimmermann /* power-domains */ 223319251c5SThomas Zimmermann #if defined CONFIG_OF && defined CONFIG_PM_GENERIC_DOMAINS 224319251c5SThomas Zimmermann int pwr_dom_count; 225319251c5SThomas Zimmermann struct device **pwr_dom_devs; 226319251c5SThomas Zimmermann struct device_link **pwr_dom_links; 227319251c5SThomas Zimmermann #endif 228319251c5SThomas Zimmermann 229319251c5SThomas Zimmermann /* modesetting */ 230177dfbdbSThomas Zimmermann u32 formats[DRM_SYSFB_PLANE_NFORMATS(1)]; 231319251c5SThomas Zimmermann struct drm_plane primary_plane; 232319251c5SThomas Zimmermann struct drm_crtc crtc; 233319251c5SThomas Zimmermann struct drm_encoder encoder; 234319251c5SThomas Zimmermann struct drm_connector connector; 235319251c5SThomas Zimmermann }; 236319251c5SThomas Zimmermann 237319251c5SThomas Zimmermann /* 238319251c5SThomas Zimmermann * Hardware 239319251c5SThomas Zimmermann */ 240319251c5SThomas Zimmermann 241319251c5SThomas Zimmermann #if defined CONFIG_OF && defined CONFIG_COMMON_CLK 242319251c5SThomas Zimmermann /* 243319251c5SThomas Zimmermann * Clock handling code. 244319251c5SThomas Zimmermann * 245319251c5SThomas Zimmermann * Here we handle the clocks property of our "simple-framebuffer" dt node. 246319251c5SThomas Zimmermann * This is necessary so that we can make sure that any clocks needed by 247319251c5SThomas Zimmermann * the display engine that the bootloader set up for us (and for which it 248319251c5SThomas Zimmermann * provided a simplefb dt node), stay up, for the life of the simplefb 249319251c5SThomas Zimmermann * driver. 250319251c5SThomas Zimmermann * 251319251c5SThomas Zimmermann * When the driver unloads, we cleanly disable, and then release the clocks. 252319251c5SThomas Zimmermann * 253319251c5SThomas Zimmermann * We only complain about errors here, no action is taken as the most likely 254319251c5SThomas Zimmermann * error can only happen due to a mismatch between the bootloader which set 255319251c5SThomas Zimmermann * up simplefb, and the clock definitions in the device tree. Chances are 256319251c5SThomas Zimmermann * that there are no adverse effects, and if there are, a clean teardown of 257319251c5SThomas Zimmermann * the fb probe will not help us much either. So just complain and carry on, 258319251c5SThomas Zimmermann * and hope that the user actually gets a working fb at the end of things. 259319251c5SThomas Zimmermann */ 260319251c5SThomas Zimmermann 261319251c5SThomas Zimmermann static void simpledrm_device_release_clocks(void *res) 262319251c5SThomas Zimmermann { 263d231cde7SThomas Zimmermann struct simpledrm_device *sdev = res; 264319251c5SThomas Zimmermann unsigned int i; 265319251c5SThomas Zimmermann 266319251c5SThomas Zimmermann for (i = 0; i < sdev->clk_count; ++i) { 267319251c5SThomas Zimmermann if (sdev->clks[i]) { 268319251c5SThomas Zimmermann clk_disable_unprepare(sdev->clks[i]); 269319251c5SThomas Zimmermann clk_put(sdev->clks[i]); 270319251c5SThomas Zimmermann } 271319251c5SThomas Zimmermann } 272319251c5SThomas Zimmermann } 273319251c5SThomas Zimmermann 274319251c5SThomas Zimmermann static int simpledrm_device_init_clocks(struct simpledrm_device *sdev) 275319251c5SThomas Zimmermann { 276b5626f6fSThomas Zimmermann struct drm_device *dev = &sdev->sysfb.dev; 277319251c5SThomas Zimmermann struct platform_device *pdev = to_platform_device(dev->dev); 278319251c5SThomas Zimmermann struct device_node *of_node = pdev->dev.of_node; 279319251c5SThomas Zimmermann struct clk *clock; 280319251c5SThomas Zimmermann unsigned int i; 281319251c5SThomas Zimmermann int ret; 282319251c5SThomas Zimmermann 283319251c5SThomas Zimmermann if (dev_get_platdata(&pdev->dev) || !of_node) 284319251c5SThomas Zimmermann return 0; 285319251c5SThomas Zimmermann 286319251c5SThomas Zimmermann sdev->clk_count = of_clk_get_parent_count(of_node); 287319251c5SThomas Zimmermann if (!sdev->clk_count) 288319251c5SThomas Zimmermann return 0; 289319251c5SThomas Zimmermann 290319251c5SThomas Zimmermann sdev->clks = drmm_kzalloc(dev, sdev->clk_count * sizeof(sdev->clks[0]), 291319251c5SThomas Zimmermann GFP_KERNEL); 292319251c5SThomas Zimmermann if (!sdev->clks) 293319251c5SThomas Zimmermann return -ENOMEM; 294319251c5SThomas Zimmermann 295319251c5SThomas Zimmermann for (i = 0; i < sdev->clk_count; ++i) { 296319251c5SThomas Zimmermann clock = of_clk_get(of_node, i); 297319251c5SThomas Zimmermann if (IS_ERR(clock)) { 298319251c5SThomas Zimmermann ret = PTR_ERR(clock); 299319251c5SThomas Zimmermann if (ret == -EPROBE_DEFER) 300319251c5SThomas Zimmermann goto err; 301319251c5SThomas Zimmermann drm_err(dev, "clock %u not found: %d\n", i, ret); 302319251c5SThomas Zimmermann continue; 303319251c5SThomas Zimmermann } 304319251c5SThomas Zimmermann ret = clk_prepare_enable(clock); 305319251c5SThomas Zimmermann if (ret) { 306319251c5SThomas Zimmermann drm_err(dev, "failed to enable clock %u: %d\n", 307319251c5SThomas Zimmermann i, ret); 308319251c5SThomas Zimmermann clk_put(clock); 309319251c5SThomas Zimmermann continue; 310319251c5SThomas Zimmermann } 311319251c5SThomas Zimmermann sdev->clks[i] = clock; 312319251c5SThomas Zimmermann } 313319251c5SThomas Zimmermann 314319251c5SThomas Zimmermann return devm_add_action_or_reset(&pdev->dev, 315319251c5SThomas Zimmermann simpledrm_device_release_clocks, 316319251c5SThomas Zimmermann sdev); 317319251c5SThomas Zimmermann 318319251c5SThomas Zimmermann err: 319319251c5SThomas Zimmermann while (i) { 320319251c5SThomas Zimmermann --i; 321319251c5SThomas Zimmermann if (sdev->clks[i]) { 322319251c5SThomas Zimmermann clk_disable_unprepare(sdev->clks[i]); 323319251c5SThomas Zimmermann clk_put(sdev->clks[i]); 324319251c5SThomas Zimmermann } 325319251c5SThomas Zimmermann } 326319251c5SThomas Zimmermann return ret; 327319251c5SThomas Zimmermann } 328319251c5SThomas Zimmermann #else 329319251c5SThomas Zimmermann static int simpledrm_device_init_clocks(struct simpledrm_device *sdev) 330319251c5SThomas Zimmermann { 331319251c5SThomas Zimmermann return 0; 332319251c5SThomas Zimmermann } 333319251c5SThomas Zimmermann #endif 334319251c5SThomas Zimmermann 335319251c5SThomas Zimmermann #if defined CONFIG_OF && defined CONFIG_REGULATOR 336319251c5SThomas Zimmermann 337319251c5SThomas Zimmermann #define SUPPLY_SUFFIX "-supply" 338319251c5SThomas Zimmermann 339319251c5SThomas Zimmermann /* 340319251c5SThomas Zimmermann * Regulator handling code. 341319251c5SThomas Zimmermann * 342319251c5SThomas Zimmermann * Here we handle the num-supplies and vin*-supply properties of our 343319251c5SThomas Zimmermann * "simple-framebuffer" dt node. This is necessary so that we can make sure 344319251c5SThomas Zimmermann * that any regulators needed by the display hardware that the bootloader 345319251c5SThomas Zimmermann * set up for us (and for which it provided a simplefb dt node), stay up, 346319251c5SThomas Zimmermann * for the life of the simplefb driver. 347319251c5SThomas Zimmermann * 348319251c5SThomas Zimmermann * When the driver unloads, we cleanly disable, and then release the 349319251c5SThomas Zimmermann * regulators. 350319251c5SThomas Zimmermann * 351319251c5SThomas Zimmermann * We only complain about errors here, no action is taken as the most likely 352319251c5SThomas Zimmermann * error can only happen due to a mismatch between the bootloader which set 353319251c5SThomas Zimmermann * up simplefb, and the regulator definitions in the device tree. Chances are 354319251c5SThomas Zimmermann * that there are no adverse effects, and if there are, a clean teardown of 355319251c5SThomas Zimmermann * the fb probe will not help us much either. So just complain and carry on, 356319251c5SThomas Zimmermann * and hope that the user actually gets a working fb at the end of things. 357319251c5SThomas Zimmermann */ 358319251c5SThomas Zimmermann 359319251c5SThomas Zimmermann static void simpledrm_device_release_regulators(void *res) 360319251c5SThomas Zimmermann { 361d231cde7SThomas Zimmermann struct simpledrm_device *sdev = res; 362319251c5SThomas Zimmermann unsigned int i; 363319251c5SThomas Zimmermann 364319251c5SThomas Zimmermann for (i = 0; i < sdev->regulator_count; ++i) { 365319251c5SThomas Zimmermann if (sdev->regulators[i]) { 366319251c5SThomas Zimmermann regulator_disable(sdev->regulators[i]); 367319251c5SThomas Zimmermann regulator_put(sdev->regulators[i]); 368319251c5SThomas Zimmermann } 369319251c5SThomas Zimmermann } 370319251c5SThomas Zimmermann } 371319251c5SThomas Zimmermann 372319251c5SThomas Zimmermann static int simpledrm_device_init_regulators(struct simpledrm_device *sdev) 373319251c5SThomas Zimmermann { 374b5626f6fSThomas Zimmermann struct drm_device *dev = &sdev->sysfb.dev; 375319251c5SThomas Zimmermann struct platform_device *pdev = to_platform_device(dev->dev); 376319251c5SThomas Zimmermann struct device_node *of_node = pdev->dev.of_node; 377319251c5SThomas Zimmermann struct property *prop; 378319251c5SThomas Zimmermann struct regulator *regulator; 379319251c5SThomas Zimmermann const char *p; 380319251c5SThomas Zimmermann unsigned int count = 0, i = 0; 381319251c5SThomas Zimmermann int ret; 382319251c5SThomas Zimmermann 383319251c5SThomas Zimmermann if (dev_get_platdata(&pdev->dev) || !of_node) 384319251c5SThomas Zimmermann return 0; 385319251c5SThomas Zimmermann 386319251c5SThomas Zimmermann /* Count the number of regulator supplies */ 387319251c5SThomas Zimmermann for_each_property_of_node(of_node, prop) { 388319251c5SThomas Zimmermann p = strstr(prop->name, SUPPLY_SUFFIX); 389319251c5SThomas Zimmermann if (p && p != prop->name) 390319251c5SThomas Zimmermann ++count; 391319251c5SThomas Zimmermann } 392319251c5SThomas Zimmermann 393319251c5SThomas Zimmermann if (!count) 394319251c5SThomas Zimmermann return 0; 395319251c5SThomas Zimmermann 396319251c5SThomas Zimmermann sdev->regulators = drmm_kzalloc(dev, 397319251c5SThomas Zimmermann count * sizeof(sdev->regulators[0]), 398319251c5SThomas Zimmermann GFP_KERNEL); 399319251c5SThomas Zimmermann if (!sdev->regulators) 400319251c5SThomas Zimmermann return -ENOMEM; 401319251c5SThomas Zimmermann 402319251c5SThomas Zimmermann for_each_property_of_node(of_node, prop) { 403319251c5SThomas Zimmermann char name[32]; /* 32 is max size of property name */ 404319251c5SThomas Zimmermann size_t len; 405319251c5SThomas Zimmermann 406319251c5SThomas Zimmermann p = strstr(prop->name, SUPPLY_SUFFIX); 407319251c5SThomas Zimmermann if (!p || p == prop->name) 408319251c5SThomas Zimmermann continue; 409319251c5SThomas Zimmermann len = strlen(prop->name) - strlen(SUPPLY_SUFFIX) + 1; 410319251c5SThomas Zimmermann strscpy(name, prop->name, min(sizeof(name), len)); 411319251c5SThomas Zimmermann 412319251c5SThomas Zimmermann regulator = regulator_get_optional(&pdev->dev, name); 413319251c5SThomas Zimmermann if (IS_ERR(regulator)) { 414319251c5SThomas Zimmermann ret = PTR_ERR(regulator); 415319251c5SThomas Zimmermann if (ret == -EPROBE_DEFER) 416319251c5SThomas Zimmermann goto err; 417319251c5SThomas Zimmermann drm_err(dev, "regulator %s not found: %d\n", 418319251c5SThomas Zimmermann name, ret); 419319251c5SThomas Zimmermann continue; 420319251c5SThomas Zimmermann } 421319251c5SThomas Zimmermann 422319251c5SThomas Zimmermann ret = regulator_enable(regulator); 423319251c5SThomas Zimmermann if (ret) { 424319251c5SThomas Zimmermann drm_err(dev, "failed to enable regulator %u: %d\n", 425319251c5SThomas Zimmermann i, ret); 426319251c5SThomas Zimmermann regulator_put(regulator); 427319251c5SThomas Zimmermann continue; 428319251c5SThomas Zimmermann } 429319251c5SThomas Zimmermann 430319251c5SThomas Zimmermann sdev->regulators[i++] = regulator; 431319251c5SThomas Zimmermann } 432319251c5SThomas Zimmermann sdev->regulator_count = i; 433319251c5SThomas Zimmermann 434319251c5SThomas Zimmermann return devm_add_action_or_reset(&pdev->dev, 435319251c5SThomas Zimmermann simpledrm_device_release_regulators, 436319251c5SThomas Zimmermann sdev); 437319251c5SThomas Zimmermann 438319251c5SThomas Zimmermann err: 439319251c5SThomas Zimmermann while (i) { 440319251c5SThomas Zimmermann --i; 441319251c5SThomas Zimmermann if (sdev->regulators[i]) { 442319251c5SThomas Zimmermann regulator_disable(sdev->regulators[i]); 443319251c5SThomas Zimmermann regulator_put(sdev->regulators[i]); 444319251c5SThomas Zimmermann } 445319251c5SThomas Zimmermann } 446319251c5SThomas Zimmermann return ret; 447319251c5SThomas Zimmermann } 448319251c5SThomas Zimmermann #else 449319251c5SThomas Zimmermann static int simpledrm_device_init_regulators(struct simpledrm_device *sdev) 450319251c5SThomas Zimmermann { 451319251c5SThomas Zimmermann return 0; 452319251c5SThomas Zimmermann } 453319251c5SThomas Zimmermann #endif 454319251c5SThomas Zimmermann 455319251c5SThomas Zimmermann #if defined CONFIG_OF && defined CONFIG_PM_GENERIC_DOMAINS 456319251c5SThomas Zimmermann /* 457319251c5SThomas Zimmermann * Generic power domain handling code. 458319251c5SThomas Zimmermann * 459319251c5SThomas Zimmermann * Here we handle the power-domains properties of our "simple-framebuffer" 460319251c5SThomas Zimmermann * dt node. This is only necessary if there is more than one power-domain. 461319251c5SThomas Zimmermann * A single power-domains is handled automatically by the driver core. Multiple 462319251c5SThomas Zimmermann * power-domains have to be handled by drivers since the driver core can't know 463319251c5SThomas Zimmermann * the correct power sequencing. Power sequencing is not an issue for simpledrm 464319251c5SThomas Zimmermann * since the bootloader has put the power domains already in the correct state. 465319251c5SThomas Zimmermann * simpledrm has only to ensure they remain active for its lifetime. 466319251c5SThomas Zimmermann * 467319251c5SThomas Zimmermann * When the driver unloads, we detach from the power-domains. 468319251c5SThomas Zimmermann * 469319251c5SThomas Zimmermann * We only complain about errors here, no action is taken as the most likely 470319251c5SThomas Zimmermann * error can only happen due to a mismatch between the bootloader which set 471319251c5SThomas Zimmermann * up the "simple-framebuffer" dt node, and the PM domain providers in the 472319251c5SThomas Zimmermann * device tree. Chances are that there are no adverse effects, and if there are, 473319251c5SThomas Zimmermann * a clean teardown of the fb probe will not help us much either. So just 474319251c5SThomas Zimmermann * complain and carry on, and hope that the user actually gets a working fb at 475319251c5SThomas Zimmermann * the end of things. 476319251c5SThomas Zimmermann */ 477319251c5SThomas Zimmermann static void simpledrm_device_detach_genpd(void *res) 478319251c5SThomas Zimmermann { 479319251c5SThomas Zimmermann int i; 480319251c5SThomas Zimmermann struct simpledrm_device *sdev = res; 481319251c5SThomas Zimmermann 482319251c5SThomas Zimmermann if (sdev->pwr_dom_count <= 1) 483319251c5SThomas Zimmermann return; 484319251c5SThomas Zimmermann 485319251c5SThomas Zimmermann for (i = sdev->pwr_dom_count - 1; i >= 0; i--) { 486319251c5SThomas Zimmermann if (sdev->pwr_dom_links[i]) 487319251c5SThomas Zimmermann device_link_del(sdev->pwr_dom_links[i]); 488319251c5SThomas Zimmermann if (!IS_ERR_OR_NULL(sdev->pwr_dom_devs[i])) 489319251c5SThomas Zimmermann dev_pm_domain_detach(sdev->pwr_dom_devs[i], true); 490319251c5SThomas Zimmermann } 491319251c5SThomas Zimmermann } 492319251c5SThomas Zimmermann 493319251c5SThomas Zimmermann static int simpledrm_device_attach_genpd(struct simpledrm_device *sdev) 494319251c5SThomas Zimmermann { 495b5626f6fSThomas Zimmermann struct device *dev = sdev->sysfb.dev.dev; 496319251c5SThomas Zimmermann int i; 497319251c5SThomas Zimmermann 498319251c5SThomas Zimmermann sdev->pwr_dom_count = of_count_phandle_with_args(dev->of_node, "power-domains", 499319251c5SThomas Zimmermann "#power-domain-cells"); 500319251c5SThomas Zimmermann /* 501319251c5SThomas Zimmermann * Single power-domain devices are handled by driver core nothing to do 502319251c5SThomas Zimmermann * here. The same for device nodes without "power-domains" property. 503319251c5SThomas Zimmermann */ 504319251c5SThomas Zimmermann if (sdev->pwr_dom_count <= 1) 505319251c5SThomas Zimmermann return 0; 506319251c5SThomas Zimmermann 507319251c5SThomas Zimmermann sdev->pwr_dom_devs = devm_kcalloc(dev, sdev->pwr_dom_count, 508319251c5SThomas Zimmermann sizeof(*sdev->pwr_dom_devs), 509319251c5SThomas Zimmermann GFP_KERNEL); 510319251c5SThomas Zimmermann if (!sdev->pwr_dom_devs) 511319251c5SThomas Zimmermann return -ENOMEM; 512319251c5SThomas Zimmermann 513319251c5SThomas Zimmermann sdev->pwr_dom_links = devm_kcalloc(dev, sdev->pwr_dom_count, 514319251c5SThomas Zimmermann sizeof(*sdev->pwr_dom_links), 515319251c5SThomas Zimmermann GFP_KERNEL); 516319251c5SThomas Zimmermann if (!sdev->pwr_dom_links) 517319251c5SThomas Zimmermann return -ENOMEM; 518319251c5SThomas Zimmermann 519319251c5SThomas Zimmermann for (i = 0; i < sdev->pwr_dom_count; i++) { 520319251c5SThomas Zimmermann sdev->pwr_dom_devs[i] = dev_pm_domain_attach_by_id(dev, i); 521319251c5SThomas Zimmermann if (IS_ERR(sdev->pwr_dom_devs[i])) { 522319251c5SThomas Zimmermann int ret = PTR_ERR(sdev->pwr_dom_devs[i]); 523319251c5SThomas Zimmermann if (ret == -EPROBE_DEFER) { 524319251c5SThomas Zimmermann simpledrm_device_detach_genpd(sdev); 525319251c5SThomas Zimmermann return ret; 526319251c5SThomas Zimmermann } 527b5626f6fSThomas Zimmermann drm_warn(&sdev->sysfb.dev, 528319251c5SThomas Zimmermann "pm_domain_attach_by_id(%u) failed: %d\n", i, ret); 529319251c5SThomas Zimmermann continue; 530319251c5SThomas Zimmermann } 531319251c5SThomas Zimmermann 532319251c5SThomas Zimmermann sdev->pwr_dom_links[i] = device_link_add(dev, 533319251c5SThomas Zimmermann sdev->pwr_dom_devs[i], 534319251c5SThomas Zimmermann DL_FLAG_STATELESS | 535319251c5SThomas Zimmermann DL_FLAG_PM_RUNTIME | 536319251c5SThomas Zimmermann DL_FLAG_RPM_ACTIVE); 537319251c5SThomas Zimmermann if (!sdev->pwr_dom_links[i]) 538b5626f6fSThomas Zimmermann drm_warn(&sdev->sysfb.dev, "failed to link power-domain %d\n", i); 539319251c5SThomas Zimmermann } 540319251c5SThomas Zimmermann 541319251c5SThomas Zimmermann return devm_add_action_or_reset(dev, simpledrm_device_detach_genpd, sdev); 542319251c5SThomas Zimmermann } 543319251c5SThomas Zimmermann #else 544319251c5SThomas Zimmermann static int simpledrm_device_attach_genpd(struct simpledrm_device *sdev) 545319251c5SThomas Zimmermann { 546319251c5SThomas Zimmermann return 0; 547319251c5SThomas Zimmermann } 548319251c5SThomas Zimmermann #endif 549319251c5SThomas Zimmermann 550319251c5SThomas Zimmermann /* 551319251c5SThomas Zimmermann * Modesetting 552319251c5SThomas Zimmermann */ 553319251c5SThomas Zimmermann 554177dfbdbSThomas Zimmermann static const u64 simpledrm_primary_plane_format_modifiers[] = { 555177dfbdbSThomas Zimmermann DRM_SYSFB_PLANE_FORMAT_MODIFIERS, 556319251c5SThomas Zimmermann }; 557319251c5SThomas Zimmermann 558319251c5SThomas Zimmermann static const struct drm_plane_helper_funcs simpledrm_primary_plane_helper_funcs = { 559177dfbdbSThomas Zimmermann DRM_SYSFB_PLANE_HELPER_FUNCS, 560319251c5SThomas Zimmermann }; 561319251c5SThomas Zimmermann 562319251c5SThomas Zimmermann static const struct drm_plane_funcs simpledrm_primary_plane_funcs = { 563177dfbdbSThomas Zimmermann DRM_SYSFB_PLANE_FUNCS, 564319251c5SThomas Zimmermann .destroy = drm_plane_cleanup, 565319251c5SThomas Zimmermann }; 566319251c5SThomas Zimmermann 567319251c5SThomas Zimmermann static const struct drm_crtc_helper_funcs simpledrm_crtc_helper_funcs = { 568ea86aba4SThomas Zimmermann DRM_SYSFB_CRTC_HELPER_FUNCS, 569319251c5SThomas Zimmermann }; 570319251c5SThomas Zimmermann 571319251c5SThomas Zimmermann static const struct drm_crtc_funcs simpledrm_crtc_funcs = { 57268ab3253SThomas Zimmermann DRM_SYSFB_CRTC_FUNCS, 573319251c5SThomas Zimmermann .destroy = drm_crtc_cleanup, 574319251c5SThomas Zimmermann }; 575319251c5SThomas Zimmermann 576319251c5SThomas Zimmermann static const struct drm_encoder_funcs simpledrm_encoder_funcs = { 577319251c5SThomas Zimmermann .destroy = drm_encoder_cleanup, 578319251c5SThomas Zimmermann }; 579319251c5SThomas Zimmermann 580319251c5SThomas Zimmermann static const struct drm_connector_helper_funcs simpledrm_connector_helper_funcs = { 58101e48e52SThomas Zimmermann DRM_SYSFB_CONNECTOR_HELPER_FUNCS, 582319251c5SThomas Zimmermann }; 583319251c5SThomas Zimmermann 584319251c5SThomas Zimmermann static const struct drm_connector_funcs simpledrm_connector_funcs = { 58501e48e52SThomas Zimmermann DRM_SYSFB_CONNECTOR_FUNCS, 586319251c5SThomas Zimmermann .destroy = drm_connector_cleanup, 587319251c5SThomas Zimmermann }; 588319251c5SThomas Zimmermann 589319251c5SThomas Zimmermann static const struct drm_mode_config_funcs simpledrm_mode_config_funcs = { 590559d105fSThomas Zimmermann DRM_SYSFB_MODE_CONFIG_FUNCS, 591319251c5SThomas Zimmermann }; 592319251c5SThomas Zimmermann 593319251c5SThomas Zimmermann /* 594319251c5SThomas Zimmermann * Init / Cleanup 595319251c5SThomas Zimmermann */ 596319251c5SThomas Zimmermann 597319251c5SThomas Zimmermann static struct simpledrm_device *simpledrm_device_create(struct drm_driver *drv, 598319251c5SThomas Zimmermann struct platform_device *pdev) 599319251c5SThomas Zimmermann { 600319251c5SThomas Zimmermann const struct simplefb_platform_data *pd = dev_get_platdata(&pdev->dev); 601319251c5SThomas Zimmermann struct device_node *of_node = pdev->dev.of_node; 602319251c5SThomas Zimmermann struct simpledrm_device *sdev; 603b5626f6fSThomas Zimmermann struct drm_sysfb_device *sysfb; 604319251c5SThomas Zimmermann struct drm_device *dev; 605319251c5SThomas Zimmermann int width, height, stride; 606319251c5SThomas Zimmermann int width_mm = 0, height_mm = 0; 607319251c5SThomas Zimmermann struct device_node *panel_node; 608319251c5SThomas Zimmermann const struct drm_format_info *format; 609319251c5SThomas Zimmermann struct resource *res, *mem = NULL; 610319251c5SThomas Zimmermann struct drm_plane *primary_plane; 611319251c5SThomas Zimmermann struct drm_crtc *crtc; 612319251c5SThomas Zimmermann struct drm_encoder *encoder; 613319251c5SThomas Zimmermann struct drm_connector *connector; 614319251c5SThomas Zimmermann unsigned long max_width, max_height; 615319251c5SThomas Zimmermann size_t nformats; 616319251c5SThomas Zimmermann int ret; 617319251c5SThomas Zimmermann 618b5626f6fSThomas Zimmermann sdev = devm_drm_dev_alloc(&pdev->dev, drv, struct simpledrm_device, sysfb.dev); 619319251c5SThomas Zimmermann if (IS_ERR(sdev)) 620319251c5SThomas Zimmermann return ERR_CAST(sdev); 621b5626f6fSThomas Zimmermann sysfb = &sdev->sysfb; 622b5626f6fSThomas Zimmermann dev = &sysfb->dev; 623319251c5SThomas Zimmermann platform_set_drvdata(pdev, sdev); 624319251c5SThomas Zimmermann 625319251c5SThomas Zimmermann /* 626319251c5SThomas Zimmermann * Hardware settings 627319251c5SThomas Zimmermann */ 628319251c5SThomas Zimmermann 629319251c5SThomas Zimmermann ret = simpledrm_device_init_clocks(sdev); 630319251c5SThomas Zimmermann if (ret) 631319251c5SThomas Zimmermann return ERR_PTR(ret); 632319251c5SThomas Zimmermann ret = simpledrm_device_init_regulators(sdev); 633319251c5SThomas Zimmermann if (ret) 634319251c5SThomas Zimmermann return ERR_PTR(ret); 635319251c5SThomas Zimmermann ret = simpledrm_device_attach_genpd(sdev); 636319251c5SThomas Zimmermann if (ret) 637319251c5SThomas Zimmermann return ERR_PTR(ret); 638319251c5SThomas Zimmermann 639319251c5SThomas Zimmermann if (pd) { 640319251c5SThomas Zimmermann width = simplefb_get_width_pd(dev, pd); 641319251c5SThomas Zimmermann if (width < 0) 642319251c5SThomas Zimmermann return ERR_PTR(width); 643319251c5SThomas Zimmermann height = simplefb_get_height_pd(dev, pd); 644319251c5SThomas Zimmermann if (height < 0) 645319251c5SThomas Zimmermann return ERR_PTR(height); 646319251c5SThomas Zimmermann stride = simplefb_get_stride_pd(dev, pd); 647319251c5SThomas Zimmermann if (stride < 0) 648319251c5SThomas Zimmermann return ERR_PTR(stride); 649319251c5SThomas Zimmermann format = simplefb_get_format_pd(dev, pd); 650319251c5SThomas Zimmermann if (IS_ERR(format)) 651319251c5SThomas Zimmermann return ERR_CAST(format); 652319251c5SThomas Zimmermann } else if (of_node) { 653319251c5SThomas Zimmermann width = simplefb_get_width_of(dev, of_node); 654319251c5SThomas Zimmermann if (width < 0) 655319251c5SThomas Zimmermann return ERR_PTR(width); 656319251c5SThomas Zimmermann height = simplefb_get_height_of(dev, of_node); 657319251c5SThomas Zimmermann if (height < 0) 658319251c5SThomas Zimmermann return ERR_PTR(height); 659319251c5SThomas Zimmermann stride = simplefb_get_stride_of(dev, of_node); 660319251c5SThomas Zimmermann if (stride < 0) 661319251c5SThomas Zimmermann return ERR_PTR(stride); 662319251c5SThomas Zimmermann format = simplefb_get_format_of(dev, of_node); 663319251c5SThomas Zimmermann if (IS_ERR(format)) 664319251c5SThomas Zimmermann return ERR_CAST(format); 665319251c5SThomas Zimmermann mem = simplefb_get_memory_of(dev, of_node); 666319251c5SThomas Zimmermann if (IS_ERR(mem)) 667319251c5SThomas Zimmermann return ERR_CAST(mem); 668319251c5SThomas Zimmermann panel_node = of_parse_phandle(of_node, "panel", 0); 669319251c5SThomas Zimmermann if (panel_node) { 670319251c5SThomas Zimmermann simplefb_read_u32_of(dev, panel_node, "width-mm", &width_mm); 671319251c5SThomas Zimmermann simplefb_read_u32_of(dev, panel_node, "height-mm", &height_mm); 672319251c5SThomas Zimmermann of_node_put(panel_node); 673319251c5SThomas Zimmermann } 674319251c5SThomas Zimmermann } else { 675319251c5SThomas Zimmermann drm_err(dev, "no simplefb configuration found\n"); 676319251c5SThomas Zimmermann return ERR_PTR(-ENODEV); 677319251c5SThomas Zimmermann } 678319251c5SThomas Zimmermann if (!stride) { 679319251c5SThomas Zimmermann stride = drm_format_info_min_pitch(format, 0, width); 680319251c5SThomas Zimmermann if (drm_WARN_ON(dev, !stride)) 681319251c5SThomas Zimmermann return ERR_PTR(-EINVAL); 682319251c5SThomas Zimmermann } 683319251c5SThomas Zimmermann 684333376e9SThomas Zimmermann sysfb->fb_mode = drm_sysfb_mode(width, height, width_mm, height_mm); 685b5626f6fSThomas Zimmermann sysfb->fb_format = format; 686b5626f6fSThomas Zimmermann sysfb->fb_pitch = stride; 687319251c5SThomas Zimmermann 688b5626f6fSThomas Zimmermann drm_dbg(dev, "display mode={" DRM_MODE_FMT "}\n", DRM_MODE_ARG(&sysfb->fb_mode)); 689319251c5SThomas Zimmermann drm_dbg(dev, "framebuffer format=%p4cc, size=%dx%d, stride=%d byte\n", 690319251c5SThomas Zimmermann &format->format, width, height, stride); 691319251c5SThomas Zimmermann 692319251c5SThomas Zimmermann /* 693319251c5SThomas Zimmermann * Memory management 694319251c5SThomas Zimmermann */ 695319251c5SThomas Zimmermann 696319251c5SThomas Zimmermann if (mem) { 697319251c5SThomas Zimmermann void *screen_base; 698319251c5SThomas Zimmermann 699319251c5SThomas Zimmermann ret = devm_aperture_acquire_for_platform_device(pdev, mem->start, 700319251c5SThomas Zimmermann resource_size(mem)); 701319251c5SThomas Zimmermann if (ret) { 702319251c5SThomas Zimmermann drm_err(dev, "could not acquire memory range %pr: %d\n", mem, ret); 703319251c5SThomas Zimmermann return ERR_PTR(ret); 704319251c5SThomas Zimmermann } 705319251c5SThomas Zimmermann 706319251c5SThomas Zimmermann drm_dbg(dev, "using system memory framebuffer at %pr\n", mem); 707319251c5SThomas Zimmermann 708319251c5SThomas Zimmermann screen_base = devm_memremap(dev->dev, mem->start, resource_size(mem), MEMREMAP_WC); 709319251c5SThomas Zimmermann if (IS_ERR(screen_base)) 710319251c5SThomas Zimmermann return screen_base; 711319251c5SThomas Zimmermann 712b5626f6fSThomas Zimmermann iosys_map_set_vaddr(&sysfb->fb_addr, screen_base); 713319251c5SThomas Zimmermann } else { 714319251c5SThomas Zimmermann void __iomem *screen_base; 715319251c5SThomas Zimmermann 716319251c5SThomas Zimmermann res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 717319251c5SThomas Zimmermann if (!res) 718319251c5SThomas Zimmermann return ERR_PTR(-EINVAL); 719319251c5SThomas Zimmermann 720319251c5SThomas Zimmermann ret = devm_aperture_acquire_for_platform_device(pdev, res->start, 721319251c5SThomas Zimmermann resource_size(res)); 722319251c5SThomas Zimmermann if (ret) { 723319251c5SThomas Zimmermann drm_err(dev, "could not acquire memory range %pr: %d\n", res, ret); 724319251c5SThomas Zimmermann return ERR_PTR(ret); 725319251c5SThomas Zimmermann } 726319251c5SThomas Zimmermann 727319251c5SThomas Zimmermann drm_dbg(dev, "using I/O memory framebuffer at %pr\n", res); 728319251c5SThomas Zimmermann 729319251c5SThomas Zimmermann mem = devm_request_mem_region(&pdev->dev, res->start, resource_size(res), 730319251c5SThomas Zimmermann drv->name); 731319251c5SThomas Zimmermann if (!mem) { 732319251c5SThomas Zimmermann /* 733319251c5SThomas Zimmermann * We cannot make this fatal. Sometimes this comes from magic 734319251c5SThomas Zimmermann * spaces our resource handlers simply don't know about. Use 735319251c5SThomas Zimmermann * the I/O-memory resource as-is and try to map that instead. 736319251c5SThomas Zimmermann */ 737319251c5SThomas Zimmermann drm_warn(dev, "could not acquire memory region %pr\n", res); 738319251c5SThomas Zimmermann mem = res; 739319251c5SThomas Zimmermann } 740319251c5SThomas Zimmermann 741319251c5SThomas Zimmermann screen_base = devm_ioremap_wc(&pdev->dev, mem->start, resource_size(mem)); 742319251c5SThomas Zimmermann if (!screen_base) 743319251c5SThomas Zimmermann return ERR_PTR(-ENOMEM); 744319251c5SThomas Zimmermann 745b5626f6fSThomas Zimmermann iosys_map_set_vaddr_iomem(&sysfb->fb_addr, screen_base); 746319251c5SThomas Zimmermann } 747319251c5SThomas Zimmermann 748319251c5SThomas Zimmermann /* 749319251c5SThomas Zimmermann * Modesetting 750319251c5SThomas Zimmermann */ 751319251c5SThomas Zimmermann 752319251c5SThomas Zimmermann ret = drmm_mode_config_init(dev); 753319251c5SThomas Zimmermann if (ret) 754319251c5SThomas Zimmermann return ERR_PTR(ret); 755319251c5SThomas Zimmermann 756319251c5SThomas Zimmermann max_width = max_t(unsigned long, width, DRM_SHADOW_PLANE_MAX_WIDTH); 757319251c5SThomas Zimmermann max_height = max_t(unsigned long, height, DRM_SHADOW_PLANE_MAX_HEIGHT); 758319251c5SThomas Zimmermann 759319251c5SThomas Zimmermann dev->mode_config.min_width = width; 760319251c5SThomas Zimmermann dev->mode_config.max_width = max_width; 761319251c5SThomas Zimmermann dev->mode_config.min_height = height; 762319251c5SThomas Zimmermann dev->mode_config.max_height = max_height; 763319251c5SThomas Zimmermann dev->mode_config.preferred_depth = format->depth; 764319251c5SThomas Zimmermann dev->mode_config.funcs = &simpledrm_mode_config_funcs; 765319251c5SThomas Zimmermann 766319251c5SThomas Zimmermann /* Primary plane */ 767319251c5SThomas Zimmermann 768319251c5SThomas Zimmermann nformats = drm_fb_build_fourcc_list(dev, &format->format, 1, 769319251c5SThomas Zimmermann sdev->formats, ARRAY_SIZE(sdev->formats)); 770319251c5SThomas Zimmermann 771319251c5SThomas Zimmermann primary_plane = &sdev->primary_plane; 772319251c5SThomas Zimmermann ret = drm_universal_plane_init(dev, primary_plane, 0, &simpledrm_primary_plane_funcs, 773319251c5SThomas Zimmermann sdev->formats, nformats, 774319251c5SThomas Zimmermann simpledrm_primary_plane_format_modifiers, 775319251c5SThomas Zimmermann DRM_PLANE_TYPE_PRIMARY, NULL); 776319251c5SThomas Zimmermann if (ret) 777319251c5SThomas Zimmermann return ERR_PTR(ret); 778319251c5SThomas Zimmermann drm_plane_helper_add(primary_plane, &simpledrm_primary_plane_helper_funcs); 779319251c5SThomas Zimmermann drm_plane_enable_fb_damage_clips(primary_plane); 780319251c5SThomas Zimmermann 781319251c5SThomas Zimmermann /* CRTC */ 782319251c5SThomas Zimmermann 783319251c5SThomas Zimmermann crtc = &sdev->crtc; 784319251c5SThomas Zimmermann ret = drm_crtc_init_with_planes(dev, crtc, primary_plane, NULL, 785319251c5SThomas Zimmermann &simpledrm_crtc_funcs, NULL); 786319251c5SThomas Zimmermann if (ret) 787319251c5SThomas Zimmermann return ERR_PTR(ret); 788319251c5SThomas Zimmermann drm_crtc_helper_add(crtc, &simpledrm_crtc_helper_funcs); 789319251c5SThomas Zimmermann 790319251c5SThomas Zimmermann /* Encoder */ 791319251c5SThomas Zimmermann 792319251c5SThomas Zimmermann encoder = &sdev->encoder; 793319251c5SThomas Zimmermann ret = drm_encoder_init(dev, encoder, &simpledrm_encoder_funcs, 794319251c5SThomas Zimmermann DRM_MODE_ENCODER_NONE, NULL); 795319251c5SThomas Zimmermann if (ret) 796319251c5SThomas Zimmermann return ERR_PTR(ret); 797319251c5SThomas Zimmermann encoder->possible_crtcs = drm_crtc_mask(crtc); 798319251c5SThomas Zimmermann 799319251c5SThomas Zimmermann /* Connector */ 800319251c5SThomas Zimmermann 801319251c5SThomas Zimmermann connector = &sdev->connector; 802319251c5SThomas Zimmermann ret = drm_connector_init(dev, connector, &simpledrm_connector_funcs, 803319251c5SThomas Zimmermann DRM_MODE_CONNECTOR_Unknown); 804319251c5SThomas Zimmermann if (ret) 805319251c5SThomas Zimmermann return ERR_PTR(ret); 806319251c5SThomas Zimmermann drm_connector_helper_add(connector, &simpledrm_connector_helper_funcs); 807319251c5SThomas Zimmermann drm_connector_set_panel_orientation_with_quirk(connector, 808319251c5SThomas Zimmermann DRM_MODE_PANEL_ORIENTATION_UNKNOWN, 809319251c5SThomas Zimmermann width, height); 810319251c5SThomas Zimmermann 811319251c5SThomas Zimmermann ret = drm_connector_attach_encoder(connector, encoder); 812319251c5SThomas Zimmermann if (ret) 813319251c5SThomas Zimmermann return ERR_PTR(ret); 814319251c5SThomas Zimmermann 815319251c5SThomas Zimmermann drm_mode_config_reset(dev); 816319251c5SThomas Zimmermann 817319251c5SThomas Zimmermann return sdev; 818319251c5SThomas Zimmermann } 819319251c5SThomas Zimmermann 820319251c5SThomas Zimmermann /* 821319251c5SThomas Zimmermann * DRM driver 822319251c5SThomas Zimmermann */ 823319251c5SThomas Zimmermann 824319251c5SThomas Zimmermann DEFINE_DRM_GEM_FOPS(simpledrm_fops); 825319251c5SThomas Zimmermann 826319251c5SThomas Zimmermann static struct drm_driver simpledrm_driver = { 827319251c5SThomas Zimmermann DRM_GEM_SHMEM_DRIVER_OPS, 828319251c5SThomas Zimmermann DRM_FBDEV_SHMEM_DRIVER_OPS, 829319251c5SThomas Zimmermann .name = DRIVER_NAME, 830319251c5SThomas Zimmermann .desc = DRIVER_DESC, 831319251c5SThomas Zimmermann .major = DRIVER_MAJOR, 832319251c5SThomas Zimmermann .minor = DRIVER_MINOR, 833319251c5SThomas Zimmermann .driver_features = DRIVER_ATOMIC | DRIVER_GEM | DRIVER_MODESET, 834319251c5SThomas Zimmermann .fops = &simpledrm_fops, 835319251c5SThomas Zimmermann }; 836319251c5SThomas Zimmermann 837319251c5SThomas Zimmermann /* 838319251c5SThomas Zimmermann * Platform driver 839319251c5SThomas Zimmermann */ 840319251c5SThomas Zimmermann 841319251c5SThomas Zimmermann static int simpledrm_probe(struct platform_device *pdev) 842319251c5SThomas Zimmermann { 843319251c5SThomas Zimmermann struct simpledrm_device *sdev; 844b5626f6fSThomas Zimmermann struct drm_sysfb_device *sysfb; 845319251c5SThomas Zimmermann struct drm_device *dev; 846319251c5SThomas Zimmermann int ret; 847319251c5SThomas Zimmermann 848319251c5SThomas Zimmermann sdev = simpledrm_device_create(&simpledrm_driver, pdev); 849319251c5SThomas Zimmermann if (IS_ERR(sdev)) 850319251c5SThomas Zimmermann return PTR_ERR(sdev); 851b5626f6fSThomas Zimmermann sysfb = &sdev->sysfb; 852b5626f6fSThomas Zimmermann dev = &sysfb->dev; 853319251c5SThomas Zimmermann 854319251c5SThomas Zimmermann ret = drm_dev_register(dev, 0); 855319251c5SThomas Zimmermann if (ret) 856319251c5SThomas Zimmermann return ret; 857319251c5SThomas Zimmermann 858b5626f6fSThomas Zimmermann drm_client_setup(dev, sdev->sysfb.fb_format); 859319251c5SThomas Zimmermann 860319251c5SThomas Zimmermann return 0; 861319251c5SThomas Zimmermann } 862319251c5SThomas Zimmermann 863319251c5SThomas Zimmermann static void simpledrm_remove(struct platform_device *pdev) 864319251c5SThomas Zimmermann { 865319251c5SThomas Zimmermann struct simpledrm_device *sdev = platform_get_drvdata(pdev); 866b5626f6fSThomas Zimmermann struct drm_device *dev = &sdev->sysfb.dev; 867319251c5SThomas Zimmermann 868319251c5SThomas Zimmermann drm_dev_unplug(dev); 869319251c5SThomas Zimmermann } 870319251c5SThomas Zimmermann 871319251c5SThomas Zimmermann static const struct of_device_id simpledrm_of_match_table[] = { 872319251c5SThomas Zimmermann { .compatible = "simple-framebuffer", }, 873319251c5SThomas Zimmermann { }, 874319251c5SThomas Zimmermann }; 875319251c5SThomas Zimmermann MODULE_DEVICE_TABLE(of, simpledrm_of_match_table); 876319251c5SThomas Zimmermann 877319251c5SThomas Zimmermann static struct platform_driver simpledrm_platform_driver = { 878319251c5SThomas Zimmermann .driver = { 879319251c5SThomas Zimmermann .name = "simple-framebuffer", /* connect to sysfb */ 880319251c5SThomas Zimmermann .of_match_table = simpledrm_of_match_table, 881319251c5SThomas Zimmermann }, 882319251c5SThomas Zimmermann .probe = simpledrm_probe, 883319251c5SThomas Zimmermann .remove = simpledrm_remove, 884319251c5SThomas Zimmermann }; 885319251c5SThomas Zimmermann 886319251c5SThomas Zimmermann module_platform_driver(simpledrm_platform_driver); 887319251c5SThomas Zimmermann 888319251c5SThomas Zimmermann MODULE_DESCRIPTION(DRIVER_DESC); 889319251c5SThomas Zimmermann MODULE_LICENSE("GPL v2"); 890