140e1a70bSNoralf Trønnes // SPDX-License-Identifier: MIT 240e1a70bSNoralf Trønnes /* 340e1a70bSNoralf Trønnes * Copyright 2020 Noralf Trønnes 440e1a70bSNoralf Trønnes */ 540e1a70bSNoralf Trønnes 640e1a70bSNoralf Trønnes #include <linux/dma-buf.h> 740e1a70bSNoralf Trønnes #include <linux/dma-mapping.h> 840e1a70bSNoralf Trønnes #include <linux/lz4.h> 940e1a70bSNoralf Trønnes #include <linux/module.h> 1040e1a70bSNoralf Trønnes #include <linux/platform_device.h> 1140e1a70bSNoralf Trønnes #include <linux/string_helpers.h> 1240e1a70bSNoralf Trønnes #include <linux/usb.h> 1340e1a70bSNoralf Trønnes #include <linux/vmalloc.h> 1440e1a70bSNoralf Trønnes #include <linux/workqueue.h> 1540e1a70bSNoralf Trønnes 1640e1a70bSNoralf Trønnes #include <drm/drm_atomic_helper.h> 1740e1a70bSNoralf Trønnes #include <drm/drm_damage_helper.h> 1840e1a70bSNoralf Trønnes #include <drm/drm_debugfs.h> 1940e1a70bSNoralf Trønnes #include <drm/drm_drv.h> 2040e1a70bSNoralf Trønnes #include <drm/drm_fb_helper.h> 2140e1a70bSNoralf Trønnes #include <drm/drm_fourcc.h> 2240e1a70bSNoralf Trønnes #include <drm/drm_gem_atomic_helper.h> 2340e1a70bSNoralf Trønnes #include <drm/drm_gem_framebuffer_helper.h> 2440e1a70bSNoralf Trønnes #include <drm/drm_gem_shmem_helper.h> 2540e1a70bSNoralf Trønnes #include <drm/drm_managed.h> 2640e1a70bSNoralf Trønnes #include <drm/drm_print.h> 2740e1a70bSNoralf Trønnes #include <drm/drm_probe_helper.h> 2840e1a70bSNoralf Trønnes #include <drm/drm_simple_kms_helper.h> 2940e1a70bSNoralf Trønnes #include <drm/gud.h> 3040e1a70bSNoralf Trønnes 3140e1a70bSNoralf Trønnes #include "gud_internal.h" 3240e1a70bSNoralf Trønnes 3340e1a70bSNoralf Trønnes /* Only used internally */ 3440e1a70bSNoralf Trønnes static const struct drm_format_info gud_drm_format_r1 = { 3540e1a70bSNoralf Trønnes .format = GUD_DRM_FORMAT_R1, 3640e1a70bSNoralf Trønnes .num_planes = 1, 3740e1a70bSNoralf Trønnes .char_per_block = { 1, 0, 0 }, 3840e1a70bSNoralf Trønnes .block_w = { 8, 0, 0 }, 3940e1a70bSNoralf Trønnes .block_h = { 1, 0, 0 }, 4040e1a70bSNoralf Trønnes .hsub = 1, 4140e1a70bSNoralf Trønnes .vsub = 1, 4240e1a70bSNoralf Trønnes }; 4340e1a70bSNoralf Trønnes 4440e1a70bSNoralf Trønnes static const struct drm_format_info gud_drm_format_xrgb1111 = { 4540e1a70bSNoralf Trønnes .format = GUD_DRM_FORMAT_XRGB1111, 4640e1a70bSNoralf Trønnes .num_planes = 1, 4740e1a70bSNoralf Trønnes .char_per_block = { 1, 0, 0 }, 4840e1a70bSNoralf Trønnes .block_w = { 2, 0, 0 }, 4940e1a70bSNoralf Trønnes .block_h = { 1, 0, 0 }, 5040e1a70bSNoralf Trønnes .hsub = 1, 5140e1a70bSNoralf Trønnes .vsub = 1, 5240e1a70bSNoralf Trønnes }; 5340e1a70bSNoralf Trønnes 5440e1a70bSNoralf Trønnes static int gud_usb_control_msg(struct usb_interface *intf, bool in, 5540e1a70bSNoralf Trønnes u8 request, u16 value, void *buf, size_t len) 5640e1a70bSNoralf Trønnes { 5740e1a70bSNoralf Trønnes u8 requesttype = USB_TYPE_VENDOR | USB_RECIP_INTERFACE; 5840e1a70bSNoralf Trønnes u8 ifnum = intf->cur_altsetting->desc.bInterfaceNumber; 5940e1a70bSNoralf Trønnes struct usb_device *usb = interface_to_usbdev(intf); 6040e1a70bSNoralf Trønnes unsigned int pipe; 6140e1a70bSNoralf Trønnes 6240e1a70bSNoralf Trønnes if (len && !buf) 6340e1a70bSNoralf Trønnes return -EINVAL; 6440e1a70bSNoralf Trønnes 6540e1a70bSNoralf Trønnes if (in) { 6640e1a70bSNoralf Trønnes pipe = usb_rcvctrlpipe(usb, 0); 6740e1a70bSNoralf Trønnes requesttype |= USB_DIR_IN; 6840e1a70bSNoralf Trønnes } else { 6940e1a70bSNoralf Trønnes pipe = usb_sndctrlpipe(usb, 0); 7040e1a70bSNoralf Trønnes requesttype |= USB_DIR_OUT; 7140e1a70bSNoralf Trønnes } 7240e1a70bSNoralf Trønnes 7340e1a70bSNoralf Trønnes return usb_control_msg(usb, pipe, request, requesttype, value, 7440e1a70bSNoralf Trønnes ifnum, buf, len, USB_CTRL_GET_TIMEOUT); 7540e1a70bSNoralf Trønnes } 7640e1a70bSNoralf Trønnes 7740e1a70bSNoralf Trønnes static int gud_get_display_descriptor(struct usb_interface *intf, 7840e1a70bSNoralf Trønnes struct gud_display_descriptor_req *desc) 7940e1a70bSNoralf Trønnes { 8040e1a70bSNoralf Trønnes void *buf; 8140e1a70bSNoralf Trønnes int ret; 8240e1a70bSNoralf Trønnes 8340e1a70bSNoralf Trønnes buf = kmalloc(sizeof(*desc), GFP_KERNEL); 8440e1a70bSNoralf Trønnes if (!buf) 8540e1a70bSNoralf Trønnes return -ENOMEM; 8640e1a70bSNoralf Trønnes 8740e1a70bSNoralf Trønnes ret = gud_usb_control_msg(intf, true, GUD_REQ_GET_DESCRIPTOR, 0, buf, sizeof(*desc)); 8840e1a70bSNoralf Trønnes memcpy(desc, buf, sizeof(*desc)); 8940e1a70bSNoralf Trønnes kfree(buf); 9040e1a70bSNoralf Trønnes if (ret < 0) 9140e1a70bSNoralf Trønnes return ret; 9240e1a70bSNoralf Trønnes if (ret != sizeof(*desc)) 9340e1a70bSNoralf Trønnes return -EIO; 9440e1a70bSNoralf Trønnes 9540e1a70bSNoralf Trønnes if (desc->magic != le32_to_cpu(GUD_DISPLAY_MAGIC)) 9640e1a70bSNoralf Trønnes return -ENODATA; 9740e1a70bSNoralf Trønnes 9840e1a70bSNoralf Trønnes DRM_DEV_DEBUG_DRIVER(&intf->dev, 9940e1a70bSNoralf Trønnes "version=%u flags=0x%x compression=0x%x max_buffer_size=%u\n", 10040e1a70bSNoralf Trønnes desc->version, le32_to_cpu(desc->flags), desc->compression, 10140e1a70bSNoralf Trønnes le32_to_cpu(desc->max_buffer_size)); 10240e1a70bSNoralf Trønnes 10340e1a70bSNoralf Trønnes if (!desc->version || !desc->max_width || !desc->max_height || 10440e1a70bSNoralf Trønnes le32_to_cpu(desc->min_width) > le32_to_cpu(desc->max_width) || 10540e1a70bSNoralf Trønnes le32_to_cpu(desc->min_height) > le32_to_cpu(desc->max_height)) 10640e1a70bSNoralf Trønnes return -EINVAL; 10740e1a70bSNoralf Trønnes 10840e1a70bSNoralf Trønnes return 0; 10940e1a70bSNoralf Trønnes } 11040e1a70bSNoralf Trønnes 11140e1a70bSNoralf Trønnes static int gud_status_to_errno(u8 status) 11240e1a70bSNoralf Trønnes { 11340e1a70bSNoralf Trønnes switch (status) { 11440e1a70bSNoralf Trønnes case GUD_STATUS_OK: 11540e1a70bSNoralf Trønnes return 0; 11640e1a70bSNoralf Trønnes case GUD_STATUS_BUSY: 11740e1a70bSNoralf Trønnes return -EBUSY; 11840e1a70bSNoralf Trønnes case GUD_STATUS_REQUEST_NOT_SUPPORTED: 11940e1a70bSNoralf Trønnes return -EOPNOTSUPP; 12040e1a70bSNoralf Trønnes case GUD_STATUS_PROTOCOL_ERROR: 12140e1a70bSNoralf Trønnes return -EPROTO; 12240e1a70bSNoralf Trønnes case GUD_STATUS_INVALID_PARAMETER: 12340e1a70bSNoralf Trønnes return -EINVAL; 12440e1a70bSNoralf Trønnes case GUD_STATUS_ERROR: 12540e1a70bSNoralf Trønnes return -EREMOTEIO; 12640e1a70bSNoralf Trønnes default: 12740e1a70bSNoralf Trønnes return -EREMOTEIO; 12840e1a70bSNoralf Trønnes } 12940e1a70bSNoralf Trønnes } 13040e1a70bSNoralf Trønnes 13140e1a70bSNoralf Trønnes static int gud_usb_get_status(struct usb_interface *intf) 13240e1a70bSNoralf Trønnes { 13340e1a70bSNoralf Trønnes int ret, status = -EIO; 13440e1a70bSNoralf Trønnes u8 *buf; 13540e1a70bSNoralf Trønnes 13640e1a70bSNoralf Trønnes buf = kmalloc(sizeof(*buf), GFP_KERNEL); 13740e1a70bSNoralf Trønnes if (!buf) 13840e1a70bSNoralf Trønnes return -ENOMEM; 13940e1a70bSNoralf Trønnes 14040e1a70bSNoralf Trønnes ret = gud_usb_control_msg(intf, true, GUD_REQ_GET_STATUS, 0, buf, sizeof(*buf)); 14140e1a70bSNoralf Trønnes if (ret == sizeof(*buf)) 14240e1a70bSNoralf Trønnes status = gud_status_to_errno(*buf); 14340e1a70bSNoralf Trønnes kfree(buf); 14440e1a70bSNoralf Trønnes 14540e1a70bSNoralf Trønnes if (ret < 0) 14640e1a70bSNoralf Trønnes return ret; 14740e1a70bSNoralf Trønnes 14840e1a70bSNoralf Trønnes return status; 14940e1a70bSNoralf Trønnes } 15040e1a70bSNoralf Trønnes 15140e1a70bSNoralf Trønnes static int gud_usb_transfer(struct gud_device *gdrm, bool in, u8 request, u16 index, 15240e1a70bSNoralf Trønnes void *buf, size_t len) 15340e1a70bSNoralf Trønnes { 15440e1a70bSNoralf Trønnes struct usb_interface *intf = to_usb_interface(gdrm->drm.dev); 15540e1a70bSNoralf Trønnes int idx, ret; 15640e1a70bSNoralf Trønnes 15740e1a70bSNoralf Trønnes drm_dbg(&gdrm->drm, "%s: request=0x%x index=%u len=%zu\n", 15840e1a70bSNoralf Trønnes in ? "get" : "set", request, index, len); 15940e1a70bSNoralf Trønnes 16040e1a70bSNoralf Trønnes if (!drm_dev_enter(&gdrm->drm, &idx)) 16140e1a70bSNoralf Trønnes return -ENODEV; 16240e1a70bSNoralf Trønnes 16340e1a70bSNoralf Trønnes mutex_lock(&gdrm->ctrl_lock); 16440e1a70bSNoralf Trønnes 16540e1a70bSNoralf Trønnes ret = gud_usb_control_msg(intf, in, request, index, buf, len); 16640e1a70bSNoralf Trønnes if (ret == -EPIPE || ((gdrm->flags & GUD_DISPLAY_FLAG_STATUS_ON_SET) && !in && ret >= 0)) { 16740e1a70bSNoralf Trønnes int status; 16840e1a70bSNoralf Trønnes 16940e1a70bSNoralf Trønnes status = gud_usb_get_status(intf); 17040e1a70bSNoralf Trønnes if (status < 0) { 17140e1a70bSNoralf Trønnes ret = status; 17240e1a70bSNoralf Trønnes } else if (ret < 0) { 17340e1a70bSNoralf Trønnes dev_err_once(gdrm->drm.dev, 17440e1a70bSNoralf Trønnes "Unexpected status OK for failed transfer\n"); 17540e1a70bSNoralf Trønnes ret = -EPIPE; 17640e1a70bSNoralf Trønnes } 17740e1a70bSNoralf Trønnes } 17840e1a70bSNoralf Trønnes 17940e1a70bSNoralf Trønnes if (ret < 0) { 18040e1a70bSNoralf Trønnes drm_dbg(&gdrm->drm, "ret=%d\n", ret); 18140e1a70bSNoralf Trønnes gdrm->stats_num_errors++; 18240e1a70bSNoralf Trønnes } 18340e1a70bSNoralf Trønnes 18440e1a70bSNoralf Trønnes mutex_unlock(&gdrm->ctrl_lock); 18540e1a70bSNoralf Trønnes drm_dev_exit(idx); 18640e1a70bSNoralf Trønnes 18740e1a70bSNoralf Trønnes return ret; 18840e1a70bSNoralf Trønnes } 18940e1a70bSNoralf Trønnes 19040e1a70bSNoralf Trønnes /* 19140e1a70bSNoralf Trønnes * @buf cannot be allocated on the stack. 19240e1a70bSNoralf Trønnes * Returns number of bytes received or negative error code on failure. 19340e1a70bSNoralf Trønnes */ 19440e1a70bSNoralf Trønnes int gud_usb_get(struct gud_device *gdrm, u8 request, u16 index, void *buf, size_t max_len) 19540e1a70bSNoralf Trønnes { 19640e1a70bSNoralf Trønnes return gud_usb_transfer(gdrm, true, request, index, buf, max_len); 19740e1a70bSNoralf Trønnes } 19840e1a70bSNoralf Trønnes 19940e1a70bSNoralf Trønnes /* 20040e1a70bSNoralf Trønnes * @buf can be allocated on the stack or NULL. 20140e1a70bSNoralf Trønnes * Returns zero on success or negative error code on failure. 20240e1a70bSNoralf Trønnes */ 20340e1a70bSNoralf Trønnes int gud_usb_set(struct gud_device *gdrm, u8 request, u16 index, void *buf, size_t len) 20440e1a70bSNoralf Trønnes { 20540e1a70bSNoralf Trønnes void *trbuf = NULL; 20640e1a70bSNoralf Trønnes int ret; 20740e1a70bSNoralf Trønnes 20840e1a70bSNoralf Trønnes if (buf && len) { 20940e1a70bSNoralf Trønnes trbuf = kmemdup(buf, len, GFP_KERNEL); 21040e1a70bSNoralf Trønnes if (!trbuf) 21140e1a70bSNoralf Trønnes return -ENOMEM; 21240e1a70bSNoralf Trønnes } 21340e1a70bSNoralf Trønnes 21440e1a70bSNoralf Trønnes ret = gud_usb_transfer(gdrm, false, request, index, trbuf, len); 21540e1a70bSNoralf Trønnes kfree(trbuf); 21640e1a70bSNoralf Trønnes if (ret < 0) 21740e1a70bSNoralf Trønnes return ret; 21840e1a70bSNoralf Trønnes 21940e1a70bSNoralf Trønnes return ret != len ? -EIO : 0; 22040e1a70bSNoralf Trønnes } 22140e1a70bSNoralf Trønnes 22240e1a70bSNoralf Trønnes /* 22340e1a70bSNoralf Trønnes * @val can be allocated on the stack. 22440e1a70bSNoralf Trønnes * Returns zero on success or negative error code on failure. 22540e1a70bSNoralf Trønnes */ 22640e1a70bSNoralf Trønnes int gud_usb_get_u8(struct gud_device *gdrm, u8 request, u16 index, u8 *val) 22740e1a70bSNoralf Trønnes { 22840e1a70bSNoralf Trønnes u8 *buf; 22940e1a70bSNoralf Trønnes int ret; 23040e1a70bSNoralf Trønnes 23140e1a70bSNoralf Trønnes buf = kmalloc(sizeof(*val), GFP_KERNEL); 23240e1a70bSNoralf Trønnes if (!buf) 23340e1a70bSNoralf Trønnes return -ENOMEM; 23440e1a70bSNoralf Trønnes 23540e1a70bSNoralf Trønnes ret = gud_usb_get(gdrm, request, index, buf, sizeof(*val)); 23640e1a70bSNoralf Trønnes *val = *buf; 23740e1a70bSNoralf Trønnes kfree(buf); 23840e1a70bSNoralf Trønnes if (ret < 0) 23940e1a70bSNoralf Trønnes return ret; 24040e1a70bSNoralf Trønnes 24140e1a70bSNoralf Trønnes return ret != sizeof(*val) ? -EIO : 0; 24240e1a70bSNoralf Trønnes } 24340e1a70bSNoralf Trønnes 24440e1a70bSNoralf Trønnes /* Returns zero on success or negative error code on failure. */ 24540e1a70bSNoralf Trønnes int gud_usb_set_u8(struct gud_device *gdrm, u8 request, u8 val) 24640e1a70bSNoralf Trønnes { 24740e1a70bSNoralf Trønnes return gud_usb_set(gdrm, request, 0, &val, sizeof(val)); 24840e1a70bSNoralf Trønnes } 24940e1a70bSNoralf Trønnes 25040e1a70bSNoralf Trønnes static int gud_get_properties(struct gud_device *gdrm) 25140e1a70bSNoralf Trønnes { 25240e1a70bSNoralf Trønnes struct gud_property_req *properties; 25340e1a70bSNoralf Trønnes unsigned int i, num_properties; 25440e1a70bSNoralf Trønnes int ret; 25540e1a70bSNoralf Trønnes 25640e1a70bSNoralf Trønnes properties = kcalloc(GUD_PROPERTIES_MAX_NUM, sizeof(*properties), GFP_KERNEL); 25740e1a70bSNoralf Trønnes if (!properties) 25840e1a70bSNoralf Trønnes return -ENOMEM; 25940e1a70bSNoralf Trønnes 26040e1a70bSNoralf Trønnes ret = gud_usb_get(gdrm, GUD_REQ_GET_PROPERTIES, 0, 26140e1a70bSNoralf Trønnes properties, GUD_PROPERTIES_MAX_NUM * sizeof(*properties)); 26240e1a70bSNoralf Trønnes if (ret <= 0) 26340e1a70bSNoralf Trønnes goto out; 26440e1a70bSNoralf Trønnes if (ret % sizeof(*properties)) { 26540e1a70bSNoralf Trønnes ret = -EIO; 26640e1a70bSNoralf Trønnes goto out; 26740e1a70bSNoralf Trønnes } 26840e1a70bSNoralf Trønnes 26940e1a70bSNoralf Trønnes num_properties = ret / sizeof(*properties); 27040e1a70bSNoralf Trønnes ret = 0; 27140e1a70bSNoralf Trønnes 27240e1a70bSNoralf Trønnes gdrm->properties = drmm_kcalloc(&gdrm->drm, num_properties, sizeof(*gdrm->properties), 27340e1a70bSNoralf Trønnes GFP_KERNEL); 27440e1a70bSNoralf Trønnes if (!gdrm->properties) { 27540e1a70bSNoralf Trønnes ret = -ENOMEM; 27640e1a70bSNoralf Trønnes goto out; 27740e1a70bSNoralf Trønnes } 27840e1a70bSNoralf Trønnes 27940e1a70bSNoralf Trønnes for (i = 0; i < num_properties; i++) { 28040e1a70bSNoralf Trønnes u16 prop = le16_to_cpu(properties[i].prop); 28140e1a70bSNoralf Trønnes u64 val = le64_to_cpu(properties[i].val); 28240e1a70bSNoralf Trønnes 28340e1a70bSNoralf Trønnes switch (prop) { 28440e1a70bSNoralf Trønnes case GUD_PROPERTY_ROTATION: 28540e1a70bSNoralf Trønnes /* 28640e1a70bSNoralf Trønnes * DRM UAPI matches the protocol so use the value directly, 28740e1a70bSNoralf Trønnes * but mask out any additions on future devices. 28840e1a70bSNoralf Trønnes */ 28940e1a70bSNoralf Trønnes val &= GUD_ROTATION_MASK; 29040e1a70bSNoralf Trønnes ret = drm_plane_create_rotation_property(&gdrm->pipe.plane, 29140e1a70bSNoralf Trønnes DRM_MODE_ROTATE_0, val); 29240e1a70bSNoralf Trønnes break; 29340e1a70bSNoralf Trønnes default: 29440e1a70bSNoralf Trønnes /* New ones might show up in future devices, skip those we don't know. */ 29540e1a70bSNoralf Trønnes drm_dbg(&gdrm->drm, "Ignoring unknown property: %u\n", prop); 29640e1a70bSNoralf Trønnes continue; 29740e1a70bSNoralf Trønnes } 29840e1a70bSNoralf Trønnes 29940e1a70bSNoralf Trønnes if (ret) 30040e1a70bSNoralf Trønnes goto out; 30140e1a70bSNoralf Trønnes 30240e1a70bSNoralf Trønnes gdrm->properties[gdrm->num_properties++] = prop; 30340e1a70bSNoralf Trønnes } 30440e1a70bSNoralf Trønnes out: 30540e1a70bSNoralf Trønnes kfree(properties); 30640e1a70bSNoralf Trønnes 30740e1a70bSNoralf Trønnes return ret; 30840e1a70bSNoralf Trønnes } 30940e1a70bSNoralf Trønnes 31040e1a70bSNoralf Trønnes /* 31140e1a70bSNoralf Trønnes * FIXME: Dma-buf sharing requires DMA support by the importing device. 31240e1a70bSNoralf Trønnes * This function is a workaround to make USB devices work as well. 31340e1a70bSNoralf Trønnes * See todo.rst for how to fix the issue in the dma-buf framework. 31440e1a70bSNoralf Trønnes */ 31540e1a70bSNoralf Trønnes static struct drm_gem_object *gud_gem_prime_import(struct drm_device *drm, struct dma_buf *dma_buf) 31640e1a70bSNoralf Trønnes { 31740e1a70bSNoralf Trønnes struct gud_device *gdrm = to_gud_device(drm); 31840e1a70bSNoralf Trønnes 31940e1a70bSNoralf Trønnes if (!gdrm->dmadev) 32040e1a70bSNoralf Trønnes return ERR_PTR(-ENODEV); 32140e1a70bSNoralf Trønnes 32240e1a70bSNoralf Trønnes return drm_gem_prime_import_dev(drm, dma_buf, gdrm->dmadev); 32340e1a70bSNoralf Trønnes } 32440e1a70bSNoralf Trønnes 32540e1a70bSNoralf Trønnes static int gud_stats_debugfs(struct seq_file *m, void *data) 32640e1a70bSNoralf Trønnes { 32740e1a70bSNoralf Trønnes struct drm_info_node *node = m->private; 32840e1a70bSNoralf Trønnes struct gud_device *gdrm = to_gud_device(node->minor->dev); 32940e1a70bSNoralf Trønnes char buf[10]; 33040e1a70bSNoralf Trønnes 33140e1a70bSNoralf Trønnes string_get_size(gdrm->bulk_len, 1, STRING_UNITS_2, buf, sizeof(buf)); 33240e1a70bSNoralf Trønnes seq_printf(m, "Max buffer size: %s\n", buf); 33340e1a70bSNoralf Trønnes seq_printf(m, "Number of errors: %u\n", gdrm->stats_num_errors); 33440e1a70bSNoralf Trønnes 33540e1a70bSNoralf Trønnes seq_puts(m, "Compression: "); 33640e1a70bSNoralf Trønnes if (gdrm->compression & GUD_COMPRESSION_LZ4) 33740e1a70bSNoralf Trønnes seq_puts(m, " lz4"); 33840e1a70bSNoralf Trønnes if (!gdrm->compression) 33940e1a70bSNoralf Trønnes seq_puts(m, " none"); 34040e1a70bSNoralf Trønnes seq_puts(m, "\n"); 34140e1a70bSNoralf Trønnes 34240e1a70bSNoralf Trønnes if (gdrm->compression) { 34340e1a70bSNoralf Trønnes u64 remainder; 34440e1a70bSNoralf Trønnes u64 ratio = div64_u64_rem(gdrm->stats_length, gdrm->stats_actual_length, 34540e1a70bSNoralf Trønnes &remainder); 34640e1a70bSNoralf Trønnes u64 ratio_frac = div64_u64(remainder * 10, gdrm->stats_actual_length); 34740e1a70bSNoralf Trønnes 34840e1a70bSNoralf Trønnes seq_printf(m, "Compression ratio: %llu.%llu\n", ratio, ratio_frac); 34940e1a70bSNoralf Trønnes } 35040e1a70bSNoralf Trønnes 35140e1a70bSNoralf Trønnes return 0; 35240e1a70bSNoralf Trønnes } 35340e1a70bSNoralf Trønnes 35440e1a70bSNoralf Trønnes static const struct drm_info_list gud_debugfs_list[] = { 35540e1a70bSNoralf Trønnes { "stats", gud_stats_debugfs, 0, NULL }, 35640e1a70bSNoralf Trønnes }; 35740e1a70bSNoralf Trønnes 35840e1a70bSNoralf Trønnes static void gud_debugfs_init(struct drm_minor *minor) 35940e1a70bSNoralf Trønnes { 36040e1a70bSNoralf Trønnes drm_debugfs_create_files(gud_debugfs_list, ARRAY_SIZE(gud_debugfs_list), 36140e1a70bSNoralf Trønnes minor->debugfs_root, minor); 36240e1a70bSNoralf Trønnes } 36340e1a70bSNoralf Trønnes 36440e1a70bSNoralf Trønnes static const struct drm_simple_display_pipe_funcs gud_pipe_funcs = { 36540e1a70bSNoralf Trønnes .check = gud_pipe_check, 36640e1a70bSNoralf Trønnes .update = gud_pipe_update, 36740e1a70bSNoralf Trønnes }; 36840e1a70bSNoralf Trønnes 36940e1a70bSNoralf Trønnes static const struct drm_mode_config_funcs gud_mode_config_funcs = { 37040e1a70bSNoralf Trønnes .fb_create = drm_gem_fb_create_with_dirty, 37140e1a70bSNoralf Trønnes .atomic_check = drm_atomic_helper_check, 37240e1a70bSNoralf Trønnes .atomic_commit = drm_atomic_helper_commit, 37340e1a70bSNoralf Trønnes }; 37440e1a70bSNoralf Trønnes 37540e1a70bSNoralf Trønnes static const u64 gud_pipe_modifiers[] = { 37640e1a70bSNoralf Trønnes DRM_FORMAT_MOD_LINEAR, 37740e1a70bSNoralf Trønnes DRM_FORMAT_MOD_INVALID 37840e1a70bSNoralf Trønnes }; 37940e1a70bSNoralf Trønnes 38040e1a70bSNoralf Trønnes DEFINE_DRM_GEM_FOPS(gud_fops); 38140e1a70bSNoralf Trønnes 38240e1a70bSNoralf Trønnes static const struct drm_driver gud_drm_driver = { 38340e1a70bSNoralf Trønnes .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC, 38440e1a70bSNoralf Trønnes .fops = &gud_fops, 38540e1a70bSNoralf Trønnes DRM_GEM_SHMEM_DRIVER_OPS, 38640e1a70bSNoralf Trønnes .gem_prime_import = gud_gem_prime_import, 38740e1a70bSNoralf Trønnes .debugfs_init = gud_debugfs_init, 38840e1a70bSNoralf Trønnes 38940e1a70bSNoralf Trønnes .name = "gud", 39040e1a70bSNoralf Trønnes .desc = "Generic USB Display", 39140e1a70bSNoralf Trønnes .date = "20200422", 39240e1a70bSNoralf Trønnes .major = 1, 39340e1a70bSNoralf Trønnes .minor = 0, 39440e1a70bSNoralf Trønnes }; 39540e1a70bSNoralf Trønnes 3962eecd93bSNoralf Trønnes static int gud_alloc_bulk_buffer(struct gud_device *gdrm) 3972eecd93bSNoralf Trønnes { 3982eecd93bSNoralf Trønnes unsigned int i, num_pages; 3992eecd93bSNoralf Trønnes struct page **pages; 4002eecd93bSNoralf Trønnes void *ptr; 4012eecd93bSNoralf Trønnes int ret; 4022eecd93bSNoralf Trønnes 4032eecd93bSNoralf Trønnes gdrm->bulk_buf = vmalloc_32(gdrm->bulk_len); 4042eecd93bSNoralf Trønnes if (!gdrm->bulk_buf) 4052eecd93bSNoralf Trønnes return -ENOMEM; 4062eecd93bSNoralf Trønnes 4072eecd93bSNoralf Trønnes num_pages = DIV_ROUND_UP(gdrm->bulk_len, PAGE_SIZE); 4082eecd93bSNoralf Trønnes pages = kmalloc_array(num_pages, sizeof(struct page *), GFP_KERNEL); 4092eecd93bSNoralf Trønnes if (!pages) 4102eecd93bSNoralf Trønnes return -ENOMEM; 4112eecd93bSNoralf Trønnes 4122eecd93bSNoralf Trønnes for (i = 0, ptr = gdrm->bulk_buf; i < num_pages; i++, ptr += PAGE_SIZE) 4132eecd93bSNoralf Trønnes pages[i] = vmalloc_to_page(ptr); 4142eecd93bSNoralf Trønnes 4152eecd93bSNoralf Trønnes ret = sg_alloc_table_from_pages(&gdrm->bulk_sgt, pages, num_pages, 4162eecd93bSNoralf Trønnes 0, gdrm->bulk_len, GFP_KERNEL); 4172eecd93bSNoralf Trønnes kfree(pages); 4182eecd93bSNoralf Trønnes 4192eecd93bSNoralf Trønnes return ret; 4202eecd93bSNoralf Trønnes } 4212eecd93bSNoralf Trønnes 422f8ac863bSNoralf Trønnes static void gud_free_buffers_and_mutex(void *data) 42340e1a70bSNoralf Trønnes { 424f8ac863bSNoralf Trønnes struct gud_device *gdrm = data; 42540e1a70bSNoralf Trønnes 42640e1a70bSNoralf Trønnes vfree(gdrm->compress_buf); 427f8ac863bSNoralf Trønnes gdrm->compress_buf = NULL; 4282eecd93bSNoralf Trønnes sg_free_table(&gdrm->bulk_sgt); 4292eecd93bSNoralf Trønnes vfree(gdrm->bulk_buf); 430f8ac863bSNoralf Trønnes gdrm->bulk_buf = NULL; 43140e1a70bSNoralf Trønnes mutex_destroy(&gdrm->ctrl_lock); 43240e1a70bSNoralf Trønnes } 43340e1a70bSNoralf Trønnes 43440e1a70bSNoralf Trønnes static int gud_probe(struct usb_interface *intf, const struct usb_device_id *id) 43540e1a70bSNoralf Trønnes { 43640e1a70bSNoralf Trønnes const struct drm_format_info *xrgb8888_emulation_format = NULL; 43740e1a70bSNoralf Trønnes bool rgb565_supported = false, xrgb8888_supported = false; 43840e1a70bSNoralf Trønnes unsigned int num_formats_dev, num_formats = 0; 43940e1a70bSNoralf Trønnes struct usb_endpoint_descriptor *bulk_out; 44040e1a70bSNoralf Trønnes struct gud_display_descriptor_req desc; 44140e1a70bSNoralf Trønnes struct device *dev = &intf->dev; 44240e1a70bSNoralf Trønnes size_t max_buffer_size = 0; 44340e1a70bSNoralf Trønnes struct gud_device *gdrm; 44440e1a70bSNoralf Trønnes struct drm_device *drm; 44540e1a70bSNoralf Trønnes u8 *formats_dev; 44640e1a70bSNoralf Trønnes u32 *formats; 44740e1a70bSNoralf Trønnes int ret, i; 44840e1a70bSNoralf Trønnes 44940e1a70bSNoralf Trønnes ret = usb_find_bulk_out_endpoint(intf->cur_altsetting, &bulk_out); 45040e1a70bSNoralf Trønnes if (ret) 45140e1a70bSNoralf Trønnes return ret; 45240e1a70bSNoralf Trønnes 45340e1a70bSNoralf Trønnes ret = gud_get_display_descriptor(intf, &desc); 45440e1a70bSNoralf Trønnes if (ret) { 45540e1a70bSNoralf Trønnes DRM_DEV_DEBUG_DRIVER(dev, "Not a display interface: ret=%d\n", ret); 45640e1a70bSNoralf Trønnes return -ENODEV; 45740e1a70bSNoralf Trønnes } 45840e1a70bSNoralf Trønnes 45940e1a70bSNoralf Trønnes if (desc.version > 1) { 46040e1a70bSNoralf Trønnes dev_err(dev, "Protocol version %u is not supported\n", desc.version); 46140e1a70bSNoralf Trønnes return -ENODEV; 46240e1a70bSNoralf Trønnes } 46340e1a70bSNoralf Trønnes 46440e1a70bSNoralf Trønnes gdrm = devm_drm_dev_alloc(dev, &gud_drm_driver, struct gud_device, drm); 46540e1a70bSNoralf Trønnes if (IS_ERR(gdrm)) 46640e1a70bSNoralf Trønnes return PTR_ERR(gdrm); 46740e1a70bSNoralf Trønnes 46840e1a70bSNoralf Trønnes drm = &gdrm->drm; 46940e1a70bSNoralf Trønnes drm->mode_config.funcs = &gud_mode_config_funcs; 47040e1a70bSNoralf Trønnes ret = drmm_mode_config_init(drm); 47140e1a70bSNoralf Trønnes if (ret) 47240e1a70bSNoralf Trønnes return ret; 47340e1a70bSNoralf Trønnes 47440e1a70bSNoralf Trønnes gdrm->flags = le32_to_cpu(desc.flags); 47540e1a70bSNoralf Trønnes gdrm->compression = desc.compression & GUD_COMPRESSION_LZ4; 47640e1a70bSNoralf Trønnes 47740e1a70bSNoralf Trønnes if (gdrm->flags & GUD_DISPLAY_FLAG_FULL_UPDATE && gdrm->compression) 47840e1a70bSNoralf Trønnes return -EINVAL; 47940e1a70bSNoralf Trønnes 48040e1a70bSNoralf Trønnes mutex_init(&gdrm->ctrl_lock); 48140e1a70bSNoralf Trønnes mutex_init(&gdrm->damage_lock); 48240e1a70bSNoralf Trønnes INIT_WORK(&gdrm->work, gud_flush_work); 48340e1a70bSNoralf Trønnes gud_clear_damage(gdrm); 48440e1a70bSNoralf Trønnes 485f8ac863bSNoralf Trønnes ret = devm_add_action(dev, gud_free_buffers_and_mutex, gdrm); 48640e1a70bSNoralf Trønnes if (ret) 48740e1a70bSNoralf Trønnes return ret; 48840e1a70bSNoralf Trønnes 48940e1a70bSNoralf Trønnes drm->mode_config.min_width = le32_to_cpu(desc.min_width); 49040e1a70bSNoralf Trønnes drm->mode_config.max_width = le32_to_cpu(desc.max_width); 49140e1a70bSNoralf Trønnes drm->mode_config.min_height = le32_to_cpu(desc.min_height); 49240e1a70bSNoralf Trønnes drm->mode_config.max_height = le32_to_cpu(desc.max_height); 49340e1a70bSNoralf Trønnes 49440e1a70bSNoralf Trønnes formats_dev = devm_kmalloc(dev, GUD_FORMATS_MAX_NUM, GFP_KERNEL); 49540e1a70bSNoralf Trønnes /* Add room for emulated XRGB8888 */ 49640e1a70bSNoralf Trønnes formats = devm_kmalloc_array(dev, GUD_FORMATS_MAX_NUM + 1, sizeof(*formats), GFP_KERNEL); 49740e1a70bSNoralf Trønnes if (!formats_dev || !formats) 49840e1a70bSNoralf Trønnes return -ENOMEM; 49940e1a70bSNoralf Trønnes 50040e1a70bSNoralf Trønnes ret = gud_usb_get(gdrm, GUD_REQ_GET_FORMATS, 0, formats_dev, GUD_FORMATS_MAX_NUM); 50140e1a70bSNoralf Trønnes if (ret < 0) 50240e1a70bSNoralf Trønnes return ret; 50340e1a70bSNoralf Trønnes 50440e1a70bSNoralf Trønnes num_formats_dev = ret; 50540e1a70bSNoralf Trønnes for (i = 0; i < num_formats_dev; i++) { 50640e1a70bSNoralf Trønnes const struct drm_format_info *info; 50740e1a70bSNoralf Trønnes size_t fmt_buf_size; 50840e1a70bSNoralf Trønnes u32 format; 50940e1a70bSNoralf Trønnes 51040e1a70bSNoralf Trønnes format = gud_to_fourcc(formats_dev[i]); 51140e1a70bSNoralf Trønnes if (!format) { 51240e1a70bSNoralf Trønnes drm_dbg(drm, "Unsupported format: 0x%02x\n", formats_dev[i]); 51340e1a70bSNoralf Trønnes continue; 51440e1a70bSNoralf Trønnes } 51540e1a70bSNoralf Trønnes 51640e1a70bSNoralf Trønnes if (format == GUD_DRM_FORMAT_R1) 51740e1a70bSNoralf Trønnes info = &gud_drm_format_r1; 51840e1a70bSNoralf Trønnes else if (format == GUD_DRM_FORMAT_XRGB1111) 51940e1a70bSNoralf Trønnes info = &gud_drm_format_xrgb1111; 52040e1a70bSNoralf Trønnes else 52140e1a70bSNoralf Trønnes info = drm_format_info(format); 52240e1a70bSNoralf Trønnes 52340e1a70bSNoralf Trønnes switch (format) { 52440e1a70bSNoralf Trønnes case GUD_DRM_FORMAT_R1: 52540e1a70bSNoralf Trønnes fallthrough; 52640e1a70bSNoralf Trønnes case GUD_DRM_FORMAT_XRGB1111: 52740e1a70bSNoralf Trønnes if (!xrgb8888_emulation_format) 52840e1a70bSNoralf Trønnes xrgb8888_emulation_format = info; 52940e1a70bSNoralf Trønnes break; 53040e1a70bSNoralf Trønnes case DRM_FORMAT_RGB565: 53140e1a70bSNoralf Trønnes rgb565_supported = true; 53240e1a70bSNoralf Trønnes if (!xrgb8888_emulation_format) 53340e1a70bSNoralf Trønnes xrgb8888_emulation_format = info; 53440e1a70bSNoralf Trønnes break; 53540e1a70bSNoralf Trønnes case DRM_FORMAT_XRGB8888: 53640e1a70bSNoralf Trønnes xrgb8888_supported = true; 53740e1a70bSNoralf Trønnes break; 538166633c8Skernel test robot } 53940e1a70bSNoralf Trønnes 54040e1a70bSNoralf Trønnes fmt_buf_size = drm_format_info_min_pitch(info, 0, drm->mode_config.max_width) * 54140e1a70bSNoralf Trønnes drm->mode_config.max_height; 54240e1a70bSNoralf Trønnes max_buffer_size = max(max_buffer_size, fmt_buf_size); 54340e1a70bSNoralf Trønnes 54440e1a70bSNoralf Trønnes if (format == GUD_DRM_FORMAT_R1 || format == GUD_DRM_FORMAT_XRGB1111) 54540e1a70bSNoralf Trønnes continue; /* Internal not for userspace */ 54640e1a70bSNoralf Trønnes 54740e1a70bSNoralf Trønnes formats[num_formats++] = format; 54840e1a70bSNoralf Trønnes } 54940e1a70bSNoralf Trønnes 55040e1a70bSNoralf Trønnes if (!num_formats && !xrgb8888_emulation_format) { 55140e1a70bSNoralf Trønnes dev_err(dev, "No supported pixel formats found\n"); 55240e1a70bSNoralf Trønnes return -EINVAL; 55340e1a70bSNoralf Trønnes } 55440e1a70bSNoralf Trønnes 55540e1a70bSNoralf Trønnes /* Prefer speed over color depth */ 55640e1a70bSNoralf Trønnes if (rgb565_supported) 55740e1a70bSNoralf Trønnes drm->mode_config.preferred_depth = 16; 55840e1a70bSNoralf Trønnes 55940e1a70bSNoralf Trønnes if (!xrgb8888_supported && xrgb8888_emulation_format) { 56040e1a70bSNoralf Trønnes gdrm->xrgb8888_emulation_format = xrgb8888_emulation_format; 56140e1a70bSNoralf Trønnes formats[num_formats++] = DRM_FORMAT_XRGB8888; 56240e1a70bSNoralf Trønnes } 56340e1a70bSNoralf Trønnes 56440e1a70bSNoralf Trønnes if (desc.max_buffer_size) 56540e1a70bSNoralf Trønnes max_buffer_size = le32_to_cpu(desc.max_buffer_size); 5662eecd93bSNoralf Trønnes /* Prevent a misbehaving device from allocating loads of RAM. 4096x4096@XRGB8888 = 64 MB */ 5672eecd93bSNoralf Trønnes if (max_buffer_size > SZ_64M) 5682eecd93bSNoralf Trønnes max_buffer_size = SZ_64M; 56940e1a70bSNoralf Trønnes 57040e1a70bSNoralf Trønnes gdrm->bulk_pipe = usb_sndbulkpipe(interface_to_usbdev(intf), usb_endpoint_num(bulk_out)); 57140e1a70bSNoralf Trønnes gdrm->bulk_len = max_buffer_size; 57240e1a70bSNoralf Trønnes 5732eecd93bSNoralf Trønnes ret = gud_alloc_bulk_buffer(gdrm); 5742eecd93bSNoralf Trønnes if (ret) 5752eecd93bSNoralf Trønnes return ret; 5762eecd93bSNoralf Trønnes 57740e1a70bSNoralf Trønnes if (gdrm->compression & GUD_COMPRESSION_LZ4) { 57840e1a70bSNoralf Trønnes gdrm->lz4_comp_mem = devm_kmalloc(dev, LZ4_MEM_COMPRESS, GFP_KERNEL); 57940e1a70bSNoralf Trønnes if (!gdrm->lz4_comp_mem) 58040e1a70bSNoralf Trønnes return -ENOMEM; 58140e1a70bSNoralf Trønnes 58240e1a70bSNoralf Trønnes gdrm->compress_buf = vmalloc(gdrm->bulk_len); 58340e1a70bSNoralf Trønnes if (!gdrm->compress_buf) 58440e1a70bSNoralf Trønnes return -ENOMEM; 58540e1a70bSNoralf Trønnes } 58640e1a70bSNoralf Trønnes 58740e1a70bSNoralf Trønnes ret = drm_simple_display_pipe_init(drm, &gdrm->pipe, &gud_pipe_funcs, 58840e1a70bSNoralf Trønnes formats, num_formats, 58940e1a70bSNoralf Trønnes gud_pipe_modifiers, NULL); 59040e1a70bSNoralf Trønnes if (ret) 59140e1a70bSNoralf Trønnes return ret; 59240e1a70bSNoralf Trønnes 59340e1a70bSNoralf Trønnes devm_kfree(dev, formats); 59440e1a70bSNoralf Trønnes devm_kfree(dev, formats_dev); 59540e1a70bSNoralf Trønnes 59640e1a70bSNoralf Trønnes ret = gud_get_properties(gdrm); 59740e1a70bSNoralf Trønnes if (ret) { 59840e1a70bSNoralf Trønnes dev_err(dev, "Failed to get properties (error=%d)\n", ret); 59940e1a70bSNoralf Trønnes return ret; 60040e1a70bSNoralf Trønnes } 60140e1a70bSNoralf Trønnes 60240e1a70bSNoralf Trønnes drm_plane_enable_fb_damage_clips(&gdrm->pipe.plane); 60340e1a70bSNoralf Trønnes 60440e1a70bSNoralf Trønnes ret = gud_get_connectors(gdrm); 60540e1a70bSNoralf Trønnes if (ret) { 60640e1a70bSNoralf Trønnes dev_err(dev, "Failed to get connectors (error=%d)\n", ret); 60740e1a70bSNoralf Trønnes return ret; 60840e1a70bSNoralf Trønnes } 60940e1a70bSNoralf Trønnes 61040e1a70bSNoralf Trønnes drm_mode_config_reset(drm); 61140e1a70bSNoralf Trønnes 61240e1a70bSNoralf Trønnes usb_set_intfdata(intf, gdrm); 61340e1a70bSNoralf Trønnes 61440e1a70bSNoralf Trønnes gdrm->dmadev = usb_intf_get_dma_device(intf); 61540e1a70bSNoralf Trønnes if (!gdrm->dmadev) 61640e1a70bSNoralf Trønnes dev_warn(dev, "buffer sharing not supported"); 61740e1a70bSNoralf Trønnes 61840e1a70bSNoralf Trønnes ret = drm_dev_register(drm, 0); 61940e1a70bSNoralf Trønnes if (ret) { 62040e1a70bSNoralf Trønnes put_device(gdrm->dmadev); 62140e1a70bSNoralf Trønnes return ret; 62240e1a70bSNoralf Trønnes } 62340e1a70bSNoralf Trønnes 62440e1a70bSNoralf Trønnes drm_kms_helper_poll_init(drm); 62540e1a70bSNoralf Trønnes 62640e1a70bSNoralf Trønnes drm_fbdev_generic_setup(drm, 0); 62740e1a70bSNoralf Trønnes 62840e1a70bSNoralf Trønnes return 0; 62940e1a70bSNoralf Trønnes } 63040e1a70bSNoralf Trønnes 63140e1a70bSNoralf Trønnes static void gud_disconnect(struct usb_interface *interface) 63240e1a70bSNoralf Trønnes { 63340e1a70bSNoralf Trønnes struct gud_device *gdrm = usb_get_intfdata(interface); 63440e1a70bSNoralf Trønnes struct drm_device *drm = &gdrm->drm; 63540e1a70bSNoralf Trønnes 63640e1a70bSNoralf Trønnes drm_dbg(drm, "%s:\n", __func__); 63740e1a70bSNoralf Trønnes 63840e1a70bSNoralf Trønnes drm_kms_helper_poll_fini(drm); 63940e1a70bSNoralf Trønnes drm_dev_unplug(drm); 64040e1a70bSNoralf Trønnes drm_atomic_helper_shutdown(drm); 64140e1a70bSNoralf Trønnes put_device(gdrm->dmadev); 64240e1a70bSNoralf Trønnes gdrm->dmadev = NULL; 64340e1a70bSNoralf Trønnes } 64440e1a70bSNoralf Trønnes 64540e1a70bSNoralf Trønnes static int gud_suspend(struct usb_interface *intf, pm_message_t message) 64640e1a70bSNoralf Trønnes { 64740e1a70bSNoralf Trønnes struct gud_device *gdrm = usb_get_intfdata(intf); 64840e1a70bSNoralf Trønnes 64940e1a70bSNoralf Trønnes return drm_mode_config_helper_suspend(&gdrm->drm); 65040e1a70bSNoralf Trønnes } 65140e1a70bSNoralf Trønnes 65240e1a70bSNoralf Trønnes static int gud_resume(struct usb_interface *intf) 65340e1a70bSNoralf Trønnes { 65440e1a70bSNoralf Trønnes struct gud_device *gdrm = usb_get_intfdata(intf); 65540e1a70bSNoralf Trønnes 65640e1a70bSNoralf Trønnes drm_mode_config_helper_resume(&gdrm->drm); 65740e1a70bSNoralf Trønnes 65840e1a70bSNoralf Trønnes return 0; 65940e1a70bSNoralf Trønnes } 66040e1a70bSNoralf Trønnes 66140e1a70bSNoralf Trønnes static const struct usb_device_id gud_id_table[] = { 66240e1a70bSNoralf Trønnes { USB_DEVICE_INTERFACE_CLASS(0x1d50, 0x614d, USB_CLASS_VENDOR_SPEC) }, 663*b3f4ef66SNoralf Trønnes { USB_DEVICE_INTERFACE_CLASS(0x16d0, 0x10a9, USB_CLASS_VENDOR_SPEC) }, 66440e1a70bSNoralf Trønnes { } 66540e1a70bSNoralf Trønnes }; 66640e1a70bSNoralf Trønnes 66740e1a70bSNoralf Trønnes MODULE_DEVICE_TABLE(usb, gud_id_table); 66840e1a70bSNoralf Trønnes 66940e1a70bSNoralf Trønnes static struct usb_driver gud_usb_driver = { 67040e1a70bSNoralf Trønnes .name = "gud", 67140e1a70bSNoralf Trønnes .probe = gud_probe, 67240e1a70bSNoralf Trønnes .disconnect = gud_disconnect, 67340e1a70bSNoralf Trønnes .id_table = gud_id_table, 67440e1a70bSNoralf Trønnes .suspend = gud_suspend, 67540e1a70bSNoralf Trønnes .resume = gud_resume, 67640e1a70bSNoralf Trønnes .reset_resume = gud_resume, 67740e1a70bSNoralf Trønnes }; 67840e1a70bSNoralf Trønnes 67940e1a70bSNoralf Trønnes module_usb_driver(gud_usb_driver); 68040e1a70bSNoralf Trønnes 68140e1a70bSNoralf Trønnes MODULE_AUTHOR("Noralf Trønnes"); 68240e1a70bSNoralf Trønnes MODULE_LICENSE("Dual MIT/GPL"); 683