111e8f5fdSThomas Zimmermann // SPDX-License-Identifier: GPL-2.0-only
211e8f5fdSThomas Zimmermann
340f853ebSThomas Zimmermann #include <linux/aperture.h>
411e8f5fdSThomas Zimmermann #include <linux/clk.h>
511e8f5fdSThomas Zimmermann #include <linux/of_clk.h>
69239f3e1SThomas Zimmermann #include <linux/minmax.h>
79a10c7e6SThierry Reding #include <linux/of_address.h>
811e8f5fdSThomas Zimmermann #include <linux/platform_data/simplefb.h>
911e8f5fdSThomas Zimmermann #include <linux/platform_device.h>
1061df9ca2SJanne Grunau #include <linux/pm_domain.h>
1111e8f5fdSThomas Zimmermann #include <linux/regulator/consumer.h>
1211e8f5fdSThomas Zimmermann
13*b86711c6SThomas Zimmermann #include <drm/clients/drm_client_setup.h>
14de40c281SThomas Zimmermann #include <drm/drm_atomic.h>
1511e8f5fdSThomas Zimmermann #include <drm/drm_atomic_state_helper.h>
1611e8f5fdSThomas Zimmermann #include <drm/drm_connector.h>
177fed7fa3SJavier Martinez Canillas #include <drm/drm_crtc_helper.h>
1811e8f5fdSThomas Zimmermann #include <drm/drm_damage_helper.h>
1911e8f5fdSThomas Zimmermann #include <drm/drm_device.h>
2011e8f5fdSThomas Zimmermann #include <drm/drm_drv.h>
215134fa75SThomas Zimmermann #include <drm/drm_fbdev_shmem.h>
2211e8f5fdSThomas Zimmermann #include <drm/drm_format_helper.h>
23e7c814d3SThomas Zimmermann #include <drm/drm_framebuffer.h>
2411e8f5fdSThomas Zimmermann #include <drm/drm_gem_atomic_helper.h>
2511e8f5fdSThomas Zimmermann #include <drm/drm_gem_framebuffer_helper.h>
2611e8f5fdSThomas Zimmermann #include <drm/drm_gem_shmem_helper.h>
2711e8f5fdSThomas Zimmermann #include <drm/drm_managed.h>
2811e8f5fdSThomas Zimmermann #include <drm/drm_modeset_helper_vtables.h>
2941e54853SJocelyn Falempe #include <drm/drm_panic.h>
3011e8f5fdSThomas Zimmermann #include <drm/drm_probe_helper.h>
3111e8f5fdSThomas Zimmermann
3211e8f5fdSThomas Zimmermann #define DRIVER_NAME "simpledrm"
3311e8f5fdSThomas Zimmermann #define DRIVER_DESC "DRM driver for simple-framebuffer platform devices"
3411e8f5fdSThomas Zimmermann #define DRIVER_MAJOR 1
3511e8f5fdSThomas Zimmermann #define DRIVER_MINOR 0
3611e8f5fdSThomas Zimmermann
3711e8f5fdSThomas Zimmermann /*
3811e8f5fdSThomas Zimmermann * Helpers for simplefb
3911e8f5fdSThomas Zimmermann */
4011e8f5fdSThomas Zimmermann
4111e8f5fdSThomas Zimmermann static int
simplefb_get_validated_int(struct drm_device * dev,const char * name,uint32_t value)4211e8f5fdSThomas Zimmermann simplefb_get_validated_int(struct drm_device *dev, const char *name,
4311e8f5fdSThomas Zimmermann uint32_t value)
4411e8f5fdSThomas Zimmermann {
4511e8f5fdSThomas Zimmermann if (value > INT_MAX) {
4611e8f5fdSThomas Zimmermann drm_err(dev, "simplefb: invalid framebuffer %s of %u\n",
4711e8f5fdSThomas Zimmermann name, value);
4811e8f5fdSThomas Zimmermann return -EINVAL;
4911e8f5fdSThomas Zimmermann }
5011e8f5fdSThomas Zimmermann return (int)value;
5111e8f5fdSThomas Zimmermann }
5211e8f5fdSThomas Zimmermann
5311e8f5fdSThomas Zimmermann static int
simplefb_get_validated_int0(struct drm_device * dev,const char * name,uint32_t value)5411e8f5fdSThomas Zimmermann simplefb_get_validated_int0(struct drm_device *dev, const char *name,
5511e8f5fdSThomas Zimmermann uint32_t value)
5611e8f5fdSThomas Zimmermann {
5711e8f5fdSThomas Zimmermann if (!value) {
5811e8f5fdSThomas Zimmermann drm_err(dev, "simplefb: invalid framebuffer %s of %u\n",
5911e8f5fdSThomas Zimmermann name, value);
6011e8f5fdSThomas Zimmermann return -EINVAL;
6111e8f5fdSThomas Zimmermann }
6211e8f5fdSThomas Zimmermann return simplefb_get_validated_int(dev, name, value);
6311e8f5fdSThomas Zimmermann }
6411e8f5fdSThomas Zimmermann
6511e8f5fdSThomas Zimmermann static const struct drm_format_info *
simplefb_get_validated_format(struct drm_device * dev,const char * format_name)6611e8f5fdSThomas Zimmermann simplefb_get_validated_format(struct drm_device *dev, const char *format_name)
6711e8f5fdSThomas Zimmermann {
6811e8f5fdSThomas Zimmermann static const struct simplefb_format formats[] = SIMPLEFB_FORMATS;
6911e8f5fdSThomas Zimmermann const struct simplefb_format *fmt = formats;
7011e8f5fdSThomas Zimmermann const struct simplefb_format *end = fmt + ARRAY_SIZE(formats);
719c6f1942SDan Carpenter const struct drm_format_info *info;
7211e8f5fdSThomas Zimmermann
7311e8f5fdSThomas Zimmermann if (!format_name) {
7411e8f5fdSThomas Zimmermann drm_err(dev, "simplefb: missing framebuffer format\n");
7511e8f5fdSThomas Zimmermann return ERR_PTR(-EINVAL);
7611e8f5fdSThomas Zimmermann }
7711e8f5fdSThomas Zimmermann
7811e8f5fdSThomas Zimmermann while (fmt < end) {
799c6f1942SDan Carpenter if (!strcmp(format_name, fmt->name)) {
809c6f1942SDan Carpenter info = drm_format_info(fmt->fourcc);
819c6f1942SDan Carpenter if (!info)
829c6f1942SDan Carpenter return ERR_PTR(-EINVAL);
839c6f1942SDan Carpenter return info;
849c6f1942SDan Carpenter }
8511e8f5fdSThomas Zimmermann ++fmt;
8611e8f5fdSThomas Zimmermann }
8711e8f5fdSThomas Zimmermann
8811e8f5fdSThomas Zimmermann drm_err(dev, "simplefb: unknown framebuffer format %s\n",
8911e8f5fdSThomas Zimmermann format_name);
9011e8f5fdSThomas Zimmermann
9111e8f5fdSThomas Zimmermann return ERR_PTR(-EINVAL);
9211e8f5fdSThomas Zimmermann }
9311e8f5fdSThomas Zimmermann
9411e8f5fdSThomas Zimmermann static int
simplefb_get_width_pd(struct drm_device * dev,const struct simplefb_platform_data * pd)9511e8f5fdSThomas Zimmermann simplefb_get_width_pd(struct drm_device *dev,
9611e8f5fdSThomas Zimmermann const struct simplefb_platform_data *pd)
9711e8f5fdSThomas Zimmermann {
9811e8f5fdSThomas Zimmermann return simplefb_get_validated_int0(dev, "width", pd->width);
9911e8f5fdSThomas Zimmermann }
10011e8f5fdSThomas Zimmermann
10111e8f5fdSThomas Zimmermann static int
simplefb_get_height_pd(struct drm_device * dev,const struct simplefb_platform_data * pd)10211e8f5fdSThomas Zimmermann simplefb_get_height_pd(struct drm_device *dev,
10311e8f5fdSThomas Zimmermann const struct simplefb_platform_data *pd)
10411e8f5fdSThomas Zimmermann {
10511e8f5fdSThomas Zimmermann return simplefb_get_validated_int0(dev, "height", pd->height);
10611e8f5fdSThomas Zimmermann }
10711e8f5fdSThomas Zimmermann
10811e8f5fdSThomas Zimmermann static int
simplefb_get_stride_pd(struct drm_device * dev,const struct simplefb_platform_data * pd)10911e8f5fdSThomas Zimmermann simplefb_get_stride_pd(struct drm_device *dev,
11011e8f5fdSThomas Zimmermann const struct simplefb_platform_data *pd)
11111e8f5fdSThomas Zimmermann {
11211e8f5fdSThomas Zimmermann return simplefb_get_validated_int(dev, "stride", pd->stride);
11311e8f5fdSThomas Zimmermann }
11411e8f5fdSThomas Zimmermann
11511e8f5fdSThomas Zimmermann static const struct drm_format_info *
simplefb_get_format_pd(struct drm_device * dev,const struct simplefb_platform_data * pd)11611e8f5fdSThomas Zimmermann simplefb_get_format_pd(struct drm_device *dev,
11711e8f5fdSThomas Zimmermann const struct simplefb_platform_data *pd)
11811e8f5fdSThomas Zimmermann {
11911e8f5fdSThomas Zimmermann return simplefb_get_validated_format(dev, pd->format);
12011e8f5fdSThomas Zimmermann }
12111e8f5fdSThomas Zimmermann
12211e8f5fdSThomas Zimmermann static int
simplefb_read_u32_of(struct drm_device * dev,struct device_node * of_node,const char * name,u32 * value)12311e8f5fdSThomas Zimmermann simplefb_read_u32_of(struct drm_device *dev, struct device_node *of_node,
12411e8f5fdSThomas Zimmermann const char *name, u32 *value)
12511e8f5fdSThomas Zimmermann {
12611e8f5fdSThomas Zimmermann int ret = of_property_read_u32(of_node, name, value);
12711e8f5fdSThomas Zimmermann
12811e8f5fdSThomas Zimmermann if (ret)
12911e8f5fdSThomas Zimmermann drm_err(dev, "simplefb: cannot parse framebuffer %s: error %d\n",
13011e8f5fdSThomas Zimmermann name, ret);
13111e8f5fdSThomas Zimmermann return ret;
13211e8f5fdSThomas Zimmermann }
13311e8f5fdSThomas Zimmermann
13411e8f5fdSThomas Zimmermann static int
simplefb_read_string_of(struct drm_device * dev,struct device_node * of_node,const char * name,const char ** value)13511e8f5fdSThomas Zimmermann simplefb_read_string_of(struct drm_device *dev, struct device_node *of_node,
13611e8f5fdSThomas Zimmermann const char *name, const char **value)
13711e8f5fdSThomas Zimmermann {
13811e8f5fdSThomas Zimmermann int ret = of_property_read_string(of_node, name, value);
13911e8f5fdSThomas Zimmermann
14011e8f5fdSThomas Zimmermann if (ret)
14111e8f5fdSThomas Zimmermann drm_err(dev, "simplefb: cannot parse framebuffer %s: error %d\n",
14211e8f5fdSThomas Zimmermann name, ret);
14311e8f5fdSThomas Zimmermann return ret;
14411e8f5fdSThomas Zimmermann }
14511e8f5fdSThomas Zimmermann
14611e8f5fdSThomas Zimmermann static int
simplefb_get_width_of(struct drm_device * dev,struct device_node * of_node)14711e8f5fdSThomas Zimmermann simplefb_get_width_of(struct drm_device *dev, struct device_node *of_node)
14811e8f5fdSThomas Zimmermann {
14911e8f5fdSThomas Zimmermann u32 width;
15011e8f5fdSThomas Zimmermann int ret = simplefb_read_u32_of(dev, of_node, "width", &width);
15111e8f5fdSThomas Zimmermann
15211e8f5fdSThomas Zimmermann if (ret)
15311e8f5fdSThomas Zimmermann return ret;
15411e8f5fdSThomas Zimmermann return simplefb_get_validated_int0(dev, "width", width);
15511e8f5fdSThomas Zimmermann }
15611e8f5fdSThomas Zimmermann
15711e8f5fdSThomas Zimmermann static int
simplefb_get_height_of(struct drm_device * dev,struct device_node * of_node)15811e8f5fdSThomas Zimmermann simplefb_get_height_of(struct drm_device *dev, struct device_node *of_node)
15911e8f5fdSThomas Zimmermann {
16011e8f5fdSThomas Zimmermann u32 height;
16111e8f5fdSThomas Zimmermann int ret = simplefb_read_u32_of(dev, of_node, "height", &height);
16211e8f5fdSThomas Zimmermann
16311e8f5fdSThomas Zimmermann if (ret)
16411e8f5fdSThomas Zimmermann return ret;
16511e8f5fdSThomas Zimmermann return simplefb_get_validated_int0(dev, "height", height);
16611e8f5fdSThomas Zimmermann }
16711e8f5fdSThomas Zimmermann
16811e8f5fdSThomas Zimmermann static int
simplefb_get_stride_of(struct drm_device * dev,struct device_node * of_node)16911e8f5fdSThomas Zimmermann simplefb_get_stride_of(struct drm_device *dev, struct device_node *of_node)
17011e8f5fdSThomas Zimmermann {
17111e8f5fdSThomas Zimmermann u32 stride;
17211e8f5fdSThomas Zimmermann int ret = simplefb_read_u32_of(dev, of_node, "stride", &stride);
17311e8f5fdSThomas Zimmermann
17411e8f5fdSThomas Zimmermann if (ret)
17511e8f5fdSThomas Zimmermann return ret;
17611e8f5fdSThomas Zimmermann return simplefb_get_validated_int(dev, "stride", stride);
17711e8f5fdSThomas Zimmermann }
17811e8f5fdSThomas Zimmermann
17911e8f5fdSThomas Zimmermann static const struct drm_format_info *
simplefb_get_format_of(struct drm_device * dev,struct device_node * of_node)18011e8f5fdSThomas Zimmermann simplefb_get_format_of(struct drm_device *dev, struct device_node *of_node)
18111e8f5fdSThomas Zimmermann {
18211e8f5fdSThomas Zimmermann const char *format;
18311e8f5fdSThomas Zimmermann int ret = simplefb_read_string_of(dev, of_node, "format", &format);
18411e8f5fdSThomas Zimmermann
18511e8f5fdSThomas Zimmermann if (ret)
18611e8f5fdSThomas Zimmermann return ERR_PTR(ret);
18711e8f5fdSThomas Zimmermann return simplefb_get_validated_format(dev, format);
18811e8f5fdSThomas Zimmermann }
18911e8f5fdSThomas Zimmermann
1909a10c7e6SThierry Reding static struct resource *
simplefb_get_memory_of(struct drm_device * dev,struct device_node * of_node)1919a10c7e6SThierry Reding simplefb_get_memory_of(struct drm_device *dev, struct device_node *of_node)
1929a10c7e6SThierry Reding {
1939a10c7e6SThierry Reding struct device_node *np;
1949a10c7e6SThierry Reding struct resource *res;
1959a10c7e6SThierry Reding int err;
1969a10c7e6SThierry Reding
1979a10c7e6SThierry Reding np = of_parse_phandle(of_node, "memory-region", 0);
1989a10c7e6SThierry Reding if (!np)
1999a10c7e6SThierry Reding return NULL;
2009a10c7e6SThierry Reding
2019a10c7e6SThierry Reding res = devm_kzalloc(dev->dev, sizeof(*res), GFP_KERNEL);
2029a10c7e6SThierry Reding if (!res)
2039a10c7e6SThierry Reding return ERR_PTR(-ENOMEM);
2049a10c7e6SThierry Reding
2059a10c7e6SThierry Reding err = of_address_to_resource(np, 0, res);
2069a10c7e6SThierry Reding if (err)
2079a10c7e6SThierry Reding return ERR_PTR(err);
2089a10c7e6SThierry Reding
2097fa5047aSRob Herring if (of_property_present(of_node, "reg"))
2109a10c7e6SThierry Reding drm_warn(dev, "preferring \"memory-region\" over \"reg\" property\n");
2119a10c7e6SThierry Reding
2129a10c7e6SThierry Reding return res;
2139a10c7e6SThierry Reding }
2149a10c7e6SThierry Reding
21511e8f5fdSThomas Zimmermann /*
21611e8f5fdSThomas Zimmermann * Simple Framebuffer device
21711e8f5fdSThomas Zimmermann */
21811e8f5fdSThomas Zimmermann
21911e8f5fdSThomas Zimmermann struct simpledrm_device {
22011e8f5fdSThomas Zimmermann struct drm_device dev;
22111e8f5fdSThomas Zimmermann
22211e8f5fdSThomas Zimmermann /* clocks */
22311e8f5fdSThomas Zimmermann #if defined CONFIG_OF && defined CONFIG_COMMON_CLK
22411e8f5fdSThomas Zimmermann unsigned int clk_count;
22511e8f5fdSThomas Zimmermann struct clk **clks;
22611e8f5fdSThomas Zimmermann #endif
22711e8f5fdSThomas Zimmermann /* regulators */
22811e8f5fdSThomas Zimmermann #if defined CONFIG_OF && defined CONFIG_REGULATOR
22911e8f5fdSThomas Zimmermann unsigned int regulator_count;
23011e8f5fdSThomas Zimmermann struct regulator **regulators;
23111e8f5fdSThomas Zimmermann #endif
23261df9ca2SJanne Grunau /* power-domains */
23361df9ca2SJanne Grunau #if defined CONFIG_OF && defined CONFIG_PM_GENERIC_DOMAINS
23461df9ca2SJanne Grunau int pwr_dom_count;
23561df9ca2SJanne Grunau struct device **pwr_dom_devs;
23661df9ca2SJanne Grunau struct device_link **pwr_dom_links;
23761df9ca2SJanne Grunau #endif
23811e8f5fdSThomas Zimmermann
23911e8f5fdSThomas Zimmermann /* simplefb settings */
24011e8f5fdSThomas Zimmermann struct drm_display_mode mode;
24111e8f5fdSThomas Zimmermann const struct drm_format_info *format;
24211e8f5fdSThomas Zimmermann unsigned int pitch;
24311e8f5fdSThomas Zimmermann
24411e8f5fdSThomas Zimmermann /* memory management */
245fa904b4cSThierry Reding struct iosys_map screen_base;
24611e8f5fdSThomas Zimmermann
24711e8f5fdSThomas Zimmermann /* modesetting */
24811e8f5fdSThomas Zimmermann uint32_t formats[8];
24911e8f5fdSThomas Zimmermann size_t nformats;
250de40c281SThomas Zimmermann struct drm_plane primary_plane;
251de40c281SThomas Zimmermann struct drm_crtc crtc;
252de40c281SThomas Zimmermann struct drm_encoder encoder;
25311e8f5fdSThomas Zimmermann struct drm_connector connector;
25411e8f5fdSThomas Zimmermann };
25511e8f5fdSThomas Zimmermann
simpledrm_device_of_dev(struct drm_device * dev)25611e8f5fdSThomas Zimmermann static struct simpledrm_device *simpledrm_device_of_dev(struct drm_device *dev)
25711e8f5fdSThomas Zimmermann {
25811e8f5fdSThomas Zimmermann return container_of(dev, struct simpledrm_device, dev);
25911e8f5fdSThomas Zimmermann }
26011e8f5fdSThomas Zimmermann
26111e8f5fdSThomas Zimmermann /*
26211e8f5fdSThomas Zimmermann * Hardware
26311e8f5fdSThomas Zimmermann */
26411e8f5fdSThomas Zimmermann
26511e8f5fdSThomas Zimmermann #if defined CONFIG_OF && defined CONFIG_COMMON_CLK
26611e8f5fdSThomas Zimmermann /*
26711e8f5fdSThomas Zimmermann * Clock handling code.
26811e8f5fdSThomas Zimmermann *
26911e8f5fdSThomas Zimmermann * Here we handle the clocks property of our "simple-framebuffer" dt node.
27011e8f5fdSThomas Zimmermann * This is necessary so that we can make sure that any clocks needed by
27111e8f5fdSThomas Zimmermann * the display engine that the bootloader set up for us (and for which it
27211e8f5fdSThomas Zimmermann * provided a simplefb dt node), stay up, for the life of the simplefb
27311e8f5fdSThomas Zimmermann * driver.
27411e8f5fdSThomas Zimmermann *
27511e8f5fdSThomas Zimmermann * When the driver unloads, we cleanly disable, and then release the clocks.
27611e8f5fdSThomas Zimmermann *
27711e8f5fdSThomas Zimmermann * We only complain about errors here, no action is taken as the most likely
27811e8f5fdSThomas Zimmermann * error can only happen due to a mismatch between the bootloader which set
27911e8f5fdSThomas Zimmermann * up simplefb, and the clock definitions in the device tree. Chances are
28011e8f5fdSThomas Zimmermann * that there are no adverse effects, and if there are, a clean teardown of
28111e8f5fdSThomas Zimmermann * the fb probe will not help us much either. So just complain and carry on,
28211e8f5fdSThomas Zimmermann * and hope that the user actually gets a working fb at the end of things.
28311e8f5fdSThomas Zimmermann */
28411e8f5fdSThomas Zimmermann
simpledrm_device_release_clocks(void * res)28511e8f5fdSThomas Zimmermann static void simpledrm_device_release_clocks(void *res)
28611e8f5fdSThomas Zimmermann {
28711e8f5fdSThomas Zimmermann struct simpledrm_device *sdev = simpledrm_device_of_dev(res);
28811e8f5fdSThomas Zimmermann unsigned int i;
28911e8f5fdSThomas Zimmermann
29011e8f5fdSThomas Zimmermann for (i = 0; i < sdev->clk_count; ++i) {
29111e8f5fdSThomas Zimmermann if (sdev->clks[i]) {
29211e8f5fdSThomas Zimmermann clk_disable_unprepare(sdev->clks[i]);
29311e8f5fdSThomas Zimmermann clk_put(sdev->clks[i]);
29411e8f5fdSThomas Zimmermann }
29511e8f5fdSThomas Zimmermann }
29611e8f5fdSThomas Zimmermann }
29711e8f5fdSThomas Zimmermann
simpledrm_device_init_clocks(struct simpledrm_device * sdev)29811e8f5fdSThomas Zimmermann static int simpledrm_device_init_clocks(struct simpledrm_device *sdev)
29911e8f5fdSThomas Zimmermann {
30011e8f5fdSThomas Zimmermann struct drm_device *dev = &sdev->dev;
301802fd575SThomas Zimmermann struct platform_device *pdev = to_platform_device(dev->dev);
30211e8f5fdSThomas Zimmermann struct device_node *of_node = pdev->dev.of_node;
30311e8f5fdSThomas Zimmermann struct clk *clock;
30411e8f5fdSThomas Zimmermann unsigned int i;
30511e8f5fdSThomas Zimmermann int ret;
30611e8f5fdSThomas Zimmermann
30711e8f5fdSThomas Zimmermann if (dev_get_platdata(&pdev->dev) || !of_node)
30811e8f5fdSThomas Zimmermann return 0;
30911e8f5fdSThomas Zimmermann
31011e8f5fdSThomas Zimmermann sdev->clk_count = of_clk_get_parent_count(of_node);
31111e8f5fdSThomas Zimmermann if (!sdev->clk_count)
31211e8f5fdSThomas Zimmermann return 0;
31311e8f5fdSThomas Zimmermann
31411e8f5fdSThomas Zimmermann sdev->clks = drmm_kzalloc(dev, sdev->clk_count * sizeof(sdev->clks[0]),
31511e8f5fdSThomas Zimmermann GFP_KERNEL);
31611e8f5fdSThomas Zimmermann if (!sdev->clks)
31711e8f5fdSThomas Zimmermann return -ENOMEM;
31811e8f5fdSThomas Zimmermann
31911e8f5fdSThomas Zimmermann for (i = 0; i < sdev->clk_count; ++i) {
32011e8f5fdSThomas Zimmermann clock = of_clk_get(of_node, i);
32111e8f5fdSThomas Zimmermann if (IS_ERR(clock)) {
32211e8f5fdSThomas Zimmermann ret = PTR_ERR(clock);
32311e8f5fdSThomas Zimmermann if (ret == -EPROBE_DEFER)
32411e8f5fdSThomas Zimmermann goto err;
32511e8f5fdSThomas Zimmermann drm_err(dev, "clock %u not found: %d\n", i, ret);
32611e8f5fdSThomas Zimmermann continue;
32711e8f5fdSThomas Zimmermann }
32811e8f5fdSThomas Zimmermann ret = clk_prepare_enable(clock);
32911e8f5fdSThomas Zimmermann if (ret) {
33011e8f5fdSThomas Zimmermann drm_err(dev, "failed to enable clock %u: %d\n",
33111e8f5fdSThomas Zimmermann i, ret);
33211e8f5fdSThomas Zimmermann clk_put(clock);
3330ff9bf9fSColin Ian King continue;
33411e8f5fdSThomas Zimmermann }
33511e8f5fdSThomas Zimmermann sdev->clks[i] = clock;
33611e8f5fdSThomas Zimmermann }
33711e8f5fdSThomas Zimmermann
33811e8f5fdSThomas Zimmermann return devm_add_action_or_reset(&pdev->dev,
33911e8f5fdSThomas Zimmermann simpledrm_device_release_clocks,
34011e8f5fdSThomas Zimmermann sdev);
34111e8f5fdSThomas Zimmermann
34211e8f5fdSThomas Zimmermann err:
34311e8f5fdSThomas Zimmermann while (i) {
34411e8f5fdSThomas Zimmermann --i;
34511e8f5fdSThomas Zimmermann if (sdev->clks[i]) {
34611e8f5fdSThomas Zimmermann clk_disable_unprepare(sdev->clks[i]);
34711e8f5fdSThomas Zimmermann clk_put(sdev->clks[i]);
34811e8f5fdSThomas Zimmermann }
34911e8f5fdSThomas Zimmermann }
35011e8f5fdSThomas Zimmermann return ret;
35111e8f5fdSThomas Zimmermann }
35211e8f5fdSThomas Zimmermann #else
simpledrm_device_init_clocks(struct simpledrm_device * sdev)35311e8f5fdSThomas Zimmermann static int simpledrm_device_init_clocks(struct simpledrm_device *sdev)
35411e8f5fdSThomas Zimmermann {
35511e8f5fdSThomas Zimmermann return 0;
35611e8f5fdSThomas Zimmermann }
35711e8f5fdSThomas Zimmermann #endif
35811e8f5fdSThomas Zimmermann
35911e8f5fdSThomas Zimmermann #if defined CONFIG_OF && defined CONFIG_REGULATOR
36011e8f5fdSThomas Zimmermann
36111e8f5fdSThomas Zimmermann #define SUPPLY_SUFFIX "-supply"
36211e8f5fdSThomas Zimmermann
36311e8f5fdSThomas Zimmermann /*
36411e8f5fdSThomas Zimmermann * Regulator handling code.
36511e8f5fdSThomas Zimmermann *
36611e8f5fdSThomas Zimmermann * Here we handle the num-supplies and vin*-supply properties of our
36711e8f5fdSThomas Zimmermann * "simple-framebuffer" dt node. This is necessary so that we can make sure
36811e8f5fdSThomas Zimmermann * that any regulators needed by the display hardware that the bootloader
36911e8f5fdSThomas Zimmermann * set up for us (and for which it provided a simplefb dt node), stay up,
37011e8f5fdSThomas Zimmermann * for the life of the simplefb driver.
37111e8f5fdSThomas Zimmermann *
37211e8f5fdSThomas Zimmermann * When the driver unloads, we cleanly disable, and then release the
37311e8f5fdSThomas Zimmermann * regulators.
37411e8f5fdSThomas Zimmermann *
37511e8f5fdSThomas Zimmermann * We only complain about errors here, no action is taken as the most likely
37611e8f5fdSThomas Zimmermann * error can only happen due to a mismatch between the bootloader which set
37711e8f5fdSThomas Zimmermann * up simplefb, and the regulator definitions in the device tree. Chances are
37811e8f5fdSThomas Zimmermann * that there are no adverse effects, and if there are, a clean teardown of
37911e8f5fdSThomas Zimmermann * the fb probe will not help us much either. So just complain and carry on,
38011e8f5fdSThomas Zimmermann * and hope that the user actually gets a working fb at the end of things.
38111e8f5fdSThomas Zimmermann */
38211e8f5fdSThomas Zimmermann
simpledrm_device_release_regulators(void * res)38311e8f5fdSThomas Zimmermann static void simpledrm_device_release_regulators(void *res)
38411e8f5fdSThomas Zimmermann {
38511e8f5fdSThomas Zimmermann struct simpledrm_device *sdev = simpledrm_device_of_dev(res);
38611e8f5fdSThomas Zimmermann unsigned int i;
38711e8f5fdSThomas Zimmermann
38811e8f5fdSThomas Zimmermann for (i = 0; i < sdev->regulator_count; ++i) {
38911e8f5fdSThomas Zimmermann if (sdev->regulators[i]) {
39011e8f5fdSThomas Zimmermann regulator_disable(sdev->regulators[i]);
39111e8f5fdSThomas Zimmermann regulator_put(sdev->regulators[i]);
39211e8f5fdSThomas Zimmermann }
39311e8f5fdSThomas Zimmermann }
39411e8f5fdSThomas Zimmermann }
39511e8f5fdSThomas Zimmermann
simpledrm_device_init_regulators(struct simpledrm_device * sdev)39611e8f5fdSThomas Zimmermann static int simpledrm_device_init_regulators(struct simpledrm_device *sdev)
39711e8f5fdSThomas Zimmermann {
39811e8f5fdSThomas Zimmermann struct drm_device *dev = &sdev->dev;
399802fd575SThomas Zimmermann struct platform_device *pdev = to_platform_device(dev->dev);
40011e8f5fdSThomas Zimmermann struct device_node *of_node = pdev->dev.of_node;
40111e8f5fdSThomas Zimmermann struct property *prop;
40211e8f5fdSThomas Zimmermann struct regulator *regulator;
40311e8f5fdSThomas Zimmermann const char *p;
40411e8f5fdSThomas Zimmermann unsigned int count = 0, i = 0;
40511e8f5fdSThomas Zimmermann int ret;
40611e8f5fdSThomas Zimmermann
40711e8f5fdSThomas Zimmermann if (dev_get_platdata(&pdev->dev) || !of_node)
40811e8f5fdSThomas Zimmermann return 0;
40911e8f5fdSThomas Zimmermann
41011e8f5fdSThomas Zimmermann /* Count the number of regulator supplies */
41111e8f5fdSThomas Zimmermann for_each_property_of_node(of_node, prop) {
41211e8f5fdSThomas Zimmermann p = strstr(prop->name, SUPPLY_SUFFIX);
41311e8f5fdSThomas Zimmermann if (p && p != prop->name)
41411e8f5fdSThomas Zimmermann ++count;
41511e8f5fdSThomas Zimmermann }
41611e8f5fdSThomas Zimmermann
41711e8f5fdSThomas Zimmermann if (!count)
41811e8f5fdSThomas Zimmermann return 0;
41911e8f5fdSThomas Zimmermann
42011e8f5fdSThomas Zimmermann sdev->regulators = drmm_kzalloc(dev,
42111e8f5fdSThomas Zimmermann count * sizeof(sdev->regulators[0]),
42211e8f5fdSThomas Zimmermann GFP_KERNEL);
42311e8f5fdSThomas Zimmermann if (!sdev->regulators)
42411e8f5fdSThomas Zimmermann return -ENOMEM;
42511e8f5fdSThomas Zimmermann
42611e8f5fdSThomas Zimmermann for_each_property_of_node(of_node, prop) {
42711e8f5fdSThomas Zimmermann char name[32]; /* 32 is max size of property name */
42811e8f5fdSThomas Zimmermann size_t len;
42911e8f5fdSThomas Zimmermann
43011e8f5fdSThomas Zimmermann p = strstr(prop->name, SUPPLY_SUFFIX);
43111e8f5fdSThomas Zimmermann if (!p || p == prop->name)
43211e8f5fdSThomas Zimmermann continue;
43311e8f5fdSThomas Zimmermann len = strlen(prop->name) - strlen(SUPPLY_SUFFIX) + 1;
43411e8f5fdSThomas Zimmermann strscpy(name, prop->name, min(sizeof(name), len));
43511e8f5fdSThomas Zimmermann
43611e8f5fdSThomas Zimmermann regulator = regulator_get_optional(&pdev->dev, name);
43711e8f5fdSThomas Zimmermann if (IS_ERR(regulator)) {
43811e8f5fdSThomas Zimmermann ret = PTR_ERR(regulator);
43911e8f5fdSThomas Zimmermann if (ret == -EPROBE_DEFER)
44011e8f5fdSThomas Zimmermann goto err;
44111e8f5fdSThomas Zimmermann drm_err(dev, "regulator %s not found: %d\n",
44211e8f5fdSThomas Zimmermann name, ret);
44311e8f5fdSThomas Zimmermann continue;
44411e8f5fdSThomas Zimmermann }
44511e8f5fdSThomas Zimmermann
44611e8f5fdSThomas Zimmermann ret = regulator_enable(regulator);
44711e8f5fdSThomas Zimmermann if (ret) {
44811e8f5fdSThomas Zimmermann drm_err(dev, "failed to enable regulator %u: %d\n",
44911e8f5fdSThomas Zimmermann i, ret);
45011e8f5fdSThomas Zimmermann regulator_put(regulator);
4510ff9bf9fSColin Ian King continue;
45211e8f5fdSThomas Zimmermann }
45311e8f5fdSThomas Zimmermann
45411e8f5fdSThomas Zimmermann sdev->regulators[i++] = regulator;
45511e8f5fdSThomas Zimmermann }
45611e8f5fdSThomas Zimmermann sdev->regulator_count = i;
45711e8f5fdSThomas Zimmermann
45811e8f5fdSThomas Zimmermann return devm_add_action_or_reset(&pdev->dev,
45911e8f5fdSThomas Zimmermann simpledrm_device_release_regulators,
46011e8f5fdSThomas Zimmermann sdev);
46111e8f5fdSThomas Zimmermann
46211e8f5fdSThomas Zimmermann err:
46311e8f5fdSThomas Zimmermann while (i) {
46411e8f5fdSThomas Zimmermann --i;
46511e8f5fdSThomas Zimmermann if (sdev->regulators[i]) {
46611e8f5fdSThomas Zimmermann regulator_disable(sdev->regulators[i]);
46711e8f5fdSThomas Zimmermann regulator_put(sdev->regulators[i]);
46811e8f5fdSThomas Zimmermann }
46911e8f5fdSThomas Zimmermann }
47011e8f5fdSThomas Zimmermann return ret;
47111e8f5fdSThomas Zimmermann }
47211e8f5fdSThomas Zimmermann #else
simpledrm_device_init_regulators(struct simpledrm_device * sdev)47311e8f5fdSThomas Zimmermann static int simpledrm_device_init_regulators(struct simpledrm_device *sdev)
47411e8f5fdSThomas Zimmermann {
47511e8f5fdSThomas Zimmermann return 0;
47611e8f5fdSThomas Zimmermann }
47711e8f5fdSThomas Zimmermann #endif
47811e8f5fdSThomas Zimmermann
47961df9ca2SJanne Grunau #if defined CONFIG_OF && defined CONFIG_PM_GENERIC_DOMAINS
48061df9ca2SJanne Grunau /*
48161df9ca2SJanne Grunau * Generic power domain handling code.
48261df9ca2SJanne Grunau *
48361df9ca2SJanne Grunau * Here we handle the power-domains properties of our "simple-framebuffer"
48461df9ca2SJanne Grunau * dt node. This is only necessary if there is more than one power-domain.
48561df9ca2SJanne Grunau * A single power-domains is handled automatically by the driver core. Multiple
48661df9ca2SJanne Grunau * power-domains have to be handled by drivers since the driver core can't know
48761df9ca2SJanne Grunau * the correct power sequencing. Power sequencing is not an issue for simpledrm
48861df9ca2SJanne Grunau * since the bootloader has put the power domains already in the correct state.
48961df9ca2SJanne Grunau * simpledrm has only to ensure they remain active for its lifetime.
49061df9ca2SJanne Grunau *
49161df9ca2SJanne Grunau * When the driver unloads, we detach from the power-domains.
49261df9ca2SJanne Grunau *
49361df9ca2SJanne Grunau * We only complain about errors here, no action is taken as the most likely
49461df9ca2SJanne Grunau * error can only happen due to a mismatch between the bootloader which set
49561df9ca2SJanne Grunau * up the "simple-framebuffer" dt node, and the PM domain providers in the
49661df9ca2SJanne Grunau * device tree. Chances are that there are no adverse effects, and if there are,
49761df9ca2SJanne Grunau * a clean teardown of the fb probe will not help us much either. So just
49861df9ca2SJanne Grunau * complain and carry on, and hope that the user actually gets a working fb at
49961df9ca2SJanne Grunau * the end of things.
50061df9ca2SJanne Grunau */
simpledrm_device_detach_genpd(void * res)50161df9ca2SJanne Grunau static void simpledrm_device_detach_genpd(void *res)
50261df9ca2SJanne Grunau {
50361df9ca2SJanne Grunau int i;
50461df9ca2SJanne Grunau struct simpledrm_device *sdev = res;
50561df9ca2SJanne Grunau
50661df9ca2SJanne Grunau if (sdev->pwr_dom_count <= 1)
50761df9ca2SJanne Grunau return;
50861df9ca2SJanne Grunau
50961df9ca2SJanne Grunau for (i = sdev->pwr_dom_count - 1; i >= 0; i--) {
510c395c83aSThierry Reding if (sdev->pwr_dom_links[i])
51161df9ca2SJanne Grunau device_link_del(sdev->pwr_dom_links[i]);
51261df9ca2SJanne Grunau if (!IS_ERR_OR_NULL(sdev->pwr_dom_devs[i]))
51361df9ca2SJanne Grunau dev_pm_domain_detach(sdev->pwr_dom_devs[i], true);
51461df9ca2SJanne Grunau }
51561df9ca2SJanne Grunau }
51661df9ca2SJanne Grunau
simpledrm_device_attach_genpd(struct simpledrm_device * sdev)51761df9ca2SJanne Grunau static int simpledrm_device_attach_genpd(struct simpledrm_device *sdev)
51861df9ca2SJanne Grunau {
51961df9ca2SJanne Grunau struct device *dev = sdev->dev.dev;
52061df9ca2SJanne Grunau int i;
52161df9ca2SJanne Grunau
52261df9ca2SJanne Grunau sdev->pwr_dom_count = of_count_phandle_with_args(dev->of_node, "power-domains",
52361df9ca2SJanne Grunau "#power-domain-cells");
52461df9ca2SJanne Grunau /*
52561df9ca2SJanne Grunau * Single power-domain devices are handled by driver core nothing to do
52661df9ca2SJanne Grunau * here. The same for device nodes without "power-domains" property.
52761df9ca2SJanne Grunau */
52861df9ca2SJanne Grunau if (sdev->pwr_dom_count <= 1)
52961df9ca2SJanne Grunau return 0;
53061df9ca2SJanne Grunau
53161df9ca2SJanne Grunau sdev->pwr_dom_devs = devm_kcalloc(dev, sdev->pwr_dom_count,
53261df9ca2SJanne Grunau sizeof(*sdev->pwr_dom_devs),
53361df9ca2SJanne Grunau GFP_KERNEL);
53461df9ca2SJanne Grunau if (!sdev->pwr_dom_devs)
53561df9ca2SJanne Grunau return -ENOMEM;
53661df9ca2SJanne Grunau
53761df9ca2SJanne Grunau sdev->pwr_dom_links = devm_kcalloc(dev, sdev->pwr_dom_count,
53861df9ca2SJanne Grunau sizeof(*sdev->pwr_dom_links),
53961df9ca2SJanne Grunau GFP_KERNEL);
54061df9ca2SJanne Grunau if (!sdev->pwr_dom_links)
54161df9ca2SJanne Grunau return -ENOMEM;
54261df9ca2SJanne Grunau
54361df9ca2SJanne Grunau for (i = 0; i < sdev->pwr_dom_count; i++) {
54461df9ca2SJanne Grunau sdev->pwr_dom_devs[i] = dev_pm_domain_attach_by_id(dev, i);
54561df9ca2SJanne Grunau if (IS_ERR(sdev->pwr_dom_devs[i])) {
54661df9ca2SJanne Grunau int ret = PTR_ERR(sdev->pwr_dom_devs[i]);
54761df9ca2SJanne Grunau if (ret == -EPROBE_DEFER) {
54861df9ca2SJanne Grunau simpledrm_device_detach_genpd(sdev);
54961df9ca2SJanne Grunau return ret;
55061df9ca2SJanne Grunau }
55161df9ca2SJanne Grunau drm_warn(&sdev->dev,
55261df9ca2SJanne Grunau "pm_domain_attach_by_id(%u) failed: %d\n", i, ret);
55361df9ca2SJanne Grunau continue;
55461df9ca2SJanne Grunau }
55561df9ca2SJanne Grunau
55661df9ca2SJanne Grunau sdev->pwr_dom_links[i] = device_link_add(dev,
55761df9ca2SJanne Grunau sdev->pwr_dom_devs[i],
55861df9ca2SJanne Grunau DL_FLAG_STATELESS |
55961df9ca2SJanne Grunau DL_FLAG_PM_RUNTIME |
56061df9ca2SJanne Grunau DL_FLAG_RPM_ACTIVE);
56161df9ca2SJanne Grunau if (!sdev->pwr_dom_links[i])
56261df9ca2SJanne Grunau drm_warn(&sdev->dev, "failed to link power-domain %d\n", i);
56361df9ca2SJanne Grunau }
56461df9ca2SJanne Grunau
56561df9ca2SJanne Grunau return devm_add_action_or_reset(dev, simpledrm_device_detach_genpd, sdev);
56661df9ca2SJanne Grunau }
56761df9ca2SJanne Grunau #else
simpledrm_device_attach_genpd(struct simpledrm_device * sdev)56861df9ca2SJanne Grunau static int simpledrm_device_attach_genpd(struct simpledrm_device *sdev)
56961df9ca2SJanne Grunau {
57061df9ca2SJanne Grunau return 0;
57161df9ca2SJanne Grunau }
57261df9ca2SJanne Grunau #endif
57361df9ca2SJanne Grunau
57411e8f5fdSThomas Zimmermann /*
57511e8f5fdSThomas Zimmermann * Modesetting
57611e8f5fdSThomas Zimmermann */
57711e8f5fdSThomas Zimmermann
578de40c281SThomas Zimmermann static const uint64_t simpledrm_primary_plane_format_modifiers[] = {
57911e8f5fdSThomas Zimmermann DRM_FORMAT_MOD_LINEAR,
58011e8f5fdSThomas Zimmermann DRM_FORMAT_MOD_INVALID
58111e8f5fdSThomas Zimmermann };
58211e8f5fdSThomas Zimmermann
simpledrm_primary_plane_helper_atomic_check(struct drm_plane * plane,struct drm_atomic_state * state)583e7c814d3SThomas Zimmermann static int simpledrm_primary_plane_helper_atomic_check(struct drm_plane *plane,
584e7c814d3SThomas Zimmermann struct drm_atomic_state *state)
585e7c814d3SThomas Zimmermann {
586e7c814d3SThomas Zimmermann struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane);
587e7c814d3SThomas Zimmermann struct drm_shadow_plane_state *new_shadow_plane_state =
588e7c814d3SThomas Zimmermann to_drm_shadow_plane_state(new_plane_state);
589e7c814d3SThomas Zimmermann struct drm_framebuffer *new_fb = new_plane_state->fb;
590e7c814d3SThomas Zimmermann struct drm_crtc *new_crtc = new_plane_state->crtc;
591e7c814d3SThomas Zimmermann struct drm_crtc_state *new_crtc_state = NULL;
592e7c814d3SThomas Zimmermann struct drm_device *dev = plane->dev;
593e7c814d3SThomas Zimmermann struct simpledrm_device *sdev = simpledrm_device_of_dev(dev);
594e7c814d3SThomas Zimmermann int ret;
595e7c814d3SThomas Zimmermann
596e7c814d3SThomas Zimmermann if (new_crtc)
597e7c814d3SThomas Zimmermann new_crtc_state = drm_atomic_get_new_crtc_state(state, new_crtc);
598e7c814d3SThomas Zimmermann
599e7c814d3SThomas Zimmermann ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state,
600e7c814d3SThomas Zimmermann DRM_PLANE_NO_SCALING,
601e7c814d3SThomas Zimmermann DRM_PLANE_NO_SCALING,
602e7c814d3SThomas Zimmermann false, false);
603e7c814d3SThomas Zimmermann if (ret)
604e7c814d3SThomas Zimmermann return ret;
605e7c814d3SThomas Zimmermann else if (!new_plane_state->visible)
606e7c814d3SThomas Zimmermann return 0;
607e7c814d3SThomas Zimmermann
608e7c814d3SThomas Zimmermann if (new_fb->format != sdev->format) {
609e7c814d3SThomas Zimmermann void *buf;
610e7c814d3SThomas Zimmermann
611e7c814d3SThomas Zimmermann /* format conversion necessary; reserve buffer */
612e7c814d3SThomas Zimmermann buf = drm_format_conv_state_reserve(&new_shadow_plane_state->fmtcnv_state,
613e7c814d3SThomas Zimmermann sdev->pitch, GFP_KERNEL);
614e7c814d3SThomas Zimmermann if (!buf)
615e7c814d3SThomas Zimmermann return -ENOMEM;
616e7c814d3SThomas Zimmermann }
617e7c814d3SThomas Zimmermann
618e7c814d3SThomas Zimmermann return 0;
619e7c814d3SThomas Zimmermann }
620e7c814d3SThomas Zimmermann
simpledrm_primary_plane_helper_atomic_update(struct drm_plane * plane,struct drm_atomic_state * state)621de40c281SThomas Zimmermann static void simpledrm_primary_plane_helper_atomic_update(struct drm_plane *plane,
6220055e45dSThomas Zimmermann struct drm_atomic_state *state)
623de40c281SThomas Zimmermann {
6240055e45dSThomas Zimmermann struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
6250055e45dSThomas Zimmermann struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
626de40c281SThomas Zimmermann struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
627de40c281SThomas Zimmermann struct drm_framebuffer *fb = plane_state->fb;
628de40c281SThomas Zimmermann struct drm_device *dev = plane->dev;
629de40c281SThomas Zimmermann struct simpledrm_device *sdev = simpledrm_device_of_dev(dev);
63052a504e8SThomas Zimmermann struct drm_atomic_helper_damage_iter iter;
63152a504e8SThomas Zimmermann struct drm_rect damage;
6324b5a51e4SThomas Zimmermann int ret, idx;
6334b5a51e4SThomas Zimmermann
6344b5a51e4SThomas Zimmermann ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE);
6354b5a51e4SThomas Zimmermann if (ret)
6364b5a51e4SThomas Zimmermann return;
637de40c281SThomas Zimmermann
638de40c281SThomas Zimmermann if (!drm_dev_enter(dev, &idx))
6394b5a51e4SThomas Zimmermann goto out_drm_gem_fb_end_cpu_access;
640de40c281SThomas Zimmermann
64152a504e8SThomas Zimmermann drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
64252a504e8SThomas Zimmermann drm_atomic_for_each_plane_damage(&iter, &damage) {
64352a504e8SThomas Zimmermann struct drm_rect dst_clip = plane_state->dst;
644fa904b4cSThierry Reding struct iosys_map dst = sdev->screen_base;
64552a504e8SThomas Zimmermann
64652a504e8SThomas Zimmermann if (!drm_rect_intersect(&dst_clip, &damage))
64752a504e8SThomas Zimmermann continue;
64852a504e8SThomas Zimmermann
64971bf5587SThomas Zimmermann iosys_map_incr(&dst, drm_fb_clip_offset(sdev->pitch, sdev->format, &dst_clip));
650fa904b4cSThierry Reding drm_fb_blit(&dst, &sdev->pitch, sdev->format->format, shadow_plane_state->data,
6514cd24d4bSThomas Zimmermann fb, &damage, &shadow_plane_state->fmtcnv_state);
65252a504e8SThomas Zimmermann }
653de40c281SThomas Zimmermann
654de40c281SThomas Zimmermann drm_dev_exit(idx);
6554b5a51e4SThomas Zimmermann out_drm_gem_fb_end_cpu_access:
6564b5a51e4SThomas Zimmermann drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
657de40c281SThomas Zimmermann }
658de40c281SThomas Zimmermann
simpledrm_primary_plane_helper_atomic_disable(struct drm_plane * plane,struct drm_atomic_state * state)659de40c281SThomas Zimmermann static void simpledrm_primary_plane_helper_atomic_disable(struct drm_plane *plane,
6600055e45dSThomas Zimmermann struct drm_atomic_state *state)
661de40c281SThomas Zimmermann {
662de40c281SThomas Zimmermann struct drm_device *dev = plane->dev;
663de40c281SThomas Zimmermann struct simpledrm_device *sdev = simpledrm_device_of_dev(dev);
664de40c281SThomas Zimmermann int idx;
665de40c281SThomas Zimmermann
666de40c281SThomas Zimmermann if (!drm_dev_enter(dev, &idx))
667de40c281SThomas Zimmermann return;
668de40c281SThomas Zimmermann
669de40c281SThomas Zimmermann /* Clear screen to black if disabled */
670fa904b4cSThierry Reding iosys_map_memset(&sdev->screen_base, 0, 0, sdev->pitch * sdev->mode.vdisplay);
671de40c281SThomas Zimmermann
672de40c281SThomas Zimmermann drm_dev_exit(idx);
673de40c281SThomas Zimmermann }
674de40c281SThomas Zimmermann
simpledrm_primary_plane_helper_get_scanout_buffer(struct drm_plane * plane,struct drm_scanout_buffer * sb)67541e54853SJocelyn Falempe static int simpledrm_primary_plane_helper_get_scanout_buffer(struct drm_plane *plane,
67641e54853SJocelyn Falempe struct drm_scanout_buffer *sb)
67741e54853SJocelyn Falempe {
67841e54853SJocelyn Falempe struct simpledrm_device *sdev = simpledrm_device_of_dev(plane->dev);
67941e54853SJocelyn Falempe
68041e54853SJocelyn Falempe sb->width = sdev->mode.hdisplay;
68141e54853SJocelyn Falempe sb->height = sdev->mode.vdisplay;
68241e54853SJocelyn Falempe sb->format = sdev->format;
68341e54853SJocelyn Falempe sb->pitch[0] = sdev->pitch;
68441e54853SJocelyn Falempe sb->map[0] = sdev->screen_base;
68541e54853SJocelyn Falempe
68641e54853SJocelyn Falempe return 0;
68741e54853SJocelyn Falempe }
68841e54853SJocelyn Falempe
689de40c281SThomas Zimmermann static const struct drm_plane_helper_funcs simpledrm_primary_plane_helper_funcs = {
690de40c281SThomas Zimmermann DRM_GEM_SHADOW_PLANE_HELPER_FUNCS,
691e7c814d3SThomas Zimmermann .atomic_check = simpledrm_primary_plane_helper_atomic_check,
692de40c281SThomas Zimmermann .atomic_update = simpledrm_primary_plane_helper_atomic_update,
693de40c281SThomas Zimmermann .atomic_disable = simpledrm_primary_plane_helper_atomic_disable,
69441e54853SJocelyn Falempe .get_scanout_buffer = simpledrm_primary_plane_helper_get_scanout_buffer,
695de40c281SThomas Zimmermann };
696de40c281SThomas Zimmermann
697de40c281SThomas Zimmermann static const struct drm_plane_funcs simpledrm_primary_plane_funcs = {
698de40c281SThomas Zimmermann .update_plane = drm_atomic_helper_update_plane,
699de40c281SThomas Zimmermann .disable_plane = drm_atomic_helper_disable_plane,
700de40c281SThomas Zimmermann .destroy = drm_plane_cleanup,
701de40c281SThomas Zimmermann DRM_GEM_SHADOW_PLANE_FUNCS,
702de40c281SThomas Zimmermann };
703de40c281SThomas Zimmermann
simpledrm_crtc_helper_mode_valid(struct drm_crtc * crtc,const struct drm_display_mode * mode)704de40c281SThomas Zimmermann static enum drm_mode_status simpledrm_crtc_helper_mode_valid(struct drm_crtc *crtc,
705de40c281SThomas Zimmermann const struct drm_display_mode *mode)
706de40c281SThomas Zimmermann {
707de40c281SThomas Zimmermann struct simpledrm_device *sdev = simpledrm_device_of_dev(crtc->dev);
708de40c281SThomas Zimmermann
709216b9bbaSThomas Zimmermann return drm_crtc_helper_mode_valid_fixed(crtc, mode, &sdev->mode);
710de40c281SThomas Zimmermann }
711de40c281SThomas Zimmermann
712de40c281SThomas Zimmermann /*
713de40c281SThomas Zimmermann * The CRTC is always enabled. Screen updates are performed by
714de40c281SThomas Zimmermann * the primary plane's atomic_update function. Disabling clears
715de40c281SThomas Zimmermann * the screen in the primary plane's atomic_disable function.
716de40c281SThomas Zimmermann */
717de40c281SThomas Zimmermann static const struct drm_crtc_helper_funcs simpledrm_crtc_helper_funcs = {
718de40c281SThomas Zimmermann .mode_valid = simpledrm_crtc_helper_mode_valid,
7197fed7fa3SJavier Martinez Canillas .atomic_check = drm_crtc_helper_atomic_check,
720de40c281SThomas Zimmermann };
721de40c281SThomas Zimmermann
722de40c281SThomas Zimmermann static const struct drm_crtc_funcs simpledrm_crtc_funcs = {
723de40c281SThomas Zimmermann .reset = drm_atomic_helper_crtc_reset,
724de40c281SThomas Zimmermann .destroy = drm_crtc_cleanup,
725de40c281SThomas Zimmermann .set_config = drm_atomic_helper_set_config,
726de40c281SThomas Zimmermann .page_flip = drm_atomic_helper_page_flip,
727de40c281SThomas Zimmermann .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
728de40c281SThomas Zimmermann .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
729de40c281SThomas Zimmermann };
730de40c281SThomas Zimmermann
731de40c281SThomas Zimmermann static const struct drm_encoder_funcs simpledrm_encoder_funcs = {
732de40c281SThomas Zimmermann .destroy = drm_encoder_cleanup,
733de40c281SThomas Zimmermann };
734de40c281SThomas Zimmermann
simpledrm_connector_helper_get_modes(struct drm_connector * connector)73511e8f5fdSThomas Zimmermann static int simpledrm_connector_helper_get_modes(struct drm_connector *connector)
73611e8f5fdSThomas Zimmermann {
73711e8f5fdSThomas Zimmermann struct simpledrm_device *sdev = simpledrm_device_of_dev(connector->dev);
73811e8f5fdSThomas Zimmermann
739d25654b3SThomas Zimmermann return drm_connector_helper_get_modes_fixed(connector, &sdev->mode);
74011e8f5fdSThomas Zimmermann }
74111e8f5fdSThomas Zimmermann
74211e8f5fdSThomas Zimmermann static const struct drm_connector_helper_funcs simpledrm_connector_helper_funcs = {
74311e8f5fdSThomas Zimmermann .get_modes = simpledrm_connector_helper_get_modes,
74411e8f5fdSThomas Zimmermann };
74511e8f5fdSThomas Zimmermann
74611e8f5fdSThomas Zimmermann static const struct drm_connector_funcs simpledrm_connector_funcs = {
74711e8f5fdSThomas Zimmermann .reset = drm_atomic_helper_connector_reset,
74811e8f5fdSThomas Zimmermann .fill_modes = drm_helper_probe_single_connector_modes,
74911e8f5fdSThomas Zimmermann .destroy = drm_connector_cleanup,
75011e8f5fdSThomas Zimmermann .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
75111e8f5fdSThomas Zimmermann .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
75211e8f5fdSThomas Zimmermann };
75311e8f5fdSThomas Zimmermann
75411e8f5fdSThomas Zimmermann static const struct drm_mode_config_funcs simpledrm_mode_config_funcs = {
75511e8f5fdSThomas Zimmermann .fb_create = drm_gem_fb_create_with_dirty,
75611e8f5fdSThomas Zimmermann .atomic_check = drm_atomic_helper_check,
75711e8f5fdSThomas Zimmermann .atomic_commit = drm_atomic_helper_commit,
75811e8f5fdSThomas Zimmermann };
75911e8f5fdSThomas Zimmermann
760c25b6960SThomas Zimmermann /*
761c25b6960SThomas Zimmermann * Init / Cleanup
762c25b6960SThomas Zimmermann */
763c25b6960SThomas Zimmermann
simpledrm_mode(unsigned int width,unsigned int height,unsigned int width_mm,unsigned int height_mm)764c25b6960SThomas Zimmermann static struct drm_display_mode simpledrm_mode(unsigned int width,
7652a6d731aSRayyan Ansari unsigned int height,
7662a6d731aSRayyan Ansari unsigned int width_mm,
7672a6d731aSRayyan Ansari unsigned int height_mm)
768c25b6960SThomas Zimmermann {
769385d1bbaSThomas Zimmermann const struct drm_display_mode mode = {
7702a6d731aSRayyan Ansari DRM_MODE_INIT(60, width, height, width_mm, height_mm)
771385d1bbaSThomas Zimmermann };
772c25b6960SThomas Zimmermann
773c25b6960SThomas Zimmermann return mode;
774c25b6960SThomas Zimmermann }
775c25b6960SThomas Zimmermann
simpledrm_device_create(struct drm_driver * drv,struct platform_device * pdev)776c25b6960SThomas Zimmermann static struct simpledrm_device *simpledrm_device_create(struct drm_driver *drv,
777c25b6960SThomas Zimmermann struct platform_device *pdev)
77811e8f5fdSThomas Zimmermann {
779c25b6960SThomas Zimmermann const struct simplefb_platform_data *pd = dev_get_platdata(&pdev->dev);
780c25b6960SThomas Zimmermann struct device_node *of_node = pdev->dev.of_node;
781c25b6960SThomas Zimmermann struct simpledrm_device *sdev;
782c25b6960SThomas Zimmermann struct drm_device *dev;
783c25b6960SThomas Zimmermann int width, height, stride;
7842a6d731aSRayyan Ansari int width_mm = 0, height_mm = 0;
7852a6d731aSRayyan Ansari struct device_node *panel_node;
786c25b6960SThomas Zimmermann const struct drm_format_info *format;
7879a10c7e6SThierry Reding struct resource *res, *mem = NULL;
788de40c281SThomas Zimmermann struct drm_plane *primary_plane;
789de40c281SThomas Zimmermann struct drm_crtc *crtc;
790de40c281SThomas Zimmermann struct drm_encoder *encoder;
791c25b6960SThomas Zimmermann struct drm_connector *connector;
7929239f3e1SThomas Zimmermann unsigned long max_width, max_height;
79311e8f5fdSThomas Zimmermann size_t nformats;
79411e8f5fdSThomas Zimmermann int ret;
79511e8f5fdSThomas Zimmermann
796c25b6960SThomas Zimmermann sdev = devm_drm_dev_alloc(&pdev->dev, drv, struct simpledrm_device, dev);
79711e8f5fdSThomas Zimmermann if (IS_ERR(sdev))
79811e8f5fdSThomas Zimmermann return ERR_CAST(sdev);
799c25b6960SThomas Zimmermann dev = &sdev->dev;
80011e8f5fdSThomas Zimmermann platform_set_drvdata(pdev, sdev);
80111e8f5fdSThomas Zimmermann
802c25b6960SThomas Zimmermann /*
803c25b6960SThomas Zimmermann * Hardware settings
804c25b6960SThomas Zimmermann */
805c25b6960SThomas Zimmermann
80611e8f5fdSThomas Zimmermann ret = simpledrm_device_init_clocks(sdev);
80711e8f5fdSThomas Zimmermann if (ret)
80811e8f5fdSThomas Zimmermann return ERR_PTR(ret);
80911e8f5fdSThomas Zimmermann ret = simpledrm_device_init_regulators(sdev);
81011e8f5fdSThomas Zimmermann if (ret)
81111e8f5fdSThomas Zimmermann return ERR_PTR(ret);
81261df9ca2SJanne Grunau ret = simpledrm_device_attach_genpd(sdev);
81361df9ca2SJanne Grunau if (ret)
81461df9ca2SJanne Grunau return ERR_PTR(ret);
815c25b6960SThomas Zimmermann
816c25b6960SThomas Zimmermann if (pd) {
817c25b6960SThomas Zimmermann width = simplefb_get_width_pd(dev, pd);
818c25b6960SThomas Zimmermann if (width < 0)
819c25b6960SThomas Zimmermann return ERR_PTR(width);
820c25b6960SThomas Zimmermann height = simplefb_get_height_pd(dev, pd);
821c25b6960SThomas Zimmermann if (height < 0)
822c25b6960SThomas Zimmermann return ERR_PTR(height);
823c25b6960SThomas Zimmermann stride = simplefb_get_stride_pd(dev, pd);
824c25b6960SThomas Zimmermann if (stride < 0)
825c25b6960SThomas Zimmermann return ERR_PTR(stride);
826c25b6960SThomas Zimmermann format = simplefb_get_format_pd(dev, pd);
827c25b6960SThomas Zimmermann if (IS_ERR(format))
828c25b6960SThomas Zimmermann return ERR_CAST(format);
829c25b6960SThomas Zimmermann } else if (of_node) {
830c25b6960SThomas Zimmermann width = simplefb_get_width_of(dev, of_node);
831c25b6960SThomas Zimmermann if (width < 0)
832c25b6960SThomas Zimmermann return ERR_PTR(width);
833c25b6960SThomas Zimmermann height = simplefb_get_height_of(dev, of_node);
834c25b6960SThomas Zimmermann if (height < 0)
835c25b6960SThomas Zimmermann return ERR_PTR(height);
836c25b6960SThomas Zimmermann stride = simplefb_get_stride_of(dev, of_node);
837c25b6960SThomas Zimmermann if (stride < 0)
838c25b6960SThomas Zimmermann return ERR_PTR(stride);
839c25b6960SThomas Zimmermann format = simplefb_get_format_of(dev, of_node);
840c25b6960SThomas Zimmermann if (IS_ERR(format))
841c25b6960SThomas Zimmermann return ERR_CAST(format);
8429a10c7e6SThierry Reding mem = simplefb_get_memory_of(dev, of_node);
8439a10c7e6SThierry Reding if (IS_ERR(mem))
8449a10c7e6SThierry Reding return ERR_CAST(mem);
8452a6d731aSRayyan Ansari panel_node = of_parse_phandle(of_node, "panel", 0);
8462a6d731aSRayyan Ansari if (panel_node) {
8472a6d731aSRayyan Ansari simplefb_read_u32_of(dev, panel_node, "width-mm", &width_mm);
8482a6d731aSRayyan Ansari simplefb_read_u32_of(dev, panel_node, "height-mm", &height_mm);
8492a6d731aSRayyan Ansari of_node_put(panel_node);
8502a6d731aSRayyan Ansari }
851c25b6960SThomas Zimmermann } else {
852c25b6960SThomas Zimmermann drm_err(dev, "no simplefb configuration found\n");
853c25b6960SThomas Zimmermann return ERR_PTR(-ENODEV);
854c25b6960SThomas Zimmermann }
8557bfa5c7bSThomas Zimmermann if (!stride) {
8567bfa5c7bSThomas Zimmermann stride = drm_format_info_min_pitch(format, 0, width);
8577bfa5c7bSThomas Zimmermann if (drm_WARN_ON(dev, !stride))
8587bfa5c7bSThomas Zimmermann return ERR_PTR(-EINVAL);
8597bfa5c7bSThomas Zimmermann }
860fd9e3169SThomas Zimmermann
8612a6d731aSRayyan Ansari /*
8622a6d731aSRayyan Ansari * Assume a monitor resolution of 96 dpi if physical dimensions
8632a6d731aSRayyan Ansari * are not specified to get a somewhat reasonable screen size.
8642a6d731aSRayyan Ansari */
8652a6d731aSRayyan Ansari if (!width_mm)
8662a6d731aSRayyan Ansari width_mm = DRM_MODE_RES_MM(width, 96ul);
8672a6d731aSRayyan Ansari if (!height_mm)
8682a6d731aSRayyan Ansari height_mm = DRM_MODE_RES_MM(height, 96ul);
8692a6d731aSRayyan Ansari
8702a6d731aSRayyan Ansari sdev->mode = simpledrm_mode(width, height, width_mm, height_mm);
871c25b6960SThomas Zimmermann sdev->format = format;
872c25b6960SThomas Zimmermann sdev->pitch = stride;
873c25b6960SThomas Zimmermann
874c25b6960SThomas Zimmermann drm_dbg(dev, "display mode={" DRM_MODE_FMT "}\n", DRM_MODE_ARG(&sdev->mode));
875c25b6960SThomas Zimmermann drm_dbg(dev, "framebuffer format=%p4cc, size=%dx%d, stride=%d byte\n",
876c25b6960SThomas Zimmermann &format->format, width, height, stride);
877c25b6960SThomas Zimmermann
878c25b6960SThomas Zimmermann /*
879c25b6960SThomas Zimmermann * Memory management
880c25b6960SThomas Zimmermann */
881c25b6960SThomas Zimmermann
8829a10c7e6SThierry Reding if (mem) {
8839a10c7e6SThierry Reding void *screen_base;
8849a10c7e6SThierry Reding
88540f853ebSThomas Zimmermann ret = devm_aperture_acquire_for_platform_device(pdev, mem->start,
88640f853ebSThomas Zimmermann resource_size(mem));
8879a10c7e6SThierry Reding if (ret) {
8889a10c7e6SThierry Reding drm_err(dev, "could not acquire memory range %pr: %d\n", mem, ret);
8899a10c7e6SThierry Reding return ERR_PTR(ret);
8909a10c7e6SThierry Reding }
8919a10c7e6SThierry Reding
8929a10c7e6SThierry Reding drm_dbg(dev, "using system memory framebuffer at %pr\n", mem);
8939a10c7e6SThierry Reding
8949a10c7e6SThierry Reding screen_base = devm_memremap(dev->dev, mem->start, resource_size(mem), MEMREMAP_WC);
895e566507bSDan Carpenter if (IS_ERR(screen_base))
896e566507bSDan Carpenter return screen_base;
8979a10c7e6SThierry Reding
8989a10c7e6SThierry Reding iosys_map_set_vaddr(&sdev->screen_base, screen_base);
8999a10c7e6SThierry Reding } else {
9009a10c7e6SThierry Reding void __iomem *screen_base;
9019a10c7e6SThierry Reding
902c25b6960SThomas Zimmermann res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
903c25b6960SThomas Zimmermann if (!res)
904c25b6960SThomas Zimmermann return ERR_PTR(-EINVAL);
905c25b6960SThomas Zimmermann
90640f853ebSThomas Zimmermann ret = devm_aperture_acquire_for_platform_device(pdev, res->start,
90740f853ebSThomas Zimmermann resource_size(res));
908c25b6960SThomas Zimmermann if (ret) {
909c1165df2SJoey Gouly drm_err(dev, "could not acquire memory range %pr: %d\n", res, ret);
910c25b6960SThomas Zimmermann return ERR_PTR(ret);
911c25b6960SThomas Zimmermann }
912c25b6960SThomas Zimmermann
9139a10c7e6SThierry Reding drm_dbg(dev, "using I/O memory framebuffer at %pr\n", res);
9149a10c7e6SThierry Reding
9159a10c7e6SThierry Reding mem = devm_request_mem_region(&pdev->dev, res->start, resource_size(res),
9169a10c7e6SThierry Reding drv->name);
917c25b6960SThomas Zimmermann if (!mem) {
918c25b6960SThomas Zimmermann /*
919c25b6960SThomas Zimmermann * We cannot make this fatal. Sometimes this comes from magic
920c25b6960SThomas Zimmermann * spaces our resource handlers simply don't know about. Use
921c25b6960SThomas Zimmermann * the I/O-memory resource as-is and try to map that instead.
922c25b6960SThomas Zimmermann */
923c25b6960SThomas Zimmermann drm_warn(dev, "could not acquire memory region %pr\n", res);
924c25b6960SThomas Zimmermann mem = res;
925c25b6960SThomas Zimmermann }
926c25b6960SThomas Zimmermann
927c25b6960SThomas Zimmermann screen_base = devm_ioremap_wc(&pdev->dev, mem->start, resource_size(mem));
928c25b6960SThomas Zimmermann if (!screen_base)
929c25b6960SThomas Zimmermann return ERR_PTR(-ENOMEM);
930fa904b4cSThierry Reding
931fa904b4cSThierry Reding iosys_map_set_vaddr_iomem(&sdev->screen_base, screen_base);
9329a10c7e6SThierry Reding }
933c25b6960SThomas Zimmermann
934c25b6960SThomas Zimmermann /*
935c25b6960SThomas Zimmermann * Modesetting
936c25b6960SThomas Zimmermann */
937c25b6960SThomas Zimmermann
938c25b6960SThomas Zimmermann ret = drmm_mode_config_init(dev);
93911e8f5fdSThomas Zimmermann if (ret)
94011e8f5fdSThomas Zimmermann return ERR_PTR(ret);
941c25b6960SThomas Zimmermann
942c25b6960SThomas Zimmermann max_width = max_t(unsigned long, width, DRM_SHADOW_PLANE_MAX_WIDTH);
943c25b6960SThomas Zimmermann max_height = max_t(unsigned long, height, DRM_SHADOW_PLANE_MAX_HEIGHT);
944c25b6960SThomas Zimmermann
945c25b6960SThomas Zimmermann dev->mode_config.min_width = width;
946c25b6960SThomas Zimmermann dev->mode_config.max_width = max_width;
947c25b6960SThomas Zimmermann dev->mode_config.min_height = height;
948c25b6960SThomas Zimmermann dev->mode_config.max_height = max_height;
94988f19f8bSThomas Zimmermann dev->mode_config.preferred_depth = format->depth;
950c25b6960SThomas Zimmermann dev->mode_config.funcs = &simpledrm_mode_config_funcs;
951c25b6960SThomas Zimmermann
952de40c281SThomas Zimmermann /* Primary plane */
953de40c281SThomas Zimmermann
9544a85b0b5SThomas Zimmermann nformats = drm_fb_build_fourcc_list(dev, &format->format, 1,
9554a85b0b5SThomas Zimmermann sdev->formats, ARRAY_SIZE(sdev->formats));
956de40c281SThomas Zimmermann
957de40c281SThomas Zimmermann primary_plane = &sdev->primary_plane;
958de40c281SThomas Zimmermann ret = drm_universal_plane_init(dev, primary_plane, 0, &simpledrm_primary_plane_funcs,
9594a85b0b5SThomas Zimmermann sdev->formats, nformats,
960de40c281SThomas Zimmermann simpledrm_primary_plane_format_modifiers,
961de40c281SThomas Zimmermann DRM_PLANE_TYPE_PRIMARY, NULL);
962de40c281SThomas Zimmermann if (ret)
963de40c281SThomas Zimmermann return ERR_PTR(ret);
964de40c281SThomas Zimmermann drm_plane_helper_add(primary_plane, &simpledrm_primary_plane_helper_funcs);
965de40c281SThomas Zimmermann drm_plane_enable_fb_damage_clips(primary_plane);
966de40c281SThomas Zimmermann
967de40c281SThomas Zimmermann /* CRTC */
968de40c281SThomas Zimmermann
969de40c281SThomas Zimmermann crtc = &sdev->crtc;
970de40c281SThomas Zimmermann ret = drm_crtc_init_with_planes(dev, crtc, primary_plane, NULL,
971de40c281SThomas Zimmermann &simpledrm_crtc_funcs, NULL);
972de40c281SThomas Zimmermann if (ret)
973de40c281SThomas Zimmermann return ERR_PTR(ret);
974de40c281SThomas Zimmermann drm_crtc_helper_add(crtc, &simpledrm_crtc_helper_funcs);
975de40c281SThomas Zimmermann
976de40c281SThomas Zimmermann /* Encoder */
977de40c281SThomas Zimmermann
978de40c281SThomas Zimmermann encoder = &sdev->encoder;
979de40c281SThomas Zimmermann ret = drm_encoder_init(dev, encoder, &simpledrm_encoder_funcs,
980de40c281SThomas Zimmermann DRM_MODE_ENCODER_NONE, NULL);
981de40c281SThomas Zimmermann if (ret)
982de40c281SThomas Zimmermann return ERR_PTR(ret);
983de40c281SThomas Zimmermann encoder->possible_crtcs = drm_crtc_mask(crtc);
984de40c281SThomas Zimmermann
985de40c281SThomas Zimmermann /* Connector */
986de40c281SThomas Zimmermann
987c25b6960SThomas Zimmermann connector = &sdev->connector;
988c25b6960SThomas Zimmermann ret = drm_connector_init(dev, connector, &simpledrm_connector_funcs,
989c25b6960SThomas Zimmermann DRM_MODE_CONNECTOR_Unknown);
99011e8f5fdSThomas Zimmermann if (ret)
99111e8f5fdSThomas Zimmermann return ERR_PTR(ret);
992c25b6960SThomas Zimmermann drm_connector_helper_add(connector, &simpledrm_connector_helper_funcs);
993c25b6960SThomas Zimmermann drm_connector_set_panel_orientation_with_quirk(connector,
994c25b6960SThomas Zimmermann DRM_MODE_PANEL_ORIENTATION_UNKNOWN,
995c25b6960SThomas Zimmermann width, height);
996c25b6960SThomas Zimmermann
997de40c281SThomas Zimmermann ret = drm_connector_attach_encoder(connector, encoder);
99811e8f5fdSThomas Zimmermann if (ret)
99911e8f5fdSThomas Zimmermann return ERR_PTR(ret);
100011e8f5fdSThomas Zimmermann
1001c25b6960SThomas Zimmermann drm_mode_config_reset(dev);
1002c25b6960SThomas Zimmermann
100311e8f5fdSThomas Zimmermann return sdev;
100411e8f5fdSThomas Zimmermann }
100511e8f5fdSThomas Zimmermann
100611e8f5fdSThomas Zimmermann /*
100711e8f5fdSThomas Zimmermann * DRM driver
100811e8f5fdSThomas Zimmermann */
100911e8f5fdSThomas Zimmermann
101011e8f5fdSThomas Zimmermann DEFINE_DRM_GEM_FOPS(simpledrm_fops);
101111e8f5fdSThomas Zimmermann
101211e8f5fdSThomas Zimmermann static struct drm_driver simpledrm_driver = {
101311e8f5fdSThomas Zimmermann DRM_GEM_SHMEM_DRIVER_OPS,
101447e35599SThomas Zimmermann DRM_FBDEV_SHMEM_DRIVER_OPS,
101511e8f5fdSThomas Zimmermann .name = DRIVER_NAME,
101611e8f5fdSThomas Zimmermann .desc = DRIVER_DESC,
101711e8f5fdSThomas Zimmermann .major = DRIVER_MAJOR,
101811e8f5fdSThomas Zimmermann .minor = DRIVER_MINOR,
101911e8f5fdSThomas Zimmermann .driver_features = DRIVER_ATOMIC | DRIVER_GEM | DRIVER_MODESET,
102011e8f5fdSThomas Zimmermann .fops = &simpledrm_fops,
102111e8f5fdSThomas Zimmermann };
102211e8f5fdSThomas Zimmermann
102311e8f5fdSThomas Zimmermann /*
102411e8f5fdSThomas Zimmermann * Platform driver
102511e8f5fdSThomas Zimmermann */
102611e8f5fdSThomas Zimmermann
simpledrm_probe(struct platform_device * pdev)102711e8f5fdSThomas Zimmermann static int simpledrm_probe(struct platform_device *pdev)
102811e8f5fdSThomas Zimmermann {
102911e8f5fdSThomas Zimmermann struct simpledrm_device *sdev;
103011e8f5fdSThomas Zimmermann struct drm_device *dev;
103111e8f5fdSThomas Zimmermann int ret;
103211e8f5fdSThomas Zimmermann
103311e8f5fdSThomas Zimmermann sdev = simpledrm_device_create(&simpledrm_driver, pdev);
103411e8f5fdSThomas Zimmermann if (IS_ERR(sdev))
103511e8f5fdSThomas Zimmermann return PTR_ERR(sdev);
103611e8f5fdSThomas Zimmermann dev = &sdev->dev;
103711e8f5fdSThomas Zimmermann
103811e8f5fdSThomas Zimmermann ret = drm_dev_register(dev, 0);
103911e8f5fdSThomas Zimmermann if (ret)
104011e8f5fdSThomas Zimmermann return ret;
104111e8f5fdSThomas Zimmermann
104247e35599SThomas Zimmermann drm_client_setup(dev, sdev->format);
104311e8f5fdSThomas Zimmermann
104411e8f5fdSThomas Zimmermann return 0;
104511e8f5fdSThomas Zimmermann }
104611e8f5fdSThomas Zimmermann
simpledrm_remove(struct platform_device * pdev)104784e6da7aSUwe Kleine-König static void simpledrm_remove(struct platform_device *pdev)
104811e8f5fdSThomas Zimmermann {
104911e8f5fdSThomas Zimmermann struct simpledrm_device *sdev = platform_get_drvdata(pdev);
105011e8f5fdSThomas Zimmermann struct drm_device *dev = &sdev->dev;
105111e8f5fdSThomas Zimmermann
10524aae79f7SThomas Zimmermann drm_dev_unplug(dev);
105311e8f5fdSThomas Zimmermann }
105411e8f5fdSThomas Zimmermann
105511e8f5fdSThomas Zimmermann static const struct of_device_id simpledrm_of_match_table[] = {
105611e8f5fdSThomas Zimmermann { .compatible = "simple-framebuffer", },
105711e8f5fdSThomas Zimmermann { },
105811e8f5fdSThomas Zimmermann };
105911e8f5fdSThomas Zimmermann MODULE_DEVICE_TABLE(of, simpledrm_of_match_table);
106011e8f5fdSThomas Zimmermann
106111e8f5fdSThomas Zimmermann static struct platform_driver simpledrm_platform_driver = {
106211e8f5fdSThomas Zimmermann .driver = {
106311e8f5fdSThomas Zimmermann .name = "simple-framebuffer", /* connect to sysfb */
106411e8f5fdSThomas Zimmermann .of_match_table = simpledrm_of_match_table,
106511e8f5fdSThomas Zimmermann },
106611e8f5fdSThomas Zimmermann .probe = simpledrm_probe,
106784e6da7aSUwe Kleine-König .remove = simpledrm_remove,
106811e8f5fdSThomas Zimmermann };
106911e8f5fdSThomas Zimmermann
107011e8f5fdSThomas Zimmermann module_platform_driver(simpledrm_platform_driver);
107111e8f5fdSThomas Zimmermann
107211e8f5fdSThomas Zimmermann MODULE_DESCRIPTION(DRIVER_DESC);
107311e8f5fdSThomas Zimmermann MODULE_LICENSE("GPL v2");
1074