xref: /linux/drivers/gpu/drm/sysfb/efidrm.c (revision 8d2b0853add1d7534dc0794e3c8e0b9e8c4ec640)
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>
16305396acSThomas 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 
26305396acSThomas 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 const struct drm_format_info *efidrm_get_format_si(struct drm_device *dev,
3732ae90c6SThomas Zimmermann 							  const struct screen_info *si)
3832ae90c6SThomas Zimmermann {
39e8c08688SThomas Zimmermann 	static const struct drm_sysfb_format formats[] = {
4032ae90c6SThomas Zimmermann 		{ PIXEL_FORMAT_XRGB1555, DRM_FORMAT_XRGB1555, },
4132ae90c6SThomas Zimmermann 		{ PIXEL_FORMAT_RGB565, DRM_FORMAT_RGB565, },
4232ae90c6SThomas Zimmermann 		{ PIXEL_FORMAT_RGB888, DRM_FORMAT_RGB888, },
4332ae90c6SThomas Zimmermann 		{ PIXEL_FORMAT_XRGB8888, DRM_FORMAT_XRGB8888, },
4432ae90c6SThomas Zimmermann 		{ PIXEL_FORMAT_XBGR8888, DRM_FORMAT_XBGR8888, },
4532ae90c6SThomas Zimmermann 		{ PIXEL_FORMAT_XRGB2101010, DRM_FORMAT_XRGB2101010, },
4632ae90c6SThomas Zimmermann 	};
4732ae90c6SThomas Zimmermann 
48e8c08688SThomas Zimmermann 	return drm_sysfb_get_format_si(dev, formats, ARRAY_SIZE(formats), si);
4932ae90c6SThomas Zimmermann }
5032ae90c6SThomas Zimmermann 
5132ae90c6SThomas Zimmermann static u64 efidrm_get_mem_flags(struct drm_device *dev, resource_size_t start,
5232ae90c6SThomas Zimmermann 				resource_size_t len)
5332ae90c6SThomas Zimmermann {
5432ae90c6SThomas Zimmermann 	u64 attribute = EFI_MEMORY_UC | EFI_MEMORY_WC |
5532ae90c6SThomas Zimmermann 			EFI_MEMORY_WT | EFI_MEMORY_WB;
5632ae90c6SThomas Zimmermann 	u64 mem_flags = EFI_MEMORY_WC | EFI_MEMORY_UC;
5732ae90c6SThomas Zimmermann 	resource_size_t end = start + len;
5832ae90c6SThomas Zimmermann 	efi_memory_desc_t md;
5932ae90c6SThomas Zimmermann 	u64 md_end;
6032ae90c6SThomas Zimmermann 
6132ae90c6SThomas Zimmermann 	if (!efi_enabled(EFI_MEMMAP) || efi_mem_desc_lookup(start, &md))
6232ae90c6SThomas Zimmermann 		goto out;
6332ae90c6SThomas Zimmermann 
6432ae90c6SThomas Zimmermann 	md_end = md.phys_addr + (md.num_pages << EFI_PAGE_SHIFT);
6532ae90c6SThomas Zimmermann 	if (end > md_end)
6632ae90c6SThomas Zimmermann 		goto out;
6732ae90c6SThomas Zimmermann 
6832ae90c6SThomas Zimmermann 	attribute &= md.attribute;
6932ae90c6SThomas Zimmermann 	if (attribute) {
7032ae90c6SThomas Zimmermann 		mem_flags |= EFI_MEMORY_WT | EFI_MEMORY_WB;
7132ae90c6SThomas Zimmermann 		mem_flags &= attribute;
7232ae90c6SThomas Zimmermann 	}
7332ae90c6SThomas Zimmermann 
7432ae90c6SThomas Zimmermann out:
7532ae90c6SThomas Zimmermann 	return mem_flags;
7632ae90c6SThomas Zimmermann }
7732ae90c6SThomas Zimmermann 
7832ae90c6SThomas Zimmermann /*
7932ae90c6SThomas Zimmermann  * EFI device
8032ae90c6SThomas Zimmermann  */
8132ae90c6SThomas Zimmermann 
8232ae90c6SThomas Zimmermann struct efidrm_device {
8332ae90c6SThomas Zimmermann 	struct drm_sysfb_device sysfb;
8432ae90c6SThomas Zimmermann 
8532ae90c6SThomas Zimmermann 	/* modesetting */
8632ae90c6SThomas Zimmermann 	u32 formats[DRM_SYSFB_PLANE_NFORMATS(1)];
8732ae90c6SThomas Zimmermann 	struct drm_plane primary_plane;
8832ae90c6SThomas Zimmermann 	struct drm_crtc crtc;
8932ae90c6SThomas Zimmermann 	struct drm_encoder encoder;
9032ae90c6SThomas Zimmermann 	struct drm_connector connector;
9132ae90c6SThomas Zimmermann };
9232ae90c6SThomas Zimmermann 
9332ae90c6SThomas Zimmermann /*
9432ae90c6SThomas Zimmermann  * Modesetting
9532ae90c6SThomas Zimmermann  */
9632ae90c6SThomas Zimmermann 
9732ae90c6SThomas Zimmermann static const u64 efidrm_primary_plane_format_modifiers[] = {
9832ae90c6SThomas Zimmermann 	DRM_SYSFB_PLANE_FORMAT_MODIFIERS,
9932ae90c6SThomas Zimmermann };
10032ae90c6SThomas Zimmermann 
10132ae90c6SThomas Zimmermann static const struct drm_plane_helper_funcs efidrm_primary_plane_helper_funcs = {
10232ae90c6SThomas Zimmermann 	DRM_SYSFB_PLANE_HELPER_FUNCS,
10332ae90c6SThomas Zimmermann };
10432ae90c6SThomas Zimmermann 
10532ae90c6SThomas Zimmermann static const struct drm_plane_funcs efidrm_primary_plane_funcs = {
10632ae90c6SThomas Zimmermann 	DRM_SYSFB_PLANE_FUNCS,
10732ae90c6SThomas Zimmermann 	.destroy = drm_plane_cleanup,
10832ae90c6SThomas Zimmermann };
10932ae90c6SThomas Zimmermann 
11032ae90c6SThomas Zimmermann static const struct drm_crtc_helper_funcs efidrm_crtc_helper_funcs = {
11132ae90c6SThomas Zimmermann 	DRM_SYSFB_CRTC_HELPER_FUNCS,
11232ae90c6SThomas Zimmermann };
11332ae90c6SThomas Zimmermann 
11432ae90c6SThomas Zimmermann static const struct drm_crtc_funcs efidrm_crtc_funcs = {
11532ae90c6SThomas Zimmermann 	DRM_SYSFB_CRTC_FUNCS,
11632ae90c6SThomas Zimmermann 	.destroy = drm_crtc_cleanup,
11732ae90c6SThomas Zimmermann };
11832ae90c6SThomas Zimmermann 
11932ae90c6SThomas Zimmermann static const struct drm_encoder_funcs efidrm_encoder_funcs = {
12032ae90c6SThomas Zimmermann 	.destroy = drm_encoder_cleanup,
12132ae90c6SThomas Zimmermann };
12232ae90c6SThomas Zimmermann 
12332ae90c6SThomas Zimmermann static const struct drm_connector_helper_funcs efidrm_connector_helper_funcs = {
12432ae90c6SThomas Zimmermann 	DRM_SYSFB_CONNECTOR_HELPER_FUNCS,
12532ae90c6SThomas Zimmermann };
12632ae90c6SThomas Zimmermann 
12732ae90c6SThomas Zimmermann static const struct drm_connector_funcs efidrm_connector_funcs = {
12832ae90c6SThomas Zimmermann 	DRM_SYSFB_CONNECTOR_FUNCS,
12932ae90c6SThomas Zimmermann 	.destroy = drm_connector_cleanup,
13032ae90c6SThomas Zimmermann };
13132ae90c6SThomas Zimmermann 
13232ae90c6SThomas Zimmermann static const struct drm_mode_config_funcs efidrm_mode_config_funcs = {
13332ae90c6SThomas Zimmermann 	DRM_SYSFB_MODE_CONFIG_FUNCS,
13432ae90c6SThomas Zimmermann };
13532ae90c6SThomas Zimmermann 
13632ae90c6SThomas Zimmermann /*
13732ae90c6SThomas Zimmermann  * Init / Cleanup
13832ae90c6SThomas Zimmermann  */
13932ae90c6SThomas Zimmermann 
14032ae90c6SThomas Zimmermann static struct efidrm_device *efidrm_device_create(struct drm_driver *drv,
14132ae90c6SThomas Zimmermann 						  struct platform_device *pdev)
14232ae90c6SThomas Zimmermann {
14332ae90c6SThomas Zimmermann 	const struct screen_info *si;
14432ae90c6SThomas Zimmermann 	const struct drm_format_info *format;
14532ae90c6SThomas Zimmermann 	int width, height, stride;
14632ae90c6SThomas Zimmermann 	u64 vsize, mem_flags;
14732ae90c6SThomas Zimmermann 	struct resource resbuf;
14832ae90c6SThomas Zimmermann 	struct resource *res;
14932ae90c6SThomas Zimmermann 	struct efidrm_device *efi;
15032ae90c6SThomas Zimmermann 	struct drm_sysfb_device *sysfb;
15132ae90c6SThomas Zimmermann 	struct drm_device *dev;
15232ae90c6SThomas Zimmermann 	struct resource *mem = NULL;
15374637552SNathan Chancellor 	void __iomem *screen_base = NULL;
15432ae90c6SThomas Zimmermann 	struct drm_plane *primary_plane;
15532ae90c6SThomas Zimmermann 	struct drm_crtc *crtc;
15632ae90c6SThomas Zimmermann 	struct drm_encoder *encoder;
15732ae90c6SThomas Zimmermann 	struct drm_connector *connector;
15832ae90c6SThomas Zimmermann 	unsigned long max_width, max_height;
15932ae90c6SThomas Zimmermann 	size_t nformats;
16032ae90c6SThomas Zimmermann 	int ret;
16132ae90c6SThomas Zimmermann 
16232ae90c6SThomas Zimmermann 	si = dev_get_platdata(&pdev->dev);
16332ae90c6SThomas Zimmermann 	if (!si)
16432ae90c6SThomas Zimmermann 		return ERR_PTR(-ENODEV);
16532ae90c6SThomas Zimmermann 	if (screen_info_video_type(si) != VIDEO_TYPE_EFI)
16632ae90c6SThomas Zimmermann 		return ERR_PTR(-ENODEV);
16732ae90c6SThomas Zimmermann 
16832ae90c6SThomas Zimmermann 	/*
16932ae90c6SThomas Zimmermann 	 * EFI DRM driver
17032ae90c6SThomas Zimmermann 	 */
17132ae90c6SThomas Zimmermann 
17232ae90c6SThomas Zimmermann 	efi = devm_drm_dev_alloc(&pdev->dev, drv, struct efidrm_device, sysfb.dev);
17332ae90c6SThomas Zimmermann 	if (IS_ERR(efi))
17432ae90c6SThomas Zimmermann 		return ERR_CAST(efi);
17532ae90c6SThomas Zimmermann 	sysfb = &efi->sysfb;
17632ae90c6SThomas Zimmermann 	dev = &sysfb->dev;
17732ae90c6SThomas Zimmermann 	platform_set_drvdata(pdev, dev);
17832ae90c6SThomas Zimmermann 
17932ae90c6SThomas Zimmermann 	/*
18032ae90c6SThomas Zimmermann 	 * Hardware settings
18132ae90c6SThomas Zimmermann 	 */
18232ae90c6SThomas Zimmermann 
18332ae90c6SThomas Zimmermann 	format = efidrm_get_format_si(dev, si);
184e8c08688SThomas Zimmermann 	if (!format)
185e8c08688SThomas Zimmermann 		return ERR_PTR(-EINVAL);
186e8c08688SThomas Zimmermann 	width = drm_sysfb_get_width_si(dev, si);
18732ae90c6SThomas Zimmermann 	if (width < 0)
18832ae90c6SThomas Zimmermann 		return ERR_PTR(width);
189e8c08688SThomas Zimmermann 	height = drm_sysfb_get_height_si(dev, si);
19032ae90c6SThomas Zimmermann 	if (height < 0)
19132ae90c6SThomas Zimmermann 		return ERR_PTR(height);
192e8c08688SThomas Zimmermann 	res = drm_sysfb_get_memory_si(dev, si, &resbuf);
19332ae90c6SThomas Zimmermann 	if (!res)
19432ae90c6SThomas Zimmermann 		return ERR_PTR(-EINVAL);
195e8c08688SThomas Zimmermann 	stride = drm_sysfb_get_stride_si(dev, si, format, width, height, resource_size(res));
19632ae90c6SThomas Zimmermann 	if (stride < 0)
19732ae90c6SThomas Zimmermann 		return ERR_PTR(stride);
198e8c08688SThomas Zimmermann 	vsize = drm_sysfb_get_visible_size_si(dev, si, height, stride, resource_size(res));
19932ae90c6SThomas Zimmermann 	if (!vsize)
20032ae90c6SThomas Zimmermann 		return ERR_PTR(-EINVAL);
20132ae90c6SThomas Zimmermann 
20232ae90c6SThomas Zimmermann 	drm_dbg(dev, "framebuffer format=%p4cc, size=%dx%d, stride=%d bytes\n",
20332ae90c6SThomas Zimmermann 		&format->format, width, height, stride);
20432ae90c6SThomas Zimmermann 
20533b4e4fcSThomas Zimmermann #if defined(CONFIG_FIRMWARE_EDID)
206305396acSThomas Zimmermann 	if (drm_edid_header_is_valid(edid_info.dummy) == 8)
207305396acSThomas Zimmermann 		sysfb->edid = edid_info.dummy;
208305396acSThomas Zimmermann #endif
20932ae90c6SThomas Zimmermann 	sysfb->fb_mode = drm_sysfb_mode(width, height, 0, 0);
21032ae90c6SThomas Zimmermann 	sysfb->fb_format = format;
21132ae90c6SThomas Zimmermann 	sysfb->fb_pitch = stride;
21232ae90c6SThomas Zimmermann 
21332ae90c6SThomas Zimmermann 	/*
21432ae90c6SThomas Zimmermann 	 * Memory management
21532ae90c6SThomas Zimmermann 	 */
21632ae90c6SThomas Zimmermann 
21732ae90c6SThomas Zimmermann 	ret = devm_aperture_acquire_for_platform_device(pdev, res->start, vsize);
21832ae90c6SThomas Zimmermann 	if (ret) {
21932ae90c6SThomas Zimmermann 		drm_err(dev, "could not acquire memory range %pr: %d\n", res, ret);
22032ae90c6SThomas Zimmermann 		return ERR_PTR(ret);
22132ae90c6SThomas Zimmermann 	}
22232ae90c6SThomas Zimmermann 
22332ae90c6SThomas Zimmermann 	drm_dbg(dev, "using I/O memory framebuffer at %pr\n", res);
22432ae90c6SThomas Zimmermann 
22532ae90c6SThomas Zimmermann 	mem = devm_request_mem_region(&pdev->dev, res->start, vsize, drv->name);
22632ae90c6SThomas Zimmermann 	if (!mem) {
22732ae90c6SThomas Zimmermann 		/*
22832ae90c6SThomas Zimmermann 		 * We cannot make this fatal. Sometimes this comes from magic
22932ae90c6SThomas Zimmermann 		 * spaces our resource handlers simply don't know about. Use
23032ae90c6SThomas Zimmermann 		 * the I/O-memory resource as-is and try to map that instead.
23132ae90c6SThomas Zimmermann 		 */
23232ae90c6SThomas Zimmermann 		drm_warn(dev, "could not acquire memory region %pr\n", res);
23332ae90c6SThomas Zimmermann 		mem = res;
23432ae90c6SThomas Zimmermann 	}
23532ae90c6SThomas Zimmermann 
23632ae90c6SThomas Zimmermann 	mem_flags = efidrm_get_mem_flags(dev, res->start, vsize);
23732ae90c6SThomas Zimmermann 
23832ae90c6SThomas Zimmermann 	if (mem_flags & EFI_MEMORY_WC)
23932ae90c6SThomas Zimmermann 		screen_base = devm_ioremap_wc(&pdev->dev, mem->start, resource_size(mem));
24032ae90c6SThomas Zimmermann 	else if (mem_flags & EFI_MEMORY_UC)
24132ae90c6SThomas Zimmermann 		screen_base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
24232ae90c6SThomas Zimmermann 	else if (mem_flags & EFI_MEMORY_WT)
24332ae90c6SThomas Zimmermann 		screen_base = devm_memremap(&pdev->dev, mem->start, resource_size(mem),
24432ae90c6SThomas Zimmermann 					    MEMREMAP_WT);
24532ae90c6SThomas Zimmermann 	else if (mem_flags & EFI_MEMORY_WB)
24632ae90c6SThomas Zimmermann 		screen_base = devm_memremap(&pdev->dev, mem->start, resource_size(mem),
24732ae90c6SThomas Zimmermann 					    MEMREMAP_WB);
24874637552SNathan Chancellor 	else
24974637552SNathan Chancellor 		drm_err(dev, "invalid mem_flags: 0x%llx\n", mem_flags);
25032ae90c6SThomas Zimmermann 	if (!screen_base)
25132ae90c6SThomas Zimmermann 		return ERR_PTR(-ENOMEM);
25232ae90c6SThomas Zimmermann 	iosys_map_set_vaddr_iomem(&sysfb->fb_addr, screen_base);
25332ae90c6SThomas Zimmermann 
25432ae90c6SThomas Zimmermann 	/*
25532ae90c6SThomas Zimmermann 	 * Modesetting
25632ae90c6SThomas Zimmermann 	 */
25732ae90c6SThomas Zimmermann 
25832ae90c6SThomas Zimmermann 	ret = drmm_mode_config_init(dev);
25932ae90c6SThomas Zimmermann 	if (ret)
26032ae90c6SThomas Zimmermann 		return ERR_PTR(ret);
26132ae90c6SThomas Zimmermann 
26232ae90c6SThomas Zimmermann 	max_width = max_t(unsigned long, width, DRM_SHADOW_PLANE_MAX_WIDTH);
26332ae90c6SThomas Zimmermann 	max_height = max_t(unsigned long, height, DRM_SHADOW_PLANE_MAX_HEIGHT);
26432ae90c6SThomas Zimmermann 
26532ae90c6SThomas Zimmermann 	dev->mode_config.min_width = width;
26632ae90c6SThomas Zimmermann 	dev->mode_config.max_width = max_width;
26732ae90c6SThomas Zimmermann 	dev->mode_config.min_height = height;
26832ae90c6SThomas Zimmermann 	dev->mode_config.max_height = max_height;
26932ae90c6SThomas Zimmermann 	dev->mode_config.preferred_depth = format->depth;
27032ae90c6SThomas Zimmermann 	dev->mode_config.funcs = &efidrm_mode_config_funcs;
27132ae90c6SThomas Zimmermann 
27232ae90c6SThomas Zimmermann 	/* Primary plane */
27332ae90c6SThomas Zimmermann 
274*1a45ef02SThomas Zimmermann 	nformats = drm_sysfb_build_fourcc_list(dev, &format->format, 1,
27532ae90c6SThomas Zimmermann 					       efi->formats, ARRAY_SIZE(efi->formats));
27632ae90c6SThomas Zimmermann 
27732ae90c6SThomas Zimmermann 	primary_plane = &efi->primary_plane;
27832ae90c6SThomas Zimmermann 	ret = drm_universal_plane_init(dev, primary_plane, 0, &efidrm_primary_plane_funcs,
27932ae90c6SThomas Zimmermann 				       efi->formats, nformats,
28032ae90c6SThomas Zimmermann 				       efidrm_primary_plane_format_modifiers,
28132ae90c6SThomas Zimmermann 				       DRM_PLANE_TYPE_PRIMARY, NULL);
28232ae90c6SThomas Zimmermann 	if (ret)
28332ae90c6SThomas Zimmermann 		return ERR_PTR(ret);
28432ae90c6SThomas Zimmermann 	drm_plane_helper_add(primary_plane, &efidrm_primary_plane_helper_funcs);
28532ae90c6SThomas Zimmermann 	drm_plane_enable_fb_damage_clips(primary_plane);
28632ae90c6SThomas Zimmermann 
28732ae90c6SThomas Zimmermann 	/* CRTC */
28832ae90c6SThomas Zimmermann 
28932ae90c6SThomas Zimmermann 	crtc = &efi->crtc;
29032ae90c6SThomas Zimmermann 	ret = drm_crtc_init_with_planes(dev, crtc, primary_plane, NULL,
29132ae90c6SThomas Zimmermann 					&efidrm_crtc_funcs, NULL);
29232ae90c6SThomas Zimmermann 	if (ret)
29332ae90c6SThomas Zimmermann 		return ERR_PTR(ret);
29432ae90c6SThomas Zimmermann 	drm_crtc_helper_add(crtc, &efidrm_crtc_helper_funcs);
29532ae90c6SThomas Zimmermann 
29632ae90c6SThomas Zimmermann 	/* Encoder */
29732ae90c6SThomas Zimmermann 
29832ae90c6SThomas Zimmermann 	encoder = &efi->encoder;
29932ae90c6SThomas Zimmermann 	ret = drm_encoder_init(dev, encoder, &efidrm_encoder_funcs,
30032ae90c6SThomas Zimmermann 			       DRM_MODE_ENCODER_NONE, NULL);
30132ae90c6SThomas Zimmermann 	if (ret)
30232ae90c6SThomas Zimmermann 		return ERR_PTR(ret);
30332ae90c6SThomas Zimmermann 	encoder->possible_crtcs = drm_crtc_mask(crtc);
30432ae90c6SThomas Zimmermann 
30532ae90c6SThomas Zimmermann 	/* Connector */
30632ae90c6SThomas Zimmermann 
30732ae90c6SThomas Zimmermann 	connector = &efi->connector;
30832ae90c6SThomas Zimmermann 	ret = drm_connector_init(dev, connector, &efidrm_connector_funcs,
30932ae90c6SThomas Zimmermann 				 DRM_MODE_CONNECTOR_Unknown);
31032ae90c6SThomas Zimmermann 	if (ret)
31132ae90c6SThomas Zimmermann 		return ERR_PTR(ret);
31232ae90c6SThomas Zimmermann 	drm_connector_helper_add(connector, &efidrm_connector_helper_funcs);
31332ae90c6SThomas Zimmermann 	drm_connector_set_panel_orientation_with_quirk(connector,
31432ae90c6SThomas Zimmermann 						       DRM_MODE_PANEL_ORIENTATION_UNKNOWN,
31532ae90c6SThomas Zimmermann 						       width, height);
316305396acSThomas Zimmermann 	if (sysfb->edid)
317305396acSThomas Zimmermann 		drm_connector_attach_edid_property(connector);
31832ae90c6SThomas Zimmermann 
31932ae90c6SThomas Zimmermann 	ret = drm_connector_attach_encoder(connector, encoder);
32032ae90c6SThomas Zimmermann 	if (ret)
32132ae90c6SThomas Zimmermann 		return ERR_PTR(ret);
32232ae90c6SThomas Zimmermann 
32332ae90c6SThomas Zimmermann 	drm_mode_config_reset(dev);
32432ae90c6SThomas Zimmermann 
32532ae90c6SThomas Zimmermann 	return efi;
32632ae90c6SThomas Zimmermann }
32732ae90c6SThomas Zimmermann 
32832ae90c6SThomas Zimmermann /*
32932ae90c6SThomas Zimmermann  * DRM driver
33032ae90c6SThomas Zimmermann  */
33132ae90c6SThomas Zimmermann 
33232ae90c6SThomas Zimmermann DEFINE_DRM_GEM_FOPS(efidrm_fops);
33332ae90c6SThomas Zimmermann 
33432ae90c6SThomas Zimmermann static struct drm_driver efidrm_driver = {
33532ae90c6SThomas Zimmermann 	DRM_GEM_SHMEM_DRIVER_OPS,
33632ae90c6SThomas Zimmermann 	DRM_FBDEV_SHMEM_DRIVER_OPS,
33732ae90c6SThomas Zimmermann 	.name			= DRIVER_NAME,
33832ae90c6SThomas Zimmermann 	.desc			= DRIVER_DESC,
33932ae90c6SThomas Zimmermann 	.major			= DRIVER_MAJOR,
34032ae90c6SThomas Zimmermann 	.minor			= DRIVER_MINOR,
34132ae90c6SThomas Zimmermann 	.driver_features	= DRIVER_ATOMIC | DRIVER_GEM | DRIVER_MODESET,
34232ae90c6SThomas Zimmermann 	.fops			= &efidrm_fops,
34332ae90c6SThomas Zimmermann };
34432ae90c6SThomas Zimmermann 
34532ae90c6SThomas Zimmermann /*
34632ae90c6SThomas Zimmermann  * Platform driver
34732ae90c6SThomas Zimmermann  */
34832ae90c6SThomas Zimmermann 
34932ae90c6SThomas Zimmermann static int efidrm_probe(struct platform_device *pdev)
35032ae90c6SThomas Zimmermann {
35132ae90c6SThomas Zimmermann 	struct efidrm_device *efi;
35232ae90c6SThomas Zimmermann 	struct drm_sysfb_device *sysfb;
35332ae90c6SThomas Zimmermann 	struct drm_device *dev;
35432ae90c6SThomas Zimmermann 	int ret;
35532ae90c6SThomas Zimmermann 
35632ae90c6SThomas Zimmermann 	efi = efidrm_device_create(&efidrm_driver, pdev);
35732ae90c6SThomas Zimmermann 	if (IS_ERR(efi))
35832ae90c6SThomas Zimmermann 		return PTR_ERR(efi);
35932ae90c6SThomas Zimmermann 	sysfb = &efi->sysfb;
36032ae90c6SThomas Zimmermann 	dev = &sysfb->dev;
36132ae90c6SThomas Zimmermann 
36232ae90c6SThomas Zimmermann 	ret = drm_dev_register(dev, 0);
36332ae90c6SThomas Zimmermann 	if (ret)
36432ae90c6SThomas Zimmermann 		return ret;
36532ae90c6SThomas Zimmermann 
36632ae90c6SThomas Zimmermann 	drm_client_setup(dev, sysfb->fb_format);
36732ae90c6SThomas Zimmermann 
36832ae90c6SThomas Zimmermann 	return 0;
36932ae90c6SThomas Zimmermann }
37032ae90c6SThomas Zimmermann 
37132ae90c6SThomas Zimmermann static void efidrm_remove(struct platform_device *pdev)
37232ae90c6SThomas Zimmermann {
37332ae90c6SThomas Zimmermann 	struct drm_device *dev = platform_get_drvdata(pdev);
37432ae90c6SThomas Zimmermann 
37532ae90c6SThomas Zimmermann 	drm_dev_unplug(dev);
37632ae90c6SThomas Zimmermann }
37732ae90c6SThomas Zimmermann 
37832ae90c6SThomas Zimmermann static struct platform_driver efidrm_platform_driver = {
37932ae90c6SThomas Zimmermann 	.driver = {
38032ae90c6SThomas Zimmermann 		.name = "efi-framebuffer",
38132ae90c6SThomas Zimmermann 	},
38232ae90c6SThomas Zimmermann 	.probe = efidrm_probe,
38332ae90c6SThomas Zimmermann 	.remove = efidrm_remove,
38432ae90c6SThomas Zimmermann };
38532ae90c6SThomas Zimmermann 
38632ae90c6SThomas Zimmermann module_platform_driver(efidrm_platform_driver);
38732ae90c6SThomas Zimmermann 
38832ae90c6SThomas Zimmermann MODULE_DESCRIPTION(DRIVER_DESC);
38932ae90c6SThomas Zimmermann MODULE_LICENSE("GPL");
390