162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2012 Red Hat
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/module.h>
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <drm/drm_drv.h>
962306a36Sopenharmony_ci#include <drm/drm_fbdev_generic.h>
1062306a36Sopenharmony_ci#include <drm/drm_file.h>
1162306a36Sopenharmony_ci#include <drm/drm_gem_shmem_helper.h>
1262306a36Sopenharmony_ci#include <drm/drm_managed.h>
1362306a36Sopenharmony_ci#include <drm/drm_modeset_helper.h>
1462306a36Sopenharmony_ci#include <drm/drm_ioctl.h>
1562306a36Sopenharmony_ci#include <drm/drm_probe_helper.h>
1662306a36Sopenharmony_ci#include <drm/drm_print.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include "udl_drv.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistatic int udl_usb_suspend(struct usb_interface *interface,
2162306a36Sopenharmony_ci			   pm_message_t message)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	struct drm_device *dev = usb_get_intfdata(interface);
2462306a36Sopenharmony_ci	int ret;
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	ret = drm_mode_config_helper_suspend(dev);
2762306a36Sopenharmony_ci	if (ret)
2862306a36Sopenharmony_ci		return ret;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	udl_sync_pending_urbs(dev);
3162306a36Sopenharmony_ci	return 0;
3262306a36Sopenharmony_ci}
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic int udl_usb_resume(struct usb_interface *interface)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	struct drm_device *dev = usb_get_intfdata(interface);
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	return drm_mode_config_helper_resume(dev);
3962306a36Sopenharmony_ci}
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic int udl_usb_reset_resume(struct usb_interface *interface)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	struct drm_device *dev = usb_get_intfdata(interface);
4462306a36Sopenharmony_ci	struct udl_device *udl = to_udl(dev);
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	udl_select_std_channel(udl);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	return drm_mode_config_helper_resume(dev);
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci/*
5262306a36Sopenharmony_ci * FIXME: Dma-buf sharing requires DMA support by the importing device.
5362306a36Sopenharmony_ci *        This function is a workaround to make USB devices work as well.
5462306a36Sopenharmony_ci *        See todo.rst for how to fix the issue in the dma-buf framework.
5562306a36Sopenharmony_ci */
5662306a36Sopenharmony_cistatic struct drm_gem_object *udl_driver_gem_prime_import(struct drm_device *dev,
5762306a36Sopenharmony_ci							  struct dma_buf *dma_buf)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	struct udl_device *udl = to_udl(dev);
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	if (!udl->dmadev)
6262306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	return drm_gem_prime_import_dev(dev, dma_buf, udl->dmadev);
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ciDEFINE_DRM_GEM_FOPS(udl_driver_fops);
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic const struct drm_driver driver = {
7062306a36Sopenharmony_ci	.driver_features = DRIVER_ATOMIC | DRIVER_GEM | DRIVER_MODESET,
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	/* GEM hooks */
7362306a36Sopenharmony_ci	.fops = &udl_driver_fops,
7462306a36Sopenharmony_ci	DRM_GEM_SHMEM_DRIVER_OPS,
7562306a36Sopenharmony_ci	.gem_prime_import = udl_driver_gem_prime_import,
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	.name = DRIVER_NAME,
7862306a36Sopenharmony_ci	.desc = DRIVER_DESC,
7962306a36Sopenharmony_ci	.date = DRIVER_DATE,
8062306a36Sopenharmony_ci	.major = DRIVER_MAJOR,
8162306a36Sopenharmony_ci	.minor = DRIVER_MINOR,
8262306a36Sopenharmony_ci	.patchlevel = DRIVER_PATCHLEVEL,
8362306a36Sopenharmony_ci};
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistatic struct udl_device *udl_driver_create(struct usb_interface *interface)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	struct udl_device *udl;
8862306a36Sopenharmony_ci	int r;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	udl = devm_drm_dev_alloc(&interface->dev, &driver,
9162306a36Sopenharmony_ci				 struct udl_device, drm);
9262306a36Sopenharmony_ci	if (IS_ERR(udl))
9362306a36Sopenharmony_ci		return udl;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	r = udl_init(udl);
9662306a36Sopenharmony_ci	if (r)
9762306a36Sopenharmony_ci		return ERR_PTR(r);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	usb_set_intfdata(interface, udl);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	return udl;
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic int udl_usb_probe(struct usb_interface *interface,
10562306a36Sopenharmony_ci			 const struct usb_device_id *id)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	int r;
10862306a36Sopenharmony_ci	struct udl_device *udl;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	udl = udl_driver_create(interface);
11162306a36Sopenharmony_ci	if (IS_ERR(udl))
11262306a36Sopenharmony_ci		return PTR_ERR(udl);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	r = drm_dev_register(&udl->drm, 0);
11562306a36Sopenharmony_ci	if (r)
11662306a36Sopenharmony_ci		return r;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	DRM_INFO("Initialized udl on minor %d\n", udl->drm.primary->index);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	drm_fbdev_generic_setup(&udl->drm, 0);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	return 0;
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic void udl_usb_disconnect(struct usb_interface *interface)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	struct drm_device *dev = usb_get_intfdata(interface);
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	drm_kms_helper_poll_fini(dev);
13062306a36Sopenharmony_ci	udl_drop_usb(dev);
13162306a36Sopenharmony_ci	drm_dev_unplug(dev);
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci/*
13562306a36Sopenharmony_ci * There are many DisplayLink-based graphics products, all with unique PIDs.
13662306a36Sopenharmony_ci * So we match on DisplayLink's VID + Vendor-Defined Interface Class (0xff)
13762306a36Sopenharmony_ci * We also require a match on SubClass (0x00) and Protocol (0x00),
13862306a36Sopenharmony_ci * which is compatible with all known USB 2.0 era graphics chips and firmware,
13962306a36Sopenharmony_ci * but allows DisplayLink to increment those for any future incompatible chips
14062306a36Sopenharmony_ci */
14162306a36Sopenharmony_cistatic const struct usb_device_id id_table[] = {
14262306a36Sopenharmony_ci	{.idVendor = 0x17e9, .bInterfaceClass = 0xff,
14362306a36Sopenharmony_ci	 .bInterfaceSubClass = 0x00,
14462306a36Sopenharmony_ci	 .bInterfaceProtocol = 0x00,
14562306a36Sopenharmony_ci	 .match_flags = USB_DEVICE_ID_MATCH_VENDOR |
14662306a36Sopenharmony_ci			USB_DEVICE_ID_MATCH_INT_CLASS |
14762306a36Sopenharmony_ci			USB_DEVICE_ID_MATCH_INT_SUBCLASS |
14862306a36Sopenharmony_ci			USB_DEVICE_ID_MATCH_INT_PROTOCOL,},
14962306a36Sopenharmony_ci	{},
15062306a36Sopenharmony_ci};
15162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, id_table);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic struct usb_driver udl_driver = {
15462306a36Sopenharmony_ci	.name = "udl",
15562306a36Sopenharmony_ci	.probe = udl_usb_probe,
15662306a36Sopenharmony_ci	.disconnect = udl_usb_disconnect,
15762306a36Sopenharmony_ci	.suspend = udl_usb_suspend,
15862306a36Sopenharmony_ci	.resume = udl_usb_resume,
15962306a36Sopenharmony_ci	.reset_resume = udl_usb_reset_resume,
16062306a36Sopenharmony_ci	.id_table = id_table,
16162306a36Sopenharmony_ci};
16262306a36Sopenharmony_cimodule_usb_driver(udl_driver);
16362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
164