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_panic.h> 29319251c5SThomas Zimmermann #include <drm/drm_probe_helper.h> 30319251c5SThomas Zimmermann 31b5626f6fSThomas Zimmermann #include "drm_sysfb_helper.h" 32b5626f6fSThomas Zimmermann 33319251c5SThomas Zimmermann #define DRIVER_NAME "simpledrm" 34319251c5SThomas Zimmermann #define DRIVER_DESC "DRM driver for simple-framebuffer platform devices" 35319251c5SThomas Zimmermann #define DRIVER_MAJOR 1 36319251c5SThomas Zimmermann #define DRIVER_MINOR 0 37319251c5SThomas Zimmermann 38319251c5SThomas Zimmermann /* 39319251c5SThomas Zimmermann * Helpers for simplefb 40319251c5SThomas Zimmermann */ 41319251c5SThomas Zimmermann 42319251c5SThomas Zimmermann static int 43319251c5SThomas Zimmermann simplefb_get_validated_int(struct drm_device *dev, const char *name, 44319251c5SThomas Zimmermann uint32_t value) 45319251c5SThomas Zimmermann { 46319251c5SThomas Zimmermann if (value > INT_MAX) { 47319251c5SThomas Zimmermann drm_err(dev, "simplefb: invalid framebuffer %s of %u\n", 48319251c5SThomas Zimmermann name, value); 49319251c5SThomas Zimmermann return -EINVAL; 50319251c5SThomas Zimmermann } 51319251c5SThomas Zimmermann return (int)value; 52319251c5SThomas Zimmermann } 53319251c5SThomas Zimmermann 54319251c5SThomas Zimmermann static int 55319251c5SThomas Zimmermann simplefb_get_validated_int0(struct drm_device *dev, const char *name, 56319251c5SThomas Zimmermann uint32_t value) 57319251c5SThomas Zimmermann { 58319251c5SThomas Zimmermann if (!value) { 59319251c5SThomas Zimmermann drm_err(dev, "simplefb: invalid framebuffer %s of %u\n", 60319251c5SThomas Zimmermann name, value); 61319251c5SThomas Zimmermann return -EINVAL; 62319251c5SThomas Zimmermann } 63319251c5SThomas Zimmermann return simplefb_get_validated_int(dev, name, value); 64319251c5SThomas Zimmermann } 65319251c5SThomas Zimmermann 66319251c5SThomas Zimmermann static const struct drm_format_info * 67319251c5SThomas Zimmermann simplefb_get_validated_format(struct drm_device *dev, const char *format_name) 68319251c5SThomas Zimmermann { 69319251c5SThomas Zimmermann static const struct simplefb_format formats[] = SIMPLEFB_FORMATS; 70319251c5SThomas Zimmermann const struct simplefb_format *fmt = formats; 71319251c5SThomas Zimmermann const struct simplefb_format *end = fmt + ARRAY_SIZE(formats); 72319251c5SThomas Zimmermann const struct drm_format_info *info; 73319251c5SThomas Zimmermann 74319251c5SThomas Zimmermann if (!format_name) { 75319251c5SThomas Zimmermann drm_err(dev, "simplefb: missing framebuffer format\n"); 76319251c5SThomas Zimmermann return ERR_PTR(-EINVAL); 77319251c5SThomas Zimmermann } 78319251c5SThomas Zimmermann 79319251c5SThomas Zimmermann while (fmt < end) { 80319251c5SThomas Zimmermann if (!strcmp(format_name, fmt->name)) { 81319251c5SThomas Zimmermann info = drm_format_info(fmt->fourcc); 82319251c5SThomas Zimmermann if (!info) 83319251c5SThomas Zimmermann return ERR_PTR(-EINVAL); 84319251c5SThomas Zimmermann return info; 85319251c5SThomas Zimmermann } 86319251c5SThomas Zimmermann ++fmt; 87319251c5SThomas Zimmermann } 88319251c5SThomas Zimmermann 89319251c5SThomas Zimmermann drm_err(dev, "simplefb: unknown framebuffer format %s\n", 90319251c5SThomas Zimmermann format_name); 91319251c5SThomas Zimmermann 92319251c5SThomas Zimmermann return ERR_PTR(-EINVAL); 93319251c5SThomas Zimmermann } 94319251c5SThomas Zimmermann 95319251c5SThomas Zimmermann static int 96319251c5SThomas Zimmermann simplefb_get_width_pd(struct drm_device *dev, 97319251c5SThomas Zimmermann const struct simplefb_platform_data *pd) 98319251c5SThomas Zimmermann { 99319251c5SThomas Zimmermann return simplefb_get_validated_int0(dev, "width", pd->width); 100319251c5SThomas Zimmermann } 101319251c5SThomas Zimmermann 102319251c5SThomas Zimmermann static int 103319251c5SThomas Zimmermann simplefb_get_height_pd(struct drm_device *dev, 104319251c5SThomas Zimmermann const struct simplefb_platform_data *pd) 105319251c5SThomas Zimmermann { 106319251c5SThomas Zimmermann return simplefb_get_validated_int0(dev, "height", pd->height); 107319251c5SThomas Zimmermann } 108319251c5SThomas Zimmermann 109319251c5SThomas Zimmermann static int 110319251c5SThomas Zimmermann simplefb_get_stride_pd(struct drm_device *dev, 111319251c5SThomas Zimmermann const struct simplefb_platform_data *pd) 112319251c5SThomas Zimmermann { 113319251c5SThomas Zimmermann return simplefb_get_validated_int(dev, "stride", pd->stride); 114319251c5SThomas Zimmermann } 115319251c5SThomas Zimmermann 116319251c5SThomas Zimmermann static const struct drm_format_info * 117319251c5SThomas Zimmermann simplefb_get_format_pd(struct drm_device *dev, 118319251c5SThomas Zimmermann const struct simplefb_platform_data *pd) 119319251c5SThomas Zimmermann { 120319251c5SThomas Zimmermann return simplefb_get_validated_format(dev, pd->format); 121319251c5SThomas Zimmermann } 122319251c5SThomas Zimmermann 123319251c5SThomas Zimmermann static int 124319251c5SThomas Zimmermann simplefb_read_u32_of(struct drm_device *dev, struct device_node *of_node, 125319251c5SThomas Zimmermann const char *name, u32 *value) 126319251c5SThomas Zimmermann { 127319251c5SThomas Zimmermann int ret = of_property_read_u32(of_node, name, value); 128319251c5SThomas Zimmermann 129319251c5SThomas Zimmermann if (ret) 130319251c5SThomas Zimmermann drm_err(dev, "simplefb: cannot parse framebuffer %s: error %d\n", 131319251c5SThomas Zimmermann name, ret); 132319251c5SThomas Zimmermann return ret; 133319251c5SThomas Zimmermann } 134319251c5SThomas Zimmermann 135319251c5SThomas Zimmermann static int 136319251c5SThomas Zimmermann simplefb_read_string_of(struct drm_device *dev, struct device_node *of_node, 137319251c5SThomas Zimmermann const char *name, const char **value) 138319251c5SThomas Zimmermann { 139319251c5SThomas Zimmermann int ret = of_property_read_string(of_node, name, value); 140319251c5SThomas Zimmermann 141319251c5SThomas Zimmermann if (ret) 142319251c5SThomas Zimmermann drm_err(dev, "simplefb: cannot parse framebuffer %s: error %d\n", 143319251c5SThomas Zimmermann name, ret); 144319251c5SThomas Zimmermann return ret; 145319251c5SThomas Zimmermann } 146319251c5SThomas Zimmermann 147319251c5SThomas Zimmermann static int 148319251c5SThomas Zimmermann simplefb_get_width_of(struct drm_device *dev, struct device_node *of_node) 149319251c5SThomas Zimmermann { 150319251c5SThomas Zimmermann u32 width; 151319251c5SThomas Zimmermann int ret = simplefb_read_u32_of(dev, of_node, "width", &width); 152319251c5SThomas Zimmermann 153319251c5SThomas Zimmermann if (ret) 154319251c5SThomas Zimmermann return ret; 155319251c5SThomas Zimmermann return simplefb_get_validated_int0(dev, "width", width); 156319251c5SThomas Zimmermann } 157319251c5SThomas Zimmermann 158319251c5SThomas Zimmermann static int 159319251c5SThomas Zimmermann simplefb_get_height_of(struct drm_device *dev, struct device_node *of_node) 160319251c5SThomas Zimmermann { 161319251c5SThomas Zimmermann u32 height; 162319251c5SThomas Zimmermann int ret = simplefb_read_u32_of(dev, of_node, "height", &height); 163319251c5SThomas Zimmermann 164319251c5SThomas Zimmermann if (ret) 165319251c5SThomas Zimmermann return ret; 166319251c5SThomas Zimmermann return simplefb_get_validated_int0(dev, "height", height); 167319251c5SThomas Zimmermann } 168319251c5SThomas Zimmermann 169319251c5SThomas Zimmermann static int 170319251c5SThomas Zimmermann simplefb_get_stride_of(struct drm_device *dev, struct device_node *of_node) 171319251c5SThomas Zimmermann { 172319251c5SThomas Zimmermann u32 stride; 173319251c5SThomas Zimmermann int ret = simplefb_read_u32_of(dev, of_node, "stride", &stride); 174319251c5SThomas Zimmermann 175319251c5SThomas Zimmermann if (ret) 176319251c5SThomas Zimmermann return ret; 177319251c5SThomas Zimmermann return simplefb_get_validated_int(dev, "stride", stride); 178319251c5SThomas Zimmermann } 179319251c5SThomas Zimmermann 180319251c5SThomas Zimmermann static const struct drm_format_info * 181319251c5SThomas Zimmermann simplefb_get_format_of(struct drm_device *dev, struct device_node *of_node) 182319251c5SThomas Zimmermann { 183319251c5SThomas Zimmermann const char *format; 184319251c5SThomas Zimmermann int ret = simplefb_read_string_of(dev, of_node, "format", &format); 185319251c5SThomas Zimmermann 186319251c5SThomas Zimmermann if (ret) 187319251c5SThomas Zimmermann return ERR_PTR(ret); 188319251c5SThomas Zimmermann return simplefb_get_validated_format(dev, format); 189319251c5SThomas Zimmermann } 190319251c5SThomas Zimmermann 191319251c5SThomas Zimmermann static struct resource * 192319251c5SThomas Zimmermann simplefb_get_memory_of(struct drm_device *dev, struct device_node *of_node) 193319251c5SThomas Zimmermann { 194319251c5SThomas Zimmermann struct device_node *np; 195319251c5SThomas Zimmermann struct resource *res; 196319251c5SThomas Zimmermann int err; 197319251c5SThomas Zimmermann 198319251c5SThomas Zimmermann np = of_parse_phandle(of_node, "memory-region", 0); 199319251c5SThomas Zimmermann if (!np) 200319251c5SThomas Zimmermann return NULL; 201319251c5SThomas Zimmermann 202319251c5SThomas Zimmermann res = devm_kzalloc(dev->dev, sizeof(*res), GFP_KERNEL); 203319251c5SThomas Zimmermann if (!res) 204319251c5SThomas Zimmermann return ERR_PTR(-ENOMEM); 205319251c5SThomas Zimmermann 206319251c5SThomas Zimmermann err = of_address_to_resource(np, 0, res); 207319251c5SThomas Zimmermann if (err) 208319251c5SThomas Zimmermann return ERR_PTR(err); 209319251c5SThomas Zimmermann 210319251c5SThomas Zimmermann if (of_property_present(of_node, "reg")) 211319251c5SThomas Zimmermann drm_warn(dev, "preferring \"memory-region\" over \"reg\" property\n"); 212319251c5SThomas Zimmermann 213319251c5SThomas Zimmermann return res; 214319251c5SThomas Zimmermann } 215319251c5SThomas Zimmermann 216319251c5SThomas Zimmermann /* 217319251c5SThomas Zimmermann * Simple Framebuffer device 218319251c5SThomas Zimmermann */ 219319251c5SThomas Zimmermann 220319251c5SThomas Zimmermann struct simpledrm_device { 221b5626f6fSThomas Zimmermann struct drm_sysfb_device sysfb; 222319251c5SThomas Zimmermann 223319251c5SThomas Zimmermann /* clocks */ 224319251c5SThomas Zimmermann #if defined CONFIG_OF && defined CONFIG_COMMON_CLK 225319251c5SThomas Zimmermann unsigned int clk_count; 226319251c5SThomas Zimmermann struct clk **clks; 227319251c5SThomas Zimmermann #endif 228319251c5SThomas Zimmermann /* regulators */ 229319251c5SThomas Zimmermann #if defined CONFIG_OF && defined CONFIG_REGULATOR 230319251c5SThomas Zimmermann unsigned int regulator_count; 231319251c5SThomas Zimmermann struct regulator **regulators; 232319251c5SThomas Zimmermann #endif 233319251c5SThomas Zimmermann /* power-domains */ 234319251c5SThomas Zimmermann #if defined CONFIG_OF && defined CONFIG_PM_GENERIC_DOMAINS 235319251c5SThomas Zimmermann int pwr_dom_count; 236319251c5SThomas Zimmermann struct device **pwr_dom_devs; 237319251c5SThomas Zimmermann struct device_link **pwr_dom_links; 238319251c5SThomas Zimmermann #endif 239319251c5SThomas Zimmermann 240319251c5SThomas Zimmermann /* modesetting */ 241319251c5SThomas Zimmermann uint32_t formats[8]; 242319251c5SThomas Zimmermann struct drm_plane primary_plane; 243319251c5SThomas Zimmermann struct drm_crtc crtc; 244319251c5SThomas Zimmermann struct drm_encoder encoder; 245319251c5SThomas Zimmermann struct drm_connector connector; 246319251c5SThomas Zimmermann }; 247319251c5SThomas Zimmermann 248319251c5SThomas Zimmermann static struct simpledrm_device *simpledrm_device_of_dev(struct drm_device *dev) 249319251c5SThomas Zimmermann { 250b5626f6fSThomas Zimmermann return container_of(to_drm_sysfb_device(dev), struct simpledrm_device, sysfb); 251319251c5SThomas Zimmermann } 252319251c5SThomas Zimmermann 253319251c5SThomas Zimmermann /* 254319251c5SThomas Zimmermann * Hardware 255319251c5SThomas Zimmermann */ 256319251c5SThomas Zimmermann 257319251c5SThomas Zimmermann #if defined CONFIG_OF && defined CONFIG_COMMON_CLK 258319251c5SThomas Zimmermann /* 259319251c5SThomas Zimmermann * Clock handling code. 260319251c5SThomas Zimmermann * 261319251c5SThomas Zimmermann * Here we handle the clocks property of our "simple-framebuffer" dt node. 262319251c5SThomas Zimmermann * This is necessary so that we can make sure that any clocks needed by 263319251c5SThomas Zimmermann * the display engine that the bootloader set up for us (and for which it 264319251c5SThomas Zimmermann * provided a simplefb dt node), stay up, for the life of the simplefb 265319251c5SThomas Zimmermann * driver. 266319251c5SThomas Zimmermann * 267319251c5SThomas Zimmermann * When the driver unloads, we cleanly disable, and then release the clocks. 268319251c5SThomas Zimmermann * 269319251c5SThomas Zimmermann * We only complain about errors here, no action is taken as the most likely 270319251c5SThomas Zimmermann * error can only happen due to a mismatch between the bootloader which set 271319251c5SThomas Zimmermann * up simplefb, and the clock definitions in the device tree. Chances are 272319251c5SThomas Zimmermann * that there are no adverse effects, and if there are, a clean teardown of 273319251c5SThomas Zimmermann * the fb probe will not help us much either. So just complain and carry on, 274319251c5SThomas Zimmermann * and hope that the user actually gets a working fb at the end of things. 275319251c5SThomas Zimmermann */ 276319251c5SThomas Zimmermann 277319251c5SThomas Zimmermann static void simpledrm_device_release_clocks(void *res) 278319251c5SThomas Zimmermann { 279319251c5SThomas Zimmermann struct simpledrm_device *sdev = simpledrm_device_of_dev(res); 280319251c5SThomas Zimmermann unsigned int i; 281319251c5SThomas Zimmermann 282319251c5SThomas Zimmermann for (i = 0; i < sdev->clk_count; ++i) { 283319251c5SThomas Zimmermann if (sdev->clks[i]) { 284319251c5SThomas Zimmermann clk_disable_unprepare(sdev->clks[i]); 285319251c5SThomas Zimmermann clk_put(sdev->clks[i]); 286319251c5SThomas Zimmermann } 287319251c5SThomas Zimmermann } 288319251c5SThomas Zimmermann } 289319251c5SThomas Zimmermann 290319251c5SThomas Zimmermann static int simpledrm_device_init_clocks(struct simpledrm_device *sdev) 291319251c5SThomas Zimmermann { 292b5626f6fSThomas Zimmermann struct drm_device *dev = &sdev->sysfb.dev; 293319251c5SThomas Zimmermann struct platform_device *pdev = to_platform_device(dev->dev); 294319251c5SThomas Zimmermann struct device_node *of_node = pdev->dev.of_node; 295319251c5SThomas Zimmermann struct clk *clock; 296319251c5SThomas Zimmermann unsigned int i; 297319251c5SThomas Zimmermann int ret; 298319251c5SThomas Zimmermann 299319251c5SThomas Zimmermann if (dev_get_platdata(&pdev->dev) || !of_node) 300319251c5SThomas Zimmermann return 0; 301319251c5SThomas Zimmermann 302319251c5SThomas Zimmermann sdev->clk_count = of_clk_get_parent_count(of_node); 303319251c5SThomas Zimmermann if (!sdev->clk_count) 304319251c5SThomas Zimmermann return 0; 305319251c5SThomas Zimmermann 306319251c5SThomas Zimmermann sdev->clks = drmm_kzalloc(dev, sdev->clk_count * sizeof(sdev->clks[0]), 307319251c5SThomas Zimmermann GFP_KERNEL); 308319251c5SThomas Zimmermann if (!sdev->clks) 309319251c5SThomas Zimmermann return -ENOMEM; 310319251c5SThomas Zimmermann 311319251c5SThomas Zimmermann for (i = 0; i < sdev->clk_count; ++i) { 312319251c5SThomas Zimmermann clock = of_clk_get(of_node, i); 313319251c5SThomas Zimmermann if (IS_ERR(clock)) { 314319251c5SThomas Zimmermann ret = PTR_ERR(clock); 315319251c5SThomas Zimmermann if (ret == -EPROBE_DEFER) 316319251c5SThomas Zimmermann goto err; 317319251c5SThomas Zimmermann drm_err(dev, "clock %u not found: %d\n", i, ret); 318319251c5SThomas Zimmermann continue; 319319251c5SThomas Zimmermann } 320319251c5SThomas Zimmermann ret = clk_prepare_enable(clock); 321319251c5SThomas Zimmermann if (ret) { 322319251c5SThomas Zimmermann drm_err(dev, "failed to enable clock %u: %d\n", 323319251c5SThomas Zimmermann i, ret); 324319251c5SThomas Zimmermann clk_put(clock); 325319251c5SThomas Zimmermann continue; 326319251c5SThomas Zimmermann } 327319251c5SThomas Zimmermann sdev->clks[i] = clock; 328319251c5SThomas Zimmermann } 329319251c5SThomas Zimmermann 330319251c5SThomas Zimmermann return devm_add_action_or_reset(&pdev->dev, 331319251c5SThomas Zimmermann simpledrm_device_release_clocks, 332319251c5SThomas Zimmermann sdev); 333319251c5SThomas Zimmermann 334319251c5SThomas Zimmermann err: 335319251c5SThomas Zimmermann while (i) { 336319251c5SThomas Zimmermann --i; 337319251c5SThomas Zimmermann if (sdev->clks[i]) { 338319251c5SThomas Zimmermann clk_disable_unprepare(sdev->clks[i]); 339319251c5SThomas Zimmermann clk_put(sdev->clks[i]); 340319251c5SThomas Zimmermann } 341319251c5SThomas Zimmermann } 342319251c5SThomas Zimmermann return ret; 343319251c5SThomas Zimmermann } 344319251c5SThomas Zimmermann #else 345319251c5SThomas Zimmermann static int simpledrm_device_init_clocks(struct simpledrm_device *sdev) 346319251c5SThomas Zimmermann { 347319251c5SThomas Zimmermann return 0; 348319251c5SThomas Zimmermann } 349319251c5SThomas Zimmermann #endif 350319251c5SThomas Zimmermann 351319251c5SThomas Zimmermann #if defined CONFIG_OF && defined CONFIG_REGULATOR 352319251c5SThomas Zimmermann 353319251c5SThomas Zimmermann #define SUPPLY_SUFFIX "-supply" 354319251c5SThomas Zimmermann 355319251c5SThomas Zimmermann /* 356319251c5SThomas Zimmermann * Regulator handling code. 357319251c5SThomas Zimmermann * 358319251c5SThomas Zimmermann * Here we handle the num-supplies and vin*-supply properties of our 359319251c5SThomas Zimmermann * "simple-framebuffer" dt node. This is necessary so that we can make sure 360319251c5SThomas Zimmermann * that any regulators needed by the display hardware that the bootloader 361319251c5SThomas Zimmermann * set up for us (and for which it provided a simplefb dt node), stay up, 362319251c5SThomas Zimmermann * for the life of the simplefb driver. 363319251c5SThomas Zimmermann * 364319251c5SThomas Zimmermann * When the driver unloads, we cleanly disable, and then release the 365319251c5SThomas Zimmermann * regulators. 366319251c5SThomas Zimmermann * 367319251c5SThomas Zimmermann * We only complain about errors here, no action is taken as the most likely 368319251c5SThomas Zimmermann * error can only happen due to a mismatch between the bootloader which set 369319251c5SThomas Zimmermann * up simplefb, and the regulator definitions in the device tree. Chances are 370319251c5SThomas Zimmermann * that there are no adverse effects, and if there are, a clean teardown of 371319251c5SThomas Zimmermann * the fb probe will not help us much either. So just complain and carry on, 372319251c5SThomas Zimmermann * and hope that the user actually gets a working fb at the end of things. 373319251c5SThomas Zimmermann */ 374319251c5SThomas Zimmermann 375319251c5SThomas Zimmermann static void simpledrm_device_release_regulators(void *res) 376319251c5SThomas Zimmermann { 377319251c5SThomas Zimmermann struct simpledrm_device *sdev = simpledrm_device_of_dev(res); 378319251c5SThomas Zimmermann unsigned int i; 379319251c5SThomas Zimmermann 380319251c5SThomas Zimmermann for (i = 0; i < sdev->regulator_count; ++i) { 381319251c5SThomas Zimmermann if (sdev->regulators[i]) { 382319251c5SThomas Zimmermann regulator_disable(sdev->regulators[i]); 383319251c5SThomas Zimmermann regulator_put(sdev->regulators[i]); 384319251c5SThomas Zimmermann } 385319251c5SThomas Zimmermann } 386319251c5SThomas Zimmermann } 387319251c5SThomas Zimmermann 388319251c5SThomas Zimmermann static int simpledrm_device_init_regulators(struct simpledrm_device *sdev) 389319251c5SThomas Zimmermann { 390b5626f6fSThomas Zimmermann struct drm_device *dev = &sdev->sysfb.dev; 391319251c5SThomas Zimmermann struct platform_device *pdev = to_platform_device(dev->dev); 392319251c5SThomas Zimmermann struct device_node *of_node = pdev->dev.of_node; 393319251c5SThomas Zimmermann struct property *prop; 394319251c5SThomas Zimmermann struct regulator *regulator; 395319251c5SThomas Zimmermann const char *p; 396319251c5SThomas Zimmermann unsigned int count = 0, i = 0; 397319251c5SThomas Zimmermann int ret; 398319251c5SThomas Zimmermann 399319251c5SThomas Zimmermann if (dev_get_platdata(&pdev->dev) || !of_node) 400319251c5SThomas Zimmermann return 0; 401319251c5SThomas Zimmermann 402319251c5SThomas Zimmermann /* Count the number of regulator supplies */ 403319251c5SThomas Zimmermann for_each_property_of_node(of_node, prop) { 404319251c5SThomas Zimmermann p = strstr(prop->name, SUPPLY_SUFFIX); 405319251c5SThomas Zimmermann if (p && p != prop->name) 406319251c5SThomas Zimmermann ++count; 407319251c5SThomas Zimmermann } 408319251c5SThomas Zimmermann 409319251c5SThomas Zimmermann if (!count) 410319251c5SThomas Zimmermann return 0; 411319251c5SThomas Zimmermann 412319251c5SThomas Zimmermann sdev->regulators = drmm_kzalloc(dev, 413319251c5SThomas Zimmermann count * sizeof(sdev->regulators[0]), 414319251c5SThomas Zimmermann GFP_KERNEL); 415319251c5SThomas Zimmermann if (!sdev->regulators) 416319251c5SThomas Zimmermann return -ENOMEM; 417319251c5SThomas Zimmermann 418319251c5SThomas Zimmermann for_each_property_of_node(of_node, prop) { 419319251c5SThomas Zimmermann char name[32]; /* 32 is max size of property name */ 420319251c5SThomas Zimmermann size_t len; 421319251c5SThomas Zimmermann 422319251c5SThomas Zimmermann p = strstr(prop->name, SUPPLY_SUFFIX); 423319251c5SThomas Zimmermann if (!p || p == prop->name) 424319251c5SThomas Zimmermann continue; 425319251c5SThomas Zimmermann len = strlen(prop->name) - strlen(SUPPLY_SUFFIX) + 1; 426319251c5SThomas Zimmermann strscpy(name, prop->name, min(sizeof(name), len)); 427319251c5SThomas Zimmermann 428319251c5SThomas Zimmermann regulator = regulator_get_optional(&pdev->dev, name); 429319251c5SThomas Zimmermann if (IS_ERR(regulator)) { 430319251c5SThomas Zimmermann ret = PTR_ERR(regulator); 431319251c5SThomas Zimmermann if (ret == -EPROBE_DEFER) 432319251c5SThomas Zimmermann goto err; 433319251c5SThomas Zimmermann drm_err(dev, "regulator %s not found: %d\n", 434319251c5SThomas Zimmermann name, ret); 435319251c5SThomas Zimmermann continue; 436319251c5SThomas Zimmermann } 437319251c5SThomas Zimmermann 438319251c5SThomas Zimmermann ret = regulator_enable(regulator); 439319251c5SThomas Zimmermann if (ret) { 440319251c5SThomas Zimmermann drm_err(dev, "failed to enable regulator %u: %d\n", 441319251c5SThomas Zimmermann i, ret); 442319251c5SThomas Zimmermann regulator_put(regulator); 443319251c5SThomas Zimmermann continue; 444319251c5SThomas Zimmermann } 445319251c5SThomas Zimmermann 446319251c5SThomas Zimmermann sdev->regulators[i++] = regulator; 447319251c5SThomas Zimmermann } 448319251c5SThomas Zimmermann sdev->regulator_count = i; 449319251c5SThomas Zimmermann 450319251c5SThomas Zimmermann return devm_add_action_or_reset(&pdev->dev, 451319251c5SThomas Zimmermann simpledrm_device_release_regulators, 452319251c5SThomas Zimmermann sdev); 453319251c5SThomas Zimmermann 454319251c5SThomas Zimmermann err: 455319251c5SThomas Zimmermann while (i) { 456319251c5SThomas Zimmermann --i; 457319251c5SThomas Zimmermann if (sdev->regulators[i]) { 458319251c5SThomas Zimmermann regulator_disable(sdev->regulators[i]); 459319251c5SThomas Zimmermann regulator_put(sdev->regulators[i]); 460319251c5SThomas Zimmermann } 461319251c5SThomas Zimmermann } 462319251c5SThomas Zimmermann return ret; 463319251c5SThomas Zimmermann } 464319251c5SThomas Zimmermann #else 465319251c5SThomas Zimmermann static int simpledrm_device_init_regulators(struct simpledrm_device *sdev) 466319251c5SThomas Zimmermann { 467319251c5SThomas Zimmermann return 0; 468319251c5SThomas Zimmermann } 469319251c5SThomas Zimmermann #endif 470319251c5SThomas Zimmermann 471319251c5SThomas Zimmermann #if defined CONFIG_OF && defined CONFIG_PM_GENERIC_DOMAINS 472319251c5SThomas Zimmermann /* 473319251c5SThomas Zimmermann * Generic power domain handling code. 474319251c5SThomas Zimmermann * 475319251c5SThomas Zimmermann * Here we handle the power-domains properties of our "simple-framebuffer" 476319251c5SThomas Zimmermann * dt node. This is only necessary if there is more than one power-domain. 477319251c5SThomas Zimmermann * A single power-domains is handled automatically by the driver core. Multiple 478319251c5SThomas Zimmermann * power-domains have to be handled by drivers since the driver core can't know 479319251c5SThomas Zimmermann * the correct power sequencing. Power sequencing is not an issue for simpledrm 480319251c5SThomas Zimmermann * since the bootloader has put the power domains already in the correct state. 481319251c5SThomas Zimmermann * simpledrm has only to ensure they remain active for its lifetime. 482319251c5SThomas Zimmermann * 483319251c5SThomas Zimmermann * When the driver unloads, we detach from the power-domains. 484319251c5SThomas Zimmermann * 485319251c5SThomas Zimmermann * We only complain about errors here, no action is taken as the most likely 486319251c5SThomas Zimmermann * error can only happen due to a mismatch between the bootloader which set 487319251c5SThomas Zimmermann * up the "simple-framebuffer" dt node, and the PM domain providers in the 488319251c5SThomas Zimmermann * device tree. Chances are that there are no adverse effects, and if there are, 489319251c5SThomas Zimmermann * a clean teardown of the fb probe will not help us much either. So just 490319251c5SThomas Zimmermann * complain and carry on, and hope that the user actually gets a working fb at 491319251c5SThomas Zimmermann * the end of things. 492319251c5SThomas Zimmermann */ 493319251c5SThomas Zimmermann static void simpledrm_device_detach_genpd(void *res) 494319251c5SThomas Zimmermann { 495319251c5SThomas Zimmermann int i; 496319251c5SThomas Zimmermann struct simpledrm_device *sdev = res; 497319251c5SThomas Zimmermann 498319251c5SThomas Zimmermann if (sdev->pwr_dom_count <= 1) 499319251c5SThomas Zimmermann return; 500319251c5SThomas Zimmermann 501319251c5SThomas Zimmermann for (i = sdev->pwr_dom_count - 1; i >= 0; i--) { 502319251c5SThomas Zimmermann if (sdev->pwr_dom_links[i]) 503319251c5SThomas Zimmermann device_link_del(sdev->pwr_dom_links[i]); 504319251c5SThomas Zimmermann if (!IS_ERR_OR_NULL(sdev->pwr_dom_devs[i])) 505319251c5SThomas Zimmermann dev_pm_domain_detach(sdev->pwr_dom_devs[i], true); 506319251c5SThomas Zimmermann } 507319251c5SThomas Zimmermann } 508319251c5SThomas Zimmermann 509319251c5SThomas Zimmermann static int simpledrm_device_attach_genpd(struct simpledrm_device *sdev) 510319251c5SThomas Zimmermann { 511b5626f6fSThomas Zimmermann struct device *dev = sdev->sysfb.dev.dev; 512319251c5SThomas Zimmermann int i; 513319251c5SThomas Zimmermann 514319251c5SThomas Zimmermann sdev->pwr_dom_count = of_count_phandle_with_args(dev->of_node, "power-domains", 515319251c5SThomas Zimmermann "#power-domain-cells"); 516319251c5SThomas Zimmermann /* 517319251c5SThomas Zimmermann * Single power-domain devices are handled by driver core nothing to do 518319251c5SThomas Zimmermann * here. The same for device nodes without "power-domains" property. 519319251c5SThomas Zimmermann */ 520319251c5SThomas Zimmermann if (sdev->pwr_dom_count <= 1) 521319251c5SThomas Zimmermann return 0; 522319251c5SThomas Zimmermann 523319251c5SThomas Zimmermann sdev->pwr_dom_devs = devm_kcalloc(dev, sdev->pwr_dom_count, 524319251c5SThomas Zimmermann sizeof(*sdev->pwr_dom_devs), 525319251c5SThomas Zimmermann GFP_KERNEL); 526319251c5SThomas Zimmermann if (!sdev->pwr_dom_devs) 527319251c5SThomas Zimmermann return -ENOMEM; 528319251c5SThomas Zimmermann 529319251c5SThomas Zimmermann sdev->pwr_dom_links = devm_kcalloc(dev, sdev->pwr_dom_count, 530319251c5SThomas Zimmermann sizeof(*sdev->pwr_dom_links), 531319251c5SThomas Zimmermann GFP_KERNEL); 532319251c5SThomas Zimmermann if (!sdev->pwr_dom_links) 533319251c5SThomas Zimmermann return -ENOMEM; 534319251c5SThomas Zimmermann 535319251c5SThomas Zimmermann for (i = 0; i < sdev->pwr_dom_count; i++) { 536319251c5SThomas Zimmermann sdev->pwr_dom_devs[i] = dev_pm_domain_attach_by_id(dev, i); 537319251c5SThomas Zimmermann if (IS_ERR(sdev->pwr_dom_devs[i])) { 538319251c5SThomas Zimmermann int ret = PTR_ERR(sdev->pwr_dom_devs[i]); 539319251c5SThomas Zimmermann if (ret == -EPROBE_DEFER) { 540319251c5SThomas Zimmermann simpledrm_device_detach_genpd(sdev); 541319251c5SThomas Zimmermann return ret; 542319251c5SThomas Zimmermann } 543b5626f6fSThomas Zimmermann drm_warn(&sdev->sysfb.dev, 544319251c5SThomas Zimmermann "pm_domain_attach_by_id(%u) failed: %d\n", i, ret); 545319251c5SThomas Zimmermann continue; 546319251c5SThomas Zimmermann } 547319251c5SThomas Zimmermann 548319251c5SThomas Zimmermann sdev->pwr_dom_links[i] = device_link_add(dev, 549319251c5SThomas Zimmermann sdev->pwr_dom_devs[i], 550319251c5SThomas Zimmermann DL_FLAG_STATELESS | 551319251c5SThomas Zimmermann DL_FLAG_PM_RUNTIME | 552319251c5SThomas Zimmermann DL_FLAG_RPM_ACTIVE); 553319251c5SThomas Zimmermann if (!sdev->pwr_dom_links[i]) 554b5626f6fSThomas Zimmermann drm_warn(&sdev->sysfb.dev, "failed to link power-domain %d\n", i); 555319251c5SThomas Zimmermann } 556319251c5SThomas Zimmermann 557319251c5SThomas Zimmermann return devm_add_action_or_reset(dev, simpledrm_device_detach_genpd, sdev); 558319251c5SThomas Zimmermann } 559319251c5SThomas Zimmermann #else 560319251c5SThomas Zimmermann static int simpledrm_device_attach_genpd(struct simpledrm_device *sdev) 561319251c5SThomas Zimmermann { 562319251c5SThomas Zimmermann return 0; 563319251c5SThomas Zimmermann } 564319251c5SThomas Zimmermann #endif 565319251c5SThomas Zimmermann 566319251c5SThomas Zimmermann /* 567319251c5SThomas Zimmermann * Modesetting 568319251c5SThomas Zimmermann */ 569319251c5SThomas Zimmermann 570319251c5SThomas Zimmermann static const uint64_t simpledrm_primary_plane_format_modifiers[] = { 571319251c5SThomas Zimmermann DRM_FORMAT_MOD_LINEAR, 572319251c5SThomas Zimmermann DRM_FORMAT_MOD_INVALID 573319251c5SThomas Zimmermann }; 574319251c5SThomas Zimmermann 575319251c5SThomas Zimmermann static int simpledrm_primary_plane_helper_atomic_check(struct drm_plane *plane, 576319251c5SThomas Zimmermann struct drm_atomic_state *state) 577319251c5SThomas Zimmermann { 578319251c5SThomas Zimmermann struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane); 579319251c5SThomas Zimmermann struct drm_shadow_plane_state *new_shadow_plane_state = 580319251c5SThomas Zimmermann to_drm_shadow_plane_state(new_plane_state); 581319251c5SThomas Zimmermann struct drm_framebuffer *new_fb = new_plane_state->fb; 582319251c5SThomas Zimmermann struct drm_crtc *new_crtc = new_plane_state->crtc; 583319251c5SThomas Zimmermann struct drm_crtc_state *new_crtc_state = NULL; 584319251c5SThomas Zimmermann struct drm_device *dev = plane->dev; 585b5626f6fSThomas Zimmermann struct drm_sysfb_device *sysfb = to_drm_sysfb_device(dev); 586319251c5SThomas Zimmermann int ret; 587319251c5SThomas Zimmermann 588319251c5SThomas Zimmermann if (new_crtc) 589319251c5SThomas Zimmermann new_crtc_state = drm_atomic_get_new_crtc_state(state, new_crtc); 590319251c5SThomas Zimmermann 591319251c5SThomas Zimmermann ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state, 592319251c5SThomas Zimmermann DRM_PLANE_NO_SCALING, 593319251c5SThomas Zimmermann DRM_PLANE_NO_SCALING, 594319251c5SThomas Zimmermann false, false); 595319251c5SThomas Zimmermann if (ret) 596319251c5SThomas Zimmermann return ret; 597319251c5SThomas Zimmermann else if (!new_plane_state->visible) 598319251c5SThomas Zimmermann return 0; 599319251c5SThomas Zimmermann 600b5626f6fSThomas Zimmermann if (new_fb->format != sysfb->fb_format) { 601319251c5SThomas Zimmermann void *buf; 602319251c5SThomas Zimmermann 603319251c5SThomas Zimmermann /* format conversion necessary; reserve buffer */ 604319251c5SThomas Zimmermann buf = drm_format_conv_state_reserve(&new_shadow_plane_state->fmtcnv_state, 605b5626f6fSThomas Zimmermann sysfb->fb_pitch, GFP_KERNEL); 606319251c5SThomas Zimmermann if (!buf) 607319251c5SThomas Zimmermann return -ENOMEM; 608319251c5SThomas Zimmermann } 609319251c5SThomas Zimmermann 610319251c5SThomas Zimmermann return 0; 611319251c5SThomas Zimmermann } 612319251c5SThomas Zimmermann 613319251c5SThomas Zimmermann static void simpledrm_primary_plane_helper_atomic_update(struct drm_plane *plane, 614319251c5SThomas Zimmermann struct drm_atomic_state *state) 615319251c5SThomas Zimmermann { 616319251c5SThomas Zimmermann struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); 617319251c5SThomas Zimmermann struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane); 618319251c5SThomas Zimmermann struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); 619319251c5SThomas Zimmermann struct drm_framebuffer *fb = plane_state->fb; 620319251c5SThomas Zimmermann struct drm_device *dev = plane->dev; 621b5626f6fSThomas Zimmermann struct drm_sysfb_device *sysfb = to_drm_sysfb_device(dev); 622319251c5SThomas Zimmermann struct drm_atomic_helper_damage_iter iter; 623319251c5SThomas Zimmermann struct drm_rect damage; 624319251c5SThomas Zimmermann int ret, idx; 625319251c5SThomas Zimmermann 626319251c5SThomas Zimmermann ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE); 627319251c5SThomas Zimmermann if (ret) 628319251c5SThomas Zimmermann return; 629319251c5SThomas Zimmermann 630319251c5SThomas Zimmermann if (!drm_dev_enter(dev, &idx)) 631319251c5SThomas Zimmermann goto out_drm_gem_fb_end_cpu_access; 632319251c5SThomas Zimmermann 633319251c5SThomas Zimmermann drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state); 634319251c5SThomas Zimmermann drm_atomic_for_each_plane_damage(&iter, &damage) { 635319251c5SThomas Zimmermann struct drm_rect dst_clip = plane_state->dst; 636b5626f6fSThomas Zimmermann struct iosys_map dst = sysfb->fb_addr; 637319251c5SThomas Zimmermann 638319251c5SThomas Zimmermann if (!drm_rect_intersect(&dst_clip, &damage)) 639319251c5SThomas Zimmermann continue; 640319251c5SThomas Zimmermann 641b5626f6fSThomas Zimmermann iosys_map_incr(&dst, drm_fb_clip_offset(sysfb->fb_pitch, sysfb->fb_format, 642b5626f6fSThomas Zimmermann &dst_clip)); 643b5626f6fSThomas Zimmermann drm_fb_blit(&dst, &sysfb->fb_pitch, sysfb->fb_format->format, 644b5626f6fSThomas Zimmermann shadow_plane_state->data, 645319251c5SThomas Zimmermann fb, &damage, &shadow_plane_state->fmtcnv_state); 646319251c5SThomas Zimmermann } 647319251c5SThomas Zimmermann 648319251c5SThomas Zimmermann drm_dev_exit(idx); 649319251c5SThomas Zimmermann out_drm_gem_fb_end_cpu_access: 650319251c5SThomas Zimmermann drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); 651319251c5SThomas Zimmermann } 652319251c5SThomas Zimmermann 653319251c5SThomas Zimmermann static void simpledrm_primary_plane_helper_atomic_disable(struct drm_plane *plane, 654319251c5SThomas Zimmermann struct drm_atomic_state *state) 655319251c5SThomas Zimmermann { 656319251c5SThomas Zimmermann struct drm_device *dev = plane->dev; 657b5626f6fSThomas Zimmermann struct drm_sysfb_device *sysfb = to_drm_sysfb_device(plane->dev); 658319251c5SThomas Zimmermann int idx; 659319251c5SThomas Zimmermann 660319251c5SThomas Zimmermann if (!drm_dev_enter(dev, &idx)) 661319251c5SThomas Zimmermann return; 662319251c5SThomas Zimmermann 663319251c5SThomas Zimmermann /* Clear screen to black if disabled */ 664b5626f6fSThomas Zimmermann iosys_map_memset(&sysfb->fb_addr, 0, 0, sysfb->fb_pitch * sysfb->fb_mode.vdisplay); 665319251c5SThomas Zimmermann 666319251c5SThomas Zimmermann drm_dev_exit(idx); 667319251c5SThomas Zimmermann } 668319251c5SThomas Zimmermann 669319251c5SThomas Zimmermann static int simpledrm_primary_plane_helper_get_scanout_buffer(struct drm_plane *plane, 670319251c5SThomas Zimmermann struct drm_scanout_buffer *sb) 671319251c5SThomas Zimmermann { 672b5626f6fSThomas Zimmermann struct drm_sysfb_device *sysfb = to_drm_sysfb_device(plane->dev); 673319251c5SThomas Zimmermann 674b5626f6fSThomas Zimmermann sb->width = sysfb->fb_mode.hdisplay; 675b5626f6fSThomas Zimmermann sb->height = sysfb->fb_mode.vdisplay; 676b5626f6fSThomas Zimmermann sb->format = sysfb->fb_format; 677b5626f6fSThomas Zimmermann sb->pitch[0] = sysfb->fb_pitch; 678b5626f6fSThomas Zimmermann sb->map[0] = sysfb->fb_addr; 679319251c5SThomas Zimmermann 680319251c5SThomas Zimmermann return 0; 681319251c5SThomas Zimmermann } 682319251c5SThomas Zimmermann 683319251c5SThomas Zimmermann static const struct drm_plane_helper_funcs simpledrm_primary_plane_helper_funcs = { 684319251c5SThomas Zimmermann DRM_GEM_SHADOW_PLANE_HELPER_FUNCS, 685319251c5SThomas Zimmermann .atomic_check = simpledrm_primary_plane_helper_atomic_check, 686319251c5SThomas Zimmermann .atomic_update = simpledrm_primary_plane_helper_atomic_update, 687319251c5SThomas Zimmermann .atomic_disable = simpledrm_primary_plane_helper_atomic_disable, 688319251c5SThomas Zimmermann .get_scanout_buffer = simpledrm_primary_plane_helper_get_scanout_buffer, 689319251c5SThomas Zimmermann }; 690319251c5SThomas Zimmermann 691319251c5SThomas Zimmermann static const struct drm_plane_funcs simpledrm_primary_plane_funcs = { 692319251c5SThomas Zimmermann .update_plane = drm_atomic_helper_update_plane, 693319251c5SThomas Zimmermann .disable_plane = drm_atomic_helper_disable_plane, 694319251c5SThomas Zimmermann .destroy = drm_plane_cleanup, 695319251c5SThomas Zimmermann DRM_GEM_SHADOW_PLANE_FUNCS, 696319251c5SThomas Zimmermann }; 697319251c5SThomas Zimmermann 698319251c5SThomas Zimmermann static const struct drm_crtc_helper_funcs simpledrm_crtc_helper_funcs = { 699*ea86aba4SThomas Zimmermann DRM_SYSFB_CRTC_HELPER_FUNCS, 700319251c5SThomas Zimmermann }; 701319251c5SThomas Zimmermann 702319251c5SThomas Zimmermann static const struct drm_crtc_funcs simpledrm_crtc_funcs = { 70368ab3253SThomas Zimmermann DRM_SYSFB_CRTC_FUNCS, 704319251c5SThomas Zimmermann .destroy = drm_crtc_cleanup, 705319251c5SThomas Zimmermann }; 706319251c5SThomas Zimmermann 707319251c5SThomas Zimmermann static const struct drm_encoder_funcs simpledrm_encoder_funcs = { 708319251c5SThomas Zimmermann .destroy = drm_encoder_cleanup, 709319251c5SThomas Zimmermann }; 710319251c5SThomas Zimmermann 711319251c5SThomas Zimmermann static const struct drm_connector_helper_funcs simpledrm_connector_helper_funcs = { 71201e48e52SThomas Zimmermann DRM_SYSFB_CONNECTOR_HELPER_FUNCS, 713319251c5SThomas Zimmermann }; 714319251c5SThomas Zimmermann 715319251c5SThomas Zimmermann static const struct drm_connector_funcs simpledrm_connector_funcs = { 71601e48e52SThomas Zimmermann DRM_SYSFB_CONNECTOR_FUNCS, 717319251c5SThomas Zimmermann .destroy = drm_connector_cleanup, 718319251c5SThomas Zimmermann }; 719319251c5SThomas Zimmermann 720319251c5SThomas Zimmermann static const struct drm_mode_config_funcs simpledrm_mode_config_funcs = { 721559d105fSThomas Zimmermann DRM_SYSFB_MODE_CONFIG_FUNCS, 722319251c5SThomas Zimmermann }; 723319251c5SThomas Zimmermann 724319251c5SThomas Zimmermann /* 725319251c5SThomas Zimmermann * Init / Cleanup 726319251c5SThomas Zimmermann */ 727319251c5SThomas Zimmermann 728319251c5SThomas Zimmermann static struct simpledrm_device *simpledrm_device_create(struct drm_driver *drv, 729319251c5SThomas Zimmermann struct platform_device *pdev) 730319251c5SThomas Zimmermann { 731319251c5SThomas Zimmermann const struct simplefb_platform_data *pd = dev_get_platdata(&pdev->dev); 732319251c5SThomas Zimmermann struct device_node *of_node = pdev->dev.of_node; 733319251c5SThomas Zimmermann struct simpledrm_device *sdev; 734b5626f6fSThomas Zimmermann struct drm_sysfb_device *sysfb; 735319251c5SThomas Zimmermann struct drm_device *dev; 736319251c5SThomas Zimmermann int width, height, stride; 737319251c5SThomas Zimmermann int width_mm = 0, height_mm = 0; 738319251c5SThomas Zimmermann struct device_node *panel_node; 739319251c5SThomas Zimmermann const struct drm_format_info *format; 740319251c5SThomas Zimmermann struct resource *res, *mem = NULL; 741319251c5SThomas Zimmermann struct drm_plane *primary_plane; 742319251c5SThomas Zimmermann struct drm_crtc *crtc; 743319251c5SThomas Zimmermann struct drm_encoder *encoder; 744319251c5SThomas Zimmermann struct drm_connector *connector; 745319251c5SThomas Zimmermann unsigned long max_width, max_height; 746319251c5SThomas Zimmermann size_t nformats; 747319251c5SThomas Zimmermann int ret; 748319251c5SThomas Zimmermann 749b5626f6fSThomas Zimmermann sdev = devm_drm_dev_alloc(&pdev->dev, drv, struct simpledrm_device, sysfb.dev); 750319251c5SThomas Zimmermann if (IS_ERR(sdev)) 751319251c5SThomas Zimmermann return ERR_CAST(sdev); 752b5626f6fSThomas Zimmermann sysfb = &sdev->sysfb; 753b5626f6fSThomas Zimmermann dev = &sysfb->dev; 754319251c5SThomas Zimmermann platform_set_drvdata(pdev, sdev); 755319251c5SThomas Zimmermann 756319251c5SThomas Zimmermann /* 757319251c5SThomas Zimmermann * Hardware settings 758319251c5SThomas Zimmermann */ 759319251c5SThomas Zimmermann 760319251c5SThomas Zimmermann ret = simpledrm_device_init_clocks(sdev); 761319251c5SThomas Zimmermann if (ret) 762319251c5SThomas Zimmermann return ERR_PTR(ret); 763319251c5SThomas Zimmermann ret = simpledrm_device_init_regulators(sdev); 764319251c5SThomas Zimmermann if (ret) 765319251c5SThomas Zimmermann return ERR_PTR(ret); 766319251c5SThomas Zimmermann ret = simpledrm_device_attach_genpd(sdev); 767319251c5SThomas Zimmermann if (ret) 768319251c5SThomas Zimmermann return ERR_PTR(ret); 769319251c5SThomas Zimmermann 770319251c5SThomas Zimmermann if (pd) { 771319251c5SThomas Zimmermann width = simplefb_get_width_pd(dev, pd); 772319251c5SThomas Zimmermann if (width < 0) 773319251c5SThomas Zimmermann return ERR_PTR(width); 774319251c5SThomas Zimmermann height = simplefb_get_height_pd(dev, pd); 775319251c5SThomas Zimmermann if (height < 0) 776319251c5SThomas Zimmermann return ERR_PTR(height); 777319251c5SThomas Zimmermann stride = simplefb_get_stride_pd(dev, pd); 778319251c5SThomas Zimmermann if (stride < 0) 779319251c5SThomas Zimmermann return ERR_PTR(stride); 780319251c5SThomas Zimmermann format = simplefb_get_format_pd(dev, pd); 781319251c5SThomas Zimmermann if (IS_ERR(format)) 782319251c5SThomas Zimmermann return ERR_CAST(format); 783319251c5SThomas Zimmermann } else if (of_node) { 784319251c5SThomas Zimmermann width = simplefb_get_width_of(dev, of_node); 785319251c5SThomas Zimmermann if (width < 0) 786319251c5SThomas Zimmermann return ERR_PTR(width); 787319251c5SThomas Zimmermann height = simplefb_get_height_of(dev, of_node); 788319251c5SThomas Zimmermann if (height < 0) 789319251c5SThomas Zimmermann return ERR_PTR(height); 790319251c5SThomas Zimmermann stride = simplefb_get_stride_of(dev, of_node); 791319251c5SThomas Zimmermann if (stride < 0) 792319251c5SThomas Zimmermann return ERR_PTR(stride); 793319251c5SThomas Zimmermann format = simplefb_get_format_of(dev, of_node); 794319251c5SThomas Zimmermann if (IS_ERR(format)) 795319251c5SThomas Zimmermann return ERR_CAST(format); 796319251c5SThomas Zimmermann mem = simplefb_get_memory_of(dev, of_node); 797319251c5SThomas Zimmermann if (IS_ERR(mem)) 798319251c5SThomas Zimmermann return ERR_CAST(mem); 799319251c5SThomas Zimmermann panel_node = of_parse_phandle(of_node, "panel", 0); 800319251c5SThomas Zimmermann if (panel_node) { 801319251c5SThomas Zimmermann simplefb_read_u32_of(dev, panel_node, "width-mm", &width_mm); 802319251c5SThomas Zimmermann simplefb_read_u32_of(dev, panel_node, "height-mm", &height_mm); 803319251c5SThomas Zimmermann of_node_put(panel_node); 804319251c5SThomas Zimmermann } 805319251c5SThomas Zimmermann } else { 806319251c5SThomas Zimmermann drm_err(dev, "no simplefb configuration found\n"); 807319251c5SThomas Zimmermann return ERR_PTR(-ENODEV); 808319251c5SThomas Zimmermann } 809319251c5SThomas Zimmermann if (!stride) { 810319251c5SThomas Zimmermann stride = drm_format_info_min_pitch(format, 0, width); 811319251c5SThomas Zimmermann if (drm_WARN_ON(dev, !stride)) 812319251c5SThomas Zimmermann return ERR_PTR(-EINVAL); 813319251c5SThomas Zimmermann } 814319251c5SThomas Zimmermann 815333376e9SThomas Zimmermann sysfb->fb_mode = drm_sysfb_mode(width, height, width_mm, height_mm); 816b5626f6fSThomas Zimmermann sysfb->fb_format = format; 817b5626f6fSThomas Zimmermann sysfb->fb_pitch = stride; 818319251c5SThomas Zimmermann 819b5626f6fSThomas Zimmermann drm_dbg(dev, "display mode={" DRM_MODE_FMT "}\n", DRM_MODE_ARG(&sysfb->fb_mode)); 820319251c5SThomas Zimmermann drm_dbg(dev, "framebuffer format=%p4cc, size=%dx%d, stride=%d byte\n", 821319251c5SThomas Zimmermann &format->format, width, height, stride); 822319251c5SThomas Zimmermann 823319251c5SThomas Zimmermann /* 824319251c5SThomas Zimmermann * Memory management 825319251c5SThomas Zimmermann */ 826319251c5SThomas Zimmermann 827319251c5SThomas Zimmermann if (mem) { 828319251c5SThomas Zimmermann void *screen_base; 829319251c5SThomas Zimmermann 830319251c5SThomas Zimmermann ret = devm_aperture_acquire_for_platform_device(pdev, mem->start, 831319251c5SThomas Zimmermann resource_size(mem)); 832319251c5SThomas Zimmermann if (ret) { 833319251c5SThomas Zimmermann drm_err(dev, "could not acquire memory range %pr: %d\n", mem, ret); 834319251c5SThomas Zimmermann return ERR_PTR(ret); 835319251c5SThomas Zimmermann } 836319251c5SThomas Zimmermann 837319251c5SThomas Zimmermann drm_dbg(dev, "using system memory framebuffer at %pr\n", mem); 838319251c5SThomas Zimmermann 839319251c5SThomas Zimmermann screen_base = devm_memremap(dev->dev, mem->start, resource_size(mem), MEMREMAP_WC); 840319251c5SThomas Zimmermann if (IS_ERR(screen_base)) 841319251c5SThomas Zimmermann return screen_base; 842319251c5SThomas Zimmermann 843b5626f6fSThomas Zimmermann iosys_map_set_vaddr(&sysfb->fb_addr, screen_base); 844319251c5SThomas Zimmermann } else { 845319251c5SThomas Zimmermann void __iomem *screen_base; 846319251c5SThomas Zimmermann 847319251c5SThomas Zimmermann res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 848319251c5SThomas Zimmermann if (!res) 849319251c5SThomas Zimmermann return ERR_PTR(-EINVAL); 850319251c5SThomas Zimmermann 851319251c5SThomas Zimmermann ret = devm_aperture_acquire_for_platform_device(pdev, res->start, 852319251c5SThomas Zimmermann resource_size(res)); 853319251c5SThomas Zimmermann if (ret) { 854319251c5SThomas Zimmermann drm_err(dev, "could not acquire memory range %pr: %d\n", res, ret); 855319251c5SThomas Zimmermann return ERR_PTR(ret); 856319251c5SThomas Zimmermann } 857319251c5SThomas Zimmermann 858319251c5SThomas Zimmermann drm_dbg(dev, "using I/O memory framebuffer at %pr\n", res); 859319251c5SThomas Zimmermann 860319251c5SThomas Zimmermann mem = devm_request_mem_region(&pdev->dev, res->start, resource_size(res), 861319251c5SThomas Zimmermann drv->name); 862319251c5SThomas Zimmermann if (!mem) { 863319251c5SThomas Zimmermann /* 864319251c5SThomas Zimmermann * We cannot make this fatal. Sometimes this comes from magic 865319251c5SThomas Zimmermann * spaces our resource handlers simply don't know about. Use 866319251c5SThomas Zimmermann * the I/O-memory resource as-is and try to map that instead. 867319251c5SThomas Zimmermann */ 868319251c5SThomas Zimmermann drm_warn(dev, "could not acquire memory region %pr\n", res); 869319251c5SThomas Zimmermann mem = res; 870319251c5SThomas Zimmermann } 871319251c5SThomas Zimmermann 872319251c5SThomas Zimmermann screen_base = devm_ioremap_wc(&pdev->dev, mem->start, resource_size(mem)); 873319251c5SThomas Zimmermann if (!screen_base) 874319251c5SThomas Zimmermann return ERR_PTR(-ENOMEM); 875319251c5SThomas Zimmermann 876b5626f6fSThomas Zimmermann iosys_map_set_vaddr_iomem(&sysfb->fb_addr, screen_base); 877319251c5SThomas Zimmermann } 878319251c5SThomas Zimmermann 879319251c5SThomas Zimmermann /* 880319251c5SThomas Zimmermann * Modesetting 881319251c5SThomas Zimmermann */ 882319251c5SThomas Zimmermann 883319251c5SThomas Zimmermann ret = drmm_mode_config_init(dev); 884319251c5SThomas Zimmermann if (ret) 885319251c5SThomas Zimmermann return ERR_PTR(ret); 886319251c5SThomas Zimmermann 887319251c5SThomas Zimmermann max_width = max_t(unsigned long, width, DRM_SHADOW_PLANE_MAX_WIDTH); 888319251c5SThomas Zimmermann max_height = max_t(unsigned long, height, DRM_SHADOW_PLANE_MAX_HEIGHT); 889319251c5SThomas Zimmermann 890319251c5SThomas Zimmermann dev->mode_config.min_width = width; 891319251c5SThomas Zimmermann dev->mode_config.max_width = max_width; 892319251c5SThomas Zimmermann dev->mode_config.min_height = height; 893319251c5SThomas Zimmermann dev->mode_config.max_height = max_height; 894319251c5SThomas Zimmermann dev->mode_config.preferred_depth = format->depth; 895319251c5SThomas Zimmermann dev->mode_config.funcs = &simpledrm_mode_config_funcs; 896319251c5SThomas Zimmermann 897319251c5SThomas Zimmermann /* Primary plane */ 898319251c5SThomas Zimmermann 899319251c5SThomas Zimmermann nformats = drm_fb_build_fourcc_list(dev, &format->format, 1, 900319251c5SThomas Zimmermann sdev->formats, ARRAY_SIZE(sdev->formats)); 901319251c5SThomas Zimmermann 902319251c5SThomas Zimmermann primary_plane = &sdev->primary_plane; 903319251c5SThomas Zimmermann ret = drm_universal_plane_init(dev, primary_plane, 0, &simpledrm_primary_plane_funcs, 904319251c5SThomas Zimmermann sdev->formats, nformats, 905319251c5SThomas Zimmermann simpledrm_primary_plane_format_modifiers, 906319251c5SThomas Zimmermann DRM_PLANE_TYPE_PRIMARY, NULL); 907319251c5SThomas Zimmermann if (ret) 908319251c5SThomas Zimmermann return ERR_PTR(ret); 909319251c5SThomas Zimmermann drm_plane_helper_add(primary_plane, &simpledrm_primary_plane_helper_funcs); 910319251c5SThomas Zimmermann drm_plane_enable_fb_damage_clips(primary_plane); 911319251c5SThomas Zimmermann 912319251c5SThomas Zimmermann /* CRTC */ 913319251c5SThomas Zimmermann 914319251c5SThomas Zimmermann crtc = &sdev->crtc; 915319251c5SThomas Zimmermann ret = drm_crtc_init_with_planes(dev, crtc, primary_plane, NULL, 916319251c5SThomas Zimmermann &simpledrm_crtc_funcs, NULL); 917319251c5SThomas Zimmermann if (ret) 918319251c5SThomas Zimmermann return ERR_PTR(ret); 919319251c5SThomas Zimmermann drm_crtc_helper_add(crtc, &simpledrm_crtc_helper_funcs); 920319251c5SThomas Zimmermann 921319251c5SThomas Zimmermann /* Encoder */ 922319251c5SThomas Zimmermann 923319251c5SThomas Zimmermann encoder = &sdev->encoder; 924319251c5SThomas Zimmermann ret = drm_encoder_init(dev, encoder, &simpledrm_encoder_funcs, 925319251c5SThomas Zimmermann DRM_MODE_ENCODER_NONE, NULL); 926319251c5SThomas Zimmermann if (ret) 927319251c5SThomas Zimmermann return ERR_PTR(ret); 928319251c5SThomas Zimmermann encoder->possible_crtcs = drm_crtc_mask(crtc); 929319251c5SThomas Zimmermann 930319251c5SThomas Zimmermann /* Connector */ 931319251c5SThomas Zimmermann 932319251c5SThomas Zimmermann connector = &sdev->connector; 933319251c5SThomas Zimmermann ret = drm_connector_init(dev, connector, &simpledrm_connector_funcs, 934319251c5SThomas Zimmermann DRM_MODE_CONNECTOR_Unknown); 935319251c5SThomas Zimmermann if (ret) 936319251c5SThomas Zimmermann return ERR_PTR(ret); 937319251c5SThomas Zimmermann drm_connector_helper_add(connector, &simpledrm_connector_helper_funcs); 938319251c5SThomas Zimmermann drm_connector_set_panel_orientation_with_quirk(connector, 939319251c5SThomas Zimmermann DRM_MODE_PANEL_ORIENTATION_UNKNOWN, 940319251c5SThomas Zimmermann width, height); 941319251c5SThomas Zimmermann 942319251c5SThomas Zimmermann ret = drm_connector_attach_encoder(connector, encoder); 943319251c5SThomas Zimmermann if (ret) 944319251c5SThomas Zimmermann return ERR_PTR(ret); 945319251c5SThomas Zimmermann 946319251c5SThomas Zimmermann drm_mode_config_reset(dev); 947319251c5SThomas Zimmermann 948319251c5SThomas Zimmermann return sdev; 949319251c5SThomas Zimmermann } 950319251c5SThomas Zimmermann 951319251c5SThomas Zimmermann /* 952319251c5SThomas Zimmermann * DRM driver 953319251c5SThomas Zimmermann */ 954319251c5SThomas Zimmermann 955319251c5SThomas Zimmermann DEFINE_DRM_GEM_FOPS(simpledrm_fops); 956319251c5SThomas Zimmermann 957319251c5SThomas Zimmermann static struct drm_driver simpledrm_driver = { 958319251c5SThomas Zimmermann DRM_GEM_SHMEM_DRIVER_OPS, 959319251c5SThomas Zimmermann DRM_FBDEV_SHMEM_DRIVER_OPS, 960319251c5SThomas Zimmermann .name = DRIVER_NAME, 961319251c5SThomas Zimmermann .desc = DRIVER_DESC, 962319251c5SThomas Zimmermann .major = DRIVER_MAJOR, 963319251c5SThomas Zimmermann .minor = DRIVER_MINOR, 964319251c5SThomas Zimmermann .driver_features = DRIVER_ATOMIC | DRIVER_GEM | DRIVER_MODESET, 965319251c5SThomas Zimmermann .fops = &simpledrm_fops, 966319251c5SThomas Zimmermann }; 967319251c5SThomas Zimmermann 968319251c5SThomas Zimmermann /* 969319251c5SThomas Zimmermann * Platform driver 970319251c5SThomas Zimmermann */ 971319251c5SThomas Zimmermann 972319251c5SThomas Zimmermann static int simpledrm_probe(struct platform_device *pdev) 973319251c5SThomas Zimmermann { 974319251c5SThomas Zimmermann struct simpledrm_device *sdev; 975b5626f6fSThomas Zimmermann struct drm_sysfb_device *sysfb; 976319251c5SThomas Zimmermann struct drm_device *dev; 977319251c5SThomas Zimmermann int ret; 978319251c5SThomas Zimmermann 979319251c5SThomas Zimmermann sdev = simpledrm_device_create(&simpledrm_driver, pdev); 980319251c5SThomas Zimmermann if (IS_ERR(sdev)) 981319251c5SThomas Zimmermann return PTR_ERR(sdev); 982b5626f6fSThomas Zimmermann sysfb = &sdev->sysfb; 983b5626f6fSThomas Zimmermann dev = &sysfb->dev; 984319251c5SThomas Zimmermann 985319251c5SThomas Zimmermann ret = drm_dev_register(dev, 0); 986319251c5SThomas Zimmermann if (ret) 987319251c5SThomas Zimmermann return ret; 988319251c5SThomas Zimmermann 989b5626f6fSThomas Zimmermann drm_client_setup(dev, sdev->sysfb.fb_format); 990319251c5SThomas Zimmermann 991319251c5SThomas Zimmermann return 0; 992319251c5SThomas Zimmermann } 993319251c5SThomas Zimmermann 994319251c5SThomas Zimmermann static void simpledrm_remove(struct platform_device *pdev) 995319251c5SThomas Zimmermann { 996319251c5SThomas Zimmermann struct simpledrm_device *sdev = platform_get_drvdata(pdev); 997b5626f6fSThomas Zimmermann struct drm_device *dev = &sdev->sysfb.dev; 998319251c5SThomas Zimmermann 999319251c5SThomas Zimmermann drm_dev_unplug(dev); 1000319251c5SThomas Zimmermann } 1001319251c5SThomas Zimmermann 1002319251c5SThomas Zimmermann static const struct of_device_id simpledrm_of_match_table[] = { 1003319251c5SThomas Zimmermann { .compatible = "simple-framebuffer", }, 1004319251c5SThomas Zimmermann { }, 1005319251c5SThomas Zimmermann }; 1006319251c5SThomas Zimmermann MODULE_DEVICE_TABLE(of, simpledrm_of_match_table); 1007319251c5SThomas Zimmermann 1008319251c5SThomas Zimmermann static struct platform_driver simpledrm_platform_driver = { 1009319251c5SThomas Zimmermann .driver = { 1010319251c5SThomas Zimmermann .name = "simple-framebuffer", /* connect to sysfb */ 1011319251c5SThomas Zimmermann .of_match_table = simpledrm_of_match_table, 1012319251c5SThomas Zimmermann }, 1013319251c5SThomas Zimmermann .probe = simpledrm_probe, 1014319251c5SThomas Zimmermann .remove = simpledrm_remove, 1015319251c5SThomas Zimmermann }; 1016319251c5SThomas Zimmermann 1017319251c5SThomas Zimmermann module_platform_driver(simpledrm_platform_driver); 1018319251c5SThomas Zimmermann 1019319251c5SThomas Zimmermann MODULE_DESCRIPTION(DRIVER_DESC); 1020319251c5SThomas Zimmermann MODULE_LICENSE("GPL v2"); 1021