@@ -617,6 +617,21 @@ Contact: Daniel Vetter
Level: Intermediate
+Remove automatic page mapping from dma-buf importing
+----------------------------------------------------
+
+When importing dma-bufs, the dma-buf and PRIME frameworks automatically map
+imported pages into the importer's DMA area. This is a problem for USB devices,
+which do not support DMA operations. By default, importing fails for USB
+devices. USB-based drivers work around this problem by employing
+drm_gem_prime_import_usb(). To fix the issue, automatic page mappings should
+be removed from the buffer-sharing code.
+
+Contact: Thomas Zimmermann <tzimmermann@suse.de>, Daniel Vetter
+
+Level: Advanced
+
+
Better Testing
==============
@@ -29,6 +29,7 @@
#include <linux/export.h>
#include <linux/dma-buf.h>
#include <linux/rbtree.h>
+#include <linux/usb.h>
#include <drm/drm.h>
#include <drm/drm_drv.h>
@@ -1055,3 +1056,47 @@ void drm_prime_gem_destroy(struct drm_gem_object *obj, struct sg_table *sg)
dma_buf_put(dma_buf);
}
EXPORT_SYMBOL(drm_prime_gem_destroy);
+
+/**
+ * drm_gem_prime_import_usb - helper library implementation of the import callback for USB devices
+ * @dev: drm_device to import into
+ * @dma_buf: dma-buf object to import
+ *
+ * This is an implementation of drm_gem_prime_import() for USB-based devices.
+ * USB devices cannot perform DMA directly. This function selects the USB host
+ * controller as DMA device instead. Drivers can use this as their
+ * &drm_driver.gem_prime_import implementation.
+ *
+ * See also drm_gem_prime_import().
+ *
+ * FIXME: The dma-buf framework expects to map the exported pages into
+ * the importer's DMA area. USB devices don't support DMA, and
+ * importing would fail. Foir the time being, this function provides
+ * a workaround by using the USB controller's DMA area. The real
+ * solution is to remove page-mapping operations from the dma-buf
+ * framework.
+ *
+ * Returns: A GEM object on success, or a pointer-encoder errno value otherwise.
+ */
+#ifdef CONFIG_USB
+struct drm_gem_object *drm_gem_prime_import_usb(struct drm_device *dev,
+ struct dma_buf *dma_buf)
+{
+ struct device *dmadev;
+ struct drm_gem_object *obj;
+
+ if (!dev_is_usb(dev->dev))
+ return ERR_PTR(-ENODEV);
+
+ dmadev = usb_intf_get_dma_device(to_usb_interface(dev->dev));
+ if (drm_WARN_ONCE(dev, !dmadev, "buffer sharing not supported"))
+ return ERR_PTR(-ENODEV);
+
+ obj = drm_gem_prime_import_dev(dev, dma_buf, dmadev);
+
+ put_device(dmadev);
+
+ return obj;
+}
+EXPORT_SYMBOL(drm_gem_prime_import_usb);
+#endif
@@ -611,7 +611,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_USB,
};
static const struct drm_mode_config_funcs gm12u320_mode_config_funcs = {
@@ -39,7 +39,7 @@ static const struct drm_driver driver = {
/* GEM hooks */
.fops = &udl_driver_fops,
- DRM_GEM_SHMEM_DRIVER_OPS,
+ DRM_GEM_SHMEM_DRIVER_OPS_USB,
.name = DRIVER_NAME,
.desc = DRIVER_DESC,
@@ -748,6 +748,37 @@ void usb_put_intf(struct usb_interface *intf)
}
EXPORT_SYMBOL_GPL(usb_put_intf);
+/**
+ * usb_get_dma_device - acquire a reference on the usb device's DMA endpoint
+ * @udev: usb device
+ *
+ * While a USB device cannot perform DMA operations by itself, many USB
+ * controllers can. A call to usb_get_dma_device() returns the DMA endpoint
+ * for the given USB device, if any. The returned device structure should be
+ * released with put_device().
+ *
+ * See also usb_intf_get_dma_device().
+ *
+ * Returns: A reference to the usb device's DMA endpoint; or NULL if none
+ * exists.
+ */
+struct device *usb_get_dma_device(struct usb_device *udev)
+{
+ struct device *dmadev;
+
+ if (!udev->bus)
+ return NULL;
+
+ dmadev = get_device(udev->bus->sysdev);
+ if (!dmadev || !dmadev->dma_mask) {
+ put_device(dmadev);
+ return NULL;
+ }
+
+ return dmadev;
+}
+EXPORT_SYMBOL_GPL(usb_get_dma_device);
+
/* USB device locking
*
* USB devices and interfaces are locked using the semaphore in their
@@ -162,4 +162,20 @@ struct sg_table *drm_gem_shmem_get_pages_sgt(struct drm_gem_object *obj);
.gem_prime_mmap = drm_gem_prime_mmap, \
.dumb_create = drm_gem_shmem_dumb_create
+#ifdef CONFIG_USB
+/**
+ * DRM_GEM_SHMEM_DRIVER_OPS_USB - Default shmem GEM operations for USB devices
+ *
+ * This macro provides a shortcut for setting the shmem GEM operations in
+ * the &drm_driver structure. Drivers for USB-based devices should use this
+ * macro instead of &DRM_GEM_SHMEM_DRIVER_OPS.
+ *
+ * FIXME: Support USB devices with default SHMEM driver ops. See the
+ * documentation of drm_gem_prime_import_usb() for details.
+ */
+#define DRM_GEM_SHMEM_DRIVER_OPS_USB \
+ DRM_GEM_SHMEM_DRIVER_OPS, \
+ .gem_prime_import = drm_gem_prime_import_usb
+#endif
+
#endif /* __DRM_GEM_SHMEM_HELPER_H__ */
@@ -110,4 +110,9 @@ int drm_prime_sg_to_page_array(struct sg_table *sgt, struct page **pages,
int drm_prime_sg_to_dma_addr_array(struct sg_table *sgt, dma_addr_t *addrs,
int max_pages);
+#ifdef CONFIG_USB
+struct drm_gem_object *drm_gem_prime_import_usb(struct drm_device *dev,
+ struct dma_buf *dma_buf);
+#endif
+
#endif /* __DRM_PRIME_H__ */
@@ -711,6 +711,7 @@ struct usb_device {
unsigned use_generic_driver:1;
};
#define to_usb_device(d) container_of(d, struct usb_device, dev)
+#define dev_is_usb(d) ((d)->bus == &usb_bus_type)
static inline struct usb_device *interface_to_usbdev(struct usb_interface *intf)
{
@@ -746,6 +747,29 @@ extern int usb_lock_device_for_reset(struct usb_device *udev,
extern int usb_reset_device(struct usb_device *dev);
extern void usb_queue_reset_device(struct usb_interface *dev);
+extern struct device *usb_get_dma_device(struct usb_device *udev);
+
+/**
+ * usb_intf_get_dma_device - acquire a reference on the usb interface's DMA endpoint
+ * @intf: the usb interface
+ *
+ * While a USB device cannot perform DMA operations by itself, many USB
+ * controllers can. A call to usb_intf_get_dma_device() returns the DMA endpoint
+ * for the given USB interface, if any. The returned device structure should be
+ * released with put_device().
+ *
+ * See also usb_get_dma_device().
+ *
+ * Returns: A reference to the usb interface's DMA endpoint; or NULL if none
+ * exists.
+ */
+static inline struct device *usb_intf_get_dma_device(struct usb_interface *intf)
+{
+ if (!intf)
+ return NULL;
+ return usb_get_dma_device(interface_to_usbdev(intf));
+}
+
#ifdef CONFIG_ACPI
extern int usb_acpi_set_power_state(struct usb_device *hdev, int index,
bool enable);