xref: /linux/drivers/gpu/drm/sysfb/efidrm.c (revision 305396ac7782c2f1aba407bab06e9da68588ffef)
132ae90c6SThomas Zimmermann // SPDX-License-Identifier: GPL-2.0-only
232ae90c6SThomas Zimmermann 
332ae90c6SThomas Zimmermann #include <linux/aperture.h>
432ae90c6SThomas Zimmermann #include <linux/efi.h>
532ae90c6SThomas Zimmermann #include <linux/limits.h>
632ae90c6SThomas Zimmermann #include <linux/platform_device.h>
732ae90c6SThomas Zimmermann #include <linux/screen_info.h>
832ae90c6SThomas Zimmermann 
932ae90c6SThomas Zimmermann #include <drm/clients/drm_client_setup.h>
1032ae90c6SThomas Zimmermann #include <drm/drm_atomic.h>
1132ae90c6SThomas Zimmermann #include <drm/drm_atomic_state_helper.h>
1232ae90c6SThomas Zimmermann #include <drm/drm_connector.h>
1332ae90c6SThomas Zimmermann #include <drm/drm_damage_helper.h>
1432ae90c6SThomas Zimmermann #include <drm/drm_device.h>
1532ae90c6SThomas Zimmermann #include <drm/drm_drv.h>
16*305396acSThomas Zimmermann #include <drm/drm_edid.h>
1732ae90c6SThomas Zimmermann #include <drm/drm_fbdev_shmem.h>
1832ae90c6SThomas Zimmermann #include <drm/drm_framebuffer.h>
1932ae90c6SThomas Zimmermann #include <drm/drm_gem_atomic_helper.h>
2032ae90c6SThomas Zimmermann #include <drm/drm_gem_framebuffer_helper.h>
2132ae90c6SThomas Zimmermann #include <drm/drm_gem_shmem_helper.h>
2232ae90c6SThomas Zimmermann #include <drm/drm_managed.h>
2332ae90c6SThomas Zimmermann #include <drm/drm_modeset_helper_vtables.h>
2432ae90c6SThomas Zimmermann #include <drm/drm_probe_helper.h>
2532ae90c6SThomas Zimmermann 
26*305396acSThomas Zimmermann #include <video/edid.h>
2732ae90c6SThomas Zimmermann #include <video/pixel_format.h>
2832ae90c6SThomas Zimmermann 
2932ae90c6SThomas Zimmermann #include "drm_sysfb_helper.h"
3032ae90c6SThomas Zimmermann 
3132ae90c6SThomas Zimmermann #define DRIVER_NAME	"efidrm"
3232ae90c6SThomas Zimmermann #define DRIVER_DESC	"DRM driver for EFI platform devices"
3332ae90c6SThomas Zimmermann #define DRIVER_MAJOR	1
3432ae90c6SThomas Zimmermann #define DRIVER_MINOR	0
3532ae90c6SThomas Zimmermann 
3632ae90c6SThomas Zimmermann static int efidrm_get_validated_int(struct drm_device *dev, const char *name,
3732ae90c6SThomas Zimmermann 				    u64 value, u32 max)
3832ae90c6SThomas Zimmermann {
3932ae90c6SThomas Zimmermann 	if (max > INT_MAX)
4032ae90c6SThomas Zimmermann 		max = INT_MAX;
4132ae90c6SThomas Zimmermann 	if (value > max) {
4232ae90c6SThomas Zimmermann 		drm_err(dev, "%s of %llu exceeds maximum of %u\n", name, value, max);
4332ae90c6SThomas Zimmermann 		return -EINVAL;
4432ae90c6SThomas Zimmermann 	}
4532ae90c6SThomas Zimmermann 	return value;
4632ae90c6SThomas Zimmermann }
4732ae90c6SThomas Zimmermann 
4832ae90c6SThomas Zimmermann static int efidrm_get_validated_int0(struct drm_device *dev, const char *name,
4932ae90c6SThomas Zimmermann 				     u64 value, u32 max)
5032ae90c6SThomas Zimmermann {
5132ae90c6SThomas Zimmermann 	if (!value) {
5232ae90c6SThomas Zimmermann 		drm_err(dev, "%s of 0 not allowed\n", name);
5332ae90c6SThomas Zimmermann 		return -EINVAL;
5432ae90c6SThomas Zimmermann 	}
5532ae90c6SThomas Zimmermann 	return efidrm_get_validated_int(dev, name, value, max);
5632ae90c6SThomas Zimmermann }
5732ae90c6SThomas Zimmermann 
5832ae90c6SThomas Zimmermann static s64 efidrm_get_validated_size0(struct drm_device *dev, const char *name,
5932ae90c6SThomas Zimmermann 				      u64 value, u64 max)
6032ae90c6SThomas Zimmermann {
6132ae90c6SThomas Zimmermann 	if (!value) {
6232ae90c6SThomas Zimmermann 		drm_err(dev, "%s of 0 not allowed\n", name);
6332ae90c6SThomas Zimmermann 		return -EINVAL;
6432ae90c6SThomas Zimmermann 	} else if (value > max) {
6532ae90c6SThomas Zimmermann 		drm_err(dev, "%s of %llu exceeds maximum of %llu\n", name, value, max);
6632ae90c6SThomas Zimmermann 		return -EINVAL;
6732ae90c6SThomas Zimmermann 	}
6832ae90c6SThomas Zimmermann 	return value;
6932ae90c6SThomas Zimmermann }
7032ae90c6SThomas Zimmermann 
7132ae90c6SThomas Zimmermann static int efidrm_get_width_si(struct drm_device *dev, const struct screen_info *si)
7232ae90c6SThomas Zimmermann {
7332ae90c6SThomas Zimmermann 	return efidrm_get_validated_int0(dev, "width", si->lfb_width, U16_MAX);
7432ae90c6SThomas Zimmermann }
7532ae90c6SThomas Zimmermann 
7632ae90c6SThomas Zimmermann static int efidrm_get_height_si(struct drm_device *dev, const struct screen_info *si)
7732ae90c6SThomas Zimmermann {
7832ae90c6SThomas Zimmermann 	return efidrm_get_validated_int0(dev, "height", si->lfb_height, U16_MAX);
7932ae90c6SThomas Zimmermann }
8032ae90c6SThomas Zimmermann 
8132ae90c6SThomas Zimmermann static struct resource *efidrm_get_memory_si(struct drm_device *dev,
8232ae90c6SThomas Zimmermann 					     const struct screen_info *si,
8332ae90c6SThomas Zimmermann 					     struct resource *res)
8432ae90c6SThomas Zimmermann {
8532ae90c6SThomas Zimmermann 	ssize_t	num;
8632ae90c6SThomas Zimmermann 
8732ae90c6SThomas Zimmermann 	num = screen_info_resources(si, res, 1);
8832ae90c6SThomas Zimmermann 	if (!num) {
8932ae90c6SThomas Zimmermann 		drm_err(dev, "memory resource not found\n");
9032ae90c6SThomas Zimmermann 		return NULL;
9132ae90c6SThomas Zimmermann 	}
9232ae90c6SThomas Zimmermann 
9332ae90c6SThomas Zimmermann 	return res;
9432ae90c6SThomas Zimmermann }
9532ae90c6SThomas Zimmermann 
9632ae90c6SThomas Zimmermann static int efidrm_get_stride_si(struct drm_device *dev, const struct screen_info *si,
9732ae90c6SThomas Zimmermann 				const struct drm_format_info *format,
9832ae90c6SThomas Zimmermann 				unsigned int width, unsigned int height, u64 size)
9932ae90c6SThomas Zimmermann {
10032ae90c6SThomas Zimmermann 	u64 lfb_linelength = si->lfb_linelength;
10132ae90c6SThomas Zimmermann 
10232ae90c6SThomas Zimmermann 	if (!lfb_linelength)
10332ae90c6SThomas Zimmermann 		lfb_linelength = drm_format_info_min_pitch(format, 0, width);
10432ae90c6SThomas Zimmermann 
10532ae90c6SThomas Zimmermann 	return efidrm_get_validated_int0(dev, "stride", lfb_linelength, div64_u64(size, height));
10632ae90c6SThomas Zimmermann }
10732ae90c6SThomas Zimmermann 
10832ae90c6SThomas Zimmermann static u64 efidrm_get_visible_size_si(struct drm_device *dev, const struct screen_info *si,
10932ae90c6SThomas Zimmermann 				      unsigned int height, unsigned int stride, u64 size)
11032ae90c6SThomas Zimmermann {
11132ae90c6SThomas Zimmermann 	u64 vsize = PAGE_ALIGN(height * stride);
11232ae90c6SThomas Zimmermann 
11332ae90c6SThomas Zimmermann 	return efidrm_get_validated_size0(dev, "visible size", vsize, size);
11432ae90c6SThomas Zimmermann }
11532ae90c6SThomas Zimmermann 
11632ae90c6SThomas Zimmermann static const struct drm_format_info *efidrm_get_format_si(struct drm_device *dev,
11732ae90c6SThomas Zimmermann 							  const struct screen_info *si)
11832ae90c6SThomas Zimmermann {
11932ae90c6SThomas Zimmermann 	static const struct {
12032ae90c6SThomas Zimmermann 		struct pixel_format pixel;
12132ae90c6SThomas Zimmermann 		u32 fourcc;
12232ae90c6SThomas Zimmermann 	} efi_formats[] = {
12332ae90c6SThomas Zimmermann 		{ PIXEL_FORMAT_XRGB1555, DRM_FORMAT_XRGB1555, },
12432ae90c6SThomas Zimmermann 		{ PIXEL_FORMAT_RGB565, DRM_FORMAT_RGB565, },
12532ae90c6SThomas Zimmermann 		{ PIXEL_FORMAT_RGB888, DRM_FORMAT_RGB888, },
12632ae90c6SThomas Zimmermann 		{ PIXEL_FORMAT_XRGB8888, DRM_FORMAT_XRGB8888, },
12732ae90c6SThomas Zimmermann 		{ PIXEL_FORMAT_XBGR8888, DRM_FORMAT_XBGR8888, },
12832ae90c6SThomas Zimmermann 		{ PIXEL_FORMAT_XRGB2101010, DRM_FORMAT_XRGB2101010, },
12932ae90c6SThomas Zimmermann 	};
13032ae90c6SThomas Zimmermann 	const struct drm_format_info *format = NULL;
13132ae90c6SThomas Zimmermann 	u32 bits_per_pixel;
13232ae90c6SThomas Zimmermann 	size_t i;
13332ae90c6SThomas Zimmermann 
13432ae90c6SThomas Zimmermann 	bits_per_pixel = __screen_info_lfb_bits_per_pixel(si);
13532ae90c6SThomas Zimmermann 
13632ae90c6SThomas Zimmermann 	for (i = 0; i < ARRAY_SIZE(efi_formats); ++i) {
13732ae90c6SThomas Zimmermann 		const struct pixel_format *f = &efi_formats[i].pixel;
13832ae90c6SThomas Zimmermann 
13932ae90c6SThomas Zimmermann 		if (bits_per_pixel == f->bits_per_pixel &&
14032ae90c6SThomas Zimmermann 		    si->red_size == f->red.length &&
14132ae90c6SThomas Zimmermann 		    si->red_pos == f->red.offset &&
14232ae90c6SThomas Zimmermann 		    si->green_size == f->green.length &&
14332ae90c6SThomas Zimmermann 		    si->green_pos == f->green.offset &&
14432ae90c6SThomas Zimmermann 		    si->blue_size == f->blue.length &&
14532ae90c6SThomas Zimmermann 		    si->blue_pos == f->blue.offset) {
14632ae90c6SThomas Zimmermann 			format = drm_format_info(efi_formats[i].fourcc);
14732ae90c6SThomas Zimmermann 			break;
14832ae90c6SThomas Zimmermann 		}
14932ae90c6SThomas Zimmermann 	}
15032ae90c6SThomas Zimmermann 
15132ae90c6SThomas Zimmermann 	if (!format)
15232ae90c6SThomas Zimmermann 		return ERR_PTR(-EINVAL);
15332ae90c6SThomas Zimmermann 	if (format->is_color_indexed)
15432ae90c6SThomas Zimmermann 		return ERR_PTR(-EINVAL);
15532ae90c6SThomas Zimmermann 
15632ae90c6SThomas Zimmermann 	return format;
15732ae90c6SThomas Zimmermann }
15832ae90c6SThomas Zimmermann 
15932ae90c6SThomas Zimmermann static u64 efidrm_get_mem_flags(struct drm_device *dev, resource_size_t start,
16032ae90c6SThomas Zimmermann 				resource_size_t len)
16132ae90c6SThomas Zimmermann {
16232ae90c6SThomas Zimmermann 	u64 attribute = EFI_MEMORY_UC | EFI_MEMORY_WC |
16332ae90c6SThomas Zimmermann 			EFI_MEMORY_WT | EFI_MEMORY_WB;
16432ae90c6SThomas Zimmermann 	u64 mem_flags = EFI_MEMORY_WC | EFI_MEMORY_UC;
16532ae90c6SThomas Zimmermann 	resource_size_t end = start + len;
16632ae90c6SThomas Zimmermann 	efi_memory_desc_t md;
16732ae90c6SThomas Zimmermann 	u64 md_end;
16832ae90c6SThomas Zimmermann 
16932ae90c6SThomas Zimmermann 	if (!efi_enabled(EFI_MEMMAP) || efi_mem_desc_lookup(start, &md))
17032ae90c6SThomas Zimmermann 		goto out;
17132ae90c6SThomas Zimmermann 
17232ae90c6SThomas Zimmermann 	md_end = md.phys_addr + (md.num_pages << EFI_PAGE_SHIFT);
17332ae90c6SThomas Zimmermann 	if (end > md_end)
17432ae90c6SThomas Zimmermann 		goto out;
17532ae90c6SThomas Zimmermann 
17632ae90c6SThomas Zimmermann 	attribute &= md.attribute;
17732ae90c6SThomas Zimmermann 	if (attribute) {
17832ae90c6SThomas Zimmermann 		mem_flags |= EFI_MEMORY_WT | EFI_MEMORY_WB;
17932ae90c6SThomas Zimmermann 		mem_flags &= attribute;
18032ae90c6SThomas Zimmermann 	}
18132ae90c6SThomas Zimmermann 
18232ae90c6SThomas Zimmermann out:
18332ae90c6SThomas Zimmermann 	return mem_flags;
18432ae90c6SThomas Zimmermann }
18532ae90c6SThomas Zimmermann 
18632ae90c6SThomas Zimmermann /*
18732ae90c6SThomas Zimmermann  * EFI device
18832ae90c6SThomas Zimmermann  */
18932ae90c6SThomas Zimmermann 
19032ae90c6SThomas Zimmermann struct efidrm_device {
19132ae90c6SThomas Zimmermann 	struct drm_sysfb_device sysfb;
19232ae90c6SThomas Zimmermann 
19332ae90c6SThomas Zimmermann 	/* modesetting */
19432ae90c6SThomas Zimmermann 	u32 formats[DRM_SYSFB_PLANE_NFORMATS(1)];
19532ae90c6SThomas Zimmermann 	struct drm_plane primary_plane;
19632ae90c6SThomas Zimmermann 	struct drm_crtc crtc;
19732ae90c6SThomas Zimmermann 	struct drm_encoder encoder;
19832ae90c6SThomas Zimmermann 	struct drm_connector connector;
19932ae90c6SThomas Zimmermann };
20032ae90c6SThomas Zimmermann 
20132ae90c6SThomas Zimmermann /*
20232ae90c6SThomas Zimmermann  * Modesetting
20332ae90c6SThomas Zimmermann  */
20432ae90c6SThomas Zimmermann 
20532ae90c6SThomas Zimmermann static const u64 efidrm_primary_plane_format_modifiers[] = {
20632ae90c6SThomas Zimmermann 	DRM_SYSFB_PLANE_FORMAT_MODIFIERS,
20732ae90c6SThomas Zimmermann };
20832ae90c6SThomas Zimmermann 
20932ae90c6SThomas Zimmermann static const struct drm_plane_helper_funcs efidrm_primary_plane_helper_funcs = {
21032ae90c6SThomas Zimmermann 	DRM_SYSFB_PLANE_HELPER_FUNCS,
21132ae90c6SThomas Zimmermann };
21232ae90c6SThomas Zimmermann 
21332ae90c6SThomas Zimmermann static const struct drm_plane_funcs efidrm_primary_plane_funcs = {
21432ae90c6SThomas Zimmermann 	DRM_SYSFB_PLANE_FUNCS,
21532ae90c6SThomas Zimmermann 	.destroy = drm_plane_cleanup,
21632ae90c6SThomas Zimmermann };
21732ae90c6SThomas Zimmermann 
21832ae90c6SThomas Zimmermann static const struct drm_crtc_helper_funcs efidrm_crtc_helper_funcs = {
21932ae90c6SThomas Zimmermann 	DRM_SYSFB_CRTC_HELPER_FUNCS,
22032ae90c6SThomas Zimmermann };
22132ae90c6SThomas Zimmermann 
22232ae90c6SThomas Zimmermann static const struct drm_crtc_funcs efidrm_crtc_funcs = {
22332ae90c6SThomas Zimmermann 	DRM_SYSFB_CRTC_FUNCS,
22432ae90c6SThomas Zimmermann 	.destroy = drm_crtc_cleanup,
22532ae90c6SThomas Zimmermann };
22632ae90c6SThomas Zimmermann 
22732ae90c6SThomas Zimmermann static const struct drm_encoder_funcs efidrm_encoder_funcs = {
22832ae90c6SThomas Zimmermann 	.destroy = drm_encoder_cleanup,
22932ae90c6SThomas Zimmermann };
23032ae90c6SThomas Zimmermann 
23132ae90c6SThomas Zimmermann static const struct drm_connector_helper_funcs efidrm_connector_helper_funcs = {
23232ae90c6SThomas Zimmermann 	DRM_SYSFB_CONNECTOR_HELPER_FUNCS,
23332ae90c6SThomas Zimmermann };
23432ae90c6SThomas Zimmermann 
23532ae90c6SThomas Zimmermann static const struct drm_connector_funcs efidrm_connector_funcs = {
23632ae90c6SThomas Zimmermann 	DRM_SYSFB_CONNECTOR_FUNCS,
23732ae90c6SThomas Zimmermann 	.destroy = drm_connector_cleanup,
23832ae90c6SThomas Zimmermann };
23932ae90c6SThomas Zimmermann 
24032ae90c6SThomas Zimmermann static const struct drm_mode_config_funcs efidrm_mode_config_funcs = {
24132ae90c6SThomas Zimmermann 	DRM_SYSFB_MODE_CONFIG_FUNCS,
24232ae90c6SThomas Zimmermann };
24332ae90c6SThomas Zimmermann 
24432ae90c6SThomas Zimmermann /*
24532ae90c6SThomas Zimmermann  * Init / Cleanup
24632ae90c6SThomas Zimmermann  */
24732ae90c6SThomas Zimmermann 
24832ae90c6SThomas Zimmermann static struct efidrm_device *efidrm_device_create(struct drm_driver *drv,
24932ae90c6SThomas Zimmermann 						  struct platform_device *pdev)
25032ae90c6SThomas Zimmermann {
25132ae90c6SThomas Zimmermann 	const struct screen_info *si;
25232ae90c6SThomas Zimmermann 	const struct drm_format_info *format;
25332ae90c6SThomas Zimmermann 	int width, height, stride;
25432ae90c6SThomas Zimmermann 	u64 vsize, mem_flags;
25532ae90c6SThomas Zimmermann 	struct resource resbuf;
25632ae90c6SThomas Zimmermann 	struct resource *res;
25732ae90c6SThomas Zimmermann 	struct efidrm_device *efi;
25832ae90c6SThomas Zimmermann 	struct drm_sysfb_device *sysfb;
25932ae90c6SThomas Zimmermann 	struct drm_device *dev;
26032ae90c6SThomas Zimmermann 	struct resource *mem = NULL;
26132ae90c6SThomas Zimmermann 	void __iomem *screen_base;
26232ae90c6SThomas Zimmermann 	struct drm_plane *primary_plane;
26332ae90c6SThomas Zimmermann 	struct drm_crtc *crtc;
26432ae90c6SThomas Zimmermann 	struct drm_encoder *encoder;
26532ae90c6SThomas Zimmermann 	struct drm_connector *connector;
26632ae90c6SThomas Zimmermann 	unsigned long max_width, max_height;
26732ae90c6SThomas Zimmermann 	size_t nformats;
26832ae90c6SThomas Zimmermann 	int ret;
26932ae90c6SThomas Zimmermann 
27032ae90c6SThomas Zimmermann 	si = dev_get_platdata(&pdev->dev);
27132ae90c6SThomas Zimmermann 	if (!si)
27232ae90c6SThomas Zimmermann 		return ERR_PTR(-ENODEV);
27332ae90c6SThomas Zimmermann 	if (screen_info_video_type(si) != VIDEO_TYPE_EFI)
27432ae90c6SThomas Zimmermann 		return ERR_PTR(-ENODEV);
27532ae90c6SThomas Zimmermann 
27632ae90c6SThomas Zimmermann 	/*
27732ae90c6SThomas Zimmermann 	 * EFI DRM driver
27832ae90c6SThomas Zimmermann 	 */
27932ae90c6SThomas Zimmermann 
28032ae90c6SThomas Zimmermann 	efi = devm_drm_dev_alloc(&pdev->dev, drv, struct efidrm_device, sysfb.dev);
28132ae90c6SThomas Zimmermann 	if (IS_ERR(efi))
28232ae90c6SThomas Zimmermann 		return ERR_CAST(efi);
28332ae90c6SThomas Zimmermann 	sysfb = &efi->sysfb;
28432ae90c6SThomas Zimmermann 	dev = &sysfb->dev;
28532ae90c6SThomas Zimmermann 	platform_set_drvdata(pdev, dev);
28632ae90c6SThomas Zimmermann 
28732ae90c6SThomas Zimmermann 	/*
28832ae90c6SThomas Zimmermann 	 * Hardware settings
28932ae90c6SThomas Zimmermann 	 */
29032ae90c6SThomas Zimmermann 
29132ae90c6SThomas Zimmermann 	format = efidrm_get_format_si(dev, si);
29232ae90c6SThomas Zimmermann 	if (IS_ERR(format))
29332ae90c6SThomas Zimmermann 		return ERR_CAST(format);
29432ae90c6SThomas Zimmermann 	width = efidrm_get_width_si(dev, si);
29532ae90c6SThomas Zimmermann 	if (width < 0)
29632ae90c6SThomas Zimmermann 		return ERR_PTR(width);
29732ae90c6SThomas Zimmermann 	height = efidrm_get_height_si(dev, si);
29832ae90c6SThomas Zimmermann 	if (height < 0)
29932ae90c6SThomas Zimmermann 		return ERR_PTR(height);
30032ae90c6SThomas Zimmermann 	res = efidrm_get_memory_si(dev, si, &resbuf);
30132ae90c6SThomas Zimmermann 	if (!res)
30232ae90c6SThomas Zimmermann 		return ERR_PTR(-EINVAL);
30332ae90c6SThomas Zimmermann 	stride = efidrm_get_stride_si(dev, si, format, width, height, resource_size(res));
30432ae90c6SThomas Zimmermann 	if (stride < 0)
30532ae90c6SThomas Zimmermann 		return ERR_PTR(stride);
30632ae90c6SThomas Zimmermann 	vsize = efidrm_get_visible_size_si(dev, si, height, stride, resource_size(res));
30732ae90c6SThomas Zimmermann 	if (!vsize)
30832ae90c6SThomas Zimmermann 		return ERR_PTR(-EINVAL);
30932ae90c6SThomas Zimmermann 
31032ae90c6SThomas Zimmermann 	drm_dbg(dev, "framebuffer format=%p4cc, size=%dx%d, stride=%d bytes\n",
31132ae90c6SThomas Zimmermann 		&format->format, width, height, stride);
31232ae90c6SThomas Zimmermann 
313*305396acSThomas Zimmermann #ifdef CONFIG_X86
314*305396acSThomas Zimmermann 	if (drm_edid_header_is_valid(edid_info.dummy) == 8)
315*305396acSThomas Zimmermann 		sysfb->edid = edid_info.dummy;
316*305396acSThomas Zimmermann #endif
31732ae90c6SThomas Zimmermann 	sysfb->fb_mode = drm_sysfb_mode(width, height, 0, 0);
31832ae90c6SThomas Zimmermann 	sysfb->fb_format = format;
31932ae90c6SThomas Zimmermann 	sysfb->fb_pitch = stride;
32032ae90c6SThomas Zimmermann 
32132ae90c6SThomas Zimmermann 	/*
32232ae90c6SThomas Zimmermann 	 * Memory management
32332ae90c6SThomas Zimmermann 	 */
32432ae90c6SThomas Zimmermann 
32532ae90c6SThomas Zimmermann 	ret = devm_aperture_acquire_for_platform_device(pdev, res->start, vsize);
32632ae90c6SThomas Zimmermann 	if (ret) {
32732ae90c6SThomas Zimmermann 		drm_err(dev, "could not acquire memory range %pr: %d\n", res, ret);
32832ae90c6SThomas Zimmermann 		return ERR_PTR(ret);
32932ae90c6SThomas Zimmermann 	}
33032ae90c6SThomas Zimmermann 
33132ae90c6SThomas Zimmermann 	drm_dbg(dev, "using I/O memory framebuffer at %pr\n", res);
33232ae90c6SThomas Zimmermann 
33332ae90c6SThomas Zimmermann 	mem = devm_request_mem_region(&pdev->dev, res->start, vsize, drv->name);
33432ae90c6SThomas Zimmermann 	if (!mem) {
33532ae90c6SThomas Zimmermann 		/*
33632ae90c6SThomas Zimmermann 		 * We cannot make this fatal. Sometimes this comes from magic
33732ae90c6SThomas Zimmermann 		 * spaces our resource handlers simply don't know about. Use
33832ae90c6SThomas Zimmermann 		 * the I/O-memory resource as-is and try to map that instead.
33932ae90c6SThomas Zimmermann 		 */
34032ae90c6SThomas Zimmermann 		drm_warn(dev, "could not acquire memory region %pr\n", res);
34132ae90c6SThomas Zimmermann 		mem = res;
34232ae90c6SThomas Zimmermann 	}
34332ae90c6SThomas Zimmermann 
34432ae90c6SThomas Zimmermann 	mem_flags = efidrm_get_mem_flags(dev, res->start, vsize);
34532ae90c6SThomas Zimmermann 
34632ae90c6SThomas Zimmermann 	if (mem_flags & EFI_MEMORY_WC)
34732ae90c6SThomas Zimmermann 		screen_base = devm_ioremap_wc(&pdev->dev, mem->start, resource_size(mem));
34832ae90c6SThomas Zimmermann 	else if (mem_flags & EFI_MEMORY_UC)
34932ae90c6SThomas Zimmermann 		screen_base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
35032ae90c6SThomas Zimmermann 	else if (mem_flags & EFI_MEMORY_WT)
35132ae90c6SThomas Zimmermann 		screen_base = devm_memremap(&pdev->dev, mem->start, resource_size(mem),
35232ae90c6SThomas Zimmermann 					    MEMREMAP_WT);
35332ae90c6SThomas Zimmermann 	else if (mem_flags & EFI_MEMORY_WB)
35432ae90c6SThomas Zimmermann 		screen_base = devm_memremap(&pdev->dev, mem->start, resource_size(mem),
35532ae90c6SThomas Zimmermann 					    MEMREMAP_WB);
35632ae90c6SThomas Zimmermann 	if (!screen_base)
35732ae90c6SThomas Zimmermann 		return ERR_PTR(-ENOMEM);
35832ae90c6SThomas Zimmermann 	iosys_map_set_vaddr_iomem(&sysfb->fb_addr, screen_base);
35932ae90c6SThomas Zimmermann 
36032ae90c6SThomas Zimmermann 	/*
36132ae90c6SThomas Zimmermann 	 * Modesetting
36232ae90c6SThomas Zimmermann 	 */
36332ae90c6SThomas Zimmermann 
36432ae90c6SThomas Zimmermann 	ret = drmm_mode_config_init(dev);
36532ae90c6SThomas Zimmermann 	if (ret)
36632ae90c6SThomas Zimmermann 		return ERR_PTR(ret);
36732ae90c6SThomas Zimmermann 
36832ae90c6SThomas Zimmermann 	max_width = max_t(unsigned long, width, DRM_SHADOW_PLANE_MAX_WIDTH);
36932ae90c6SThomas Zimmermann 	max_height = max_t(unsigned long, height, DRM_SHADOW_PLANE_MAX_HEIGHT);
37032ae90c6SThomas Zimmermann 
37132ae90c6SThomas Zimmermann 	dev->mode_config.min_width = width;
37232ae90c6SThomas Zimmermann 	dev->mode_config.max_width = max_width;
37332ae90c6SThomas Zimmermann 	dev->mode_config.min_height = height;
37432ae90c6SThomas Zimmermann 	dev->mode_config.max_height = max_height;
37532ae90c6SThomas Zimmermann 	dev->mode_config.preferred_depth = format->depth;
37632ae90c6SThomas Zimmermann 	dev->mode_config.funcs = &efidrm_mode_config_funcs;
37732ae90c6SThomas Zimmermann 
37832ae90c6SThomas Zimmermann 	/* Primary plane */
37932ae90c6SThomas Zimmermann 
38032ae90c6SThomas Zimmermann 	nformats = drm_fb_build_fourcc_list(dev, &format->format, 1,
38132ae90c6SThomas Zimmermann 					    efi->formats, ARRAY_SIZE(efi->formats));
38232ae90c6SThomas Zimmermann 
38332ae90c6SThomas Zimmermann 	primary_plane = &efi->primary_plane;
38432ae90c6SThomas Zimmermann 	ret = drm_universal_plane_init(dev, primary_plane, 0, &efidrm_primary_plane_funcs,
38532ae90c6SThomas Zimmermann 				       efi->formats, nformats,
38632ae90c6SThomas Zimmermann 				       efidrm_primary_plane_format_modifiers,
38732ae90c6SThomas Zimmermann 				       DRM_PLANE_TYPE_PRIMARY, NULL);
38832ae90c6SThomas Zimmermann 	if (ret)
38932ae90c6SThomas Zimmermann 		return ERR_PTR(ret);
39032ae90c6SThomas Zimmermann 	drm_plane_helper_add(primary_plane, &efidrm_primary_plane_helper_funcs);
39132ae90c6SThomas Zimmermann 	drm_plane_enable_fb_damage_clips(primary_plane);
39232ae90c6SThomas Zimmermann 
39332ae90c6SThomas Zimmermann 	/* CRTC */
39432ae90c6SThomas Zimmermann 
39532ae90c6SThomas Zimmermann 	crtc = &efi->crtc;
39632ae90c6SThomas Zimmermann 	ret = drm_crtc_init_with_planes(dev, crtc, primary_plane, NULL,
39732ae90c6SThomas Zimmermann 					&efidrm_crtc_funcs, NULL);
39832ae90c6SThomas Zimmermann 	if (ret)
39932ae90c6SThomas Zimmermann 		return ERR_PTR(ret);
40032ae90c6SThomas Zimmermann 	drm_crtc_helper_add(crtc, &efidrm_crtc_helper_funcs);
40132ae90c6SThomas Zimmermann 
40232ae90c6SThomas Zimmermann 	/* Encoder */
40332ae90c6SThomas Zimmermann 
40432ae90c6SThomas Zimmermann 	encoder = &efi->encoder;
40532ae90c6SThomas Zimmermann 	ret = drm_encoder_init(dev, encoder, &efidrm_encoder_funcs,
40632ae90c6SThomas Zimmermann 			       DRM_MODE_ENCODER_NONE, NULL);
40732ae90c6SThomas Zimmermann 	if (ret)
40832ae90c6SThomas Zimmermann 		return ERR_PTR(ret);
40932ae90c6SThomas Zimmermann 	encoder->possible_crtcs = drm_crtc_mask(crtc);
41032ae90c6SThomas Zimmermann 
41132ae90c6SThomas Zimmermann 	/* Connector */
41232ae90c6SThomas Zimmermann 
41332ae90c6SThomas Zimmermann 	connector = &efi->connector;
41432ae90c6SThomas Zimmermann 	ret = drm_connector_init(dev, connector, &efidrm_connector_funcs,
41532ae90c6SThomas Zimmermann 				 DRM_MODE_CONNECTOR_Unknown);
41632ae90c6SThomas Zimmermann 	if (ret)
41732ae90c6SThomas Zimmermann 		return ERR_PTR(ret);
41832ae90c6SThomas Zimmermann 	drm_connector_helper_add(connector, &efidrm_connector_helper_funcs);
41932ae90c6SThomas Zimmermann 	drm_connector_set_panel_orientation_with_quirk(connector,
42032ae90c6SThomas Zimmermann 						       DRM_MODE_PANEL_ORIENTATION_UNKNOWN,
42132ae90c6SThomas Zimmermann 						       width, height);
422*305396acSThomas Zimmermann 	if (sysfb->edid)
423*305396acSThomas Zimmermann 		drm_connector_attach_edid_property(connector);
42432ae90c6SThomas Zimmermann 
42532ae90c6SThomas Zimmermann 	ret = drm_connector_attach_encoder(connector, encoder);
42632ae90c6SThomas Zimmermann 	if (ret)
42732ae90c6SThomas Zimmermann 		return ERR_PTR(ret);
42832ae90c6SThomas Zimmermann 
42932ae90c6SThomas Zimmermann 	drm_mode_config_reset(dev);
43032ae90c6SThomas Zimmermann 
43132ae90c6SThomas Zimmermann 	return efi;
43232ae90c6SThomas Zimmermann }
43332ae90c6SThomas Zimmermann 
43432ae90c6SThomas Zimmermann /*
43532ae90c6SThomas Zimmermann  * DRM driver
43632ae90c6SThomas Zimmermann  */
43732ae90c6SThomas Zimmermann 
43832ae90c6SThomas Zimmermann DEFINE_DRM_GEM_FOPS(efidrm_fops);
43932ae90c6SThomas Zimmermann 
44032ae90c6SThomas Zimmermann static struct drm_driver efidrm_driver = {
44132ae90c6SThomas Zimmermann 	DRM_GEM_SHMEM_DRIVER_OPS,
44232ae90c6SThomas Zimmermann 	DRM_FBDEV_SHMEM_DRIVER_OPS,
44332ae90c6SThomas Zimmermann 	.name			= DRIVER_NAME,
44432ae90c6SThomas Zimmermann 	.desc			= DRIVER_DESC,
44532ae90c6SThomas Zimmermann 	.major			= DRIVER_MAJOR,
44632ae90c6SThomas Zimmermann 	.minor			= DRIVER_MINOR,
44732ae90c6SThomas Zimmermann 	.driver_features	= DRIVER_ATOMIC | DRIVER_GEM | DRIVER_MODESET,
44832ae90c6SThomas Zimmermann 	.fops			= &efidrm_fops,
44932ae90c6SThomas Zimmermann };
45032ae90c6SThomas Zimmermann 
45132ae90c6SThomas Zimmermann /*
45232ae90c6SThomas Zimmermann  * Platform driver
45332ae90c6SThomas Zimmermann  */
45432ae90c6SThomas Zimmermann 
45532ae90c6SThomas Zimmermann static int efidrm_probe(struct platform_device *pdev)
45632ae90c6SThomas Zimmermann {
45732ae90c6SThomas Zimmermann 	struct efidrm_device *efi;
45832ae90c6SThomas Zimmermann 	struct drm_sysfb_device *sysfb;
45932ae90c6SThomas Zimmermann 	struct drm_device *dev;
46032ae90c6SThomas Zimmermann 	int ret;
46132ae90c6SThomas Zimmermann 
46232ae90c6SThomas Zimmermann 	efi = efidrm_device_create(&efidrm_driver, pdev);
46332ae90c6SThomas Zimmermann 	if (IS_ERR(efi))
46432ae90c6SThomas Zimmermann 		return PTR_ERR(efi);
46532ae90c6SThomas Zimmermann 	sysfb = &efi->sysfb;
46632ae90c6SThomas Zimmermann 	dev = &sysfb->dev;
46732ae90c6SThomas Zimmermann 
46832ae90c6SThomas Zimmermann 	ret = drm_dev_register(dev, 0);
46932ae90c6SThomas Zimmermann 	if (ret)
47032ae90c6SThomas Zimmermann 		return ret;
47132ae90c6SThomas Zimmermann 
47232ae90c6SThomas Zimmermann 	drm_client_setup(dev, sysfb->fb_format);
47332ae90c6SThomas Zimmermann 
47432ae90c6SThomas Zimmermann 	return 0;
47532ae90c6SThomas Zimmermann }
47632ae90c6SThomas Zimmermann 
47732ae90c6SThomas Zimmermann static void efidrm_remove(struct platform_device *pdev)
47832ae90c6SThomas Zimmermann {
47932ae90c6SThomas Zimmermann 	struct drm_device *dev = platform_get_drvdata(pdev);
48032ae90c6SThomas Zimmermann 
48132ae90c6SThomas Zimmermann 	drm_dev_unplug(dev);
48232ae90c6SThomas Zimmermann }
48332ae90c6SThomas Zimmermann 
48432ae90c6SThomas Zimmermann static struct platform_driver efidrm_platform_driver = {
48532ae90c6SThomas Zimmermann 	.driver = {
48632ae90c6SThomas Zimmermann 		.name = "efi-framebuffer",
48732ae90c6SThomas Zimmermann 	},
48832ae90c6SThomas Zimmermann 	.probe = efidrm_probe,
48932ae90c6SThomas Zimmermann 	.remove = efidrm_remove,
49032ae90c6SThomas Zimmermann };
49132ae90c6SThomas Zimmermann 
49232ae90c6SThomas Zimmermann module_platform_driver(efidrm_platform_driver);
49332ae90c6SThomas Zimmermann 
49432ae90c6SThomas Zimmermann MODULE_DESCRIPTION(DRIVER_DESC);
49532ae90c6SThomas Zimmermann MODULE_LICENSE("GPL");
496