@@ -19,6 +19,7 @@
#include <drm/drm.h>
#include <drm/drm_device.h>
#include <drm/drm_drv.h>
+#include <drm/drm_fb_helper.h>
#include <drm/drm_gem_shmem_helper.h>
#include <drm/drm_prime.h>
#include <drm/drm_print.h>
@@ -49,8 +50,20 @@ static const struct drm_gem_object_funcs drm_gem_shmem_funcs = {
.vm_ops = &drm_gem_shmem_vm_ops,
};
+static const struct drm_gem_object_funcs drm_gem_shmem_funcs_fbdev = {
+ .free = drm_gem_shmem_object_free,
+ .print_info = drm_gem_shmem_object_print_info,
+ .pin = drm_gem_shmem_object_pin,
+ .unpin = drm_gem_shmem_object_unpin,
+ .get_sg_table = drm_gem_shmem_object_get_sg_table,
+ .vmap = drm_gem_shmem_object_vmap,
+ .vunmap = drm_gem_shmem_object_vunmap,
+ .mmap = drm_gem_shmem_object_mmap_fbdev,
+ .vm_ops = &drm_gem_shmem_vm_ops_fbdev,
+};
+
static struct drm_gem_shmem_object *
-__drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private)
+__drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private, bool fbdev)
{
struct drm_gem_shmem_object *shmem;
struct drm_gem_object *obj;
@@ -70,8 +83,12 @@ __drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private)
obj = &shmem->base;
}
- if (!obj->funcs)
- obj->funcs = &drm_gem_shmem_funcs;
+ if (!obj->funcs) {
+ if (fbdev)
+ obj->funcs = &drm_gem_shmem_funcs_fbdev;
+ else
+ obj->funcs = &drm_gem_shmem_funcs;
+ }
if (private) {
drm_gem_private_object_init(dev, obj, size);
@@ -124,7 +141,7 @@ __drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private)
*/
struct drm_gem_shmem_object *drm_gem_shmem_create(struct drm_device *dev, size_t size)
{
- return __drm_gem_shmem_create(dev, size, false);
+ return __drm_gem_shmem_create(dev, size, false, false);
}
EXPORT_SYMBOL_GPL(drm_gem_shmem_create);
@@ -415,12 +432,12 @@ EXPORT_SYMBOL(drm_gem_shmem_vunmap);
static struct drm_gem_shmem_object *
drm_gem_shmem_create_with_handle(struct drm_file *file_priv,
struct drm_device *dev, size_t size,
- uint32_t *handle)
+ bool fbdev, uint32_t *handle)
{
struct drm_gem_shmem_object *shmem;
int ret;
- shmem = drm_gem_shmem_create(dev, size);
+ shmem = __drm_gem_shmem_create(dev, size, false, fbdev);
if (IS_ERR(shmem))
return shmem;
@@ -496,6 +513,29 @@ bool drm_gem_shmem_purge(struct drm_gem_shmem_object *shmem)
}
EXPORT_SYMBOL(drm_gem_shmem_purge);
+static int __drm_gem_shmem_dumb_create(struct drm_file *file, struct drm_device *dev,
+ bool fbdev, struct drm_mode_create_dumb *args)
+{
+ u32 min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
+ struct drm_gem_shmem_object *shmem;
+
+ if (!args->pitch || !args->size) {
+ args->pitch = min_pitch;
+ args->size = PAGE_ALIGN(args->pitch * args->height);
+ } else {
+ /* ensure sane minimum values */
+ if (args->pitch < min_pitch)
+ args->pitch = min_pitch;
+ if (args->size < args->pitch * args->height)
+ args->size = PAGE_ALIGN(args->pitch * args->height);
+ }
+
+ shmem = drm_gem_shmem_create_with_handle(file, dev, args->size, fbdev,
+ &args->handle);
+
+ return PTR_ERR_OR_ZERO(shmem);
+}
+
/**
* drm_gem_shmem_dumb_create - Create a dumb shmem buffer object
* @file: DRM file structure to create the dumb buffer for
@@ -516,26 +556,38 @@ EXPORT_SYMBOL(drm_gem_shmem_purge);
int drm_gem_shmem_dumb_create(struct drm_file *file, struct drm_device *dev,
struct drm_mode_create_dumb *args)
{
- u32 min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
- struct drm_gem_shmem_object *shmem;
-
- if (!args->pitch || !args->size) {
- args->pitch = min_pitch;
- args->size = PAGE_ALIGN(args->pitch * args->height);
- } else {
- /* ensure sane minimum values */
- if (args->pitch < min_pitch)
- args->pitch = min_pitch;
- if (args->size < args->pitch * args->height)
- args->size = PAGE_ALIGN(args->pitch * args->height);
- }
-
- shmem = drm_gem_shmem_create_with_handle(file, dev, args->size, &args->handle);
-
- return PTR_ERR_OR_ZERO(shmem);
+ return __drm_gem_shmem_dumb_create(file, dev, false, args);
}
EXPORT_SYMBOL_GPL(drm_gem_shmem_dumb_create);
+/**
+ * drm_gem_shmem_dumb_create_fbdev - Create a dumb shmem buffer object for fbdev
+ * @file: DRM file structure to create the dumb buffer for
+ * @dev: DRM device
+ * @args: IOCTL data
+ *
+ * This function computes the pitch of the dumb buffer and rounds it up to an
+ * integer number of bytes per pixel. Drivers for hardware that doesn't have
+ * any additional restrictions on the pitch can directly use this function as
+ * their &drm_driver.dumb_create_fbdev callback.
+ *
+ * For hardware with additional restrictions, drivers can adjust the fields
+ * set up by userspace before calling into this function.
+ *
+ * Returns:
+ * 0 on success or a negative error code on failure.
+ */
+int drm_gem_shmem_dumb_create_fbdev(struct drm_file *file, struct drm_device *dev,
+ struct drm_mode_create_dumb *args)
+{
+#if defined(CONFIG_DRM_FBDEV_EMULATION)
+ return __drm_gem_shmem_dumb_create(file, dev, true, args);
+#else
+ return -ENOSYS;
+#endif
+}
+EXPORT_SYMBOL_GPL(drm_gem_shmem_dumb_create_fbdev);
+
static vm_fault_t drm_gem_shmem_fault(struct vm_fault *vmf)
{
struct vm_area_struct *vma = vmf->vma;
@@ -635,6 +687,103 @@ int drm_gem_shmem_mmap(struct drm_gem_shmem_object *shmem, struct vm_area_struct
}
EXPORT_SYMBOL_GPL(drm_gem_shmem_mmap);
+#if defined(CONFIG_DRM_FBDEV_EMULATION)
+static vm_fault_t drm_gem_shmem_fault_fbdev(struct vm_fault *vmf)
+{
+ struct vm_area_struct *vma = vmf->vma;
+ struct drm_gem_object *obj = vma->vm_private_data;
+ struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);
+ loff_t num_pages = obj->size >> PAGE_SHIFT;
+ struct drm_device *dev = obj->dev;
+ vm_fault_t ret;
+ struct page *page;
+ pgoff_t page_offset;
+
+ /* We don't use vmf->pgoff since that has the fake offset */
+ page_offset = (vmf->address - vma->vm_start) >> PAGE_SHIFT;
+
+ mutex_lock(&shmem->pages_lock);
+
+ if (page_offset >= num_pages || WARN_ON_ONCE(!shmem->pages) || shmem->madv < 0) {
+ ret = VM_FAULT_SIGBUS;
+ goto err_mutex_unlock;
+ }
+
+ page = shmem->pages[page_offset];
+
+ get_page(page);
+
+ if (vmf->vma->vm_file)
+ page->mapping = vmf->vma->vm_file->f_mapping;
+ else
+ drm_err(dev, "no mapping available\n");
+
+ if (drm_WARN_ON_ONCE(dev, !page->mapping)) {
+ ret = VM_FAULT_SIGBUS;
+ goto err_put_page;
+ }
+
+ /* for page_mkclean(); include the fake offset in the page index */
+ page->index = vmf->pgoff;
+
+ vmf->page = page;
+
+ mutex_unlock(&shmem->pages_lock);
+
+ return 0;
+
+err_put_page:
+ put_page(page);
+err_mutex_unlock:
+ mutex_unlock(&shmem->pages_lock);
+ return ret;
+}
+
+static vm_fault_t drm_gem_shmem_vm_page_mkwrite_fbdev(struct vm_fault *vmf)
+{
+ struct drm_gem_object *obj = vmf->vma->vm_private_data;
+
+ return drm_fb_helper_vm_page_mkwrite(obj->dev->fb_helper, vmf);
+}
+
+const struct vm_operations_struct drm_gem_shmem_vm_ops_fbdev = {
+ .open = drm_gem_shmem_vm_open,
+ .close = drm_gem_shmem_vm_close,
+ .fault = drm_gem_shmem_fault_fbdev,
+ .page_mkwrite = drm_gem_shmem_vm_page_mkwrite_fbdev,
+};
+EXPORT_SYMBOL_GPL(drm_gem_shmem_vm_ops_fbdev);
+
+int drm_gem_shmem_mmap_fbdev(struct drm_gem_shmem_object *shmem, struct vm_area_struct *vma)
+{
+ struct drm_gem_object *obj = &shmem->base;
+ int ret;
+
+ if (obj->import_attach) {
+ /* Drop the reference drm_gem_mmap_obj() acquired.*/
+ drm_gem_object_put(obj);
+ vma->vm_private_data = NULL;
+
+ return dma_buf_mmap(obj->dma_buf, vma, 0);
+ }
+
+ ret = drm_gem_shmem_get_pages(shmem);
+ if (ret) {
+ drm_gem_vm_close(vma);
+ return ret;
+ }
+
+ vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
+ vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
+ vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot);
+ if (shmem->map_wc)
+ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(drm_gem_shmem_mmap_fbdev);
+#endif
+
/**
* drm_gem_shmem_print_info() - Print &drm_gem_shmem_object info for debugfs
* @shmem: shmem GEM object
@@ -751,7 +900,7 @@ drm_gem_shmem_prime_import_sg_table(struct drm_device *dev,
size_t size = PAGE_ALIGN(attach->dmabuf->size);
struct drm_gem_shmem_object *shmem;
- shmem = __drm_gem_shmem_create(dev, size, true);
+ shmem = __drm_gem_shmem_create(dev, size, true, false);
if (IS_ERR(shmem))
return ERR_CAST(shmem);
@@ -382,7 +382,7 @@ DEFINE_DRM_GEM_FOPS(gud_fops);
static const struct drm_driver gud_drm_driver = {
.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
.fops = &gud_fops,
- DRM_GEM_SHMEM_DRIVER_OPS,
+ DRM_GEM_SHMEM_DRIVER_OPS_WITH_SHADOW_PLANES,
.gem_prime_import = gud_gem_prime_import,
.debugfs_init = gud_debugfs_init,
@@ -38,7 +38,7 @@ static struct drm_driver hyperv_driver = {
.minor = DRIVER_MINOR,
.fops = &hv_fops,
- DRM_GEM_SHMEM_DRIVER_OPS,
+ DRM_GEM_SHMEM_DRIVER_OPS_WITH_SHADOW_PLANES,
};
static int hyperv_pci_probe(struct pci_dev *pdev,
@@ -38,7 +38,7 @@ static const struct drm_driver mgag200_driver = {
.major = DRIVER_MAJOR,
.minor = DRIVER_MINOR,
.patchlevel = DRIVER_PATCHLEVEL,
- DRM_GEM_SHMEM_DRIVER_OPS,
+ DRM_GEM_SHMEM_DRIVER_OPS_WITH_SHADOW_PLANES,
};
/*
@@ -542,7 +542,7 @@ static const struct drm_driver cirrus_driver = {
.minor = DRIVER_MINOR,
.fops = &cirrus_fops,
- DRM_GEM_SHMEM_DRIVER_OPS,
+ DRM_GEM_SHMEM_DRIVER_OPS_WITH_SHADOW_PLANES,
};
static int cirrus_pci_probe(struct pci_dev *pdev,
@@ -620,7 +620,7 @@ static const struct drm_driver gm12u320_drm_driver = {
.minor = DRIVER_MINOR,
.fops = &gm12u320_fops,
- DRM_GEM_SHMEM_DRIVER_OPS,
+ DRM_GEM_SHMEM_DRIVER_OPS_WITH_SHADOW_PLANES,
.gem_prime_import = gm12u320_gem_prime_import,
};
@@ -871,7 +871,7 @@ simpledrm_device_create(struct drm_driver *drv, struct platform_device *pdev)
DEFINE_DRM_GEM_FOPS(simpledrm_fops);
static struct drm_driver simpledrm_driver = {
- DRM_GEM_SHMEM_DRIVER_OPS,
+ DRM_GEM_SHMEM_DRIVER_OPS_WITH_SHADOW_PLANES,
.name = DRIVER_NAME,
.desc = DRIVER_DESC,
.date = DRIVER_DATE,
@@ -55,7 +55,7 @@ static const struct drm_driver driver = {
/* GEM hooks */
.fops = &udl_driver_fops,
- DRM_GEM_SHMEM_DRIVER_OPS,
+ DRM_GEM_SHMEM_DRIVER_OPS_WITH_SHADOW_PLANES,
.gem_prime_import = udl_driver_gem_prime_import,
.name = DRIVER_NAME,
@@ -116,7 +116,7 @@ static const struct drm_driver vkms_driver = {
.driver_features = DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_GEM,
.release = vkms_release,
.fops = &vkms_driver_fops,
- DRM_GEM_SHMEM_DRIVER_OPS,
+ DRM_GEM_SHMEM_DRIVER_OPS_WITH_SHADOW_PLANES,
.debugfs_init = vkms_config_debugfs_init,
@@ -139,6 +139,11 @@ void drm_gem_shmem_print_info(const struct drm_gem_shmem_object *shmem,
extern const struct vm_operations_struct drm_gem_shmem_vm_ops;
+#if defined(CONFIG_DRM_FBDEV_EMULATION)
+extern const struct vm_operations_struct drm_gem_shmem_vm_ops_fbdev;
+int drm_gem_shmem_mmap_fbdev(struct drm_gem_shmem_object *shmem, struct vm_area_struct *vma);
+#endif
+
/*
* GEM object functions
*/
@@ -272,6 +277,27 @@ static inline int drm_gem_shmem_object_mmap(struct drm_gem_object *obj, struct v
return drm_gem_shmem_mmap(shmem, vma);
}
+#if defined(CONFIG_DRM_FBDEV_EMULATION)
+/**
+ * drm_gem_shmem_object_mmap_fbdev - GEM object function for drm_gem_shmem_mmap_fbdev()
+ * @obj: GEM object
+ * @vma: VMA for the area to be mapped
+ *
+ * This function wraps drm_gem_shmem_mmap(). Drivers that employ the shmem helpers should
+ * use it as their &drm_gem_object_funcs.mmap handler.
+ *
+ * Returns:
+ * 0 on success or a negative error code on failure.
+ */
+static inline int drm_gem_shmem_object_mmap_fbdev(struct drm_gem_object *obj,
+ struct vm_area_struct *vma)
+{
+ struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);
+
+ return drm_gem_shmem_mmap_fbdev(shmem, vma);
+}
+#endif
+
/*
* Driver ops
*/
@@ -282,18 +308,47 @@ drm_gem_shmem_prime_import_sg_table(struct drm_device *dev,
struct sg_table *sgt);
int drm_gem_shmem_dumb_create(struct drm_file *file, struct drm_device *dev,
struct drm_mode_create_dumb *args);
+int drm_gem_shmem_dumb_create_fbdev(struct drm_file *file, struct drm_device *dev,
+ struct drm_mode_create_dumb *args);
/**
- * DRM_GEM_SHMEM_DRIVER_OPS - Default shmem GEM operations
+ * DRM_GEM_SHMEM_DRIVER_OPS_WITH_DUMB_CREATE - Default shmem GEM operations
+ * @dumb_create_func: callback function for .dumb_create
+ * @dumb_create_fbdev_func: callback function for .dumb_create_fbdev
*
* This macro provides a shortcut for setting the shmem GEM operations in
- * the &drm_driver structure.
+ * the &drm_driver structure. The callbacks for creating dumb buffers are
+ * given as parameters. Use DRM_GEM_SHMEM_DRIVER_OPS or
+ * DRM_GEM_SHMEM_OPS_WITH_SHADOW_PLANES if possible.
*/
-#define DRM_GEM_SHMEM_DRIVER_OPS \
+#define DRM_GEM_SHMEM_DRIVER_OPS_WITH_DUMB_CREATE(dumb_create_func, dumb_create_fbdev_func) \
.prime_handle_to_fd = drm_gem_prime_handle_to_fd, \
.prime_fd_to_handle = drm_gem_prime_fd_to_handle, \
.gem_prime_import_sg_table = drm_gem_shmem_prime_import_sg_table, \
.gem_prime_mmap = drm_gem_prime_mmap, \
- .dumb_create = drm_gem_shmem_dumb_create
+ .dumb_create = dumb_create_func, \
+ .dumb_create_fbdev = dumb_create_fbdev_func
+
+/**
+ * DRM_GEM_SHMEM_DRIVER_OPS - Default shmem GEM operations
+ *
+ * This macro provides a shortcut for setting the shmem GEM operations in
+ * the &drm_driver structure.
+ */
+#define DRM_GEM_SHMEM_DRIVER_OPS \
+ DRM_GEM_SHMEM_DRIVER_OPS_WITH_DUMB_CREATE(drm_gem_shmem_dumb_create, NULL)
+
+/**
+ * DRM_GEM_SHMEM_DRIVER_OPS_WITH_SHADOW_PLANES - Default shmem GEM operations
+ * for drivers that use shadow
+ * planes
+ *
+ * This macro provides a shortcut for setting the shmem GEM operations in
+ * the &drm_driver structure. Drivers that employ shmem GEM for shadow
+ * buffering should use this macro.
+ */
+#define DRM_GEM_SHMEM_DRIVER_OPS_WITH_SHADOW_PLANES \
+ DRM_GEM_SHMEM_DRIVER_OPS_WITH_DUMB_CREATE(drm_gem_shmem_dumb_create, \
+ drm_gem_shmem_dumb_create_fbdev)
#endif /* __DRM_GEM_SHMEM_HELPER_H__ */
Implement struct drm_driver.dumb_create_fbdev for GEM SHMEM. The created buffer object returned by this function implements deferred I/O for its mmap operation. Add this feature to a number of drivers that use GEM SHMEM helpers as shadow planes over their regular video memory. The new macro DRM_GEM_SHMEM_DRIVER_OPS_WITH_SHADOW_PLANES sets the regular GEM functions and dumb_create_fbdev in struct drm_driver. Fbdev emulation on these drivers will now mmap the GEM SHMEM pages directly with deferred I/O without an intermediate shadow buffer. Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de> --- drivers/gpu/drm/drm_gem_shmem_helper.c | 197 +++++++++++++++++++++--- drivers/gpu/drm/gud/gud_drv.c | 2 +- drivers/gpu/drm/hyperv/hyperv_drm_drv.c | 2 +- drivers/gpu/drm/mgag200/mgag200_drv.c | 2 +- drivers/gpu/drm/tiny/cirrus.c | 2 +- drivers/gpu/drm/tiny/gm12u320.c | 2 +- drivers/gpu/drm/tiny/simpledrm.c | 2 +- drivers/gpu/drm/udl/udl_drv.c | 2 +- drivers/gpu/drm/vkms/vkms_drv.c | 2 +- include/drm/drm_gem_shmem_helper.h | 63 +++++++- 10 files changed, 240 insertions(+), 36 deletions(-)