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