diff options
Diffstat (limited to 'drivers/gpu/drm/udl')
| -rw-r--r-- | drivers/gpu/drm/udl/Kconfig | 9 | ||||
| -rw-r--r-- | drivers/gpu/drm/udl/Makefile | 9 | ||||
| -rw-r--r-- | drivers/gpu/drm/udl/udl_connector.c | 208 | ||||
| -rw-r--r-- | drivers/gpu/drm/udl/udl_connector.h | 13 | ||||
| -rw-r--r-- | drivers/gpu/drm/udl/udl_dmabuf.c | 266 | ||||
| -rw-r--r-- | drivers/gpu/drm/udl/udl_drv.c | 124 | ||||
| -rw-r--r-- | drivers/gpu/drm/udl/udl_drv.h | 135 | ||||
| -rw-r--r-- | drivers/gpu/drm/udl/udl_edid.c | 81 | ||||
| -rw-r--r-- | drivers/gpu/drm/udl/udl_edid.h | 15 | ||||
| -rw-r--r-- | drivers/gpu/drm/udl/udl_encoder.c | 73 | ||||
| -rw-r--r-- | drivers/gpu/drm/udl/udl_fb.c | 535 | ||||
| -rw-r--r-- | drivers/gpu/drm/udl/udl_gem.c | 231 | ||||
| -rw-r--r-- | drivers/gpu/drm/udl/udl_main.c | 368 | ||||
| -rw-r--r-- | drivers/gpu/drm/udl/udl_modeset.c | 656 | ||||
| -rw-r--r-- | drivers/gpu/drm/udl/udl_proto.h | 68 | ||||
| -rw-r--r-- | drivers/gpu/drm/udl/udl_transfer.c | 80 |
16 files changed, 848 insertions, 2023 deletions
diff --git a/drivers/gpu/drm/udl/Kconfig b/drivers/gpu/drm/udl/Kconfig index 1616ec4f4d84..d7a6abef7d78 100644 --- a/drivers/gpu/drm/udl/Kconfig +++ b/drivers/gpu/drm/udl/Kconfig @@ -1,10 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0-only config DRM_UDL tristate "DisplayLink" depends on DRM - depends on USB_SUPPORT + depends on USB depends on USB_ARCH_HAS_HCD - select USB + depends on MMU + select DRM_CLIENT_SELECTION + select DRM_GEM_SHMEM_HELPER select DRM_KMS_HELPER help This is a KMS driver for the USB displaylink video adapters. - Say M/Y to add support for these devices via drm/kms interfaces. + Say M/Y to add support for these devices via drm/kms interfaces. diff --git a/drivers/gpu/drm/udl/Makefile b/drivers/gpu/drm/udl/Makefile index 36f2e825102b..43d69a16af18 100644 --- a/drivers/gpu/drm/udl/Makefile +++ b/drivers/gpu/drm/udl/Makefile @@ -1,3 +1,10 @@ -udl-y := udl_drv.o udl_modeset.o udl_connector.o udl_encoder.o udl_main.o udl_fb.o udl_transfer.o udl_gem.o udl_dmabuf.o +# SPDX-License-Identifier: GPL-2.0-only + +udl-y := \ + udl_drv.o \ + udl_edid.o \ + udl_main.o \ + udl_modeset.o \ + udl_transfer.o obj-$(CONFIG_DRM_UDL) := udl.o diff --git a/drivers/gpu/drm/udl/udl_connector.c b/drivers/gpu/drm/udl/udl_connector.c deleted file mode 100644 index 68e88bed77ca..000000000000 --- a/drivers/gpu/drm/udl/udl_connector.c +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright (C) 2012 Red Hat - * based in parts on udlfb.c: - * Copyright (C) 2009 Roberto De Ioris <roberto@unbit.it> - * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@gmail.com> - * Copyright (C) 2009 Bernie Thompson <bernie@plugable.com> - * - * This file is subject to the terms and conditions of the GNU General Public - * License v2. See the file COPYING in the main directory of this archive for - * more details. - */ - -#include <drm/drmP.h> -#include <drm/drm_crtc.h> -#include <drm/drm_edid.h> -#include <drm/drm_crtc_helper.h> -#include "udl_connector.h" -#include "udl_drv.h" - -static bool udl_get_edid_block(struct udl_device *udl, int block_idx, - u8 *buff) -{ - int ret, i; - u8 *read_buff; - - read_buff = kmalloc(2, GFP_KERNEL); - if (!read_buff) - return false; - - for (i = 0; i < EDID_LENGTH; i++) { - int bval = (i + block_idx * EDID_LENGTH) << 8; - ret = usb_control_msg(udl->udev, - usb_rcvctrlpipe(udl->udev, 0), - (0x02), (0x80 | (0x02 << 5)), bval, - 0xA1, read_buff, 2, HZ); - if (ret < 1) { - DRM_ERROR("Read EDID byte %d failed err %x\n", i, ret); - kfree(read_buff); - return false; - } - buff[i] = read_buff[1]; - } - - kfree(read_buff); - return true; -} - -static bool udl_get_edid(struct udl_device *udl, u8 **result_buff, - int *result_buff_size) -{ - int i, extensions; - u8 *block_buff = NULL, *buff_ptr; - - block_buff = kmalloc(EDID_LENGTH, GFP_KERNEL); - if (block_buff == NULL) - return false; - - if (udl_get_edid_block(udl, 0, block_buff) && - memchr_inv(block_buff, 0, EDID_LENGTH)) { - extensions = ((struct edid *)block_buff)->extensions; - if (extensions > 0) { - /* we have to read all extensions one by one */ - *result_buff_size = EDID_LENGTH * (extensions + 1); - *result_buff = kmalloc(*result_buff_size, GFP_KERNEL); - buff_ptr = *result_buff; - if (buff_ptr == NULL) { - kfree(block_buff); - return false; - } - memcpy(buff_ptr, block_buff, EDID_LENGTH); - kfree(block_buff); - buff_ptr += EDID_LENGTH; - for (i = 1; i < extensions; ++i) { - if (udl_get_edid_block(udl, i, buff_ptr)) { - buff_ptr += EDID_LENGTH; - } else { - kfree(*result_buff); - *result_buff = NULL; - return false; - } - } - return true; - } - /* we have only base edid block */ - *result_buff = block_buff; - *result_buff_size = EDID_LENGTH; - return true; - } - - kfree(block_buff); - - return false; -} - -static int udl_get_modes(struct drm_connector *connector) -{ - struct udl_drm_connector *udl_connector = - container_of(connector, - struct udl_drm_connector, - connector); - - drm_connector_update_edid_property(connector, udl_connector->edid); - if (udl_connector->edid) - return drm_add_edid_modes(connector, udl_connector->edid); - return 0; -} - -static enum drm_mode_status udl_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - struct udl_device *udl = connector->dev->dev_private; - if (!udl->sku_pixel_limit) - return 0; - - if (mode->vdisplay * mode->hdisplay > udl->sku_pixel_limit) - return MODE_VIRTUAL_Y; - - return 0; -} - -static enum drm_connector_status -udl_detect(struct drm_connector *connector, bool force) -{ - u8 *edid_buff = NULL; - int edid_buff_size = 0; - struct udl_device *udl = connector->dev->dev_private; - struct udl_drm_connector *udl_connector = - container_of(connector, - struct udl_drm_connector, - connector); - - /* cleanup previous edid */ - if (udl_connector->edid != NULL) { - kfree(udl_connector->edid); - udl_connector->edid = NULL; - } - - - if (!udl_get_edid(udl, &edid_buff, &edid_buff_size)) - return connector_status_disconnected; - - udl_connector->edid = (struct edid *)edid_buff; - - return connector_status_connected; -} - -static struct drm_encoder* -udl_best_single_encoder(struct drm_connector *connector) -{ - int enc_id = connector->encoder_ids[0]; - return drm_encoder_find(connector->dev, NULL, enc_id); -} - -static int udl_connector_set_property(struct drm_connector *connector, - struct drm_property *property, - uint64_t val) -{ - return 0; -} - -static void udl_connector_destroy(struct drm_connector *connector) -{ - struct udl_drm_connector *udl_connector = - container_of(connector, - struct udl_drm_connector, - connector); - - drm_connector_unregister(connector); - drm_connector_cleanup(connector); - kfree(udl_connector->edid); - kfree(connector); -} - -static const struct drm_connector_helper_funcs udl_connector_helper_funcs = { - .get_modes = udl_get_modes, - .mode_valid = udl_mode_valid, - .best_encoder = udl_best_single_encoder, -}; - -static const struct drm_connector_funcs udl_connector_funcs = { - .dpms = drm_helper_connector_dpms, - .detect = udl_detect, - .fill_modes = drm_helper_probe_single_connector_modes, - .destroy = udl_connector_destroy, - .set_property = udl_connector_set_property, -}; - -int udl_connector_init(struct drm_device *dev, struct drm_encoder *encoder) -{ - struct udl_drm_connector *udl_connector; - struct drm_connector *connector; - - udl_connector = kzalloc(sizeof(struct udl_drm_connector), GFP_KERNEL); - if (!udl_connector) - return -ENOMEM; - - connector = &udl_connector->connector; - drm_connector_init(dev, connector, &udl_connector_funcs, - DRM_MODE_CONNECTOR_DVII); - drm_connector_helper_add(connector, &udl_connector_helper_funcs); - - drm_connector_register(connector); - drm_connector_attach_encoder(connector, encoder); - connector->polled = DRM_CONNECTOR_POLL_HPD | - DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT; - - return 0; -} diff --git a/drivers/gpu/drm/udl/udl_connector.h b/drivers/gpu/drm/udl/udl_connector.h deleted file mode 100644 index 0fb0db5c4612..000000000000 --- a/drivers/gpu/drm/udl/udl_connector.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef __UDL_CONNECTOR_H__ -#define __UDL_CONNECTOR_H__ - -#include <drm/drm_crtc.h> - -struct udl_drm_connector { - struct drm_connector connector; - /* last udl_detect edid */ - struct edid *edid; -}; - - -#endif //__UDL_CONNECTOR_H__ diff --git a/drivers/gpu/drm/udl/udl_dmabuf.c b/drivers/gpu/drm/udl/udl_dmabuf.c deleted file mode 100644 index 556f62662aa9..000000000000 --- a/drivers/gpu/drm/udl/udl_dmabuf.c +++ /dev/null @@ -1,266 +0,0 @@ -/* - * udl_dmabuf.c - * - * Copyright (c) 2014 The Chromium OS Authors - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <drm/drmP.h> -#include "udl_drv.h" -#include <linux/shmem_fs.h> -#include <linux/dma-buf.h> - -struct udl_drm_dmabuf_attachment { - struct sg_table sgt; - enum dma_data_direction dir; - bool is_mapped; -}; - -static int udl_attach_dma_buf(struct dma_buf *dmabuf, - struct dma_buf_attachment *attach) -{ - struct udl_drm_dmabuf_attachment *udl_attach; - - DRM_DEBUG_PRIME("[DEV:%s] size:%zd\n", dev_name(attach->dev), - attach->dmabuf->size); - - udl_attach = kzalloc(sizeof(*udl_attach), GFP_KERNEL); - if (!udl_attach) - return -ENOMEM; - - udl_attach->dir = DMA_NONE; - attach->priv = udl_attach; - - return 0; -} - -static void udl_detach_dma_buf(struct dma_buf *dmabuf, - struct dma_buf_attachment *attach) -{ - struct udl_drm_dmabuf_attachment *udl_attach = attach->priv; - struct sg_table *sgt; - - if (!udl_attach) - return; - - DRM_DEBUG_PRIME("[DEV:%s] size:%zd\n", dev_name(attach->dev), - attach->dmabuf->size); - - sgt = &udl_attach->sgt; - - if (udl_attach->dir != DMA_NONE) - dma_unmap_sg(attach->dev, sgt->sgl, sgt->nents, - udl_attach->dir); - - sg_free_table(sgt); - kfree(udl_attach); - attach->priv = NULL; -} - -static struct sg_table *udl_map_dma_buf(struct dma_buf_attachment *attach, - enum dma_data_direction dir) -{ - struct udl_drm_dmabuf_attachment *udl_attach = attach->priv; - struct udl_gem_object *obj = to_udl_bo(attach->dmabuf->priv); - struct drm_device *dev = obj->base.dev; - struct udl_device *udl = dev->dev_private; - struct scatterlist *rd, *wr; - struct sg_table *sgt = NULL; - unsigned int i; - int page_count; - int nents, ret; - - DRM_DEBUG_PRIME("[DEV:%s] size:%zd dir=%d\n", dev_name(attach->dev), - attach->dmabuf->size, dir); - - /* just return current sgt if already requested. */ - if (udl_attach->dir == dir && udl_attach->is_mapped) - return &udl_attach->sgt; - - if (!obj->pages) { - ret = udl_gem_get_pages(obj); - if (ret) { - DRM_ERROR("failed to map pages.\n"); - return ERR_PTR(ret); - } - } - - page_count = obj->base.size / PAGE_SIZE; - obj->sg = drm_prime_pages_to_sg(obj->pages, page_count); - if (IS_ERR(obj->sg)) { - DRM_ERROR("failed to allocate sgt.\n"); - return ERR_CAST(obj->sg); - } - - sgt = &udl_attach->sgt; - - ret = sg_alloc_table(sgt, obj->sg->orig_nents, GFP_KERNEL); - if (ret) { - DRM_ERROR("failed to alloc sgt.\n"); - return ERR_PTR(-ENOMEM); - } - - mutex_lock(&udl->gem_lock); - - rd = obj->sg->sgl; - wr = sgt->sgl; - for (i = 0; i < sgt->orig_nents; ++i) { - sg_set_page(wr, sg_page(rd), rd->length, rd->offset); - rd = sg_next(rd); - wr = sg_next(wr); - } - - if (dir != DMA_NONE) { - nents = dma_map_sg(attach->dev, sgt->sgl, sgt->orig_nents, dir); - if (!nents) { - DRM_ERROR("failed to map sgl with iommu.\n"); - sg_free_table(sgt); - sgt = ERR_PTR(-EIO); - goto err_unlock; - } - } - - udl_attach->is_mapped = true; - udl_attach->dir = dir; - attach->priv = udl_attach; - -err_unlock: - mutex_unlock(&udl->gem_lock); - return sgt; -} - -static void udl_unmap_dma_buf(struct dma_buf_attachment *attach, - struct sg_table *sgt, - enum dma_data_direction dir) -{ - /* Nothing to do. */ - DRM_DEBUG_PRIME("[DEV:%s] size:%zd dir:%d\n", dev_name(attach->dev), - attach->dmabuf->size, dir); -} - -static void *udl_dmabuf_kmap(struct dma_buf *dma_buf, unsigned long page_num) -{ - /* TODO */ - - return NULL; -} - -static void udl_dmabuf_kunmap(struct dma_buf *dma_buf, - unsigned long page_num, void *addr) -{ - /* TODO */ -} - -static int udl_dmabuf_mmap(struct dma_buf *dma_buf, - struct vm_area_struct *vma) -{ - /* TODO */ - - return -EINVAL; -} - -static const struct dma_buf_ops udl_dmabuf_ops = { - .attach = udl_attach_dma_buf, - .detach = udl_detach_dma_buf, - .map_dma_buf = udl_map_dma_buf, - .unmap_dma_buf = udl_unmap_dma_buf, - .map = udl_dmabuf_kmap, - .unmap = udl_dmabuf_kunmap, - .mmap = udl_dmabuf_mmap, - .release = drm_gem_dmabuf_release, -}; - -struct dma_buf *udl_gem_prime_export(struct drm_device *dev, - struct drm_gem_object *obj, int flags) -{ - DEFINE_DMA_BUF_EXPORT_INFO(exp_info); - - exp_info.ops = &udl_dmabuf_ops; - exp_info.size = obj->size; - exp_info.flags = flags; - exp_info.priv = obj; - - return drm_gem_dmabuf_export(dev, &exp_info); -} - -static int udl_prime_create(struct drm_device *dev, - size_t size, - struct sg_table *sg, - struct udl_gem_object **obj_p) -{ - struct udl_gem_object *obj; - int npages; - - npages = size / PAGE_SIZE; - - *obj_p = NULL; - obj = udl_gem_alloc_object(dev, npages * PAGE_SIZE); - if (!obj) - return -ENOMEM; - - obj->sg = sg; - obj->pages = kvmalloc_array(npages, sizeof(struct page *), GFP_KERNEL); - if (obj->pages == NULL) { - DRM_ERROR("obj pages is NULL %d\n", npages); - return -ENOMEM; - } - - drm_prime_sg_to_page_addr_arrays(sg, obj->pages, NULL, npages); - - *obj_p = obj; - return 0; -} - -struct drm_gem_object *udl_gem_prime_import(struct drm_device *dev, - struct dma_buf *dma_buf) -{ - struct dma_buf_attachment *attach; - struct sg_table *sg; - struct udl_gem_object *uobj; - int ret; - - /* need to attach */ - get_device(dev->dev); - attach = dma_buf_attach(dma_buf, dev->dev); - if (IS_ERR(attach)) { - put_device(dev->dev); - return ERR_CAST(attach); - } - - get_dma_buf(dma_buf); - - sg = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); - if (IS_ERR(sg)) { - ret = PTR_ERR(sg); - goto fail_detach; - } - - ret = udl_prime_create(dev, dma_buf->size, sg, &uobj); - if (ret) - goto fail_unmap; - - uobj->base.import_attach = attach; - uobj->flags = UDL_BO_WC; - - return &uobj->base; - -fail_unmap: - dma_buf_unmap_attachment(attach, sg, DMA_BIDIRECTIONAL); -fail_detach: - dma_buf_detach(dma_buf, attach); - dma_buf_put(dma_buf); - put_device(dev->dev); - return ERR_PTR(ret); -} diff --git a/drivers/gpu/drm/udl/udl_drv.c b/drivers/gpu/drm/udl/udl_drv.c index a63e3011e971..1922988625eb 100644 --- a/drivers/gpu/drm/udl/udl_drv.c +++ b/drivers/gpu/drm/udl/udl_drv.c @@ -1,22 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2012 Red Hat - * - * This file is subject to the terms and conditions of the GNU General Public - * License v2. See the file COPYING in the main directory of this archive for - * more details. */ #include <linux/module.h> -#include <drm/drmP.h> -#include <drm/drm_crtc_helper.h> + +#include <drm/clients/drm_client_setup.h> +#include <drm/drm_drv.h> +#include <drm/drm_fbdev_shmem.h> +#include <drm/drm_file.h> +#include <drm/drm_gem_shmem_helper.h> +#include <drm/drm_managed.h> +#include <drm/drm_modeset_helper.h> +#include <drm/drm_ioctl.h> +#include <drm/drm_probe_helper.h> +#include <drm/drm_print.h> + #include "udl_drv.h" static int udl_usb_suspend(struct usb_interface *interface, pm_message_t message) { struct drm_device *dev = usb_get_intfdata(interface); + struct udl_device *udl = to_udl(dev); + int ret; + + ret = drm_mode_config_helper_suspend(dev); + if (ret) + return ret; - drm_kms_helper_poll_disable(dev); + udl_sync_pending_urbs(udl); return 0; } @@ -24,88 +37,83 @@ static int udl_usb_resume(struct usb_interface *interface) { struct drm_device *dev = usb_get_intfdata(interface); - drm_kms_helper_poll_enable(dev); - udl_modeset_restore(dev); - return 0; + return drm_mode_config_helper_resume(dev); } -static const struct vm_operations_struct udl_gem_vm_ops = { - .fault = udl_gem_fault, - .open = drm_gem_vm_open, - .close = drm_gem_vm_close, -}; +static int udl_usb_reset_resume(struct usb_interface *interface) +{ + struct drm_device *dev = usb_get_intfdata(interface); + struct udl_device *udl = to_udl(dev); -static const struct file_operations udl_driver_fops = { - .owner = THIS_MODULE, - .open = drm_open, - .mmap = udl_drm_gem_mmap, - .poll = drm_poll, - .read = drm_read, - .unlocked_ioctl = drm_ioctl, - .release = drm_release, - .compat_ioctl = drm_compat_ioctl, - .llseek = noop_llseek, -}; + udl_select_std_channel(udl); -static struct drm_driver driver = { - .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME, - .load = udl_driver_load, - .unload = udl_driver_unload, + return drm_mode_config_helper_resume(dev); +} - /* gem hooks */ - .gem_free_object_unlocked = udl_gem_free_object, - .gem_vm_ops = &udl_gem_vm_ops, +DEFINE_DRM_GEM_FOPS(udl_driver_fops); - .dumb_create = udl_dumb_create, - .dumb_map_offset = udl_gem_mmap, - .fops = &udl_driver_fops, +static const struct drm_driver driver = { + .driver_features = DRIVER_ATOMIC | DRIVER_GEM | DRIVER_MODESET, - .prime_handle_to_fd = drm_gem_prime_handle_to_fd, - .prime_fd_to_handle = drm_gem_prime_fd_to_handle, - .gem_prime_export = udl_gem_prime_export, - .gem_prime_import = udl_gem_prime_import, + /* GEM hooks */ + .fops = &udl_driver_fops, + DRM_GEM_SHMEM_DRIVER_OPS, + DRM_FBDEV_SHMEM_DRIVER_OPS, .name = DRIVER_NAME, .desc = DRIVER_DESC, - .date = DRIVER_DATE, .major = DRIVER_MAJOR, .minor = DRIVER_MINOR, .patchlevel = DRIVER_PATCHLEVEL, }; +static struct udl_device *udl_driver_create(struct usb_interface *interface) +{ + struct udl_device *udl; + int r; + + udl = devm_drm_dev_alloc(&interface->dev, &driver, + struct udl_device, drm); + if (IS_ERR(udl)) + return udl; + + r = udl_init(udl); + if (r) + return ERR_PTR(r); + + usb_set_intfdata(interface, udl); + + return udl; +} + static int udl_usb_probe(struct usb_interface *interface, const struct usb_device_id *id) { - struct usb_device *udev = interface_to_usbdev(interface); - struct drm_device *dev; int r; + struct udl_device *udl; - dev = drm_dev_alloc(&driver, &interface->dev); - if (IS_ERR(dev)) - return PTR_ERR(dev); + udl = udl_driver_create(interface); + if (IS_ERR(udl)) + return PTR_ERR(udl); - r = drm_dev_register(dev, (unsigned long)udev); + r = drm_dev_register(&udl->drm, 0); if (r) - goto err_free; + return r; - usb_set_intfdata(interface, dev); - DRM_INFO("Initialized udl on minor %d\n", dev->primary->index); + DRM_INFO("Initialized udl on minor %d\n", udl->drm.primary->index); - return 0; + drm_client_setup(&udl->drm, NULL); -err_free: - drm_dev_put(dev); - return r; + return 0; } static void udl_usb_disconnect(struct usb_interface *interface) { struct drm_device *dev = usb_get_intfdata(interface); + struct udl_device *udl = to_udl(dev); - drm_kms_helper_poll_disable(dev); - udl_fbdev_unplug(dev); - udl_drop_usb(dev); drm_dev_unplug(dev); + udl_drop_usb(udl); } /* @@ -133,7 +141,9 @@ static struct usb_driver udl_driver = { .disconnect = udl_usb_disconnect, .suspend = udl_usb_suspend, .resume = udl_usb_resume, + .reset_resume = udl_usb_reset_resume, .id_table = id_table, }; module_usb_driver(udl_driver); +MODULE_DESCRIPTION("KMS driver for the USB displaylink video adapters"); MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/udl/udl_drv.h b/drivers/gpu/drm/udl/udl_drv.h index e9e9b1ff678e..145bb95ccc48 100644 --- a/drivers/gpu/drm/udl/udl_drv.h +++ b/drivers/gpu/drm/udl/udl_drv.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (C) 2012 Red Hat * @@ -5,153 +6,85 @@ * Copyright (C) 2009 Roberto De Ioris <roberto@unbit.it> * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@gmail.com> * Copyright (C) 2009 Bernie Thompson <bernie@plugable.com> - * - * This file is subject to the terms and conditions of the GNU General Public - * License v2. See the file COPYING in the main directory of this archive for - * more details. */ #ifndef UDL_DRV_H #define UDL_DRV_H +#include <linux/mm_types.h> #include <linux/usb.h> + +#include <drm/drm_connector.h> +#include <drm/drm_crtc.h> +#include <drm/drm_device.h> +#include <drm/drm_encoder.h> +#include <drm/drm_framebuffer.h> #include <drm/drm_gem.h> -#include <linux/mm_types.h> +#include <drm/drm_plane.h> + +struct drm_mode_create_dumb; #define DRIVER_NAME "udl" #define DRIVER_DESC "DisplayLink" -#define DRIVER_DATE "20120220" #define DRIVER_MAJOR 0 #define DRIVER_MINOR 0 #define DRIVER_PATCHLEVEL 1 -#define UDL_BO_CACHEABLE (1 << 0) -#define UDL_BO_WC (1 << 1) - struct udl_device; struct urb_node { struct list_head entry; struct udl_device *dev; - struct delayed_work release_urb_work; struct urb *urb; }; struct urb_list { struct list_head list; spinlock_t lock; - struct semaphore limit_sem; + wait_queue_head_t sleep; int available; int count; size_t size; }; -struct udl_fbdev; - struct udl_device { - struct device *dev; - struct drm_device *ddev; - struct usb_device *udev; - struct drm_crtc *crtc; + struct drm_device drm; - struct mutex gem_lock; + unsigned long sku_pixel_limit; - int sku_pixel_limit; + struct drm_plane primary_plane; + struct drm_crtc crtc; + struct drm_encoder encoder; + struct drm_connector connector; struct urb_list urbs; - atomic_t lost_pixels; /* 1 = a render op failed. Need screen refresh */ - - struct udl_fbdev *fbdev; - char mode_buf[1024]; - uint32_t mode_buf_len; - atomic_t bytes_rendered; /* raw pixel-bytes driver asked to render */ - atomic_t bytes_identical; /* saved effort with backbuffer comparison */ - atomic_t bytes_sent; /* to usb, after compression including overhead */ - atomic_t cpu_kcycles_used; /* transpired during pixel processing */ }; -struct udl_gem_object { - struct drm_gem_object base; - struct page **pages; - void *vmapping; - struct sg_table *sg; - unsigned int flags; -}; - -#define to_udl_bo(x) container_of(x, struct udl_gem_object, base) - -struct udl_framebuffer { - struct drm_framebuffer base; - struct udl_gem_object *obj; - bool active_16; /* active on the 16-bit channel */ -}; +#define to_udl(x) container_of(x, struct udl_device, drm) -#define to_udl_fb(x) container_of(x, struct udl_framebuffer, base) +static inline struct usb_device *udl_to_usb_device(struct udl_device *udl) +{ + return interface_to_usbdev(to_usb_interface(udl->drm.dev)); +} /* modeset */ -int udl_modeset_init(struct drm_device *dev); -void udl_modeset_restore(struct drm_device *dev); -void udl_modeset_cleanup(struct drm_device *dev); -int udl_connector_init(struct drm_device *dev, struct drm_encoder *encoder); +int udl_modeset_init(struct udl_device *udl); +struct drm_connector *udl_connector_init(struct drm_device *dev); -struct drm_encoder *udl_encoder_init(struct drm_device *dev); +struct urb *udl_get_urb(struct udl_device *udl); -struct urb *udl_get_urb(struct drm_device *dev); - -int udl_submit_urb(struct drm_device *dev, struct urb *urb, size_t len); +int udl_submit_urb(struct udl_device *udl, struct urb *urb, size_t len); +void udl_sync_pending_urbs(struct udl_device *udl); void udl_urb_completion(struct urb *urb); -int udl_driver_load(struct drm_device *dev, unsigned long flags); -void udl_driver_unload(struct drm_device *dev); - -int udl_fbdev_init(struct drm_device *dev); -void udl_fbdev_cleanup(struct drm_device *dev); -void udl_fbdev_unplug(struct drm_device *dev); -struct drm_framebuffer * -udl_fb_user_fb_create(struct drm_device *dev, - struct drm_file *file, - const struct drm_mode_fb_cmd2 *mode_cmd); +int udl_init(struct udl_device *udl); -int udl_render_hline(struct drm_device *dev, int log_bpp, struct urb **urb_ptr, +int udl_render_hline(struct udl_device *udl, int log_bpp, struct urb **urb_ptr, const char *front, char **urb_buf_ptr, - u32 byte_offset, u32 device_byte_offset, u32 byte_width, - int *ident_ptr, int *sent_ptr); - -int udl_dumb_create(struct drm_file *file_priv, - struct drm_device *dev, - struct drm_mode_create_dumb *args); -int udl_gem_mmap(struct drm_file *file_priv, struct drm_device *dev, - uint32_t handle, uint64_t *offset); - -void udl_gem_free_object(struct drm_gem_object *gem_obj); -struct udl_gem_object *udl_gem_alloc_object(struct drm_device *dev, - size_t size); -struct dma_buf *udl_gem_prime_export(struct drm_device *dev, - struct drm_gem_object *obj, int flags); -struct drm_gem_object *udl_gem_prime_import(struct drm_device *dev, - struct dma_buf *dma_buf); - -int udl_gem_get_pages(struct udl_gem_object *obj); -void udl_gem_put_pages(struct udl_gem_object *obj); -int udl_gem_vmap(struct udl_gem_object *obj); -void udl_gem_vunmap(struct udl_gem_object *obj); -int udl_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma); -vm_fault_t udl_gem_fault(struct vm_fault *vmf); - -int udl_handle_damage(struct udl_framebuffer *fb, int x, int y, - int width, int height); - -int udl_drop_usb(struct drm_device *dev); - -#define CMD_WRITE_RAW8 "\xAF\x60" /**< 8 bit raw write command. */ -#define CMD_WRITE_RL8 "\xAF\x61" /**< 8 bit run length command. */ -#define CMD_WRITE_COPY8 "\xAF\x62" /**< 8 bit copy command. */ -#define CMD_WRITE_RLX8 "\xAF\x63" /**< 8 bit extended run length command. */ - -#define CMD_WRITE_RAW16 "\xAF\x68" /**< 16 bit raw write command. */ -#define CMD_WRITE_RL16 "\xAF\x69" /**< 16 bit run length command. */ -#define CMD_WRITE_COPY16 "\xAF\x6A" /**< 16 bit copy command. */ -#define CMD_WRITE_RLX16 "\xAF\x6B" /**< 16 bit extended run length command. */ + u32 byte_offset, u32 device_byte_offset, u32 byte_width); + +int udl_drop_usb(struct udl_device *udl); +int udl_select_std_channel(struct udl_device *udl); #endif diff --git a/drivers/gpu/drm/udl/udl_edid.c b/drivers/gpu/drm/udl/udl_edid.c new file mode 100644 index 000000000000..af4cff2a7c51 --- /dev/null +++ b/drivers/gpu/drm/udl/udl_edid.c @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <linux/string.h> + +#include <drm/drm_drv.h> +#include <drm/drm_edid.h> +#include <drm/drm_print.h> + +#include "udl_drv.h" +#include "udl_edid.h" + +static int udl_read_edid_block(void *data, u8 *buf, unsigned int block, size_t len) +{ + struct udl_device *udl = data; + struct drm_device *dev = &udl->drm; + struct usb_device *udev = udl_to_usb_device(udl); + u8 *read_buff; + int idx, ret; + size_t i; + + read_buff = kmalloc(2, GFP_KERNEL); + if (!read_buff) + return -ENOMEM; + + if (!drm_dev_enter(dev, &idx)) { + ret = -ENODEV; + goto err_kfree; + } + + for (i = 0; i < len; i++) { + int bval = (i + block * EDID_LENGTH) << 8; + + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + 0x02, (0x80 | (0x02 << 5)), bval, + 0xA1, read_buff, 2, USB_CTRL_GET_TIMEOUT); + if (ret < 0) { + drm_err(dev, "Read EDID byte %zu failed err %x\n", i, ret); + goto err_drm_dev_exit; + } else if (ret < 1) { + ret = -EIO; + drm_err(dev, "Read EDID byte %zu failed\n", i); + goto err_drm_dev_exit; + } + + buf[i] = read_buff[1]; + } + + drm_dev_exit(idx); + kfree(read_buff); + + return 0; + +err_drm_dev_exit: + drm_dev_exit(idx); +err_kfree: + kfree(read_buff); + return ret; +} + +bool udl_probe_edid(struct udl_device *udl) +{ + u8 hdr[8]; + int ret; + + ret = udl_read_edid_block(udl, hdr, 0, sizeof(hdr)); + if (ret) + return false; + + /* + * The adapter sends all-zeros if no monitor has been + * connected. We consider anything else a connection. + */ + return !mem_is_zero(hdr, sizeof(hdr)); +} + +const struct drm_edid *udl_edid_read(struct drm_connector *connector) +{ + struct udl_device *udl = to_udl(connector->dev); + + return drm_edid_read_custom(connector, udl_read_edid_block, udl); +} diff --git a/drivers/gpu/drm/udl/udl_edid.h b/drivers/gpu/drm/udl/udl_edid.h new file mode 100644 index 000000000000..fe15ff3752b7 --- /dev/null +++ b/drivers/gpu/drm/udl/udl_edid.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef UDL_EDID_H +#define UDL_EDID_H + +#include <linux/types.h> + +struct drm_connector; +struct drm_edid; +struct udl_device; + +bool udl_probe_edid(struct udl_device *udl); +const struct drm_edid *udl_edid_read(struct drm_connector *connector); + +#endif diff --git a/drivers/gpu/drm/udl/udl_encoder.c b/drivers/gpu/drm/udl/udl_encoder.c deleted file mode 100644 index 59a4b34e87ed..000000000000 --- a/drivers/gpu/drm/udl/udl_encoder.c +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2012 Red Hat - * based in parts on udlfb.c: - * Copyright (C) 2009 Roberto De Ioris <roberto@unbit.it> - * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@gmail.com> - * Copyright (C) 2009 Bernie Thompson <bernie@plugable.com> - * - * This file is subject to the terms and conditions of the GNU General Public - * License v2. See the file COPYING in the main directory of this archive for - * more details. - */ - -#include <drm/drmP.h> -#include <drm/drm_crtc.h> -#include <drm/drm_crtc_helper.h> -#include "udl_drv.h" - -/* dummy encoder */ -static void udl_enc_destroy(struct drm_encoder *encoder) -{ - drm_encoder_cleanup(encoder); - kfree(encoder); -} - -static void udl_encoder_disable(struct drm_encoder *encoder) -{ -} - -static void udl_encoder_prepare(struct drm_encoder *encoder) -{ -} - -static void udl_encoder_commit(struct drm_encoder *encoder) -{ -} - -static void udl_encoder_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ -} - -static void -udl_encoder_dpms(struct drm_encoder *encoder, int mode) -{ -} - -static const struct drm_encoder_helper_funcs udl_helper_funcs = { - .dpms = udl_encoder_dpms, - .prepare = udl_encoder_prepare, - .mode_set = udl_encoder_mode_set, - .commit = udl_encoder_commit, - .disable = udl_encoder_disable, -}; - -static const struct drm_encoder_funcs udl_enc_funcs = { - .destroy = udl_enc_destroy, -}; - -struct drm_encoder *udl_encoder_init(struct drm_device *dev) -{ - struct drm_encoder *encoder; - - encoder = kzalloc(sizeof(struct drm_encoder), GFP_KERNEL); - if (!encoder) - return NULL; - - drm_encoder_init(dev, encoder, &udl_enc_funcs, DRM_MODE_ENCODER_TMDS, - NULL); - drm_encoder_helper_add(encoder, &udl_helper_funcs); - encoder->possible_crtcs = 1; - return encoder; -} diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c deleted file mode 100644 index dd9ffded223b..000000000000 --- a/drivers/gpu/drm/udl/udl_fb.c +++ /dev/null @@ -1,535 +0,0 @@ -/* - * Copyright (C) 2012 Red Hat - * - * based in parts on udlfb.c: - * Copyright (C) 2009 Roberto De Ioris <roberto@unbit.it> - * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@gmail.com> - * Copyright (C) 2009 Bernie Thompson <bernie@plugable.com> - * - * This file is subject to the terms and conditions of the GNU General Public - * License v2. See the file COPYING in the main directory of this archive for - * more details. - */ -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/fb.h> -#include <linux/dma-buf.h> -#include <linux/mem_encrypt.h> - -#include <drm/drmP.h> -#include <drm/drm_crtc.h> -#include <drm/drm_crtc_helper.h> -#include "udl_drv.h" - -#include <drm/drm_fb_helper.h> - -#define DL_DEFIO_WRITE_DELAY (HZ/20) /* fb_deferred_io.delay in jiffies */ - -static int fb_defio = 0; /* Optionally enable experimental fb_defio mmap support */ -static int fb_bpp = 16; - -module_param(fb_bpp, int, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP); -module_param(fb_defio, int, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP); - -struct udl_fbdev { - struct drm_fb_helper helper; - struct udl_framebuffer ufb; - int fb_count; -}; - -#define DL_ALIGN_UP(x, a) ALIGN(x, a) -#define DL_ALIGN_DOWN(x, a) ALIGN_DOWN(x, a) - -/** Read the red component (0..255) of a 32 bpp colour. */ -#define DLO_RGB_GETRED(col) (uint8_t)((col) & 0xFF) - -/** Read the green component (0..255) of a 32 bpp colour. */ -#define DLO_RGB_GETGRN(col) (uint8_t)(((col) >> 8) & 0xFF) - -/** Read the blue component (0..255) of a 32 bpp colour. */ -#define DLO_RGB_GETBLU(col) (uint8_t)(((col) >> 16) & 0xFF) - -/** Return red/green component of a 16 bpp colour number. */ -#define DLO_RG16(red, grn) (uint8_t)((((red) & 0xF8) | ((grn) >> 5)) & 0xFF) - -/** Return green/blue component of a 16 bpp colour number. */ -#define DLO_GB16(grn, blu) (uint8_t)(((((grn) & 0x1C) << 3) | ((blu) >> 3)) & 0xFF) - -/** Return 8 bpp colour number from red, green and blue components. */ -#define DLO_RGB8(red, grn, blu) ((((red) << 5) | (((grn) & 3) << 3) | ((blu) & 7)) & 0xFF) - -#if 0 -static uint8_t rgb8(uint32_t col) -{ - uint8_t red = DLO_RGB_GETRED(col); - uint8_t grn = DLO_RGB_GETGRN(col); - uint8_t blu = DLO_RGB_GETBLU(col); - - return DLO_RGB8(red, grn, blu); -} - -static uint16_t rgb16(uint32_t col) -{ - uint8_t red = DLO_RGB_GETRED(col); - uint8_t grn = DLO_RGB_GETGRN(col); - uint8_t blu = DLO_RGB_GETBLU(col); - - return (DLO_RG16(red, grn) << 8) + DLO_GB16(grn, blu); -} -#endif - -int udl_handle_damage(struct udl_framebuffer *fb, int x, int y, - int width, int height) -{ - struct drm_device *dev = fb->base.dev; - struct udl_device *udl = dev->dev_private; - int i, ret; - char *cmd; - cycles_t start_cycles, end_cycles; - int bytes_sent = 0; - int bytes_identical = 0; - struct urb *urb; - int aligned_x; - int log_bpp; - - BUG_ON(!is_power_of_2(fb->base.format->cpp[0])); - log_bpp = __ffs(fb->base.format->cpp[0]); - - if (!fb->active_16) - return 0; - - if (!fb->obj->vmapping) { - ret = udl_gem_vmap(fb->obj); - if (ret == -ENOMEM) { - DRM_ERROR("failed to vmap fb\n"); - return 0; - } - if (!fb->obj->vmapping) { - DRM_ERROR("failed to vmapping\n"); - return 0; - } - } - - aligned_x = DL_ALIGN_DOWN(x, sizeof(unsigned long)); - width = DL_ALIGN_UP(width + (x-aligned_x), sizeof(unsigned long)); - x = aligned_x; - - if ((width <= 0) || - (x + width > fb->base.width) || - (y + height > fb->base.height)) - return -EINVAL; - - start_cycles = get_cycles(); - - urb = udl_get_urb(dev); - if (!urb) - return 0; - cmd = urb->transfer_buffer; - - for (i = y; i < y + height ; i++) { - const int line_offset = fb->base.pitches[0] * i; - const int byte_offset = line_offset + (x << log_bpp); - const int dev_byte_offset = (fb->base.width * i + x) << log_bpp; - if (udl_render_hline(dev, log_bpp, &urb, - (char *) fb->obj->vmapping, - &cmd, byte_offset, dev_byte_offset, - width << log_bpp, - &bytes_identical, &bytes_sent)) - goto error; - } - - if (cmd > (char *) urb->transfer_buffer) { - /* Send partial buffer remaining before exiting */ - int len; - if (cmd < (char *) urb->transfer_buffer + urb->transfer_buffer_length) - *cmd++ = 0xAF; - len = cmd - (char *) urb->transfer_buffer; - ret = udl_submit_urb(dev, urb, len); - bytes_sent += len; - } else - udl_urb_completion(urb); - -error: - atomic_add(bytes_sent, &udl->bytes_sent); - atomic_add(bytes_identical, &udl->bytes_identical); - atomic_add((width * height) << log_bpp, &udl->bytes_rendered); - end_cycles = get_cycles(); - atomic_add(((unsigned int) ((end_cycles - start_cycles) - >> 10)), /* Kcycles */ - &udl->cpu_kcycles_used); - - return 0; -} - -static int udl_fb_mmap(struct fb_info *info, struct vm_area_struct *vma) -{ - unsigned long start = vma->vm_start; - unsigned long size = vma->vm_end - vma->vm_start; - unsigned long offset; - unsigned long page, pos; - - if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) - return -EINVAL; - - offset = vma->vm_pgoff << PAGE_SHIFT; - - if (offset > info->fix.smem_len || size > info->fix.smem_len - offset) - return -EINVAL; - - pos = (unsigned long)info->fix.smem_start + offset; - - pr_debug("mmap() framebuffer addr:%lu size:%lu\n", - pos, size); - - /* We don't want the framebuffer to be mapped encrypted */ - vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot); - - while (size > 0) { - page = vmalloc_to_pfn((void *)pos); - if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) - return -EAGAIN; - - start += PAGE_SIZE; - pos += PAGE_SIZE; - if (size > PAGE_SIZE) - size -= PAGE_SIZE; - else - size = 0; - } - - /* VM_IO | VM_DONTEXPAND | VM_DONTDUMP are set by remap_pfn_range() */ - return 0; -} - -/* - * It's common for several clients to have framebuffer open simultaneously. - * e.g. both fbcon and X. Makes things interesting. - * Assumes caller is holding info->lock (for open and release at least) - */ -static int udl_fb_open(struct fb_info *info, int user) -{ - struct udl_fbdev *ufbdev = info->par; - struct drm_device *dev = ufbdev->ufb.base.dev; - struct udl_device *udl = dev->dev_private; - - /* If the USB device is gone, we don't accept new opens */ - if (drm_dev_is_unplugged(udl->ddev)) - return -ENODEV; - - ufbdev->fb_count++; - -#ifdef CONFIG_DRM_FBDEV_EMULATION - if (fb_defio && (info->fbdefio == NULL)) { - /* enable defio at last moment if not disabled by client */ - - struct fb_deferred_io *fbdefio; - - fbdefio = kzalloc(sizeof(struct fb_deferred_io), GFP_KERNEL); - - if (fbdefio) { - fbdefio->delay = DL_DEFIO_WRITE_DELAY; - fbdefio->deferred_io = drm_fb_helper_deferred_io; - } - - info->fbdefio = fbdefio; - fb_deferred_io_init(info); - } -#endif - - pr_debug("open /dev/fb%d user=%d fb_info=%p count=%d\n", - info->node, user, info, ufbdev->fb_count); - - return 0; -} - - -/* - * Assumes caller is holding info->lock mutex (for open and release at least) - */ -static int udl_fb_release(struct fb_info *info, int user) -{ - struct udl_fbdev *ufbdev = info->par; - - ufbdev->fb_count--; - -#ifdef CONFIG_DRM_FBDEV_EMULATION - if ((ufbdev->fb_count == 0) && (info->fbdefio)) { - fb_deferred_io_cleanup(info); - kfree(info->fbdefio); - info->fbdefio = NULL; - info->fbops->fb_mmap = udl_fb_mmap; - } -#endif - - pr_debug("released /dev/fb%d user=%d count=%d\n", - info->node, user, ufbdev->fb_count); - - return 0; -} - -static struct fb_ops udlfb_ops = { - .owner = THIS_MODULE, - DRM_FB_HELPER_DEFAULT_OPS, - .fb_fillrect = drm_fb_helper_sys_fillrect, - .fb_copyarea = drm_fb_helper_sys_copyarea, - .fb_imageblit = drm_fb_helper_sys_imageblit, - .fb_mmap = udl_fb_mmap, - .fb_open = udl_fb_open, - .fb_release = udl_fb_release, -}; - -static int udl_user_framebuffer_dirty(struct drm_framebuffer *fb, - struct drm_file *file, - unsigned flags, unsigned color, - struct drm_clip_rect *clips, - unsigned num_clips) -{ - struct udl_framebuffer *ufb = to_udl_fb(fb); - int i; - int ret = 0; - - drm_modeset_lock_all(fb->dev); - - if (!ufb->active_16) - goto unlock; - - if (ufb->obj->base.import_attach) { - ret = dma_buf_begin_cpu_access(ufb->obj->base.import_attach->dmabuf, - DMA_FROM_DEVICE); - if (ret) - goto unlock; - } - - for (i = 0; i < num_clips; i++) { - ret = udl_handle_damage(ufb, clips[i].x1, clips[i].y1, - clips[i].x2 - clips[i].x1, - clips[i].y2 - clips[i].y1); - if (ret) - break; - } - - if (ufb->obj->base.import_attach) { - ret = dma_buf_end_cpu_access(ufb->obj->base.import_attach->dmabuf, - DMA_FROM_DEVICE); - } - - unlock: - drm_modeset_unlock_all(fb->dev); - - return ret; -} - -static void udl_user_framebuffer_destroy(struct drm_framebuffer *fb) -{ - struct udl_framebuffer *ufb = to_udl_fb(fb); - - if (ufb->obj) - drm_gem_object_put_unlocked(&ufb->obj->base); - - drm_framebuffer_cleanup(fb); - kfree(ufb); -} - -static const struct drm_framebuffer_funcs udlfb_funcs = { - .destroy = udl_user_framebuffer_destroy, - .dirty = udl_user_framebuffer_dirty, -}; - - -static int -udl_framebuffer_init(struct drm_device *dev, - struct udl_framebuffer *ufb, - const struct drm_mode_fb_cmd2 *mode_cmd, - struct udl_gem_object *obj) -{ - int ret; - - ufb->obj = obj; - drm_helper_mode_fill_fb_struct(dev, &ufb->base, mode_cmd); - ret = drm_framebuffer_init(dev, &ufb->base, &udlfb_funcs); - return ret; -} - - -static int udlfb_create(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size *sizes) -{ - struct udl_fbdev *ufbdev = - container_of(helper, struct udl_fbdev, helper); - struct drm_device *dev = ufbdev->helper.dev; - struct fb_info *info; - struct drm_framebuffer *fb; - struct drm_mode_fb_cmd2 mode_cmd; - struct udl_gem_object *obj; - uint32_t size; - int ret = 0; - - if (sizes->surface_bpp == 24) - sizes->surface_bpp = 32; - - mode_cmd.width = sizes->surface_width; - mode_cmd.height = sizes->surface_height; - mode_cmd.pitches[0] = mode_cmd.width * ((sizes->surface_bpp + 7) / 8); - - mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, - sizes->surface_depth); - - size = mode_cmd.pitches[0] * mode_cmd.height; - size = ALIGN(size, PAGE_SIZE); - - obj = udl_gem_alloc_object(dev, size); - if (!obj) - goto out; - - ret = udl_gem_vmap(obj); - if (ret) { - DRM_ERROR("failed to vmap fb\n"); - goto out_gfree; - } - - info = drm_fb_helper_alloc_fbi(helper); - if (IS_ERR(info)) { - ret = PTR_ERR(info); - goto out_gfree; - } - info->par = ufbdev; - - ret = udl_framebuffer_init(dev, &ufbdev->ufb, &mode_cmd, obj); - if (ret) - goto out_gfree; - - fb = &ufbdev->ufb.base; - - ufbdev->helper.fb = fb; - - strcpy(info->fix.id, "udldrmfb"); - - info->screen_base = ufbdev->ufb.obj->vmapping; - info->fix.smem_len = size; - info->fix.smem_start = (unsigned long)ufbdev->ufb.obj->vmapping; - - info->fbops = &udlfb_ops; - drm_fb_helper_fill_fix(info, fb->pitches[0], fb->format->depth); - drm_fb_helper_fill_var(info, &ufbdev->helper, sizes->fb_width, sizes->fb_height); - - DRM_DEBUG_KMS("allocated %dx%d vmal %p\n", - fb->width, fb->height, - ufbdev->ufb.obj->vmapping); - - return ret; -out_gfree: - drm_gem_object_put_unlocked(&ufbdev->ufb.obj->base); -out: - return ret; -} - -static const struct drm_fb_helper_funcs udl_fb_helper_funcs = { - .fb_probe = udlfb_create, -}; - -static void udl_fbdev_destroy(struct drm_device *dev, - struct udl_fbdev *ufbdev) -{ - drm_fb_helper_unregister_fbi(&ufbdev->helper); - drm_fb_helper_fini(&ufbdev->helper); - if (ufbdev->ufb.obj) { - drm_framebuffer_unregister_private(&ufbdev->ufb.base); - drm_framebuffer_cleanup(&ufbdev->ufb.base); - drm_gem_object_put_unlocked(&ufbdev->ufb.obj->base); - } -} - -int udl_fbdev_init(struct drm_device *dev) -{ - struct udl_device *udl = dev->dev_private; - int bpp_sel = fb_bpp; - struct udl_fbdev *ufbdev; - int ret; - - ufbdev = kzalloc(sizeof(struct udl_fbdev), GFP_KERNEL); - if (!ufbdev) - return -ENOMEM; - - udl->fbdev = ufbdev; - - drm_fb_helper_prepare(dev, &ufbdev->helper, &udl_fb_helper_funcs); - - ret = drm_fb_helper_init(dev, &ufbdev->helper, 1); - if (ret) - goto free; - - ret = drm_fb_helper_single_add_all_connectors(&ufbdev->helper); - if (ret) - goto fini; - - /* disable all the possible outputs/crtcs before entering KMS mode */ - drm_helper_disable_unused_functions(dev); - - ret = drm_fb_helper_initial_config(&ufbdev->helper, bpp_sel); - if (ret) - goto fini; - - return 0; - -fini: - drm_fb_helper_fini(&ufbdev->helper); -free: - kfree(ufbdev); - return ret; -} - -void udl_fbdev_cleanup(struct drm_device *dev) -{ - struct udl_device *udl = dev->dev_private; - if (!udl->fbdev) - return; - - udl_fbdev_destroy(dev, udl->fbdev); - kfree(udl->fbdev); - udl->fbdev = NULL; -} - -void udl_fbdev_unplug(struct drm_device *dev) -{ - struct udl_device *udl = dev->dev_private; - struct udl_fbdev *ufbdev; - if (!udl->fbdev) - return; - - ufbdev = udl->fbdev; - drm_fb_helper_unlink_fbi(&ufbdev->helper); -} - -struct drm_framebuffer * -udl_fb_user_fb_create(struct drm_device *dev, - struct drm_file *file, - const struct drm_mode_fb_cmd2 *mode_cmd) -{ - struct drm_gem_object *obj; - struct udl_framebuffer *ufb; - int ret; - uint32_t size; - - obj = drm_gem_object_lookup(file, mode_cmd->handles[0]); - if (obj == NULL) - return ERR_PTR(-ENOENT); - - size = mode_cmd->pitches[0] * mode_cmd->height; - size = ALIGN(size, PAGE_SIZE); - - if (size > obj->size) { - DRM_ERROR("object size not sufficient for fb %d %zu %d %d\n", size, obj->size, mode_cmd->pitches[0], mode_cmd->height); - return ERR_PTR(-ENOMEM); - } - - ufb = kzalloc(sizeof(*ufb), GFP_KERNEL); - if (ufb == NULL) - return ERR_PTR(-ENOMEM); - - ret = udl_framebuffer_init(dev, ufb, mode_cmd, to_udl_bo(obj)); - if (ret) { - kfree(ufb); - return ERR_PTR(-EINVAL); - } - return &ufb->base; -} diff --git a/drivers/gpu/drm/udl/udl_gem.c b/drivers/gpu/drm/udl/udl_gem.c deleted file mode 100644 index d5a23295dd80..000000000000 --- a/drivers/gpu/drm/udl/udl_gem.c +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Copyright (C) 2012 Red Hat - * - * This file is subject to the terms and conditions of the GNU General Public - * License v2. See the file COPYING in the main directory of this archive for - * more details. - */ - -#include <drm/drmP.h> -#include "udl_drv.h" -#include <linux/shmem_fs.h> -#include <linux/dma-buf.h> - -struct udl_gem_object *udl_gem_alloc_object(struct drm_device *dev, - size_t size) -{ - struct udl_gem_object *obj; - - obj = kzalloc(sizeof(*obj), GFP_KERNEL); - if (obj == NULL) - return NULL; - - if (drm_gem_object_init(dev, &obj->base, size) != 0) { - kfree(obj); - return NULL; - } - - obj->flags = UDL_BO_CACHEABLE; - return obj; -} - -static int -udl_gem_create(struct drm_file *file, - struct drm_device *dev, - uint64_t size, - uint32_t *handle_p) -{ - struct udl_gem_object *obj; - int ret; - u32 handle; - - size = roundup(size, PAGE_SIZE); - - obj = udl_gem_alloc_object(dev, size); - if (obj == NULL) - return -ENOMEM; - - ret = drm_gem_handle_create(file, &obj->base, &handle); - if (ret) { - drm_gem_object_release(&obj->base); - kfree(obj); - return ret; - } - - drm_gem_object_put_unlocked(&obj->base); - *handle_p = handle; - return 0; -} - -static void update_vm_cache_attr(struct udl_gem_object *obj, - struct vm_area_struct *vma) -{ - DRM_DEBUG_KMS("flags = 0x%x\n", obj->flags); - - /* non-cacheable as default. */ - if (obj->flags & UDL_BO_CACHEABLE) { - vma->vm_page_prot = vm_get_page_prot(vma->vm_flags); - } else if (obj->flags & UDL_BO_WC) { - vma->vm_page_prot = - pgprot_writecombine(vm_get_page_prot(vma->vm_flags)); - } else { - vma->vm_page_prot = - pgprot_noncached(vm_get_page_prot(vma->vm_flags)); - } -} - -int udl_dumb_create(struct drm_file *file, - struct drm_device *dev, - struct drm_mode_create_dumb *args) -{ - args->pitch = args->width * DIV_ROUND_UP(args->bpp, 8); - args->size = args->pitch * args->height; - return udl_gem_create(file, dev, - args->size, &args->handle); -} - -int udl_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) -{ - int ret; - - ret = drm_gem_mmap(filp, vma); - if (ret) - return ret; - - vma->vm_flags &= ~VM_PFNMAP; - vma->vm_flags |= VM_MIXEDMAP; - - update_vm_cache_attr(to_udl_bo(vma->vm_private_data), vma); - - return ret; -} - -vm_fault_t udl_gem_fault(struct vm_fault *vmf) -{ - struct vm_area_struct *vma = vmf->vma; - struct udl_gem_object *obj = to_udl_bo(vma->vm_private_data); - struct page *page; - unsigned int page_offset; - - page_offset = (vmf->address - vma->vm_start) >> PAGE_SHIFT; - - if (!obj->pages) - return VM_FAULT_SIGBUS; - - page = obj->pages[page_offset]; - return vmf_insert_page(vma, vmf->address, page); -} - -int udl_gem_get_pages(struct udl_gem_object *obj) -{ - struct page **pages; - - if (obj->pages) - return 0; - - pages = drm_gem_get_pages(&obj->base); - if (IS_ERR(pages)) - return PTR_ERR(pages); - - obj->pages = pages; - - return 0; -} - -void udl_gem_put_pages(struct udl_gem_object *obj) -{ - if (obj->base.import_attach) { - kvfree(obj->pages); - obj->pages = NULL; - return; - } - - drm_gem_put_pages(&obj->base, obj->pages, false, false); - obj->pages = NULL; -} - -int udl_gem_vmap(struct udl_gem_object *obj) -{ - int page_count = obj->base.size / PAGE_SIZE; - int ret; - - if (obj->base.import_attach) { - obj->vmapping = dma_buf_vmap(obj->base.import_attach->dmabuf); - if (!obj->vmapping) - return -ENOMEM; - return 0; - } - - ret = udl_gem_get_pages(obj); - if (ret) - return ret; - - obj->vmapping = vmap(obj->pages, page_count, 0, PAGE_KERNEL); - if (!obj->vmapping) - return -ENOMEM; - return 0; -} - -void udl_gem_vunmap(struct udl_gem_object *obj) -{ - if (obj->base.import_attach) { - dma_buf_vunmap(obj->base.import_attach->dmabuf, obj->vmapping); - return; - } - - vunmap(obj->vmapping); - - udl_gem_put_pages(obj); -} - -void udl_gem_free_object(struct drm_gem_object *gem_obj) -{ - struct udl_gem_object *obj = to_udl_bo(gem_obj); - - if (obj->vmapping) - udl_gem_vunmap(obj); - - if (gem_obj->import_attach) { - drm_prime_gem_destroy(gem_obj, obj->sg); - put_device(gem_obj->dev->dev); - } - - if (obj->pages) - udl_gem_put_pages(obj); - - drm_gem_free_mmap_offset(gem_obj); -} - -/* the dumb interface doesn't work with the GEM straight MMAP - interface, it expects to do MMAP on the drm fd, like normal */ -int udl_gem_mmap(struct drm_file *file, struct drm_device *dev, - uint32_t handle, uint64_t *offset) -{ - struct udl_gem_object *gobj; - struct drm_gem_object *obj; - struct udl_device *udl = dev->dev_private; - int ret = 0; - - mutex_lock(&udl->gem_lock); - obj = drm_gem_object_lookup(file, handle); - if (obj == NULL) { - ret = -ENOENT; - goto unlock; - } - gobj = to_udl_bo(obj); - - ret = udl_gem_get_pages(gobj); - if (ret) - goto out; - ret = drm_gem_create_mmap_offset(obj); - if (ret) - goto out; - - *offset = drm_vma_node_offset_addr(&gobj->base.vma_node); - -out: - drm_gem_object_put(&gobj->base); -unlock: - mutex_unlock(&udl->gem_lock); - return ret; -} diff --git a/drivers/gpu/drm/udl/udl_main.c b/drivers/gpu/drm/udl/udl_main.c index 1b014d92855b..bc58991a6f14 100644 --- a/drivers/gpu/drm/udl/udl_main.c +++ b/drivers/gpu/drm/udl/udl_main.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2012 Red Hat * @@ -5,13 +6,14 @@ * Copyright (C) 2009 Roberto De Ioris <roberto@unbit.it> * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@gmail.com> * Copyright (C) 2009 Bernie Thompson <bernie@plugable.com> - * - * This file is subject to the terms and conditions of the GNU General Public - * License v2. See the file COPYING in the main directory of this archive for - * more details. */ -#include <drm/drmP.h> -#include <drm/drm_crtc_helper.h> + +#include <linux/unaligned.h> + +#include <drm/drm.h> +#include <drm/drm_print.h> +#include <drm/drm_probe_helper.h> + #include "udl_drv.h" /* -BULK_SIZE as per usb-skeleton. Can we get full page and avoid overhead? */ @@ -20,97 +22,123 @@ #define NR_USB_REQUEST_CHANNEL 0x12 #define MAX_TRANSFER (PAGE_SIZE*16 - BULK_SIZE) -#define WRITES_IN_FLIGHT (4) +#define WRITES_IN_FLIGHT (20) #define MAX_VENDOR_DESCRIPTOR_SIZE 256 -#define GET_URB_TIMEOUT HZ -#define FREE_URB_TIMEOUT (HZ*2) +#define UDL_SKU_PIXEL_LIMIT_DEFAULT 2080000 + +static struct urb *udl_get_urb_locked(struct udl_device *udl, long timeout); -static int udl_parse_vendor_descriptor(struct drm_device *dev, - struct usb_device *usbdev) +/* + * Try to make sense of whatever we parse. Therefore return @end on + * errors, but don't fail hard. + */ +static const u8 *udl_parse_key_value_pair(struct udl_device *udl, const u8 *pos, const u8 *end) { - struct udl_device *udl = dev->dev_private; - char *desc; - char *buf; - char *desc_end; + u16 key; + u8 len; + + /* read key */ + if (pos >= end - 2) + return end; + key = get_unaligned_le16(pos); + pos += 2; + + /* read value length */ + if (pos >= end - 1) + return end; + len = *pos++; + + /* read value */ + if (pos >= end - len) + return end; + switch (key) { + case 0x0200: { /* maximum number of pixels */ + unsigned int sku_pixel_limit; + + if (len < sizeof(__le32)) + break; + sku_pixel_limit = get_unaligned_le32(pos); + if (sku_pixel_limit >= 16 * UDL_SKU_PIXEL_LIMIT_DEFAULT) + break; /* almost 100 MiB, so probably bogus */ + udl->sku_pixel_limit = sku_pixel_limit; + break; + } + default: + break; + } + pos += len; - u8 total_len = 0; + return pos; +} + +static int udl_parse_vendor_descriptor(struct udl_device *udl) +{ + struct drm_device *dev = &udl->drm; + struct usb_device *udev = udl_to_usb_device(udl); + bool detected = false; + void *buf; + int ret; + unsigned int len; + const u8 *desc; + const u8 *desc_end; buf = kzalloc(MAX_VENDOR_DESCRIPTOR_SIZE, GFP_KERNEL); if (!buf) - return false; + return -ENOMEM; + + ret = usb_get_descriptor(udev, 0x5f, /* vendor specific */ + 0, buf, MAX_VENDOR_DESCRIPTOR_SIZE); + if (ret < 0) + goto out; + len = ret; + + if (len < 5) + goto out; + desc = buf; + desc_end = desc + len; - total_len = usb_get_descriptor(usbdev, 0x5f, /* vendor specific */ - 0, desc, MAX_VENDOR_DESCRIPTOR_SIZE); - if (total_len > 5) { - DRM_INFO("vendor descriptor length:%x data:%11ph\n", - total_len, desc); - - if ((desc[0] != total_len) || /* descriptor length */ - (desc[1] != 0x5f) || /* vendor descriptor type */ - (desc[2] != 0x01) || /* version (2 bytes) */ - (desc[3] != 0x00) || - (desc[4] != total_len - 2)) /* length after type */ - goto unrecognized; - - desc_end = desc + total_len; - desc += 5; /* the fixed header we've already parsed */ - - while (desc < desc_end) { - u8 length; - u16 key; - - key = le16_to_cpu(*((u16 *) desc)); - desc += sizeof(u16); - length = *desc; - desc++; - - switch (key) { - case 0x0200: { /* max_area */ - u32 max_area; - max_area = le32_to_cpu(*((u32 *)desc)); - DRM_DEBUG("DL chip limited to %d pixel modes\n", - max_area); - udl->sku_pixel_limit = max_area; - break; - } - default: - break; - } - desc += length; - } - } + if ((desc[0] != len) || /* descriptor length */ + (desc[1] != 0x5f) || /* vendor descriptor type */ + (desc[2] != 0x01) || /* version (2 bytes) */ + (desc[3] != 0x00) || + (desc[4] != len - 2)) /* length after type */ + goto out; + desc += 5; - goto success; + detected = true; -unrecognized: - /* allow udlfb to load for now even if firmware unrecognized */ - DRM_ERROR("Unrecognized vendor firmware descriptor\n"); + while (desc < desc_end) + desc = udl_parse_key_value_pair(udl, desc, desc_end); -success: +out: + if (!detected) + drm_warn(dev, "Unrecognized vendor firmware descriptor\n"); kfree(buf); - return true; + + return 0; } /* * Need to ensure a channel is selected before submitting URBs */ -static int udl_select_std_channel(struct udl_device *udl) +int udl_select_std_channel(struct udl_device *udl) { - int ret; static const u8 set_def_chn[] = {0x57, 0xCD, 0xDC, 0xA7, 0x1C, 0x88, 0x5E, 0x15, 0x60, 0xFE, 0xC6, 0x97, 0x16, 0x3D, 0x47, 0xF2}; + void *sendbuf; + int ret; + struct usb_device *udev = udl_to_usb_device(udl); sendbuf = kmemdup(set_def_chn, sizeof(set_def_chn), GFP_KERNEL); if (!sendbuf) return -ENOMEM; - ret = usb_control_msg(udl->udev, - usb_sndctrlpipe(udl->udev, 0), + ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), NR_USB_REQUEST_CHANNEL, (USB_DIR_OUT | USB_TYPE_VENDOR), 0, 0, sendbuf, sizeof(set_def_chn), @@ -119,14 +147,6 @@ static int udl_select_std_channel(struct udl_device *udl) return ret < 0 ? ret : 0; } -static void udl_release_urb_work(struct work_struct *work) -{ - struct urb_node *unode = container_of(work, struct urb_node, - release_urb_work.work); - - up(&unode->dev->urbs.limit_sem); -} - void udl_urb_completion(struct urb *urb) { struct urb_node *unode = urb->context; @@ -137,10 +157,10 @@ void udl_urb_completion(struct urb *urb) if (urb->status) { if (!(urb->status == -ENOENT || urb->status == -ECONNRESET || + urb->status == -EPROTO || urb->status == -ESHUTDOWN)) { DRM_ERROR("%s - nonzero write bulk status received: %d\n", __func__, urb->status); - atomic_set(&udl->lost_pixels, 1); } } @@ -151,78 +171,58 @@ void udl_urb_completion(struct urb *urb) udl->urbs.available++; spin_unlock_irqrestore(&udl->urbs.lock, flags); -#if 0 - /* - * When using fb_defio, we deadlock if up() is called - * while another is waiting. So queue to another process. - */ - if (fb_defio) - schedule_delayed_work(&unode->release_urb_work, 0); - else -#endif - up(&udl->urbs.limit_sem); + wake_up(&udl->urbs.sleep); } -static void udl_free_urb_list(struct drm_device *dev) +static void udl_free_urb_list(struct udl_device *udl) { - struct udl_device *udl = dev->dev_private; - int count = udl->urbs.count; - struct list_head *node; struct urb_node *unode; struct urb *urb; DRM_DEBUG("Waiting for completes and freeing all render urbs\n"); /* keep waiting and freeing, until we've got 'em all */ - while (count--) { - down(&udl->urbs.limit_sem); - + while (udl->urbs.count) { spin_lock_irq(&udl->urbs.lock); - - node = udl->urbs.list.next; /* have reserved one with sem */ - list_del_init(node); - + urb = udl_get_urb_locked(udl, MAX_SCHEDULE_TIMEOUT); + udl->urbs.count--; spin_unlock_irq(&udl->urbs.lock); - - unode = list_entry(node, struct urb_node, entry); - urb = unode->urb; - + if (WARN_ON(!urb)) + break; + unode = urb->context; /* Free each separately allocated piece */ usb_free_coherent(urb->dev, udl->urbs.size, urb->transfer_buffer, urb->transfer_dma); usb_free_urb(urb); - kfree(node); + kfree(unode); } - udl->urbs.count = 0; + + wake_up_all(&udl->urbs.sleep); } -static int udl_alloc_urb_list(struct drm_device *dev, int count, size_t size) +static int udl_alloc_urb_list(struct udl_device *udl, int count, size_t size) { - struct udl_device *udl = dev->dev_private; struct urb *urb; struct urb_node *unode; char *buf; size_t wanted_size = count * size; + struct usb_device *udev = udl_to_usb_device(udl); spin_lock_init(&udl->urbs.lock); - -retry: - udl->urbs.size = size; INIT_LIST_HEAD(&udl->urbs.list); - - sema_init(&udl->urbs.limit_sem, 0); + init_waitqueue_head(&udl->urbs.sleep); udl->urbs.count = 0; udl->urbs.available = 0; +retry: + udl->urbs.size = size; + while (udl->urbs.count * size < wanted_size) { unode = kzalloc(sizeof(struct urb_node), GFP_KERNEL); if (!unode) break; unode->dev = udl; - INIT_DELAYED_WORK(&unode->release_urb_work, - udl_release_urb_work); - urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) { kfree(unode); @@ -230,27 +230,26 @@ retry: } unode->urb = urb; - buf = usb_alloc_coherent(udl->udev, size, GFP_KERNEL, + buf = usb_alloc_coherent(udev, size, GFP_KERNEL, &urb->transfer_dma); if (!buf) { kfree(unode); usb_free_urb(urb); if (size > PAGE_SIZE) { size /= 2; - udl_free_urb_list(dev); + udl_free_urb_list(udl); goto retry; } break; } /* urb->transfer_buffer_length set to actual before submit */ - usb_fill_bulk_urb(urb, udl->udev, usb_sndbulkpipe(udl->udev, 1), - buf, size, udl_urb_completion, unode); + usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, 1), + buf, size, udl_urb_completion, unode); urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; list_add_tail(&unode->entry, &udl->urbs.list); - up(&udl->urbs.limit_sem); udl->urbs.count++; udl->urbs.available++; } @@ -260,124 +259,129 @@ retry: return udl->urbs.count; } -struct urb *udl_get_urb(struct drm_device *dev) +static struct urb *udl_get_urb_locked(struct udl_device *udl, long timeout) { - struct udl_device *udl = dev->dev_private; - int ret = 0; - struct list_head *entry; struct urb_node *unode; - struct urb *urb = NULL; + + assert_spin_locked(&udl->urbs.lock); /* Wait for an in-flight buffer to complete and get re-queued */ - ret = down_timeout(&udl->urbs.limit_sem, GET_URB_TIMEOUT); - if (ret) { - atomic_set(&udl->lost_pixels, 1); - DRM_INFO("wait for urb interrupted: %x available: %d\n", - ret, udl->urbs.available); - goto error; + if (!wait_event_lock_irq_timeout(udl->urbs.sleep, + !udl->urbs.count || + !list_empty(&udl->urbs.list), + udl->urbs.lock, timeout)) { + DRM_INFO("wait for urb interrupted: available: %d\n", + udl->urbs.available); + return NULL; } - spin_lock_irq(&udl->urbs.lock); + if (!udl->urbs.count) + return NULL; - BUG_ON(list_empty(&udl->urbs.list)); /* reserved one with limit_sem */ - entry = udl->urbs.list.next; - list_del_init(entry); + unode = list_first_entry(&udl->urbs.list, struct urb_node, entry); + list_del_init(&unode->entry); udl->urbs.available--; - spin_unlock_irq(&udl->urbs.lock); + return unode->urb; +} - unode = list_entry(entry, struct urb_node, entry); - urb = unode->urb; +#define GET_URB_TIMEOUT HZ +struct urb *udl_get_urb(struct udl_device *udl) +{ + struct urb *urb; -error: + spin_lock_irq(&udl->urbs.lock); + urb = udl_get_urb_locked(udl, GET_URB_TIMEOUT); + spin_unlock_irq(&udl->urbs.lock); return urb; } -int udl_submit_urb(struct drm_device *dev, struct urb *urb, size_t len) +int udl_submit_urb(struct udl_device *udl, struct urb *urb, size_t len) { - struct udl_device *udl = dev->dev_private; int ret; - BUG_ON(len > udl->urbs.size); - + if (WARN_ON(len > udl->urbs.size)) { + ret = -EINVAL; + goto error; + } urb->transfer_buffer_length = len; /* set to actual payload len */ ret = usb_submit_urb(urb, GFP_ATOMIC); + error: if (ret) { udl_urb_completion(urb); /* because no one else will */ - atomic_set(&udl->lost_pixels, 1); DRM_ERROR("usb_submit_urb error %x\n", ret); } return ret; } -int udl_driver_load(struct drm_device *dev, unsigned long flags) +/* wait until all pending URBs have been processed */ +void udl_sync_pending_urbs(struct udl_device *udl) +{ + struct drm_device *dev = &udl->drm; + + spin_lock_irq(&udl->urbs.lock); + /* 2 seconds as a sane timeout */ + if (!wait_event_lock_irq_timeout(udl->urbs.sleep, + udl->urbs.available == udl->urbs.count, + udl->urbs.lock, + msecs_to_jiffies(2000))) + drm_err(dev, "Timeout for syncing pending URBs\n"); + spin_unlock_irq(&udl->urbs.lock); +} + +int udl_init(struct udl_device *udl) { - struct usb_device *udev = (void*)flags; - struct udl_device *udl; + struct drm_device *dev = &udl->drm; int ret = -ENOMEM; + struct device *dma_dev; DRM_DEBUG("\n"); - udl = kzalloc(sizeof(struct udl_device), GFP_KERNEL); - if (!udl) - return -ENOMEM; - udl->udev = udev; - udl->ddev = dev; - dev->dev_private = udl; + dma_dev = usb_intf_get_dma_device(to_usb_interface(dev->dev)); + if (dma_dev) { + drm_dev_set_dma_dev(dev, dma_dev); + put_device(dma_dev); + } else { + drm_warn(dev, "buffer sharing not supported"); /* not an error */ + } - mutex_init(&udl->gem_lock); + /* + * Not all devices provide vendor descriptors with device + * information. Initialize to default values of real-world + * devices. It is just enough memory for FullHD. + */ + udl->sku_pixel_limit = UDL_SKU_PIXEL_LIMIT_DEFAULT; - if (!udl_parse_vendor_descriptor(dev, udl->udev)) { - ret = -ENODEV; - DRM_ERROR("firmware not recognized. Assume incompatible device\n"); + ret = udl_parse_vendor_descriptor(udl); + if (ret) goto err; - } if (udl_select_std_channel(udl)) DRM_ERROR("Selecting channel failed\n"); - if (!udl_alloc_urb_list(dev, WRITES_IN_FLIGHT, MAX_TRANSFER)) { + if (!udl_alloc_urb_list(udl, WRITES_IN_FLIGHT, MAX_TRANSFER)) { DRM_ERROR("udl_alloc_urb_list failed\n"); + ret = -ENOMEM; goto err; } DRM_DEBUG("\n"); - ret = udl_modeset_init(dev); - if (ret) - goto err; - - ret = udl_fbdev_init(dev); + ret = udl_modeset_init(udl); if (ret) goto err; - drm_kms_helper_poll_init(dev); - return 0; err: if (udl->urbs.count) - udl_free_urb_list(dev); - kfree(udl); + udl_free_urb_list(udl); DRM_ERROR("%d\n", ret); return ret; } -int udl_drop_usb(struct drm_device *dev) +int udl_drop_usb(struct udl_device *udl) { - udl_free_urb_list(dev); - return 0; -} + udl_free_urb_list(udl); -void udl_driver_unload(struct drm_device *dev) -{ - struct udl_device *udl = dev->dev_private; - - drm_kms_helper_poll_fini(dev); - - if (udl->urbs.count) - udl_free_urb_list(dev); - - udl_fbdev_cleanup(dev); - udl_modeset_cleanup(dev); - kfree(udl); + return 0; } diff --git a/drivers/gpu/drm/udl/udl_modeset.c b/drivers/gpu/drm/udl/udl_modeset.c index 7e37765cf5ac..231e829bd709 100644 --- a/drivers/gpu/drm/udl/udl_modeset.c +++ b/drivers/gpu/drm/udl/udl_modeset.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2012 Red Hat * @@ -5,90 +6,93 @@ * Copyright (C) 2009 Roberto De Ioris <roberto@unbit.it> * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@gmail.com> * Copyright (C) 2009 Bernie Thompson <bernie@plugable.com> - - * This file is subject to the terms and conditions of the GNU General Public - * License v2. See the file COPYING in the main directory of this archive for - * more details. */ -#include <drm/drmP.h> -#include <drm/drm_crtc.h> +#include <linux/bitfield.h> + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> #include <drm/drm_crtc_helper.h> -#include <drm/drm_plane_helper.h> +#include <drm/drm_damage_helper.h> +#include <drm/drm_drv.h> +#include <drm/drm_edid.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_gem_atomic_helper.h> +#include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_gem_shmem_helper.h> +#include <drm/drm_modeset_helper_vtables.h> +#include <drm/drm_probe_helper.h> +#include <drm/drm_vblank.h> + #include "udl_drv.h" +#include "udl_edid.h" +#include "udl_proto.h" /* - * All DisplayLink bulk operations start with 0xAF, followed by specific code - * All operations are written to buffers which then later get sent to device + * All DisplayLink bulk operations start with 0xaf (UDL_MSG_BULK), followed by + * a specific command code. All operations are written to a command buffer, which + * the driver sends to the device. */ static char *udl_set_register(char *buf, u8 reg, u8 val) { - *buf++ = 0xAF; - *buf++ = 0x20; + *buf++ = UDL_MSG_BULK; + *buf++ = UDL_CMD_WRITEREG; *buf++ = reg; *buf++ = val; + return buf; } static char *udl_vidreg_lock(char *buf) { - return udl_set_register(buf, 0xFF, 0x00); + return udl_set_register(buf, UDL_REG_VIDREG, UDL_VIDREG_LOCK); } static char *udl_vidreg_unlock(char *buf) { - return udl_set_register(buf, 0xFF, 0xFF); + return udl_set_register(buf, UDL_REG_VIDREG, UDL_VIDREG_UNLOCK); } -/* - * On/Off for driving the DisplayLink framebuffer to the display - * 0x00 H and V sync on - * 0x01 H and V sync off (screen blank but powered) - * 0x07 DPMS powerdown (requires modeset to come back) - */ -static char *udl_set_blank(char *buf, int dpms_mode) +static char *udl_set_blank_mode(char *buf, u8 mode) { - u8 reg; - switch (dpms_mode) { - case DRM_MODE_DPMS_OFF: - reg = 0x07; - break; - case DRM_MODE_DPMS_STANDBY: - reg = 0x05; - break; - case DRM_MODE_DPMS_SUSPEND: - reg = 0x01; - break; - case DRM_MODE_DPMS_ON: - reg = 0x00; - break; - } - - return udl_set_register(buf, 0x1f, reg); + return udl_set_register(buf, UDL_REG_BLANKMODE, mode); } static char *udl_set_color_depth(char *buf, u8 selection) { - return udl_set_register(buf, 0x00, selection); + return udl_set_register(buf, UDL_REG_COLORDEPTH, selection); } -static char *udl_set_base16bpp(char *wrptr, u32 base) +static char *udl_set_base16bpp(char *buf, u32 base) { - /* the base pointer is 16 bits wide, 0x20 is hi byte. */ - wrptr = udl_set_register(wrptr, 0x20, base >> 16); - wrptr = udl_set_register(wrptr, 0x21, base >> 8); - return udl_set_register(wrptr, 0x22, base); + /* the base pointer is 24 bits wide, 0x20 is hi byte. */ + u8 reg20 = FIELD_GET(UDL_BASE_ADDR2_MASK, base); + u8 reg21 = FIELD_GET(UDL_BASE_ADDR1_MASK, base); + u8 reg22 = FIELD_GET(UDL_BASE_ADDR0_MASK, base); + + buf = udl_set_register(buf, UDL_REG_BASE16BPP_ADDR2, reg20); + buf = udl_set_register(buf, UDL_REG_BASE16BPP_ADDR1, reg21); + buf = udl_set_register(buf, UDL_REG_BASE16BPP_ADDR0, reg22); + + return buf; } /* * DisplayLink HW has separate 16bpp and 8bpp framebuffers. * In 24bpp modes, the low 323 RGB bits go in the 8bpp framebuffer */ -static char *udl_set_base8bpp(char *wrptr, u32 base) +static char *udl_set_base8bpp(char *buf, u32 base) { - wrptr = udl_set_register(wrptr, 0x26, base >> 16); - wrptr = udl_set_register(wrptr, 0x27, base >> 8); - return udl_set_register(wrptr, 0x28, base); + /* the base pointer is 24 bits wide, 0x26 is hi byte. */ + u8 reg26 = FIELD_GET(UDL_BASE_ADDR2_MASK, base); + u8 reg27 = FIELD_GET(UDL_BASE_ADDR1_MASK, base); + u8 reg28 = FIELD_GET(UDL_BASE_ADDR0_MASK, base); + + buf = udl_set_register(buf, UDL_REG_BASE8BPP_ADDR2, reg26); + buf = udl_set_register(buf, UDL_REG_BASE8BPP_ADDR1, reg27); + buf = udl_set_register(buf, UDL_REG_BASE8BPP_ADDR0, reg28); + + return buf; } static char *udl_set_register_16(char *wrptr, u8 reg, u16 value) @@ -139,84 +143,46 @@ static char *udl_set_register_lfsr16(char *wrptr, u8 reg, u16 value) } /* - * This takes a standard fbdev screeninfo struct and all of its monitor mode - * details and converts them into the DisplayLink equivalent register commands. - ERR(vreg(dev, 0x00, (color_depth == 16) ? 0 : 1)); - ERR(vreg_lfsr16(dev, 0x01, xDisplayStart)); - ERR(vreg_lfsr16(dev, 0x03, xDisplayEnd)); - ERR(vreg_lfsr16(dev, 0x05, yDisplayStart)); - ERR(vreg_lfsr16(dev, 0x07, yDisplayEnd)); - ERR(vreg_lfsr16(dev, 0x09, xEndCount)); - ERR(vreg_lfsr16(dev, 0x0B, hSyncStart)); - ERR(vreg_lfsr16(dev, 0x0D, hSyncEnd)); - ERR(vreg_big_endian(dev, 0x0F, hPixels)); - ERR(vreg_lfsr16(dev, 0x11, yEndCount)); - ERR(vreg_lfsr16(dev, 0x13, vSyncStart)); - ERR(vreg_lfsr16(dev, 0x15, vSyncEnd)); - ERR(vreg_big_endian(dev, 0x17, vPixels)); - ERR(vreg_little_endian(dev, 0x1B, pixelClock5KHz)); - - ERR(vreg(dev, 0x1F, 0)); - - ERR(vbuf(dev, WRITE_VIDREG_UNLOCK, DSIZEOF(WRITE_VIDREG_UNLOCK))); + * Takes a DRM display mode and converts it into the DisplayLink + * equivalent register commands. */ -static char *udl_set_vid_cmds(char *wrptr, struct drm_display_mode *mode) +static char *udl_set_display_mode(char *buf, struct drm_display_mode *mode) { - u16 xds, yds; - u16 xde, yde; - u16 yec; - - /* x display start */ - xds = mode->crtc_htotal - mode->crtc_hsync_start; - wrptr = udl_set_register_lfsr16(wrptr, 0x01, xds); - /* x display end */ - xde = xds + mode->crtc_hdisplay; - wrptr = udl_set_register_lfsr16(wrptr, 0x03, xde); - - /* y display start */ - yds = mode->crtc_vtotal - mode->crtc_vsync_start; - wrptr = udl_set_register_lfsr16(wrptr, 0x05, yds); - /* y display end */ - yde = yds + mode->crtc_vdisplay; - wrptr = udl_set_register_lfsr16(wrptr, 0x07, yde); + u16 reg01 = mode->crtc_htotal - mode->crtc_hsync_start; + u16 reg03 = reg01 + mode->crtc_hdisplay; + u16 reg05 = mode->crtc_vtotal - mode->crtc_vsync_start; + u16 reg07 = reg05 + mode->crtc_vdisplay; + u16 reg09 = mode->crtc_htotal - 1; + u16 reg0b = 1; /* libdlo hardcodes hsync start to 1 */ + u16 reg0d = mode->crtc_hsync_end - mode->crtc_hsync_start + 1; + u16 reg0f = mode->hdisplay; + u16 reg11 = mode->crtc_vtotal; + u16 reg13 = 0; /* libdlo hardcodes vsync start to 0 */ + u16 reg15 = mode->crtc_vsync_end - mode->crtc_vsync_start; + u16 reg17 = mode->crtc_vdisplay; + u16 reg1b = mode->clock / 5; + + buf = udl_set_register_lfsr16(buf, UDL_REG_XDISPLAYSTART, reg01); + buf = udl_set_register_lfsr16(buf, UDL_REG_XDISPLAYEND, reg03); + buf = udl_set_register_lfsr16(buf, UDL_REG_YDISPLAYSTART, reg05); + buf = udl_set_register_lfsr16(buf, UDL_REG_YDISPLAYEND, reg07); + buf = udl_set_register_lfsr16(buf, UDL_REG_XENDCOUNT, reg09); + buf = udl_set_register_lfsr16(buf, UDL_REG_HSYNCSTART, reg0b); + buf = udl_set_register_lfsr16(buf, UDL_REG_HSYNCEND, reg0d); + buf = udl_set_register_16(buf, UDL_REG_HPIXELS, reg0f); + buf = udl_set_register_lfsr16(buf, UDL_REG_YENDCOUNT, reg11); + buf = udl_set_register_lfsr16(buf, UDL_REG_VSYNCSTART, reg13); + buf = udl_set_register_lfsr16(buf, UDL_REG_VSYNCEND, reg15); + buf = udl_set_register_16(buf, UDL_REG_VPIXELS, reg17); + buf = udl_set_register_16be(buf, UDL_REG_PIXELCLOCK5KHZ, reg1b); - /* x end count is active + blanking - 1 */ - wrptr = udl_set_register_lfsr16(wrptr, 0x09, - mode->crtc_htotal - 1); - - /* libdlo hardcodes hsync start to 1 */ - wrptr = udl_set_register_lfsr16(wrptr, 0x0B, 1); - - /* hsync end is width of sync pulse + 1 */ - wrptr = udl_set_register_lfsr16(wrptr, 0x0D, - mode->crtc_hsync_end - mode->crtc_hsync_start + 1); - - /* hpixels is active pixels */ - wrptr = udl_set_register_16(wrptr, 0x0F, mode->hdisplay); - - /* yendcount is vertical active + vertical blanking */ - yec = mode->crtc_vtotal; - wrptr = udl_set_register_lfsr16(wrptr, 0x11, yec); - - /* libdlo hardcodes vsync start to 0 */ - wrptr = udl_set_register_lfsr16(wrptr, 0x13, 0); - - /* vsync end is width of vsync pulse */ - wrptr = udl_set_register_lfsr16(wrptr, 0x15, mode->crtc_vsync_end - mode->crtc_vsync_start); - - /* vpixels is active pixels */ - wrptr = udl_set_register_16(wrptr, 0x17, mode->crtc_vdisplay); - - wrptr = udl_set_register_16be(wrptr, 0x1B, - mode->clock / 5); - - return wrptr; + return buf; } static char *udl_dummy_render(char *wrptr) { - *wrptr++ = 0xAF; - *wrptr++ = 0x6A; /* copy */ + *wrptr++ = UDL_MSG_BULK; + *wrptr++ = UDL_CMD_WRITECOPY16; *wrptr++ = 0x00; /* from addr */ *wrptr++ = 0x00; *wrptr++ = 0x00; @@ -227,244 +193,352 @@ static char *udl_dummy_render(char *wrptr) return wrptr; } -static int udl_crtc_write_mode_to_hw(struct drm_crtc *crtc) +static long udl_log_cpp(unsigned int cpp) { - struct drm_device *dev = crtc->dev; - struct udl_device *udl = dev->dev_private; + if (WARN_ON(!is_power_of_2(cpp))) + return -EINVAL; + return __ffs(cpp); +} + +static int udl_handle_damage(struct drm_framebuffer *fb, + const struct iosys_map *map, + const struct drm_rect *clip) +{ + struct drm_device *dev = fb->dev; + struct udl_device *udl = to_udl(dev); + void *vaddr = map->vaddr; /* TODO: Use mapping abstraction properly */ + int i, ret; + char *cmd; struct urb *urb; - char *buf; - int retval; + int log_bpp; - urb = udl_get_urb(dev); + ret = udl_log_cpp(fb->format->cpp[0]); + if (ret < 0) + return ret; + log_bpp = ret; + + urb = udl_get_urb(udl); if (!urb) return -ENOMEM; + cmd = urb->transfer_buffer; + + for (i = clip->y1; i < clip->y2; i++) { + const int line_offset = fb->pitches[0] * i; + const int byte_offset = line_offset + (clip->x1 << log_bpp); + const int dev_byte_offset = (fb->width * i + clip->x1) << log_bpp; + const int byte_width = drm_rect_width(clip) << log_bpp; + ret = udl_render_hline(udl, log_bpp, &urb, (char *)vaddr, + &cmd, byte_offset, dev_byte_offset, + byte_width); + if (ret) + return ret; + } - buf = (char *)urb->transfer_buffer; + if (cmd > (char *)urb->transfer_buffer) { + /* Send partial buffer remaining before exiting */ + int len; + if (cmd < (char *)urb->transfer_buffer + urb->transfer_buffer_length) + *cmd++ = UDL_MSG_BULK; + len = cmd - (char *)urb->transfer_buffer; + ret = udl_submit_urb(udl, urb, len); + } else { + udl_urb_completion(urb); + } - memcpy(buf, udl->mode_buf, udl->mode_buf_len); - retval = udl_submit_urb(dev, urb, udl->mode_buf_len); - DRM_DEBUG("write mode info %d\n", udl->mode_buf_len); - return retval; + return 0; } +/* + * Primary plane + */ -static void udl_crtc_dpms(struct drm_crtc *crtc, int mode) -{ - struct drm_device *dev = crtc->dev; - struct udl_device *udl = dev->dev_private; - int retval; - - if (mode == DRM_MODE_DPMS_OFF) { - char *buf; - struct urb *urb; - urb = udl_get_urb(dev); - if (!urb) - return; - - buf = (char *)urb->transfer_buffer; - buf = udl_vidreg_lock(buf); - buf = udl_set_blank(buf, mode); - buf = udl_vidreg_unlock(buf); - - buf = udl_dummy_render(buf); - retval = udl_submit_urb(dev, urb, buf - (char *) - urb->transfer_buffer); - } else { - if (udl->mode_buf_len == 0) { - DRM_ERROR("Trying to enable DPMS with no mode\n"); - return; - } - udl_crtc_write_mode_to_hw(crtc); - } +static const uint32_t udl_primary_plane_formats[] = { + DRM_FORMAT_RGB565, + DRM_FORMAT_XRGB8888, +}; -} +static const uint64_t udl_primary_plane_fmtmods[] = { + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_MOD_INVALID +}; -#if 0 -static int -udl_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb, - int x, int y, enum mode_set_atomic state) +static int udl_primary_plane_helper_atomic_check(struct drm_plane *plane, + struct drm_atomic_state *state) { - return 0; + struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane); + struct drm_crtc *new_crtc = new_plane_state->crtc; + struct drm_crtc_state *new_crtc_state = NULL; + + if (new_crtc) + new_crtc_state = drm_atomic_get_new_crtc_state(state, new_crtc); + + return drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state, + DRM_PLANE_NO_SCALING, + DRM_PLANE_NO_SCALING, + false, false); } -static int -udl_pipe_set_base(struct drm_crtc *crtc, int x, int y, - struct drm_framebuffer *old_fb) +static void udl_primary_plane_helper_atomic_update(struct drm_plane *plane, + struct drm_atomic_state *state) { - return 0; + struct drm_device *dev = plane->dev; + struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); + struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); + struct drm_framebuffer *fb = plane_state->fb; + struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane); + struct drm_atomic_helper_damage_iter iter; + struct drm_rect damage; + int ret, idx; + + if (!fb) + return; /* no framebuffer; plane is disabled */ + + ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE); + if (ret) + return; + + if (!drm_dev_enter(dev, &idx)) + goto out_drm_gem_fb_end_cpu_access; + + drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state); + drm_atomic_for_each_plane_damage(&iter, &damage) { + udl_handle_damage(fb, &shadow_plane_state->data[0], &damage); + } + + drm_dev_exit(idx); + +out_drm_gem_fb_end_cpu_access: + drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); } -#endif -static int udl_crtc_mode_set(struct drm_crtc *crtc, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode, - int x, int y, - struct drm_framebuffer *old_fb) +static const struct drm_plane_helper_funcs udl_primary_plane_helper_funcs = { + DRM_GEM_SHADOW_PLANE_HELPER_FUNCS, + .atomic_check = udl_primary_plane_helper_atomic_check, + .atomic_update = udl_primary_plane_helper_atomic_update, +}; + +static const struct drm_plane_funcs udl_primary_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = drm_plane_cleanup, + DRM_GEM_SHADOW_PLANE_FUNCS, +}; + +/* + * CRTC + */ +static void udl_crtc_helper_atomic_enable(struct drm_crtc *crtc, struct drm_atomic_state *state) { struct drm_device *dev = crtc->dev; - struct udl_framebuffer *ufb = to_udl_fb(crtc->primary->fb); - struct udl_device *udl = dev->dev_private; + struct udl_device *udl = to_udl(dev); + struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + struct drm_display_mode *mode = &crtc_state->mode; + struct urb *urb; char *buf; - char *wrptr; - int color_depth = 0; - - udl->crtc = crtc; + int idx; - buf = (char *)udl->mode_buf; + if (!drm_dev_enter(dev, &idx)) + return; - /* for now we just clip 24 -> 16 - if we fix that fix this */ - /*if (crtc->fb->bits_per_pixel != 16) - color_depth = 1; */ + urb = udl_get_urb(udl); + if (!urb) + goto out; - /* This first section has to do with setting the base address on the - * controller * associated with the display. There are 2 base - * pointers, currently, we only * use the 16 bpp segment. - */ - wrptr = udl_vidreg_lock(buf); - wrptr = udl_set_color_depth(wrptr, color_depth); + buf = (char *)urb->transfer_buffer; + buf = udl_vidreg_lock(buf); + buf = udl_set_color_depth(buf, UDL_COLORDEPTH_16BPP); /* set base for 16bpp segment to 0 */ - wrptr = udl_set_base16bpp(wrptr, 0); + buf = udl_set_base16bpp(buf, 0); /* set base for 8bpp segment to end of fb */ - wrptr = udl_set_base8bpp(wrptr, 2 * mode->vdisplay * mode->hdisplay); + buf = udl_set_base8bpp(buf, 2 * mode->vdisplay * mode->hdisplay); + buf = udl_set_display_mode(buf, mode); + buf = udl_set_blank_mode(buf, UDL_BLANKMODE_ON); + buf = udl_vidreg_unlock(buf); + buf = udl_dummy_render(buf); - wrptr = udl_set_vid_cmds(wrptr, adjusted_mode); - wrptr = udl_set_blank(wrptr, DRM_MODE_DPMS_ON); - wrptr = udl_vidreg_unlock(wrptr); + udl_submit_urb(udl, urb, buf - (char *)urb->transfer_buffer); - wrptr = udl_dummy_render(wrptr); +out: + drm_dev_exit(idx); +} - if (old_fb) { - struct udl_framebuffer *uold_fb = to_udl_fb(old_fb); - uold_fb->active_16 = false; - } - ufb->active_16 = true; - udl->mode_buf_len = wrptr - buf; +static void udl_crtc_helper_atomic_disable(struct drm_crtc *crtc, struct drm_atomic_state *state) +{ + struct drm_device *dev = crtc->dev; + struct udl_device *udl = to_udl(dev); + struct urb *urb; + char *buf; + int idx; - /* damage all of it */ - udl_handle_damage(ufb, 0, 0, ufb->base.width, ufb->base.height); - return 0; -} + if (!drm_dev_enter(dev, &idx)) + return; + urb = udl_get_urb(udl); + if (!urb) + goto out; -static void udl_crtc_disable(struct drm_crtc *crtc) -{ - udl_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); -} + buf = (char *)urb->transfer_buffer; + buf = udl_vidreg_lock(buf); + buf = udl_set_blank_mode(buf, UDL_BLANKMODE_POWERDOWN); + buf = udl_vidreg_unlock(buf); + buf = udl_dummy_render(buf); -static void udl_crtc_destroy(struct drm_crtc *crtc) -{ - drm_crtc_cleanup(crtc); - kfree(crtc); + udl_submit_urb(udl, urb, buf - (char *)urb->transfer_buffer); + +out: + drm_dev_exit(idx); } -static int udl_crtc_page_flip(struct drm_crtc *crtc, - struct drm_framebuffer *fb, - struct drm_pending_vblank_event *event, - uint32_t page_flip_flags, - struct drm_modeset_acquire_ctx *ctx) -{ - struct udl_framebuffer *ufb = to_udl_fb(fb); - struct drm_device *dev = crtc->dev; +static const struct drm_crtc_helper_funcs udl_crtc_helper_funcs = { + .atomic_check = drm_crtc_helper_atomic_check, + .atomic_enable = udl_crtc_helper_atomic_enable, + .atomic_disable = udl_crtc_helper_atomic_disable, +}; - struct drm_framebuffer *old_fb = crtc->primary->fb; - if (old_fb) { - struct udl_framebuffer *uold_fb = to_udl_fb(old_fb); - uold_fb->active_16 = false; - } - ufb->active_16 = true; +static const struct drm_crtc_funcs udl_crtc_funcs = { + .reset = drm_atomic_helper_crtc_reset, + .destroy = drm_crtc_cleanup, + .set_config = drm_atomic_helper_set_config, + .page_flip = drm_atomic_helper_page_flip, + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, +}; - udl_handle_damage(ufb, 0, 0, fb->width, fb->height); +/* + * Encoder + */ - spin_lock_irq(&dev->event_lock); - if (event) - drm_crtc_send_vblank_event(crtc, event); - spin_unlock_irq(&dev->event_lock); - crtc->primary->fb = fb; +static const struct drm_encoder_funcs udl_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; - return 0; -} +/* + * Connector + */ -static void udl_crtc_prepare(struct drm_crtc *crtc) +static int udl_connector_helper_get_modes(struct drm_connector *connector) { + const struct drm_edid *drm_edid; + int count; + + drm_edid = udl_edid_read(connector); + drm_edid_connector_update(connector, drm_edid); + count = drm_edid_connector_add_modes(connector); + drm_edid_free(drm_edid); + + return count; } -static void udl_crtc_commit(struct drm_crtc *crtc) +static int udl_connector_helper_detect_ctx(struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx, + bool force) { - udl_crtc_dpms(crtc, DRM_MODE_DPMS_ON); + struct udl_device *udl = to_udl(connector->dev); + + if (udl_probe_edid(udl)) + return connector_status_connected; + + return connector_status_disconnected; } -static const struct drm_crtc_helper_funcs udl_helper_funcs = { - .dpms = udl_crtc_dpms, - .mode_set = udl_crtc_mode_set, - .prepare = udl_crtc_prepare, - .commit = udl_crtc_commit, - .disable = udl_crtc_disable, +static const struct drm_connector_helper_funcs udl_connector_helper_funcs = { + .get_modes = udl_connector_helper_get_modes, + .detect_ctx = udl_connector_helper_detect_ctx, }; -static const struct drm_crtc_funcs udl_crtc_funcs = { - .set_config = drm_crtc_helper_set_config, - .destroy = udl_crtc_destroy, - .page_flip = udl_crtc_page_flip, +static const struct drm_connector_funcs udl_connector_funcs = { + .reset = drm_atomic_helper_connector_reset, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = drm_connector_cleanup, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; -static int udl_crtc_init(struct drm_device *dev) -{ - struct drm_crtc *crtc; +/* + * Modesetting + */ - crtc = kzalloc(sizeof(struct drm_crtc) + sizeof(struct drm_connector *), GFP_KERNEL); - if (crtc == NULL) - return -ENOMEM; +static enum drm_mode_status udl_mode_config_mode_valid(struct drm_device *dev, + const struct drm_display_mode *mode) +{ + struct udl_device *udl = to_udl(dev); - drm_crtc_init(dev, crtc, &udl_crtc_funcs); - drm_crtc_helper_add(crtc, &udl_helper_funcs); + if (udl->sku_pixel_limit) { + if (mode->vdisplay * mode->hdisplay > udl->sku_pixel_limit) + return MODE_MEM; + } - return 0; + return MODE_OK; } -static const struct drm_mode_config_funcs udl_mode_funcs = { - .fb_create = udl_fb_user_fb_create, - .output_poll_changed = NULL, +static const struct drm_mode_config_funcs udl_mode_config_funcs = { + .fb_create = drm_gem_fb_create_with_dirty, + .mode_valid = udl_mode_config_mode_valid, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, }; -int udl_modeset_init(struct drm_device *dev) +int udl_modeset_init(struct udl_device *udl) { + struct drm_device *dev = &udl->drm; + struct drm_plane *primary_plane; + struct drm_crtc *crtc; struct drm_encoder *encoder; - drm_mode_config_init(dev); + struct drm_connector *connector; + int ret; + + ret = drmm_mode_config_init(dev); + if (ret) + return ret; dev->mode_config.min_width = 640; dev->mode_config.min_height = 480; - dev->mode_config.max_width = 2048; dev->mode_config.max_height = 2048; - - dev->mode_config.prefer_shadow = 0; - dev->mode_config.preferred_depth = 24; - - dev->mode_config.funcs = &udl_mode_funcs; - - udl_crtc_init(dev); - - encoder = udl_encoder_init(dev); - - udl_connector_init(dev, encoder); + dev->mode_config.preferred_depth = 16; + dev->mode_config.funcs = &udl_mode_config_funcs; + + primary_plane = &udl->primary_plane; + ret = drm_universal_plane_init(dev, primary_plane, 0, + &udl_primary_plane_funcs, + udl_primary_plane_formats, + ARRAY_SIZE(udl_primary_plane_formats), + udl_primary_plane_fmtmods, + DRM_PLANE_TYPE_PRIMARY, NULL); + if (ret) + return ret; + drm_plane_helper_add(primary_plane, &udl_primary_plane_helper_funcs); + drm_plane_enable_fb_damage_clips(primary_plane); + + crtc = &udl->crtc; + ret = drm_crtc_init_with_planes(dev, crtc, primary_plane, NULL, + &udl_crtc_funcs, NULL); + if (ret) + return ret; + drm_crtc_helper_add(crtc, &udl_crtc_helper_funcs); + + encoder = &udl->encoder; + ret = drm_encoder_init(dev, encoder, &udl_encoder_funcs, DRM_MODE_ENCODER_DAC, NULL); + if (ret) + return ret; + encoder->possible_crtcs = drm_crtc_mask(crtc); + + connector = &udl->connector; + ret = drm_connector_init(dev, connector, &udl_connector_funcs, DRM_MODE_CONNECTOR_VGA); + if (ret) + return ret; + drm_connector_helper_add(connector, &udl_connector_helper_funcs); + + connector->polled = DRM_CONNECTOR_POLL_CONNECT | + DRM_CONNECTOR_POLL_DISCONNECT; + + ret = drm_connector_attach_encoder(connector, encoder); + if (ret) + return ret; + + drm_mode_config_reset(dev); + drmm_kms_helper_poll_init(dev); return 0; } - -void udl_modeset_restore(struct drm_device *dev) -{ - struct udl_device *udl = dev->dev_private; - struct udl_framebuffer *ufb; - - if (!udl->crtc || !udl->crtc->primary->fb) - return; - udl_crtc_commit(udl->crtc); - ufb = to_udl_fb(udl->crtc->primary->fb); - udl_handle_damage(ufb, 0, 0, ufb->base.width, ufb->base.height); -} - -void udl_modeset_cleanup(struct drm_device *dev) -{ - drm_mode_config_cleanup(dev); -} diff --git a/drivers/gpu/drm/udl/udl_proto.h b/drivers/gpu/drm/udl/udl_proto.h new file mode 100644 index 000000000000..c92d2109584c --- /dev/null +++ b/drivers/gpu/drm/udl/udl_proto.h @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef UDL_PROTO_H +#define UDL_PROTO_H + +#include <linux/bits.h> + +#define UDL_MSG_BULK 0xaf + +/* Register access */ +#define UDL_CMD_WRITEREG 0x20 /* See register constants below */ + +/* Framebuffer access */ +#define UDL_CMD_WRITERAW8 0x60 /* 8 bit raw write command. */ +#define UDL_CMD_WRITERL8 0x61 /* 8 bit run length command. */ +#define UDL_CMD_WRITECOPY8 0x62 /* 8 bit copy command. */ +#define UDL_CMD_WRITERLX8 0x63 /* 8 bit extended run length command. */ +#define UDL_CMD_WRITERAW16 0x68 /* 16 bit raw write command. */ +#define UDL_CMD_WRITERL16 0x69 /* 16 bit run length command. */ +#define UDL_CMD_WRITECOPY16 0x6a /* 16 bit copy command. */ +#define UDL_CMD_WRITERLX16 0x6b /* 16 bit extended run length command. */ + +/* Color depth */ +#define UDL_REG_COLORDEPTH 0x00 +#define UDL_COLORDEPTH_16BPP 0 +#define UDL_COLORDEPTH_24BPP 1 + +/* Display-mode settings */ +#define UDL_REG_XDISPLAYSTART 0x01 +#define UDL_REG_XDISPLAYEND 0x03 +#define UDL_REG_YDISPLAYSTART 0x05 +#define UDL_REG_YDISPLAYEND 0x07 +#define UDL_REG_XENDCOUNT 0x09 +#define UDL_REG_HSYNCSTART 0x0b +#define UDL_REG_HSYNCEND 0x0d +#define UDL_REG_HPIXELS 0x0f +#define UDL_REG_YENDCOUNT 0x11 +#define UDL_REG_VSYNCSTART 0x13 +#define UDL_REG_VSYNCEND 0x15 +#define UDL_REG_VPIXELS 0x17 +#define UDL_REG_PIXELCLOCK5KHZ 0x1b + +/* On/Off for driving the DisplayLink framebuffer to the display */ +#define UDL_REG_BLANKMODE 0x1f +#define UDL_BLANKMODE_ON 0x00 /* hsync and vsync on, visible */ +#define UDL_BLANKMODE_BLANKED 0x01 /* hsync and vsync on, blanked */ +#define UDL_BLANKMODE_VSYNC_OFF 0x03 /* vsync off, blanked */ +#define UDL_BLANKMODE_HSYNC_OFF 0x05 /* hsync off, blanked */ +#define UDL_BLANKMODE_POWERDOWN 0x07 /* powered off; requires modeset */ + +/* Framebuffer address */ +#define UDL_REG_BASE16BPP_ADDR2 0x20 +#define UDL_REG_BASE16BPP_ADDR1 0x21 +#define UDL_REG_BASE16BPP_ADDR0 0x22 +#define UDL_REG_BASE8BPP_ADDR2 0x26 +#define UDL_REG_BASE8BPP_ADDR1 0x27 +#define UDL_REG_BASE8BPP_ADDR0 0x28 + +#define UDL_BASE_ADDR0_MASK GENMASK(7, 0) +#define UDL_BASE_ADDR1_MASK GENMASK(15, 8) +#define UDL_BASE_ADDR2_MASK GENMASK(23, 16) + +/* Lock/unlock video registers */ +#define UDL_REG_VIDREG 0xff +#define UDL_VIDREG_LOCK 0x00 +#define UDL_VIDREG_UNLOCK 0xff + +#endif diff --git a/drivers/gpu/drm/udl/udl_transfer.c b/drivers/gpu/drm/udl/udl_transfer.c index ce87661e544f..7d670b3a5293 100644 --- a/drivers/gpu/drm/udl/udl_transfer.c +++ b/drivers/gpu/drm/udl/udl_transfer.c @@ -1,22 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2012 Red Hat * based in parts on udlfb.c: * Copyright (C) 2009 Roberto De Ioris <roberto@unbit.it> * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@gmail.com> * Copyright (C) 2009 Bernie Thompson <bernie@plugable.com> - * - * This file is subject to the terms and conditions of the GNU General Public - * License v2. See the file COPYING in the main directory of this archive for - * more details. */ -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/fb.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> -#include <drm/drmP.h> #include "udl_drv.h" +#include "udl_proto.h" #define MAX_CMD_PIXELS 255 @@ -32,46 +26,6 @@ #define MIN_RAW_PIX_BYTES 2 #define MIN_RAW_CMD_BYTES (RAW_HEADER_BYTES + MIN_RAW_PIX_BYTES) -/* - * Trims identical data from front and back of line - * Sets new front buffer address and width - * And returns byte count of identical pixels - * Assumes CPU natural alignment (unsigned long) - * for back and front buffer ptrs and width - */ -#if 0 -static int udl_trim_hline(const u8 *bback, const u8 **bfront, int *width_bytes) -{ - int j, k; - const unsigned long *back = (const unsigned long *) bback; - const unsigned long *front = (const unsigned long *) *bfront; - const int width = *width_bytes / sizeof(unsigned long); - int identical = width; - int start = width; - int end = width; - - for (j = 0; j < width; j++) { - if (back[j] != front[j]) { - start = j; - break; - } - } - - for (k = width - 1; k > j; k--) { - if (back[k] != front[k]) { - end = k+1; - break; - } - } - - identical = start + (width - end); - *bfront = (u8 *) &front[start]; - *width_bytes = (end - start) * sizeof(unsigned long); - - return identical * sizeof(unsigned long); -} -#endif - static inline u16 pixel32_to_be16(const uint32_t pixel) { return (((pixel >> 3) & 0x001f) | @@ -136,8 +90,8 @@ static void udl_compress_hline16( const u8 *cmd_pixel_start, *cmd_pixel_end = NULL; uint16_t pixel_val16; - *cmd++ = 0xaf; - *cmd++ = 0x6b; + *cmd++ = UDL_MSG_BULK; + *cmd++ = UDL_CMD_WRITERLX16; *cmd++ = (uint8_t) ((dev_addr >> 16) & 0xFF); *cmd++ = (uint8_t) ((dev_addr >> 8) & 0xFF); *cmd++ = (uint8_t) ((dev_addr) & 0xFF); @@ -199,7 +153,7 @@ static void udl_compress_hline16( if (cmd_buffer_end <= MIN_RLX_CMD_BYTES + cmd) { /* Fill leftover bytes with no-ops */ if (cmd_buffer_end > cmd) - memset(cmd, 0xAF, cmd_buffer_end - cmd); + memset(cmd, UDL_MSG_BULK, cmd_buffer_end - cmd); cmd = (uint8_t *) cmd_buffer_end; } @@ -216,11 +170,10 @@ static void udl_compress_hline16( * (that we can only write to, slowly, and can never read), and (optionally) * our shadow copy that tracks what's been sent to that hardware buffer. */ -int udl_render_hline(struct drm_device *dev, int log_bpp, struct urb **urb_ptr, +int udl_render_hline(struct udl_device *udl, int log_bpp, struct urb **urb_ptr, const char *front, char **urb_buf_ptr, u32 byte_offset, u32 device_byte_offset, - u32 byte_width, - int *ident_ptr, int *sent_ptr) + u32 byte_width) { const u8 *line_start, *line_end, *next_pixel; u32 base16 = 0 + (device_byte_offset >> log_bpp) * 2; @@ -228,7 +181,11 @@ int udl_render_hline(struct drm_device *dev, int log_bpp, struct urb **urb_ptr, u8 *cmd = *urb_buf_ptr; u8 *cmd_end = (u8 *) urb->transfer_buffer + urb->transfer_buffer_length; - BUG_ON(!(log_bpp == 1 || log_bpp == 2)); + if (WARN_ON(!(log_bpp == 1 || log_bpp == 2))) { + /* need to finish URB at error from this function */ + udl_urb_completion(urb); + return -EINVAL; + } line_start = (u8 *) (front + byte_offset); next_pixel = line_start; @@ -242,12 +199,12 @@ int udl_render_hline(struct drm_device *dev, int log_bpp, struct urb **urb_ptr, if (cmd >= cmd_end) { int len = cmd - (u8 *) urb->transfer_buffer; - if (udl_submit_urb(dev, urb, len)) - return 1; /* lost pixels is set */ - *sent_ptr += len; - urb = udl_get_urb(dev); + int ret = udl_submit_urb(udl, urb, len); + if (ret) + return ret; + urb = udl_get_urb(udl); if (!urb) - return 1; /* lost_pixels is set */ + return -EAGAIN; *urb_ptr = urb; cmd = urb->transfer_buffer; cmd_end = &cmd[urb->transfer_buffer_length]; @@ -258,4 +215,3 @@ int udl_render_hline(struct drm_device *dev, int log_bpp, struct urb **urb_ptr, return 0; } - |
