xref: /linux-6.15/drivers/gpu/drm/tiny/simpledrm.c (revision cb2e1c21)
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