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