162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2012 Red Hat
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * based in parts on udlfb.c:
662306a36Sopenharmony_ci * Copyright (C) 2009 Roberto De Ioris <roberto@unbit.it>
762306a36Sopenharmony_ci * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@gmail.com>
862306a36Sopenharmony_ci * Copyright (C) 2009 Bernie Thompson <bernie@plugable.com>
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <drm/drm.h>
1262306a36Sopenharmony_ci#include <drm/drm_print.h>
1362306a36Sopenharmony_ci#include <drm/drm_probe_helper.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include "udl_drv.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/* -BULK_SIZE as per usb-skeleton. Can we get full page and avoid overhead? */
1862306a36Sopenharmony_ci#define BULK_SIZE 512
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define NR_USB_REQUEST_CHANNEL 0x12
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define MAX_TRANSFER (PAGE_SIZE*16 - BULK_SIZE)
2362306a36Sopenharmony_ci#define WRITES_IN_FLIGHT (20)
2462306a36Sopenharmony_ci#define MAX_VENDOR_DESCRIPTOR_SIZE 256
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic struct urb *udl_get_urb_locked(struct udl_device *udl, long timeout);
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic int udl_parse_vendor_descriptor(struct udl_device *udl)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	struct usb_device *udev = udl_to_usb_device(udl);
3162306a36Sopenharmony_ci	char *desc;
3262306a36Sopenharmony_ci	char *buf;
3362306a36Sopenharmony_ci	char *desc_end;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	u8 total_len = 0;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	buf = kzalloc(MAX_VENDOR_DESCRIPTOR_SIZE, GFP_KERNEL);
3862306a36Sopenharmony_ci	if (!buf)
3962306a36Sopenharmony_ci		return false;
4062306a36Sopenharmony_ci	desc = buf;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	total_len = usb_get_descriptor(udev, 0x5f, /* vendor specific */
4362306a36Sopenharmony_ci				    0, desc, MAX_VENDOR_DESCRIPTOR_SIZE);
4462306a36Sopenharmony_ci	if (total_len > 5) {
4562306a36Sopenharmony_ci		DRM_INFO("vendor descriptor length:%x data:%11ph\n",
4662306a36Sopenharmony_ci			total_len, desc);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci		if ((desc[0] != total_len) || /* descriptor length */
4962306a36Sopenharmony_ci		    (desc[1] != 0x5f) ||   /* vendor descriptor type */
5062306a36Sopenharmony_ci		    (desc[2] != 0x01) ||   /* version (2 bytes) */
5162306a36Sopenharmony_ci		    (desc[3] != 0x00) ||
5262306a36Sopenharmony_ci		    (desc[4] != total_len - 2)) /* length after type */
5362306a36Sopenharmony_ci			goto unrecognized;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci		desc_end = desc + total_len;
5662306a36Sopenharmony_ci		desc += 5; /* the fixed header we've already parsed */
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci		while (desc < desc_end) {
5962306a36Sopenharmony_ci			u8 length;
6062306a36Sopenharmony_ci			u16 key;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci			key = le16_to_cpu(*((u16 *) desc));
6362306a36Sopenharmony_ci			desc += sizeof(u16);
6462306a36Sopenharmony_ci			length = *desc;
6562306a36Sopenharmony_ci			desc++;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci			switch (key) {
6862306a36Sopenharmony_ci			case 0x0200: { /* max_area */
6962306a36Sopenharmony_ci				u32 max_area;
7062306a36Sopenharmony_ci				max_area = le32_to_cpu(*((u32 *)desc));
7162306a36Sopenharmony_ci				DRM_DEBUG("DL chip limited to %d pixel modes\n",
7262306a36Sopenharmony_ci					max_area);
7362306a36Sopenharmony_ci				udl->sku_pixel_limit = max_area;
7462306a36Sopenharmony_ci				break;
7562306a36Sopenharmony_ci			}
7662306a36Sopenharmony_ci			default:
7762306a36Sopenharmony_ci				break;
7862306a36Sopenharmony_ci			}
7962306a36Sopenharmony_ci			desc += length;
8062306a36Sopenharmony_ci		}
8162306a36Sopenharmony_ci	}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	goto success;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ciunrecognized:
8662306a36Sopenharmony_ci	/* allow udlfb to load for now even if firmware unrecognized */
8762306a36Sopenharmony_ci	DRM_ERROR("Unrecognized vendor firmware descriptor\n");
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cisuccess:
9062306a36Sopenharmony_ci	kfree(buf);
9162306a36Sopenharmony_ci	return true;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci/*
9562306a36Sopenharmony_ci * Need to ensure a channel is selected before submitting URBs
9662306a36Sopenharmony_ci */
9762306a36Sopenharmony_ciint udl_select_std_channel(struct udl_device *udl)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	static const u8 set_def_chn[] = {0x57, 0xCD, 0xDC, 0xA7,
10062306a36Sopenharmony_ci					 0x1C, 0x88, 0x5E, 0x15,
10162306a36Sopenharmony_ci					 0x60, 0xFE, 0xC6, 0x97,
10262306a36Sopenharmony_ci					 0x16, 0x3D, 0x47, 0xF2};
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	void *sendbuf;
10562306a36Sopenharmony_ci	int ret;
10662306a36Sopenharmony_ci	struct usb_device *udev = udl_to_usb_device(udl);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	sendbuf = kmemdup(set_def_chn, sizeof(set_def_chn), GFP_KERNEL);
10962306a36Sopenharmony_ci	if (!sendbuf)
11062306a36Sopenharmony_ci		return -ENOMEM;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
11362306a36Sopenharmony_ci			      NR_USB_REQUEST_CHANNEL,
11462306a36Sopenharmony_ci			      (USB_DIR_OUT | USB_TYPE_VENDOR), 0, 0,
11562306a36Sopenharmony_ci			      sendbuf, sizeof(set_def_chn),
11662306a36Sopenharmony_ci			      USB_CTRL_SET_TIMEOUT);
11762306a36Sopenharmony_ci	kfree(sendbuf);
11862306a36Sopenharmony_ci	return ret < 0 ? ret : 0;
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_civoid udl_urb_completion(struct urb *urb)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	struct urb_node *unode = urb->context;
12462306a36Sopenharmony_ci	struct udl_device *udl = unode->dev;
12562306a36Sopenharmony_ci	unsigned long flags;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	/* sync/async unlink faults aren't errors */
12862306a36Sopenharmony_ci	if (urb->status) {
12962306a36Sopenharmony_ci		if (!(urb->status == -ENOENT ||
13062306a36Sopenharmony_ci		    urb->status == -ECONNRESET ||
13162306a36Sopenharmony_ci		    urb->status == -EPROTO ||
13262306a36Sopenharmony_ci		    urb->status == -ESHUTDOWN)) {
13362306a36Sopenharmony_ci			DRM_ERROR("%s - nonzero write bulk status received: %d\n",
13462306a36Sopenharmony_ci				__func__, urb->status);
13562306a36Sopenharmony_ci		}
13662306a36Sopenharmony_ci	}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	urb->transfer_buffer_length = udl->urbs.size; /* reset to actual */
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	spin_lock_irqsave(&udl->urbs.lock, flags);
14162306a36Sopenharmony_ci	list_add_tail(&unode->entry, &udl->urbs.list);
14262306a36Sopenharmony_ci	udl->urbs.available++;
14362306a36Sopenharmony_ci	spin_unlock_irqrestore(&udl->urbs.lock, flags);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	wake_up(&udl->urbs.sleep);
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistatic void udl_free_urb_list(struct drm_device *dev)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	struct udl_device *udl = to_udl(dev);
15162306a36Sopenharmony_ci	struct urb_node *unode;
15262306a36Sopenharmony_ci	struct urb *urb;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	DRM_DEBUG("Waiting for completes and freeing all render urbs\n");
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	/* keep waiting and freeing, until we've got 'em all */
15762306a36Sopenharmony_ci	while (udl->urbs.count) {
15862306a36Sopenharmony_ci		spin_lock_irq(&udl->urbs.lock);
15962306a36Sopenharmony_ci		urb = udl_get_urb_locked(udl, MAX_SCHEDULE_TIMEOUT);
16062306a36Sopenharmony_ci		udl->urbs.count--;
16162306a36Sopenharmony_ci		spin_unlock_irq(&udl->urbs.lock);
16262306a36Sopenharmony_ci		if (WARN_ON(!urb))
16362306a36Sopenharmony_ci			break;
16462306a36Sopenharmony_ci		unode = urb->context;
16562306a36Sopenharmony_ci		/* Free each separately allocated piece */
16662306a36Sopenharmony_ci		usb_free_coherent(urb->dev, udl->urbs.size,
16762306a36Sopenharmony_ci				  urb->transfer_buffer, urb->transfer_dma);
16862306a36Sopenharmony_ci		usb_free_urb(urb);
16962306a36Sopenharmony_ci		kfree(unode);
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	wake_up_all(&udl->urbs.sleep);
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistatic int udl_alloc_urb_list(struct drm_device *dev, int count, size_t size)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	struct udl_device *udl = to_udl(dev);
17862306a36Sopenharmony_ci	struct urb *urb;
17962306a36Sopenharmony_ci	struct urb_node *unode;
18062306a36Sopenharmony_ci	char *buf;
18162306a36Sopenharmony_ci	size_t wanted_size = count * size;
18262306a36Sopenharmony_ci	struct usb_device *udev = udl_to_usb_device(udl);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	spin_lock_init(&udl->urbs.lock);
18562306a36Sopenharmony_ci	INIT_LIST_HEAD(&udl->urbs.list);
18662306a36Sopenharmony_ci	init_waitqueue_head(&udl->urbs.sleep);
18762306a36Sopenharmony_ci	udl->urbs.count = 0;
18862306a36Sopenharmony_ci	udl->urbs.available = 0;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ciretry:
19162306a36Sopenharmony_ci	udl->urbs.size = size;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	while (udl->urbs.count * size < wanted_size) {
19462306a36Sopenharmony_ci		unode = kzalloc(sizeof(struct urb_node), GFP_KERNEL);
19562306a36Sopenharmony_ci		if (!unode)
19662306a36Sopenharmony_ci			break;
19762306a36Sopenharmony_ci		unode->dev = udl;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci		urb = usb_alloc_urb(0, GFP_KERNEL);
20062306a36Sopenharmony_ci		if (!urb) {
20162306a36Sopenharmony_ci			kfree(unode);
20262306a36Sopenharmony_ci			break;
20362306a36Sopenharmony_ci		}
20462306a36Sopenharmony_ci		unode->urb = urb;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci		buf = usb_alloc_coherent(udev, size, GFP_KERNEL,
20762306a36Sopenharmony_ci					 &urb->transfer_dma);
20862306a36Sopenharmony_ci		if (!buf) {
20962306a36Sopenharmony_ci			kfree(unode);
21062306a36Sopenharmony_ci			usb_free_urb(urb);
21162306a36Sopenharmony_ci			if (size > PAGE_SIZE) {
21262306a36Sopenharmony_ci				size /= 2;
21362306a36Sopenharmony_ci				udl_free_urb_list(dev);
21462306a36Sopenharmony_ci				goto retry;
21562306a36Sopenharmony_ci			}
21662306a36Sopenharmony_ci			break;
21762306a36Sopenharmony_ci		}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci		/* urb->transfer_buffer_length set to actual before submit */
22062306a36Sopenharmony_ci		usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, 1),
22162306a36Sopenharmony_ci				  buf, size, udl_urb_completion, unode);
22262306a36Sopenharmony_ci		urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci		list_add_tail(&unode->entry, &udl->urbs.list);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci		udl->urbs.count++;
22762306a36Sopenharmony_ci		udl->urbs.available++;
22862306a36Sopenharmony_ci	}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	DRM_DEBUG("allocated %d %d byte urbs\n", udl->urbs.count, (int) size);
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	return udl->urbs.count;
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_cistatic struct urb *udl_get_urb_locked(struct udl_device *udl, long timeout)
23662306a36Sopenharmony_ci{
23762306a36Sopenharmony_ci	struct urb_node *unode;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	assert_spin_locked(&udl->urbs.lock);
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	/* Wait for an in-flight buffer to complete and get re-queued */
24262306a36Sopenharmony_ci	if (!wait_event_lock_irq_timeout(udl->urbs.sleep,
24362306a36Sopenharmony_ci					 !udl->urbs.count ||
24462306a36Sopenharmony_ci					 !list_empty(&udl->urbs.list),
24562306a36Sopenharmony_ci					 udl->urbs.lock, timeout)) {
24662306a36Sopenharmony_ci		DRM_INFO("wait for urb interrupted: available: %d\n",
24762306a36Sopenharmony_ci			 udl->urbs.available);
24862306a36Sopenharmony_ci		return NULL;
24962306a36Sopenharmony_ci	}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	if (!udl->urbs.count)
25262306a36Sopenharmony_ci		return NULL;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	unode = list_first_entry(&udl->urbs.list, struct urb_node, entry);
25562306a36Sopenharmony_ci	list_del_init(&unode->entry);
25662306a36Sopenharmony_ci	udl->urbs.available--;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	return unode->urb;
25962306a36Sopenharmony_ci}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci#define GET_URB_TIMEOUT	HZ
26262306a36Sopenharmony_cistruct urb *udl_get_urb(struct drm_device *dev)
26362306a36Sopenharmony_ci{
26462306a36Sopenharmony_ci	struct udl_device *udl = to_udl(dev);
26562306a36Sopenharmony_ci	struct urb *urb;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	spin_lock_irq(&udl->urbs.lock);
26862306a36Sopenharmony_ci	urb = udl_get_urb_locked(udl, GET_URB_TIMEOUT);
26962306a36Sopenharmony_ci	spin_unlock_irq(&udl->urbs.lock);
27062306a36Sopenharmony_ci	return urb;
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ciint udl_submit_urb(struct drm_device *dev, struct urb *urb, size_t len)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	struct udl_device *udl = to_udl(dev);
27662306a36Sopenharmony_ci	int ret;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	if (WARN_ON(len > udl->urbs.size)) {
27962306a36Sopenharmony_ci		ret = -EINVAL;
28062306a36Sopenharmony_ci		goto error;
28162306a36Sopenharmony_ci	}
28262306a36Sopenharmony_ci	urb->transfer_buffer_length = len; /* set to actual payload len */
28362306a36Sopenharmony_ci	ret = usb_submit_urb(urb, GFP_ATOMIC);
28462306a36Sopenharmony_ci error:
28562306a36Sopenharmony_ci	if (ret) {
28662306a36Sopenharmony_ci		udl_urb_completion(urb); /* because no one else will */
28762306a36Sopenharmony_ci		DRM_ERROR("usb_submit_urb error %x\n", ret);
28862306a36Sopenharmony_ci	}
28962306a36Sopenharmony_ci	return ret;
29062306a36Sopenharmony_ci}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci/* wait until all pending URBs have been processed */
29362306a36Sopenharmony_civoid udl_sync_pending_urbs(struct drm_device *dev)
29462306a36Sopenharmony_ci{
29562306a36Sopenharmony_ci	struct udl_device *udl = to_udl(dev);
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	spin_lock_irq(&udl->urbs.lock);
29862306a36Sopenharmony_ci	/* 2 seconds as a sane timeout */
29962306a36Sopenharmony_ci	if (!wait_event_lock_irq_timeout(udl->urbs.sleep,
30062306a36Sopenharmony_ci					 udl->urbs.available == udl->urbs.count,
30162306a36Sopenharmony_ci					 udl->urbs.lock,
30262306a36Sopenharmony_ci					 msecs_to_jiffies(2000)))
30362306a36Sopenharmony_ci		drm_err(dev, "Timeout for syncing pending URBs\n");
30462306a36Sopenharmony_ci	spin_unlock_irq(&udl->urbs.lock);
30562306a36Sopenharmony_ci}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ciint udl_init(struct udl_device *udl)
30862306a36Sopenharmony_ci{
30962306a36Sopenharmony_ci	struct drm_device *dev = &udl->drm;
31062306a36Sopenharmony_ci	int ret = -ENOMEM;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	DRM_DEBUG("\n");
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	udl->dmadev = usb_intf_get_dma_device(to_usb_interface(dev->dev));
31562306a36Sopenharmony_ci	if (!udl->dmadev)
31662306a36Sopenharmony_ci		drm_warn(dev, "buffer sharing not supported"); /* not an error */
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	mutex_init(&udl->gem_lock);
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	if (!udl_parse_vendor_descriptor(udl)) {
32162306a36Sopenharmony_ci		ret = -ENODEV;
32262306a36Sopenharmony_ci		DRM_ERROR("firmware not recognized. Assume incompatible device\n");
32362306a36Sopenharmony_ci		goto err;
32462306a36Sopenharmony_ci	}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	if (udl_select_std_channel(udl))
32762306a36Sopenharmony_ci		DRM_ERROR("Selecting channel failed\n");
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	if (!udl_alloc_urb_list(dev, WRITES_IN_FLIGHT, MAX_TRANSFER)) {
33062306a36Sopenharmony_ci		DRM_ERROR("udl_alloc_urb_list failed\n");
33162306a36Sopenharmony_ci		goto err;
33262306a36Sopenharmony_ci	}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	DRM_DEBUG("\n");
33562306a36Sopenharmony_ci	ret = udl_modeset_init(dev);
33662306a36Sopenharmony_ci	if (ret)
33762306a36Sopenharmony_ci		goto err;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	drm_kms_helper_poll_init(dev);
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	return 0;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_cierr:
34462306a36Sopenharmony_ci	if (udl->urbs.count)
34562306a36Sopenharmony_ci		udl_free_urb_list(dev);
34662306a36Sopenharmony_ci	put_device(udl->dmadev);
34762306a36Sopenharmony_ci	DRM_ERROR("%d\n", ret);
34862306a36Sopenharmony_ci	return ret;
34962306a36Sopenharmony_ci}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ciint udl_drop_usb(struct drm_device *dev)
35262306a36Sopenharmony_ci{
35362306a36Sopenharmony_ci	struct udl_device *udl = to_udl(dev);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	udl_free_urb_list(dev);
35662306a36Sopenharmony_ci	put_device(udl->dmadev);
35762306a36Sopenharmony_ci	udl->dmadev = NULL;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	return 0;
36062306a36Sopenharmony_ci}
361