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