All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 0/3] Generic USB Display driver
@ 2021-01-20 17:00 ` Noralf Trønnes
  0 siblings, 0 replies; 53+ messages in thread
From: Noralf Trønnes @ 2021-01-20 17:00 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-usb, sam, peter, lkundrak, markus, pontus.fuchs, hudson,
	th020394, Noralf Trønnes

Hi,

A while back I had the idea to turn a Raspberry Pi Zero into a $5
USB to HDMI/SDTV/DSI/DPI display adapter.

The reason for calling it 'Generic' is so anyone can make a USB
display/adapter against this driver, all that's needed is to add a USB
vid:pid.

Unfortunately I've had some compounding health problems that have
severally limited the time I can spend in front of a computer. For this
reason I've decided to keep the gadget driver out-of-tree and focus on
getting the host driver merged first.

See the wiki[1] for more information and images for the Raspberry Pi
Zero/4.

One big change this time is that I've followed Peter Stuge's advice to
not let DRM stuff leak into the USB protocol. This has made the protocol
easier to understand just from reading the header file.

Noralf.

[1] https://github.com/notro/gud/wiki


Noralf Trønnes (3):
  drm/uapi: Add USB connector type
  drm/probe-helper: Check epoch counter in output_poll_execute()
  drm: Add Generic USB Display driver

 MAINTAINERS                         |   8 +
 drivers/gpu/drm/Kconfig             |   2 +
 drivers/gpu/drm/Makefile            |   1 +
 drivers/gpu/drm/drm_probe_helper.c  |   7 +-
 drivers/gpu/drm/gud/Kconfig         |  14 +
 drivers/gpu/drm/gud/Makefile        |   4 +
 drivers/gpu/drm/gud/gud_connector.c | 722 ++++++++++++++++++++++++++++
 drivers/gpu/drm/gud/gud_drv.c       | 620 ++++++++++++++++++++++++
 drivers/gpu/drm/gud/gud_internal.h  | 148 ++++++
 drivers/gpu/drm/gud/gud_pipe.c      | 472 ++++++++++++++++++
 include/drm/gud.h                   | 356 ++++++++++++++
 include/uapi/drm/drm_mode.h         |   1 +
 12 files changed, 2354 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/gud/Kconfig
 create mode 100644 drivers/gpu/drm/gud/Makefile
 create mode 100644 drivers/gpu/drm/gud/gud_connector.c
 create mode 100644 drivers/gpu/drm/gud/gud_drv.c
 create mode 100644 drivers/gpu/drm/gud/gud_internal.h
 create mode 100644 drivers/gpu/drm/gud/gud_pipe.c
 create mode 100644 include/drm/gud.h

-- 
2.23.0


^ permalink raw reply	[flat|nested] 53+ messages in thread

* [PATCH v4 0/3] Generic USB Display driver
@ 2021-01-20 17:00 ` Noralf Trønnes
  0 siblings, 0 replies; 53+ messages in thread
From: Noralf Trønnes @ 2021-01-20 17:00 UTC (permalink / raw)
  To: dri-devel
  Cc: hudson, markus, sam, linux-usb, th020394, lkundrak, pontus.fuchs, peter

Hi,

A while back I had the idea to turn a Raspberry Pi Zero into a $5
USB to HDMI/SDTV/DSI/DPI display adapter.

The reason for calling it 'Generic' is so anyone can make a USB
display/adapter against this driver, all that's needed is to add a USB
vid:pid.

Unfortunately I've had some compounding health problems that have
severally limited the time I can spend in front of a computer. For this
reason I've decided to keep the gadget driver out-of-tree and focus on
getting the host driver merged first.

See the wiki[1] for more information and images for the Raspberry Pi
Zero/4.

One big change this time is that I've followed Peter Stuge's advice to
not let DRM stuff leak into the USB protocol. This has made the protocol
easier to understand just from reading the header file.

Noralf.

[1] https://github.com/notro/gud/wiki


Noralf Trønnes (3):
  drm/uapi: Add USB connector type
  drm/probe-helper: Check epoch counter in output_poll_execute()
  drm: Add Generic USB Display driver

 MAINTAINERS                         |   8 +
 drivers/gpu/drm/Kconfig             |   2 +
 drivers/gpu/drm/Makefile            |   1 +
 drivers/gpu/drm/drm_probe_helper.c  |   7 +-
 drivers/gpu/drm/gud/Kconfig         |  14 +
 drivers/gpu/drm/gud/Makefile        |   4 +
 drivers/gpu/drm/gud/gud_connector.c | 722 ++++++++++++++++++++++++++++
 drivers/gpu/drm/gud/gud_drv.c       | 620 ++++++++++++++++++++++++
 drivers/gpu/drm/gud/gud_internal.h  | 148 ++++++
 drivers/gpu/drm/gud/gud_pipe.c      | 472 ++++++++++++++++++
 include/drm/gud.h                   | 356 ++++++++++++++
 include/uapi/drm/drm_mode.h         |   1 +
 12 files changed, 2354 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/gud/Kconfig
 create mode 100644 drivers/gpu/drm/gud/Makefile
 create mode 100644 drivers/gpu/drm/gud/gud_connector.c
 create mode 100644 drivers/gpu/drm/gud/gud_drv.c
 create mode 100644 drivers/gpu/drm/gud/gud_internal.h
 create mode 100644 drivers/gpu/drm/gud/gud_pipe.c
 create mode 100644 include/drm/gud.h

-- 
2.23.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [PATCH v4 1/3] drm/uapi: Add USB connector type
  2021-01-20 17:00 ` Noralf Trønnes
@ 2021-01-20 17:00   ` Noralf Trønnes
  -1 siblings, 0 replies; 53+ messages in thread
From: Noralf Trønnes @ 2021-01-20 17:00 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-usb, sam, peter, lkundrak, markus, pontus.fuchs, hudson,
	th020394, Noralf Trønnes

Add a connector type for USB connected display panels.

Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
---
 include/uapi/drm/drm_mode.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
index fed66a03c7ae..33024cc5d26e 100644
--- a/include/uapi/drm/drm_mode.h
+++ b/include/uapi/drm/drm_mode.h
@@ -367,6 +367,7 @@ enum drm_mode_subconnector {
 #define DRM_MODE_CONNECTOR_DPI		17
 #define DRM_MODE_CONNECTOR_WRITEBACK	18
 #define DRM_MODE_CONNECTOR_SPI		19
+#define DRM_MODE_CONNECTOR_USB		20
 
 /**
  * struct drm_mode_get_connector - Get connector metadata.
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v4 1/3] drm/uapi: Add USB connector type
@ 2021-01-20 17:00   ` Noralf Trønnes
  0 siblings, 0 replies; 53+ messages in thread
From: Noralf Trønnes @ 2021-01-20 17:00 UTC (permalink / raw)
  To: dri-devel
  Cc: hudson, markus, sam, linux-usb, th020394, lkundrak, pontus.fuchs, peter

Add a connector type for USB connected display panels.

Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
---
 include/uapi/drm/drm_mode.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
index fed66a03c7ae..33024cc5d26e 100644
--- a/include/uapi/drm/drm_mode.h
+++ b/include/uapi/drm/drm_mode.h
@@ -367,6 +367,7 @@ enum drm_mode_subconnector {
 #define DRM_MODE_CONNECTOR_DPI		17
 #define DRM_MODE_CONNECTOR_WRITEBACK	18
 #define DRM_MODE_CONNECTOR_SPI		19
+#define DRM_MODE_CONNECTOR_USB		20
 
 /**
  * struct drm_mode_get_connector - Get connector metadata.
-- 
2.23.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v4 2/3] drm/probe-helper: Check epoch counter in output_poll_execute()
  2021-01-20 17:00 ` Noralf Trønnes
@ 2021-01-20 17:00   ` Noralf Trønnes
  -1 siblings, 0 replies; 53+ messages in thread
From: Noralf Trønnes @ 2021-01-20 17:00 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-usb, sam, peter, lkundrak, markus, pontus.fuchs, hudson,
	th020394, Noralf Trønnes

drm_helper_hpd_irq_event() checks the epoch counter to determine
connector status change. This was introduced in
commit 5186421cbfe2 ("drm: Introduce epoch counter to drm_connector").
Do the same for output_poll_execute() so it can detect other changes
beside connection status value changes.

Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
---
 drivers/gpu/drm/drm_probe_helper.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c
index d6017726cc2a..e5432dcf6999 100644
--- a/drivers/gpu/drm/drm_probe_helper.c
+++ b/drivers/gpu/drm/drm_probe_helper.c
@@ -623,6 +623,7 @@ static void output_poll_execute(struct work_struct *work)
 	struct drm_connector_list_iter conn_iter;
 	enum drm_connector_status old_status;
 	bool repoll = false, changed;
+	u64 old_epoch_counter;
 
 	if (!dev->mode_config.poll_enabled)
 		return;
@@ -659,8 +660,9 @@ static void output_poll_execute(struct work_struct *work)
 
 		repoll = true;
 
+		old_epoch_counter = connector->epoch_counter;
 		connector->status = drm_helper_probe_detect(connector, NULL, false);
-		if (old_status != connector->status) {
+		if (old_epoch_counter != connector->epoch_counter) {
 			const char *old, *new;
 
 			/*
@@ -689,6 +691,9 @@ static void output_poll_execute(struct work_struct *work)
 				      connector->base.id,
 				      connector->name,
 				      old, new);
+			DRM_DEBUG_KMS("[CONNECTOR:%d:%s] epoch counter %llu -> %llu\n",
+				      connector->base.id, connector->name,
+				      old_epoch_counter, connector->epoch_counter);
 
 			changed = true;
 		}
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v4 2/3] drm/probe-helper: Check epoch counter in output_poll_execute()
@ 2021-01-20 17:00   ` Noralf Trønnes
  0 siblings, 0 replies; 53+ messages in thread
From: Noralf Trønnes @ 2021-01-20 17:00 UTC (permalink / raw)
  To: dri-devel
  Cc: hudson, markus, sam, linux-usb, th020394, lkundrak, pontus.fuchs, peter

drm_helper_hpd_irq_event() checks the epoch counter to determine
connector status change. This was introduced in
commit 5186421cbfe2 ("drm: Introduce epoch counter to drm_connector").
Do the same for output_poll_execute() so it can detect other changes
beside connection status value changes.

Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
---
 drivers/gpu/drm/drm_probe_helper.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c
index d6017726cc2a..e5432dcf6999 100644
--- a/drivers/gpu/drm/drm_probe_helper.c
+++ b/drivers/gpu/drm/drm_probe_helper.c
@@ -623,6 +623,7 @@ static void output_poll_execute(struct work_struct *work)
 	struct drm_connector_list_iter conn_iter;
 	enum drm_connector_status old_status;
 	bool repoll = false, changed;
+	u64 old_epoch_counter;
 
 	if (!dev->mode_config.poll_enabled)
 		return;
@@ -659,8 +660,9 @@ static void output_poll_execute(struct work_struct *work)
 
 		repoll = true;
 
+		old_epoch_counter = connector->epoch_counter;
 		connector->status = drm_helper_probe_detect(connector, NULL, false);
-		if (old_status != connector->status) {
+		if (old_epoch_counter != connector->epoch_counter) {
 			const char *old, *new;
 
 			/*
@@ -689,6 +691,9 @@ static void output_poll_execute(struct work_struct *work)
 				      connector->base.id,
 				      connector->name,
 				      old, new);
+			DRM_DEBUG_KMS("[CONNECTOR:%d:%s] epoch counter %llu -> %llu\n",
+				      connector->base.id, connector->name,
+				      old_epoch_counter, connector->epoch_counter);
 
 			changed = true;
 		}
-- 
2.23.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v4 3/3] drm: Add Generic USB Display driver
  2021-01-20 17:00 ` Noralf Trønnes
@ 2021-01-20 17:00   ` Noralf Trønnes
  -1 siblings, 0 replies; 53+ messages in thread
From: Noralf Trønnes @ 2021-01-20 17:00 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-usb, sam, peter, lkundrak, markus, pontus.fuchs, hudson,
	th020394, Noralf Trønnes

This adds a generic USB display driver with the intention that it can be
used with future USB interfaced low end displays/adapters. The Linux
gadget device driver will serve as the canonical device implementation.

The following DRM properties are supported:
- Plane rotation
- Connector TV properties

There is also support for backlight brightness exposed as a backlight
device.

Display modes can be made available to the host driver either as DRM
display modes or through EDID. If both are present, EDID is just passed
on to userspace.

Performance is preferred over color depth, so if the device supports
RGB565, DRM_CAP_DUMB_PREFERRED_DEPTH will return 16.

If the device transfer buffer can't fit an uncompressed framebuffer
update, the update is split up into parts that do fit.

Optimal user experience is achieved by providing damage reports either by
setting FB_DAMAGE_CLIPS on pageflips or calling DRM_IOCTL_MODE_DIRTYFB.

LZ4 compression is used if the device supports it.

The driver supports a one bit monochrome transfer format: R1. This is not
implemented in the gadget driver. It is added in preparation for future
monochrome e-ink displays.

The driver is MIT licensed to smooth the path for any BSD port of the
driver.

v2:
- Use devm_drm_dev_alloc() and drmm_mode_config_init()
- drm_fbdev_generic_setup: Use preferred_bpp=0, 16 was a copy paste error
- The drm_backlight_helper is dropped, copy in the code
- Support protocol version backwards compatibility for device

v3:
- Use donated Openmoko USB pid
- Use direct compression from framebuffer when pitch matches, not only on
  full frames, so split updates can benefit
- Use __le16 in struct gud_drm_req_get_connector_status
- Set edid property when the device only provides edid
- Clear compression fields in struct gud_drm_req_set_buffer
- Fix protocol version negotiation
- Remove mode->vrefresh, it's calculated

v4:
- Drop the status req polling which was a workaround for something that
  turned out to be a dwc2 udc driver problem
- Add a flag for the Linux gadget to require a status request on
  SET operations. Other devices will only get status req on STALL errors
- Use protocol specific error codes (Peter)
- Add a flag for devices that want to receive the entire framebuffer on
  each flush (Lubomir)
- Retry a failed framebuffer flush
- If mode has changed wait for worker and clear pending damage before
  queuing up new damage, fb width/height might have changed
- Increase error counter on bulk transfer failures
- Use DRM_MODE_CONNECTOR_USB
- Handle R1 kmalloc error (Peter)
- Don't try and replicate the USB get descriptor request standard for the
  display descriptor (Peter)
- Make max_buffer_size optional (Peter), drop the pow2 requirement since
  it's not necessary anymore.
- Don't pre-alloc a control request buffer, it was only 4k
- Let gud.h describe the whole protocol explicitly and don't let DRM
  leak into it (Peter)
- Drop display mode .hskew and .vscan from the protocol
- Shorten names: s/GUD_DRM_/GUD_/ s/gud_drm_/gud_/ (Peter)
- Fix gud_pipe_check() connector picking when switching connector
- Drop gud_drm_driver_gem_create_object() cached is default now
- Retrieve USB device from struct drm_device.dev instead of keeping a
  pointer
- Honour fb->offsets[0]
- Fix mode fetching when connector status is forced
- Check EDID length reported by the device
- Use drm_do_get_edid() so userspace can overrride EDID
- Set epoch counter to signal connector status change
- gud_drm_driver can be const now

Cc: Peter Stuge <peter@stuge.se>
Cc: Lubomir Rintel <lkundrak@v3.sk>
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
---
 MAINTAINERS                         |   8 +
 drivers/gpu/drm/Kconfig             |   2 +
 drivers/gpu/drm/Makefile            |   1 +
 drivers/gpu/drm/gud/Kconfig         |  14 +
 drivers/gpu/drm/gud/Makefile        |   4 +
 drivers/gpu/drm/gud/gud_connector.c | 722 ++++++++++++++++++++++++++++
 drivers/gpu/drm/gud/gud_drv.c       | 620 ++++++++++++++++++++++++
 drivers/gpu/drm/gud/gud_internal.h  | 148 ++++++
 drivers/gpu/drm/gud/gud_pipe.c      | 472 ++++++++++++++++++
 include/drm/gud.h                   | 356 ++++++++++++++
 10 files changed, 2347 insertions(+)
 create mode 100644 drivers/gpu/drm/gud/Kconfig
 create mode 100644 drivers/gpu/drm/gud/Makefile
 create mode 100644 drivers/gpu/drm/gud/gud_connector.c
 create mode 100644 drivers/gpu/drm/gud/gud_drv.c
 create mode 100644 drivers/gpu/drm/gud/gud_internal.h
 create mode 100644 drivers/gpu/drm/gud/gud_pipe.c
 create mode 100644 include/drm/gud.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 00112c044608..e7f71ac55f08 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5525,6 +5525,14 @@ S:	Maintained
 F:	Documentation/devicetree/bindings/display/panel/feiyang,fy07024di26a30d.yaml
 F:	drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c
 
+DRM DRIVER FOR GENERIC USB DISPLAY
+M:	Noralf Trønnes <noralf@tronnes.org>
+S:	Maintained
+W:	https://github.com/notro/gud/wiki
+T:	git git://anongit.freedesktop.org/drm/drm-misc
+F:	drivers/gpu/drm/gud/
+F:	include/drm/gud.h
+
 DRM DRIVER FOR GRAIN MEDIA GM12U320 PROJECTORS
 M:	Hans de Goede <hdegoede@redhat.com>
 S:	Maintained
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 147d61b9674e..6d3f47933e51 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -388,6 +388,8 @@ source "drivers/gpu/drm/tidss/Kconfig"
 
 source "drivers/gpu/drm/xlnx/Kconfig"
 
+source "drivers/gpu/drm/gud/Kconfig"
+
 # Keep legacy drivers last
 
 menuconfig DRM_LEGACY
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 81569009f884..78dd8e12525d 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -124,3 +124,4 @@ obj-$(CONFIG_DRM_ASPEED_GFX) += aspeed/
 obj-$(CONFIG_DRM_MCDE) += mcde/
 obj-$(CONFIG_DRM_TIDSS) += tidss/
 obj-y			+= xlnx/
+obj-y			+= gud/
diff --git a/drivers/gpu/drm/gud/Kconfig b/drivers/gpu/drm/gud/Kconfig
new file mode 100644
index 000000000000..d832cd63687c
--- /dev/null
+++ b/drivers/gpu/drm/gud/Kconfig
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config DRM_GUD
+	tristate "Generic USB Display"
+	depends on DRM && USB
+	select LZ4_COMPRESS
+	select DRM_KMS_HELPER
+	select DRM_GEM_SHMEM_HELPER
+	select BACKLIGHT_CLASS_DEVICE
+	help
+	  This is a DRM display driver for Generic USB Displays or display
+	  adapters.
+
+	  If M is selected the module will be called gud.
diff --git a/drivers/gpu/drm/gud/Makefile b/drivers/gpu/drm/gud/Makefile
new file mode 100644
index 000000000000..d2bb53a65de6
--- /dev/null
+++ b/drivers/gpu/drm/gud/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+
+gud-objs			:= gud_drv.o gud_pipe.o gud_connector.o
+obj-$(CONFIG_DRM_GUD)		+= gud.o
diff --git a/drivers/gpu/drm/gud/gud_connector.c b/drivers/gpu/drm/gud/gud_connector.c
new file mode 100644
index 000000000000..a4b9bbf48e19
--- /dev/null
+++ b/drivers/gpu/drm/gud/gud_connector.c
@@ -0,0 +1,722 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2020 Noralf Trønnes
+ */
+
+#include <linux/backlight.h>
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_state_helper.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_encoder.h>
+#include <drm/drm_file.h>
+#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_simple_kms_helper.h>
+#include <drm/gud.h>
+
+#include "gud_internal.h"
+
+struct gud_connector {
+	struct drm_connector connector;
+	struct drm_encoder encoder;
+	struct backlight_device *backlight;
+
+	/* Supported properties */
+	u16 *properties;
+	unsigned int num_properties;
+
+	/* Initial gadget tv state if applicable, applied on state reset */
+	struct drm_tv_connector_state initial_tv_state;
+
+	/*
+	 * Initial gadget backlight brightness if applicable, applied on state reset.
+	 * The value -ENODEV is used to signal no backlight.
+	 */
+	int initial_brightness;
+
+	unsigned int num_modes;
+	size_t edid_len;
+};
+
+static inline struct gud_connector *to_gud_connector(struct drm_connector *connector)
+{
+	return container_of(connector, struct gud_connector, connector);
+}
+
+static int gud_connector_backlight_update_status(struct backlight_device *bd)
+{
+	struct drm_connector *connector = bl_get_data(bd);
+	struct drm_connector_state *connector_state;
+	struct drm_device *dev = connector->dev;
+	struct drm_modeset_acquire_ctx ctx;
+	struct drm_atomic_state *state;
+	int ret;
+
+	state = drm_atomic_state_alloc(dev);
+	if (!state)
+		return -ENOMEM;
+
+	drm_modeset_acquire_init(&ctx, 0);
+	state->acquire_ctx = &ctx;
+retry:
+	connector_state = drm_atomic_get_connector_state(state, connector);
+	if (IS_ERR(connector_state)) {
+		ret = PTR_ERR(connector_state);
+		goto out;
+	}
+
+	/* Reuse tv.brightness to avoid having to subclass */
+	connector_state->tv.brightness = bd->props.brightness;
+
+	ret = drm_atomic_commit(state);
+out:
+	if (ret == -EDEADLK) {
+		drm_atomic_state_clear(state);
+		drm_modeset_backoff(&ctx);
+		goto retry;
+	}
+
+	drm_atomic_state_put(state);
+
+	drm_modeset_drop_locks(&ctx);
+	drm_modeset_acquire_fini(&ctx);
+
+	return ret;
+}
+
+static int gud_connector_backlight_get_brightness(struct backlight_device *bd)
+{
+	struct drm_connector *connector = bl_get_data(bd);
+	struct drm_device *dev = connector->dev;
+	int brightness;
+
+	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
+	brightness = connector->state->tv.brightness;
+	drm_modeset_unlock(&dev->mode_config.connection_mutex);
+
+	return brightness;
+}
+
+static const struct backlight_ops gud_connector_backlight_ops = {
+	.get_brightness = gud_connector_backlight_get_brightness,
+	.update_status	= gud_connector_backlight_update_status,
+};
+
+static int gud_connector_backlight_register(struct gud_connector *gconn)
+{
+	struct drm_connector *connector = &gconn->connector;
+	struct backlight_device *bd;
+	const char *name;
+	const struct backlight_properties props = {
+		.type = BACKLIGHT_RAW,
+		.scale = BACKLIGHT_SCALE_NON_LINEAR,
+		.max_brightness = 100,
+	};
+
+	name = kasprintf(GFP_KERNEL, "card%d-%s-backlight",
+			 connector->dev->primary->index, connector->name);
+	if (!name)
+		return -ENOMEM;
+
+	bd = backlight_device_register(name, connector->kdev, connector,
+				       &gud_connector_backlight_ops, &props);
+	kfree(name);
+	if (IS_ERR(bd))
+		return PTR_ERR(bd);
+
+	gconn->backlight = bd;
+
+	return 0;
+}
+
+static int gud_connector_status_request(struct drm_connector *connector)
+{
+	struct gud_connector *gconn = to_gud_connector(connector);
+	struct gud_device *gdrm = to_gud_device(connector->dev);
+	struct gud_connector_status_req req;
+	u16 num_modes, edid_len;
+	int ret;
+
+	ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_STATUS,
+			  connector->index, &req, sizeof(req));
+	if (ret)
+		return ret;
+
+	switch (req.status & GUD_CONNECTOR_STATUS_CONNECTED_MASK) {
+	case GUD_CONNECTOR_STATUS_DISCONNECTED:
+		ret = connector_status_disconnected;
+		break;
+	case GUD_CONNECTOR_STATUS_CONNECTED:
+		ret = connector_status_connected;
+		break;
+	default:
+		ret = connector_status_unknown;
+		break;
+	};
+
+	num_modes = le16_to_cpu(req.num_modes);
+	edid_len = le16_to_cpu(req.edid_len);
+
+	if (edid_len % EDID_LENGTH) {
+		drm_err(connector->dev, "%s: Invalid EDID size: %u\n", connector->name, edid_len);
+		edid_len = 0;
+	}
+
+	if (req.status & GUD_CONNECTOR_STATUS_CHANGED ||
+	    gconn->num_modes != num_modes || gconn->edid_len != edid_len)
+		connector->epoch_counter += 1;
+
+	gconn->num_modes = num_modes;
+	gconn->edid_len = edid_len;
+
+	if (!num_modes && !edid_len && ret != connector_status_disconnected)
+		drm_dbg_kms(connector->dev, "%s: No modes or EDID.\n", connector->name);
+
+	return ret;
+}
+
+static int gud_connector_detect(struct drm_connector *connector,
+				struct drm_modeset_acquire_ctx *ctx, bool force)
+{
+	struct gud_device *gdrm = to_gud_device(connector->dev);
+	int idx, ret;
+
+	if (!drm_dev_enter(connector->dev, &idx))
+		return connector_status_disconnected;
+
+	if (force) {
+		ret = gud_usb_set(gdrm, GUD_REQ_SET_CONNECTOR_FORCE_DETECT,
+				  connector->index, NULL, 0);
+		if (ret) {
+			ret = connector_status_unknown;
+			goto exit;
+		}
+	}
+
+	ret = gud_connector_status_request(connector);
+	if (ret < 0)
+		ret = connector_status_unknown;
+exit:
+	drm_dev_exit(idx);
+
+	return ret;
+}
+
+struct gud_connector_get_edid_ctx {
+	struct gud_connector *gconn;
+	void *buf;
+};
+
+static int gud_connector_get_edid_block(void *data, u8 *buf, unsigned int block, size_t len)
+{
+	struct gud_connector_get_edid_ctx *ctx = data;
+	struct gud_connector *gconn = ctx->gconn;
+	size_t start = block * EDID_LENGTH;
+
+	if (start + len > gconn->edid_len)
+		return -1;
+
+	if (!block) {
+		struct gud_device *gdrm = to_gud_device(gconn->connector.dev);
+		int ret;
+
+		/* Check because drm_do_get_edid() will retry on failure */
+		if (!ctx->buf)
+			ctx->buf = kmalloc(gconn->edid_len, GFP_KERNEL);
+		if (!ctx->buf)
+			return -1;
+
+		ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_EDID, gconn->connector.index,
+				  ctx->buf, gconn->edid_len);
+		if (ret)
+			return -1;
+	}
+
+	memcpy(buf, ctx->buf + start, len);
+
+	return 0;
+}
+
+static int gud_connector_get_modes(struct drm_connector *connector)
+{
+	struct gud_connector *gconn = to_gud_connector(connector);
+	struct gud_device *gdrm = to_gud_device(connector->dev);
+	struct gud_connector_get_edid_ctx edid_ctx = {
+		.gconn = gconn,
+	};
+	struct gud_display_mode_req *reqmodes = NULL;
+	unsigned int i, num_modes = 0;
+	struct edid *edid = NULL;
+	bool edid_override;
+	int idx, ret;
+
+	if (!drm_dev_enter(connector->dev, &idx))
+		return 0;
+
+	if (connector->force) {
+		ret = gud_connector_status_request(connector);
+		if (ret < 0)
+			goto out;
+	}
+
+	edid = drm_do_get_edid(connector, gud_connector_get_edid_block, &edid_ctx);
+	edid_override = edid && !edid_ctx.buf;
+	kfree(edid_ctx.buf);
+	drm_connector_update_edid_property(connector, edid);
+
+	if (!gconn->num_modes || edid_override) {
+		num_modes = drm_add_edid_modes(connector, edid);
+		goto out;
+	}
+
+	reqmodes = kmalloc_array(gconn->num_modes, sizeof(*reqmodes), GFP_KERNEL);
+	if (!reqmodes)
+		goto out;
+
+	ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_MODES, connector->index,
+			  reqmodes, gconn->num_modes * sizeof(*reqmodes));
+	if (ret)
+		goto out;
+
+	for (i = 0; i < gconn->num_modes; i++) {
+		struct drm_display_mode *mode;
+
+		mode = drm_mode_create(connector->dev);
+		if (!mode)
+			goto out;
+
+		gud_to_display_mode(mode, &reqmodes[i]);
+		drm_mode_probed_add(connector, mode);
+		num_modes++;
+	}
+out:
+	kfree(reqmodes);
+	kfree(edid);
+	drm_dev_exit(idx);
+
+	return num_modes;
+}
+
+static int gud_connector_atomic_check(struct drm_connector *connector,
+				      struct drm_atomic_state *state)
+{
+	struct drm_connector_state *new_state;
+	struct drm_crtc_state *new_crtc_state;
+	struct drm_connector_state *old_state;
+
+	new_state = drm_atomic_get_new_connector_state(state, connector);
+	if (!new_state->crtc)
+		return 0;
+
+	old_state = drm_atomic_get_old_connector_state(state, connector);
+	new_crtc_state = drm_atomic_get_new_crtc_state(state, new_state->crtc);
+
+	if (old_state->tv.margins.left != new_state->tv.margins.left ||
+	    old_state->tv.margins.right != new_state->tv.margins.right ||
+	    old_state->tv.margins.top != new_state->tv.margins.top ||
+	    old_state->tv.margins.bottom != new_state->tv.margins.bottom ||
+	    old_state->tv.mode != new_state->tv.mode ||
+	    old_state->tv.brightness != new_state->tv.brightness ||
+	    old_state->tv.contrast != new_state->tv.contrast ||
+	    old_state->tv.flicker_reduction != new_state->tv.flicker_reduction ||
+	    old_state->tv.overscan != new_state->tv.overscan ||
+	    old_state->tv.saturation != new_state->tv.saturation ||
+	    old_state->tv.hue != new_state->tv.hue)
+		new_crtc_state->connectors_changed = true;
+
+	return 0;
+}
+
+static const struct drm_connector_helper_funcs gud_connector_helper_funcs = {
+	.detect_ctx = gud_connector_detect,
+	.get_modes = gud_connector_get_modes,
+	.atomic_check = gud_connector_atomic_check,
+};
+
+static int gud_connector_late_register(struct drm_connector *connector)
+{
+	struct gud_connector *gconn = to_gud_connector(connector);
+
+	if (gconn->initial_brightness < 0)
+		return 0;
+
+	return gud_connector_backlight_register(gconn);
+}
+
+static void gud_connector_early_unregister(struct drm_connector *connector)
+{
+	struct gud_connector *gconn = to_gud_connector(connector);
+
+	backlight_device_unregister(gconn->backlight);
+}
+
+static void gud_connector_destroy(struct drm_connector *connector)
+{
+	struct gud_connector *gconn = to_gud_connector(connector);
+
+	drm_connector_cleanup(connector);
+	kfree(gconn->properties);
+	kfree(gconn);
+}
+
+static void gud_connector_reset(struct drm_connector *connector)
+{
+	struct gud_connector *gconn = to_gud_connector(connector);
+
+	drm_atomic_helper_connector_reset(connector);
+	connector->state->tv = gconn->initial_tv_state;
+	/* Set margins from command line */
+	drm_atomic_helper_connector_tv_reset(connector);
+	if (gconn->initial_brightness >= 0)
+		connector->state->tv.brightness = gconn->initial_brightness;
+}
+
+static const struct drm_connector_funcs gud_connector_funcs = {
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.late_register = gud_connector_late_register,
+	.early_unregister = gud_connector_early_unregister,
+	.destroy = gud_connector_destroy,
+	.reset = gud_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+/*
+ * The tv.mode property is shared among the connectors and its enum names are
+ * driver specific. This means that if more than one connector uses tv.mode,
+ * the enum names has to be the same.
+ */
+static int gud_connector_add_tv_mode(struct gud_device *gdrm,
+				     struct drm_connector *connector, u64 val)
+{
+	unsigned int i, num_modes;
+	const char **modes;
+	size_t buf_len;
+	char *buf;
+	int ret;
+
+	num_modes = val >> GUD_CONNECTOR_TV_MODE_NUM_SHIFT;
+
+	if (!num_modes)
+		return -EINVAL;
+
+	buf_len = num_modes * GUD_CONNECTOR_TV_MODE_NAME_LEN;
+	modes = kmalloc_array(num_modes, sizeof(*modes), GFP_KERNEL);
+	buf = kmalloc(buf_len, GFP_KERNEL);
+	if (!modes || !buf) {
+		ret = -ENOMEM;
+		goto free;
+	}
+
+	ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_TV_MODE_VALUES,
+			  connector->index, buf, buf_len);
+	if (ret)
+		goto free;
+
+	for (i = 0; i < num_modes; i++)
+		modes[i] = &buf[i * GUD_CONNECTOR_TV_MODE_NAME_LEN];
+
+	ret = drm_mode_create_tv_properties(connector->dev, num_modes, modes);
+free:
+	kfree(modes);
+	kfree(buf);
+
+	return ret;
+}
+
+static struct drm_property *
+gud_connector_property_lookup(struct drm_connector *connector, u16 prop)
+{
+	struct drm_mode_config *config = &connector->dev->mode_config;
+
+	switch (prop) {
+	case GUD_PROPERTY_TV_LEFT_MARGIN:
+		return config->tv_left_margin_property;
+	case GUD_PROPERTY_TV_RIGHT_MARGIN:
+		return config->tv_right_margin_property;
+	case GUD_PROPERTY_TV_TOP_MARGIN:
+		return config->tv_top_margin_property;
+	case GUD_PROPERTY_TV_BOTTOM_MARGIN:
+		return config->tv_bottom_margin_property;
+	case GUD_PROPERTY_TV_MODE:
+		return config->tv_mode_property;
+	case GUD_PROPERTY_TV_BRIGHTNESS:
+		return config->tv_brightness_property;
+	case GUD_PROPERTY_TV_CONTRAST:
+		return config->tv_contrast_property;
+	case GUD_PROPERTY_TV_FLICKER_REDUCTION:
+		return config->tv_flicker_reduction_property;
+	case GUD_PROPERTY_TV_OVERSCAN:
+		return config->tv_overscan_property;
+	case GUD_PROPERTY_TV_SATURATION:
+		return config->tv_saturation_property;
+	case GUD_PROPERTY_TV_HUE:
+		return config->tv_hue_property;
+	default:
+		return ERR_PTR(-EINVAL);
+	}
+}
+
+static unsigned int *gud_connector_tv_state_val(u16 prop, struct drm_tv_connector_state *state)
+{
+	switch (prop) {
+	case GUD_PROPERTY_TV_LEFT_MARGIN:
+		return &state->margins.left;
+	case GUD_PROPERTY_TV_RIGHT_MARGIN:
+		return &state->margins.right;
+	case GUD_PROPERTY_TV_TOP_MARGIN:
+		return &state->margins.top;
+	case GUD_PROPERTY_TV_BOTTOM_MARGIN:
+		return &state->margins.bottom;
+	case GUD_PROPERTY_TV_MODE:
+		return &state->mode;
+	case GUD_PROPERTY_TV_BRIGHTNESS:
+		return &state->brightness;
+	case GUD_PROPERTY_TV_CONTRAST:
+		return &state->contrast;
+	case GUD_PROPERTY_TV_FLICKER_REDUCTION:
+		return &state->flicker_reduction;
+	case GUD_PROPERTY_TV_OVERSCAN:
+		return &state->overscan;
+	case GUD_PROPERTY_TV_SATURATION:
+		return &state->saturation;
+	case GUD_PROPERTY_TV_HUE:
+		return &state->hue;
+	default:
+		return ERR_PTR(-EINVAL);
+	}
+}
+
+static int gud_connector_add_properties(struct gud_device *gdrm, struct gud_connector *gconn,
+					unsigned int num_properties)
+{
+	struct drm_device *drm = &gdrm->drm;
+	struct drm_connector *connector = &gconn->connector;
+	struct gud_property_req *properties;
+	unsigned int i;
+	int ret;
+
+	gconn->properties = kcalloc(num_properties, sizeof(*gconn->properties), GFP_KERNEL);
+	if (!gconn->properties)
+		return -ENOMEM;
+
+	properties = kcalloc(num_properties, sizeof(*properties), GFP_KERNEL);
+	if (!properties)
+		return -ENOMEM;
+
+	ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_PROPERTIES, connector->index,
+			  properties, num_properties * sizeof(*properties));
+	if (ret)
+		goto out;
+
+	for (i = 0; i < num_properties; i++) {
+		u16 prop = le16_to_cpu(properties[i].prop);
+		u64 val = le64_to_cpu(properties[i].val);
+		struct drm_property *property;
+		unsigned int *state_val;
+
+		drm_dbg(drm, "property: %u = %llu(0x%llx)\n", prop, val, val);
+
+		switch (prop) {
+		case GUD_PROPERTY_TV_LEFT_MARGIN:
+			fallthrough;
+		case GUD_PROPERTY_TV_RIGHT_MARGIN:
+			fallthrough;
+		case GUD_PROPERTY_TV_TOP_MARGIN:
+			fallthrough;
+		case GUD_PROPERTY_TV_BOTTOM_MARGIN:
+			ret = drm_mode_create_tv_margin_properties(drm);
+			if (ret)
+				goto out;
+			break;
+		case GUD_PROPERTY_TV_MODE:
+			ret = gud_connector_add_tv_mode(gdrm, connector, val);
+			if (ret)
+				goto out;
+			val = val & (BIT(GUD_CONNECTOR_TV_MODE_NUM_SHIFT) - 1);
+			break;
+		case GUD_PROPERTY_TV_BRIGHTNESS:
+			fallthrough;
+		case GUD_PROPERTY_TV_CONTRAST:
+			fallthrough;
+		case GUD_PROPERTY_TV_FLICKER_REDUCTION:
+			fallthrough;
+		case GUD_PROPERTY_TV_OVERSCAN:
+			fallthrough;
+		case GUD_PROPERTY_TV_SATURATION:
+			fallthrough;
+		case GUD_PROPERTY_TV_HUE:
+			/* This is a no-op if already added. */
+			ret = drm_mode_create_tv_properties(drm, 0, NULL);
+			if (ret)
+				goto out;
+			break;
+		case GUD_PROPERTY_BACKLIGHT_BRIGHTNESS:
+			if (val > 100) {
+				ret = -EINVAL;
+				goto out;
+			}
+			gconn->initial_brightness = val;
+			break;
+		default:
+			/* New ones might show up in future devices, skip those we don't know. */
+			drm_dbg(drm, "Unknown property: %u\n", prop);
+			continue;
+		}
+
+		gconn->properties[gconn->num_properties++] = prop;
+
+		if (prop == GUD_PROPERTY_BACKLIGHT_BRIGHTNESS)
+			continue; /* not a DRM property */
+
+		property = gud_connector_property_lookup(connector, prop);
+		if (WARN_ON(IS_ERR(property)))
+			continue;
+
+		state_val = gud_connector_tv_state_val(prop, &gconn->initial_tv_state);
+		if (WARN_ON(IS_ERR(state_val)))
+			continue;
+
+		*state_val = val;
+		drm_object_attach_property(&connector->base, property, 0);
+	}
+out:
+	kfree(properties);
+
+	return ret;
+}
+
+int gud_connector_fill_properties(struct drm_connector *connector,
+				  struct drm_connector_state *connector_state,
+				  struct gud_property_req *properties)
+{
+	struct gud_connector *gconn;
+	unsigned int i;
+
+	gconn = to_gud_connector(connector);
+
+	/* Only interested in the count? */
+	if (!connector_state)
+		return gconn->num_properties;
+
+	for (i = 0; i < gconn->num_properties; i++) {
+		u16 prop = gconn->properties[i];
+		u64 val;
+
+		if (prop == GUD_PROPERTY_BACKLIGHT_BRIGHTNESS) {
+			val = connector_state->tv.brightness;
+		} else {
+			unsigned int *state_val;
+
+			state_val = gud_connector_tv_state_val(prop, &connector_state->tv);
+			if (WARN_ON_ONCE(IS_ERR(state_val)))
+				return PTR_ERR(state_val);
+
+			val = *state_val;
+		}
+
+		properties[i].prop = cpu_to_le16(prop);
+		properties[i].val = cpu_to_le64(val);
+	}
+
+	return gconn->num_properties;
+}
+
+int gud_connector_create(struct gud_device *gdrm, unsigned int index)
+{
+	struct gud_connector_descriptor_req desc;
+	struct drm_device *drm = &gdrm->drm;
+	struct gud_connector *gconn;
+	struct drm_connector *connector;
+	struct drm_encoder *encoder;
+	int ret, connector_type;
+	u32 flags;
+
+	ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR, index, &desc, sizeof(desc));
+	if (ret)
+		return ret;
+
+	gconn = kzalloc(sizeof(*gconn), GFP_KERNEL);
+	if (!gconn)
+		return -ENOMEM;
+
+	gconn->initial_brightness = -ENODEV;
+	flags = le32_to_cpu(desc.flags);
+	connector = &gconn->connector;
+
+	drm_dbg(drm, "Connector: index=%u type=%u flags=0x%x num_properties=%u\n",
+		index, desc.connector_type, flags, desc.num_properties);
+
+	switch (desc.connector_type) {
+	case GUD_CONNECTOR_TYPE_PANEL:
+		connector_type = DRM_MODE_CONNECTOR_USB;
+		break;
+	case GUD_CONNECTOR_TYPE_VGA:
+		connector_type = DRM_MODE_CONNECTOR_VGA;
+		break;
+	case GUD_CONNECTOR_TYPE_DVI:
+		connector_type = DRM_MODE_CONNECTOR_DVID;
+		break;
+	case GUD_CONNECTOR_TYPE_COMPOSITE:
+		connector_type = DRM_MODE_CONNECTOR_Composite;
+		break;
+	case GUD_CONNECTOR_TYPE_SVIDEO:
+		connector_type = DRM_MODE_CONNECTOR_SVIDEO;
+		break;
+	case GUD_CONNECTOR_TYPE_COMPONENT:
+		connector_type = DRM_MODE_CONNECTOR_Component;
+		break;
+	case GUD_CONNECTOR_TYPE_DISPLAYPORT:
+		connector_type = DRM_MODE_CONNECTOR_DisplayPort;
+		break;
+	case GUD_CONNECTOR_TYPE_HDMI:
+		connector_type = DRM_MODE_CONNECTOR_HDMIA;
+		break;
+	default: /* future types */
+		connector_type = DRM_MODE_CONNECTOR_USB;
+		break;
+	};
+
+	drm_connector_helper_add(connector, &gud_connector_helper_funcs);
+	ret = drm_connector_init(drm, connector, &gud_connector_funcs, connector_type);
+	if (ret) {
+		kfree(connector);
+		return ret;
+	}
+
+	if (WARN_ON(connector->index != index))
+		return -EINVAL;
+
+	if (flags & GUD_CONNECTOR_FLAGS_POLL_STATUS)
+		connector->polled = (DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT);
+	if (flags & GUD_CONNECTOR_FLAGS_INTERLACE)
+		connector->interlace_allowed = true;
+	if (flags & GUD_CONNECTOR_FLAGS_DOUBLESCAN)
+		connector->doublescan_allowed = true;
+
+	if (desc.num_properties) {
+		ret = gud_connector_add_properties(gdrm, gconn, desc.num_properties);
+		if (ret) {
+			dev_err(drm->dev, "Failed to add connector/%u properties\n", index);
+			return ret;
+		}
+	}
+
+	/* The first connector is attached to the existing simple pipe encoder */
+	if (!connector->index) {
+		encoder = &gdrm->pipe.encoder;
+	} else {
+		encoder = &gconn->encoder;
+
+		ret = drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_NONE);
+		if (ret)
+			return ret;
+
+		encoder->possible_crtcs = 1;
+	}
+
+	return drm_connector_attach_encoder(connector, encoder);
+}
diff --git a/drivers/gpu/drm/gud/gud_drv.c b/drivers/gpu/drm/gud/gud_drv.c
new file mode 100644
index 000000000000..6c5e9eeb8cdc
--- /dev/null
+++ b/drivers/gpu/drm/gud/gud_drv.c
@@ -0,0 +1,620 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2020 Noralf Trønnes
+ */
+
+#include <linux/dma-buf.h>
+#include <linux/lz4.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/string_helpers.h>
+#include <linux/usb.h>
+#include <linux/vmalloc.h>
+#include <linux/workqueue.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_damage_helper.h>
+#include <drm/drm_debugfs.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_gem_shmem_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_simple_kms_helper.h>
+#include <drm/gud.h>
+
+#include "gud_internal.h"
+
+/* Only used internally */
+static const struct drm_format_info gud_drm_format_r1 = {
+	.format = GUD_DRM_FORMAT_R1,
+	.num_planes = 1,
+	.char_per_block = { 1, 0, 0 },
+	.block_w = { 8, 0, 0 },
+	.block_h = { 1, 0, 0 },
+	.hsub = 1,
+	.vsub = 1,
+};
+
+static int gud_usb_control_msg(struct usb_device *usb, u8 ifnum, bool in,
+			       u8 request, u16 value, void *buf, size_t len)
+{
+	u8 requesttype = USB_TYPE_VENDOR | USB_RECIP_INTERFACE;
+	unsigned int pipe;
+	int ret;
+
+	if (in) {
+		pipe = usb_rcvctrlpipe(usb, 0);
+		requesttype |= USB_DIR_IN;
+	} else {
+		pipe = usb_sndctrlpipe(usb, 0);
+		requesttype |= USB_DIR_OUT;
+	}
+
+	ret = usb_control_msg(usb, pipe, request, requesttype, value,
+			      ifnum, buf, len, USB_CTRL_GET_TIMEOUT);
+	if (ret < 0)
+		return ret;
+	if (ret != len)
+		return -EIO;
+
+	return 0;
+}
+
+static int gud_get_display_descriptor(struct usb_interface *interface,
+				      struct gud_display_descriptor_req *desc)
+{
+	u8 ifnum = interface->cur_altsetting->desc.bInterfaceNumber;
+	struct usb_device *usb = interface_to_usbdev(interface);
+	void *buf;
+	int ret;
+
+	buf = kmalloc(sizeof(*desc), GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	ret = gud_usb_control_msg(usb, ifnum, true, GUD_REQ_GET_DESCRIPTOR, 0, buf, sizeof(*desc));
+	memcpy(desc, buf, sizeof(*desc));
+	kfree(buf);
+	if (ret)
+		return ret;
+
+	if (desc->magic != GUD_DISPLAY_MAGIC)
+		return -ENODATA;
+
+	DRM_DEV_DEBUG_DRIVER(&interface->dev,
+			     "version=%u flags=0x%x compression=0x%x num_formats=%u num_connectors=%u max_buffer_size=%u\n",
+			     desc->version, le32_to_cpu(desc->flags), desc->compression,
+			     desc->num_formats, desc->num_connectors,
+			     le32_to_cpu(desc->max_buffer_size));
+
+	if (!desc->version || !desc->num_formats || !desc->num_connectors ||
+	    !desc->max_width || !desc->max_height ||
+	    le32_to_cpu(desc->min_width) > le32_to_cpu(desc->max_width) ||
+	    le32_to_cpu(desc->min_height) > le32_to_cpu(desc->max_height))
+		return -EINVAL;
+
+	return 0;
+}
+
+static int gud_usb_get_status(struct usb_device *usb, u8 ifnum, u8 *status)
+{
+	u8 *buf;
+	int ret;
+
+	buf = kmalloc(sizeof(*buf), GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	ret = gud_usb_control_msg(usb, ifnum, true, GUD_REQ_GET_STATUS, 0, buf, sizeof(*buf));
+	*status = *buf;
+	kfree(buf);
+
+	return ret;
+}
+
+static int gud_status_to_errno(u8 status)
+{
+	switch (status) {
+	case GUD_STATUS_OK:
+		return 0;
+	case GUD_STATUS_BUSY:
+		return -EBUSY;
+	case GUD_STATUS_REQUEST_NOT_SUPPORTED:
+		return -EOPNOTSUPP;
+	case GUD_STATUS_PROTOCOL_ERROR:
+		return -EPROTO;
+	case GUD_STATUS_INVALID_PARAMETER:
+		return -EINVAL;
+	case GUD_STATUS_ERROR:
+		return -EREMOTEIO;
+	default:
+		return -EREMOTEIO;
+	}
+}
+
+static int gud_usb_transfer(struct gud_device *gdrm, bool in, u8 request, u16 index,
+			    void *buf, size_t len)
+{
+	struct usb_device *usb = gud_to_usb_device(gdrm);
+	void *trbuf = NULL;
+	int idx, ret;
+
+	drm_dbg(&gdrm->drm, "%s: request=0x%x index=%u len=%zu\n",
+		in ? "get" : "set", request, index, len);
+
+	if (!drm_dev_enter(&gdrm->drm, &idx))
+		return -ENODEV;
+
+	mutex_lock(&gdrm->ctrl_lock);
+
+	if (buf) {
+		if (in)
+			trbuf = kmalloc(len, GFP_KERNEL);
+		else
+			trbuf = kmemdup(buf, len, GFP_KERNEL);
+		if (!trbuf) {
+			ret = -ENOMEM;
+			goto unlock;
+		}
+	}
+
+	ret = gud_usb_control_msg(usb, gdrm->ifnum, in, request, index, trbuf, len);
+	if (ret == -EPIPE || (!ret && !in && (gdrm->flags & GUD_DISPLAY_FLAG_STATUS_ON_SET))) {
+		bool error = ret;
+		u8 status;
+
+		ret = gud_usb_get_status(usb, gdrm->ifnum, &status);
+		if (!ret) {
+			if (error && status == GUD_STATUS_OK) {
+				dev_err_once(gdrm->drm.dev,
+					     "Unexpected status OK for failed transfer\n");
+				ret = -EPIPE;
+			} else {
+				ret = gud_status_to_errno(status);
+			}
+		}
+	}
+
+	if (!ret && in && buf)
+		memcpy(buf, trbuf, len);
+
+	if (ret) {
+		drm_dbg(&gdrm->drm, "ret=%d\n", ret);
+		gdrm->stats_num_errors++;
+	}
+
+	kfree(trbuf);
+unlock:
+	mutex_unlock(&gdrm->ctrl_lock);
+	drm_dev_exit(idx);
+
+	return ret;
+}
+
+int gud_usb_get(struct gud_device *gdrm, u8 request, u16 index, void *buf, size_t len)
+{
+	return gud_usb_transfer(gdrm, true, request, index, buf, len);
+}
+
+int gud_usb_set(struct gud_device *gdrm, u8 request, u16 index, void *buf, size_t len)
+{
+	return gud_usb_transfer(gdrm, false, request, index, buf, len);
+}
+
+int gud_usb_write8(struct gud_device *gdrm, u8 request, u8 val)
+{
+	return gud_usb_set(gdrm, request, 0, &val, sizeof(val));
+}
+
+static int gud_set_version(struct usb_device *usb, u8 ifnum, u32 flags, u8 version)
+{
+	u8 *buf;
+	int ret;
+
+	buf = kmalloc(sizeof(*buf), GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	*buf = version;
+	ret = gud_usb_control_msg(usb, ifnum, false, GUD_REQ_SET_VERSION, 0, buf, sizeof(*buf));
+	kfree(buf);
+	if (ret == -EPIPE)
+		return -EPROTONOSUPPORT;
+	if (ret)
+		return ret;
+
+	if (flags & GUD_DISPLAY_FLAG_STATUS_ON_SET) {
+		u8 status;
+
+		ret = gud_usb_get_status(usb, ifnum, &status);
+		if (!ret && status != GUD_STATUS_OK)
+			ret = -EPROTONOSUPPORT;
+	}
+
+	return ret;
+}
+
+static int gud_get_properties(struct gud_device *gdrm, unsigned int num_properties)
+{
+	struct gud_property_req *properties;
+	unsigned int i;
+	int ret;
+
+	if (!num_properties)
+		return 0;
+
+	gdrm->properties = drmm_kcalloc(&gdrm->drm, num_properties, sizeof(*gdrm->properties),
+					GFP_KERNEL);
+	if (!gdrm->properties)
+		return -ENOMEM;
+
+	properties = kcalloc(num_properties, sizeof(*properties), GFP_KERNEL);
+	if (!properties)
+		return -ENOMEM;
+
+	ret = gud_usb_get(gdrm, GUD_REQ_GET_PROPERTIES, 0,
+			  properties, num_properties * sizeof(*properties));
+	if (ret)
+		goto out;
+
+	for (i = 0; i < num_properties; i++) {
+		u16 prop = le16_to_cpu(properties[i].prop);
+		u64 val = le64_to_cpu(properties[i].val);
+
+		switch (prop) {
+		case GUD_PROPERTY_ROTATION:
+			/*
+			 * DRM UAPI matches the protocol so use the value directly,
+			 * but mask out any additions on future devices.
+			 */
+			val &= GUD_ROTATION_MASK;
+			ret = drm_plane_create_rotation_property(&gdrm->pipe.plane,
+								 DRM_MODE_ROTATE_0, val);
+			break;
+		default:
+			/* New ones might show up in future devices, skip those we don't know. */
+			drm_dbg(&gdrm->drm, "Unknown property: %u\n", prop);
+			continue;
+		}
+
+		if (ret)
+			goto out;
+
+		gdrm->properties[gdrm->num_properties++] = prop;
+	}
+out:
+	kfree(properties);
+
+	return ret;
+}
+
+static int gud_stats_debugfs(struct seq_file *m, void *data)
+{
+	struct drm_info_node *node = m->private;
+	struct gud_device *gdrm = to_gud_device(node->minor->dev);
+	char buf[10];
+
+	string_get_size(gdrm->bulk_len, 1, STRING_UNITS_2, buf, sizeof(buf));
+	seq_printf(m, "Max buffer size: %s\n", buf);
+	seq_printf(m, "Number of errors:  %u\n", gdrm->stats_num_errors);
+
+	seq_puts(m, "Compression:      ");
+	if (gdrm->compression & GUD_COMPRESSION_LZ4)
+		seq_puts(m, " lz4");
+	seq_puts(m, "\n");
+
+	if (gdrm->compression) {
+		u64 remainder;
+		u64 ratio = div64_u64_rem(gdrm->stats_length, gdrm->stats_actual_length,
+					  &remainder);
+		u64 ratio_frac = div64_u64(remainder * 10, gdrm->stats_actual_length);
+
+		seq_printf(m, "Compression ratio: %llu.%llu\n", ratio, ratio_frac);
+	}
+
+	return 0;
+}
+
+static const struct drm_info_list gud_debugfs_list[] = {
+	{ "stats", gud_stats_debugfs, 0, NULL },
+};
+
+static void gud_debugfs_init(struct drm_minor *minor)
+{
+	drm_debugfs_create_files(gud_debugfs_list, ARRAY_SIZE(gud_debugfs_list),
+				 minor->debugfs_root, minor);
+}
+
+static const struct drm_simple_display_pipe_funcs gud_pipe_funcs = {
+	.check      = gud_pipe_check,
+	.update	    = gud_pipe_update,
+	.prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb,
+};
+
+static const struct drm_mode_config_funcs gud_mode_config_funcs = {
+	.fb_create = drm_gem_fb_create_with_dirty,
+	.atomic_check = drm_atomic_helper_check,
+	.atomic_commit = drm_atomic_helper_commit,
+};
+
+static const u64 gud_pipe_modifiers[] = {
+	DRM_FORMAT_MOD_LINEAR,
+	DRM_FORMAT_MOD_INVALID
+};
+
+DEFINE_DRM_GEM_FOPS(gud_fops);
+
+static const struct drm_driver gud_drm_driver = {
+	.driver_features	= DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
+	.fops			= &gud_fops,
+	DRM_GEM_SHMEM_DRIVER_OPS,
+	.debugfs_init		= gud_debugfs_init,
+
+	.name			= "gud",
+	.desc			= "Generic USB Display",
+	.date			= "20200422",
+	.major			= 1,
+	.minor			= 0,
+};
+
+static void gud_free_buffers_and_mutex(struct drm_device *drm, void *unused)
+{
+	struct gud_device *gdrm = to_gud_device(drm);
+
+	vfree(gdrm->compress_buf);
+	kfree(gdrm->bulk_buf);
+	mutex_destroy(&gdrm->ctrl_lock);
+	mutex_destroy(&gdrm->damage_lock);
+}
+
+static int gud_probe(struct usb_interface *interface, const struct usb_device_id *id)
+{
+	u8 ifnum = interface->cur_altsetting->desc.bInterfaceNumber;
+	struct usb_device *usb = interface_to_usbdev(interface);
+	struct device *dev = &interface->dev;
+	const struct drm_format_info *xrgb8888_emulation_format = NULL;
+	bool rgb565_supported = false, xrgb8888_supported = false;
+	struct usb_endpoint_descriptor *bulk_out;
+	struct gud_display_descriptor_req desc;
+	unsigned int num_formats = 0;
+	struct gud_device *gdrm;
+	size_t max_buffer_size = 0;
+	struct drm_device *drm;
+	u8 *formats_dev;
+	u32 *formats;
+	int ret, i;
+
+	ret = usb_find_bulk_out_endpoint(interface->cur_altsetting, &bulk_out);
+	if (ret)
+		return ret;
+
+	ret = gud_get_display_descriptor(interface, &desc);
+	if (ret) {
+		DRM_DEV_DEBUG_DRIVER(dev, "Not a display interface: ret=%d\n", ret);
+		return -ENODEV;
+	}
+
+	if (desc.version > 1) {
+		ret = gud_set_version(usb, ifnum, le32_to_cpu(desc.flags), 1);
+		if (ret) {
+			if (ret == -EPROTONOSUPPORT)
+				dev_err(dev, "Protocol version %u is not supported\n",
+					desc.version);
+			return ret;
+		}
+	}
+
+	gdrm = devm_drm_dev_alloc(dev, &gud_drm_driver, struct gud_device, drm);
+	if (IS_ERR(gdrm))
+		return PTR_ERR(gdrm);
+
+	drm = &gdrm->drm;
+	drm->mode_config.funcs = &gud_mode_config_funcs;
+	ret = drmm_mode_config_init(drm);
+	if (ret)
+		return ret;
+
+	gdrm->ifnum = ifnum;
+	gdrm->flags = le32_to_cpu(desc.flags);
+	gdrm->compression = desc.compression & GUD_COMPRESSION_LZ4;
+
+	if (gdrm->flags & GUD_DISPLAY_FLAG_FULL_UPDATE && gdrm->compression)
+		return -EINVAL;
+
+	mutex_init(&gdrm->ctrl_lock);
+	mutex_init(&gdrm->damage_lock);
+	INIT_WORK(&gdrm->work, gud_flush_work);
+	gud_clear_damage(gdrm);
+
+	ret = drmm_add_action_or_reset(drm, gud_free_buffers_and_mutex, NULL);
+	if (ret)
+		return ret;
+
+	drm->mode_config.min_width = le32_to_cpu(desc.min_width);
+	drm->mode_config.max_width = le32_to_cpu(desc.max_width);
+	drm->mode_config.min_height = le32_to_cpu(desc.min_height);
+	drm->mode_config.max_height = le32_to_cpu(desc.max_height);
+
+	formats_dev = devm_kmalloc(dev, desc.num_formats, GFP_KERNEL);
+	/* Add room for emulated XRGB8888 */
+	formats = devm_kmalloc_array(dev, desc.num_formats + 1, sizeof(*formats), GFP_KERNEL);
+	if (!formats_dev || !formats)
+		return -ENOMEM;
+
+	ret = gud_usb_get(gdrm, GUD_REQ_GET_FORMATS, 0, formats_dev, desc.num_formats);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < desc.num_formats; i++) {
+		const struct drm_format_info *info;
+		size_t fmt_buf_size;
+		u32 format;
+
+		format = gud_to_fourcc(formats_dev[i]);
+		if (!format) {
+			drm_dbg(drm, "Unsupported format: 0x%02x\n", formats_dev[i]);
+			continue;
+		}
+
+		if (format == GUD_DRM_FORMAT_R1)
+			info = &gud_drm_format_r1;
+		else
+			info = drm_format_info(format);
+
+		switch (format) {
+		case GUD_DRM_FORMAT_R1:
+			xrgb8888_emulation_format = info;
+			break;
+		case DRM_FORMAT_RGB565:
+			rgb565_supported = true;
+			if (!xrgb8888_emulation_format)
+				xrgb8888_emulation_format = info;
+			break;
+		case DRM_FORMAT_XRGB8888:
+			xrgb8888_supported = true;
+			break;
+		};
+
+		fmt_buf_size = drm_format_info_min_pitch(info, 0, drm->mode_config.max_width) *
+			       drm->mode_config.max_height;
+		max_buffer_size = max(max_buffer_size, fmt_buf_size);
+
+		if (format == GUD_DRM_FORMAT_R1)
+			continue; /* Internal not for userspace */
+
+		formats[num_formats++] = format;
+	}
+
+	if (!num_formats && !xrgb8888_emulation_format) {
+		dev_err(dev, "No supported pixel formats found\n");
+		return -EINVAL;
+	}
+
+	/* Prefer speed over color depth */
+	if (rgb565_supported)
+		drm->mode_config.preferred_depth = 16;
+
+	if (!xrgb8888_supported && xrgb8888_emulation_format) {
+		gdrm->xrgb8888_emulation_format = xrgb8888_emulation_format;
+		formats[num_formats++] = DRM_FORMAT_XRGB8888;
+	}
+
+	if (desc.max_buffer_size)
+		max_buffer_size = le32_to_cpu(desc.max_buffer_size);
+retry:
+	/*
+	 * Use plain kmalloc here since devm_kmalloc() places struct devres at the beginning
+	 * of the buffer it allocates. This wastes a lot of memory when allocating big buffers.
+	 * Asking for 2M would actually allocate 4M. This would also prevent getting the biggest
+	 * possible buffer potentially leading to split transfers.
+	 */
+	gdrm->bulk_buf = kmalloc(max_buffer_size, GFP_KERNEL | __GFP_NOWARN);
+	if (!gdrm->bulk_buf) {
+		max_buffer_size = roundup_pow_of_two(max_buffer_size) / 2;
+		if (max_buffer_size < SZ_512K)
+			return -ENOMEM;
+		goto retry;
+	}
+
+	gdrm->bulk_pipe = usb_sndbulkpipe(usb, usb_endpoint_num(bulk_out));
+	gdrm->bulk_len = max_buffer_size;
+
+	if (gdrm->compression & GUD_COMPRESSION_LZ4) {
+		gdrm->lz4_comp_mem = devm_kmalloc(dev, LZ4_MEM_COMPRESS, GFP_KERNEL);
+		if (!gdrm->lz4_comp_mem)
+			return -ENOMEM;
+
+		gdrm->compress_buf = vmalloc(gdrm->bulk_len);
+		if (!gdrm->compress_buf)
+			return -ENOMEM;
+	}
+
+	ret = drm_simple_display_pipe_init(drm, &gdrm->pipe, &gud_pipe_funcs,
+					   formats, num_formats,
+					   gud_pipe_modifiers, NULL);
+	if (ret)
+		return ret;
+
+	devm_kfree(dev, formats);
+	devm_kfree(dev, formats_dev);
+
+	ret = gud_get_properties(gdrm, desc.num_properties);
+	if (ret)
+		return ret;
+
+	drm_plane_enable_fb_damage_clips(&gdrm->pipe.plane);
+
+	for (i = 0; i < desc.num_connectors; i++) {
+		ret = gud_connector_create(gdrm, i);
+		if (ret)
+			return ret;
+	}
+
+	drm_mode_config_reset(drm);
+
+	usb_set_intfdata(interface, gdrm);
+
+	ret = drm_dev_register(drm, 0);
+	if (ret)
+		return ret;
+
+	drm_kms_helper_poll_init(drm);
+
+	drm_fbdev_generic_setup(drm, 0);
+
+	return 0;
+}
+
+static void gud_disconnect(struct usb_interface *interface)
+{
+	struct gud_device *gdrm = usb_get_intfdata(interface);
+	struct drm_device *drm = &gdrm->drm;
+
+	drm_dbg(drm, "%s:\n", __func__);
+
+	drm_kms_helper_poll_fini(drm);
+	drm_dev_unplug(drm);
+	drm_atomic_helper_shutdown(drm);
+}
+
+static int gud_suspend(struct usb_interface *interface, pm_message_t message)
+{
+	struct gud_device *gdrm = usb_get_intfdata(interface);
+
+	return drm_mode_config_helper_suspend(&gdrm->drm);
+}
+
+static int gud_resume(struct usb_interface *interface)
+{
+	struct gud_device *gdrm = usb_get_intfdata(interface);
+
+	drm_mode_config_helper_resume(&gdrm->drm);
+
+	return 0;
+}
+
+static const struct usb_device_id gud_id_table[] = {
+	{ USB_DEVICE_INTERFACE_CLASS(0x1d50, 0x614d, USB_CLASS_VENDOR_SPEC) },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(usb, gud_id_table);
+
+static struct usb_driver gud_usb_driver = {
+	.name		= "gud",
+	.probe		= gud_probe,
+	.disconnect	= gud_disconnect,
+	.id_table	= gud_id_table,
+	.suspend	= gud_suspend,
+	.resume		= gud_resume,
+	.reset_resume	= gud_resume,
+};
+
+module_usb_driver(gud_usb_driver);
+
+MODULE_AUTHOR("Noralf Trønnes");
+MODULE_LICENSE("Dual MIT/GPL");
diff --git a/drivers/gpu/drm/gud/gud_internal.h b/drivers/gpu/drm/gud/gud_internal.h
new file mode 100644
index 000000000000..46e7d7ed2c69
--- /dev/null
+++ b/drivers/gpu/drm/gud/gud_internal.h
@@ -0,0 +1,148 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef __LINUX_GUD_INTERNAL_H
+#define __LINUX_GUD_INTERNAL_H
+
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+#include <uapi/drm/drm_fourcc.h>
+
+#include <drm/drm_modes.h>
+#include <drm/drm_simple_kms_helper.h>
+
+struct gud_device {
+	struct drm_device drm;
+	struct drm_simple_display_pipe pipe;
+	struct work_struct work;
+	u8 ifnum;
+	u32 flags;
+	const struct drm_format_info *xrgb8888_emulation_format;
+
+	u16 *properties;
+	unsigned int num_properties;
+
+	unsigned int bulk_pipe;
+	void *bulk_buf;
+	size_t bulk_len;
+
+	u8 compression;
+	void *lz4_comp_mem;
+	void *compress_buf;
+
+	u64 stats_length;
+	u64 stats_actual_length;
+	unsigned int stats_num_errors;
+
+	struct mutex ctrl_lock; /* Serialize get/set and status transfers */
+
+	struct mutex damage_lock; /* Protects the following members: */
+	struct drm_framebuffer *fb;
+	struct drm_rect damage;
+	bool prev_flush_failed;
+};
+
+static inline struct gud_device *to_gud_device(struct drm_device *drm)
+{
+	return container_of(drm, struct gud_device, drm);
+}
+
+static inline struct usb_device *gud_to_usb_device(struct gud_device *gdrm)
+{
+	return interface_to_usbdev(to_usb_interface(gdrm->drm.dev));
+}
+
+int gud_usb_get(struct gud_device *gdrm, u8 request, u16 index, void *buf, size_t len);
+int gud_usb_set(struct gud_device *gdrm, u8 request, u16 index, void *buf, size_t len);
+int gud_usb_write8(struct gud_device *gdrm, u8 request, u8 val);
+
+void gud_clear_damage(struct gud_device *gdrm);
+void gud_flush_work(struct work_struct *work);
+int gud_pipe_check(struct drm_simple_display_pipe *pipe,
+		   struct drm_plane_state *new_plane_state,
+		   struct drm_crtc_state *new_crtc_state);
+void gud_pipe_update(struct drm_simple_display_pipe *pipe,
+		     struct drm_plane_state *old_state);
+int gud_connector_fill_properties(struct drm_connector *connector,
+				  struct drm_connector_state *connector_state,
+				  struct gud_property_req *properties);
+int gud_connector_create(struct gud_device *gdrm, unsigned int index);
+
+#define GUD_DRM_FORMAT_R1	fourcc_code('R', '1', ' ', ' ')
+
+static inline u8 gud_from_fourcc(u32 fourcc)
+{
+	switch (fourcc) {
+	case GUD_DRM_FORMAT_R1:
+		return GUD_PIXEL_FORMAT_R1;
+	case DRM_FORMAT_RGB565:
+		return GUD_PIXEL_FORMAT_RGB565;
+	case DRM_FORMAT_XRGB8888:
+		return GUD_PIXEL_FORMAT_XRGB8888;
+	case DRM_FORMAT_ARGB8888:
+		return GUD_PIXEL_FORMAT_ARGB8888;
+	};
+
+	return 0;
+}
+
+static inline u32 gud_to_fourcc(u8 format)
+{
+	switch (format) {
+	case GUD_PIXEL_FORMAT_R1:
+		return GUD_DRM_FORMAT_R1;
+	case GUD_PIXEL_FORMAT_RGB565:
+		return DRM_FORMAT_RGB565;
+	case GUD_PIXEL_FORMAT_XRGB8888:
+		return DRM_FORMAT_XRGB8888;
+	case GUD_PIXEL_FORMAT_ARGB8888:
+		return DRM_FORMAT_ARGB8888;
+	};
+
+	return 0;
+}
+
+static inline void gud_from_display_mode(struct gud_display_mode_req *dst,
+					 const struct drm_display_mode *src)
+{
+	u32 flags = src->flags & GUD_DISPLAY_MODE_FLAG_USER_MASK;
+
+	if (src->type & DRM_MODE_TYPE_PREFERRED)
+		flags |= GUD_DISPLAY_MODE_FLAG_PREFERRED;
+
+	dst->clock = cpu_to_le32(src->clock);
+	dst->hdisplay = cpu_to_le16(src->hdisplay);
+	dst->hsync_start = cpu_to_le16(src->hsync_start);
+	dst->hsync_end = cpu_to_le16(src->hsync_end);
+	dst->htotal = cpu_to_le16(src->htotal);
+	dst->vdisplay = cpu_to_le16(src->vdisplay);
+	dst->vsync_start = cpu_to_le16(src->vsync_start);
+	dst->vsync_end = cpu_to_le16(src->vsync_end);
+	dst->vtotal = cpu_to_le16(src->vtotal);
+	dst->flags = cpu_to_le32(flags);
+}
+
+static inline void gud_to_display_mode(struct drm_display_mode *dst,
+				       const struct gud_display_mode_req *src)
+{
+	u32 flags = le32_to_cpu(src->flags);
+
+	memset(dst, 0, sizeof(*dst));
+	dst->clock = le32_to_cpu(src->clock);
+	dst->hdisplay = le16_to_cpu(src->hdisplay);
+	dst->hsync_start = le16_to_cpu(src->hsync_start);
+	dst->hsync_end = le16_to_cpu(src->hsync_end);
+	dst->htotal = le16_to_cpu(src->htotal);
+	dst->vdisplay = le16_to_cpu(src->vdisplay);
+	dst->vsync_start = le16_to_cpu(src->vsync_start);
+	dst->vsync_end = le16_to_cpu(src->vsync_end);
+	dst->vtotal = le16_to_cpu(src->vtotal);
+	dst->flags = flags & GUD_DISPLAY_MODE_FLAG_USER_MASK;
+	dst->type = DRM_MODE_TYPE_DRIVER;
+	if (flags & GUD_DISPLAY_MODE_FLAG_PREFERRED)
+		dst->type |= DRM_MODE_TYPE_PREFERRED;
+	drm_mode_set_name(dst);
+}
+
+#endif
diff --git a/drivers/gpu/drm/gud/gud_pipe.c b/drivers/gpu/drm/gud/gud_pipe.c
new file mode 100644
index 000000000000..1eef930bcf9b
--- /dev/null
+++ b/drivers/gpu/drm/gud/gud_pipe.c
@@ -0,0 +1,472 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2020 Noralf Trønnes
+ */
+
+#include <linux/dma-buf.h>
+#include <linux/lz4.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_damage_helper.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_format_helper.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_gem_shmem_helper.h>
+#include <drm/drm_print.h>
+#include <drm/drm_rect.h>
+#include <drm/drm_simple_kms_helper.h>
+#include <drm/gud.h>
+
+#include "gud_internal.h"
+
+static bool gud_is_big_endian(void)
+{
+#if defined(__BIG_ENDIAN)
+	return true;
+#else
+	return false;
+#endif
+}
+
+static size_t gud_xrgb8888_to_r124(u8 *dst, const struct drm_format_info *format,
+				   void *src, struct drm_framebuffer *fb,
+				   struct drm_rect *rect)
+{
+	unsigned int block_width = drm_format_info_block_width(format, 0);
+	unsigned int bits_per_pixel = 8 / block_width;
+	unsigned int x, y, width, height;
+	u8 *p, *block = dst; /* Assign to silence compiler warning */
+	size_t len;
+	void *buf;
+
+	WARN_ON_ONCE(format->char_per_block[0] != 1);
+
+	/* Start on a byte boundary */
+	rect->x1 = ALIGN_DOWN(rect->x1, block_width);
+	width = drm_rect_width(rect);
+	height = drm_rect_height(rect);
+	len = drm_format_info_min_pitch(format, 0, width) * height;
+
+	buf = kmalloc(width * height, GFP_KERNEL);
+	if (!buf)
+		return 0;
+
+	drm_fb_xrgb8888_to_gray8(buf, src, fb, rect);
+
+	p = buf;
+	for (y = 0; y < drm_rect_height(rect); y++) {
+		for (x = 0; x < drm_rect_width(rect); x++) {
+			if (!(x % block_width)) {
+				block = dst++;
+				*block = 0;
+			}
+
+			*block <<= bits_per_pixel;
+			*block |= (*p++) >> (8 - bits_per_pixel);
+		}
+	}
+
+	kfree(buf);
+
+	return len;
+}
+
+static int gud_prep_flush(struct gud_device *gdrm, struct drm_framebuffer *fb,
+			  const struct drm_format_info *format, struct drm_rect *rect,
+			  struct gud_set_buffer_req *req)
+{
+	struct dma_buf_attachment *import_attach = fb->obj[0]->import_attach;
+	u8 compression = gdrm->compression;
+	struct dma_buf_map map;
+	void *vaddr, *buf;
+	size_t pitch, len;
+	int ret = 0;
+
+	pitch = drm_format_info_min_pitch(format, 0, drm_rect_width(rect));
+	len = pitch * drm_rect_height(rect);
+	if (len > gdrm->bulk_len)
+		return -E2BIG;
+
+	ret = drm_gem_shmem_vmap(fb->obj[0], &map);
+	if (ret)
+		return ret;
+
+	vaddr = map.vaddr + fb->offsets[0];
+
+	if (import_attach) {
+		ret = dma_buf_begin_cpu_access(import_attach->dmabuf, DMA_FROM_DEVICE);
+		if (ret)
+			goto vunmap;
+	}
+retry:
+	if (compression)
+		buf = gdrm->compress_buf;
+	else
+		buf = gdrm->bulk_buf;
+
+	/*
+	 * Imported buffers are assumed to be write-combined and thus uncached
+	 * with slow reads (at least on ARM).
+	 */
+	if (format != fb->format) {
+		if (format->format == GUD_DRM_FORMAT_R1) {
+			len = gud_xrgb8888_to_r124(buf, format, vaddr, fb, rect);
+			if (!len) {
+				ret = -ENOMEM;
+				goto end_cpu_access;
+			}
+		} else if (format->format == DRM_FORMAT_RGB565) {
+			drm_fb_xrgb8888_to_rgb565(buf, vaddr, fb, rect, gud_is_big_endian());
+		}
+	} else if (gud_is_big_endian() && format->cpp[0] > 1) {
+		drm_fb_swab(buf, vaddr, fb, rect, !import_attach);
+	} else if (compression && !import_attach && pitch == fb->pitches[0]) {
+		/* can compress directly from the framebuffer */
+		buf = vaddr + rect->y1 * pitch;
+	} else {
+		drm_fb_memcpy(buf, vaddr, fb, rect);
+	}
+
+	memset(req, 0, sizeof(*req));
+	req->x = cpu_to_le32(rect->x1);
+	req->y = cpu_to_le32(rect->y1);
+	req->width = cpu_to_le32(drm_rect_width(rect));
+	req->height = cpu_to_le32(drm_rect_height(rect));
+	req->length = cpu_to_le32(len);
+
+	if (compression & GUD_COMPRESSION_LZ4) {
+		int complen;
+
+		complen = LZ4_compress_default(buf, gdrm->bulk_buf, len, len, gdrm->lz4_comp_mem);
+		if (complen < 0) {
+			compression = 0;
+			goto retry;
+		}
+
+		req->compression = GUD_COMPRESSION_LZ4;
+		req->compressed_length = cpu_to_le32(complen);
+	}
+
+end_cpu_access:
+	if (import_attach)
+		dma_buf_end_cpu_access(import_attach->dmabuf, DMA_FROM_DEVICE);
+vunmap:
+	drm_gem_shmem_vunmap(fb->obj[0], &map);
+
+	return ret;
+}
+
+static int gud_flush_rect(struct gud_device *gdrm, struct drm_framebuffer *fb,
+			  const struct drm_format_info *format, struct drm_rect *rect)
+{
+	struct usb_device *usb = gud_to_usb_device(gdrm);
+	struct gud_set_buffer_req req;
+	int ret, actual_length;
+	size_t len, trlen;
+
+	drm_dbg(&gdrm->drm, "Flushing [FB:%d] " DRM_RECT_FMT "\n", fb->base.id, DRM_RECT_ARG(rect));
+
+	ret = gud_prep_flush(gdrm, fb, format, rect, &req);
+	if (ret)
+		return ret;
+
+	len = le32_to_cpu(req.length);
+
+	if (req.compression)
+		trlen = le32_to_cpu(req.compressed_length);
+	else
+		trlen = len;
+
+	gdrm->stats_length += len;
+	/* Did it wrap around? */
+	if (gdrm->stats_length <= len && gdrm->stats_actual_length) {
+		gdrm->stats_length = len;
+		gdrm->stats_actual_length = 0;
+	}
+	gdrm->stats_actual_length += trlen;
+
+	if (!(gdrm->flags & GUD_DISPLAY_FLAG_FULL_UPDATE) || gdrm->prev_flush_failed) {
+		ret = gud_usb_set(gdrm, GUD_REQ_SET_BUFFER, 0, &req, sizeof(req));
+		if (ret)
+			return ret;
+	}
+
+	ret = usb_bulk_msg(usb, gdrm->bulk_pipe, gdrm->bulk_buf, trlen,
+			   &actual_length, msecs_to_jiffies(3000));
+	if (!ret && trlen != actual_length)
+		ret = -EIO;
+	if (ret)
+		gdrm->stats_num_errors++;
+
+	return ret;
+}
+
+void gud_clear_damage(struct gud_device *gdrm)
+{
+	gdrm->damage.x1 = INT_MAX;
+	gdrm->damage.y1 = INT_MAX;
+	gdrm->damage.x2 = 0;
+	gdrm->damage.y2 = 0;
+}
+
+static void gud_add_damage(struct gud_device *gdrm, struct drm_rect *damage)
+{
+	gdrm->damage.x1 = min(gdrm->damage.x1, damage->x1);
+	gdrm->damage.y1 = min(gdrm->damage.y1, damage->y1);
+	gdrm->damage.x2 = max(gdrm->damage.x2, damage->x2);
+	gdrm->damage.y2 = max(gdrm->damage.y2, damage->y2);
+}
+
+static void gud_retry_failed_flush(struct gud_device *gdrm, struct drm_framebuffer *fb,
+				   struct drm_rect *damage)
+{
+	/*
+	 * pipe_update waits for the worker when the display mode is going to change.
+	 * This ensures that the width and height is still the same making it safe to
+	 * add back the damage.
+	 */
+
+	mutex_lock(&gdrm->damage_lock);
+	if (!gdrm->fb) {
+		drm_framebuffer_get(fb);
+		gdrm->fb = fb;
+	}
+	gud_add_damage(gdrm, damage);
+	mutex_unlock(&gdrm->damage_lock);
+
+	/* Retry only once to avoid a possible storm in case of continues errors. */
+	if (!gdrm->prev_flush_failed)
+		queue_work(system_long_wq, &gdrm->work);
+	gdrm->prev_flush_failed = true;
+}
+
+void gud_flush_work(struct work_struct *work)
+{
+	struct gud_device *gdrm = container_of(work, struct gud_device, work);
+	const struct drm_format_info *format;
+	struct drm_framebuffer *fb;
+	struct drm_rect damage;
+	unsigned int i, lines;
+	int idx, ret = 0;
+	size_t pitch;
+
+	if (!drm_dev_enter(&gdrm->drm, &idx))
+		return;
+
+	mutex_lock(&gdrm->damage_lock);
+	fb = gdrm->fb;
+	gdrm->fb = NULL;
+	damage = gdrm->damage;
+	gud_clear_damage(gdrm);
+	mutex_unlock(&gdrm->damage_lock);
+
+	if (!fb)
+		goto out;
+
+	format = fb->format;
+	if (format->format == DRM_FORMAT_XRGB8888 && gdrm->xrgb8888_emulation_format)
+		format = gdrm->xrgb8888_emulation_format;
+
+	/* Split update if it's too big */
+	pitch = drm_format_info_min_pitch(format, 0, drm_rect_width(&damage));
+	lines = drm_rect_height(&damage);
+
+	if (gdrm->bulk_len < lines * pitch)
+		lines = gdrm->bulk_len / pitch;
+
+	for (i = 0; i < DIV_ROUND_UP(drm_rect_height(&damage), lines); i++) {
+		struct drm_rect rect = damage;
+
+		rect.y1 += i * lines;
+		rect.y2 = min_t(u32, rect.y1 + lines, damage.y2);
+
+		ret = gud_flush_rect(gdrm, fb, format, &rect);
+		if (ret) {
+			if (ret != -ENODEV && ret != -ECONNRESET &&
+			    ret != -ESHUTDOWN && ret != -EPROTO) {
+				gud_retry_failed_flush(gdrm, fb, &damage);
+				dev_err_once(fb->dev->dev,
+					     "Failed to flush framebuffer: error=%d\n", ret);
+			}
+			break;
+		}
+
+		gdrm->prev_flush_failed = false;
+	}
+
+	drm_framebuffer_put(fb);
+out:
+	drm_dev_exit(idx);
+}
+
+static void gud_fb_queue_damage(struct gud_device *gdrm, struct drm_framebuffer *fb,
+				struct drm_rect *damage)
+{
+	struct drm_framebuffer *old_fb = NULL;
+
+	mutex_lock(&gdrm->damage_lock);
+
+	if (fb != gdrm->fb) {
+		old_fb = gdrm->fb;
+		drm_framebuffer_get(fb);
+		gdrm->fb = fb;
+	}
+
+	gud_add_damage(gdrm, damage);
+
+	mutex_unlock(&gdrm->damage_lock);
+
+	queue_work(system_long_wq, &gdrm->work);
+
+	if (old_fb)
+		drm_framebuffer_put(old_fb);
+}
+
+int gud_pipe_check(struct drm_simple_display_pipe *pipe,
+		   struct drm_plane_state *new_plane_state,
+		   struct drm_crtc_state *new_crtc_state)
+{
+	struct gud_device *gdrm = to_gud_device(pipe->crtc.dev);
+	struct drm_plane_state *old_plane_state = pipe->plane.state;
+	const struct drm_display_mode *mode = &new_crtc_state->mode;
+	struct drm_atomic_state *state = new_plane_state->state;
+	struct drm_framebuffer *old_fb = old_plane_state->fb;
+	struct drm_connector_state *connector_state = NULL;
+	struct drm_framebuffer *fb = new_plane_state->fb;
+	const struct drm_format_info *format = fb->format;
+	struct drm_connector *connector;
+	int idx, ret, num_properties;
+	struct gud_state_req *req;
+	unsigned int i;
+	size_t len;
+
+	if (WARN_ON_ONCE(!fb))
+		return -EINVAL;
+
+	if (old_plane_state->rotation != new_plane_state->rotation)
+		new_crtc_state->mode_changed = true;
+
+	if (old_fb && old_fb->format != format)
+		new_crtc_state->mode_changed = true;
+
+	if (!new_crtc_state->mode_changed && !new_crtc_state->connectors_changed)
+		return 0;
+
+	/* Only one connector is supported */
+	if (hweight32(new_crtc_state->connector_mask) != 1)
+		return -EINVAL;
+
+	if (format->format == DRM_FORMAT_XRGB8888 && gdrm->xrgb8888_emulation_format)
+		format = gdrm->xrgb8888_emulation_format;
+
+	for_each_new_connector_in_state(state, connector, connector_state, i) {
+		if (connector_state->crtc)
+			break;
+	}
+
+	if (WARN_ON_ONCE(!connector_state))
+		return -ENOENT;
+
+	num_properties = gud_connector_fill_properties(connector, NULL, NULL);
+	if (num_properties < 0)
+		return num_properties;
+
+	num_properties += gdrm->num_properties;
+
+	len = struct_size(req, properties, num_properties);
+	req = kzalloc(len, GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	gud_from_display_mode(&req->mode, mode);
+
+	req->format = gud_from_fourcc(format->format);
+	if (WARN_ON_ONCE(!req->format))
+		return -EINVAL;
+
+	req->connector = drm_connector_index(connector);
+	req->num_properties = num_properties;
+
+	num_properties = gud_connector_fill_properties(connector, connector_state,
+						       req->properties);
+
+	for (i = 0; i < gdrm->num_properties; i++) {
+		u16 prop = gdrm->properties[i];
+		u64 val;
+
+		switch (prop) {
+		case GUD_PROPERTY_ROTATION:
+			/* DRM UAPI matches the protocol so use value directly */
+			val = new_plane_state->rotation;
+			break;
+		default:
+			WARN_ON_ONCE(1);
+			ret = -EINVAL;
+			goto out;
+		}
+
+		req->properties[num_properties + i].prop = cpu_to_le16(prop);
+		req->properties[num_properties + i].val = cpu_to_le64(val);
+	}
+
+	if (drm_dev_enter(fb->dev, &idx)) {
+		ret = gud_usb_set(gdrm, GUD_REQ_SET_STATE_CHECK, 0, req, len);
+		drm_dev_exit(idx);
+	}  else {
+		ret = -ENODEV;
+	}
+out:
+	kfree(req);
+
+	return ret;
+}
+
+void gud_pipe_update(struct drm_simple_display_pipe *pipe,
+		     struct drm_plane_state *old_state)
+{
+	struct drm_device *drm = pipe->crtc.dev;
+	struct gud_device *gdrm = to_gud_device(drm);
+	struct drm_plane_state *state = pipe->plane.state;
+	struct drm_framebuffer *fb = state->fb;
+	struct drm_crtc *crtc = &pipe->crtc;
+	struct drm_rect damage;
+	int idx;
+
+	if (crtc->state->mode_changed || !crtc->state->enable) {
+		cancel_work_sync(&gdrm->work);
+		mutex_lock(&gdrm->damage_lock);
+		if (gdrm->fb) {
+			drm_framebuffer_put(gdrm->fb);
+			gdrm->fb = NULL;
+		}
+		gud_clear_damage(gdrm);
+		mutex_unlock(&gdrm->damage_lock);
+	}
+
+	if (!drm_dev_enter(drm, &idx))
+		return;
+
+	if (!old_state->fb)
+		gud_usb_write8(gdrm, GUD_REQ_SET_CONTROLLER_ENABLE, 1);
+
+	if (fb && (crtc->state->mode_changed || crtc->state->connectors_changed))
+		gud_usb_set(gdrm, GUD_REQ_SET_STATE_COMMIT, 0, NULL, 0);
+
+	if (crtc->state->active_changed)
+		gud_usb_write8(gdrm, GUD_REQ_SET_DISPLAY_ENABLE, crtc->state->active);
+
+	if (drm_atomic_helper_damage_merged(old_state, state, &damage)) {
+		if (gdrm->flags & GUD_DISPLAY_FLAG_FULL_UPDATE)
+			drm_rect_init(&damage, 0, 0, fb->width, fb->height);
+		gud_fb_queue_damage(gdrm, fb, &damage);
+	}
+
+	if (!crtc->state->enable)
+		gud_usb_write8(gdrm, GUD_REQ_SET_CONTROLLER_ENABLE, 0);
+
+	drm_dev_exit(idx);
+}
diff --git a/include/drm/gud.h b/include/drm/gud.h
new file mode 100644
index 000000000000..ebf19f50f780
--- /dev/null
+++ b/include/drm/gud.h
@@ -0,0 +1,356 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright 2020 Noralf Trønnes
+ */
+
+#ifndef __LINUX_GUD_H
+#define __LINUX_GUD_H
+
+#include <linux/types.h>
+
+/*
+ * struct gud_display_descriptor_req - Display descriptor
+ * @magic: Magic value GUD_DISPLAY_MAGIC
+ * @version: Protocol version
+ * @flags: Flags
+ *         - STATUS_ON_SET: Always do a status request after a SET request.
+ *                          This is used by the Linux gadget driver since it has
+ *                          no way to control the status stage of a control OUT
+ *                          request that has a payload.
+ *         - FULL_UPDATE:   Always send the entire framebuffer when flushing changes.
+ *                          The GUD_REQ_SET_BUFFER request will not be sent
+ *                          before each bulk transfer, it will only be sent if the
+ *                          previous bulk transfer had failed. This gives the device
+ *                          a chance to reset its state machine if needed.
+ *                          This flag can not be used in combination with compression.
+ * @compression: Supported compression types
+ *               - GUD_COMPRESSION_LZ4: LZ4 lossless compression.
+ * @max_buffer_size: Maximum buffer size the device can handle (optional).
+ *                   This is useful for devices that don't have a big enough
+ *                   buffer to decompress the entire framebuffer in one go.
+ * @min_width: Minimum pixel width the controller can handle
+ * @max_width: Maximum width
+ * @min_height: Minimum height
+ * @max_height: Maximum height
+ * @num_formats: Number of supported pixel formats
+ * @num_properties: Number of properties that are not connector properties
+ * @num_connectors: Number of connectors
+ *
+ * Devices that have only one display mode will have min_width == max_width
+ * and min_height == max_height.
+ */
+struct gud_display_descriptor_req {
+	__u32 magic;
+#define GUD_DISPLAY_MAGIC			0x1d50614d
+	__u8 version;
+	__le32 flags;
+#define GUD_DISPLAY_FLAG_STATUS_ON_SET		BIT(0)
+#define GUD_DISPLAY_FLAG_FULL_UPDATE		BIT(1)
+	__u8 compression;
+#define GUD_COMPRESSION_LZ4			BIT(0)
+	__le32 max_buffer_size;
+	__le32 min_width;
+	__le32 max_width;
+	__le32 min_height;
+	__le32 max_height;
+	__u8 num_formats;
+	__u8 num_properties;
+	__u8 num_connectors;
+} __packed;
+
+/*
+ * struct gud_property_req - Property
+ * @prop: Property
+ * @val: Value
+ */
+struct gud_property_req {
+	__le16 prop;
+	__le64 val;
+} __packed;
+
+/*
+ * struct gud_display_mode_req - Display mode
+ * @clock: Pixel clock in kHz
+ * @hdisplay: Horizontal display size
+ * @hsync_start: Horizontal sync start
+ * @hsync_end: Horizontal sync end
+ * @htotal: Horizontal total size
+ * @vdisplay: Vertical display size
+ * @vsync_start: Vertical sync start
+ * @vsync_end: Vertical sync end
+ * @vtotal: Vertical total size
+ * @flags: Bits 0-13 are the same as in the RandR protocol and also what DRM uses.
+ *         The deprecated bits are reused for internal protocol flags leaving us
+ *         free to follow DRM for the other bits in the future.
+ *         - FLAG_PREFERRED: Set on the preferred display mode.
+ */
+struct gud_display_mode_req {
+	__le32 clock;
+	__le16 hdisplay;
+	__le16 hsync_start;
+	__le16 hsync_end;
+	__le16 htotal;
+	__le16 vdisplay;
+	__le16 vsync_start;
+	__le16 vsync_end;
+	__le16 vtotal;
+	__le32 flags;
+#define GUD_DISPLAY_MODE_FLAG_PHSYNC		BIT(0)
+#define GUD_DISPLAY_MODE_FLAG_NHSYNC		BIT(1)
+#define GUD_DISPLAY_MODE_FLAG_PVSYNC		BIT(2)
+#define GUD_DISPLAY_MODE_FLAG_NVSYNC		BIT(3)
+#define GUD_DISPLAY_MODE_FLAG_INTERLACE		BIT(4)
+#define GUD_DISPLAY_MODE_FLAG_DBLSCAN		BIT(5)
+#define GUD_DISPLAY_MODE_FLAG_CSYNC		BIT(6)
+#define GUD_DISPLAY_MODE_FLAG_PCSYNC		BIT(7)
+#define GUD_DISPLAY_MODE_FLAG_NCSYNC		BIT(8)
+#define GUD_DISPLAY_MODE_FLAG_HSKEW		BIT(9)
+/* BCast and PixelMultiplex are deprecated */
+#define GUD_DISPLAY_MODE_FLAG_DBLCLK		BIT(12)
+#define GUD_DISPLAY_MODE_FLAG_CLKDIV2		BIT(13)
+#define GUD_DISPLAY_MODE_FLAG_USER_MASK		\
+		(GUD_DISPLAY_MODE_FLAG_PHSYNC | GUD_DISPLAY_MODE_FLAG_NHSYNC | \
+		GUD_DISPLAY_MODE_FLAG_PVSYNC | GUD_DISPLAY_MODE_FLAG_NVSYNC | \
+		GUD_DISPLAY_MODE_FLAG_INTERLACE | GUD_DISPLAY_MODE_FLAG_DBLSCAN | \
+		GUD_DISPLAY_MODE_FLAG_CSYNC | GUD_DISPLAY_MODE_FLAG_PCSYNC | \
+		GUD_DISPLAY_MODE_FLAG_NCSYNC | GUD_DISPLAY_MODE_FLAG_HSKEW | \
+		GUD_DISPLAY_MODE_FLAG_DBLCLK | GUD_DISPLAY_MODE_FLAG_CLKDIV2)
+/* Internal protocol flags */
+#define GUD_DISPLAY_MODE_FLAG_PREFERRED		BIT(10)
+} __packed;
+
+/*
+ * struct gud_connector_descriptor_req - Connector descriptor
+ * @connector_type: Connector type (GUD_CONNECTOR_TYPE_*).
+ *                  If the host doesn't support the type it should fall back to PANEL.
+ * @flags: Flags
+ *         - POLL_STATUS: Connector status can change (polled every 10 seconds)
+ *         - INTERLACE: Interlaced modes are supported
+ *         - DOUBLESCAN: Doublescan modes are supported
+ * @num_properties: Number of supported properties
+ */
+struct gud_connector_descriptor_req {
+	__u8 connector_type;
+#define GUD_CONNECTOR_TYPE_PANEL		0
+#define GUD_CONNECTOR_TYPE_VGA			1
+#define GUD_CONNECTOR_TYPE_COMPOSITE		2
+#define GUD_CONNECTOR_TYPE_SVIDEO		3
+#define GUD_CONNECTOR_TYPE_COMPONENT		4
+#define GUD_CONNECTOR_TYPE_DVI			5
+#define GUD_CONNECTOR_TYPE_DISPLAYPORT		6
+#define GUD_CONNECTOR_TYPE_HDMI			7
+	__le32 flags;
+#define GUD_CONNECTOR_FLAGS_POLL_STATUS		BIT(0)
+#define GUD_CONNECTOR_FLAGS_INTERLACE		BIT(1)
+#define GUD_CONNECTOR_FLAGS_DOUBLESCAN		BIT(2)
+	__u8 num_properties;
+} __packed;
+
+/*
+ * struct gud_connector_status_req - Connector status
+ * @status: Status
+ *          - DISCONNECTED: Connector is disconnected
+ *          - CONNECTED: Connector is connected
+ *          - UNKNOWN: Connection status is unknown
+ *          Flags:
+ *          - CHANGED: A change has happened since the last request
+ * @num_modes: Number of available display modes
+ * @edid_len: Length of EDID data
+ *
+ * If @num_modes is zero, EDID is used to create display modes.
+ * If both @num_modes and @edid_len are set, EDID is just passed on to userspace
+ * in the EDID connector property.
+ *
+ * Userspace will get a HOTPLUG uevent if one of the following is true:
+ * - Connection status has changed
+ * - @num_modes or @edid_len has changed
+ * - CHANGED is set
+ */
+struct gud_connector_status_req {
+	__u8 status;
+#define GUD_CONNECTOR_STATUS_DISCONNECTED	0x00
+#define GUD_CONNECTOR_STATUS_CONNECTED		0x01
+#define GUD_CONNECTOR_STATUS_UNKNOWN		0x02
+#define GUD_CONNECTOR_STATUS_CONNECTED_MASK	0x03
+#define GUD_CONNECTOR_STATUS_CHANGED		BIT(7)
+	__le16 num_modes;
+	__le16 edid_len;
+} __packed;
+
+/*
+ * struct gud_set_buffer_req - Set buffer transfer info
+ * @x: X position of rectangle
+ * @y: Y position
+ * @width: Pixel width of rectangle
+ * @height: Pixel height
+ * @length: Buffer length in bytes
+ * @compression: Transfer compression
+ * @compressed_length: Compressed buffer length
+ *
+ * This request is issued right before the bulk transfer.
+ * @x, @y, @width and @height specifies the rectangle where the buffer should be
+ * placed inside the framebuffer.
+ */
+struct gud_set_buffer_req {
+	__le32 x;
+	__le32 y;
+	__le32 width;
+	__le32 height;
+	__le32 length;
+	__u8 compression;
+	__le32 compressed_length;
+} __packed;
+
+/*
+ * struct gud_state_req - Display state
+ * @mode: Display mode
+ * @format: Pixel format GUD_PIXEL_FORMAT_*
+ * @connector: Connector index
+ * @num_properties: Number of properties in the state
+ * @properties: Array of properties
+ *
+ * The entire state is transferred each time there's a change.
+ */
+struct gud_state_req {
+	struct gud_display_mode_req mode;
+	__u8 format;
+	__u8 connector;
+	__u8 num_properties;
+	struct gud_property_req properties[];
+} __packed;
+
+/* List of supported connector properties: */
+
+/* Margins in pixels to deal with overscan, range 0-100 */
+#define GUD_PROPERTY_TV_LEFT_MARGIN			1
+#define GUD_PROPERTY_TV_RIGHT_MARGIN			2
+#define GUD_PROPERTY_TV_TOP_MARGIN			3
+#define GUD_PROPERTY_TV_BOTTOM_MARGIN			4
+/* Number of modes are placed at _SHIFT in val on retrieval */
+#define GUD_PROPERTY_TV_MODE				5
+  #define GUD_CONNECTOR_TV_MODE_NUM_SHIFT	16
+/* Brightness in percent, range 0-100 */
+#define GUD_PROPERTY_TV_BRIGHTNESS			6
+/* Contrast in percent, range 0-100 */
+#define GUD_PROPERTY_TV_CONTRAST			7
+/* Flicker reduction in percent, range 0-100 */
+#define GUD_PROPERTY_TV_FLICKER_REDUCTION		8
+/* Overscan in percent, range 0-100 */
+#define GUD_PROPERTY_TV_OVERSCAN			9
+/* Saturation in percent, range 0-100 */
+#define GUD_PROPERTY_TV_SATURATION			10
+/* Hue in percent, range 0-100 */
+#define GUD_PROPERTY_TV_HUE				11
+
+/*
+ * Backlight brightness is in the range 0-100 inclusive. The value represents the human perceptual
+ * brightness and not a linear PWM value. 0 is minimum brightness which should not turn the
+ * backlight completely off. The DPMS connector property should be used to control power which will
+ * trigger a GUD_REQ_SET_DISPLAY_ENABLE request.
+ *
+ * This does not map to a DRM property, it is used with the backlight device.
+ */
+#define GUD_PROPERTY_BACKLIGHT_BRIGHTNESS		12
+
+/* List of supported properties that are not connector propeties: */
+
+/*
+ * Plane rotation. Should return the supported bitmask on
+ * GUD_REQ_GET_PROPERTIES. GUD_ROTATION_0 is mandatory.
+ *
+ * Note: This is not display rotation so 90/270 will need scaling to make it fit (unless squared).
+ */
+#define GUD_PROPERTY_ROTATION				50
+  #define GUD_ROTATION_0			BIT(0)
+  #define GUD_ROTATION_90			BIT(1)
+  #define GUD_ROTATION_180			BIT(2)
+  #define GUD_ROTATION_270			BIT(3)
+  #define GUD_ROTATION_REFLECT_X		BIT(4)
+  #define GUD_ROTATION_REFLECT_Y		BIT(5)
+  #define GUD_ROTATION_MASK			(GUD_ROTATION_0 | GUD_ROTATION_90 | \
+						GUD_ROTATION_180 | GUD_ROTATION_270 | \
+						GUD_ROTATION_REFLECT_X | GUD_ROTATION_REFLECT_Y)
+
+/* USB Control requests: */
+
+/* Get status from the last GET/SET control request. Value is u8. */
+#define GUD_REQ_GET_STATUS				0x00
+  /* Status values: */
+  #define GUD_STATUS_OK				0x00
+  #define GUD_STATUS_BUSY			0x01
+  #define GUD_STATUS_REQUEST_NOT_SUPPORTED	0x02
+  #define GUD_STATUS_PROTOCOL_ERROR		0x03
+  #define GUD_STATUS_INVALID_PARAMETER		0x04
+  #define GUD_STATUS_ERROR			0x05
+
+/* Get display descriptor as a &gud_display_descriptor_req */
+#define GUD_REQ_GET_DESCRIPTOR				0x01
+
+/*
+ * If the host driver doesn't support the device protocol version it will send the versions it
+ * supports starting with the latest. If the device isn't backwards compatible or doesn't support
+ * the version the host suggests, it shall stall the request. The version is sent as u8.
+ */
+#define GUD_REQ_SET_VERSION				0x30
+
+/* Get supported pixel formats as a byte array of GUD_PIXEL_FORMAT_* */
+#define GUD_REQ_GET_FORMATS				0x40
+  /* R1 is a 1-bit monochrome transfer format presented to userspace as XRGB8888 */
+  #define GUD_PIXEL_FORMAT_R1			0x01
+  #define GUD_PIXEL_FORMAT_RGB565		0x40
+  #define GUD_PIXEL_FORMAT_XRGB8888		0x80
+  #define GUD_PIXEL_FORMAT_ARGB8888		0x81
+
+/*
+ * Get supported properties that are not connector propeties as a &gud_property_req array.
+ * gud_property_req.val often contains the initial value for the property.
+ */
+#define GUD_REQ_GET_PROPERTIES				0x41
+
+/* Connector requests have the connector index passed in the wValue field */
+
+/* Get connector descriptor as a &gud_connector_descriptor_req */
+#define GUD_REQ_GET_CONNECTOR				0x50
+
+/*
+ * Get properties supported by the connector as a &gud_property_req array.
+ * gud_property_req.val often contains the initial value for the property.
+ */
+#define GUD_REQ_GET_CONNECTOR_PROPERTIES		0x51
+
+/*
+ * Issued when there's a TV_MODE property present.
+ * Gets an array of the supported TV_MODE names each entry of length
+ * GUD_CONNECTOR_TV_MODE_NAME_LEN. Names must be NUL-terminated.
+ */
+#define GUD_REQ_GET_CONNECTOR_TV_MODE_VALUES		0x52
+  #define GUD_CONNECTOR_TV_MODE_NAME_LEN	16
+
+/* When userspace checks connector status, this is issued first, not used for poll requests. */
+#define GUD_REQ_SET_CONNECTOR_FORCE_DETECT		0x53
+
+/* Get connector status as &gud_connector_status_req. */
+#define GUD_REQ_GET_CONNECTOR_STATUS			0x54
+
+/* Get &gud_display_mode_req array of supported display modes */
+#define GUD_REQ_GET_CONNECTOR_MODES			0x55
+
+/* Get Extended Display Identification Data */
+#define GUD_REQ_GET_CONNECTOR_EDID			0x56
+
+/* Set buffer properties before bulk transfer as &gud_set_buffer_req */
+#define GUD_REQ_SET_BUFFER				0x60
+
+/* Check display configuration as &gud_state_req */
+#define GUD_REQ_SET_STATE_CHECK				0x61
+
+/* Apply the previous STATE_CHECK configuration */
+#define GUD_REQ_SET_STATE_COMMIT			0x62
+
+/* Enable/disable the display controller, value is u8: 0/1 */
+#define GUD_REQ_SET_CONTROLLER_ENABLE			0x63
+
+/* Enable/disable display/output (DPMS), value is u8: 0/1 */
+#define GUD_REQ_SET_DISPLAY_ENABLE			0x64
+
+#endif
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v4 3/3] drm: Add Generic USB Display driver
@ 2021-01-20 17:00   ` Noralf Trønnes
  0 siblings, 0 replies; 53+ messages in thread
From: Noralf Trønnes @ 2021-01-20 17:00 UTC (permalink / raw)
  To: dri-devel
  Cc: hudson, markus, sam, linux-usb, th020394, lkundrak, pontus.fuchs, peter

This adds a generic USB display driver with the intention that it can be
used with future USB interfaced low end displays/adapters. The Linux
gadget device driver will serve as the canonical device implementation.

The following DRM properties are supported:
- Plane rotation
- Connector TV properties

There is also support for backlight brightness exposed as a backlight
device.

Display modes can be made available to the host driver either as DRM
display modes or through EDID. If both are present, EDID is just passed
on to userspace.

Performance is preferred over color depth, so if the device supports
RGB565, DRM_CAP_DUMB_PREFERRED_DEPTH will return 16.

If the device transfer buffer can't fit an uncompressed framebuffer
update, the update is split up into parts that do fit.

Optimal user experience is achieved by providing damage reports either by
setting FB_DAMAGE_CLIPS on pageflips or calling DRM_IOCTL_MODE_DIRTYFB.

LZ4 compression is used if the device supports it.

The driver supports a one bit monochrome transfer format: R1. This is not
implemented in the gadget driver. It is added in preparation for future
monochrome e-ink displays.

The driver is MIT licensed to smooth the path for any BSD port of the
driver.

v2:
- Use devm_drm_dev_alloc() and drmm_mode_config_init()
- drm_fbdev_generic_setup: Use preferred_bpp=0, 16 was a copy paste error
- The drm_backlight_helper is dropped, copy in the code
- Support protocol version backwards compatibility for device

v3:
- Use donated Openmoko USB pid
- Use direct compression from framebuffer when pitch matches, not only on
  full frames, so split updates can benefit
- Use __le16 in struct gud_drm_req_get_connector_status
- Set edid property when the device only provides edid
- Clear compression fields in struct gud_drm_req_set_buffer
- Fix protocol version negotiation
- Remove mode->vrefresh, it's calculated

v4:
- Drop the status req polling which was a workaround for something that
  turned out to be a dwc2 udc driver problem
- Add a flag for the Linux gadget to require a status request on
  SET operations. Other devices will only get status req on STALL errors
- Use protocol specific error codes (Peter)
- Add a flag for devices that want to receive the entire framebuffer on
  each flush (Lubomir)
- Retry a failed framebuffer flush
- If mode has changed wait for worker and clear pending damage before
  queuing up new damage, fb width/height might have changed
- Increase error counter on bulk transfer failures
- Use DRM_MODE_CONNECTOR_USB
- Handle R1 kmalloc error (Peter)
- Don't try and replicate the USB get descriptor request standard for the
  display descriptor (Peter)
- Make max_buffer_size optional (Peter), drop the pow2 requirement since
  it's not necessary anymore.
- Don't pre-alloc a control request buffer, it was only 4k
- Let gud.h describe the whole protocol explicitly and don't let DRM
  leak into it (Peter)
- Drop display mode .hskew and .vscan from the protocol
- Shorten names: s/GUD_DRM_/GUD_/ s/gud_drm_/gud_/ (Peter)
- Fix gud_pipe_check() connector picking when switching connector
- Drop gud_drm_driver_gem_create_object() cached is default now
- Retrieve USB device from struct drm_device.dev instead of keeping a
  pointer
- Honour fb->offsets[0]
- Fix mode fetching when connector status is forced
- Check EDID length reported by the device
- Use drm_do_get_edid() so userspace can overrride EDID
- Set epoch counter to signal connector status change
- gud_drm_driver can be const now

Cc: Peter Stuge <peter@stuge.se>
Cc: Lubomir Rintel <lkundrak@v3.sk>
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
---
 MAINTAINERS                         |   8 +
 drivers/gpu/drm/Kconfig             |   2 +
 drivers/gpu/drm/Makefile            |   1 +
 drivers/gpu/drm/gud/Kconfig         |  14 +
 drivers/gpu/drm/gud/Makefile        |   4 +
 drivers/gpu/drm/gud/gud_connector.c | 722 ++++++++++++++++++++++++++++
 drivers/gpu/drm/gud/gud_drv.c       | 620 ++++++++++++++++++++++++
 drivers/gpu/drm/gud/gud_internal.h  | 148 ++++++
 drivers/gpu/drm/gud/gud_pipe.c      | 472 ++++++++++++++++++
 include/drm/gud.h                   | 356 ++++++++++++++
 10 files changed, 2347 insertions(+)
 create mode 100644 drivers/gpu/drm/gud/Kconfig
 create mode 100644 drivers/gpu/drm/gud/Makefile
 create mode 100644 drivers/gpu/drm/gud/gud_connector.c
 create mode 100644 drivers/gpu/drm/gud/gud_drv.c
 create mode 100644 drivers/gpu/drm/gud/gud_internal.h
 create mode 100644 drivers/gpu/drm/gud/gud_pipe.c
 create mode 100644 include/drm/gud.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 00112c044608..e7f71ac55f08 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5525,6 +5525,14 @@ S:	Maintained
 F:	Documentation/devicetree/bindings/display/panel/feiyang,fy07024di26a30d.yaml
 F:	drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c
 
+DRM DRIVER FOR GENERIC USB DISPLAY
+M:	Noralf Trønnes <noralf@tronnes.org>
+S:	Maintained
+W:	https://github.com/notro/gud/wiki
+T:	git git://anongit.freedesktop.org/drm/drm-misc
+F:	drivers/gpu/drm/gud/
+F:	include/drm/gud.h
+
 DRM DRIVER FOR GRAIN MEDIA GM12U320 PROJECTORS
 M:	Hans de Goede <hdegoede@redhat.com>
 S:	Maintained
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 147d61b9674e..6d3f47933e51 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -388,6 +388,8 @@ source "drivers/gpu/drm/tidss/Kconfig"
 
 source "drivers/gpu/drm/xlnx/Kconfig"
 
+source "drivers/gpu/drm/gud/Kconfig"
+
 # Keep legacy drivers last
 
 menuconfig DRM_LEGACY
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 81569009f884..78dd8e12525d 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -124,3 +124,4 @@ obj-$(CONFIG_DRM_ASPEED_GFX) += aspeed/
 obj-$(CONFIG_DRM_MCDE) += mcde/
 obj-$(CONFIG_DRM_TIDSS) += tidss/
 obj-y			+= xlnx/
+obj-y			+= gud/
diff --git a/drivers/gpu/drm/gud/Kconfig b/drivers/gpu/drm/gud/Kconfig
new file mode 100644
index 000000000000..d832cd63687c
--- /dev/null
+++ b/drivers/gpu/drm/gud/Kconfig
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config DRM_GUD
+	tristate "Generic USB Display"
+	depends on DRM && USB
+	select LZ4_COMPRESS
+	select DRM_KMS_HELPER
+	select DRM_GEM_SHMEM_HELPER
+	select BACKLIGHT_CLASS_DEVICE
+	help
+	  This is a DRM display driver for Generic USB Displays or display
+	  adapters.
+
+	  If M is selected the module will be called gud.
diff --git a/drivers/gpu/drm/gud/Makefile b/drivers/gpu/drm/gud/Makefile
new file mode 100644
index 000000000000..d2bb53a65de6
--- /dev/null
+++ b/drivers/gpu/drm/gud/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+
+gud-objs			:= gud_drv.o gud_pipe.o gud_connector.o
+obj-$(CONFIG_DRM_GUD)		+= gud.o
diff --git a/drivers/gpu/drm/gud/gud_connector.c b/drivers/gpu/drm/gud/gud_connector.c
new file mode 100644
index 000000000000..a4b9bbf48e19
--- /dev/null
+++ b/drivers/gpu/drm/gud/gud_connector.c
@@ -0,0 +1,722 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2020 Noralf Trønnes
+ */
+
+#include <linux/backlight.h>
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_state_helper.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_encoder.h>
+#include <drm/drm_file.h>
+#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_simple_kms_helper.h>
+#include <drm/gud.h>
+
+#include "gud_internal.h"
+
+struct gud_connector {
+	struct drm_connector connector;
+	struct drm_encoder encoder;
+	struct backlight_device *backlight;
+
+	/* Supported properties */
+	u16 *properties;
+	unsigned int num_properties;
+
+	/* Initial gadget tv state if applicable, applied on state reset */
+	struct drm_tv_connector_state initial_tv_state;
+
+	/*
+	 * Initial gadget backlight brightness if applicable, applied on state reset.
+	 * The value -ENODEV is used to signal no backlight.
+	 */
+	int initial_brightness;
+
+	unsigned int num_modes;
+	size_t edid_len;
+};
+
+static inline struct gud_connector *to_gud_connector(struct drm_connector *connector)
+{
+	return container_of(connector, struct gud_connector, connector);
+}
+
+static int gud_connector_backlight_update_status(struct backlight_device *bd)
+{
+	struct drm_connector *connector = bl_get_data(bd);
+	struct drm_connector_state *connector_state;
+	struct drm_device *dev = connector->dev;
+	struct drm_modeset_acquire_ctx ctx;
+	struct drm_atomic_state *state;
+	int ret;
+
+	state = drm_atomic_state_alloc(dev);
+	if (!state)
+		return -ENOMEM;
+
+	drm_modeset_acquire_init(&ctx, 0);
+	state->acquire_ctx = &ctx;
+retry:
+	connector_state = drm_atomic_get_connector_state(state, connector);
+	if (IS_ERR(connector_state)) {
+		ret = PTR_ERR(connector_state);
+		goto out;
+	}
+
+	/* Reuse tv.brightness to avoid having to subclass */
+	connector_state->tv.brightness = bd->props.brightness;
+
+	ret = drm_atomic_commit(state);
+out:
+	if (ret == -EDEADLK) {
+		drm_atomic_state_clear(state);
+		drm_modeset_backoff(&ctx);
+		goto retry;
+	}
+
+	drm_atomic_state_put(state);
+
+	drm_modeset_drop_locks(&ctx);
+	drm_modeset_acquire_fini(&ctx);
+
+	return ret;
+}
+
+static int gud_connector_backlight_get_brightness(struct backlight_device *bd)
+{
+	struct drm_connector *connector = bl_get_data(bd);
+	struct drm_device *dev = connector->dev;
+	int brightness;
+
+	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
+	brightness = connector->state->tv.brightness;
+	drm_modeset_unlock(&dev->mode_config.connection_mutex);
+
+	return brightness;
+}
+
+static const struct backlight_ops gud_connector_backlight_ops = {
+	.get_brightness = gud_connector_backlight_get_brightness,
+	.update_status	= gud_connector_backlight_update_status,
+};
+
+static int gud_connector_backlight_register(struct gud_connector *gconn)
+{
+	struct drm_connector *connector = &gconn->connector;
+	struct backlight_device *bd;
+	const char *name;
+	const struct backlight_properties props = {
+		.type = BACKLIGHT_RAW,
+		.scale = BACKLIGHT_SCALE_NON_LINEAR,
+		.max_brightness = 100,
+	};
+
+	name = kasprintf(GFP_KERNEL, "card%d-%s-backlight",
+			 connector->dev->primary->index, connector->name);
+	if (!name)
+		return -ENOMEM;
+
+	bd = backlight_device_register(name, connector->kdev, connector,
+				       &gud_connector_backlight_ops, &props);
+	kfree(name);
+	if (IS_ERR(bd))
+		return PTR_ERR(bd);
+
+	gconn->backlight = bd;
+
+	return 0;
+}
+
+static int gud_connector_status_request(struct drm_connector *connector)
+{
+	struct gud_connector *gconn = to_gud_connector(connector);
+	struct gud_device *gdrm = to_gud_device(connector->dev);
+	struct gud_connector_status_req req;
+	u16 num_modes, edid_len;
+	int ret;
+
+	ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_STATUS,
+			  connector->index, &req, sizeof(req));
+	if (ret)
+		return ret;
+
+	switch (req.status & GUD_CONNECTOR_STATUS_CONNECTED_MASK) {
+	case GUD_CONNECTOR_STATUS_DISCONNECTED:
+		ret = connector_status_disconnected;
+		break;
+	case GUD_CONNECTOR_STATUS_CONNECTED:
+		ret = connector_status_connected;
+		break;
+	default:
+		ret = connector_status_unknown;
+		break;
+	};
+
+	num_modes = le16_to_cpu(req.num_modes);
+	edid_len = le16_to_cpu(req.edid_len);
+
+	if (edid_len % EDID_LENGTH) {
+		drm_err(connector->dev, "%s: Invalid EDID size: %u\n", connector->name, edid_len);
+		edid_len = 0;
+	}
+
+	if (req.status & GUD_CONNECTOR_STATUS_CHANGED ||
+	    gconn->num_modes != num_modes || gconn->edid_len != edid_len)
+		connector->epoch_counter += 1;
+
+	gconn->num_modes = num_modes;
+	gconn->edid_len = edid_len;
+
+	if (!num_modes && !edid_len && ret != connector_status_disconnected)
+		drm_dbg_kms(connector->dev, "%s: No modes or EDID.\n", connector->name);
+
+	return ret;
+}
+
+static int gud_connector_detect(struct drm_connector *connector,
+				struct drm_modeset_acquire_ctx *ctx, bool force)
+{
+	struct gud_device *gdrm = to_gud_device(connector->dev);
+	int idx, ret;
+
+	if (!drm_dev_enter(connector->dev, &idx))
+		return connector_status_disconnected;
+
+	if (force) {
+		ret = gud_usb_set(gdrm, GUD_REQ_SET_CONNECTOR_FORCE_DETECT,
+				  connector->index, NULL, 0);
+		if (ret) {
+			ret = connector_status_unknown;
+			goto exit;
+		}
+	}
+
+	ret = gud_connector_status_request(connector);
+	if (ret < 0)
+		ret = connector_status_unknown;
+exit:
+	drm_dev_exit(idx);
+
+	return ret;
+}
+
+struct gud_connector_get_edid_ctx {
+	struct gud_connector *gconn;
+	void *buf;
+};
+
+static int gud_connector_get_edid_block(void *data, u8 *buf, unsigned int block, size_t len)
+{
+	struct gud_connector_get_edid_ctx *ctx = data;
+	struct gud_connector *gconn = ctx->gconn;
+	size_t start = block * EDID_LENGTH;
+
+	if (start + len > gconn->edid_len)
+		return -1;
+
+	if (!block) {
+		struct gud_device *gdrm = to_gud_device(gconn->connector.dev);
+		int ret;
+
+		/* Check because drm_do_get_edid() will retry on failure */
+		if (!ctx->buf)
+			ctx->buf = kmalloc(gconn->edid_len, GFP_KERNEL);
+		if (!ctx->buf)
+			return -1;
+
+		ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_EDID, gconn->connector.index,
+				  ctx->buf, gconn->edid_len);
+		if (ret)
+			return -1;
+	}
+
+	memcpy(buf, ctx->buf + start, len);
+
+	return 0;
+}
+
+static int gud_connector_get_modes(struct drm_connector *connector)
+{
+	struct gud_connector *gconn = to_gud_connector(connector);
+	struct gud_device *gdrm = to_gud_device(connector->dev);
+	struct gud_connector_get_edid_ctx edid_ctx = {
+		.gconn = gconn,
+	};
+	struct gud_display_mode_req *reqmodes = NULL;
+	unsigned int i, num_modes = 0;
+	struct edid *edid = NULL;
+	bool edid_override;
+	int idx, ret;
+
+	if (!drm_dev_enter(connector->dev, &idx))
+		return 0;
+
+	if (connector->force) {
+		ret = gud_connector_status_request(connector);
+		if (ret < 0)
+			goto out;
+	}
+
+	edid = drm_do_get_edid(connector, gud_connector_get_edid_block, &edid_ctx);
+	edid_override = edid && !edid_ctx.buf;
+	kfree(edid_ctx.buf);
+	drm_connector_update_edid_property(connector, edid);
+
+	if (!gconn->num_modes || edid_override) {
+		num_modes = drm_add_edid_modes(connector, edid);
+		goto out;
+	}
+
+	reqmodes = kmalloc_array(gconn->num_modes, sizeof(*reqmodes), GFP_KERNEL);
+	if (!reqmodes)
+		goto out;
+
+	ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_MODES, connector->index,
+			  reqmodes, gconn->num_modes * sizeof(*reqmodes));
+	if (ret)
+		goto out;
+
+	for (i = 0; i < gconn->num_modes; i++) {
+		struct drm_display_mode *mode;
+
+		mode = drm_mode_create(connector->dev);
+		if (!mode)
+			goto out;
+
+		gud_to_display_mode(mode, &reqmodes[i]);
+		drm_mode_probed_add(connector, mode);
+		num_modes++;
+	}
+out:
+	kfree(reqmodes);
+	kfree(edid);
+	drm_dev_exit(idx);
+
+	return num_modes;
+}
+
+static int gud_connector_atomic_check(struct drm_connector *connector,
+				      struct drm_atomic_state *state)
+{
+	struct drm_connector_state *new_state;
+	struct drm_crtc_state *new_crtc_state;
+	struct drm_connector_state *old_state;
+
+	new_state = drm_atomic_get_new_connector_state(state, connector);
+	if (!new_state->crtc)
+		return 0;
+
+	old_state = drm_atomic_get_old_connector_state(state, connector);
+	new_crtc_state = drm_atomic_get_new_crtc_state(state, new_state->crtc);
+
+	if (old_state->tv.margins.left != new_state->tv.margins.left ||
+	    old_state->tv.margins.right != new_state->tv.margins.right ||
+	    old_state->tv.margins.top != new_state->tv.margins.top ||
+	    old_state->tv.margins.bottom != new_state->tv.margins.bottom ||
+	    old_state->tv.mode != new_state->tv.mode ||
+	    old_state->tv.brightness != new_state->tv.brightness ||
+	    old_state->tv.contrast != new_state->tv.contrast ||
+	    old_state->tv.flicker_reduction != new_state->tv.flicker_reduction ||
+	    old_state->tv.overscan != new_state->tv.overscan ||
+	    old_state->tv.saturation != new_state->tv.saturation ||
+	    old_state->tv.hue != new_state->tv.hue)
+		new_crtc_state->connectors_changed = true;
+
+	return 0;
+}
+
+static const struct drm_connector_helper_funcs gud_connector_helper_funcs = {
+	.detect_ctx = gud_connector_detect,
+	.get_modes = gud_connector_get_modes,
+	.atomic_check = gud_connector_atomic_check,
+};
+
+static int gud_connector_late_register(struct drm_connector *connector)
+{
+	struct gud_connector *gconn = to_gud_connector(connector);
+
+	if (gconn->initial_brightness < 0)
+		return 0;
+
+	return gud_connector_backlight_register(gconn);
+}
+
+static void gud_connector_early_unregister(struct drm_connector *connector)
+{
+	struct gud_connector *gconn = to_gud_connector(connector);
+
+	backlight_device_unregister(gconn->backlight);
+}
+
+static void gud_connector_destroy(struct drm_connector *connector)
+{
+	struct gud_connector *gconn = to_gud_connector(connector);
+
+	drm_connector_cleanup(connector);
+	kfree(gconn->properties);
+	kfree(gconn);
+}
+
+static void gud_connector_reset(struct drm_connector *connector)
+{
+	struct gud_connector *gconn = to_gud_connector(connector);
+
+	drm_atomic_helper_connector_reset(connector);
+	connector->state->tv = gconn->initial_tv_state;
+	/* Set margins from command line */
+	drm_atomic_helper_connector_tv_reset(connector);
+	if (gconn->initial_brightness >= 0)
+		connector->state->tv.brightness = gconn->initial_brightness;
+}
+
+static const struct drm_connector_funcs gud_connector_funcs = {
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.late_register = gud_connector_late_register,
+	.early_unregister = gud_connector_early_unregister,
+	.destroy = gud_connector_destroy,
+	.reset = gud_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+/*
+ * The tv.mode property is shared among the connectors and its enum names are
+ * driver specific. This means that if more than one connector uses tv.mode,
+ * the enum names has to be the same.
+ */
+static int gud_connector_add_tv_mode(struct gud_device *gdrm,
+				     struct drm_connector *connector, u64 val)
+{
+	unsigned int i, num_modes;
+	const char **modes;
+	size_t buf_len;
+	char *buf;
+	int ret;
+
+	num_modes = val >> GUD_CONNECTOR_TV_MODE_NUM_SHIFT;
+
+	if (!num_modes)
+		return -EINVAL;
+
+	buf_len = num_modes * GUD_CONNECTOR_TV_MODE_NAME_LEN;
+	modes = kmalloc_array(num_modes, sizeof(*modes), GFP_KERNEL);
+	buf = kmalloc(buf_len, GFP_KERNEL);
+	if (!modes || !buf) {
+		ret = -ENOMEM;
+		goto free;
+	}
+
+	ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_TV_MODE_VALUES,
+			  connector->index, buf, buf_len);
+	if (ret)
+		goto free;
+
+	for (i = 0; i < num_modes; i++)
+		modes[i] = &buf[i * GUD_CONNECTOR_TV_MODE_NAME_LEN];
+
+	ret = drm_mode_create_tv_properties(connector->dev, num_modes, modes);
+free:
+	kfree(modes);
+	kfree(buf);
+
+	return ret;
+}
+
+static struct drm_property *
+gud_connector_property_lookup(struct drm_connector *connector, u16 prop)
+{
+	struct drm_mode_config *config = &connector->dev->mode_config;
+
+	switch (prop) {
+	case GUD_PROPERTY_TV_LEFT_MARGIN:
+		return config->tv_left_margin_property;
+	case GUD_PROPERTY_TV_RIGHT_MARGIN:
+		return config->tv_right_margin_property;
+	case GUD_PROPERTY_TV_TOP_MARGIN:
+		return config->tv_top_margin_property;
+	case GUD_PROPERTY_TV_BOTTOM_MARGIN:
+		return config->tv_bottom_margin_property;
+	case GUD_PROPERTY_TV_MODE:
+		return config->tv_mode_property;
+	case GUD_PROPERTY_TV_BRIGHTNESS:
+		return config->tv_brightness_property;
+	case GUD_PROPERTY_TV_CONTRAST:
+		return config->tv_contrast_property;
+	case GUD_PROPERTY_TV_FLICKER_REDUCTION:
+		return config->tv_flicker_reduction_property;
+	case GUD_PROPERTY_TV_OVERSCAN:
+		return config->tv_overscan_property;
+	case GUD_PROPERTY_TV_SATURATION:
+		return config->tv_saturation_property;
+	case GUD_PROPERTY_TV_HUE:
+		return config->tv_hue_property;
+	default:
+		return ERR_PTR(-EINVAL);
+	}
+}
+
+static unsigned int *gud_connector_tv_state_val(u16 prop, struct drm_tv_connector_state *state)
+{
+	switch (prop) {
+	case GUD_PROPERTY_TV_LEFT_MARGIN:
+		return &state->margins.left;
+	case GUD_PROPERTY_TV_RIGHT_MARGIN:
+		return &state->margins.right;
+	case GUD_PROPERTY_TV_TOP_MARGIN:
+		return &state->margins.top;
+	case GUD_PROPERTY_TV_BOTTOM_MARGIN:
+		return &state->margins.bottom;
+	case GUD_PROPERTY_TV_MODE:
+		return &state->mode;
+	case GUD_PROPERTY_TV_BRIGHTNESS:
+		return &state->brightness;
+	case GUD_PROPERTY_TV_CONTRAST:
+		return &state->contrast;
+	case GUD_PROPERTY_TV_FLICKER_REDUCTION:
+		return &state->flicker_reduction;
+	case GUD_PROPERTY_TV_OVERSCAN:
+		return &state->overscan;
+	case GUD_PROPERTY_TV_SATURATION:
+		return &state->saturation;
+	case GUD_PROPERTY_TV_HUE:
+		return &state->hue;
+	default:
+		return ERR_PTR(-EINVAL);
+	}
+}
+
+static int gud_connector_add_properties(struct gud_device *gdrm, struct gud_connector *gconn,
+					unsigned int num_properties)
+{
+	struct drm_device *drm = &gdrm->drm;
+	struct drm_connector *connector = &gconn->connector;
+	struct gud_property_req *properties;
+	unsigned int i;
+	int ret;
+
+	gconn->properties = kcalloc(num_properties, sizeof(*gconn->properties), GFP_KERNEL);
+	if (!gconn->properties)
+		return -ENOMEM;
+
+	properties = kcalloc(num_properties, sizeof(*properties), GFP_KERNEL);
+	if (!properties)
+		return -ENOMEM;
+
+	ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_PROPERTIES, connector->index,
+			  properties, num_properties * sizeof(*properties));
+	if (ret)
+		goto out;
+
+	for (i = 0; i < num_properties; i++) {
+		u16 prop = le16_to_cpu(properties[i].prop);
+		u64 val = le64_to_cpu(properties[i].val);
+		struct drm_property *property;
+		unsigned int *state_val;
+
+		drm_dbg(drm, "property: %u = %llu(0x%llx)\n", prop, val, val);
+
+		switch (prop) {
+		case GUD_PROPERTY_TV_LEFT_MARGIN:
+			fallthrough;
+		case GUD_PROPERTY_TV_RIGHT_MARGIN:
+			fallthrough;
+		case GUD_PROPERTY_TV_TOP_MARGIN:
+			fallthrough;
+		case GUD_PROPERTY_TV_BOTTOM_MARGIN:
+			ret = drm_mode_create_tv_margin_properties(drm);
+			if (ret)
+				goto out;
+			break;
+		case GUD_PROPERTY_TV_MODE:
+			ret = gud_connector_add_tv_mode(gdrm, connector, val);
+			if (ret)
+				goto out;
+			val = val & (BIT(GUD_CONNECTOR_TV_MODE_NUM_SHIFT) - 1);
+			break;
+		case GUD_PROPERTY_TV_BRIGHTNESS:
+			fallthrough;
+		case GUD_PROPERTY_TV_CONTRAST:
+			fallthrough;
+		case GUD_PROPERTY_TV_FLICKER_REDUCTION:
+			fallthrough;
+		case GUD_PROPERTY_TV_OVERSCAN:
+			fallthrough;
+		case GUD_PROPERTY_TV_SATURATION:
+			fallthrough;
+		case GUD_PROPERTY_TV_HUE:
+			/* This is a no-op if already added. */
+			ret = drm_mode_create_tv_properties(drm, 0, NULL);
+			if (ret)
+				goto out;
+			break;
+		case GUD_PROPERTY_BACKLIGHT_BRIGHTNESS:
+			if (val > 100) {
+				ret = -EINVAL;
+				goto out;
+			}
+			gconn->initial_brightness = val;
+			break;
+		default:
+			/* New ones might show up in future devices, skip those we don't know. */
+			drm_dbg(drm, "Unknown property: %u\n", prop);
+			continue;
+		}
+
+		gconn->properties[gconn->num_properties++] = prop;
+
+		if (prop == GUD_PROPERTY_BACKLIGHT_BRIGHTNESS)
+			continue; /* not a DRM property */
+
+		property = gud_connector_property_lookup(connector, prop);
+		if (WARN_ON(IS_ERR(property)))
+			continue;
+
+		state_val = gud_connector_tv_state_val(prop, &gconn->initial_tv_state);
+		if (WARN_ON(IS_ERR(state_val)))
+			continue;
+
+		*state_val = val;
+		drm_object_attach_property(&connector->base, property, 0);
+	}
+out:
+	kfree(properties);
+
+	return ret;
+}
+
+int gud_connector_fill_properties(struct drm_connector *connector,
+				  struct drm_connector_state *connector_state,
+				  struct gud_property_req *properties)
+{
+	struct gud_connector *gconn;
+	unsigned int i;
+
+	gconn = to_gud_connector(connector);
+
+	/* Only interested in the count? */
+	if (!connector_state)
+		return gconn->num_properties;
+
+	for (i = 0; i < gconn->num_properties; i++) {
+		u16 prop = gconn->properties[i];
+		u64 val;
+
+		if (prop == GUD_PROPERTY_BACKLIGHT_BRIGHTNESS) {
+			val = connector_state->tv.brightness;
+		} else {
+			unsigned int *state_val;
+
+			state_val = gud_connector_tv_state_val(prop, &connector_state->tv);
+			if (WARN_ON_ONCE(IS_ERR(state_val)))
+				return PTR_ERR(state_val);
+
+			val = *state_val;
+		}
+
+		properties[i].prop = cpu_to_le16(prop);
+		properties[i].val = cpu_to_le64(val);
+	}
+
+	return gconn->num_properties;
+}
+
+int gud_connector_create(struct gud_device *gdrm, unsigned int index)
+{
+	struct gud_connector_descriptor_req desc;
+	struct drm_device *drm = &gdrm->drm;
+	struct gud_connector *gconn;
+	struct drm_connector *connector;
+	struct drm_encoder *encoder;
+	int ret, connector_type;
+	u32 flags;
+
+	ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR, index, &desc, sizeof(desc));
+	if (ret)
+		return ret;
+
+	gconn = kzalloc(sizeof(*gconn), GFP_KERNEL);
+	if (!gconn)
+		return -ENOMEM;
+
+	gconn->initial_brightness = -ENODEV;
+	flags = le32_to_cpu(desc.flags);
+	connector = &gconn->connector;
+
+	drm_dbg(drm, "Connector: index=%u type=%u flags=0x%x num_properties=%u\n",
+		index, desc.connector_type, flags, desc.num_properties);
+
+	switch (desc.connector_type) {
+	case GUD_CONNECTOR_TYPE_PANEL:
+		connector_type = DRM_MODE_CONNECTOR_USB;
+		break;
+	case GUD_CONNECTOR_TYPE_VGA:
+		connector_type = DRM_MODE_CONNECTOR_VGA;
+		break;
+	case GUD_CONNECTOR_TYPE_DVI:
+		connector_type = DRM_MODE_CONNECTOR_DVID;
+		break;
+	case GUD_CONNECTOR_TYPE_COMPOSITE:
+		connector_type = DRM_MODE_CONNECTOR_Composite;
+		break;
+	case GUD_CONNECTOR_TYPE_SVIDEO:
+		connector_type = DRM_MODE_CONNECTOR_SVIDEO;
+		break;
+	case GUD_CONNECTOR_TYPE_COMPONENT:
+		connector_type = DRM_MODE_CONNECTOR_Component;
+		break;
+	case GUD_CONNECTOR_TYPE_DISPLAYPORT:
+		connector_type = DRM_MODE_CONNECTOR_DisplayPort;
+		break;
+	case GUD_CONNECTOR_TYPE_HDMI:
+		connector_type = DRM_MODE_CONNECTOR_HDMIA;
+		break;
+	default: /* future types */
+		connector_type = DRM_MODE_CONNECTOR_USB;
+		break;
+	};
+
+	drm_connector_helper_add(connector, &gud_connector_helper_funcs);
+	ret = drm_connector_init(drm, connector, &gud_connector_funcs, connector_type);
+	if (ret) {
+		kfree(connector);
+		return ret;
+	}
+
+	if (WARN_ON(connector->index != index))
+		return -EINVAL;
+
+	if (flags & GUD_CONNECTOR_FLAGS_POLL_STATUS)
+		connector->polled = (DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT);
+	if (flags & GUD_CONNECTOR_FLAGS_INTERLACE)
+		connector->interlace_allowed = true;
+	if (flags & GUD_CONNECTOR_FLAGS_DOUBLESCAN)
+		connector->doublescan_allowed = true;
+
+	if (desc.num_properties) {
+		ret = gud_connector_add_properties(gdrm, gconn, desc.num_properties);
+		if (ret) {
+			dev_err(drm->dev, "Failed to add connector/%u properties\n", index);
+			return ret;
+		}
+	}
+
+	/* The first connector is attached to the existing simple pipe encoder */
+	if (!connector->index) {
+		encoder = &gdrm->pipe.encoder;
+	} else {
+		encoder = &gconn->encoder;
+
+		ret = drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_NONE);
+		if (ret)
+			return ret;
+
+		encoder->possible_crtcs = 1;
+	}
+
+	return drm_connector_attach_encoder(connector, encoder);
+}
diff --git a/drivers/gpu/drm/gud/gud_drv.c b/drivers/gpu/drm/gud/gud_drv.c
new file mode 100644
index 000000000000..6c5e9eeb8cdc
--- /dev/null
+++ b/drivers/gpu/drm/gud/gud_drv.c
@@ -0,0 +1,620 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2020 Noralf Trønnes
+ */
+
+#include <linux/dma-buf.h>
+#include <linux/lz4.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/string_helpers.h>
+#include <linux/usb.h>
+#include <linux/vmalloc.h>
+#include <linux/workqueue.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_damage_helper.h>
+#include <drm/drm_debugfs.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_gem_shmem_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_simple_kms_helper.h>
+#include <drm/gud.h>
+
+#include "gud_internal.h"
+
+/* Only used internally */
+static const struct drm_format_info gud_drm_format_r1 = {
+	.format = GUD_DRM_FORMAT_R1,
+	.num_planes = 1,
+	.char_per_block = { 1, 0, 0 },
+	.block_w = { 8, 0, 0 },
+	.block_h = { 1, 0, 0 },
+	.hsub = 1,
+	.vsub = 1,
+};
+
+static int gud_usb_control_msg(struct usb_device *usb, u8 ifnum, bool in,
+			       u8 request, u16 value, void *buf, size_t len)
+{
+	u8 requesttype = USB_TYPE_VENDOR | USB_RECIP_INTERFACE;
+	unsigned int pipe;
+	int ret;
+
+	if (in) {
+		pipe = usb_rcvctrlpipe(usb, 0);
+		requesttype |= USB_DIR_IN;
+	} else {
+		pipe = usb_sndctrlpipe(usb, 0);
+		requesttype |= USB_DIR_OUT;
+	}
+
+	ret = usb_control_msg(usb, pipe, request, requesttype, value,
+			      ifnum, buf, len, USB_CTRL_GET_TIMEOUT);
+	if (ret < 0)
+		return ret;
+	if (ret != len)
+		return -EIO;
+
+	return 0;
+}
+
+static int gud_get_display_descriptor(struct usb_interface *interface,
+				      struct gud_display_descriptor_req *desc)
+{
+	u8 ifnum = interface->cur_altsetting->desc.bInterfaceNumber;
+	struct usb_device *usb = interface_to_usbdev(interface);
+	void *buf;
+	int ret;
+
+	buf = kmalloc(sizeof(*desc), GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	ret = gud_usb_control_msg(usb, ifnum, true, GUD_REQ_GET_DESCRIPTOR, 0, buf, sizeof(*desc));
+	memcpy(desc, buf, sizeof(*desc));
+	kfree(buf);
+	if (ret)
+		return ret;
+
+	if (desc->magic != GUD_DISPLAY_MAGIC)
+		return -ENODATA;
+
+	DRM_DEV_DEBUG_DRIVER(&interface->dev,
+			     "version=%u flags=0x%x compression=0x%x num_formats=%u num_connectors=%u max_buffer_size=%u\n",
+			     desc->version, le32_to_cpu(desc->flags), desc->compression,
+			     desc->num_formats, desc->num_connectors,
+			     le32_to_cpu(desc->max_buffer_size));
+
+	if (!desc->version || !desc->num_formats || !desc->num_connectors ||
+	    !desc->max_width || !desc->max_height ||
+	    le32_to_cpu(desc->min_width) > le32_to_cpu(desc->max_width) ||
+	    le32_to_cpu(desc->min_height) > le32_to_cpu(desc->max_height))
+		return -EINVAL;
+
+	return 0;
+}
+
+static int gud_usb_get_status(struct usb_device *usb, u8 ifnum, u8 *status)
+{
+	u8 *buf;
+	int ret;
+
+	buf = kmalloc(sizeof(*buf), GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	ret = gud_usb_control_msg(usb, ifnum, true, GUD_REQ_GET_STATUS, 0, buf, sizeof(*buf));
+	*status = *buf;
+	kfree(buf);
+
+	return ret;
+}
+
+static int gud_status_to_errno(u8 status)
+{
+	switch (status) {
+	case GUD_STATUS_OK:
+		return 0;
+	case GUD_STATUS_BUSY:
+		return -EBUSY;
+	case GUD_STATUS_REQUEST_NOT_SUPPORTED:
+		return -EOPNOTSUPP;
+	case GUD_STATUS_PROTOCOL_ERROR:
+		return -EPROTO;
+	case GUD_STATUS_INVALID_PARAMETER:
+		return -EINVAL;
+	case GUD_STATUS_ERROR:
+		return -EREMOTEIO;
+	default:
+		return -EREMOTEIO;
+	}
+}
+
+static int gud_usb_transfer(struct gud_device *gdrm, bool in, u8 request, u16 index,
+			    void *buf, size_t len)
+{
+	struct usb_device *usb = gud_to_usb_device(gdrm);
+	void *trbuf = NULL;
+	int idx, ret;
+
+	drm_dbg(&gdrm->drm, "%s: request=0x%x index=%u len=%zu\n",
+		in ? "get" : "set", request, index, len);
+
+	if (!drm_dev_enter(&gdrm->drm, &idx))
+		return -ENODEV;
+
+	mutex_lock(&gdrm->ctrl_lock);
+
+	if (buf) {
+		if (in)
+			trbuf = kmalloc(len, GFP_KERNEL);
+		else
+			trbuf = kmemdup(buf, len, GFP_KERNEL);
+		if (!trbuf) {
+			ret = -ENOMEM;
+			goto unlock;
+		}
+	}
+
+	ret = gud_usb_control_msg(usb, gdrm->ifnum, in, request, index, trbuf, len);
+	if (ret == -EPIPE || (!ret && !in && (gdrm->flags & GUD_DISPLAY_FLAG_STATUS_ON_SET))) {
+		bool error = ret;
+		u8 status;
+
+		ret = gud_usb_get_status(usb, gdrm->ifnum, &status);
+		if (!ret) {
+			if (error && status == GUD_STATUS_OK) {
+				dev_err_once(gdrm->drm.dev,
+					     "Unexpected status OK for failed transfer\n");
+				ret = -EPIPE;
+			} else {
+				ret = gud_status_to_errno(status);
+			}
+		}
+	}
+
+	if (!ret && in && buf)
+		memcpy(buf, trbuf, len);
+
+	if (ret) {
+		drm_dbg(&gdrm->drm, "ret=%d\n", ret);
+		gdrm->stats_num_errors++;
+	}
+
+	kfree(trbuf);
+unlock:
+	mutex_unlock(&gdrm->ctrl_lock);
+	drm_dev_exit(idx);
+
+	return ret;
+}
+
+int gud_usb_get(struct gud_device *gdrm, u8 request, u16 index, void *buf, size_t len)
+{
+	return gud_usb_transfer(gdrm, true, request, index, buf, len);
+}
+
+int gud_usb_set(struct gud_device *gdrm, u8 request, u16 index, void *buf, size_t len)
+{
+	return gud_usb_transfer(gdrm, false, request, index, buf, len);
+}
+
+int gud_usb_write8(struct gud_device *gdrm, u8 request, u8 val)
+{
+	return gud_usb_set(gdrm, request, 0, &val, sizeof(val));
+}
+
+static int gud_set_version(struct usb_device *usb, u8 ifnum, u32 flags, u8 version)
+{
+	u8 *buf;
+	int ret;
+
+	buf = kmalloc(sizeof(*buf), GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	*buf = version;
+	ret = gud_usb_control_msg(usb, ifnum, false, GUD_REQ_SET_VERSION, 0, buf, sizeof(*buf));
+	kfree(buf);
+	if (ret == -EPIPE)
+		return -EPROTONOSUPPORT;
+	if (ret)
+		return ret;
+
+	if (flags & GUD_DISPLAY_FLAG_STATUS_ON_SET) {
+		u8 status;
+
+		ret = gud_usb_get_status(usb, ifnum, &status);
+		if (!ret && status != GUD_STATUS_OK)
+			ret = -EPROTONOSUPPORT;
+	}
+
+	return ret;
+}
+
+static int gud_get_properties(struct gud_device *gdrm, unsigned int num_properties)
+{
+	struct gud_property_req *properties;
+	unsigned int i;
+	int ret;
+
+	if (!num_properties)
+		return 0;
+
+	gdrm->properties = drmm_kcalloc(&gdrm->drm, num_properties, sizeof(*gdrm->properties),
+					GFP_KERNEL);
+	if (!gdrm->properties)
+		return -ENOMEM;
+
+	properties = kcalloc(num_properties, sizeof(*properties), GFP_KERNEL);
+	if (!properties)
+		return -ENOMEM;
+
+	ret = gud_usb_get(gdrm, GUD_REQ_GET_PROPERTIES, 0,
+			  properties, num_properties * sizeof(*properties));
+	if (ret)
+		goto out;
+
+	for (i = 0; i < num_properties; i++) {
+		u16 prop = le16_to_cpu(properties[i].prop);
+		u64 val = le64_to_cpu(properties[i].val);
+
+		switch (prop) {
+		case GUD_PROPERTY_ROTATION:
+			/*
+			 * DRM UAPI matches the protocol so use the value directly,
+			 * but mask out any additions on future devices.
+			 */
+			val &= GUD_ROTATION_MASK;
+			ret = drm_plane_create_rotation_property(&gdrm->pipe.plane,
+								 DRM_MODE_ROTATE_0, val);
+			break;
+		default:
+			/* New ones might show up in future devices, skip those we don't know. */
+			drm_dbg(&gdrm->drm, "Unknown property: %u\n", prop);
+			continue;
+		}
+
+		if (ret)
+			goto out;
+
+		gdrm->properties[gdrm->num_properties++] = prop;
+	}
+out:
+	kfree(properties);
+
+	return ret;
+}
+
+static int gud_stats_debugfs(struct seq_file *m, void *data)
+{
+	struct drm_info_node *node = m->private;
+	struct gud_device *gdrm = to_gud_device(node->minor->dev);
+	char buf[10];
+
+	string_get_size(gdrm->bulk_len, 1, STRING_UNITS_2, buf, sizeof(buf));
+	seq_printf(m, "Max buffer size: %s\n", buf);
+	seq_printf(m, "Number of errors:  %u\n", gdrm->stats_num_errors);
+
+	seq_puts(m, "Compression:      ");
+	if (gdrm->compression & GUD_COMPRESSION_LZ4)
+		seq_puts(m, " lz4");
+	seq_puts(m, "\n");
+
+	if (gdrm->compression) {
+		u64 remainder;
+		u64 ratio = div64_u64_rem(gdrm->stats_length, gdrm->stats_actual_length,
+					  &remainder);
+		u64 ratio_frac = div64_u64(remainder * 10, gdrm->stats_actual_length);
+
+		seq_printf(m, "Compression ratio: %llu.%llu\n", ratio, ratio_frac);
+	}
+
+	return 0;
+}
+
+static const struct drm_info_list gud_debugfs_list[] = {
+	{ "stats", gud_stats_debugfs, 0, NULL },
+};
+
+static void gud_debugfs_init(struct drm_minor *minor)
+{
+	drm_debugfs_create_files(gud_debugfs_list, ARRAY_SIZE(gud_debugfs_list),
+				 minor->debugfs_root, minor);
+}
+
+static const struct drm_simple_display_pipe_funcs gud_pipe_funcs = {
+	.check      = gud_pipe_check,
+	.update	    = gud_pipe_update,
+	.prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb,
+};
+
+static const struct drm_mode_config_funcs gud_mode_config_funcs = {
+	.fb_create = drm_gem_fb_create_with_dirty,
+	.atomic_check = drm_atomic_helper_check,
+	.atomic_commit = drm_atomic_helper_commit,
+};
+
+static const u64 gud_pipe_modifiers[] = {
+	DRM_FORMAT_MOD_LINEAR,
+	DRM_FORMAT_MOD_INVALID
+};
+
+DEFINE_DRM_GEM_FOPS(gud_fops);
+
+static const struct drm_driver gud_drm_driver = {
+	.driver_features	= DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
+	.fops			= &gud_fops,
+	DRM_GEM_SHMEM_DRIVER_OPS,
+	.debugfs_init		= gud_debugfs_init,
+
+	.name			= "gud",
+	.desc			= "Generic USB Display",
+	.date			= "20200422",
+	.major			= 1,
+	.minor			= 0,
+};
+
+static void gud_free_buffers_and_mutex(struct drm_device *drm, void *unused)
+{
+	struct gud_device *gdrm = to_gud_device(drm);
+
+	vfree(gdrm->compress_buf);
+	kfree(gdrm->bulk_buf);
+	mutex_destroy(&gdrm->ctrl_lock);
+	mutex_destroy(&gdrm->damage_lock);
+}
+
+static int gud_probe(struct usb_interface *interface, const struct usb_device_id *id)
+{
+	u8 ifnum = interface->cur_altsetting->desc.bInterfaceNumber;
+	struct usb_device *usb = interface_to_usbdev(interface);
+	struct device *dev = &interface->dev;
+	const struct drm_format_info *xrgb8888_emulation_format = NULL;
+	bool rgb565_supported = false, xrgb8888_supported = false;
+	struct usb_endpoint_descriptor *bulk_out;
+	struct gud_display_descriptor_req desc;
+	unsigned int num_formats = 0;
+	struct gud_device *gdrm;
+	size_t max_buffer_size = 0;
+	struct drm_device *drm;
+	u8 *formats_dev;
+	u32 *formats;
+	int ret, i;
+
+	ret = usb_find_bulk_out_endpoint(interface->cur_altsetting, &bulk_out);
+	if (ret)
+		return ret;
+
+	ret = gud_get_display_descriptor(interface, &desc);
+	if (ret) {
+		DRM_DEV_DEBUG_DRIVER(dev, "Not a display interface: ret=%d\n", ret);
+		return -ENODEV;
+	}
+
+	if (desc.version > 1) {
+		ret = gud_set_version(usb, ifnum, le32_to_cpu(desc.flags), 1);
+		if (ret) {
+			if (ret == -EPROTONOSUPPORT)
+				dev_err(dev, "Protocol version %u is not supported\n",
+					desc.version);
+			return ret;
+		}
+	}
+
+	gdrm = devm_drm_dev_alloc(dev, &gud_drm_driver, struct gud_device, drm);
+	if (IS_ERR(gdrm))
+		return PTR_ERR(gdrm);
+
+	drm = &gdrm->drm;
+	drm->mode_config.funcs = &gud_mode_config_funcs;
+	ret = drmm_mode_config_init(drm);
+	if (ret)
+		return ret;
+
+	gdrm->ifnum = ifnum;
+	gdrm->flags = le32_to_cpu(desc.flags);
+	gdrm->compression = desc.compression & GUD_COMPRESSION_LZ4;
+
+	if (gdrm->flags & GUD_DISPLAY_FLAG_FULL_UPDATE && gdrm->compression)
+		return -EINVAL;
+
+	mutex_init(&gdrm->ctrl_lock);
+	mutex_init(&gdrm->damage_lock);
+	INIT_WORK(&gdrm->work, gud_flush_work);
+	gud_clear_damage(gdrm);
+
+	ret = drmm_add_action_or_reset(drm, gud_free_buffers_and_mutex, NULL);
+	if (ret)
+		return ret;
+
+	drm->mode_config.min_width = le32_to_cpu(desc.min_width);
+	drm->mode_config.max_width = le32_to_cpu(desc.max_width);
+	drm->mode_config.min_height = le32_to_cpu(desc.min_height);
+	drm->mode_config.max_height = le32_to_cpu(desc.max_height);
+
+	formats_dev = devm_kmalloc(dev, desc.num_formats, GFP_KERNEL);
+	/* Add room for emulated XRGB8888 */
+	formats = devm_kmalloc_array(dev, desc.num_formats + 1, sizeof(*formats), GFP_KERNEL);
+	if (!formats_dev || !formats)
+		return -ENOMEM;
+
+	ret = gud_usb_get(gdrm, GUD_REQ_GET_FORMATS, 0, formats_dev, desc.num_formats);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < desc.num_formats; i++) {
+		const struct drm_format_info *info;
+		size_t fmt_buf_size;
+		u32 format;
+
+		format = gud_to_fourcc(formats_dev[i]);
+		if (!format) {
+			drm_dbg(drm, "Unsupported format: 0x%02x\n", formats_dev[i]);
+			continue;
+		}
+
+		if (format == GUD_DRM_FORMAT_R1)
+			info = &gud_drm_format_r1;
+		else
+			info = drm_format_info(format);
+
+		switch (format) {
+		case GUD_DRM_FORMAT_R1:
+			xrgb8888_emulation_format = info;
+			break;
+		case DRM_FORMAT_RGB565:
+			rgb565_supported = true;
+			if (!xrgb8888_emulation_format)
+				xrgb8888_emulation_format = info;
+			break;
+		case DRM_FORMAT_XRGB8888:
+			xrgb8888_supported = true;
+			break;
+		};
+
+		fmt_buf_size = drm_format_info_min_pitch(info, 0, drm->mode_config.max_width) *
+			       drm->mode_config.max_height;
+		max_buffer_size = max(max_buffer_size, fmt_buf_size);
+
+		if (format == GUD_DRM_FORMAT_R1)
+			continue; /* Internal not for userspace */
+
+		formats[num_formats++] = format;
+	}
+
+	if (!num_formats && !xrgb8888_emulation_format) {
+		dev_err(dev, "No supported pixel formats found\n");
+		return -EINVAL;
+	}
+
+	/* Prefer speed over color depth */
+	if (rgb565_supported)
+		drm->mode_config.preferred_depth = 16;
+
+	if (!xrgb8888_supported && xrgb8888_emulation_format) {
+		gdrm->xrgb8888_emulation_format = xrgb8888_emulation_format;
+		formats[num_formats++] = DRM_FORMAT_XRGB8888;
+	}
+
+	if (desc.max_buffer_size)
+		max_buffer_size = le32_to_cpu(desc.max_buffer_size);
+retry:
+	/*
+	 * Use plain kmalloc here since devm_kmalloc() places struct devres at the beginning
+	 * of the buffer it allocates. This wastes a lot of memory when allocating big buffers.
+	 * Asking for 2M would actually allocate 4M. This would also prevent getting the biggest
+	 * possible buffer potentially leading to split transfers.
+	 */
+	gdrm->bulk_buf = kmalloc(max_buffer_size, GFP_KERNEL | __GFP_NOWARN);
+	if (!gdrm->bulk_buf) {
+		max_buffer_size = roundup_pow_of_two(max_buffer_size) / 2;
+		if (max_buffer_size < SZ_512K)
+			return -ENOMEM;
+		goto retry;
+	}
+
+	gdrm->bulk_pipe = usb_sndbulkpipe(usb, usb_endpoint_num(bulk_out));
+	gdrm->bulk_len = max_buffer_size;
+
+	if (gdrm->compression & GUD_COMPRESSION_LZ4) {
+		gdrm->lz4_comp_mem = devm_kmalloc(dev, LZ4_MEM_COMPRESS, GFP_KERNEL);
+		if (!gdrm->lz4_comp_mem)
+			return -ENOMEM;
+
+		gdrm->compress_buf = vmalloc(gdrm->bulk_len);
+		if (!gdrm->compress_buf)
+			return -ENOMEM;
+	}
+
+	ret = drm_simple_display_pipe_init(drm, &gdrm->pipe, &gud_pipe_funcs,
+					   formats, num_formats,
+					   gud_pipe_modifiers, NULL);
+	if (ret)
+		return ret;
+
+	devm_kfree(dev, formats);
+	devm_kfree(dev, formats_dev);
+
+	ret = gud_get_properties(gdrm, desc.num_properties);
+	if (ret)
+		return ret;
+
+	drm_plane_enable_fb_damage_clips(&gdrm->pipe.plane);
+
+	for (i = 0; i < desc.num_connectors; i++) {
+		ret = gud_connector_create(gdrm, i);
+		if (ret)
+			return ret;
+	}
+
+	drm_mode_config_reset(drm);
+
+	usb_set_intfdata(interface, gdrm);
+
+	ret = drm_dev_register(drm, 0);
+	if (ret)
+		return ret;
+
+	drm_kms_helper_poll_init(drm);
+
+	drm_fbdev_generic_setup(drm, 0);
+
+	return 0;
+}
+
+static void gud_disconnect(struct usb_interface *interface)
+{
+	struct gud_device *gdrm = usb_get_intfdata(interface);
+	struct drm_device *drm = &gdrm->drm;
+
+	drm_dbg(drm, "%s:\n", __func__);
+
+	drm_kms_helper_poll_fini(drm);
+	drm_dev_unplug(drm);
+	drm_atomic_helper_shutdown(drm);
+}
+
+static int gud_suspend(struct usb_interface *interface, pm_message_t message)
+{
+	struct gud_device *gdrm = usb_get_intfdata(interface);
+
+	return drm_mode_config_helper_suspend(&gdrm->drm);
+}
+
+static int gud_resume(struct usb_interface *interface)
+{
+	struct gud_device *gdrm = usb_get_intfdata(interface);
+
+	drm_mode_config_helper_resume(&gdrm->drm);
+
+	return 0;
+}
+
+static const struct usb_device_id gud_id_table[] = {
+	{ USB_DEVICE_INTERFACE_CLASS(0x1d50, 0x614d, USB_CLASS_VENDOR_SPEC) },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(usb, gud_id_table);
+
+static struct usb_driver gud_usb_driver = {
+	.name		= "gud",
+	.probe		= gud_probe,
+	.disconnect	= gud_disconnect,
+	.id_table	= gud_id_table,
+	.suspend	= gud_suspend,
+	.resume		= gud_resume,
+	.reset_resume	= gud_resume,
+};
+
+module_usb_driver(gud_usb_driver);
+
+MODULE_AUTHOR("Noralf Trønnes");
+MODULE_LICENSE("Dual MIT/GPL");
diff --git a/drivers/gpu/drm/gud/gud_internal.h b/drivers/gpu/drm/gud/gud_internal.h
new file mode 100644
index 000000000000..46e7d7ed2c69
--- /dev/null
+++ b/drivers/gpu/drm/gud/gud_internal.h
@@ -0,0 +1,148 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef __LINUX_GUD_INTERNAL_H
+#define __LINUX_GUD_INTERNAL_H
+
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+#include <uapi/drm/drm_fourcc.h>
+
+#include <drm/drm_modes.h>
+#include <drm/drm_simple_kms_helper.h>
+
+struct gud_device {
+	struct drm_device drm;
+	struct drm_simple_display_pipe pipe;
+	struct work_struct work;
+	u8 ifnum;
+	u32 flags;
+	const struct drm_format_info *xrgb8888_emulation_format;
+
+	u16 *properties;
+	unsigned int num_properties;
+
+	unsigned int bulk_pipe;
+	void *bulk_buf;
+	size_t bulk_len;
+
+	u8 compression;
+	void *lz4_comp_mem;
+	void *compress_buf;
+
+	u64 stats_length;
+	u64 stats_actual_length;
+	unsigned int stats_num_errors;
+
+	struct mutex ctrl_lock; /* Serialize get/set and status transfers */
+
+	struct mutex damage_lock; /* Protects the following members: */
+	struct drm_framebuffer *fb;
+	struct drm_rect damage;
+	bool prev_flush_failed;
+};
+
+static inline struct gud_device *to_gud_device(struct drm_device *drm)
+{
+	return container_of(drm, struct gud_device, drm);
+}
+
+static inline struct usb_device *gud_to_usb_device(struct gud_device *gdrm)
+{
+	return interface_to_usbdev(to_usb_interface(gdrm->drm.dev));
+}
+
+int gud_usb_get(struct gud_device *gdrm, u8 request, u16 index, void *buf, size_t len);
+int gud_usb_set(struct gud_device *gdrm, u8 request, u16 index, void *buf, size_t len);
+int gud_usb_write8(struct gud_device *gdrm, u8 request, u8 val);
+
+void gud_clear_damage(struct gud_device *gdrm);
+void gud_flush_work(struct work_struct *work);
+int gud_pipe_check(struct drm_simple_display_pipe *pipe,
+		   struct drm_plane_state *new_plane_state,
+		   struct drm_crtc_state *new_crtc_state);
+void gud_pipe_update(struct drm_simple_display_pipe *pipe,
+		     struct drm_plane_state *old_state);
+int gud_connector_fill_properties(struct drm_connector *connector,
+				  struct drm_connector_state *connector_state,
+				  struct gud_property_req *properties);
+int gud_connector_create(struct gud_device *gdrm, unsigned int index);
+
+#define GUD_DRM_FORMAT_R1	fourcc_code('R', '1', ' ', ' ')
+
+static inline u8 gud_from_fourcc(u32 fourcc)
+{
+	switch (fourcc) {
+	case GUD_DRM_FORMAT_R1:
+		return GUD_PIXEL_FORMAT_R1;
+	case DRM_FORMAT_RGB565:
+		return GUD_PIXEL_FORMAT_RGB565;
+	case DRM_FORMAT_XRGB8888:
+		return GUD_PIXEL_FORMAT_XRGB8888;
+	case DRM_FORMAT_ARGB8888:
+		return GUD_PIXEL_FORMAT_ARGB8888;
+	};
+
+	return 0;
+}
+
+static inline u32 gud_to_fourcc(u8 format)
+{
+	switch (format) {
+	case GUD_PIXEL_FORMAT_R1:
+		return GUD_DRM_FORMAT_R1;
+	case GUD_PIXEL_FORMAT_RGB565:
+		return DRM_FORMAT_RGB565;
+	case GUD_PIXEL_FORMAT_XRGB8888:
+		return DRM_FORMAT_XRGB8888;
+	case GUD_PIXEL_FORMAT_ARGB8888:
+		return DRM_FORMAT_ARGB8888;
+	};
+
+	return 0;
+}
+
+static inline void gud_from_display_mode(struct gud_display_mode_req *dst,
+					 const struct drm_display_mode *src)
+{
+	u32 flags = src->flags & GUD_DISPLAY_MODE_FLAG_USER_MASK;
+
+	if (src->type & DRM_MODE_TYPE_PREFERRED)
+		flags |= GUD_DISPLAY_MODE_FLAG_PREFERRED;
+
+	dst->clock = cpu_to_le32(src->clock);
+	dst->hdisplay = cpu_to_le16(src->hdisplay);
+	dst->hsync_start = cpu_to_le16(src->hsync_start);
+	dst->hsync_end = cpu_to_le16(src->hsync_end);
+	dst->htotal = cpu_to_le16(src->htotal);
+	dst->vdisplay = cpu_to_le16(src->vdisplay);
+	dst->vsync_start = cpu_to_le16(src->vsync_start);
+	dst->vsync_end = cpu_to_le16(src->vsync_end);
+	dst->vtotal = cpu_to_le16(src->vtotal);
+	dst->flags = cpu_to_le32(flags);
+}
+
+static inline void gud_to_display_mode(struct drm_display_mode *dst,
+				       const struct gud_display_mode_req *src)
+{
+	u32 flags = le32_to_cpu(src->flags);
+
+	memset(dst, 0, sizeof(*dst));
+	dst->clock = le32_to_cpu(src->clock);
+	dst->hdisplay = le16_to_cpu(src->hdisplay);
+	dst->hsync_start = le16_to_cpu(src->hsync_start);
+	dst->hsync_end = le16_to_cpu(src->hsync_end);
+	dst->htotal = le16_to_cpu(src->htotal);
+	dst->vdisplay = le16_to_cpu(src->vdisplay);
+	dst->vsync_start = le16_to_cpu(src->vsync_start);
+	dst->vsync_end = le16_to_cpu(src->vsync_end);
+	dst->vtotal = le16_to_cpu(src->vtotal);
+	dst->flags = flags & GUD_DISPLAY_MODE_FLAG_USER_MASK;
+	dst->type = DRM_MODE_TYPE_DRIVER;
+	if (flags & GUD_DISPLAY_MODE_FLAG_PREFERRED)
+		dst->type |= DRM_MODE_TYPE_PREFERRED;
+	drm_mode_set_name(dst);
+}
+
+#endif
diff --git a/drivers/gpu/drm/gud/gud_pipe.c b/drivers/gpu/drm/gud/gud_pipe.c
new file mode 100644
index 000000000000..1eef930bcf9b
--- /dev/null
+++ b/drivers/gpu/drm/gud/gud_pipe.c
@@ -0,0 +1,472 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2020 Noralf Trønnes
+ */
+
+#include <linux/dma-buf.h>
+#include <linux/lz4.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_damage_helper.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_format_helper.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_gem_shmem_helper.h>
+#include <drm/drm_print.h>
+#include <drm/drm_rect.h>
+#include <drm/drm_simple_kms_helper.h>
+#include <drm/gud.h>
+
+#include "gud_internal.h"
+
+static bool gud_is_big_endian(void)
+{
+#if defined(__BIG_ENDIAN)
+	return true;
+#else
+	return false;
+#endif
+}
+
+static size_t gud_xrgb8888_to_r124(u8 *dst, const struct drm_format_info *format,
+				   void *src, struct drm_framebuffer *fb,
+				   struct drm_rect *rect)
+{
+	unsigned int block_width = drm_format_info_block_width(format, 0);
+	unsigned int bits_per_pixel = 8 / block_width;
+	unsigned int x, y, width, height;
+	u8 *p, *block = dst; /* Assign to silence compiler warning */
+	size_t len;
+	void *buf;
+
+	WARN_ON_ONCE(format->char_per_block[0] != 1);
+
+	/* Start on a byte boundary */
+	rect->x1 = ALIGN_DOWN(rect->x1, block_width);
+	width = drm_rect_width(rect);
+	height = drm_rect_height(rect);
+	len = drm_format_info_min_pitch(format, 0, width) * height;
+
+	buf = kmalloc(width * height, GFP_KERNEL);
+	if (!buf)
+		return 0;
+
+	drm_fb_xrgb8888_to_gray8(buf, src, fb, rect);
+
+	p = buf;
+	for (y = 0; y < drm_rect_height(rect); y++) {
+		for (x = 0; x < drm_rect_width(rect); x++) {
+			if (!(x % block_width)) {
+				block = dst++;
+				*block = 0;
+			}
+
+			*block <<= bits_per_pixel;
+			*block |= (*p++) >> (8 - bits_per_pixel);
+		}
+	}
+
+	kfree(buf);
+
+	return len;
+}
+
+static int gud_prep_flush(struct gud_device *gdrm, struct drm_framebuffer *fb,
+			  const struct drm_format_info *format, struct drm_rect *rect,
+			  struct gud_set_buffer_req *req)
+{
+	struct dma_buf_attachment *import_attach = fb->obj[0]->import_attach;
+	u8 compression = gdrm->compression;
+	struct dma_buf_map map;
+	void *vaddr, *buf;
+	size_t pitch, len;
+	int ret = 0;
+
+	pitch = drm_format_info_min_pitch(format, 0, drm_rect_width(rect));
+	len = pitch * drm_rect_height(rect);
+	if (len > gdrm->bulk_len)
+		return -E2BIG;
+
+	ret = drm_gem_shmem_vmap(fb->obj[0], &map);
+	if (ret)
+		return ret;
+
+	vaddr = map.vaddr + fb->offsets[0];
+
+	if (import_attach) {
+		ret = dma_buf_begin_cpu_access(import_attach->dmabuf, DMA_FROM_DEVICE);
+		if (ret)
+			goto vunmap;
+	}
+retry:
+	if (compression)
+		buf = gdrm->compress_buf;
+	else
+		buf = gdrm->bulk_buf;
+
+	/*
+	 * Imported buffers are assumed to be write-combined and thus uncached
+	 * with slow reads (at least on ARM).
+	 */
+	if (format != fb->format) {
+		if (format->format == GUD_DRM_FORMAT_R1) {
+			len = gud_xrgb8888_to_r124(buf, format, vaddr, fb, rect);
+			if (!len) {
+				ret = -ENOMEM;
+				goto end_cpu_access;
+			}
+		} else if (format->format == DRM_FORMAT_RGB565) {
+			drm_fb_xrgb8888_to_rgb565(buf, vaddr, fb, rect, gud_is_big_endian());
+		}
+	} else if (gud_is_big_endian() && format->cpp[0] > 1) {
+		drm_fb_swab(buf, vaddr, fb, rect, !import_attach);
+	} else if (compression && !import_attach && pitch == fb->pitches[0]) {
+		/* can compress directly from the framebuffer */
+		buf = vaddr + rect->y1 * pitch;
+	} else {
+		drm_fb_memcpy(buf, vaddr, fb, rect);
+	}
+
+	memset(req, 0, sizeof(*req));
+	req->x = cpu_to_le32(rect->x1);
+	req->y = cpu_to_le32(rect->y1);
+	req->width = cpu_to_le32(drm_rect_width(rect));
+	req->height = cpu_to_le32(drm_rect_height(rect));
+	req->length = cpu_to_le32(len);
+
+	if (compression & GUD_COMPRESSION_LZ4) {
+		int complen;
+
+		complen = LZ4_compress_default(buf, gdrm->bulk_buf, len, len, gdrm->lz4_comp_mem);
+		if (complen < 0) {
+			compression = 0;
+			goto retry;
+		}
+
+		req->compression = GUD_COMPRESSION_LZ4;
+		req->compressed_length = cpu_to_le32(complen);
+	}
+
+end_cpu_access:
+	if (import_attach)
+		dma_buf_end_cpu_access(import_attach->dmabuf, DMA_FROM_DEVICE);
+vunmap:
+	drm_gem_shmem_vunmap(fb->obj[0], &map);
+
+	return ret;
+}
+
+static int gud_flush_rect(struct gud_device *gdrm, struct drm_framebuffer *fb,
+			  const struct drm_format_info *format, struct drm_rect *rect)
+{
+	struct usb_device *usb = gud_to_usb_device(gdrm);
+	struct gud_set_buffer_req req;
+	int ret, actual_length;
+	size_t len, trlen;
+
+	drm_dbg(&gdrm->drm, "Flushing [FB:%d] " DRM_RECT_FMT "\n", fb->base.id, DRM_RECT_ARG(rect));
+
+	ret = gud_prep_flush(gdrm, fb, format, rect, &req);
+	if (ret)
+		return ret;
+
+	len = le32_to_cpu(req.length);
+
+	if (req.compression)
+		trlen = le32_to_cpu(req.compressed_length);
+	else
+		trlen = len;
+
+	gdrm->stats_length += len;
+	/* Did it wrap around? */
+	if (gdrm->stats_length <= len && gdrm->stats_actual_length) {
+		gdrm->stats_length = len;
+		gdrm->stats_actual_length = 0;
+	}
+	gdrm->stats_actual_length += trlen;
+
+	if (!(gdrm->flags & GUD_DISPLAY_FLAG_FULL_UPDATE) || gdrm->prev_flush_failed) {
+		ret = gud_usb_set(gdrm, GUD_REQ_SET_BUFFER, 0, &req, sizeof(req));
+		if (ret)
+			return ret;
+	}
+
+	ret = usb_bulk_msg(usb, gdrm->bulk_pipe, gdrm->bulk_buf, trlen,
+			   &actual_length, msecs_to_jiffies(3000));
+	if (!ret && trlen != actual_length)
+		ret = -EIO;
+	if (ret)
+		gdrm->stats_num_errors++;
+
+	return ret;
+}
+
+void gud_clear_damage(struct gud_device *gdrm)
+{
+	gdrm->damage.x1 = INT_MAX;
+	gdrm->damage.y1 = INT_MAX;
+	gdrm->damage.x2 = 0;
+	gdrm->damage.y2 = 0;
+}
+
+static void gud_add_damage(struct gud_device *gdrm, struct drm_rect *damage)
+{
+	gdrm->damage.x1 = min(gdrm->damage.x1, damage->x1);
+	gdrm->damage.y1 = min(gdrm->damage.y1, damage->y1);
+	gdrm->damage.x2 = max(gdrm->damage.x2, damage->x2);
+	gdrm->damage.y2 = max(gdrm->damage.y2, damage->y2);
+}
+
+static void gud_retry_failed_flush(struct gud_device *gdrm, struct drm_framebuffer *fb,
+				   struct drm_rect *damage)
+{
+	/*
+	 * pipe_update waits for the worker when the display mode is going to change.
+	 * This ensures that the width and height is still the same making it safe to
+	 * add back the damage.
+	 */
+
+	mutex_lock(&gdrm->damage_lock);
+	if (!gdrm->fb) {
+		drm_framebuffer_get(fb);
+		gdrm->fb = fb;
+	}
+	gud_add_damage(gdrm, damage);
+	mutex_unlock(&gdrm->damage_lock);
+
+	/* Retry only once to avoid a possible storm in case of continues errors. */
+	if (!gdrm->prev_flush_failed)
+		queue_work(system_long_wq, &gdrm->work);
+	gdrm->prev_flush_failed = true;
+}
+
+void gud_flush_work(struct work_struct *work)
+{
+	struct gud_device *gdrm = container_of(work, struct gud_device, work);
+	const struct drm_format_info *format;
+	struct drm_framebuffer *fb;
+	struct drm_rect damage;
+	unsigned int i, lines;
+	int idx, ret = 0;
+	size_t pitch;
+
+	if (!drm_dev_enter(&gdrm->drm, &idx))
+		return;
+
+	mutex_lock(&gdrm->damage_lock);
+	fb = gdrm->fb;
+	gdrm->fb = NULL;
+	damage = gdrm->damage;
+	gud_clear_damage(gdrm);
+	mutex_unlock(&gdrm->damage_lock);
+
+	if (!fb)
+		goto out;
+
+	format = fb->format;
+	if (format->format == DRM_FORMAT_XRGB8888 && gdrm->xrgb8888_emulation_format)
+		format = gdrm->xrgb8888_emulation_format;
+
+	/* Split update if it's too big */
+	pitch = drm_format_info_min_pitch(format, 0, drm_rect_width(&damage));
+	lines = drm_rect_height(&damage);
+
+	if (gdrm->bulk_len < lines * pitch)
+		lines = gdrm->bulk_len / pitch;
+
+	for (i = 0; i < DIV_ROUND_UP(drm_rect_height(&damage), lines); i++) {
+		struct drm_rect rect = damage;
+
+		rect.y1 += i * lines;
+		rect.y2 = min_t(u32, rect.y1 + lines, damage.y2);
+
+		ret = gud_flush_rect(gdrm, fb, format, &rect);
+		if (ret) {
+			if (ret != -ENODEV && ret != -ECONNRESET &&
+			    ret != -ESHUTDOWN && ret != -EPROTO) {
+				gud_retry_failed_flush(gdrm, fb, &damage);
+				dev_err_once(fb->dev->dev,
+					     "Failed to flush framebuffer: error=%d\n", ret);
+			}
+			break;
+		}
+
+		gdrm->prev_flush_failed = false;
+	}
+
+	drm_framebuffer_put(fb);
+out:
+	drm_dev_exit(idx);
+}
+
+static void gud_fb_queue_damage(struct gud_device *gdrm, struct drm_framebuffer *fb,
+				struct drm_rect *damage)
+{
+	struct drm_framebuffer *old_fb = NULL;
+
+	mutex_lock(&gdrm->damage_lock);
+
+	if (fb != gdrm->fb) {
+		old_fb = gdrm->fb;
+		drm_framebuffer_get(fb);
+		gdrm->fb = fb;
+	}
+
+	gud_add_damage(gdrm, damage);
+
+	mutex_unlock(&gdrm->damage_lock);
+
+	queue_work(system_long_wq, &gdrm->work);
+
+	if (old_fb)
+		drm_framebuffer_put(old_fb);
+}
+
+int gud_pipe_check(struct drm_simple_display_pipe *pipe,
+		   struct drm_plane_state *new_plane_state,
+		   struct drm_crtc_state *new_crtc_state)
+{
+	struct gud_device *gdrm = to_gud_device(pipe->crtc.dev);
+	struct drm_plane_state *old_plane_state = pipe->plane.state;
+	const struct drm_display_mode *mode = &new_crtc_state->mode;
+	struct drm_atomic_state *state = new_plane_state->state;
+	struct drm_framebuffer *old_fb = old_plane_state->fb;
+	struct drm_connector_state *connector_state = NULL;
+	struct drm_framebuffer *fb = new_plane_state->fb;
+	const struct drm_format_info *format = fb->format;
+	struct drm_connector *connector;
+	int idx, ret, num_properties;
+	struct gud_state_req *req;
+	unsigned int i;
+	size_t len;
+
+	if (WARN_ON_ONCE(!fb))
+		return -EINVAL;
+
+	if (old_plane_state->rotation != new_plane_state->rotation)
+		new_crtc_state->mode_changed = true;
+
+	if (old_fb && old_fb->format != format)
+		new_crtc_state->mode_changed = true;
+
+	if (!new_crtc_state->mode_changed && !new_crtc_state->connectors_changed)
+		return 0;
+
+	/* Only one connector is supported */
+	if (hweight32(new_crtc_state->connector_mask) != 1)
+		return -EINVAL;
+
+	if (format->format == DRM_FORMAT_XRGB8888 && gdrm->xrgb8888_emulation_format)
+		format = gdrm->xrgb8888_emulation_format;
+
+	for_each_new_connector_in_state(state, connector, connector_state, i) {
+		if (connector_state->crtc)
+			break;
+	}
+
+	if (WARN_ON_ONCE(!connector_state))
+		return -ENOENT;
+
+	num_properties = gud_connector_fill_properties(connector, NULL, NULL);
+	if (num_properties < 0)
+		return num_properties;
+
+	num_properties += gdrm->num_properties;
+
+	len = struct_size(req, properties, num_properties);
+	req = kzalloc(len, GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	gud_from_display_mode(&req->mode, mode);
+
+	req->format = gud_from_fourcc(format->format);
+	if (WARN_ON_ONCE(!req->format))
+		return -EINVAL;
+
+	req->connector = drm_connector_index(connector);
+	req->num_properties = num_properties;
+
+	num_properties = gud_connector_fill_properties(connector, connector_state,
+						       req->properties);
+
+	for (i = 0; i < gdrm->num_properties; i++) {
+		u16 prop = gdrm->properties[i];
+		u64 val;
+
+		switch (prop) {
+		case GUD_PROPERTY_ROTATION:
+			/* DRM UAPI matches the protocol so use value directly */
+			val = new_plane_state->rotation;
+			break;
+		default:
+			WARN_ON_ONCE(1);
+			ret = -EINVAL;
+			goto out;
+		}
+
+		req->properties[num_properties + i].prop = cpu_to_le16(prop);
+		req->properties[num_properties + i].val = cpu_to_le64(val);
+	}
+
+	if (drm_dev_enter(fb->dev, &idx)) {
+		ret = gud_usb_set(gdrm, GUD_REQ_SET_STATE_CHECK, 0, req, len);
+		drm_dev_exit(idx);
+	}  else {
+		ret = -ENODEV;
+	}
+out:
+	kfree(req);
+
+	return ret;
+}
+
+void gud_pipe_update(struct drm_simple_display_pipe *pipe,
+		     struct drm_plane_state *old_state)
+{
+	struct drm_device *drm = pipe->crtc.dev;
+	struct gud_device *gdrm = to_gud_device(drm);
+	struct drm_plane_state *state = pipe->plane.state;
+	struct drm_framebuffer *fb = state->fb;
+	struct drm_crtc *crtc = &pipe->crtc;
+	struct drm_rect damage;
+	int idx;
+
+	if (crtc->state->mode_changed || !crtc->state->enable) {
+		cancel_work_sync(&gdrm->work);
+		mutex_lock(&gdrm->damage_lock);
+		if (gdrm->fb) {
+			drm_framebuffer_put(gdrm->fb);
+			gdrm->fb = NULL;
+		}
+		gud_clear_damage(gdrm);
+		mutex_unlock(&gdrm->damage_lock);
+	}
+
+	if (!drm_dev_enter(drm, &idx))
+		return;
+
+	if (!old_state->fb)
+		gud_usb_write8(gdrm, GUD_REQ_SET_CONTROLLER_ENABLE, 1);
+
+	if (fb && (crtc->state->mode_changed || crtc->state->connectors_changed))
+		gud_usb_set(gdrm, GUD_REQ_SET_STATE_COMMIT, 0, NULL, 0);
+
+	if (crtc->state->active_changed)
+		gud_usb_write8(gdrm, GUD_REQ_SET_DISPLAY_ENABLE, crtc->state->active);
+
+	if (drm_atomic_helper_damage_merged(old_state, state, &damage)) {
+		if (gdrm->flags & GUD_DISPLAY_FLAG_FULL_UPDATE)
+			drm_rect_init(&damage, 0, 0, fb->width, fb->height);
+		gud_fb_queue_damage(gdrm, fb, &damage);
+	}
+
+	if (!crtc->state->enable)
+		gud_usb_write8(gdrm, GUD_REQ_SET_CONTROLLER_ENABLE, 0);
+
+	drm_dev_exit(idx);
+}
diff --git a/include/drm/gud.h b/include/drm/gud.h
new file mode 100644
index 000000000000..ebf19f50f780
--- /dev/null
+++ b/include/drm/gud.h
@@ -0,0 +1,356 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright 2020 Noralf Trønnes
+ */
+
+#ifndef __LINUX_GUD_H
+#define __LINUX_GUD_H
+
+#include <linux/types.h>
+
+/*
+ * struct gud_display_descriptor_req - Display descriptor
+ * @magic: Magic value GUD_DISPLAY_MAGIC
+ * @version: Protocol version
+ * @flags: Flags
+ *         - STATUS_ON_SET: Always do a status request after a SET request.
+ *                          This is used by the Linux gadget driver since it has
+ *                          no way to control the status stage of a control OUT
+ *                          request that has a payload.
+ *         - FULL_UPDATE:   Always send the entire framebuffer when flushing changes.
+ *                          The GUD_REQ_SET_BUFFER request will not be sent
+ *                          before each bulk transfer, it will only be sent if the
+ *                          previous bulk transfer had failed. This gives the device
+ *                          a chance to reset its state machine if needed.
+ *                          This flag can not be used in combination with compression.
+ * @compression: Supported compression types
+ *               - GUD_COMPRESSION_LZ4: LZ4 lossless compression.
+ * @max_buffer_size: Maximum buffer size the device can handle (optional).
+ *                   This is useful for devices that don't have a big enough
+ *                   buffer to decompress the entire framebuffer in one go.
+ * @min_width: Minimum pixel width the controller can handle
+ * @max_width: Maximum width
+ * @min_height: Minimum height
+ * @max_height: Maximum height
+ * @num_formats: Number of supported pixel formats
+ * @num_properties: Number of properties that are not connector properties
+ * @num_connectors: Number of connectors
+ *
+ * Devices that have only one display mode will have min_width == max_width
+ * and min_height == max_height.
+ */
+struct gud_display_descriptor_req {
+	__u32 magic;
+#define GUD_DISPLAY_MAGIC			0x1d50614d
+	__u8 version;
+	__le32 flags;
+#define GUD_DISPLAY_FLAG_STATUS_ON_SET		BIT(0)
+#define GUD_DISPLAY_FLAG_FULL_UPDATE		BIT(1)
+	__u8 compression;
+#define GUD_COMPRESSION_LZ4			BIT(0)
+	__le32 max_buffer_size;
+	__le32 min_width;
+	__le32 max_width;
+	__le32 min_height;
+	__le32 max_height;
+	__u8 num_formats;
+	__u8 num_properties;
+	__u8 num_connectors;
+} __packed;
+
+/*
+ * struct gud_property_req - Property
+ * @prop: Property
+ * @val: Value
+ */
+struct gud_property_req {
+	__le16 prop;
+	__le64 val;
+} __packed;
+
+/*
+ * struct gud_display_mode_req - Display mode
+ * @clock: Pixel clock in kHz
+ * @hdisplay: Horizontal display size
+ * @hsync_start: Horizontal sync start
+ * @hsync_end: Horizontal sync end
+ * @htotal: Horizontal total size
+ * @vdisplay: Vertical display size
+ * @vsync_start: Vertical sync start
+ * @vsync_end: Vertical sync end
+ * @vtotal: Vertical total size
+ * @flags: Bits 0-13 are the same as in the RandR protocol and also what DRM uses.
+ *         The deprecated bits are reused for internal protocol flags leaving us
+ *         free to follow DRM for the other bits in the future.
+ *         - FLAG_PREFERRED: Set on the preferred display mode.
+ */
+struct gud_display_mode_req {
+	__le32 clock;
+	__le16 hdisplay;
+	__le16 hsync_start;
+	__le16 hsync_end;
+	__le16 htotal;
+	__le16 vdisplay;
+	__le16 vsync_start;
+	__le16 vsync_end;
+	__le16 vtotal;
+	__le32 flags;
+#define GUD_DISPLAY_MODE_FLAG_PHSYNC		BIT(0)
+#define GUD_DISPLAY_MODE_FLAG_NHSYNC		BIT(1)
+#define GUD_DISPLAY_MODE_FLAG_PVSYNC		BIT(2)
+#define GUD_DISPLAY_MODE_FLAG_NVSYNC		BIT(3)
+#define GUD_DISPLAY_MODE_FLAG_INTERLACE		BIT(4)
+#define GUD_DISPLAY_MODE_FLAG_DBLSCAN		BIT(5)
+#define GUD_DISPLAY_MODE_FLAG_CSYNC		BIT(6)
+#define GUD_DISPLAY_MODE_FLAG_PCSYNC		BIT(7)
+#define GUD_DISPLAY_MODE_FLAG_NCSYNC		BIT(8)
+#define GUD_DISPLAY_MODE_FLAG_HSKEW		BIT(9)
+/* BCast and PixelMultiplex are deprecated */
+#define GUD_DISPLAY_MODE_FLAG_DBLCLK		BIT(12)
+#define GUD_DISPLAY_MODE_FLAG_CLKDIV2		BIT(13)
+#define GUD_DISPLAY_MODE_FLAG_USER_MASK		\
+		(GUD_DISPLAY_MODE_FLAG_PHSYNC | GUD_DISPLAY_MODE_FLAG_NHSYNC | \
+		GUD_DISPLAY_MODE_FLAG_PVSYNC | GUD_DISPLAY_MODE_FLAG_NVSYNC | \
+		GUD_DISPLAY_MODE_FLAG_INTERLACE | GUD_DISPLAY_MODE_FLAG_DBLSCAN | \
+		GUD_DISPLAY_MODE_FLAG_CSYNC | GUD_DISPLAY_MODE_FLAG_PCSYNC | \
+		GUD_DISPLAY_MODE_FLAG_NCSYNC | GUD_DISPLAY_MODE_FLAG_HSKEW | \
+		GUD_DISPLAY_MODE_FLAG_DBLCLK | GUD_DISPLAY_MODE_FLAG_CLKDIV2)
+/* Internal protocol flags */
+#define GUD_DISPLAY_MODE_FLAG_PREFERRED		BIT(10)
+} __packed;
+
+/*
+ * struct gud_connector_descriptor_req - Connector descriptor
+ * @connector_type: Connector type (GUD_CONNECTOR_TYPE_*).
+ *                  If the host doesn't support the type it should fall back to PANEL.
+ * @flags: Flags
+ *         - POLL_STATUS: Connector status can change (polled every 10 seconds)
+ *         - INTERLACE: Interlaced modes are supported
+ *         - DOUBLESCAN: Doublescan modes are supported
+ * @num_properties: Number of supported properties
+ */
+struct gud_connector_descriptor_req {
+	__u8 connector_type;
+#define GUD_CONNECTOR_TYPE_PANEL		0
+#define GUD_CONNECTOR_TYPE_VGA			1
+#define GUD_CONNECTOR_TYPE_COMPOSITE		2
+#define GUD_CONNECTOR_TYPE_SVIDEO		3
+#define GUD_CONNECTOR_TYPE_COMPONENT		4
+#define GUD_CONNECTOR_TYPE_DVI			5
+#define GUD_CONNECTOR_TYPE_DISPLAYPORT		6
+#define GUD_CONNECTOR_TYPE_HDMI			7
+	__le32 flags;
+#define GUD_CONNECTOR_FLAGS_POLL_STATUS		BIT(0)
+#define GUD_CONNECTOR_FLAGS_INTERLACE		BIT(1)
+#define GUD_CONNECTOR_FLAGS_DOUBLESCAN		BIT(2)
+	__u8 num_properties;
+} __packed;
+
+/*
+ * struct gud_connector_status_req - Connector status
+ * @status: Status
+ *          - DISCONNECTED: Connector is disconnected
+ *          - CONNECTED: Connector is connected
+ *          - UNKNOWN: Connection status is unknown
+ *          Flags:
+ *          - CHANGED: A change has happened since the last request
+ * @num_modes: Number of available display modes
+ * @edid_len: Length of EDID data
+ *
+ * If @num_modes is zero, EDID is used to create display modes.
+ * If both @num_modes and @edid_len are set, EDID is just passed on to userspace
+ * in the EDID connector property.
+ *
+ * Userspace will get a HOTPLUG uevent if one of the following is true:
+ * - Connection status has changed
+ * - @num_modes or @edid_len has changed
+ * - CHANGED is set
+ */
+struct gud_connector_status_req {
+	__u8 status;
+#define GUD_CONNECTOR_STATUS_DISCONNECTED	0x00
+#define GUD_CONNECTOR_STATUS_CONNECTED		0x01
+#define GUD_CONNECTOR_STATUS_UNKNOWN		0x02
+#define GUD_CONNECTOR_STATUS_CONNECTED_MASK	0x03
+#define GUD_CONNECTOR_STATUS_CHANGED		BIT(7)
+	__le16 num_modes;
+	__le16 edid_len;
+} __packed;
+
+/*
+ * struct gud_set_buffer_req - Set buffer transfer info
+ * @x: X position of rectangle
+ * @y: Y position
+ * @width: Pixel width of rectangle
+ * @height: Pixel height
+ * @length: Buffer length in bytes
+ * @compression: Transfer compression
+ * @compressed_length: Compressed buffer length
+ *
+ * This request is issued right before the bulk transfer.
+ * @x, @y, @width and @height specifies the rectangle where the buffer should be
+ * placed inside the framebuffer.
+ */
+struct gud_set_buffer_req {
+	__le32 x;
+	__le32 y;
+	__le32 width;
+	__le32 height;
+	__le32 length;
+	__u8 compression;
+	__le32 compressed_length;
+} __packed;
+
+/*
+ * struct gud_state_req - Display state
+ * @mode: Display mode
+ * @format: Pixel format GUD_PIXEL_FORMAT_*
+ * @connector: Connector index
+ * @num_properties: Number of properties in the state
+ * @properties: Array of properties
+ *
+ * The entire state is transferred each time there's a change.
+ */
+struct gud_state_req {
+	struct gud_display_mode_req mode;
+	__u8 format;
+	__u8 connector;
+	__u8 num_properties;
+	struct gud_property_req properties[];
+} __packed;
+
+/* List of supported connector properties: */
+
+/* Margins in pixels to deal with overscan, range 0-100 */
+#define GUD_PROPERTY_TV_LEFT_MARGIN			1
+#define GUD_PROPERTY_TV_RIGHT_MARGIN			2
+#define GUD_PROPERTY_TV_TOP_MARGIN			3
+#define GUD_PROPERTY_TV_BOTTOM_MARGIN			4
+/* Number of modes are placed at _SHIFT in val on retrieval */
+#define GUD_PROPERTY_TV_MODE				5
+  #define GUD_CONNECTOR_TV_MODE_NUM_SHIFT	16
+/* Brightness in percent, range 0-100 */
+#define GUD_PROPERTY_TV_BRIGHTNESS			6
+/* Contrast in percent, range 0-100 */
+#define GUD_PROPERTY_TV_CONTRAST			7
+/* Flicker reduction in percent, range 0-100 */
+#define GUD_PROPERTY_TV_FLICKER_REDUCTION		8
+/* Overscan in percent, range 0-100 */
+#define GUD_PROPERTY_TV_OVERSCAN			9
+/* Saturation in percent, range 0-100 */
+#define GUD_PROPERTY_TV_SATURATION			10
+/* Hue in percent, range 0-100 */
+#define GUD_PROPERTY_TV_HUE				11
+
+/*
+ * Backlight brightness is in the range 0-100 inclusive. The value represents the human perceptual
+ * brightness and not a linear PWM value. 0 is minimum brightness which should not turn the
+ * backlight completely off. The DPMS connector property should be used to control power which will
+ * trigger a GUD_REQ_SET_DISPLAY_ENABLE request.
+ *
+ * This does not map to a DRM property, it is used with the backlight device.
+ */
+#define GUD_PROPERTY_BACKLIGHT_BRIGHTNESS		12
+
+/* List of supported properties that are not connector propeties: */
+
+/*
+ * Plane rotation. Should return the supported bitmask on
+ * GUD_REQ_GET_PROPERTIES. GUD_ROTATION_0 is mandatory.
+ *
+ * Note: This is not display rotation so 90/270 will need scaling to make it fit (unless squared).
+ */
+#define GUD_PROPERTY_ROTATION				50
+  #define GUD_ROTATION_0			BIT(0)
+  #define GUD_ROTATION_90			BIT(1)
+  #define GUD_ROTATION_180			BIT(2)
+  #define GUD_ROTATION_270			BIT(3)
+  #define GUD_ROTATION_REFLECT_X		BIT(4)
+  #define GUD_ROTATION_REFLECT_Y		BIT(5)
+  #define GUD_ROTATION_MASK			(GUD_ROTATION_0 | GUD_ROTATION_90 | \
+						GUD_ROTATION_180 | GUD_ROTATION_270 | \
+						GUD_ROTATION_REFLECT_X | GUD_ROTATION_REFLECT_Y)
+
+/* USB Control requests: */
+
+/* Get status from the last GET/SET control request. Value is u8. */
+#define GUD_REQ_GET_STATUS				0x00
+  /* Status values: */
+  #define GUD_STATUS_OK				0x00
+  #define GUD_STATUS_BUSY			0x01
+  #define GUD_STATUS_REQUEST_NOT_SUPPORTED	0x02
+  #define GUD_STATUS_PROTOCOL_ERROR		0x03
+  #define GUD_STATUS_INVALID_PARAMETER		0x04
+  #define GUD_STATUS_ERROR			0x05
+
+/* Get display descriptor as a &gud_display_descriptor_req */
+#define GUD_REQ_GET_DESCRIPTOR				0x01
+
+/*
+ * If the host driver doesn't support the device protocol version it will send the versions it
+ * supports starting with the latest. If the device isn't backwards compatible or doesn't support
+ * the version the host suggests, it shall stall the request. The version is sent as u8.
+ */
+#define GUD_REQ_SET_VERSION				0x30
+
+/* Get supported pixel formats as a byte array of GUD_PIXEL_FORMAT_* */
+#define GUD_REQ_GET_FORMATS				0x40
+  /* R1 is a 1-bit monochrome transfer format presented to userspace as XRGB8888 */
+  #define GUD_PIXEL_FORMAT_R1			0x01
+  #define GUD_PIXEL_FORMAT_RGB565		0x40
+  #define GUD_PIXEL_FORMAT_XRGB8888		0x80
+  #define GUD_PIXEL_FORMAT_ARGB8888		0x81
+
+/*
+ * Get supported properties that are not connector propeties as a &gud_property_req array.
+ * gud_property_req.val often contains the initial value for the property.
+ */
+#define GUD_REQ_GET_PROPERTIES				0x41
+
+/* Connector requests have the connector index passed in the wValue field */
+
+/* Get connector descriptor as a &gud_connector_descriptor_req */
+#define GUD_REQ_GET_CONNECTOR				0x50
+
+/*
+ * Get properties supported by the connector as a &gud_property_req array.
+ * gud_property_req.val often contains the initial value for the property.
+ */
+#define GUD_REQ_GET_CONNECTOR_PROPERTIES		0x51
+
+/*
+ * Issued when there's a TV_MODE property present.
+ * Gets an array of the supported TV_MODE names each entry of length
+ * GUD_CONNECTOR_TV_MODE_NAME_LEN. Names must be NUL-terminated.
+ */
+#define GUD_REQ_GET_CONNECTOR_TV_MODE_VALUES		0x52
+  #define GUD_CONNECTOR_TV_MODE_NAME_LEN	16
+
+/* When userspace checks connector status, this is issued first, not used for poll requests. */
+#define GUD_REQ_SET_CONNECTOR_FORCE_DETECT		0x53
+
+/* Get connector status as &gud_connector_status_req. */
+#define GUD_REQ_GET_CONNECTOR_STATUS			0x54
+
+/* Get &gud_display_mode_req array of supported display modes */
+#define GUD_REQ_GET_CONNECTOR_MODES			0x55
+
+/* Get Extended Display Identification Data */
+#define GUD_REQ_GET_CONNECTOR_EDID			0x56
+
+/* Set buffer properties before bulk transfer as &gud_set_buffer_req */
+#define GUD_REQ_SET_BUFFER				0x60
+
+/* Check display configuration as &gud_state_req */
+#define GUD_REQ_SET_STATE_CHECK				0x61
+
+/* Apply the previous STATE_CHECK configuration */
+#define GUD_REQ_SET_STATE_COMMIT			0x62
+
+/* Enable/disable the display controller, value is u8: 0/1 */
+#define GUD_REQ_SET_CONTROLLER_ENABLE			0x63
+
+/* Enable/disable display/output (DPMS), value is u8: 0/1 */
+#define GUD_REQ_SET_DISPLAY_ENABLE			0x64
+
+#endif
-- 
2.23.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 2/3] drm/probe-helper: Check epoch counter in output_poll_execute()
  2021-01-20 17:00   ` Noralf Trønnes
@ 2021-01-20 17:41     ` Daniel Vetter
  -1 siblings, 0 replies; 53+ messages in thread
From: Daniel Vetter @ 2021-01-20 17:41 UTC (permalink / raw)
  To: Noralf Trønnes
  Cc: dri-devel, hudson, markus, Sam Ravnborg, USB list, Tyler Hardin,
	Lubomir Rintel, pontus.fuchs, peter

On Wed, Jan 20, 2021 at 6:10 PM Noralf Trønnes <noralf@tronnes.org> wrote:
>
> drm_helper_hpd_irq_event() checks the epoch counter to determine
> connector status change. This was introduced in
> commit 5186421cbfe2 ("drm: Introduce epoch counter to drm_connector").
> Do the same for output_poll_execute() so it can detect other changes
> beside connection status value changes.
>
> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
> ---
>  drivers/gpu/drm/drm_probe_helper.c | 7 ++++++-
>  1 file changed, 6 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c
> index d6017726cc2a..e5432dcf6999 100644
> --- a/drivers/gpu/drm/drm_probe_helper.c
> +++ b/drivers/gpu/drm/drm_probe_helper.c
> @@ -623,6 +623,7 @@ static void output_poll_execute(struct work_struct *work)
>         struct drm_connector_list_iter conn_iter;
>         enum drm_connector_status old_status;
>         bool repoll = false, changed;
> +       u64 old_epoch_counter;
>
>         if (!dev->mode_config.poll_enabled)
>                 return;
> @@ -659,8 +660,9 @@ static void output_poll_execute(struct work_struct *work)
>
>                 repoll = true;
>
> +               old_epoch_counter = connector->epoch_counter;
>                 connector->status = drm_helper_probe_detect(connector, NULL, false);
> -               if (old_status != connector->status) {
Was about to ask whether we're not missing connector status changes
here, but that's already handled. Not sure why this wasn't done as
part of the original patch, I'd include a cc: stable here.

Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>

> +               if (old_epoch_counter != connector->epoch_counter) {
>                         const char *old, *new;
>
>                         /*
> @@ -689,6 +691,9 @@ static void output_poll_execute(struct work_struct *work)
>                                       connector->base.id,
>                                       connector->name,
>                                       old, new);
> +                       DRM_DEBUG_KMS("[CONNECTOR:%d:%s] epoch counter %llu -> %llu\n",
> +                                     connector->base.id, connector->name,
> +                                     old_epoch_counter, connector->epoch_counter);
>
>                         changed = true;
>                 }
> --
> 2.23.0
>
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel



-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 2/3] drm/probe-helper: Check epoch counter in output_poll_execute()
@ 2021-01-20 17:41     ` Daniel Vetter
  0 siblings, 0 replies; 53+ messages in thread
From: Daniel Vetter @ 2021-01-20 17:41 UTC (permalink / raw)
  To: Noralf Trønnes
  Cc: hudson, markus, peter, USB list, dri-devel, Tyler Hardin,
	Lubomir Rintel, pontus.fuchs, Sam Ravnborg

On Wed, Jan 20, 2021 at 6:10 PM Noralf Trønnes <noralf@tronnes.org> wrote:
>
> drm_helper_hpd_irq_event() checks the epoch counter to determine
> connector status change. This was introduced in
> commit 5186421cbfe2 ("drm: Introduce epoch counter to drm_connector").
> Do the same for output_poll_execute() so it can detect other changes
> beside connection status value changes.
>
> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
> ---
>  drivers/gpu/drm/drm_probe_helper.c | 7 ++++++-
>  1 file changed, 6 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c
> index d6017726cc2a..e5432dcf6999 100644
> --- a/drivers/gpu/drm/drm_probe_helper.c
> +++ b/drivers/gpu/drm/drm_probe_helper.c
> @@ -623,6 +623,7 @@ static void output_poll_execute(struct work_struct *work)
>         struct drm_connector_list_iter conn_iter;
>         enum drm_connector_status old_status;
>         bool repoll = false, changed;
> +       u64 old_epoch_counter;
>
>         if (!dev->mode_config.poll_enabled)
>                 return;
> @@ -659,8 +660,9 @@ static void output_poll_execute(struct work_struct *work)
>
>                 repoll = true;
>
> +               old_epoch_counter = connector->epoch_counter;
>                 connector->status = drm_helper_probe_detect(connector, NULL, false);
> -               if (old_status != connector->status) {
Was about to ask whether we're not missing connector status changes
here, but that's already handled. Not sure why this wasn't done as
part of the original patch, I'd include a cc: stable here.

Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>

> +               if (old_epoch_counter != connector->epoch_counter) {
>                         const char *old, *new;
>
>                         /*
> @@ -689,6 +691,9 @@ static void output_poll_execute(struct work_struct *work)
>                                       connector->base.id,
>                                       connector->name,
>                                       old, new);
> +                       DRM_DEBUG_KMS("[CONNECTOR:%d:%s] epoch counter %llu -> %llu\n",
> +                                     connector->base.id, connector->name,
> +                                     old_epoch_counter, connector->epoch_counter);
>
>                         changed = true;
>                 }
> --
> 2.23.0
>
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel



-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 1/3] drm/uapi: Add USB connector type
  2021-01-20 17:00   ` Noralf Trønnes
@ 2021-01-20 17:42     ` Daniel Vetter
  -1 siblings, 0 replies; 53+ messages in thread
From: Daniel Vetter @ 2021-01-20 17:42 UTC (permalink / raw)
  To: Noralf Trønnes
  Cc: dri-devel, hudson, markus, Sam Ravnborg, USB list, Tyler Hardin,
	Lubomir Rintel, pontus.fuchs, peter

On Wed, Jan 20, 2021 at 6:10 PM Noralf Trønnes <noralf@tronnes.org> wrote:
>
> Add a connector type for USB connected display panels.
>
> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
> ---
>  include/uapi/drm/drm_mode.h | 1 +
>  1 file changed, 1 insertion(+)
>
> diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
> index fed66a03c7ae..33024cc5d26e 100644
> --- a/include/uapi/drm/drm_mode.h
> +++ b/include/uapi/drm/drm_mode.h
> @@ -367,6 +367,7 @@ enum drm_mode_subconnector {
>  #define DRM_MODE_CONNECTOR_DPI         17
>  #define DRM_MODE_CONNECTOR_WRITEBACK   18
>  #define DRM_MODE_CONNECTOR_SPI         19
> +#define DRM_MODE_CONNECTOR_USB         20

Beware, new connector types have in the past resulted in userspace
burning&crashing. Maybe it's become better ...

Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
>
>  /**
>   * struct drm_mode_get_connector - Get connector metadata.
> --
> 2.23.0
>
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel



-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 1/3] drm/uapi: Add USB connector type
@ 2021-01-20 17:42     ` Daniel Vetter
  0 siblings, 0 replies; 53+ messages in thread
From: Daniel Vetter @ 2021-01-20 17:42 UTC (permalink / raw)
  To: Noralf Trønnes
  Cc: hudson, markus, peter, USB list, dri-devel, Tyler Hardin,
	Lubomir Rintel, pontus.fuchs, Sam Ravnborg

On Wed, Jan 20, 2021 at 6:10 PM Noralf Trønnes <noralf@tronnes.org> wrote:
>
> Add a connector type for USB connected display panels.
>
> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
> ---
>  include/uapi/drm/drm_mode.h | 1 +
>  1 file changed, 1 insertion(+)
>
> diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
> index fed66a03c7ae..33024cc5d26e 100644
> --- a/include/uapi/drm/drm_mode.h
> +++ b/include/uapi/drm/drm_mode.h
> @@ -367,6 +367,7 @@ enum drm_mode_subconnector {
>  #define DRM_MODE_CONNECTOR_DPI         17
>  #define DRM_MODE_CONNECTOR_WRITEBACK   18
>  #define DRM_MODE_CONNECTOR_SPI         19
> +#define DRM_MODE_CONNECTOR_USB         20

Beware, new connector types have in the past resulted in userspace
burning&crashing. Maybe it's become better ...

Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
>
>  /**
>   * struct drm_mode_get_connector - Get connector metadata.
> --
> 2.23.0
>
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel



-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 3/3] drm: Add Generic USB Display driver
  2021-01-20 17:00   ` Noralf Trønnes
  (?)
@ 2021-01-20 18:02   ` Daniel Vetter
  2021-01-24 16:17       ` Noralf Trønnes
  -1 siblings, 1 reply; 53+ messages in thread
From: Daniel Vetter @ 2021-01-20 18:02 UTC (permalink / raw)
  To: Noralf Trønnes
  Cc: dri-devel, hudson, markus, Sam Ravnborg, USB list, Tyler Hardin,
	Lubomir Rintel, pontus.fuchs, peter

On Wed, Jan 20, 2021 at 6:11 PM Noralf Trønnes <noralf@tronnes.org> wrote:
>
> This adds a generic USB display driver with the intention that it can be
> used with future USB interfaced low end displays/adapters. The Linux
> gadget device driver will serve as the canonical device implementation.
>
> The following DRM properties are supported:
> - Plane rotation
> - Connector TV properties
>
> There is also support for backlight brightness exposed as a backlight
> device.
>
> Display modes can be made available to the host driver either as DRM
> display modes or through EDID. If both are present, EDID is just passed
> on to userspace.
>
> Performance is preferred over color depth, so if the device supports
> RGB565, DRM_CAP_DUMB_PREFERRED_DEPTH will return 16.
>
> If the device transfer buffer can't fit an uncompressed framebuffer
> update, the update is split up into parts that do fit.
>
> Optimal user experience is achieved by providing damage reports either by
> setting FB_DAMAGE_CLIPS on pageflips or calling DRM_IOCTL_MODE_DIRTYFB.
>
> LZ4 compression is used if the device supports it.
>
> The driver supports a one bit monochrome transfer format: R1. This is not
> implemented in the gadget driver. It is added in preparation for future
> monochrome e-ink displays.
>
> The driver is MIT licensed to smooth the path for any BSD port of the
> driver.
>
> v2:
> - Use devm_drm_dev_alloc() and drmm_mode_config_init()
> - drm_fbdev_generic_setup: Use preferred_bpp=0, 16 was a copy paste error
> - The drm_backlight_helper is dropped, copy in the code

I think the backlight is still a bit problematic, since you're using
kms locks within the backlight callbacks. Other display drivers use
backlight within their kms locks. This means inconsistent locking
rules, which upsets lockdep.

Since you're already handling brightness as a special case in many
places I don't think it's a big shuffle:
- add a mutex to the connector struct
- move brightness value to connector struct, out of the connector_state
- use the new mutex to protect backlight state both from modeset side
(if needed, I'm not entirely sure about that) and the backlight side

Some more things below, but in general I'd say Acked-by: Daniel Vetter
<daniel.vetter> fwiw (probably not so much).

Cheers, Daniel

> - Support protocol version backwards compatibility for device
>
> v3:
> - Use donated Openmoko USB pid
> - Use direct compression from framebuffer when pitch matches, not only on
>   full frames, so split updates can benefit
> - Use __le16 in struct gud_drm_req_get_connector_status
> - Set edid property when the device only provides edid
> - Clear compression fields in struct gud_drm_req_set_buffer
> - Fix protocol version negotiation
> - Remove mode->vrefresh, it's calculated
>
> v4:
> - Drop the status req polling which was a workaround for something that
>   turned out to be a dwc2 udc driver problem
> - Add a flag for the Linux gadget to require a status request on
>   SET operations. Other devices will only get status req on STALL errors
> - Use protocol specific error codes (Peter)
> - Add a flag for devices that want to receive the entire framebuffer on
>   each flush (Lubomir)
> - Retry a failed framebuffer flush
> - If mode has changed wait for worker and clear pending damage before
>   queuing up new damage, fb width/height might have changed
> - Increase error counter on bulk transfer failures
> - Use DRM_MODE_CONNECTOR_USB
> - Handle R1 kmalloc error (Peter)
> - Don't try and replicate the USB get descriptor request standard for the
>   display descriptor (Peter)
> - Make max_buffer_size optional (Peter), drop the pow2 requirement since
>   it's not necessary anymore.
> - Don't pre-alloc a control request buffer, it was only 4k
> - Let gud.h describe the whole protocol explicitly and don't let DRM
>   leak into it (Peter)
> - Drop display mode .hskew and .vscan from the protocol
> - Shorten names: s/GUD_DRM_/GUD_/ s/gud_drm_/gud_/ (Peter)
> - Fix gud_pipe_check() connector picking when switching connector
> - Drop gud_drm_driver_gem_create_object() cached is default now
> - Retrieve USB device from struct drm_device.dev instead of keeping a
>   pointer
> - Honour fb->offsets[0]
> - Fix mode fetching when connector status is forced
> - Check EDID length reported by the device
> - Use drm_do_get_edid() so userspace can overrride EDID
> - Set epoch counter to signal connector status change
> - gud_drm_driver can be const now
>
> Cc: Peter Stuge <peter@stuge.se>
> Cc: Lubomir Rintel <lkundrak@v3.sk>
> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>


> ---
>  MAINTAINERS                         |   8 +
>  drivers/gpu/drm/Kconfig             |   2 +
>  drivers/gpu/drm/Makefile            |   1 +
>  drivers/gpu/drm/gud/Kconfig         |  14 +
>  drivers/gpu/drm/gud/Makefile        |   4 +
>  drivers/gpu/drm/gud/gud_connector.c | 722 ++++++++++++++++++++++++++++
>  drivers/gpu/drm/gud/gud_drv.c       | 620 ++++++++++++++++++++++++
>  drivers/gpu/drm/gud/gud_internal.h  | 148 ++++++
>  drivers/gpu/drm/gud/gud_pipe.c      | 472 ++++++++++++++++++
>  include/drm/gud.h                   | 356 ++++++++++++++
>  10 files changed, 2347 insertions(+)
>  create mode 100644 drivers/gpu/drm/gud/Kconfig
>  create mode 100644 drivers/gpu/drm/gud/Makefile
>  create mode 100644 drivers/gpu/drm/gud/gud_connector.c
>  create mode 100644 drivers/gpu/drm/gud/gud_drv.c
>  create mode 100644 drivers/gpu/drm/gud/gud_internal.h
>  create mode 100644 drivers/gpu/drm/gud/gud_pipe.c
>  create mode 100644 include/drm/gud.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 00112c044608..e7f71ac55f08 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -5525,6 +5525,14 @@ S:       Maintained
>  F:     Documentation/devicetree/bindings/display/panel/feiyang,fy07024di26a30d.yaml
>  F:     drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c
>
> +DRM DRIVER FOR GENERIC USB DISPLAY
> +M:     Noralf Trønnes <noralf@tronnes.org>
> +S:     Maintained
> +W:     https://github.com/notro/gud/wiki
> +T:     git git://anongit.freedesktop.org/drm/drm-misc
> +F:     drivers/gpu/drm/gud/
> +F:     include/drm/gud.h
> +
>  DRM DRIVER FOR GRAIN MEDIA GM12U320 PROJECTORS
>  M:     Hans de Goede <hdegoede@redhat.com>
>  S:     Maintained
> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> index 147d61b9674e..6d3f47933e51 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -388,6 +388,8 @@ source "drivers/gpu/drm/tidss/Kconfig"
>
>  source "drivers/gpu/drm/xlnx/Kconfig"
>
> +source "drivers/gpu/drm/gud/Kconfig"
> +
>  # Keep legacy drivers last
>
>  menuconfig DRM_LEGACY
> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> index 81569009f884..78dd8e12525d 100644
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -124,3 +124,4 @@ obj-$(CONFIG_DRM_ASPEED_GFX) += aspeed/
>  obj-$(CONFIG_DRM_MCDE) += mcde/
>  obj-$(CONFIG_DRM_TIDSS) += tidss/
>  obj-y                  += xlnx/
> +obj-y                  += gud/
> diff --git a/drivers/gpu/drm/gud/Kconfig b/drivers/gpu/drm/gud/Kconfig
> new file mode 100644
> index 000000000000..d832cd63687c
> --- /dev/null
> +++ b/drivers/gpu/drm/gud/Kconfig
> @@ -0,0 +1,14 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +config DRM_GUD
> +       tristate "Generic USB Display"
> +       depends on DRM && USB
> +       select LZ4_COMPRESS
> +       select DRM_KMS_HELPER
> +       select DRM_GEM_SHMEM_HELPER
> +       select BACKLIGHT_CLASS_DEVICE
> +       help
> +         This is a DRM display driver for Generic USB Displays or display
> +         adapters.
> +
> +         If M is selected the module will be called gud.
> diff --git a/drivers/gpu/drm/gud/Makefile b/drivers/gpu/drm/gud/Makefile
> new file mode 100644
> index 000000000000..d2bb53a65de6
> --- /dev/null
> +++ b/drivers/gpu/drm/gud/Makefile
> @@ -0,0 +1,4 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +gud-objs                       := gud_drv.o gud_pipe.o gud_connector.o
> +obj-$(CONFIG_DRM_GUD)          += gud.o
> diff --git a/drivers/gpu/drm/gud/gud_connector.c b/drivers/gpu/drm/gud/gud_connector.c
> new file mode 100644
> index 000000000000..a4b9bbf48e19
> --- /dev/null
> +++ b/drivers/gpu/drm/gud/gud_connector.c
> @@ -0,0 +1,722 @@
> +// SPDX-License-Identifier: MIT
> +/*
> + * Copyright 2020 Noralf Trønnes
> + */
> +
> +#include <linux/backlight.h>
> +
> +#include <drm/drm_atomic.h>
> +#include <drm/drm_atomic_state_helper.h>
> +#include <drm/drm_connector.h>
> +#include <drm/drm_drv.h>
> +#include <drm/drm_encoder.h>
> +#include <drm/drm_file.h>
> +#include <drm/drm_modeset_helper_vtables.h>
> +#include <drm/drm_print.h>
> +#include <drm/drm_probe_helper.h>
> +#include <drm/drm_simple_kms_helper.h>
> +#include <drm/gud.h>
> +
> +#include "gud_internal.h"
> +
> +struct gud_connector {
> +       struct drm_connector connector;
> +       struct drm_encoder encoder;
> +       struct backlight_device *backlight;
> +
> +       /* Supported properties */
> +       u16 *properties;
> +       unsigned int num_properties;
> +
> +       /* Initial gadget tv state if applicable, applied on state reset */
> +       struct drm_tv_connector_state initial_tv_state;
> +
> +       /*
> +        * Initial gadget backlight brightness if applicable, applied on state reset.
> +        * The value -ENODEV is used to signal no backlight.
> +        */
> +       int initial_brightness;
> +
> +       unsigned int num_modes;
> +       size_t edid_len;
> +};
> +
> +static inline struct gud_connector *to_gud_connector(struct drm_connector *connector)
> +{
> +       return container_of(connector, struct gud_connector, connector);
> +}
> +
> +static int gud_connector_backlight_update_status(struct backlight_device *bd)
> +{
> +       struct drm_connector *connector = bl_get_data(bd);
> +       struct drm_connector_state *connector_state;
> +       struct drm_device *dev = connector->dev;
> +       struct drm_modeset_acquire_ctx ctx;
> +       struct drm_atomic_state *state;
> +       int ret;
> +
> +       state = drm_atomic_state_alloc(dev);
> +       if (!state)
> +               return -ENOMEM;
> +
> +       drm_modeset_acquire_init(&ctx, 0);
> +       state->acquire_ctx = &ctx;
> +retry:
> +       connector_state = drm_atomic_get_connector_state(state, connector);
> +       if (IS_ERR(connector_state)) {
> +               ret = PTR_ERR(connector_state);
> +               goto out;
> +       }
> +
> +       /* Reuse tv.brightness to avoid having to subclass */
> +       connector_state->tv.brightness = bd->props.brightness;
> +
> +       ret = drm_atomic_commit(state);
> +out:
> +       if (ret == -EDEADLK) {
> +               drm_atomic_state_clear(state);
> +               drm_modeset_backoff(&ctx);
> +               goto retry;
> +       }
> +
> +       drm_atomic_state_put(state);
> +
> +       drm_modeset_drop_locks(&ctx);
> +       drm_modeset_acquire_fini(&ctx);
> +
> +       return ret;
> +}
> +
> +static int gud_connector_backlight_get_brightness(struct backlight_device *bd)
> +{
> +       struct drm_connector *connector = bl_get_data(bd);
> +       struct drm_device *dev = connector->dev;
> +       int brightness;
> +
> +       drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
> +       brightness = connector->state->tv.brightness;
> +       drm_modeset_unlock(&dev->mode_config.connection_mutex);
> +
> +       return brightness;
> +}
> +
> +static const struct backlight_ops gud_connector_backlight_ops = {
> +       .get_brightness = gud_connector_backlight_get_brightness,
> +       .update_status  = gud_connector_backlight_update_status,
> +};
> +
> +static int gud_connector_backlight_register(struct gud_connector *gconn)
> +{
> +       struct drm_connector *connector = &gconn->connector;
> +       struct backlight_device *bd;
> +       const char *name;
> +       const struct backlight_properties props = {
> +               .type = BACKLIGHT_RAW,
> +               .scale = BACKLIGHT_SCALE_NON_LINEAR,
> +               .max_brightness = 100,
> +       };
> +
> +       name = kasprintf(GFP_KERNEL, "card%d-%s-backlight",
> +                        connector->dev->primary->index, connector->name);
> +       if (!name)
> +               return -ENOMEM;
> +
> +       bd = backlight_device_register(name, connector->kdev, connector,
> +                                      &gud_connector_backlight_ops, &props);
> +       kfree(name);
> +       if (IS_ERR(bd))
> +               return PTR_ERR(bd);
> +
> +       gconn->backlight = bd;
> +
> +       return 0;
> +}
> +
> +static int gud_connector_status_request(struct drm_connector *connector)
> +{
> +       struct gud_connector *gconn = to_gud_connector(connector);
> +       struct gud_device *gdrm = to_gud_device(connector->dev);
> +       struct gud_connector_status_req req;
> +       u16 num_modes, edid_len;
> +       int ret;
> +
> +       ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_STATUS,
> +                         connector->index, &req, sizeof(req));
> +       if (ret)
> +               return ret;
> +
> +       switch (req.status & GUD_CONNECTOR_STATUS_CONNECTED_MASK) {
> +       case GUD_CONNECTOR_STATUS_DISCONNECTED:
> +               ret = connector_status_disconnected;
> +               break;
> +       case GUD_CONNECTOR_STATUS_CONNECTED:
> +               ret = connector_status_connected;
> +               break;
> +       default:
> +               ret = connector_status_unknown;
> +               break;
> +       };
> +
> +       num_modes = le16_to_cpu(req.num_modes);
> +       edid_len = le16_to_cpu(req.edid_len);
> +
> +       if (edid_len % EDID_LENGTH) {
> +               drm_err(connector->dev, "%s: Invalid EDID size: %u\n", connector->name, edid_len);
> +               edid_len = 0;
> +       }
> +
> +       if (req.status & GUD_CONNECTOR_STATUS_CHANGED ||
> +           gconn->num_modes != num_modes || gconn->edid_len != edid_len)
> +               connector->epoch_counter += 1;
> +
> +       gconn->num_modes = num_modes;
> +       gconn->edid_len = edid_len;
> +
> +       if (!num_modes && !edid_len && ret != connector_status_disconnected)
> +               drm_dbg_kms(connector->dev, "%s: No modes or EDID.\n", connector->name);
> +
> +       return ret;
> +}
> +
> +static int gud_connector_detect(struct drm_connector *connector,
> +                               struct drm_modeset_acquire_ctx *ctx, bool force)
> +{
> +       struct gud_device *gdrm = to_gud_device(connector->dev);
> +       int idx, ret;
> +
> +       if (!drm_dev_enter(connector->dev, &idx))
> +               return connector_status_disconnected;
> +
> +       if (force) {
> +               ret = gud_usb_set(gdrm, GUD_REQ_SET_CONNECTOR_FORCE_DETECT,
> +                                 connector->index, NULL, 0);
> +               if (ret) {
> +                       ret = connector_status_unknown;
> +                       goto exit;
> +               }
> +       }
> +
> +       ret = gud_connector_status_request(connector);
> +       if (ret < 0)
> +               ret = connector_status_unknown;
> +exit:
> +       drm_dev_exit(idx);
> +
> +       return ret;
> +}
> +
> +struct gud_connector_get_edid_ctx {
> +       struct gud_connector *gconn;
> +       void *buf;
> +};
> +
> +static int gud_connector_get_edid_block(void *data, u8 *buf, unsigned int block, size_t len)
> +{
> +       struct gud_connector_get_edid_ctx *ctx = data;
> +       struct gud_connector *gconn = ctx->gconn;
> +       size_t start = block * EDID_LENGTH;
> +
> +       if (start + len > gconn->edid_len)
> +               return -1;
> +
> +       if (!block) {
> +               struct gud_device *gdrm = to_gud_device(gconn->connector.dev);
> +               int ret;
> +
> +               /* Check because drm_do_get_edid() will retry on failure */
> +               if (!ctx->buf)
> +                       ctx->buf = kmalloc(gconn->edid_len, GFP_KERNEL);
> +               if (!ctx->buf)
> +                       return -1;
> +
> +               ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_EDID, gconn->connector.index,
> +                                 ctx->buf, gconn->edid_len);
> +               if (ret)
> +                       return -1;
> +       }
> +
> +       memcpy(buf, ctx->buf + start, len);
> +
> +       return 0;
> +}
> +
> +static int gud_connector_get_modes(struct drm_connector *connector)
> +{
> +       struct gud_connector *gconn = to_gud_connector(connector);
> +       struct gud_device *gdrm = to_gud_device(connector->dev);
> +       struct gud_connector_get_edid_ctx edid_ctx = {
> +               .gconn = gconn,
> +       };
> +       struct gud_display_mode_req *reqmodes = NULL;
> +       unsigned int i, num_modes = 0;
> +       struct edid *edid = NULL;
> +       bool edid_override;
> +       int idx, ret;
> +
> +       if (!drm_dev_enter(connector->dev, &idx))
> +               return 0;
> +
> +       if (connector->force) {
> +               ret = gud_connector_status_request(connector);
> +               if (ret < 0)
> +                       goto out;
> +       }
> +
> +       edid = drm_do_get_edid(connector, gud_connector_get_edid_block, &edid_ctx);
> +       edid_override = edid && !edid_ctx.buf;
> +       kfree(edid_ctx.buf);
> +       drm_connector_update_edid_property(connector, edid);
> +
> +       if (!gconn->num_modes || edid_override) {
> +               num_modes = drm_add_edid_modes(connector, edid);
> +               goto out;
> +       }
> +
> +       reqmodes = kmalloc_array(gconn->num_modes, sizeof(*reqmodes), GFP_KERNEL);
> +       if (!reqmodes)
> +               goto out;
> +
> +       ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_MODES, connector->index,
> +                         reqmodes, gconn->num_modes * sizeof(*reqmodes));
> +       if (ret)
> +               goto out;
> +
> +       for (i = 0; i < gconn->num_modes; i++) {
> +               struct drm_display_mode *mode;
> +
> +               mode = drm_mode_create(connector->dev);
> +               if (!mode)
> +                       goto out;
> +
> +               gud_to_display_mode(mode, &reqmodes[i]);
> +               drm_mode_probed_add(connector, mode);
> +               num_modes++;
> +       }
> +out:
> +       kfree(reqmodes);
> +       kfree(edid);
> +       drm_dev_exit(idx);
> +
> +       return num_modes;
> +}
> +
> +static int gud_connector_atomic_check(struct drm_connector *connector,
> +                                     struct drm_atomic_state *state)
> +{
> +       struct drm_connector_state *new_state;
> +       struct drm_crtc_state *new_crtc_state;
> +       struct drm_connector_state *old_state;
> +
> +       new_state = drm_atomic_get_new_connector_state(state, connector);
> +       if (!new_state->crtc)
> +               return 0;
> +
> +       old_state = drm_atomic_get_old_connector_state(state, connector);
> +       new_crtc_state = drm_atomic_get_new_crtc_state(state, new_state->crtc);
> +
> +       if (old_state->tv.margins.left != new_state->tv.margins.left ||
> +           old_state->tv.margins.right != new_state->tv.margins.right ||
> +           old_state->tv.margins.top != new_state->tv.margins.top ||
> +           old_state->tv.margins.bottom != new_state->tv.margins.bottom ||
> +           old_state->tv.mode != new_state->tv.mode ||
> +           old_state->tv.brightness != new_state->tv.brightness ||
> +           old_state->tv.contrast != new_state->tv.contrast ||
> +           old_state->tv.flicker_reduction != new_state->tv.flicker_reduction ||
> +           old_state->tv.overscan != new_state->tv.overscan ||
> +           old_state->tv.saturation != new_state->tv.saturation ||
> +           old_state->tv.hue != new_state->tv.hue)
> +               new_crtc_state->connectors_changed = true;
> +
> +       return 0;
> +}
> +
> +static const struct drm_connector_helper_funcs gud_connector_helper_funcs = {
> +       .detect_ctx = gud_connector_detect,
> +       .get_modes = gud_connector_get_modes,
> +       .atomic_check = gud_connector_atomic_check,
> +};
> +
> +static int gud_connector_late_register(struct drm_connector *connector)
> +{
> +       struct gud_connector *gconn = to_gud_connector(connector);
> +
> +       if (gconn->initial_brightness < 0)
> +               return 0;
> +
> +       return gud_connector_backlight_register(gconn);
> +}
> +
> +static void gud_connector_early_unregister(struct drm_connector *connector)
> +{
> +       struct gud_connector *gconn = to_gud_connector(connector);
> +
> +       backlight_device_unregister(gconn->backlight);
> +}
> +
> +static void gud_connector_destroy(struct drm_connector *connector)
> +{
> +       struct gud_connector *gconn = to_gud_connector(connector);
> +
> +       drm_connector_cleanup(connector);
> +       kfree(gconn->properties);
> +       kfree(gconn);
> +}
> +
> +static void gud_connector_reset(struct drm_connector *connector)
> +{
> +       struct gud_connector *gconn = to_gud_connector(connector);
> +
> +       drm_atomic_helper_connector_reset(connector);
> +       connector->state->tv = gconn->initial_tv_state;
> +       /* Set margins from command line */
> +       drm_atomic_helper_connector_tv_reset(connector);
> +       if (gconn->initial_brightness >= 0)
> +               connector->state->tv.brightness = gconn->initial_brightness;
> +}
> +
> +static const struct drm_connector_funcs gud_connector_funcs = {
> +       .fill_modes = drm_helper_probe_single_connector_modes,
> +       .late_register = gud_connector_late_register,
> +       .early_unregister = gud_connector_early_unregister,
> +       .destroy = gud_connector_destroy,
> +       .reset = gud_connector_reset,
> +       .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> +       .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> +};
> +
> +/*
> + * The tv.mode property is shared among the connectors and its enum names are
> + * driver specific. This means that if more than one connector uses tv.mode,
> + * the enum names has to be the same.
> + */
> +static int gud_connector_add_tv_mode(struct gud_device *gdrm,
> +                                    struct drm_connector *connector, u64 val)
> +{
> +       unsigned int i, num_modes;
> +       const char **modes;
> +       size_t buf_len;
> +       char *buf;
> +       int ret;
> +
> +       num_modes = val >> GUD_CONNECTOR_TV_MODE_NUM_SHIFT;
> +
> +       if (!num_modes)
> +               return -EINVAL;
> +
> +       buf_len = num_modes * GUD_CONNECTOR_TV_MODE_NAME_LEN;
> +       modes = kmalloc_array(num_modes, sizeof(*modes), GFP_KERNEL);
> +       buf = kmalloc(buf_len, GFP_KERNEL);
> +       if (!modes || !buf) {
> +               ret = -ENOMEM;
> +               goto free;
> +       }
> +
> +       ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_TV_MODE_VALUES,
> +                         connector->index, buf, buf_len);
> +       if (ret)
> +               goto free;
> +
> +       for (i = 0; i < num_modes; i++)
> +               modes[i] = &buf[i * GUD_CONNECTOR_TV_MODE_NAME_LEN];
> +
> +       ret = drm_mode_create_tv_properties(connector->dev, num_modes, modes);
> +free:
> +       kfree(modes);
> +       kfree(buf);
> +
> +       return ret;
> +}
> +
> +static struct drm_property *
> +gud_connector_property_lookup(struct drm_connector *connector, u16 prop)
> +{
> +       struct drm_mode_config *config = &connector->dev->mode_config;
> +
> +       switch (prop) {
> +       case GUD_PROPERTY_TV_LEFT_MARGIN:
> +               return config->tv_left_margin_property;
> +       case GUD_PROPERTY_TV_RIGHT_MARGIN:
> +               return config->tv_right_margin_property;
> +       case GUD_PROPERTY_TV_TOP_MARGIN:
> +               return config->tv_top_margin_property;
> +       case GUD_PROPERTY_TV_BOTTOM_MARGIN:
> +               return config->tv_bottom_margin_property;
> +       case GUD_PROPERTY_TV_MODE:
> +               return config->tv_mode_property;
> +       case GUD_PROPERTY_TV_BRIGHTNESS:
> +               return config->tv_brightness_property;
> +       case GUD_PROPERTY_TV_CONTRAST:
> +               return config->tv_contrast_property;
> +       case GUD_PROPERTY_TV_FLICKER_REDUCTION:
> +               return config->tv_flicker_reduction_property;
> +       case GUD_PROPERTY_TV_OVERSCAN:
> +               return config->tv_overscan_property;
> +       case GUD_PROPERTY_TV_SATURATION:
> +               return config->tv_saturation_property;
> +       case GUD_PROPERTY_TV_HUE:
> +               return config->tv_hue_property;
> +       default:
> +               return ERR_PTR(-EINVAL);
> +       }
> +}
> +
> +static unsigned int *gud_connector_tv_state_val(u16 prop, struct drm_tv_connector_state *state)
> +{
> +       switch (prop) {
> +       case GUD_PROPERTY_TV_LEFT_MARGIN:
> +               return &state->margins.left;
> +       case GUD_PROPERTY_TV_RIGHT_MARGIN:
> +               return &state->margins.right;
> +       case GUD_PROPERTY_TV_TOP_MARGIN:
> +               return &state->margins.top;
> +       case GUD_PROPERTY_TV_BOTTOM_MARGIN:
> +               return &state->margins.bottom;
> +       case GUD_PROPERTY_TV_MODE:
> +               return &state->mode;
> +       case GUD_PROPERTY_TV_BRIGHTNESS:
> +               return &state->brightness;
> +       case GUD_PROPERTY_TV_CONTRAST:
> +               return &state->contrast;
> +       case GUD_PROPERTY_TV_FLICKER_REDUCTION:
> +               return &state->flicker_reduction;
> +       case GUD_PROPERTY_TV_OVERSCAN:
> +               return &state->overscan;
> +       case GUD_PROPERTY_TV_SATURATION:
> +               return &state->saturation;
> +       case GUD_PROPERTY_TV_HUE:
> +               return &state->hue;
> +       default:
> +               return ERR_PTR(-EINVAL);
> +       }
> +}
> +
> +static int gud_connector_add_properties(struct gud_device *gdrm, struct gud_connector *gconn,
> +                                       unsigned int num_properties)
> +{
> +       struct drm_device *drm = &gdrm->drm;
> +       struct drm_connector *connector = &gconn->connector;
> +       struct gud_property_req *properties;
> +       unsigned int i;
> +       int ret;
> +
> +       gconn->properties = kcalloc(num_properties, sizeof(*gconn->properties), GFP_KERNEL);
> +       if (!gconn->properties)
> +               return -ENOMEM;
> +
> +       properties = kcalloc(num_properties, sizeof(*properties), GFP_KERNEL);
> +       if (!properties)
> +               return -ENOMEM;
> +
> +       ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_PROPERTIES, connector->index,
> +                         properties, num_properties * sizeof(*properties));
> +       if (ret)
> +               goto out;
> +
> +       for (i = 0; i < num_properties; i++) {
> +               u16 prop = le16_to_cpu(properties[i].prop);
> +               u64 val = le64_to_cpu(properties[i].val);
> +               struct drm_property *property;
> +               unsigned int *state_val;
> +
> +               drm_dbg(drm, "property: %u = %llu(0x%llx)\n", prop, val, val);
> +
> +               switch (prop) {
> +               case GUD_PROPERTY_TV_LEFT_MARGIN:
> +                       fallthrough;
> +               case GUD_PROPERTY_TV_RIGHT_MARGIN:
> +                       fallthrough;
> +               case GUD_PROPERTY_TV_TOP_MARGIN:
> +                       fallthrough;
> +               case GUD_PROPERTY_TV_BOTTOM_MARGIN:
> +                       ret = drm_mode_create_tv_margin_properties(drm);
> +                       if (ret)
> +                               goto out;
> +                       break;
> +               case GUD_PROPERTY_TV_MODE:
> +                       ret = gud_connector_add_tv_mode(gdrm, connector, val);
> +                       if (ret)
> +                               goto out;
> +                       val = val & (BIT(GUD_CONNECTOR_TV_MODE_NUM_SHIFT) - 1);
> +                       break;
> +               case GUD_PROPERTY_TV_BRIGHTNESS:
> +                       fallthrough;
> +               case GUD_PROPERTY_TV_CONTRAST:
> +                       fallthrough;
> +               case GUD_PROPERTY_TV_FLICKER_REDUCTION:
> +                       fallthrough;
> +               case GUD_PROPERTY_TV_OVERSCAN:
> +                       fallthrough;
> +               case GUD_PROPERTY_TV_SATURATION:
> +                       fallthrough;
> +               case GUD_PROPERTY_TV_HUE:
> +                       /* This is a no-op if already added. */
> +                       ret = drm_mode_create_tv_properties(drm, 0, NULL);
> +                       if (ret)
> +                               goto out;
> +                       break;
> +               case GUD_PROPERTY_BACKLIGHT_BRIGHTNESS:
> +                       if (val > 100) {
> +                               ret = -EINVAL;
> +                               goto out;
> +                       }
> +                       gconn->initial_brightness = val;
> +                       break;
> +               default:
> +                       /* New ones might show up in future devices, skip those we don't know. */
> +                       drm_dbg(drm, "Unknown property: %u\n", prop);
> +                       continue;
> +               }
> +
> +               gconn->properties[gconn->num_properties++] = prop;
> +
> +               if (prop == GUD_PROPERTY_BACKLIGHT_BRIGHTNESS)
> +                       continue; /* not a DRM property */
> +
> +               property = gud_connector_property_lookup(connector, prop);
> +               if (WARN_ON(IS_ERR(property)))
> +                       continue;
> +
> +               state_val = gud_connector_tv_state_val(prop, &gconn->initial_tv_state);
> +               if (WARN_ON(IS_ERR(state_val)))
> +                       continue;
> +
> +               *state_val = val;
> +               drm_object_attach_property(&connector->base, property, 0);
> +       }
> +out:
> +       kfree(properties);
> +
> +       return ret;
> +}
> +
> +int gud_connector_fill_properties(struct drm_connector *connector,
> +                                 struct drm_connector_state *connector_state,
> +                                 struct gud_property_req *properties)
> +{
> +       struct gud_connector *gconn;
> +       unsigned int i;
> +
> +       gconn = to_gud_connector(connector);
> +
> +       /* Only interested in the count? */
> +       if (!connector_state)
> +               return gconn->num_properties;
> +
> +       for (i = 0; i < gconn->num_properties; i++) {
> +               u16 prop = gconn->properties[i];
> +               u64 val;
> +
> +               if (prop == GUD_PROPERTY_BACKLIGHT_BRIGHTNESS) {
> +                       val = connector_state->tv.brightness;
> +               } else {
> +                       unsigned int *state_val;
> +
> +                       state_val = gud_connector_tv_state_val(prop, &connector_state->tv);
> +                       if (WARN_ON_ONCE(IS_ERR(state_val)))
> +                               return PTR_ERR(state_val);
> +
> +                       val = *state_val;
> +               }
> +
> +               properties[i].prop = cpu_to_le16(prop);
> +               properties[i].val = cpu_to_le64(val);
> +       }
> +
> +       return gconn->num_properties;
> +}
> +
> +int gud_connector_create(struct gud_device *gdrm, unsigned int index)
> +{
> +       struct gud_connector_descriptor_req desc;
> +       struct drm_device *drm = &gdrm->drm;
> +       struct gud_connector *gconn;
> +       struct drm_connector *connector;
> +       struct drm_encoder *encoder;
> +       int ret, connector_type;
> +       u32 flags;
> +
> +       ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR, index, &desc, sizeof(desc));
> +       if (ret)
> +               return ret;
> +
> +       gconn = kzalloc(sizeof(*gconn), GFP_KERNEL);

Would be nice to do that with drmm_, but we don't have the
drmm_connector_alloc wrapper yet.

> +       if (!gconn)
> +               return -ENOMEM;
> +
> +       gconn->initial_brightness = -ENODEV;
> +       flags = le32_to_cpu(desc.flags);
> +       connector = &gconn->connector;
> +
> +       drm_dbg(drm, "Connector: index=%u type=%u flags=0x%x num_properties=%u\n",
> +               index, desc.connector_type, flags, desc.num_properties);
> +
> +       switch (desc.connector_type) {
> +       case GUD_CONNECTOR_TYPE_PANEL:
> +               connector_type = DRM_MODE_CONNECTOR_USB;
> +               break;
> +       case GUD_CONNECTOR_TYPE_VGA:
> +               connector_type = DRM_MODE_CONNECTOR_VGA;
> +               break;
> +       case GUD_CONNECTOR_TYPE_DVI:
> +               connector_type = DRM_MODE_CONNECTOR_DVID;
> +               break;
> +       case GUD_CONNECTOR_TYPE_COMPOSITE:
> +               connector_type = DRM_MODE_CONNECTOR_Composite;
> +               break;
> +       case GUD_CONNECTOR_TYPE_SVIDEO:
> +               connector_type = DRM_MODE_CONNECTOR_SVIDEO;
> +               break;
> +       case GUD_CONNECTOR_TYPE_COMPONENT:
> +               connector_type = DRM_MODE_CONNECTOR_Component;
> +               break;
> +       case GUD_CONNECTOR_TYPE_DISPLAYPORT:
> +               connector_type = DRM_MODE_CONNECTOR_DisplayPort;
> +               break;
> +       case GUD_CONNECTOR_TYPE_HDMI:
> +               connector_type = DRM_MODE_CONNECTOR_HDMIA;
> +               break;
> +       default: /* future types */
> +               connector_type = DRM_MODE_CONNECTOR_USB;
> +               break;
> +       };
> +
> +       drm_connector_helper_add(connector, &gud_connector_helper_funcs);
> +       ret = drm_connector_init(drm, connector, &gud_connector_funcs, connector_type);
> +       if (ret) {
> +               kfree(connector);
> +               return ret;
> +       }
> +
> +       if (WARN_ON(connector->index != index))
> +               return -EINVAL;
> +
> +       if (flags & GUD_CONNECTOR_FLAGS_POLL_STATUS)
> +               connector->polled = (DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT);
> +       if (flags & GUD_CONNECTOR_FLAGS_INTERLACE)
> +               connector->interlace_allowed = true;
> +       if (flags & GUD_CONNECTOR_FLAGS_DOUBLESCAN)
> +               connector->doublescan_allowed = true;
> +
> +       if (desc.num_properties) {
> +               ret = gud_connector_add_properties(gdrm, gconn, desc.num_properties);
> +               if (ret) {
> +                       dev_err(drm->dev, "Failed to add connector/%u properties\n", index);
> +                       return ret;
> +               }
> +       }
> +
> +       /* The first connector is attached to the existing simple pipe encoder */
> +       if (!connector->index) {
> +               encoder = &gdrm->pipe.encoder;
> +       } else {
> +               encoder = &gconn->encoder;
> +
> +               ret = drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_NONE);
> +               if (ret)
> +                       return ret;
> +
> +               encoder->possible_crtcs = 1;
> +       }
> +
> +       return drm_connector_attach_encoder(connector, encoder);
> +}
> diff --git a/drivers/gpu/drm/gud/gud_drv.c b/drivers/gpu/drm/gud/gud_drv.c
> new file mode 100644
> index 000000000000..6c5e9eeb8cdc
> --- /dev/null
> +++ b/drivers/gpu/drm/gud/gud_drv.c
> @@ -0,0 +1,620 @@
> +// SPDX-License-Identifier: MIT
> +/*
> + * Copyright 2020 Noralf Trønnes
> + */
> +
> +#include <linux/dma-buf.h>
> +#include <linux/lz4.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/string_helpers.h>
> +#include <linux/usb.h>
> +#include <linux/vmalloc.h>
> +#include <linux/workqueue.h>
> +
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_damage_helper.h>
> +#include <drm/drm_debugfs.h>
> +#include <drm/drm_drv.h>
> +#include <drm/drm_fb_helper.h>
> +#include <drm/drm_fourcc.h>
> +#include <drm/drm_gem_shmem_helper.h>
> +#include <drm/drm_gem_framebuffer_helper.h>
> +#include <drm/drm_managed.h>
> +#include <drm/drm_print.h>
> +#include <drm/drm_probe_helper.h>
> +#include <drm/drm_simple_kms_helper.h>
> +#include <drm/gud.h>
> +
> +#include "gud_internal.h"
> +
> +/* Only used internally */
> +static const struct drm_format_info gud_drm_format_r1 = {
> +       .format = GUD_DRM_FORMAT_R1,
> +       .num_planes = 1,
> +       .char_per_block = { 1, 0, 0 },
> +       .block_w = { 8, 0, 0 },
> +       .block_h = { 1, 0, 0 },
> +       .hsub = 1,
> +       .vsub = 1,
> +};
> +
> +static int gud_usb_control_msg(struct usb_device *usb, u8 ifnum, bool in,
> +                              u8 request, u16 value, void *buf, size_t len)
> +{
> +       u8 requesttype = USB_TYPE_VENDOR | USB_RECIP_INTERFACE;
> +       unsigned int pipe;
> +       int ret;
> +
> +       if (in) {
> +               pipe = usb_rcvctrlpipe(usb, 0);
> +               requesttype |= USB_DIR_IN;
> +       } else {
> +               pipe = usb_sndctrlpipe(usb, 0);
> +               requesttype |= USB_DIR_OUT;
> +       }
> +
> +       ret = usb_control_msg(usb, pipe, request, requesttype, value,
> +                             ifnum, buf, len, USB_CTRL_GET_TIMEOUT);
> +       if (ret < 0)
> +               return ret;
> +       if (ret != len)
> +               return -EIO;
> +
> +       return 0;
> +}
> +
> +static int gud_get_display_descriptor(struct usb_interface *interface,
> +                                     struct gud_display_descriptor_req *desc)
> +{
> +       u8 ifnum = interface->cur_altsetting->desc.bInterfaceNumber;
> +       struct usb_device *usb = interface_to_usbdev(interface);
> +       void *buf;
> +       int ret;
> +
> +       buf = kmalloc(sizeof(*desc), GFP_KERNEL);
> +       if (!buf)
> +               return -ENOMEM;
> +
> +       ret = gud_usb_control_msg(usb, ifnum, true, GUD_REQ_GET_DESCRIPTOR, 0, buf, sizeof(*desc));
> +       memcpy(desc, buf, sizeof(*desc));
> +       kfree(buf);
> +       if (ret)
> +               return ret;
> +
> +       if (desc->magic != GUD_DISPLAY_MAGIC)
> +               return -ENODATA;
> +
> +       DRM_DEV_DEBUG_DRIVER(&interface->dev,
> +                            "version=%u flags=0x%x compression=0x%x num_formats=%u num_connectors=%u max_buffer_size=%u\n",
> +                            desc->version, le32_to_cpu(desc->flags), desc->compression,
> +                            desc->num_formats, desc->num_connectors,
> +                            le32_to_cpu(desc->max_buffer_size));
> +
> +       if (!desc->version || !desc->num_formats || !desc->num_connectors ||
> +           !desc->max_width || !desc->max_height ||
> +           le32_to_cpu(desc->min_width) > le32_to_cpu(desc->max_width) ||
> +           le32_to_cpu(desc->min_height) > le32_to_cpu(desc->max_height))
> +               return -EINVAL;
> +
> +       return 0;
> +}
> +
> +static int gud_usb_get_status(struct usb_device *usb, u8 ifnum, u8 *status)
> +{
> +       u8 *buf;
> +       int ret;
> +
> +       buf = kmalloc(sizeof(*buf), GFP_KERNEL);
> +       if (!buf)
> +               return -ENOMEM;
> +
> +       ret = gud_usb_control_msg(usb, ifnum, true, GUD_REQ_GET_STATUS, 0, buf, sizeof(*buf));
> +       *status = *buf;
> +       kfree(buf);
> +
> +       return ret;
> +}
> +
> +static int gud_status_to_errno(u8 status)
> +{
> +       switch (status) {
> +       case GUD_STATUS_OK:
> +               return 0;
> +       case GUD_STATUS_BUSY:
> +               return -EBUSY;
> +       case GUD_STATUS_REQUEST_NOT_SUPPORTED:
> +               return -EOPNOTSUPP;
> +       case GUD_STATUS_PROTOCOL_ERROR:
> +               return -EPROTO;
> +       case GUD_STATUS_INVALID_PARAMETER:
> +               return -EINVAL;
> +       case GUD_STATUS_ERROR:
> +               return -EREMOTEIO;
> +       default:
> +               return -EREMOTEIO;
> +       }
> +}
> +
> +static int gud_usb_transfer(struct gud_device *gdrm, bool in, u8 request, u16 index,
> +                           void *buf, size_t len)
> +{
> +       struct usb_device *usb = gud_to_usb_device(gdrm);
> +       void *trbuf = NULL;
> +       int idx, ret;
> +
> +       drm_dbg(&gdrm->drm, "%s: request=0x%x index=%u len=%zu\n",
> +               in ? "get" : "set", request, index, len);
> +
> +       if (!drm_dev_enter(&gdrm->drm, &idx))
> +               return -ENODEV;
> +
> +       mutex_lock(&gdrm->ctrl_lock);
> +
> +       if (buf) {
> +               if (in)
> +                       trbuf = kmalloc(len, GFP_KERNEL);
> +               else
> +                       trbuf = kmemdup(buf, len, GFP_KERNEL);
> +               if (!trbuf) {
> +                       ret = -ENOMEM;
> +                       goto unlock;
> +               }
> +       }
> +
> +       ret = gud_usb_control_msg(usb, gdrm->ifnum, in, request, index, trbuf, len);
> +       if (ret == -EPIPE || (!ret && !in && (gdrm->flags & GUD_DISPLAY_FLAG_STATUS_ON_SET))) {
> +               bool error = ret;
> +               u8 status;
> +
> +               ret = gud_usb_get_status(usb, gdrm->ifnum, &status);
> +               if (!ret) {
> +                       if (error && status == GUD_STATUS_OK) {
> +                               dev_err_once(gdrm->drm.dev,
> +                                            "Unexpected status OK for failed transfer\n");
> +                               ret = -EPIPE;
> +                       } else {
> +                               ret = gud_status_to_errno(status);
> +                       }
> +               }
> +       }
> +
> +       if (!ret && in && buf)
> +               memcpy(buf, trbuf, len);
> +
> +       if (ret) {
> +               drm_dbg(&gdrm->drm, "ret=%d\n", ret);
> +               gdrm->stats_num_errors++;
> +       }
> +
> +       kfree(trbuf);
> +unlock:
> +       mutex_unlock(&gdrm->ctrl_lock);
> +       drm_dev_exit(idx);
> +
> +       return ret;
> +}
> +
> +int gud_usb_get(struct gud_device *gdrm, u8 request, u16 index, void *buf, size_t len)
> +{
> +       return gud_usb_transfer(gdrm, true, request, index, buf, len);
> +}
> +
> +int gud_usb_set(struct gud_device *gdrm, u8 request, u16 index, void *buf, size_t len)
> +{
> +       return gud_usb_transfer(gdrm, false, request, index, buf, len);
> +}
> +
> +int gud_usb_write8(struct gud_device *gdrm, u8 request, u8 val)
> +{
> +       return gud_usb_set(gdrm, request, 0, &val, sizeof(val));
> +}
> +
> +static int gud_set_version(struct usb_device *usb, u8 ifnum, u32 flags, u8 version)
> +{
> +       u8 *buf;
> +       int ret;
> +
> +       buf = kmalloc(sizeof(*buf), GFP_KERNEL);
> +       if (!buf)
> +               return -ENOMEM;
> +
> +       *buf = version;
> +       ret = gud_usb_control_msg(usb, ifnum, false, GUD_REQ_SET_VERSION, 0, buf, sizeof(*buf));
> +       kfree(buf);
> +       if (ret == -EPIPE)
> +               return -EPROTONOSUPPORT;
> +       if (ret)
> +               return ret;
> +
> +       if (flags & GUD_DISPLAY_FLAG_STATUS_ON_SET) {
> +               u8 status;
> +
> +               ret = gud_usb_get_status(usb, ifnum, &status);
> +               if (!ret && status != GUD_STATUS_OK)
> +                       ret = -EPROTONOSUPPORT;
> +       }
> +
> +       return ret;
> +}
> +
> +static int gud_get_properties(struct gud_device *gdrm, unsigned int num_properties)
> +{
> +       struct gud_property_req *properties;
> +       unsigned int i;
> +       int ret;
> +
> +       if (!num_properties)
> +               return 0;
> +
> +       gdrm->properties = drmm_kcalloc(&gdrm->drm, num_properties, sizeof(*gdrm->properties),
> +                                       GFP_KERNEL);
> +       if (!gdrm->properties)
> +               return -ENOMEM;
> +
> +       properties = kcalloc(num_properties, sizeof(*properties), GFP_KERNEL);
> +       if (!properties)
> +               return -ENOMEM;
> +
> +       ret = gud_usb_get(gdrm, GUD_REQ_GET_PROPERTIES, 0,
> +                         properties, num_properties * sizeof(*properties));
> +       if (ret)
> +               goto out;
> +
> +       for (i = 0; i < num_properties; i++) {
> +               u16 prop = le16_to_cpu(properties[i].prop);
> +               u64 val = le64_to_cpu(properties[i].val);
> +
> +               switch (prop) {
> +               case GUD_PROPERTY_ROTATION:
> +                       /*
> +                        * DRM UAPI matches the protocol so use the value directly,
> +                        * but mask out any additions on future devices.
> +                        */
> +                       val &= GUD_ROTATION_MASK;
> +                       ret = drm_plane_create_rotation_property(&gdrm->pipe.plane,
> +                                                                DRM_MODE_ROTATE_0, val);
> +                       break;
> +               default:
> +                       /* New ones might show up in future devices, skip those we don't know. */
> +                       drm_dbg(&gdrm->drm, "Unknown property: %u\n", prop);
> +                       continue;
> +               }
> +
> +               if (ret)
> +                       goto out;
> +
> +               gdrm->properties[gdrm->num_properties++] = prop;
> +       }
> +out:
> +       kfree(properties);
> +
> +       return ret;
> +}
> +
> +static int gud_stats_debugfs(struct seq_file *m, void *data)
> +{
> +       struct drm_info_node *node = m->private;
> +       struct gud_device *gdrm = to_gud_device(node->minor->dev);
> +       char buf[10];
> +
> +       string_get_size(gdrm->bulk_len, 1, STRING_UNITS_2, buf, sizeof(buf));
> +       seq_printf(m, "Max buffer size: %s\n", buf);
> +       seq_printf(m, "Number of errors:  %u\n", gdrm->stats_num_errors);
> +
> +       seq_puts(m, "Compression:      ");
> +       if (gdrm->compression & GUD_COMPRESSION_LZ4)
> +               seq_puts(m, " lz4");
> +       seq_puts(m, "\n");
> +
> +       if (gdrm->compression) {
> +               u64 remainder;
> +               u64 ratio = div64_u64_rem(gdrm->stats_length, gdrm->stats_actual_length,
> +                                         &remainder);
> +               u64 ratio_frac = div64_u64(remainder * 10, gdrm->stats_actual_length);
> +
> +               seq_printf(m, "Compression ratio: %llu.%llu\n", ratio, ratio_frac);
> +       }
> +
> +       return 0;
> +}
> +
> +static const struct drm_info_list gud_debugfs_list[] = {
> +       { "stats", gud_stats_debugfs, 0, NULL },
> +};
> +
> +static void gud_debugfs_init(struct drm_minor *minor)
> +{
> +       drm_debugfs_create_files(gud_debugfs_list, ARRAY_SIZE(gud_debugfs_list),
> +                                minor->debugfs_root, minor);
> +}
> +
> +static const struct drm_simple_display_pipe_funcs gud_pipe_funcs = {
> +       .check      = gud_pipe_check,
> +       .update     = gud_pipe_update,
> +       .prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb,
> +};
> +
> +static const struct drm_mode_config_funcs gud_mode_config_funcs = {
> +       .fb_create = drm_gem_fb_create_with_dirty,
> +       .atomic_check = drm_atomic_helper_check,
> +       .atomic_commit = drm_atomic_helper_commit,
> +};
> +
> +static const u64 gud_pipe_modifiers[] = {
> +       DRM_FORMAT_MOD_LINEAR,
> +       DRM_FORMAT_MOD_INVALID
> +};
> +
> +DEFINE_DRM_GEM_FOPS(gud_fops);
> +
> +static const struct drm_driver gud_drm_driver = {
> +       .driver_features        = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
> +       .fops                   = &gud_fops,
> +       DRM_GEM_SHMEM_DRIVER_OPS,
> +       .debugfs_init           = gud_debugfs_init,
> +
> +       .name                   = "gud",
> +       .desc                   = "Generic USB Display",
> +       .date                   = "20200422",
> +       .major                  = 1,
> +       .minor                  = 0,
> +};
> +
> +static void gud_free_buffers_and_mutex(struct drm_device *drm, void *unused)
> +{
> +       struct gud_device *gdrm = to_gud_device(drm);
> +
> +       vfree(gdrm->compress_buf);
> +       kfree(gdrm->bulk_buf);
> +       mutex_destroy(&gdrm->ctrl_lock);
> +       mutex_destroy(&gdrm->damage_lock);
> +}
> +
> +static int gud_probe(struct usb_interface *interface, const struct usb_device_id *id)
> +{
> +       u8 ifnum = interface->cur_altsetting->desc.bInterfaceNumber;
> +       struct usb_device *usb = interface_to_usbdev(interface);
> +       struct device *dev = &interface->dev;
> +       const struct drm_format_info *xrgb8888_emulation_format = NULL;
> +       bool rgb565_supported = false, xrgb8888_supported = false;
> +       struct usb_endpoint_descriptor *bulk_out;
> +       struct gud_display_descriptor_req desc;
> +       unsigned int num_formats = 0;
> +       struct gud_device *gdrm;
> +       size_t max_buffer_size = 0;
> +       struct drm_device *drm;
> +       u8 *formats_dev;
> +       u32 *formats;
> +       int ret, i;
> +
> +       ret = usb_find_bulk_out_endpoint(interface->cur_altsetting, &bulk_out);
> +       if (ret)
> +               return ret;
> +
> +       ret = gud_get_display_descriptor(interface, &desc);
> +       if (ret) {
> +               DRM_DEV_DEBUG_DRIVER(dev, "Not a display interface: ret=%d\n", ret);
> +               return -ENODEV;
> +       }
> +
> +       if (desc.version > 1) {
> +               ret = gud_set_version(usb, ifnum, le32_to_cpu(desc.flags), 1);
> +               if (ret) {
> +                       if (ret == -EPROTONOSUPPORT)
> +                               dev_err(dev, "Protocol version %u is not supported\n",
> +                                       desc.version);
> +                       return ret;
> +               }
> +       }
> +
> +       gdrm = devm_drm_dev_alloc(dev, &gud_drm_driver, struct gud_device, drm);
> +       if (IS_ERR(gdrm))
> +               return PTR_ERR(gdrm);
> +
> +       drm = &gdrm->drm;
> +       drm->mode_config.funcs = &gud_mode_config_funcs;
> +       ret = drmm_mode_config_init(drm);
> +       if (ret)
> +               return ret;
> +
> +       gdrm->ifnum = ifnum;
> +       gdrm->flags = le32_to_cpu(desc.flags);
> +       gdrm->compression = desc.compression & GUD_COMPRESSION_LZ4;
> +
> +       if (gdrm->flags & GUD_DISPLAY_FLAG_FULL_UPDATE && gdrm->compression)
> +               return -EINVAL;
> +
> +       mutex_init(&gdrm->ctrl_lock);
> +       mutex_init(&gdrm->damage_lock);
> +       INIT_WORK(&gdrm->work, gud_flush_work);
> +       gud_clear_damage(gdrm);
> +
> +       ret = drmm_add_action_or_reset(drm, gud_free_buffers_and_mutex, NULL);
> +       if (ret)
> +               return ret;
> +
> +       drm->mode_config.min_width = le32_to_cpu(desc.min_width);
> +       drm->mode_config.max_width = le32_to_cpu(desc.max_width);
> +       drm->mode_config.min_height = le32_to_cpu(desc.min_height);
> +       drm->mode_config.max_height = le32_to_cpu(desc.max_height);
> +
> +       formats_dev = devm_kmalloc(dev, desc.num_formats, GFP_KERNEL);
> +       /* Add room for emulated XRGB8888 */
> +       formats = devm_kmalloc_array(dev, desc.num_formats + 1, sizeof(*formats), GFP_KERNEL);
> +       if (!formats_dev || !formats)
> +               return -ENOMEM;

Today I learned that we copy these, might be good to explain this in
the kerneldoc for all these functions that the caller can throw away
the formats and modifiers array afterwards. I got really confused here
for a moment.

> +
> +       ret = gud_usb_get(gdrm, GUD_REQ_GET_FORMATS, 0, formats_dev, desc.num_formats);
> +       if (ret)
> +               return ret;
> +
> +       for (i = 0; i < desc.num_formats; i++) {
> +               const struct drm_format_info *info;
> +               size_t fmt_buf_size;
> +               u32 format;
> +
> +               format = gud_to_fourcc(formats_dev[i]);
> +               if (!format) {
> +                       drm_dbg(drm, "Unsupported format: 0x%02x\n", formats_dev[i]);
> +                       continue;
> +               }
> +
> +               if (format == GUD_DRM_FORMAT_R1)
> +                       info = &gud_drm_format_r1;
> +               else
> +                       info = drm_format_info(format);
> +
> +               switch (format) {
> +               case GUD_DRM_FORMAT_R1:
> +                       xrgb8888_emulation_format = info;
> +                       break;
> +               case DRM_FORMAT_RGB565:
> +                       rgb565_supported = true;
> +                       if (!xrgb8888_emulation_format)
> +                               xrgb8888_emulation_format = info;
> +                       break;
> +               case DRM_FORMAT_XRGB8888:
> +                       xrgb8888_supported = true;
> +                       break;
> +               };
> +
> +               fmt_buf_size = drm_format_info_min_pitch(info, 0, drm->mode_config.max_width) *
> +                              drm->mode_config.max_height;
> +               max_buffer_size = max(max_buffer_size, fmt_buf_size);
> +
> +               if (format == GUD_DRM_FORMAT_R1)
> +                       continue; /* Internal not for userspace */
> +
> +               formats[num_formats++] = format;
> +       }
> +
> +       if (!num_formats && !xrgb8888_emulation_format) {
> +               dev_err(dev, "No supported pixel formats found\n");
> +               return -EINVAL;
> +       }
> +
> +       /* Prefer speed over color depth */
> +       if (rgb565_supported)
> +               drm->mode_config.preferred_depth = 16;
> +
> +       if (!xrgb8888_supported && xrgb8888_emulation_format) {
> +               gdrm->xrgb8888_emulation_format = xrgb8888_emulation_format;
> +               formats[num_formats++] = DRM_FORMAT_XRGB8888;
> +       }
> +
> +       if (desc.max_buffer_size)
> +               max_buffer_size = le32_to_cpu(desc.max_buffer_size);
> +retry:
> +       /*
> +        * Use plain kmalloc here since devm_kmalloc() places struct devres at the beginning
> +        * of the buffer it allocates. This wastes a lot of memory when allocating big buffers.
> +        * Asking for 2M would actually allocate 4M. This would also prevent getting the biggest
> +        * possible buffer potentially leading to split transfers.
> +        */
> +       gdrm->bulk_buf = kmalloc(max_buffer_size, GFP_KERNEL | __GFP_NOWARN);
> +       if (!gdrm->bulk_buf) {
> +               max_buffer_size = roundup_pow_of_two(max_buffer_size) / 2;
> +               if (max_buffer_size < SZ_512K)
> +                       return -ENOMEM;
> +               goto retry;
> +       }
> +
> +       gdrm->bulk_pipe = usb_sndbulkpipe(usb, usb_endpoint_num(bulk_out));
> +       gdrm->bulk_len = max_buffer_size;
> +
> +       if (gdrm->compression & GUD_COMPRESSION_LZ4) {
> +               gdrm->lz4_comp_mem = devm_kmalloc(dev, LZ4_MEM_COMPRESS, GFP_KERNEL);
> +               if (!gdrm->lz4_comp_mem)
> +                       return -ENOMEM;
> +
> +               gdrm->compress_buf = vmalloc(gdrm->bulk_len);
> +               if (!gdrm->compress_buf)
> +                       return -ENOMEM;
> +       }
> +
> +       ret = drm_simple_display_pipe_init(drm, &gdrm->pipe, &gud_pipe_funcs,
> +                                          formats, num_formats,
> +                                          gud_pipe_modifiers, NULL);
> +       if (ret)
> +               return ret;
> +
> +       devm_kfree(dev, formats);
> +       devm_kfree(dev, formats_dev);
> +
> +       ret = gud_get_properties(gdrm, desc.num_properties);
> +       if (ret)
> +               return ret;
> +
> +       drm_plane_enable_fb_damage_clips(&gdrm->pipe.plane);
> +
> +       for (i = 0; i < desc.num_connectors; i++) {
> +               ret = gud_connector_create(gdrm, i);
> +               if (ret)
> +                       return ret;
> +       }
> +
> +       drm_mode_config_reset(drm);
> +
> +       usb_set_intfdata(interface, gdrm);
> +
> +       ret = drm_dev_register(drm, 0);
> +       if (ret)
> +               return ret;
> +
> +       drm_kms_helper_poll_init(drm);
> +
> +       drm_fbdev_generic_setup(drm, 0);
> +
> +       return 0;
> +}
> +
> +static void gud_disconnect(struct usb_interface *interface)
> +{
> +       struct gud_device *gdrm = usb_get_intfdata(interface);
> +       struct drm_device *drm = &gdrm->drm;
> +
> +       drm_dbg(drm, "%s:\n", __func__);
> +
> +       drm_kms_helper_poll_fini(drm);
> +       drm_dev_unplug(drm);
> +       drm_atomic_helper_shutdown(drm);
> +}
> +
> +static int gud_suspend(struct usb_interface *interface, pm_message_t message)
> +{
> +       struct gud_device *gdrm = usb_get_intfdata(interface);
> +
> +       return drm_mode_config_helper_suspend(&gdrm->drm);
> +}
> +
> +static int gud_resume(struct usb_interface *interface)
> +{
> +       struct gud_device *gdrm = usb_get_intfdata(interface);
> +
> +       drm_mode_config_helper_resume(&gdrm->drm);
> +
> +       return 0;
> +}
> +
> +static const struct usb_device_id gud_id_table[] = {
> +       { USB_DEVICE_INTERFACE_CLASS(0x1d50, 0x614d, USB_CLASS_VENDOR_SPEC) },
> +       { }
> +};
> +
> +MODULE_DEVICE_TABLE(usb, gud_id_table);
> +
> +static struct usb_driver gud_usb_driver = {
> +       .name           = "gud",
> +       .probe          = gud_probe,
> +       .disconnect     = gud_disconnect,
> +       .id_table       = gud_id_table,
> +       .suspend        = gud_suspend,
> +       .resume         = gud_resume,
> +       .reset_resume   = gud_resume,
> +};
> +
> +module_usb_driver(gud_usb_driver);
> +
> +MODULE_AUTHOR("Noralf Trønnes");
> +MODULE_LICENSE("Dual MIT/GPL");
> diff --git a/drivers/gpu/drm/gud/gud_internal.h b/drivers/gpu/drm/gud/gud_internal.h
> new file mode 100644
> index 000000000000..46e7d7ed2c69
> --- /dev/null
> +++ b/drivers/gpu/drm/gud/gud_internal.h
> @@ -0,0 +1,148 @@
> +/* SPDX-License-Identifier: MIT */
> +
> +#ifndef __LINUX_GUD_INTERNAL_H
> +#define __LINUX_GUD_INTERNAL_H
> +
> +#include <linux/list.h>
> +#include <linux/mutex.h>
> +#include <linux/usb.h>
> +#include <linux/workqueue.h>
> +#include <uapi/drm/drm_fourcc.h>
> +
> +#include <drm/drm_modes.h>
> +#include <drm/drm_simple_kms_helper.h>
> +
> +struct gud_device {
> +       struct drm_device drm;
> +       struct drm_simple_display_pipe pipe;
> +       struct work_struct work;
> +       u8 ifnum;
> +       u32 flags;
> +       const struct drm_format_info *xrgb8888_emulation_format;
> +
> +       u16 *properties;
> +       unsigned int num_properties;
> +
> +       unsigned int bulk_pipe;
> +       void *bulk_buf;
> +       size_t bulk_len;
> +
> +       u8 compression;
> +       void *lz4_comp_mem;
> +       void *compress_buf;
> +
> +       u64 stats_length;
> +       u64 stats_actual_length;
> +       unsigned int stats_num_errors;
> +
> +       struct mutex ctrl_lock; /* Serialize get/set and status transfers */
> +
> +       struct mutex damage_lock; /* Protects the following members: */
> +       struct drm_framebuffer *fb;
> +       struct drm_rect damage;
> +       bool prev_flush_failed;
> +};
> +
> +static inline struct gud_device *to_gud_device(struct drm_device *drm)
> +{
> +       return container_of(drm, struct gud_device, drm);
> +}
> +
> +static inline struct usb_device *gud_to_usb_device(struct gud_device *gdrm)
> +{
> +       return interface_to_usbdev(to_usb_interface(gdrm->drm.dev));
> +}
> +
> +int gud_usb_get(struct gud_device *gdrm, u8 request, u16 index, void *buf, size_t len);
> +int gud_usb_set(struct gud_device *gdrm, u8 request, u16 index, void *buf, size_t len);
> +int gud_usb_write8(struct gud_device *gdrm, u8 request, u8 val);
> +
> +void gud_clear_damage(struct gud_device *gdrm);
> +void gud_flush_work(struct work_struct *work);
> +int gud_pipe_check(struct drm_simple_display_pipe *pipe,
> +                  struct drm_plane_state *new_plane_state,
> +                  struct drm_crtc_state *new_crtc_state);
> +void gud_pipe_update(struct drm_simple_display_pipe *pipe,
> +                    struct drm_plane_state *old_state);
> +int gud_connector_fill_properties(struct drm_connector *connector,
> +                                 struct drm_connector_state *connector_state,
> +                                 struct gud_property_req *properties);
> +int gud_connector_create(struct gud_device *gdrm, unsigned int index);
> +
> +#define GUD_DRM_FORMAT_R1      fourcc_code('R', '1', ' ', ' ')

Hm I'd use somet 32bit value which isn't an legal ascii code, just to
make it clear it's all internal.

> +
> +static inline u8 gud_from_fourcc(u32 fourcc)
> +{
> +       switch (fourcc) {
> +       case GUD_DRM_FORMAT_R1:
> +               return GUD_PIXEL_FORMAT_R1;
> +       case DRM_FORMAT_RGB565:
> +               return GUD_PIXEL_FORMAT_RGB565;
> +       case DRM_FORMAT_XRGB8888:
> +               return GUD_PIXEL_FORMAT_XRGB8888;
> +       case DRM_FORMAT_ARGB8888:
> +               return GUD_PIXEL_FORMAT_ARGB8888;
> +       };
> +
> +       return 0;
> +}
> +
> +static inline u32 gud_to_fourcc(u8 format)
> +{
> +       switch (format) {
> +       case GUD_PIXEL_FORMAT_R1:
> +               return GUD_DRM_FORMAT_R1;
> +       case GUD_PIXEL_FORMAT_RGB565:
> +               return DRM_FORMAT_RGB565;
> +       case GUD_PIXEL_FORMAT_XRGB8888:
> +               return DRM_FORMAT_XRGB8888;
> +       case GUD_PIXEL_FORMAT_ARGB8888:
> +               return DRM_FORMAT_ARGB8888;
> +       };
> +
> +       return 0;
> +}
> +
> +static inline void gud_from_display_mode(struct gud_display_mode_req *dst,
> +                                        const struct drm_display_mode *src)
> +{
> +       u32 flags = src->flags & GUD_DISPLAY_MODE_FLAG_USER_MASK;
> +
> +       if (src->type & DRM_MODE_TYPE_PREFERRED)
> +               flags |= GUD_DISPLAY_MODE_FLAG_PREFERRED;
> +
> +       dst->clock = cpu_to_le32(src->clock);
> +       dst->hdisplay = cpu_to_le16(src->hdisplay);
> +       dst->hsync_start = cpu_to_le16(src->hsync_start);
> +       dst->hsync_end = cpu_to_le16(src->hsync_end);
> +       dst->htotal = cpu_to_le16(src->htotal);
> +       dst->vdisplay = cpu_to_le16(src->vdisplay);
> +       dst->vsync_start = cpu_to_le16(src->vsync_start);
> +       dst->vsync_end = cpu_to_le16(src->vsync_end);
> +       dst->vtotal = cpu_to_le16(src->vtotal);
> +       dst->flags = cpu_to_le32(flags);
> +}
> +
> +static inline void gud_to_display_mode(struct drm_display_mode *dst,
> +                                      const struct gud_display_mode_req *src)
> +{
> +       u32 flags = le32_to_cpu(src->flags);
> +
> +       memset(dst, 0, sizeof(*dst));
> +       dst->clock = le32_to_cpu(src->clock);
> +       dst->hdisplay = le16_to_cpu(src->hdisplay);
> +       dst->hsync_start = le16_to_cpu(src->hsync_start);
> +       dst->hsync_end = le16_to_cpu(src->hsync_end);
> +       dst->htotal = le16_to_cpu(src->htotal);
> +       dst->vdisplay = le16_to_cpu(src->vdisplay);
> +       dst->vsync_start = le16_to_cpu(src->vsync_start);
> +       dst->vsync_end = le16_to_cpu(src->vsync_end);
> +       dst->vtotal = le16_to_cpu(src->vtotal);
> +       dst->flags = flags & GUD_DISPLAY_MODE_FLAG_USER_MASK;
> +       dst->type = DRM_MODE_TYPE_DRIVER;
> +       if (flags & GUD_DISPLAY_MODE_FLAG_PREFERRED)
> +               dst->type |= DRM_MODE_TYPE_PREFERRED;
> +       drm_mode_set_name(dst);
> +}
> +
> +#endif
> diff --git a/drivers/gpu/drm/gud/gud_pipe.c b/drivers/gpu/drm/gud/gud_pipe.c
> new file mode 100644
> index 000000000000..1eef930bcf9b
> --- /dev/null
> +++ b/drivers/gpu/drm/gud/gud_pipe.c
> @@ -0,0 +1,472 @@
> +// SPDX-License-Identifier: MIT
> +/*
> + * Copyright 2020 Noralf Trønnes
> + */
> +
> +#include <linux/dma-buf.h>
> +#include <linux/lz4.h>
> +#include <linux/usb.h>
> +#include <linux/workqueue.h>
> +
> +#include <drm/drm_atomic.h>
> +#include <drm/drm_connector.h>
> +#include <drm/drm_damage_helper.h>
> +#include <drm/drm_drv.h>
> +#include <drm/drm_format_helper.h>
> +#include <drm/drm_fourcc.h>
> +#include <drm/drm_framebuffer.h>
> +#include <drm/drm_gem_shmem_helper.h>
> +#include <drm/drm_print.h>
> +#include <drm/drm_rect.h>
> +#include <drm/drm_simple_kms_helper.h>
> +#include <drm/gud.h>
> +
> +#include "gud_internal.h"
> +
> +static bool gud_is_big_endian(void)
> +{
> +#if defined(__BIG_ENDIAN)
> +       return true;
> +#else
> +       return false;
> +#endif
> +}
> +
> +static size_t gud_xrgb8888_to_r124(u8 *dst, const struct drm_format_info *format,
> +                                  void *src, struct drm_framebuffer *fb,
> +                                  struct drm_rect *rect)
> +{
> +       unsigned int block_width = drm_format_info_block_width(format, 0);
> +       unsigned int bits_per_pixel = 8 / block_width;
> +       unsigned int x, y, width, height;
> +       u8 *p, *block = dst; /* Assign to silence compiler warning */
> +       size_t len;
> +       void *buf;
> +
> +       WARN_ON_ONCE(format->char_per_block[0] != 1);
> +
> +       /* Start on a byte boundary */
> +       rect->x1 = ALIGN_DOWN(rect->x1, block_width);
> +       width = drm_rect_width(rect);
> +       height = drm_rect_height(rect);
> +       len = drm_format_info_min_pitch(format, 0, width) * height;
> +
> +       buf = kmalloc(width * height, GFP_KERNEL);
> +       if (!buf)
> +               return 0;
> +
> +       drm_fb_xrgb8888_to_gray8(buf, src, fb, rect);
> +
> +       p = buf;
> +       for (y = 0; y < drm_rect_height(rect); y++) {
> +               for (x = 0; x < drm_rect_width(rect); x++) {
> +                       if (!(x % block_width)) {
> +                               block = dst++;
> +                               *block = 0;
> +                       }
> +
> +                       *block <<= bits_per_pixel;
> +                       *block |= (*p++) >> (8 - bits_per_pixel);
> +               }
> +       }
> +
> +       kfree(buf);
> +
> +       return len;
> +}
> +
> +static int gud_prep_flush(struct gud_device *gdrm, struct drm_framebuffer *fb,
> +                         const struct drm_format_info *format, struct drm_rect *rect,
> +                         struct gud_set_buffer_req *req)
> +{
> +       struct dma_buf_attachment *import_attach = fb->obj[0]->import_attach;
> +       u8 compression = gdrm->compression;
> +       struct dma_buf_map map;
> +       void *vaddr, *buf;
> +       size_t pitch, len;
> +       int ret = 0;
> +
> +       pitch = drm_format_info_min_pitch(format, 0, drm_rect_width(rect));
> +       len = pitch * drm_rect_height(rect);
> +       if (len > gdrm->bulk_len)
> +               return -E2BIG;
> +
> +       ret = drm_gem_shmem_vmap(fb->obj[0], &map);
> +       if (ret)
> +               return ret;
> +
> +       vaddr = map.vaddr + fb->offsets[0];
> +
> +       if (import_attach) {
> +               ret = dma_buf_begin_cpu_access(import_attach->dmabuf, DMA_FROM_DEVICE);
> +               if (ret)
> +                       goto vunmap;
> +       }
> +retry:
> +       if (compression)
> +               buf = gdrm->compress_buf;
> +       else
> +               buf = gdrm->bulk_buf;
> +
> +       /*
> +        * Imported buffers are assumed to be write-combined and thus uncached
> +        * with slow reads (at least on ARM).
> +        */
> +       if (format != fb->format) {
> +               if (format->format == GUD_DRM_FORMAT_R1) {
> +                       len = gud_xrgb8888_to_r124(buf, format, vaddr, fb, rect);
> +                       if (!len) {
> +                               ret = -ENOMEM;
> +                               goto end_cpu_access;
> +                       }
> +               } else if (format->format == DRM_FORMAT_RGB565) {
> +                       drm_fb_xrgb8888_to_rgb565(buf, vaddr, fb, rect, gud_is_big_endian());
> +               }
> +       } else if (gud_is_big_endian() && format->cpp[0] > 1) {
> +               drm_fb_swab(buf, vaddr, fb, rect, !import_attach);
> +       } else if (compression && !import_attach && pitch == fb->pitches[0]) {
> +               /* can compress directly from the framebuffer */
> +               buf = vaddr + rect->y1 * pitch;
> +       } else {
> +               drm_fb_memcpy(buf, vaddr, fb, rect);
> +       }
> +
> +       memset(req, 0, sizeof(*req));
> +       req->x = cpu_to_le32(rect->x1);
> +       req->y = cpu_to_le32(rect->y1);
> +       req->width = cpu_to_le32(drm_rect_width(rect));
> +       req->height = cpu_to_le32(drm_rect_height(rect));
> +       req->length = cpu_to_le32(len);
> +
> +       if (compression & GUD_COMPRESSION_LZ4) {
> +               int complen;
> +
> +               complen = LZ4_compress_default(buf, gdrm->bulk_buf, len, len, gdrm->lz4_comp_mem);
> +               if (complen < 0) {
> +                       compression = 0;
> +                       goto retry;
> +               }
> +
> +               req->compression = GUD_COMPRESSION_LZ4;
> +               req->compressed_length = cpu_to_le32(complen);
> +       }
> +
> +end_cpu_access:
> +       if (import_attach)
> +               dma_buf_end_cpu_access(import_attach->dmabuf, DMA_FROM_DEVICE);
> +vunmap:
> +       drm_gem_shmem_vunmap(fb->obj[0], &map);
> +
> +       return ret;
> +}
> +
> +static int gud_flush_rect(struct gud_device *gdrm, struct drm_framebuffer *fb,
> +                         const struct drm_format_info *format, struct drm_rect *rect)
> +{
> +       struct usb_device *usb = gud_to_usb_device(gdrm);
> +       struct gud_set_buffer_req req;
> +       int ret, actual_length;
> +       size_t len, trlen;
> +
> +       drm_dbg(&gdrm->drm, "Flushing [FB:%d] " DRM_RECT_FMT "\n", fb->base.id, DRM_RECT_ARG(rect));
> +
> +       ret = gud_prep_flush(gdrm, fb, format, rect, &req);
> +       if (ret)
> +               return ret;
> +
> +       len = le32_to_cpu(req.length);
> +
> +       if (req.compression)
> +               trlen = le32_to_cpu(req.compressed_length);
> +       else
> +               trlen = len;
> +
> +       gdrm->stats_length += len;
> +       /* Did it wrap around? */
> +       if (gdrm->stats_length <= len && gdrm->stats_actual_length) {
> +               gdrm->stats_length = len;
> +               gdrm->stats_actual_length = 0;
> +       }
> +       gdrm->stats_actual_length += trlen;
> +
> +       if (!(gdrm->flags & GUD_DISPLAY_FLAG_FULL_UPDATE) || gdrm->prev_flush_failed) {
> +               ret = gud_usb_set(gdrm, GUD_REQ_SET_BUFFER, 0, &req, sizeof(req));
> +               if (ret)
> +                       return ret;
> +       }
> +
> +       ret = usb_bulk_msg(usb, gdrm->bulk_pipe, gdrm->bulk_buf, trlen,
> +                          &actual_length, msecs_to_jiffies(3000));
> +       if (!ret && trlen != actual_length)
> +               ret = -EIO;
> +       if (ret)
> +               gdrm->stats_num_errors++;
> +
> +       return ret;
> +}
> +
> +void gud_clear_damage(struct gud_device *gdrm)
> +{
> +       gdrm->damage.x1 = INT_MAX;
> +       gdrm->damage.y1 = INT_MAX;
> +       gdrm->damage.x2 = 0;
> +       gdrm->damage.y2 = 0;
> +}
> +
> +static void gud_add_damage(struct gud_device *gdrm, struct drm_rect *damage)
> +{
> +       gdrm->damage.x1 = min(gdrm->damage.x1, damage->x1);
> +       gdrm->damage.y1 = min(gdrm->damage.y1, damage->y1);
> +       gdrm->damage.x2 = max(gdrm->damage.x2, damage->x2);
> +       gdrm->damage.y2 = max(gdrm->damage.y2, damage->y2);
> +}
> +
> +static void gud_retry_failed_flush(struct gud_device *gdrm, struct drm_framebuffer *fb,
> +                                  struct drm_rect *damage)
> +{
> +       /*
> +        * pipe_update waits for the worker when the display mode is going to change.
> +        * This ensures that the width and height is still the same making it safe to
> +        * add back the damage.
> +        */
> +
> +       mutex_lock(&gdrm->damage_lock);
> +       if (!gdrm->fb) {
> +               drm_framebuffer_get(fb);
> +               gdrm->fb = fb;
> +       }
> +       gud_add_damage(gdrm, damage);
> +       mutex_unlock(&gdrm->damage_lock);
> +
> +       /* Retry only once to avoid a possible storm in case of continues errors. */
> +       if (!gdrm->prev_flush_failed)
> +               queue_work(system_long_wq, &gdrm->work);
> +       gdrm->prev_flush_failed = true;
> +}
> +
> +void gud_flush_work(struct work_struct *work)
> +{
> +       struct gud_device *gdrm = container_of(work, struct gud_device, work);
> +       const struct drm_format_info *format;
> +       struct drm_framebuffer *fb;
> +       struct drm_rect damage;
> +       unsigned int i, lines;
> +       int idx, ret = 0;
> +       size_t pitch;
> +
> +       if (!drm_dev_enter(&gdrm->drm, &idx))
> +               return;
> +
> +       mutex_lock(&gdrm->damage_lock);
> +       fb = gdrm->fb;
> +       gdrm->fb = NULL;
> +       damage = gdrm->damage;
> +       gud_clear_damage(gdrm);
> +       mutex_unlock(&gdrm->damage_lock);
> +
> +       if (!fb)
> +               goto out;
> +
> +       format = fb->format;
> +       if (format->format == DRM_FORMAT_XRGB8888 && gdrm->xrgb8888_emulation_format)
> +               format = gdrm->xrgb8888_emulation_format;
> +
> +       /* Split update if it's too big */
> +       pitch = drm_format_info_min_pitch(format, 0, drm_rect_width(&damage));
> +       lines = drm_rect_height(&damage);
> +
> +       if (gdrm->bulk_len < lines * pitch)
> +               lines = gdrm->bulk_len / pitch;
> +
> +       for (i = 0; i < DIV_ROUND_UP(drm_rect_height(&damage), lines); i++) {
> +               struct drm_rect rect = damage;
> +
> +               rect.y1 += i * lines;
> +               rect.y2 = min_t(u32, rect.y1 + lines, damage.y2);
> +
> +               ret = gud_flush_rect(gdrm, fb, format, &rect);
> +               if (ret) {
> +                       if (ret != -ENODEV && ret != -ECONNRESET &&
> +                           ret != -ESHUTDOWN && ret != -EPROTO) {
> +                               gud_retry_failed_flush(gdrm, fb, &damage);
> +                               dev_err_once(fb->dev->dev,
> +                                            "Failed to flush framebuffer: error=%d\n", ret);
> +                       }
> +                       break;
> +               }
> +
> +               gdrm->prev_flush_failed = false;
> +       }
> +
> +       drm_framebuffer_put(fb);
> +out:
> +       drm_dev_exit(idx);
> +}
> +
> +static void gud_fb_queue_damage(struct gud_device *gdrm, struct drm_framebuffer *fb,
> +                               struct drm_rect *damage)
> +{
> +       struct drm_framebuffer *old_fb = NULL;
> +
> +       mutex_lock(&gdrm->damage_lock);
> +
> +       if (fb != gdrm->fb) {
> +               old_fb = gdrm->fb;
> +               drm_framebuffer_get(fb);
> +               gdrm->fb = fb;
> +       }
> +
> +       gud_add_damage(gdrm, damage);
> +
> +       mutex_unlock(&gdrm->damage_lock);
> +
> +       queue_work(system_long_wq, &gdrm->work);
> +
> +       if (old_fb)
> +               drm_framebuffer_put(old_fb);
> +}
> +
> +int gud_pipe_check(struct drm_simple_display_pipe *pipe,
> +                  struct drm_plane_state *new_plane_state,
> +                  struct drm_crtc_state *new_crtc_state)
> +{
> +       struct gud_device *gdrm = to_gud_device(pipe->crtc.dev);
> +       struct drm_plane_state *old_plane_state = pipe->plane.state;
> +       const struct drm_display_mode *mode = &new_crtc_state->mode;
> +       struct drm_atomic_state *state = new_plane_state->state;
> +       struct drm_framebuffer *old_fb = old_plane_state->fb;
> +       struct drm_connector_state *connector_state = NULL;
> +       struct drm_framebuffer *fb = new_plane_state->fb;
> +       const struct drm_format_info *format = fb->format;
> +       struct drm_connector *connector;
> +       int idx, ret, num_properties;
> +       struct gud_state_req *req;
> +       unsigned int i;
> +       size_t len;
> +
> +       if (WARN_ON_ONCE(!fb))
> +               return -EINVAL;
> +
> +       if (old_plane_state->rotation != new_plane_state->rotation)
> +               new_crtc_state->mode_changed = true;
> +
> +       if (old_fb && old_fb->format != format)
> +               new_crtc_state->mode_changed = true;
> +
> +       if (!new_crtc_state->mode_changed && !new_crtc_state->connectors_changed)
> +               return 0;
> +
> +       /* Only one connector is supported */
> +       if (hweight32(new_crtc_state->connector_mask) != 1)
> +               return -EINVAL;
> +
> +       if (format->format == DRM_FORMAT_XRGB8888 && gdrm->xrgb8888_emulation_format)
> +               format = gdrm->xrgb8888_emulation_format;
> +
> +       for_each_new_connector_in_state(state, connector, connector_state, i) {
> +               if (connector_state->crtc)
> +                       break;
> +       }
> +
> +       if (WARN_ON_ONCE(!connector_state))
> +               return -ENOENT;
> +
> +       num_properties = gud_connector_fill_properties(connector, NULL, NULL);
> +       if (num_properties < 0)
> +               return num_properties;
> +
> +       num_properties += gdrm->num_properties;
> +
> +       len = struct_size(req, properties, num_properties);
> +       req = kzalloc(len, GFP_KERNEL);
> +       if (!req)
> +               return -ENOMEM;
> +
> +       gud_from_display_mode(&req->mode, mode);
> +
> +       req->format = gud_from_fourcc(format->format);
> +       if (WARN_ON_ONCE(!req->format))
> +               return -EINVAL;
> +
> +       req->connector = drm_connector_index(connector);
> +       req->num_properties = num_properties;
> +
> +       num_properties = gud_connector_fill_properties(connector, connector_state,
> +                                                      req->properties);
> +
> +       for (i = 0; i < gdrm->num_properties; i++) {
> +               u16 prop = gdrm->properties[i];
> +               u64 val;
> +
> +               switch (prop) {
> +               case GUD_PROPERTY_ROTATION:
> +                       /* DRM UAPI matches the protocol so use value directly */
> +                       val = new_plane_state->rotation;
> +                       break;
> +               default:
> +                       WARN_ON_ONCE(1);
> +                       ret = -EINVAL;
> +                       goto out;
> +               }
> +
> +               req->properties[num_properties + i].prop = cpu_to_le16(prop);
> +               req->properties[num_properties + i].val = cpu_to_le64(val);
> +       }
> +
> +       if (drm_dev_enter(fb->dev, &idx)) {
> +               ret = gud_usb_set(gdrm, GUD_REQ_SET_STATE_CHECK, 0, req, len);
> +               drm_dev_exit(idx);
> +       }  else {
> +               ret = -ENODEV;
> +       }
> +out:
> +       kfree(req);
> +
> +       return ret;
> +}
> +
> +void gud_pipe_update(struct drm_simple_display_pipe *pipe,
> +                    struct drm_plane_state *old_state)
> +{
> +       struct drm_device *drm = pipe->crtc.dev;
> +       struct gud_device *gdrm = to_gud_device(drm);
> +       struct drm_plane_state *state = pipe->plane.state;
> +       struct drm_framebuffer *fb = state->fb;
> +       struct drm_crtc *crtc = &pipe->crtc;
> +       struct drm_rect damage;
> +       int idx;
> +
> +       if (crtc->state->mode_changed || !crtc->state->enable) {
> +               cancel_work_sync(&gdrm->work);
> +               mutex_lock(&gdrm->damage_lock);
> +               if (gdrm->fb) {
> +                       drm_framebuffer_put(gdrm->fb);
> +                       gdrm->fb = NULL;
> +               }
> +               gud_clear_damage(gdrm);
> +               mutex_unlock(&gdrm->damage_lock);
> +       }
> +
> +       if (!drm_dev_enter(drm, &idx))
> +               return;
> +
> +       if (!old_state->fb)
> +               gud_usb_write8(gdrm, GUD_REQ_SET_CONTROLLER_ENABLE, 1);
> +
> +       if (fb && (crtc->state->mode_changed || crtc->state->connectors_changed))
> +               gud_usb_set(gdrm, GUD_REQ_SET_STATE_COMMIT, 0, NULL, 0);
> +
> +       if (crtc->state->active_changed)
> +               gud_usb_write8(gdrm, GUD_REQ_SET_DISPLAY_ENABLE, crtc->state->active);
> +
> +       if (drm_atomic_helper_damage_merged(old_state, state, &damage)) {
> +               if (gdrm->flags & GUD_DISPLAY_FLAG_FULL_UPDATE)
> +                       drm_rect_init(&damage, 0, 0, fb->width, fb->height);
> +               gud_fb_queue_damage(gdrm, fb, &damage);
> +       }
> +
> +       if (!crtc->state->enable)
> +               gud_usb_write8(gdrm, GUD_REQ_SET_CONTROLLER_ENABLE, 0);
> +
> +       drm_dev_exit(idx);
> +}
> diff --git a/include/drm/gud.h b/include/drm/gud.h
> new file mode 100644
> index 000000000000..ebf19f50f780
> --- /dev/null
> +++ b/include/drm/gud.h
> @@ -0,0 +1,356 @@
> +/* SPDX-License-Identifier: MIT */
> +/*
> + * Copyright 2020 Noralf Trønnes
> + */
> +
> +#ifndef __LINUX_GUD_H
> +#define __LINUX_GUD_H
> +
> +#include <linux/types.h>
> +
> +/*
> + * struct gud_display_descriptor_req - Display descriptor
> + * @magic: Magic value GUD_DISPLAY_MAGIC
> + * @version: Protocol version
> + * @flags: Flags
> + *         - STATUS_ON_SET: Always do a status request after a SET request.
> + *                          This is used by the Linux gadget driver since it has
> + *                          no way to control the status stage of a control OUT
> + *                          request that has a payload.
> + *         - FULL_UPDATE:   Always send the entire framebuffer when flushing changes.
> + *                          The GUD_REQ_SET_BUFFER request will not be sent
> + *                          before each bulk transfer, it will only be sent if the
> + *                          previous bulk transfer had failed. This gives the device
> + *                          a chance to reset its state machine if needed.
> + *                          This flag can not be used in combination with compression.
> + * @compression: Supported compression types
> + *               - GUD_COMPRESSION_LZ4: LZ4 lossless compression.
> + * @max_buffer_size: Maximum buffer size the device can handle (optional).
> + *                   This is useful for devices that don't have a big enough
> + *                   buffer to decompress the entire framebuffer in one go.
> + * @min_width: Minimum pixel width the controller can handle
> + * @max_width: Maximum width
> + * @min_height: Minimum height
> + * @max_height: Maximum height
> + * @num_formats: Number of supported pixel formats
> + * @num_properties: Number of properties that are not connector properties
> + * @num_connectors: Number of connectors
> + *
> + * Devices that have only one display mode will have min_width == max_width
> + * and min_height == max_height.
> + */
> +struct gud_display_descriptor_req {
> +       __u32 magic;
> +#define GUD_DISPLAY_MAGIC                      0x1d50614d
> +       __u8 version;
> +       __le32 flags;
> +#define GUD_DISPLAY_FLAG_STATUS_ON_SET         BIT(0)
> +#define GUD_DISPLAY_FLAG_FULL_UPDATE           BIT(1)
> +       __u8 compression;
> +#define GUD_COMPRESSION_LZ4                    BIT(0)
> +       __le32 max_buffer_size;
> +       __le32 min_width;
> +       __le32 max_width;
> +       __le32 min_height;
> +       __le32 max_height;
> +       __u8 num_formats;
> +       __u8 num_properties;
> +       __u8 num_connectors;
> +} __packed;
> +
> +/*
> + * struct gud_property_req - Property
> + * @prop: Property
> + * @val: Value
> + */
> +struct gud_property_req {
> +       __le16 prop;
> +       __le64 val;
> +} __packed;
> +
> +/*
> + * struct gud_display_mode_req - Display mode
> + * @clock: Pixel clock in kHz
> + * @hdisplay: Horizontal display size
> + * @hsync_start: Horizontal sync start
> + * @hsync_end: Horizontal sync end
> + * @htotal: Horizontal total size
> + * @vdisplay: Vertical display size
> + * @vsync_start: Vertical sync start
> + * @vsync_end: Vertical sync end
> + * @vtotal: Vertical total size
> + * @flags: Bits 0-13 are the same as in the RandR protocol and also what DRM uses.
> + *         The deprecated bits are reused for internal protocol flags leaving us
> + *         free to follow DRM for the other bits in the future.
> + *         - FLAG_PREFERRED: Set on the preferred display mode.
> + */
> +struct gud_display_mode_req {
> +       __le32 clock;
> +       __le16 hdisplay;
> +       __le16 hsync_start;
> +       __le16 hsync_end;
> +       __le16 htotal;
> +       __le16 vdisplay;
> +       __le16 vsync_start;
> +       __le16 vsync_end;
> +       __le16 vtotal;
> +       __le32 flags;
> +#define GUD_DISPLAY_MODE_FLAG_PHSYNC           BIT(0)
> +#define GUD_DISPLAY_MODE_FLAG_NHSYNC           BIT(1)
> +#define GUD_DISPLAY_MODE_FLAG_PVSYNC           BIT(2)
> +#define GUD_DISPLAY_MODE_FLAG_NVSYNC           BIT(3)
> +#define GUD_DISPLAY_MODE_FLAG_INTERLACE                BIT(4)
> +#define GUD_DISPLAY_MODE_FLAG_DBLSCAN          BIT(5)
> +#define GUD_DISPLAY_MODE_FLAG_CSYNC            BIT(6)
> +#define GUD_DISPLAY_MODE_FLAG_PCSYNC           BIT(7)
> +#define GUD_DISPLAY_MODE_FLAG_NCSYNC           BIT(8)
> +#define GUD_DISPLAY_MODE_FLAG_HSKEW            BIT(9)
> +/* BCast and PixelMultiplex are deprecated */
> +#define GUD_DISPLAY_MODE_FLAG_DBLCLK           BIT(12)
> +#define GUD_DISPLAY_MODE_FLAG_CLKDIV2          BIT(13)
> +#define GUD_DISPLAY_MODE_FLAG_USER_MASK                \
> +               (GUD_DISPLAY_MODE_FLAG_PHSYNC | GUD_DISPLAY_MODE_FLAG_NHSYNC | \
> +               GUD_DISPLAY_MODE_FLAG_PVSYNC | GUD_DISPLAY_MODE_FLAG_NVSYNC | \
> +               GUD_DISPLAY_MODE_FLAG_INTERLACE | GUD_DISPLAY_MODE_FLAG_DBLSCAN | \
> +               GUD_DISPLAY_MODE_FLAG_CSYNC | GUD_DISPLAY_MODE_FLAG_PCSYNC | \
> +               GUD_DISPLAY_MODE_FLAG_NCSYNC | GUD_DISPLAY_MODE_FLAG_HSKEW | \
> +               GUD_DISPLAY_MODE_FLAG_DBLCLK | GUD_DISPLAY_MODE_FLAG_CLKDIV2)
> +/* Internal protocol flags */
> +#define GUD_DISPLAY_MODE_FLAG_PREFERRED                BIT(10)
> +} __packed;
> +
> +/*
> + * struct gud_connector_descriptor_req - Connector descriptor
> + * @connector_type: Connector type (GUD_CONNECTOR_TYPE_*).
> + *                  If the host doesn't support the type it should fall back to PANEL.
> + * @flags: Flags
> + *         - POLL_STATUS: Connector status can change (polled every 10 seconds)
> + *         - INTERLACE: Interlaced modes are supported
> + *         - DOUBLESCAN: Doublescan modes are supported
> + * @num_properties: Number of supported properties
> + */
> +struct gud_connector_descriptor_req {
> +       __u8 connector_type;
> +#define GUD_CONNECTOR_TYPE_PANEL               0
> +#define GUD_CONNECTOR_TYPE_VGA                 1
> +#define GUD_CONNECTOR_TYPE_COMPOSITE           2
> +#define GUD_CONNECTOR_TYPE_SVIDEO              3
> +#define GUD_CONNECTOR_TYPE_COMPONENT           4
> +#define GUD_CONNECTOR_TYPE_DVI                 5
> +#define GUD_CONNECTOR_TYPE_DISPLAYPORT         6
> +#define GUD_CONNECTOR_TYPE_HDMI                        7
> +       __le32 flags;
> +#define GUD_CONNECTOR_FLAGS_POLL_STATUS                BIT(0)
> +#define GUD_CONNECTOR_FLAGS_INTERLACE          BIT(1)
> +#define GUD_CONNECTOR_FLAGS_DOUBLESCAN         BIT(2)
> +       __u8 num_properties;
> +} __packed;
> +
> +/*
> + * struct gud_connector_status_req - Connector status
> + * @status: Status
> + *          - DISCONNECTED: Connector is disconnected
> + *          - CONNECTED: Connector is connected
> + *          - UNKNOWN: Connection status is unknown
> + *          Flags:
> + *          - CHANGED: A change has happened since the last request
> + * @num_modes: Number of available display modes
> + * @edid_len: Length of EDID data
> + *
> + * If @num_modes is zero, EDID is used to create display modes.
> + * If both @num_modes and @edid_len are set, EDID is just passed on to userspace
> + * in the EDID connector property.
> + *
> + * Userspace will get a HOTPLUG uevent if one of the following is true:
> + * - Connection status has changed
> + * - @num_modes or @edid_len has changed
> + * - CHANGED is set
> + */
> +struct gud_connector_status_req {
> +       __u8 status;
> +#define GUD_CONNECTOR_STATUS_DISCONNECTED      0x00
> +#define GUD_CONNECTOR_STATUS_CONNECTED         0x01
> +#define GUD_CONNECTOR_STATUS_UNKNOWN           0x02
> +#define GUD_CONNECTOR_STATUS_CONNECTED_MASK    0x03
> +#define GUD_CONNECTOR_STATUS_CHANGED           BIT(7)
> +       __le16 num_modes;
> +       __le16 edid_len;
> +} __packed;
> +
> +/*
> + * struct gud_set_buffer_req - Set buffer transfer info
> + * @x: X position of rectangle
> + * @y: Y position
> + * @width: Pixel width of rectangle
> + * @height: Pixel height
> + * @length: Buffer length in bytes
> + * @compression: Transfer compression
> + * @compressed_length: Compressed buffer length
> + *
> + * This request is issued right before the bulk transfer.
> + * @x, @y, @width and @height specifies the rectangle where the buffer should be
> + * placed inside the framebuffer.
> + */
> +struct gud_set_buffer_req {
> +       __le32 x;
> +       __le32 y;
> +       __le32 width;
> +       __le32 height;
> +       __le32 length;
> +       __u8 compression;
> +       __le32 compressed_length;
> +} __packed;
> +
> +/*
> + * struct gud_state_req - Display state
> + * @mode: Display mode
> + * @format: Pixel format GUD_PIXEL_FORMAT_*
> + * @connector: Connector index
> + * @num_properties: Number of properties in the state
> + * @properties: Array of properties
> + *
> + * The entire state is transferred each time there's a change.
> + */
> +struct gud_state_req {
> +       struct gud_display_mode_req mode;
> +       __u8 format;
> +       __u8 connector;
> +       __u8 num_properties;
> +       struct gud_property_req properties[];
> +} __packed;
> +
> +/* List of supported connector properties: */
> +
> +/* Margins in pixels to deal with overscan, range 0-100 */
> +#define GUD_PROPERTY_TV_LEFT_MARGIN                    1
> +#define GUD_PROPERTY_TV_RIGHT_MARGIN                   2
> +#define GUD_PROPERTY_TV_TOP_MARGIN                     3
> +#define GUD_PROPERTY_TV_BOTTOM_MARGIN                  4
> +/* Number of modes are placed at _SHIFT in val on retrieval */
> +#define GUD_PROPERTY_TV_MODE                           5
> +  #define GUD_CONNECTOR_TV_MODE_NUM_SHIFT      16
> +/* Brightness in percent, range 0-100 */
> +#define GUD_PROPERTY_TV_BRIGHTNESS                     6
> +/* Contrast in percent, range 0-100 */
> +#define GUD_PROPERTY_TV_CONTRAST                       7
> +/* Flicker reduction in percent, range 0-100 */
> +#define GUD_PROPERTY_TV_FLICKER_REDUCTION              8
> +/* Overscan in percent, range 0-100 */
> +#define GUD_PROPERTY_TV_OVERSCAN                       9
> +/* Saturation in percent, range 0-100 */
> +#define GUD_PROPERTY_TV_SATURATION                     10
> +/* Hue in percent, range 0-100 */
> +#define GUD_PROPERTY_TV_HUE                            11
> +
> +/*
> + * Backlight brightness is in the range 0-100 inclusive. The value represents the human perceptual
> + * brightness and not a linear PWM value. 0 is minimum brightness which should not turn the
> + * backlight completely off. The DPMS connector property should be used to control power which will
> + * trigger a GUD_REQ_SET_DISPLAY_ENABLE request.
> + *
> + * This does not map to a DRM property, it is used with the backlight device.
> + */
> +#define GUD_PROPERTY_BACKLIGHT_BRIGHTNESS              12
> +
> +/* List of supported properties that are not connector propeties: */
> +
> +/*
> + * Plane rotation. Should return the supported bitmask on
> + * GUD_REQ_GET_PROPERTIES. GUD_ROTATION_0 is mandatory.
> + *
> + * Note: This is not display rotation so 90/270 will need scaling to make it fit (unless squared).
> + */
> +#define GUD_PROPERTY_ROTATION                          50
> +  #define GUD_ROTATION_0                       BIT(0)
> +  #define GUD_ROTATION_90                      BIT(1)
> +  #define GUD_ROTATION_180                     BIT(2)
> +  #define GUD_ROTATION_270                     BIT(3)
> +  #define GUD_ROTATION_REFLECT_X               BIT(4)
> +  #define GUD_ROTATION_REFLECT_Y               BIT(5)
> +  #define GUD_ROTATION_MASK                    (GUD_ROTATION_0 | GUD_ROTATION_90 | \
> +                                               GUD_ROTATION_180 | GUD_ROTATION_270 | \
> +                                               GUD_ROTATION_REFLECT_X | GUD_ROTATION_REFLECT_Y)
> +
> +/* USB Control requests: */
> +
> +/* Get status from the last GET/SET control request. Value is u8. */
> +#define GUD_REQ_GET_STATUS                             0x00
> +  /* Status values: */
> +  #define GUD_STATUS_OK                                0x00
> +  #define GUD_STATUS_BUSY                      0x01
> +  #define GUD_STATUS_REQUEST_NOT_SUPPORTED     0x02
> +  #define GUD_STATUS_PROTOCOL_ERROR            0x03
> +  #define GUD_STATUS_INVALID_PARAMETER         0x04
> +  #define GUD_STATUS_ERROR                     0x05
> +
> +/* Get display descriptor as a &gud_display_descriptor_req */
> +#define GUD_REQ_GET_DESCRIPTOR                         0x01
> +
> +/*
> + * If the host driver doesn't support the device protocol version it will send the versions it
> + * supports starting with the latest. If the device isn't backwards compatible or doesn't support
> + * the version the host suggests, it shall stall the request. The version is sent as u8.
> + */
> +#define GUD_REQ_SET_VERSION                            0x30
> +
> +/* Get supported pixel formats as a byte array of GUD_PIXEL_FORMAT_* */
> +#define GUD_REQ_GET_FORMATS                            0x40
> +  /* R1 is a 1-bit monochrome transfer format presented to userspace as XRGB8888 */
> +  #define GUD_PIXEL_FORMAT_R1                  0x01
> +  #define GUD_PIXEL_FORMAT_RGB565              0x40
> +  #define GUD_PIXEL_FORMAT_XRGB8888            0x80
> +  #define GUD_PIXEL_FORMAT_ARGB8888            0x81
> +
> +/*
> + * Get supported properties that are not connector propeties as a &gud_property_req array.
> + * gud_property_req.val often contains the initial value for the property.
> + */
> +#define GUD_REQ_GET_PROPERTIES                         0x41
> +
> +/* Connector requests have the connector index passed in the wValue field */
> +
> +/* Get connector descriptor as a &gud_connector_descriptor_req */
> +#define GUD_REQ_GET_CONNECTOR                          0x50
> +
> +/*
> + * Get properties supported by the connector as a &gud_property_req array.
> + * gud_property_req.val often contains the initial value for the property.
> + */
> +#define GUD_REQ_GET_CONNECTOR_PROPERTIES               0x51
> +
> +/*
> + * Issued when there's a TV_MODE property present.
> + * Gets an array of the supported TV_MODE names each entry of length
> + * GUD_CONNECTOR_TV_MODE_NAME_LEN. Names must be NUL-terminated.
> + */
> +#define GUD_REQ_GET_CONNECTOR_TV_MODE_VALUES           0x52
> +  #define GUD_CONNECTOR_TV_MODE_NAME_LEN       16
> +
> +/* When userspace checks connector status, this is issued first, not used for poll requests. */
> +#define GUD_REQ_SET_CONNECTOR_FORCE_DETECT             0x53
> +
> +/* Get connector status as &gud_connector_status_req. */
> +#define GUD_REQ_GET_CONNECTOR_STATUS                   0x54
> +
> +/* Get &gud_display_mode_req array of supported display modes */
> +#define GUD_REQ_GET_CONNECTOR_MODES                    0x55
> +
> +/* Get Extended Display Identification Data */
> +#define GUD_REQ_GET_CONNECTOR_EDID                     0x56
> +
> +/* Set buffer properties before bulk transfer as &gud_set_buffer_req */
> +#define GUD_REQ_SET_BUFFER                             0x60
> +
> +/* Check display configuration as &gud_state_req */
> +#define GUD_REQ_SET_STATE_CHECK                                0x61
> +
> +/* Apply the previous STATE_CHECK configuration */
> +#define GUD_REQ_SET_STATE_COMMIT                       0x62
> +
> +/* Enable/disable the display controller, value is u8: 0/1 */
> +#define GUD_REQ_SET_CONTROLLER_ENABLE                  0x63
> +
> +/* Enable/disable display/output (DPMS), value is u8: 0/1 */
> +#define GUD_REQ_SET_DISPLAY_ENABLE                     0x64
> +
> +#endif
> --
> 2.23.0
>
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel



-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 1/3] drm/uapi: Add USB connector type
  2021-01-20 17:42     ` Daniel Vetter
@ 2021-01-21  7:45       ` Thomas Zimmermann
  -1 siblings, 0 replies; 53+ messages in thread
From: Thomas Zimmermann @ 2021-01-21  7:45 UTC (permalink / raw)
  To: Daniel Vetter, Noralf Trønnes
  Cc: hudson, markus, peter, USB list, dri-devel, Tyler Hardin,
	Lubomir Rintel, pontus.fuchs, Sam Ravnborg


[-- Attachment #1.1: Type: text/plain, Size: 1777 bytes --]

Hi Noralf,

glad to hear from you! Welcome back!

Am 20.01.21 um 18:42 schrieb Daniel Vetter:
> On Wed, Jan 20, 2021 at 6:10 PM Noralf Trønnes <noralf@tronnes.org> wrote:
>>
>> Add a connector type for USB connected display panels.
>>
>> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
>> ---
>>   include/uapi/drm/drm_mode.h | 1 +
>>   1 file changed, 1 insertion(+)
>>
>> diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
>> index fed66a03c7ae..33024cc5d26e 100644
>> --- a/include/uapi/drm/drm_mode.h
>> +++ b/include/uapi/drm/drm_mode.h
>> @@ -367,6 +367,7 @@ enum drm_mode_subconnector {
>>   #define DRM_MODE_CONNECTOR_DPI         17
>>   #define DRM_MODE_CONNECTOR_WRITEBACK   18
>>   #define DRM_MODE_CONNECTOR_SPI         19
>> +#define DRM_MODE_CONNECTOR_USB         20

I would not call it USB. I could imagine that at some point a generic 
USB protocol could serve simple displays (i.e. in the sense of USB HID 
or data or imaging). (Maybe Thunderbold already counts.) Anyway, USB 
should be reserved for this case.

Best regards
Thomas

> 
> Beware, new connector types have in the past resulted in userspace
> burning&crashing. Maybe it's become better ...
> 
> Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
>>
>>   /**
>>    * struct drm_mode_get_connector - Get connector metadata.
>> --
>> 2.23.0
>>
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel@lists.freedesktop.org
>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
> 
> 
> 

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Felix Imendörffer


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 1/3] drm/uapi: Add USB connector type
@ 2021-01-21  7:45       ` Thomas Zimmermann
  0 siblings, 0 replies; 53+ messages in thread
From: Thomas Zimmermann @ 2021-01-21  7:45 UTC (permalink / raw)
  To: Daniel Vetter, Noralf Trønnes
  Cc: hudson, markus, Sam Ravnborg, USB list, dri-devel, Tyler Hardin,
	Lubomir Rintel, pontus.fuchs, peter


[-- Attachment #1.1.1: Type: text/plain, Size: 1777 bytes --]

Hi Noralf,

glad to hear from you! Welcome back!

Am 20.01.21 um 18:42 schrieb Daniel Vetter:
> On Wed, Jan 20, 2021 at 6:10 PM Noralf Trønnes <noralf@tronnes.org> wrote:
>>
>> Add a connector type for USB connected display panels.
>>
>> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
>> ---
>>   include/uapi/drm/drm_mode.h | 1 +
>>   1 file changed, 1 insertion(+)
>>
>> diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
>> index fed66a03c7ae..33024cc5d26e 100644
>> --- a/include/uapi/drm/drm_mode.h
>> +++ b/include/uapi/drm/drm_mode.h
>> @@ -367,6 +367,7 @@ enum drm_mode_subconnector {
>>   #define DRM_MODE_CONNECTOR_DPI         17
>>   #define DRM_MODE_CONNECTOR_WRITEBACK   18
>>   #define DRM_MODE_CONNECTOR_SPI         19
>> +#define DRM_MODE_CONNECTOR_USB         20

I would not call it USB. I could imagine that at some point a generic 
USB protocol could serve simple displays (i.e. in the sense of USB HID 
or data or imaging). (Maybe Thunderbold already counts.) Anyway, USB 
should be reserved for this case.

Best regards
Thomas

> 
> Beware, new connector types have in the past resulted in userspace
> burning&crashing. Maybe it's become better ...
> 
> Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
>>
>>   /**
>>    * struct drm_mode_get_connector - Get connector metadata.
>> --
>> 2.23.0
>>
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel@lists.freedesktop.org
>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
> 
> 
> 

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Felix Imendörffer


[-- Attachment #1.2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

[-- Attachment #2: Type: text/plain, Size: 160 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 1/3] drm/uapi: Add USB connector type
  2021-01-21  7:45       ` Thomas Zimmermann
@ 2021-01-21  8:27         ` Daniel Vetter
  -1 siblings, 0 replies; 53+ messages in thread
From: Daniel Vetter @ 2021-01-21  8:27 UTC (permalink / raw)
  To: Thomas Zimmermann
  Cc: Noralf Trønnes, hudson, markus, peter, USB list, dri-devel,
	Tyler Hardin, Lubomir Rintel, pontus.fuchs, Sam Ravnborg

On Thu, Jan 21, 2021 at 8:45 AM Thomas Zimmermann <tzimmermann@suse.de> wrote:
>
> Hi Noralf,
>
> glad to hear from you! Welcome back!
>
> Am 20.01.21 um 18:42 schrieb Daniel Vetter:
> > On Wed, Jan 20, 2021 at 6:10 PM Noralf Trønnes <noralf@tronnes.org> wrote:
> >>
> >> Add a connector type for USB connected display panels.
> >>
> >> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
> >> ---
> >>   include/uapi/drm/drm_mode.h | 1 +
> >>   1 file changed, 1 insertion(+)
> >>
> >> diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
> >> index fed66a03c7ae..33024cc5d26e 100644
> >> --- a/include/uapi/drm/drm_mode.h
> >> +++ b/include/uapi/drm/drm_mode.h
> >> @@ -367,6 +367,7 @@ enum drm_mode_subconnector {
> >>   #define DRM_MODE_CONNECTOR_DPI         17
> >>   #define DRM_MODE_CONNECTOR_WRITEBACK   18
> >>   #define DRM_MODE_CONNECTOR_SPI         19
> >> +#define DRM_MODE_CONNECTOR_USB         20
>
> I would not call it USB. I could imagine that at some point a generic
> USB protocol could serve simple displays (i.e. in the sense of USB HID
> or data or imaging). (Maybe Thunderbold already counts.) Anyway, USB
> should be reserved for this case.

We end up calling those DisplayPort, since that's what's being
transported over thunderbolt or usb-C. So the usb connector would be
called usb-C. I think the reason we don't do fancy connector names is
that adding them is a bit a pain. Plus drm/i915 specifically has some
very quirky connector enumerating that doesn't match much with reality
unfortunately anyway :-/
-Daniel

>
> Best regards
> Thomas
>
> >
> > Beware, new connector types have in the past resulted in userspace
> > burning&crashing. Maybe it's become better ...
> >
> > Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
> >>
> >>   /**
> >>    * struct drm_mode_get_connector - Get connector metadata.
> >> --
> >> 2.23.0
> >>
> >> _______________________________________________
> >> dri-devel mailing list
> >> dri-devel@lists.freedesktop.org
> >> https://lists.freedesktop.org/mailman/listinfo/dri-devel
> >
> >
> >
>
> --
> Thomas Zimmermann
> Graphics Driver Developer
> SUSE Software Solutions Germany GmbH
> Maxfeldstr. 5, 90409 Nürnberg, Germany
> (HRB 36809, AG Nürnberg)
> Geschäftsführer: Felix Imendörffer
>


-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 1/3] drm/uapi: Add USB connector type
@ 2021-01-21  8:27         ` Daniel Vetter
  0 siblings, 0 replies; 53+ messages in thread
From: Daniel Vetter @ 2021-01-21  8:27 UTC (permalink / raw)
  To: Thomas Zimmermann
  Cc: hudson, markus, Sam Ravnborg, USB list, dri-devel, Tyler Hardin,
	Lubomir Rintel, pontus.fuchs, peter

On Thu, Jan 21, 2021 at 8:45 AM Thomas Zimmermann <tzimmermann@suse.de> wrote:
>
> Hi Noralf,
>
> glad to hear from you! Welcome back!
>
> Am 20.01.21 um 18:42 schrieb Daniel Vetter:
> > On Wed, Jan 20, 2021 at 6:10 PM Noralf Trønnes <noralf@tronnes.org> wrote:
> >>
> >> Add a connector type for USB connected display panels.
> >>
> >> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
> >> ---
> >>   include/uapi/drm/drm_mode.h | 1 +
> >>   1 file changed, 1 insertion(+)
> >>
> >> diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
> >> index fed66a03c7ae..33024cc5d26e 100644
> >> --- a/include/uapi/drm/drm_mode.h
> >> +++ b/include/uapi/drm/drm_mode.h
> >> @@ -367,6 +367,7 @@ enum drm_mode_subconnector {
> >>   #define DRM_MODE_CONNECTOR_DPI         17
> >>   #define DRM_MODE_CONNECTOR_WRITEBACK   18
> >>   #define DRM_MODE_CONNECTOR_SPI         19
> >> +#define DRM_MODE_CONNECTOR_USB         20
>
> I would not call it USB. I could imagine that at some point a generic
> USB protocol could serve simple displays (i.e. in the sense of USB HID
> or data or imaging). (Maybe Thunderbold already counts.) Anyway, USB
> should be reserved for this case.

We end up calling those DisplayPort, since that's what's being
transported over thunderbolt or usb-C. So the usb connector would be
called usb-C. I think the reason we don't do fancy connector names is
that adding them is a bit a pain. Plus drm/i915 specifically has some
very quirky connector enumerating that doesn't match much with reality
unfortunately anyway :-/
-Daniel

>
> Best regards
> Thomas
>
> >
> > Beware, new connector types have in the past resulted in userspace
> > burning&crashing. Maybe it's become better ...
> >
> > Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
> >>
> >>   /**
> >>    * struct drm_mode_get_connector - Get connector metadata.
> >> --
> >> 2.23.0
> >>
> >> _______________________________________________
> >> dri-devel mailing list
> >> dri-devel@lists.freedesktop.org
> >> https://lists.freedesktop.org/mailman/listinfo/dri-devel
> >
> >
> >
>
> --
> Thomas Zimmermann
> Graphics Driver Developer
> SUSE Software Solutions Germany GmbH
> Maxfeldstr. 5, 90409 Nürnberg, Germany
> (HRB 36809, AG Nürnberg)
> Geschäftsführer: Felix Imendörffer
>


-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 1/3] drm/uapi: Add USB connector type
  2021-01-21  8:27         ` Daniel Vetter
@ 2021-01-21  8:40           ` Simon Ser
  -1 siblings, 0 replies; 53+ messages in thread
From: Simon Ser @ 2021-01-21  8:40 UTC (permalink / raw)
  To: Daniel Vetter
  Cc: Thomas Zimmermann, hudson, markus, Sam Ravnborg, USB list,
	dri-devel, Tyler Hardin, Lubomir Rintel, pontus.fuchs, peter

On Thursday, January 21st, 2021 at 9:27 AM, Daniel Vetter <daniel@ffwll.ch> wrote:

> > I would not call it USB. I could imagine that at some point a generic
> > USB protocol could serve simple displays (i.e. in the sense of USB HID
> > or data or imaging). (Maybe Thunderbold already counts.) Anyway, USB
> > should be reserved for this case.
>
> We end up calling those DisplayPort, since that's what's being
> transported over thunderbolt or usb-C. So the usb connector would be
> called usb-C. I think the reason we don't do fancy connector names is
> that adding them is a bit a pain. Plus drm/i915 specifically has some
> very quirky connector enumerating that doesn't match much with reality
> unfortunately anyway :-/

Maybe could add "USB-C" to the list of subconnector types and use that?

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 1/3] drm/uapi: Add USB connector type
@ 2021-01-21  8:40           ` Simon Ser
  0 siblings, 0 replies; 53+ messages in thread
From: Simon Ser @ 2021-01-21  8:40 UTC (permalink / raw)
  To: Daniel Vetter
  Cc: hudson, markus, peter, USB list, dri-devel, Tyler Hardin,
	Lubomir Rintel, pontus.fuchs, Thomas Zimmermann, Sam Ravnborg

On Thursday, January 21st, 2021 at 9:27 AM, Daniel Vetter <daniel@ffwll.ch> wrote:

> > I would not call it USB. I could imagine that at some point a generic
> > USB protocol could serve simple displays (i.e. in the sense of USB HID
> > or data or imaging). (Maybe Thunderbold already counts.) Anyway, USB
> > should be reserved for this case.
>
> We end up calling those DisplayPort, since that's what's being
> transported over thunderbolt or usb-C. So the usb connector would be
> called usb-C. I think the reason we don't do fancy connector names is
> that adding them is a bit a pain. Plus drm/i915 specifically has some
> very quirky connector enumerating that doesn't match much with reality
> unfortunately anyway :-/

Maybe could add "USB-C" to the list of subconnector types and use that?
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 0/3] Generic USB Display driver
  2021-01-20 17:00 ` Noralf Trønnes
@ 2021-01-21  9:59   ` Thomas Zimmermann
  -1 siblings, 0 replies; 53+ messages in thread
From: Thomas Zimmermann @ 2021-01-21  9:59 UTC (permalink / raw)
  To: Noralf Trønnes, dri-devel
  Cc: hudson, markus, sam, linux-usb, th020394, lkundrak, pontus.fuchs, peter


[-- Attachment #1.1: Type: text/plain, Size: 2760 bytes --]

Hi

Am 20.01.21 um 18:00 schrieb Noralf Trønnes:
> Hi,
> 
> A while back I had the idea to turn a Raspberry Pi Zero into a $5
> USB to HDMI/SDTV/DSI/DPI display adapter.
> 
> The reason for calling it 'Generic' is so anyone can make a USB
> display/adapter against this driver, all that's needed is to add a USB
> vid:pid.

Well, I'd strongly ask to not call it "generic", because it isn't. We 
have other USB drivers and anyone can make a USB display with these 
protocols as well. That doesn't make them generic. A USB-standardized 
protocol would be generic. Maybe call it custom, or home-made.

Best regards
Thomas

> 
> Unfortunately I've had some compounding health problems that have
> severally limited the time I can spend in front of a computer. For this
> reason I've decided to keep the gadget driver out-of-tree and focus on
> getting the host driver merged first.
> 
> See the wiki[1] for more information and images for the Raspberry Pi
> Zero/4.
> 
> One big change this time is that I've followed Peter Stuge's advice to
> not let DRM stuff leak into the USB protocol. This has made the protocol
> easier to understand just from reading the header file.
> 
> Noralf.
> 
> [1] https://github.com/notro/gud/wiki
> 
> 
> Noralf Trønnes (3):
>    drm/uapi: Add USB connector type
>    drm/probe-helper: Check epoch counter in output_poll_execute()
>    drm: Add Generic USB Display driver
> 
>   MAINTAINERS                         |   8 +
>   drivers/gpu/drm/Kconfig             |   2 +
>   drivers/gpu/drm/Makefile            |   1 +
>   drivers/gpu/drm/drm_probe_helper.c  |   7 +-
>   drivers/gpu/drm/gud/Kconfig         |  14 +
>   drivers/gpu/drm/gud/Makefile        |   4 +
>   drivers/gpu/drm/gud/gud_connector.c | 722 ++++++++++++++++++++++++++++
>   drivers/gpu/drm/gud/gud_drv.c       | 620 ++++++++++++++++++++++++
>   drivers/gpu/drm/gud/gud_internal.h  | 148 ++++++
>   drivers/gpu/drm/gud/gud_pipe.c      | 472 ++++++++++++++++++
>   include/drm/gud.h                   | 356 ++++++++++++++
>   include/uapi/drm/drm_mode.h         |   1 +
>   12 files changed, 2354 insertions(+), 1 deletion(-)
>   create mode 100644 drivers/gpu/drm/gud/Kconfig
>   create mode 100644 drivers/gpu/drm/gud/Makefile
>   create mode 100644 drivers/gpu/drm/gud/gud_connector.c
>   create mode 100644 drivers/gpu/drm/gud/gud_drv.c
>   create mode 100644 drivers/gpu/drm/gud/gud_internal.h
>   create mode 100644 drivers/gpu/drm/gud/gud_pipe.c
>   create mode 100644 include/drm/gud.h
> 

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Felix Imendörffer


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 0/3] Generic USB Display driver
@ 2021-01-21  9:59   ` Thomas Zimmermann
  0 siblings, 0 replies; 53+ messages in thread
From: Thomas Zimmermann @ 2021-01-21  9:59 UTC (permalink / raw)
  To: Noralf Trønnes, dri-devel
  Cc: hudson, markus, peter, linux-usb, th020394, lkundrak, pontus.fuchs, sam


[-- Attachment #1.1.1: Type: text/plain, Size: 2760 bytes --]

Hi

Am 20.01.21 um 18:00 schrieb Noralf Trønnes:
> Hi,
> 
> A while back I had the idea to turn a Raspberry Pi Zero into a $5
> USB to HDMI/SDTV/DSI/DPI display adapter.
> 
> The reason for calling it 'Generic' is so anyone can make a USB
> display/adapter against this driver, all that's needed is to add a USB
> vid:pid.

Well, I'd strongly ask to not call it "generic", because it isn't. We 
have other USB drivers and anyone can make a USB display with these 
protocols as well. That doesn't make them generic. A USB-standardized 
protocol would be generic. Maybe call it custom, or home-made.

Best regards
Thomas

> 
> Unfortunately I've had some compounding health problems that have
> severally limited the time I can spend in front of a computer. For this
> reason I've decided to keep the gadget driver out-of-tree and focus on
> getting the host driver merged first.
> 
> See the wiki[1] for more information and images for the Raspberry Pi
> Zero/4.
> 
> One big change this time is that I've followed Peter Stuge's advice to
> not let DRM stuff leak into the USB protocol. This has made the protocol
> easier to understand just from reading the header file.
> 
> Noralf.
> 
> [1] https://github.com/notro/gud/wiki
> 
> 
> Noralf Trønnes (3):
>    drm/uapi: Add USB connector type
>    drm/probe-helper: Check epoch counter in output_poll_execute()
>    drm: Add Generic USB Display driver
> 
>   MAINTAINERS                         |   8 +
>   drivers/gpu/drm/Kconfig             |   2 +
>   drivers/gpu/drm/Makefile            |   1 +
>   drivers/gpu/drm/drm_probe_helper.c  |   7 +-
>   drivers/gpu/drm/gud/Kconfig         |  14 +
>   drivers/gpu/drm/gud/Makefile        |   4 +
>   drivers/gpu/drm/gud/gud_connector.c | 722 ++++++++++++++++++++++++++++
>   drivers/gpu/drm/gud/gud_drv.c       | 620 ++++++++++++++++++++++++
>   drivers/gpu/drm/gud/gud_internal.h  | 148 ++++++
>   drivers/gpu/drm/gud/gud_pipe.c      | 472 ++++++++++++++++++
>   include/drm/gud.h                   | 356 ++++++++++++++
>   include/uapi/drm/drm_mode.h         |   1 +
>   12 files changed, 2354 insertions(+), 1 deletion(-)
>   create mode 100644 drivers/gpu/drm/gud/Kconfig
>   create mode 100644 drivers/gpu/drm/gud/Makefile
>   create mode 100644 drivers/gpu/drm/gud/gud_connector.c
>   create mode 100644 drivers/gpu/drm/gud/gud_drv.c
>   create mode 100644 drivers/gpu/drm/gud/gud_internal.h
>   create mode 100644 drivers/gpu/drm/gud/gud_pipe.c
>   create mode 100644 include/drm/gud.h
> 

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Felix Imendörffer


[-- Attachment #1.2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

[-- Attachment #2: Type: text/plain, Size: 160 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 1/3] drm/uapi: Add USB connector type
  2021-01-21  8:27         ` Daniel Vetter
@ 2021-01-21 10:01           ` Thomas Zimmermann
  -1 siblings, 0 replies; 53+ messages in thread
From: Thomas Zimmermann @ 2021-01-21 10:01 UTC (permalink / raw)
  To: Daniel Vetter
  Cc: hudson, markus, Sam Ravnborg, USB list, dri-devel, Tyler Hardin,
	Lubomir Rintel, pontus.fuchs, peter


[-- Attachment #1.1: Type: text/plain, Size: 2870 bytes --]

Hi

Am 21.01.21 um 09:27 schrieb Daniel Vetter:
> On Thu, Jan 21, 2021 at 8:45 AM Thomas Zimmermann <tzimmermann@suse.de> wrote:
>>
>> Hi Noralf,
>>
>> glad to hear from you! Welcome back!
>>
>> Am 20.01.21 um 18:42 schrieb Daniel Vetter:
>>> On Wed, Jan 20, 2021 at 6:10 PM Noralf Trønnes <noralf@tronnes.org> wrote:
>>>>
>>>> Add a connector type for USB connected display panels.
>>>>
>>>> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
>>>> ---
>>>>    include/uapi/drm/drm_mode.h | 1 +
>>>>    1 file changed, 1 insertion(+)
>>>>
>>>> diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
>>>> index fed66a03c7ae..33024cc5d26e 100644
>>>> --- a/include/uapi/drm/drm_mode.h
>>>> +++ b/include/uapi/drm/drm_mode.h
>>>> @@ -367,6 +367,7 @@ enum drm_mode_subconnector {
>>>>    #define DRM_MODE_CONNECTOR_DPI         17
>>>>    #define DRM_MODE_CONNECTOR_WRITEBACK   18
>>>>    #define DRM_MODE_CONNECTOR_SPI         19
>>>> +#define DRM_MODE_CONNECTOR_USB         20
>>
>> I would not call it USB. I could imagine that at some point a generic
>> USB protocol could serve simple displays (i.e. in the sense of USB HID
>> or data or imaging). (Maybe Thunderbold already counts.) Anyway, USB
>> should be reserved for this case.
> 
> We end up calling those DisplayPort, since that's what's being
> transported over thunderbolt or usb-C. So the usb connector would be
> called usb-C. I think the reason we don't do fancy connector names is
> that adding them is a bit a pain. Plus drm/i915 specifically has some
> very quirky connector enumerating that doesn't match much with reality
> unfortunately anyway :-/

In the case of the other USB drivers, IIRC we use the connector type 
that is at the output (i.e., HDMI in the case of udl). I think we should 
do the same here. Or use 'Unknown'.

Best regards
Thomas

> -Daniel
> 
>>
>> Best regards
>> Thomas
>>
>>>
>>> Beware, new connector types have in the past resulted in userspace
>>> burning&crashing. Maybe it's become better ...
>>>
>>> Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
>>>>
>>>>    /**
>>>>     * struct drm_mode_get_connector - Get connector metadata.
>>>> --
>>>> 2.23.0
>>>>
>>>> _______________________________________________
>>>> dri-devel mailing list
>>>> dri-devel@lists.freedesktop.org
>>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>>
>>>
>>>
>>
>> --
>> Thomas Zimmermann
>> Graphics Driver Developer
>> SUSE Software Solutions Germany GmbH
>> Maxfeldstr. 5, 90409 Nürnberg, Germany
>> (HRB 36809, AG Nürnberg)
>> Geschäftsführer: Felix Imendörffer
>>
> 
> 

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Felix Imendörffer


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 1/3] drm/uapi: Add USB connector type
@ 2021-01-21 10:01           ` Thomas Zimmermann
  0 siblings, 0 replies; 53+ messages in thread
From: Thomas Zimmermann @ 2021-01-21 10:01 UTC (permalink / raw)
  To: Daniel Vetter
  Cc: hudson, markus, peter, USB list, dri-devel, Tyler Hardin,
	Lubomir Rintel, pontus.fuchs, Sam Ravnborg


[-- Attachment #1.1.1: Type: text/plain, Size: 2870 bytes --]

Hi

Am 21.01.21 um 09:27 schrieb Daniel Vetter:
> On Thu, Jan 21, 2021 at 8:45 AM Thomas Zimmermann <tzimmermann@suse.de> wrote:
>>
>> Hi Noralf,
>>
>> glad to hear from you! Welcome back!
>>
>> Am 20.01.21 um 18:42 schrieb Daniel Vetter:
>>> On Wed, Jan 20, 2021 at 6:10 PM Noralf Trønnes <noralf@tronnes.org> wrote:
>>>>
>>>> Add a connector type for USB connected display panels.
>>>>
>>>> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
>>>> ---
>>>>    include/uapi/drm/drm_mode.h | 1 +
>>>>    1 file changed, 1 insertion(+)
>>>>
>>>> diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
>>>> index fed66a03c7ae..33024cc5d26e 100644
>>>> --- a/include/uapi/drm/drm_mode.h
>>>> +++ b/include/uapi/drm/drm_mode.h
>>>> @@ -367,6 +367,7 @@ enum drm_mode_subconnector {
>>>>    #define DRM_MODE_CONNECTOR_DPI         17
>>>>    #define DRM_MODE_CONNECTOR_WRITEBACK   18
>>>>    #define DRM_MODE_CONNECTOR_SPI         19
>>>> +#define DRM_MODE_CONNECTOR_USB         20
>>
>> I would not call it USB. I could imagine that at some point a generic
>> USB protocol could serve simple displays (i.e. in the sense of USB HID
>> or data or imaging). (Maybe Thunderbold already counts.) Anyway, USB
>> should be reserved for this case.
> 
> We end up calling those DisplayPort, since that's what's being
> transported over thunderbolt or usb-C. So the usb connector would be
> called usb-C. I think the reason we don't do fancy connector names is
> that adding them is a bit a pain. Plus drm/i915 specifically has some
> very quirky connector enumerating that doesn't match much with reality
> unfortunately anyway :-/

In the case of the other USB drivers, IIRC we use the connector type 
that is at the output (i.e., HDMI in the case of udl). I think we should 
do the same here. Or use 'Unknown'.

Best regards
Thomas

> -Daniel
> 
>>
>> Best regards
>> Thomas
>>
>>>
>>> Beware, new connector types have in the past resulted in userspace
>>> burning&crashing. Maybe it's become better ...
>>>
>>> Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
>>>>
>>>>    /**
>>>>     * struct drm_mode_get_connector - Get connector metadata.
>>>> --
>>>> 2.23.0
>>>>
>>>> _______________________________________________
>>>> dri-devel mailing list
>>>> dri-devel@lists.freedesktop.org
>>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>>
>>>
>>>
>>
>> --
>> Thomas Zimmermann
>> Graphics Driver Developer
>> SUSE Software Solutions Germany GmbH
>> Maxfeldstr. 5, 90409 Nürnberg, Germany
>> (HRB 36809, AG Nürnberg)
>> Geschäftsführer: Felix Imendörffer
>>
> 
> 

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Felix Imendörffer


[-- Attachment #1.2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

[-- Attachment #2: Type: text/plain, Size: 160 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 0/3] Generic USB Display driver
  2021-01-21  9:59   ` Thomas Zimmermann
@ 2021-01-21 10:20     ` Simon Ser
  -1 siblings, 0 replies; 53+ messages in thread
From: Simon Ser @ 2021-01-21 10:20 UTC (permalink / raw)
  To: Thomas Zimmermann
  Cc: Noralf Trønnes, dri-devel, hudson, markus, peter, linux-usb,
	th020394, lkundrak, pontus.fuchs, sam

On Thursday, January 21st, 2021 at 10:59 AM, Thomas Zimmermann <tzimmermann@suse.de> wrote:

> Well, I'd strongly ask to not call it "generic", because it isn't. We
> have other USB drivers and anyone can make a USB display with these
> protocols as well. That doesn't make them generic. A USB-standardized
> protocol would be generic. Maybe call it custom, or home-made.

Maybe rename it to "GUD USB Display driver"? :P

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 0/3] Generic USB Display driver
@ 2021-01-21 10:20     ` Simon Ser
  0 siblings, 0 replies; 53+ messages in thread
From: Simon Ser @ 2021-01-21 10:20 UTC (permalink / raw)
  To: Thomas Zimmermann
  Cc: hudson, markus, sam, linux-usb, dri-devel, th020394, lkundrak,
	pontus.fuchs, peter

On Thursday, January 21st, 2021 at 10:59 AM, Thomas Zimmermann <tzimmermann@suse.de> wrote:

> Well, I'd strongly ask to not call it "generic", because it isn't. We
> have other USB drivers and anyone can make a USB display with these
> protocols as well. That doesn't make them generic. A USB-standardized
> protocol would be generic. Maybe call it custom, or home-made.

Maybe rename it to "GUD USB Display driver"? :P
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 1/3] drm/uapi: Add USB connector type
  2021-01-21 10:01           ` Thomas Zimmermann
@ 2021-01-21 18:07             ` Noralf Trønnes
  -1 siblings, 0 replies; 53+ messages in thread
From: Noralf Trønnes @ 2021-01-21 18:07 UTC (permalink / raw)
  To: Thomas Zimmermann, Daniel Vetter
  Cc: hudson, markus, peter, USB list, dri-devel, Tyler Hardin,
	Lubomir Rintel, pontus.fuchs, Sam Ravnborg



Den 21.01.2021 11.01, skrev Thomas Zimmermann:
> Hi
> 
> Am 21.01.21 um 09:27 schrieb Daniel Vetter:
>> On Thu, Jan 21, 2021 at 8:45 AM Thomas Zimmermann
>> <tzimmermann@suse.de> wrote:
>>>
>>> Hi Noralf,
>>>
>>> glad to hear from you! Welcome back!

Thanks Thomas!

>>>
>>> Am 20.01.21 um 18:42 schrieb Daniel Vetter:
>>>> On Wed, Jan 20, 2021 at 6:10 PM Noralf Trønnes <noralf@tronnes.org>
>>>> wrote:
>>>>>
>>>>> Add a connector type for USB connected display panels.
>>>>>
>>>>> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
>>>>> ---

I have forgotten to update drm_connector_enum_list which maps type to name.

>>>>>    include/uapi/drm/drm_mode.h | 1 +
>>>>>    1 file changed, 1 insertion(+)
>>>>>
>>>>> diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
>>>>> index fed66a03c7ae..33024cc5d26e 100644
>>>>> --- a/include/uapi/drm/drm_mode.h
>>>>> +++ b/include/uapi/drm/drm_mode.h
>>>>> @@ -367,6 +367,7 @@ enum drm_mode_subconnector {
>>>>>    #define DRM_MODE_CONNECTOR_DPI         17
>>>>>    #define DRM_MODE_CONNECTOR_WRITEBACK   18
>>>>>    #define DRM_MODE_CONNECTOR_SPI         19
>>>>> +#define DRM_MODE_CONNECTOR_USB         20
>>>
>>> I would not call it USB. I could imagine that at some point a generic
>>> USB protocol could serve simple displays (i.e. in the sense of USB HID
>>> or data or imaging). (Maybe Thunderbold already counts.) Anyway, USB
>>> should be reserved for this case.
>>
>> We end up calling those DisplayPort, since that's what's being
>> transported over thunderbolt or usb-C. So the usb connector would be
>> called usb-C. I think the reason we don't do fancy connector names is
>> that adding them is a bit a pain. Plus drm/i915 specifically has some
>> very quirky connector enumerating that doesn't match much with reality
>> unfortunately anyway :-/
> 
> In the case of the other USB drivers, IIRC we use the connector type
> that is at the output (i.e., HDMI in the case of udl). I think we should
> do the same here. Or use 'Unknown'.
> 

There are 2 DRM USB drivers and they use:
- udl: DRM_MODE_CONNECTOR_DVII
- gm12u320: DRM_MODE_CONNECTOR_VGA

gm12u320 is a mini projector so it doesn't actually have a VGA
connector. I have never seen a udl device but I assume it has a DVII
connector?

For display adapters it makes sense to use the connector on the adapter
as the reported connector, but for display panels that don't have any
connector except for the cable that is connected to the hosts USB
connector, why can't it be called a USB connector? That's the connector
the user sees.

Ofc as Daniel mentions it's a downside that userspace doesn't know about
the connector type, and who knows when it will updated (if I don't do it).
Weston will name it: "UNNAMED-%d"
Mutter: "Unknown%d-%d"
X: "Unknown%d-%d"

Sam and Laurent has discussed adding a PANEL connector type instead of
adding more connector types for panel connectors. I think that would
have been a better choice instead of the SPI connector type that I added
in 2019. But I think PANEL was meant for panels connected to an internal
connector.

Here's my protocol connector types and how it's mapped to DRM:

#define GUD_CONNECTOR_TYPE_PANEL		0
#define GUD_CONNECTOR_TYPE_VGA			1
#define GUD_CONNECTOR_TYPE_COMPOSITE		2
#define GUD_CONNECTOR_TYPE_SVIDEO		3
#define GUD_CONNECTOR_TYPE_COMPONENT		4
#define GUD_CONNECTOR_TYPE_DVI			5
#define GUD_CONNECTOR_TYPE_DISPLAYPORT		6
#define GUD_CONNECTOR_TYPE_HDMI			7

static int gud_gadget_ctrl_get_connector(struct gud_gadget *gdg,
unsigned int index,
					 struct gud_connector_descriptor_req *desc)
{
...
	gconn = &gdg->connectors[index];

	switch (gconn->connector->connector_type) {
	case DRM_MODE_CONNECTOR_VGA:
		desc->connector_type = GUD_CONNECTOR_TYPE_VGA;
		break;
	case DRM_MODE_CONNECTOR_DVII:
		fallthrough;
	case DRM_MODE_CONNECTOR_DVID:
		fallthrough;
	case DRM_MODE_CONNECTOR_DVIA:
		desc->connector_type = GUD_CONNECTOR_TYPE_DVI;
		break;
	case DRM_MODE_CONNECTOR_Composite:
		desc->connector_type = GUD_CONNECTOR_TYPE_COMPOSITE;
		break;
	case DRM_MODE_CONNECTOR_SVIDEO:
		desc->connector_type = GUD_CONNECTOR_TYPE_SVIDEO;
		break;
	case DRM_MODE_CONNECTOR_Component:
		desc->connector_type = GUD_CONNECTOR_TYPE_COMPONENT;
		break;
	case DRM_MODE_CONNECTOR_DisplayPort:
		desc->connector_type = GUD_CONNECTOR_TYPE_DISPLAYPORT;
		break;
	case DRM_MODE_CONNECTOR_HDMIA:
		fallthrough;
	case DRM_MODE_CONNECTOR_HDMIB:
		desc->connector_type = GUD_CONNECTOR_TYPE_HDMI;
		break;
	default:
		desc->connector_type = GUD_CONNECTOR_TYPE_PANEL;
		break;
	};


int gud_connector_create(struct gud_device *gdrm, unsigned int index)
{
...
	switch (desc.connector_type) {
	case GUD_CONNECTOR_TYPE_PANEL:
		connector_type = DRM_MODE_CONNECTOR_USB;
		break;
	case GUD_CONNECTOR_TYPE_VGA:
		connector_type = DRM_MODE_CONNECTOR_VGA;
		break;
	case GUD_CONNECTOR_TYPE_DVI:
		connector_type = DRM_MODE_CONNECTOR_DVID;
		break;
	case GUD_CONNECTOR_TYPE_COMPOSITE:
		connector_type = DRM_MODE_CONNECTOR_Composite;
		break;
	case GUD_CONNECTOR_TYPE_SVIDEO:
		connector_type = DRM_MODE_CONNECTOR_SVIDEO;
		break;
	case GUD_CONNECTOR_TYPE_COMPONENT:
		connector_type = DRM_MODE_CONNECTOR_Component;
		break;
	case GUD_CONNECTOR_TYPE_DISPLAYPORT:
		connector_type = DRM_MODE_CONNECTOR_DisplayPort;
		break;
	case GUD_CONNECTOR_TYPE_HDMI:
		connector_type = DRM_MODE_CONNECTOR_HDMIA;
		break;
	default: /* future types */
		connector_type = DRM_MODE_CONNECTOR_USB;
		break;
	};

Noralf.

> Best regards
> Thomas
> 
>> -Daniel
>>
>>>
>>> Best regards
>>> Thomas
>>>
>>>>
>>>> Beware, new connector types have in the past resulted in userspace
>>>> burning&crashing. Maybe it's become better ...
>>>>
>>>> Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
>>>>>
>>>>>    /**
>>>>>     * struct drm_mode_get_connector - Get connector metadata.
>>>>> -- 
>>>>> 2.23.0
>>>>>
>>>>> _______________________________________________
>>>>> dri-devel mailing list
>>>>> dri-devel@lists.freedesktop.org
>>>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>>>
>>>>
>>>>
>>>
>>> -- 
>>> Thomas Zimmermann
>>> Graphics Driver Developer
>>> SUSE Software Solutions Germany GmbH
>>> Maxfeldstr. 5, 90409 Nürnberg, Germany
>>> (HRB 36809, AG Nürnberg)
>>> Geschäftsführer: Felix Imendörffer
>>>
>>
>>
> 
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
> 

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 1/3] drm/uapi: Add USB connector type
@ 2021-01-21 18:07             ` Noralf Trønnes
  0 siblings, 0 replies; 53+ messages in thread
From: Noralf Trønnes @ 2021-01-21 18:07 UTC (permalink / raw)
  To: Thomas Zimmermann, Daniel Vetter
  Cc: hudson, markus, Sam Ravnborg, USB list, dri-devel, Tyler Hardin,
	Lubomir Rintel, pontus.fuchs, peter



Den 21.01.2021 11.01, skrev Thomas Zimmermann:
> Hi
> 
> Am 21.01.21 um 09:27 schrieb Daniel Vetter:
>> On Thu, Jan 21, 2021 at 8:45 AM Thomas Zimmermann
>> <tzimmermann@suse.de> wrote:
>>>
>>> Hi Noralf,
>>>
>>> glad to hear from you! Welcome back!

Thanks Thomas!

>>>
>>> Am 20.01.21 um 18:42 schrieb Daniel Vetter:
>>>> On Wed, Jan 20, 2021 at 6:10 PM Noralf Trønnes <noralf@tronnes.org>
>>>> wrote:
>>>>>
>>>>> Add a connector type for USB connected display panels.
>>>>>
>>>>> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
>>>>> ---

I have forgotten to update drm_connector_enum_list which maps type to name.

>>>>>    include/uapi/drm/drm_mode.h | 1 +
>>>>>    1 file changed, 1 insertion(+)
>>>>>
>>>>> diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
>>>>> index fed66a03c7ae..33024cc5d26e 100644
>>>>> --- a/include/uapi/drm/drm_mode.h
>>>>> +++ b/include/uapi/drm/drm_mode.h
>>>>> @@ -367,6 +367,7 @@ enum drm_mode_subconnector {
>>>>>    #define DRM_MODE_CONNECTOR_DPI         17
>>>>>    #define DRM_MODE_CONNECTOR_WRITEBACK   18
>>>>>    #define DRM_MODE_CONNECTOR_SPI         19
>>>>> +#define DRM_MODE_CONNECTOR_USB         20
>>>
>>> I would not call it USB. I could imagine that at some point a generic
>>> USB protocol could serve simple displays (i.e. in the sense of USB HID
>>> or data or imaging). (Maybe Thunderbold already counts.) Anyway, USB
>>> should be reserved for this case.
>>
>> We end up calling those DisplayPort, since that's what's being
>> transported over thunderbolt or usb-C. So the usb connector would be
>> called usb-C. I think the reason we don't do fancy connector names is
>> that adding them is a bit a pain. Plus drm/i915 specifically has some
>> very quirky connector enumerating that doesn't match much with reality
>> unfortunately anyway :-/
> 
> In the case of the other USB drivers, IIRC we use the connector type
> that is at the output (i.e., HDMI in the case of udl). I think we should
> do the same here. Or use 'Unknown'.
> 

There are 2 DRM USB drivers and they use:
- udl: DRM_MODE_CONNECTOR_DVII
- gm12u320: DRM_MODE_CONNECTOR_VGA

gm12u320 is a mini projector so it doesn't actually have a VGA
connector. I have never seen a udl device but I assume it has a DVII
connector?

For display adapters it makes sense to use the connector on the adapter
as the reported connector, but for display panels that don't have any
connector except for the cable that is connected to the hosts USB
connector, why can't it be called a USB connector? That's the connector
the user sees.

Ofc as Daniel mentions it's a downside that userspace doesn't know about
the connector type, and who knows when it will updated (if I don't do it).
Weston will name it: "UNNAMED-%d"
Mutter: "Unknown%d-%d"
X: "Unknown%d-%d"

Sam and Laurent has discussed adding a PANEL connector type instead of
adding more connector types for panel connectors. I think that would
have been a better choice instead of the SPI connector type that I added
in 2019. But I think PANEL was meant for panels connected to an internal
connector.

Here's my protocol connector types and how it's mapped to DRM:

#define GUD_CONNECTOR_TYPE_PANEL		0
#define GUD_CONNECTOR_TYPE_VGA			1
#define GUD_CONNECTOR_TYPE_COMPOSITE		2
#define GUD_CONNECTOR_TYPE_SVIDEO		3
#define GUD_CONNECTOR_TYPE_COMPONENT		4
#define GUD_CONNECTOR_TYPE_DVI			5
#define GUD_CONNECTOR_TYPE_DISPLAYPORT		6
#define GUD_CONNECTOR_TYPE_HDMI			7

static int gud_gadget_ctrl_get_connector(struct gud_gadget *gdg,
unsigned int index,
					 struct gud_connector_descriptor_req *desc)
{
...
	gconn = &gdg->connectors[index];

	switch (gconn->connector->connector_type) {
	case DRM_MODE_CONNECTOR_VGA:
		desc->connector_type = GUD_CONNECTOR_TYPE_VGA;
		break;
	case DRM_MODE_CONNECTOR_DVII:
		fallthrough;
	case DRM_MODE_CONNECTOR_DVID:
		fallthrough;
	case DRM_MODE_CONNECTOR_DVIA:
		desc->connector_type = GUD_CONNECTOR_TYPE_DVI;
		break;
	case DRM_MODE_CONNECTOR_Composite:
		desc->connector_type = GUD_CONNECTOR_TYPE_COMPOSITE;
		break;
	case DRM_MODE_CONNECTOR_SVIDEO:
		desc->connector_type = GUD_CONNECTOR_TYPE_SVIDEO;
		break;
	case DRM_MODE_CONNECTOR_Component:
		desc->connector_type = GUD_CONNECTOR_TYPE_COMPONENT;
		break;
	case DRM_MODE_CONNECTOR_DisplayPort:
		desc->connector_type = GUD_CONNECTOR_TYPE_DISPLAYPORT;
		break;
	case DRM_MODE_CONNECTOR_HDMIA:
		fallthrough;
	case DRM_MODE_CONNECTOR_HDMIB:
		desc->connector_type = GUD_CONNECTOR_TYPE_HDMI;
		break;
	default:
		desc->connector_type = GUD_CONNECTOR_TYPE_PANEL;
		break;
	};


int gud_connector_create(struct gud_device *gdrm, unsigned int index)
{
...
	switch (desc.connector_type) {
	case GUD_CONNECTOR_TYPE_PANEL:
		connector_type = DRM_MODE_CONNECTOR_USB;
		break;
	case GUD_CONNECTOR_TYPE_VGA:
		connector_type = DRM_MODE_CONNECTOR_VGA;
		break;
	case GUD_CONNECTOR_TYPE_DVI:
		connector_type = DRM_MODE_CONNECTOR_DVID;
		break;
	case GUD_CONNECTOR_TYPE_COMPOSITE:
		connector_type = DRM_MODE_CONNECTOR_Composite;
		break;
	case GUD_CONNECTOR_TYPE_SVIDEO:
		connector_type = DRM_MODE_CONNECTOR_SVIDEO;
		break;
	case GUD_CONNECTOR_TYPE_COMPONENT:
		connector_type = DRM_MODE_CONNECTOR_Component;
		break;
	case GUD_CONNECTOR_TYPE_DISPLAYPORT:
		connector_type = DRM_MODE_CONNECTOR_DisplayPort;
		break;
	case GUD_CONNECTOR_TYPE_HDMI:
		connector_type = DRM_MODE_CONNECTOR_HDMIA;
		break;
	default: /* future types */
		connector_type = DRM_MODE_CONNECTOR_USB;
		break;
	};

Noralf.

> Best regards
> Thomas
> 
>> -Daniel
>>
>>>
>>> Best regards
>>> Thomas
>>>
>>>>
>>>> Beware, new connector types have in the past resulted in userspace
>>>> burning&crashing. Maybe it's become better ...
>>>>
>>>> Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
>>>>>
>>>>>    /**
>>>>>     * struct drm_mode_get_connector - Get connector metadata.
>>>>> -- 
>>>>> 2.23.0
>>>>>
>>>>> _______________________________________________
>>>>> dri-devel mailing list
>>>>> dri-devel@lists.freedesktop.org
>>>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>>>
>>>>
>>>>
>>>
>>> -- 
>>> Thomas Zimmermann
>>> Graphics Driver Developer
>>> SUSE Software Solutions Germany GmbH
>>> Maxfeldstr. 5, 90409 Nürnberg, Germany
>>> (HRB 36809, AG Nürnberg)
>>> Geschäftsführer: Felix Imendörffer
>>>
>>
>>
> 
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
> 
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 1/3] drm/uapi: Add USB connector type
  2021-01-21 18:07             ` Noralf Trønnes
@ 2021-01-22  7:54               ` Thomas Zimmermann
  -1 siblings, 0 replies; 53+ messages in thread
From: Thomas Zimmermann @ 2021-01-22  7:54 UTC (permalink / raw)
  To: Noralf Trønnes, Daniel Vetter
  Cc: hudson, markus, Sam Ravnborg, USB list, dri-devel, Tyler Hardin,
	Lubomir Rintel, pontus.fuchs, peter


[-- Attachment #1.1: Type: text/plain, Size: 7922 bytes --]

Hi

Am 21.01.21 um 19:07 schrieb Noralf Trønnes:
> 
> 
> Den 21.01.2021 11.01, skrev Thomas Zimmermann:
>> Hi
>>
>> Am 21.01.21 um 09:27 schrieb Daniel Vetter:
>>> On Thu, Jan 21, 2021 at 8:45 AM Thomas Zimmermann
>>> <tzimmermann@suse.de> wrote:
>>>>
>>>> Hi Noralf,
>>>>
>>>> glad to hear from you! Welcome back!
> 
> Thanks Thomas!
> 
>>>>
>>>> Am 20.01.21 um 18:42 schrieb Daniel Vetter:
>>>>> On Wed, Jan 20, 2021 at 6:10 PM Noralf Trønnes <noralf@tronnes.org>
>>>>> wrote:
>>>>>>
>>>>>> Add a connector type for USB connected display panels.
>>>>>>
>>>>>> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
>>>>>> ---
> 
> I have forgotten to update drm_connector_enum_list which maps type to name.
> 
>>>>>>     include/uapi/drm/drm_mode.h | 1 +
>>>>>>     1 file changed, 1 insertion(+)
>>>>>>
>>>>>> diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
>>>>>> index fed66a03c7ae..33024cc5d26e 100644
>>>>>> --- a/include/uapi/drm/drm_mode.h
>>>>>> +++ b/include/uapi/drm/drm_mode.h
>>>>>> @@ -367,6 +367,7 @@ enum drm_mode_subconnector {
>>>>>>     #define DRM_MODE_CONNECTOR_DPI         17
>>>>>>     #define DRM_MODE_CONNECTOR_WRITEBACK   18
>>>>>>     #define DRM_MODE_CONNECTOR_SPI         19
>>>>>> +#define DRM_MODE_CONNECTOR_USB         20
>>>>
>>>> I would not call it USB. I could imagine that at some point a generic
>>>> USB protocol could serve simple displays (i.e. in the sense of USB HID
>>>> or data or imaging). (Maybe Thunderbold already counts.) Anyway, USB
>>>> should be reserved for this case.
>>>
>>> We end up calling those DisplayPort, since that's what's being
>>> transported over thunderbolt or usb-C. So the usb connector would be
>>> called usb-C. I think the reason we don't do fancy connector names is
>>> that adding them is a bit a pain. Plus drm/i915 specifically has some
>>> very quirky connector enumerating that doesn't match much with reality
>>> unfortunately anyway :-/
>>
>> In the case of the other USB drivers, IIRC we use the connector type
>> that is at the output (i.e., HDMI in the case of udl). I think we should
>> do the same here. Or use 'Unknown'.
>>
> 
> There are 2 DRM USB drivers and they use:
> - udl: DRM_MODE_CONNECTOR_DVII

Mine has plain old VGA. Maybe we should change generally this to Unknown.

> - gm12u320: DRM_MODE_CONNECTOR_VGA
> 
> gm12u320 is a mini projector so it doesn't actually have a VGA
> connector. I have never seen a udl device but I assume it has a DVII
> connector?
> 
> For display adapters it makes sense to use the connector on the adapter
> as the reported connector, but for display panels that don't have any
> connector except for the cable that is connected to the hosts USB
> connector, why can't it be called a USB connector? That's the connector
> the user sees.

It's not the relevant connector for the display output. USB is the bus 
system. (Making your argument in terms of discrete GPUs, the connector 
would always be PCI then.)

> 
> Ofc as Daniel mentions it's a downside that userspace doesn't know about
> the connector type, and who knows when it will updated (if I don't do it).
> Weston will name it: "UNNAMED-%d"
> Mutter: "Unknown%d-%d"
> X: "Unknown%d-%d"
> 
> Sam and Laurent has discussed adding a PANEL connector type instead of
> adding more connector types for panel connectors. I think that would
> have been a better choice instead of the SPI connector type that I added
> in 2019. But I think PANEL was meant for panels connected to an internal
> connector.
> 
> Here's my protocol connector types and how it's mapped to DRM:
> 
> #define GUD_CONNECTOR_TYPE_PANEL		0
> #define GUD_CONNECTOR_TYPE_VGA			1
> #define GUD_CONNECTOR_TYPE_COMPOSITE		2
> #define GUD_CONNECTOR_TYPE_SVIDEO		3
> #define GUD_CONNECTOR_TYPE_COMPONENT		4
> #define GUD_CONNECTOR_TYPE_DVI			5
> #define GUD_CONNECTOR_TYPE_DISPLAYPORT		6
> #define GUD_CONNECTOR_TYPE_HDMI			7
> 
> static int gud_gadget_ctrl_get_connector(struct gud_gadget *gdg,
> unsigned int index,
> 					 struct gud_connector_descriptor_req *desc)
> {
> ...
> 	gconn = &gdg->connectors[index];
> 
> 	switch (gconn->connector->connector_type) {
> 	case DRM_MODE_CONNECTOR_VGA:
> 		desc->connector_type = GUD_CONNECTOR_TYPE_VGA;
> 		break;
> 	case DRM_MODE_CONNECTOR_DVII:
> 		fallthrough;
> 	case DRM_MODE_CONNECTOR_DVID:
> 		fallthrough;
> 	case DRM_MODE_CONNECTOR_DVIA:
> 		desc->connector_type = GUD_CONNECTOR_TYPE_DVI;
> 		break;
> 	case DRM_MODE_CONNECTOR_Composite:
> 		desc->connector_type = GUD_CONNECTOR_TYPE_COMPOSITE;
> 		break;
> 	case DRM_MODE_CONNECTOR_SVIDEO:
> 		desc->connector_type = GUD_CONNECTOR_TYPE_SVIDEO;
> 		break;
> 	case DRM_MODE_CONNECTOR_Component:
> 		desc->connector_type = GUD_CONNECTOR_TYPE_COMPONENT;
> 		break;
> 	case DRM_MODE_CONNECTOR_DisplayPort:
> 		desc->connector_type = GUD_CONNECTOR_TYPE_DISPLAYPORT;
> 		break;
> 	case DRM_MODE_CONNECTOR_HDMIA:
> 		fallthrough;
> 	case DRM_MODE_CONNECTOR_HDMIB:
> 		desc->connector_type = GUD_CONNECTOR_TYPE_HDMI;
> 		break;
> 	default:
> 		desc->connector_type = GUD_CONNECTOR_TYPE_PANEL;
> 		break;
> 	};
> 
> 
> int gud_connector_create(struct gud_device *gdrm, unsigned int index)
> {
> ...
> 	switch (desc.connector_type) {
> 	case GUD_CONNECTOR_TYPE_PANEL:
> 		connector_type = DRM_MODE_CONNECTOR_USB;
> 		break;
> 	case GUD_CONNECTOR_TYPE_VGA:
> 		connector_type = DRM_MODE_CONNECTOR_VGA;
> 		break;
> 	case GUD_CONNECTOR_TYPE_DVI:
> 		connector_type = DRM_MODE_CONNECTOR_DVID;
> 		break;
> 	case GUD_CONNECTOR_TYPE_COMPOSITE:
> 		connector_type = DRM_MODE_CONNECTOR_Composite;
> 		break;
> 	case GUD_CONNECTOR_TYPE_SVIDEO:
> 		connector_type = DRM_MODE_CONNECTOR_SVIDEO;
> 		break;
> 	case GUD_CONNECTOR_TYPE_COMPONENT:
> 		connector_type = DRM_MODE_CONNECTOR_Component;
> 		break;
> 	case GUD_CONNECTOR_TYPE_DISPLAYPORT:
> 		connector_type = DRM_MODE_CONNECTOR_DisplayPort;
> 		break;
> 	case GUD_CONNECTOR_TYPE_HDMI:
> 		connector_type = DRM_MODE_CONNECTOR_HDMIA;
> 		break;
> 	default: /* future types */
> 		connector_type = DRM_MODE_CONNECTOR_USB;

The more I look at it the more I think it should be 'Unknown' here.

BTW, can I try this out somehow? I do have an RPi3. Do I need a special 
disk image?

Best regards
Thomas

> 		break;
> 	};
> 
> Noralf.
> 
>> Best regards
>> Thomas
>>
>>> -Daniel
>>>
>>>>
>>>> Best regards
>>>> Thomas
>>>>
>>>>>
>>>>> Beware, new connector types have in the past resulted in userspace
>>>>> burning&crashing. Maybe it's become better ...
>>>>>
>>>>> Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
>>>>>>
>>>>>>     /**
>>>>>>      * struct drm_mode_get_connector - Get connector metadata.
>>>>>> -- 
>>>>>> 2.23.0
>>>>>>
>>>>>> _______________________________________________
>>>>>> dri-devel mailing list
>>>>>> dri-devel@lists.freedesktop.org
>>>>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>>>>
>>>>>
>>>>>
>>>>
>>>> -- 
>>>> Thomas Zimmermann
>>>> Graphics Driver Developer
>>>> SUSE Software Solutions Germany GmbH
>>>> Maxfeldstr. 5, 90409 Nürnberg, Germany
>>>> (HRB 36809, AG Nürnberg)
>>>> Geschäftsführer: Felix Imendörffer
>>>>
>>>
>>>
>>
>>
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel@lists.freedesktop.org
>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
> 

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Felix Imendörffer


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 1/3] drm/uapi: Add USB connector type
@ 2021-01-22  7:54               ` Thomas Zimmermann
  0 siblings, 0 replies; 53+ messages in thread
From: Thomas Zimmermann @ 2021-01-22  7:54 UTC (permalink / raw)
  To: Noralf Trønnes, Daniel Vetter
  Cc: hudson, markus, peter, USB list, dri-devel, Tyler Hardin,
	Lubomir Rintel, pontus.fuchs, Sam Ravnborg


[-- Attachment #1.1.1: Type: text/plain, Size: 7922 bytes --]

Hi

Am 21.01.21 um 19:07 schrieb Noralf Trønnes:
> 
> 
> Den 21.01.2021 11.01, skrev Thomas Zimmermann:
>> Hi
>>
>> Am 21.01.21 um 09:27 schrieb Daniel Vetter:
>>> On Thu, Jan 21, 2021 at 8:45 AM Thomas Zimmermann
>>> <tzimmermann@suse.de> wrote:
>>>>
>>>> Hi Noralf,
>>>>
>>>> glad to hear from you! Welcome back!
> 
> Thanks Thomas!
> 
>>>>
>>>> Am 20.01.21 um 18:42 schrieb Daniel Vetter:
>>>>> On Wed, Jan 20, 2021 at 6:10 PM Noralf Trønnes <noralf@tronnes.org>
>>>>> wrote:
>>>>>>
>>>>>> Add a connector type for USB connected display panels.
>>>>>>
>>>>>> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
>>>>>> ---
> 
> I have forgotten to update drm_connector_enum_list which maps type to name.
> 
>>>>>>     include/uapi/drm/drm_mode.h | 1 +
>>>>>>     1 file changed, 1 insertion(+)
>>>>>>
>>>>>> diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
>>>>>> index fed66a03c7ae..33024cc5d26e 100644
>>>>>> --- a/include/uapi/drm/drm_mode.h
>>>>>> +++ b/include/uapi/drm/drm_mode.h
>>>>>> @@ -367,6 +367,7 @@ enum drm_mode_subconnector {
>>>>>>     #define DRM_MODE_CONNECTOR_DPI         17
>>>>>>     #define DRM_MODE_CONNECTOR_WRITEBACK   18
>>>>>>     #define DRM_MODE_CONNECTOR_SPI         19
>>>>>> +#define DRM_MODE_CONNECTOR_USB         20
>>>>
>>>> I would not call it USB. I could imagine that at some point a generic
>>>> USB protocol could serve simple displays (i.e. in the sense of USB HID
>>>> or data or imaging). (Maybe Thunderbold already counts.) Anyway, USB
>>>> should be reserved for this case.
>>>
>>> We end up calling those DisplayPort, since that's what's being
>>> transported over thunderbolt or usb-C. So the usb connector would be
>>> called usb-C. I think the reason we don't do fancy connector names is
>>> that adding them is a bit a pain. Plus drm/i915 specifically has some
>>> very quirky connector enumerating that doesn't match much with reality
>>> unfortunately anyway :-/
>>
>> In the case of the other USB drivers, IIRC we use the connector type
>> that is at the output (i.e., HDMI in the case of udl). I think we should
>> do the same here. Or use 'Unknown'.
>>
> 
> There are 2 DRM USB drivers and they use:
> - udl: DRM_MODE_CONNECTOR_DVII

Mine has plain old VGA. Maybe we should change generally this to Unknown.

> - gm12u320: DRM_MODE_CONNECTOR_VGA
> 
> gm12u320 is a mini projector so it doesn't actually have a VGA
> connector. I have never seen a udl device but I assume it has a DVII
> connector?
> 
> For display adapters it makes sense to use the connector on the adapter
> as the reported connector, but for display panels that don't have any
> connector except for the cable that is connected to the hosts USB
> connector, why can't it be called a USB connector? That's the connector
> the user sees.

It's not the relevant connector for the display output. USB is the bus 
system. (Making your argument in terms of discrete GPUs, the connector 
would always be PCI then.)

> 
> Ofc as Daniel mentions it's a downside that userspace doesn't know about
> the connector type, and who knows when it will updated (if I don't do it).
> Weston will name it: "UNNAMED-%d"
> Mutter: "Unknown%d-%d"
> X: "Unknown%d-%d"
> 
> Sam and Laurent has discussed adding a PANEL connector type instead of
> adding more connector types for panel connectors. I think that would
> have been a better choice instead of the SPI connector type that I added
> in 2019. But I think PANEL was meant for panels connected to an internal
> connector.
> 
> Here's my protocol connector types and how it's mapped to DRM:
> 
> #define GUD_CONNECTOR_TYPE_PANEL		0
> #define GUD_CONNECTOR_TYPE_VGA			1
> #define GUD_CONNECTOR_TYPE_COMPOSITE		2
> #define GUD_CONNECTOR_TYPE_SVIDEO		3
> #define GUD_CONNECTOR_TYPE_COMPONENT		4
> #define GUD_CONNECTOR_TYPE_DVI			5
> #define GUD_CONNECTOR_TYPE_DISPLAYPORT		6
> #define GUD_CONNECTOR_TYPE_HDMI			7
> 
> static int gud_gadget_ctrl_get_connector(struct gud_gadget *gdg,
> unsigned int index,
> 					 struct gud_connector_descriptor_req *desc)
> {
> ...
> 	gconn = &gdg->connectors[index];
> 
> 	switch (gconn->connector->connector_type) {
> 	case DRM_MODE_CONNECTOR_VGA:
> 		desc->connector_type = GUD_CONNECTOR_TYPE_VGA;
> 		break;
> 	case DRM_MODE_CONNECTOR_DVII:
> 		fallthrough;
> 	case DRM_MODE_CONNECTOR_DVID:
> 		fallthrough;
> 	case DRM_MODE_CONNECTOR_DVIA:
> 		desc->connector_type = GUD_CONNECTOR_TYPE_DVI;
> 		break;
> 	case DRM_MODE_CONNECTOR_Composite:
> 		desc->connector_type = GUD_CONNECTOR_TYPE_COMPOSITE;
> 		break;
> 	case DRM_MODE_CONNECTOR_SVIDEO:
> 		desc->connector_type = GUD_CONNECTOR_TYPE_SVIDEO;
> 		break;
> 	case DRM_MODE_CONNECTOR_Component:
> 		desc->connector_type = GUD_CONNECTOR_TYPE_COMPONENT;
> 		break;
> 	case DRM_MODE_CONNECTOR_DisplayPort:
> 		desc->connector_type = GUD_CONNECTOR_TYPE_DISPLAYPORT;
> 		break;
> 	case DRM_MODE_CONNECTOR_HDMIA:
> 		fallthrough;
> 	case DRM_MODE_CONNECTOR_HDMIB:
> 		desc->connector_type = GUD_CONNECTOR_TYPE_HDMI;
> 		break;
> 	default:
> 		desc->connector_type = GUD_CONNECTOR_TYPE_PANEL;
> 		break;
> 	};
> 
> 
> int gud_connector_create(struct gud_device *gdrm, unsigned int index)
> {
> ...
> 	switch (desc.connector_type) {
> 	case GUD_CONNECTOR_TYPE_PANEL:
> 		connector_type = DRM_MODE_CONNECTOR_USB;
> 		break;
> 	case GUD_CONNECTOR_TYPE_VGA:
> 		connector_type = DRM_MODE_CONNECTOR_VGA;
> 		break;
> 	case GUD_CONNECTOR_TYPE_DVI:
> 		connector_type = DRM_MODE_CONNECTOR_DVID;
> 		break;
> 	case GUD_CONNECTOR_TYPE_COMPOSITE:
> 		connector_type = DRM_MODE_CONNECTOR_Composite;
> 		break;
> 	case GUD_CONNECTOR_TYPE_SVIDEO:
> 		connector_type = DRM_MODE_CONNECTOR_SVIDEO;
> 		break;
> 	case GUD_CONNECTOR_TYPE_COMPONENT:
> 		connector_type = DRM_MODE_CONNECTOR_Component;
> 		break;
> 	case GUD_CONNECTOR_TYPE_DISPLAYPORT:
> 		connector_type = DRM_MODE_CONNECTOR_DisplayPort;
> 		break;
> 	case GUD_CONNECTOR_TYPE_HDMI:
> 		connector_type = DRM_MODE_CONNECTOR_HDMIA;
> 		break;
> 	default: /* future types */
> 		connector_type = DRM_MODE_CONNECTOR_USB;

The more I look at it the more I think it should be 'Unknown' here.

BTW, can I try this out somehow? I do have an RPi3. Do I need a special 
disk image?

Best regards
Thomas

> 		break;
> 	};
> 
> Noralf.
> 
>> Best regards
>> Thomas
>>
>>> -Daniel
>>>
>>>>
>>>> Best regards
>>>> Thomas
>>>>
>>>>>
>>>>> Beware, new connector types have in the past resulted in userspace
>>>>> burning&crashing. Maybe it's become better ...
>>>>>
>>>>> Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
>>>>>>
>>>>>>     /**
>>>>>>      * struct drm_mode_get_connector - Get connector metadata.
>>>>>> -- 
>>>>>> 2.23.0
>>>>>>
>>>>>> _______________________________________________
>>>>>> dri-devel mailing list
>>>>>> dri-devel@lists.freedesktop.org
>>>>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>>>>
>>>>>
>>>>>
>>>>
>>>> -- 
>>>> Thomas Zimmermann
>>>> Graphics Driver Developer
>>>> SUSE Software Solutions Germany GmbH
>>>> Maxfeldstr. 5, 90409 Nürnberg, Germany
>>>> (HRB 36809, AG Nürnberg)
>>>> Geschäftsführer: Felix Imendörffer
>>>>
>>>
>>>
>>
>>
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel@lists.freedesktop.org
>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
> 

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Felix Imendörffer


[-- Attachment #1.2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

[-- Attachment #2: Type: text/plain, Size: 160 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 1/3] drm/uapi: Add USB connector type
  2021-01-22  7:54               ` Thomas Zimmermann
@ 2021-01-22  7:59                 ` Thomas Zimmermann
  -1 siblings, 0 replies; 53+ messages in thread
From: Thomas Zimmermann @ 2021-01-22  7:59 UTC (permalink / raw)
  To: Noralf Trønnes, Daniel Vetter
  Cc: hudson, markus, peter, USB list, dri-devel, Tyler Hardin,
	Lubomir Rintel, pontus.fuchs, Sam Ravnborg


[-- Attachment #1.1: Type: text/plain, Size: 2059 bytes --]



Am 22.01.21 um 08:54 schrieb Thomas Zimmermann:
The more I look at it the more I think it should be 'Unknown' here.
> 
> BTW, can I try this out somehow? I do have an RPi3. Do I need a special 
> disk image?

Oh, I saw that wiki url now. I'll check this out.

> 
> Best regards
> Thomas
> 
>>         break;
>>     };
>>
>> Noralf.
>>
>>> Best regards
>>> Thomas
>>>
>>>> -Daniel
>>>>
>>>>>
>>>>> Best regards
>>>>> Thomas
>>>>>
>>>>>>
>>>>>> Beware, new connector types have in the past resulted in userspace
>>>>>> burning&crashing. Maybe it's become better ...
>>>>>>
>>>>>> Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
>>>>>>>
>>>>>>>     /**
>>>>>>>      * struct drm_mode_get_connector - Get connector metadata.
>>>>>>> -- 
>>>>>>> 2.23.0
>>>>>>>
>>>>>>> _______________________________________________
>>>>>>> dri-devel mailing list
>>>>>>> dri-devel@lists.freedesktop.org
>>>>>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>>>>>
>>>>>>
>>>>>>
>>>>>
>>>>> -- 
>>>>> Thomas Zimmermann
>>>>> Graphics Driver Developer
>>>>> SUSE Software Solutions Germany GmbH
>>>>> Maxfeldstr. 5, 90409 Nürnberg, Germany
>>>>> (HRB 36809, AG Nürnberg)
>>>>> Geschäftsführer: Felix Imendörffer
>>>>>
>>>>
>>>>
>>>
>>>
>>> _______________________________________________
>>> dri-devel mailing list
>>> dri-devel@lists.freedesktop.org
>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>>
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel@lists.freedesktop.org
>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>
> 
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
> 

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Felix Imendörffer


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 1/3] drm/uapi: Add USB connector type
@ 2021-01-22  7:59                 ` Thomas Zimmermann
  0 siblings, 0 replies; 53+ messages in thread
From: Thomas Zimmermann @ 2021-01-22  7:59 UTC (permalink / raw)
  To: Noralf Trønnes, Daniel Vetter
  Cc: hudson, markus, Sam Ravnborg, USB list, dri-devel, Tyler Hardin,
	Lubomir Rintel, pontus.fuchs, peter


[-- Attachment #1.1.1: Type: text/plain, Size: 2059 bytes --]



Am 22.01.21 um 08:54 schrieb Thomas Zimmermann:
The more I look at it the more I think it should be 'Unknown' here.
> 
> BTW, can I try this out somehow? I do have an RPi3. Do I need a special 
> disk image?

Oh, I saw that wiki url now. I'll check this out.

> 
> Best regards
> Thomas
> 
>>         break;
>>     };
>>
>> Noralf.
>>
>>> Best regards
>>> Thomas
>>>
>>>> -Daniel
>>>>
>>>>>
>>>>> Best regards
>>>>> Thomas
>>>>>
>>>>>>
>>>>>> Beware, new connector types have in the past resulted in userspace
>>>>>> burning&crashing. Maybe it's become better ...
>>>>>>
>>>>>> Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
>>>>>>>
>>>>>>>     /**
>>>>>>>      * struct drm_mode_get_connector - Get connector metadata.
>>>>>>> -- 
>>>>>>> 2.23.0
>>>>>>>
>>>>>>> _______________________________________________
>>>>>>> dri-devel mailing list
>>>>>>> dri-devel@lists.freedesktop.org
>>>>>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>>>>>
>>>>>>
>>>>>>
>>>>>
>>>>> -- 
>>>>> Thomas Zimmermann
>>>>> Graphics Driver Developer
>>>>> SUSE Software Solutions Germany GmbH
>>>>> Maxfeldstr. 5, 90409 Nürnberg, Germany
>>>>> (HRB 36809, AG Nürnberg)
>>>>> Geschäftsführer: Felix Imendörffer
>>>>>
>>>>
>>>>
>>>
>>>
>>> _______________________________________________
>>> dri-devel mailing list
>>> dri-devel@lists.freedesktop.org
>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>>
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel@lists.freedesktop.org
>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>
> 
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
> 

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Felix Imendörffer


[-- Attachment #1.2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

[-- Attachment #2: Type: text/plain, Size: 160 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 1/3] drm/uapi: Add USB connector type
  2021-01-22  7:54               ` Thomas Zimmermann
@ 2021-01-22 11:44                 ` Noralf Trønnes
  -1 siblings, 0 replies; 53+ messages in thread
From: Noralf Trønnes @ 2021-01-22 11:44 UTC (permalink / raw)
  To: Thomas Zimmermann, Daniel Vetter
  Cc: hudson, markus, Sam Ravnborg, USB list, dri-devel, Tyler Hardin,
	Lubomir Rintel, pontus.fuchs, peter



Den 22.01.2021 08.54, skrev Thomas Zimmermann:
> Hi
> 
> Am 21.01.21 um 19:07 schrieb Noralf Trønnes:
>>
>>
>> Den 21.01.2021 11.01, skrev Thomas Zimmermann:
>>> Hi
>>>
>>> Am 21.01.21 um 09:27 schrieb Daniel Vetter:
>>>> On Thu, Jan 21, 2021 at 8:45 AM Thomas Zimmermann
>>>> <tzimmermann@suse.de> wrote:
>>>>>
>>>>> Hi Noralf,
>>>>>
>>>>> glad to hear from you! Welcome back!
>>
>> Thanks Thomas!
>>
>>>>>
>>>>> Am 20.01.21 um 18:42 schrieb Daniel Vetter:
>>>>>> On Wed, Jan 20, 2021 at 6:10 PM Noralf Trønnes <noralf@tronnes.org>
>>>>>> wrote:
>>>>>>>
>>>>>>> Add a connector type for USB connected display panels.
>>>>>>>
>>>>>>> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
>>>>>>> ---
>>
>> I have forgotten to update drm_connector_enum_list which maps type to
>> name.
>>
>>>>>>>     include/uapi/drm/drm_mode.h | 1 +
>>>>>>>     1 file changed, 1 insertion(+)
>>>>>>>
>>>>>>> diff --git a/include/uapi/drm/drm_mode.h
>>>>>>> b/include/uapi/drm/drm_mode.h
>>>>>>> index fed66a03c7ae..33024cc5d26e 100644
>>>>>>> --- a/include/uapi/drm/drm_mode.h
>>>>>>> +++ b/include/uapi/drm/drm_mode.h
>>>>>>> @@ -367,6 +367,7 @@ enum drm_mode_subconnector {
>>>>>>>     #define DRM_MODE_CONNECTOR_DPI         17
>>>>>>>     #define DRM_MODE_CONNECTOR_WRITEBACK   18
>>>>>>>     #define DRM_MODE_CONNECTOR_SPI         19
>>>>>>> +#define DRM_MODE_CONNECTOR_USB         20
>>>>>
>>>>> I would not call it USB. I could imagine that at some point a generic
>>>>> USB protocol could serve simple displays (i.e. in the sense of USB HID
>>>>> or data or imaging). (Maybe Thunderbold already counts.) Anyway, USB
>>>>> should be reserved for this case.
>>>>
>>>> We end up calling those DisplayPort, since that's what's being
>>>> transported over thunderbolt or usb-C. So the usb connector would be
>>>> called usb-C. I think the reason we don't do fancy connector names is
>>>> that adding them is a bit a pain. Plus drm/i915 specifically has some
>>>> very quirky connector enumerating that doesn't match much with reality
>>>> unfortunately anyway :-/
>>>
>>> In the case of the other USB drivers, IIRC we use the connector type
>>> that is at the output (i.e., HDMI in the case of udl). I think we should
>>> do the same here. Or use 'Unknown'.
>>>
>>
>> There are 2 DRM USB drivers and they use:
>> - udl: DRM_MODE_CONNECTOR_DVII
> 
> Mine has plain old VGA.

Ok, maybe the Displaylink protocol doesn't provide info about the
connector type or if it does the driver doesn't know about it.

> Maybe we should change generally this to Unknown.
> 
>> - gm12u320: DRM_MODE_CONNECTOR_VGA
>>
>> gm12u320 is a mini projector so it doesn't actually have a VGA
>> connector. I have never seen a udl device but I assume it has a DVII
>> connector?
>>
>> For display adapters it makes sense to use the connector on the adapter
>> as the reported connector, but for display panels that don't have any
>> connector except for the cable that is connected to the hosts USB
>> connector, why can't it be called a USB connector? That's the connector
>> the user sees.
> 
> It's not the relevant connector for the display output. USB is the bus
> system. (Making your argument in terms of discrete GPUs, the connector
> would always be PCI then.)
> 

Yes strictly speaking USB is the bus and the connectors have other
names: USB (type)-A, USB-C etc., but I don't understand the problem
here. Why does it matter that it is a bus?

And wrt PCI it wouldn't be a PCI connector if the card has some other
connector for the display, but if it was possible to connect a display
directly to the PCI connector, then yes I would call that a PCI connector.

This begs the question: Why does the kernel provide info to userspace
about the connector type?

My take is that it is so the user can know which display is connected to
which port on the computer.

What's your opinion?

>>
>> Ofc as Daniel mentions it's a downside that userspace doesn't know about
>> the connector type, and who knows when it will updated (if I don't do
>> it).
>> Weston will name it: "UNNAMED-%d"
>> Mutter: "Unknown%d-%d"
>> X: "Unknown%d-%d"
>>
>> Sam and Laurent has discussed adding a PANEL connector type instead of
>> adding more connector types for panel connectors. I think that would
>> have been a better choice instead of the SPI connector type that I added
>> in 2019. But I think PANEL was meant for panels connected to an internal
>> connector.
>>
>> Here's my protocol connector types and how it's mapped to DRM:
>>
>> #define GUD_CONNECTOR_TYPE_PANEL        0
>> #define GUD_CONNECTOR_TYPE_VGA            1
>> #define GUD_CONNECTOR_TYPE_COMPOSITE        2
>> #define GUD_CONNECTOR_TYPE_SVIDEO        3
>> #define GUD_CONNECTOR_TYPE_COMPONENT        4
>> #define GUD_CONNECTOR_TYPE_DVI            5
>> #define GUD_CONNECTOR_TYPE_DISPLAYPORT        6
>> #define GUD_CONNECTOR_TYPE_HDMI            7
>>
>> static int gud_gadget_ctrl_get_connector(struct gud_gadget *gdg,
>> unsigned int index,
>>                      struct gud_connector_descriptor_req *desc)
>> {
>> ...
>>     gconn = &gdg->connectors[index];
>>
>>     switch (gconn->connector->connector_type) {
>>     case DRM_MODE_CONNECTOR_VGA:
>>         desc->connector_type = GUD_CONNECTOR_TYPE_VGA;
>>         break;
>>     case DRM_MODE_CONNECTOR_DVII:
>>         fallthrough;
>>     case DRM_MODE_CONNECTOR_DVID:
>>         fallthrough;
>>     case DRM_MODE_CONNECTOR_DVIA:
>>         desc->connector_type = GUD_CONNECTOR_TYPE_DVI;
>>         break;
>>     case DRM_MODE_CONNECTOR_Composite:
>>         desc->connector_type = GUD_CONNECTOR_TYPE_COMPOSITE;
>>         break;
>>     case DRM_MODE_CONNECTOR_SVIDEO:
>>         desc->connector_type = GUD_CONNECTOR_TYPE_SVIDEO;
>>         break;
>>     case DRM_MODE_CONNECTOR_Component:
>>         desc->connector_type = GUD_CONNECTOR_TYPE_COMPONENT;
>>         break;
>>     case DRM_MODE_CONNECTOR_DisplayPort:
>>         desc->connector_type = GUD_CONNECTOR_TYPE_DISPLAYPORT;
>>         break;
>>     case DRM_MODE_CONNECTOR_HDMIA:
>>         fallthrough;
>>     case DRM_MODE_CONNECTOR_HDMIB:
>>         desc->connector_type = GUD_CONNECTOR_TYPE_HDMI;
>>         break;
>>     default:
>>         desc->connector_type = GUD_CONNECTOR_TYPE_PANEL;
>>         break;
>>     };
>>
>>
>> int gud_connector_create(struct gud_device *gdrm, unsigned int index)
>> {
>> ...
>>     switch (desc.connector_type) {
>>     case GUD_CONNECTOR_TYPE_PANEL:
>>         connector_type = DRM_MODE_CONNECTOR_USB;
>>         break;
>>     case GUD_CONNECTOR_TYPE_VGA:
>>         connector_type = DRM_MODE_CONNECTOR_VGA;
>>         break;
>>     case GUD_CONNECTOR_TYPE_DVI:
>>         connector_type = DRM_MODE_CONNECTOR_DVID;
>>         break;
>>     case GUD_CONNECTOR_TYPE_COMPOSITE:
>>         connector_type = DRM_MODE_CONNECTOR_Composite;
>>         break;
>>     case GUD_CONNECTOR_TYPE_SVIDEO:
>>         connector_type = DRM_MODE_CONNECTOR_SVIDEO;
>>         break;
>>     case GUD_CONNECTOR_TYPE_COMPONENT:
>>         connector_type = DRM_MODE_CONNECTOR_Component;
>>         break;
>>     case GUD_CONNECTOR_TYPE_DISPLAYPORT:
>>         connector_type = DRM_MODE_CONNECTOR_DisplayPort;
>>         break;
>>     case GUD_CONNECTOR_TYPE_HDMI:
>>         connector_type = DRM_MODE_CONNECTOR_HDMIA;
>>         break;
>>     default: /* future types */
>>         connector_type = DRM_MODE_CONNECTOR_USB;
> 
> The more I look at it the more I think it should be 'Unknown' here.
> 

I don't understand this, how will that be better for the user?

> BTW, can I try this out somehow? I do have an RPi3. Do I need a special
> disk image?

The Pi3 doesn'have a USB device/otg connector so I haven't made an image
for that one. Only the Pi Zero, model A and Pi 4 have that.

The Pi2 and Pi3 have a USB hub on the soc's single USB port.

Noralf.

> 
> Best regards
> Thomas
> 
>>         break;
>>     };
>>
>> Noralf.
>>
>>> Best regards
>>> Thomas
>>>
>>>> -Daniel
>>>>
>>>>>
>>>>> Best regards
>>>>> Thomas
>>>>>
>>>>>>
>>>>>> Beware, new connector types have in the past resulted in userspace
>>>>>> burning&crashing. Maybe it's become better ...
>>>>>>
>>>>>> Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
>>>>>>>
>>>>>>>     /**
>>>>>>>      * struct drm_mode_get_connector - Get connector metadata.
>>>>>>> -- 
>>>>>>> 2.23.0
>>>>>>>
>>>>>>> _______________________________________________
>>>>>>> dri-devel mailing list
>>>>>>> dri-devel@lists.freedesktop.org
>>>>>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>>>>>
>>>>>>
>>>>>>
>>>>>
>>>>> -- 
>>>>> Thomas Zimmermann
>>>>> Graphics Driver Developer
>>>>> SUSE Software Solutions Germany GmbH
>>>>> Maxfeldstr. 5, 90409 Nürnberg, Germany
>>>>> (HRB 36809, AG Nürnberg)
>>>>> Geschäftsführer: Felix Imendörffer
>>>>>
>>>>
>>>>
>>>
>>>
>>> _______________________________________________
>>> dri-devel mailing list
>>> dri-devel@lists.freedesktop.org
>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>>
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel@lists.freedesktop.org
>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>
> 

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 1/3] drm/uapi: Add USB connector type
@ 2021-01-22 11:44                 ` Noralf Trønnes
  0 siblings, 0 replies; 53+ messages in thread
From: Noralf Trønnes @ 2021-01-22 11:44 UTC (permalink / raw)
  To: Thomas Zimmermann, Daniel Vetter
  Cc: hudson, markus, peter, USB list, dri-devel, Tyler Hardin,
	Lubomir Rintel, pontus.fuchs, Sam Ravnborg



Den 22.01.2021 08.54, skrev Thomas Zimmermann:
> Hi
> 
> Am 21.01.21 um 19:07 schrieb Noralf Trønnes:
>>
>>
>> Den 21.01.2021 11.01, skrev Thomas Zimmermann:
>>> Hi
>>>
>>> Am 21.01.21 um 09:27 schrieb Daniel Vetter:
>>>> On Thu, Jan 21, 2021 at 8:45 AM Thomas Zimmermann
>>>> <tzimmermann@suse.de> wrote:
>>>>>
>>>>> Hi Noralf,
>>>>>
>>>>> glad to hear from you! Welcome back!
>>
>> Thanks Thomas!
>>
>>>>>
>>>>> Am 20.01.21 um 18:42 schrieb Daniel Vetter:
>>>>>> On Wed, Jan 20, 2021 at 6:10 PM Noralf Trønnes <noralf@tronnes.org>
>>>>>> wrote:
>>>>>>>
>>>>>>> Add a connector type for USB connected display panels.
>>>>>>>
>>>>>>> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
>>>>>>> ---
>>
>> I have forgotten to update drm_connector_enum_list which maps type to
>> name.
>>
>>>>>>>     include/uapi/drm/drm_mode.h | 1 +
>>>>>>>     1 file changed, 1 insertion(+)
>>>>>>>
>>>>>>> diff --git a/include/uapi/drm/drm_mode.h
>>>>>>> b/include/uapi/drm/drm_mode.h
>>>>>>> index fed66a03c7ae..33024cc5d26e 100644
>>>>>>> --- a/include/uapi/drm/drm_mode.h
>>>>>>> +++ b/include/uapi/drm/drm_mode.h
>>>>>>> @@ -367,6 +367,7 @@ enum drm_mode_subconnector {
>>>>>>>     #define DRM_MODE_CONNECTOR_DPI         17
>>>>>>>     #define DRM_MODE_CONNECTOR_WRITEBACK   18
>>>>>>>     #define DRM_MODE_CONNECTOR_SPI         19
>>>>>>> +#define DRM_MODE_CONNECTOR_USB         20
>>>>>
>>>>> I would not call it USB. I could imagine that at some point a generic
>>>>> USB protocol could serve simple displays (i.e. in the sense of USB HID
>>>>> or data or imaging). (Maybe Thunderbold already counts.) Anyway, USB
>>>>> should be reserved for this case.
>>>>
>>>> We end up calling those DisplayPort, since that's what's being
>>>> transported over thunderbolt or usb-C. So the usb connector would be
>>>> called usb-C. I think the reason we don't do fancy connector names is
>>>> that adding them is a bit a pain. Plus drm/i915 specifically has some
>>>> very quirky connector enumerating that doesn't match much with reality
>>>> unfortunately anyway :-/
>>>
>>> In the case of the other USB drivers, IIRC we use the connector type
>>> that is at the output (i.e., HDMI in the case of udl). I think we should
>>> do the same here. Or use 'Unknown'.
>>>
>>
>> There are 2 DRM USB drivers and they use:
>> - udl: DRM_MODE_CONNECTOR_DVII
> 
> Mine has plain old VGA.

Ok, maybe the Displaylink protocol doesn't provide info about the
connector type or if it does the driver doesn't know about it.

> Maybe we should change generally this to Unknown.
> 
>> - gm12u320: DRM_MODE_CONNECTOR_VGA
>>
>> gm12u320 is a mini projector so it doesn't actually have a VGA
>> connector. I have never seen a udl device but I assume it has a DVII
>> connector?
>>
>> For display adapters it makes sense to use the connector on the adapter
>> as the reported connector, but for display panels that don't have any
>> connector except for the cable that is connected to the hosts USB
>> connector, why can't it be called a USB connector? That's the connector
>> the user sees.
> 
> It's not the relevant connector for the display output. USB is the bus
> system. (Making your argument in terms of discrete GPUs, the connector
> would always be PCI then.)
> 

Yes strictly speaking USB is the bus and the connectors have other
names: USB (type)-A, USB-C etc., but I don't understand the problem
here. Why does it matter that it is a bus?

And wrt PCI it wouldn't be a PCI connector if the card has some other
connector for the display, but if it was possible to connect a display
directly to the PCI connector, then yes I would call that a PCI connector.

This begs the question: Why does the kernel provide info to userspace
about the connector type?

My take is that it is so the user can know which display is connected to
which port on the computer.

What's your opinion?

>>
>> Ofc as Daniel mentions it's a downside that userspace doesn't know about
>> the connector type, and who knows when it will updated (if I don't do
>> it).
>> Weston will name it: "UNNAMED-%d"
>> Mutter: "Unknown%d-%d"
>> X: "Unknown%d-%d"
>>
>> Sam and Laurent has discussed adding a PANEL connector type instead of
>> adding more connector types for panel connectors. I think that would
>> have been a better choice instead of the SPI connector type that I added
>> in 2019. But I think PANEL was meant for panels connected to an internal
>> connector.
>>
>> Here's my protocol connector types and how it's mapped to DRM:
>>
>> #define GUD_CONNECTOR_TYPE_PANEL        0
>> #define GUD_CONNECTOR_TYPE_VGA            1
>> #define GUD_CONNECTOR_TYPE_COMPOSITE        2
>> #define GUD_CONNECTOR_TYPE_SVIDEO        3
>> #define GUD_CONNECTOR_TYPE_COMPONENT        4
>> #define GUD_CONNECTOR_TYPE_DVI            5
>> #define GUD_CONNECTOR_TYPE_DISPLAYPORT        6
>> #define GUD_CONNECTOR_TYPE_HDMI            7
>>
>> static int gud_gadget_ctrl_get_connector(struct gud_gadget *gdg,
>> unsigned int index,
>>                      struct gud_connector_descriptor_req *desc)
>> {
>> ...
>>     gconn = &gdg->connectors[index];
>>
>>     switch (gconn->connector->connector_type) {
>>     case DRM_MODE_CONNECTOR_VGA:
>>         desc->connector_type = GUD_CONNECTOR_TYPE_VGA;
>>         break;
>>     case DRM_MODE_CONNECTOR_DVII:
>>         fallthrough;
>>     case DRM_MODE_CONNECTOR_DVID:
>>         fallthrough;
>>     case DRM_MODE_CONNECTOR_DVIA:
>>         desc->connector_type = GUD_CONNECTOR_TYPE_DVI;
>>         break;
>>     case DRM_MODE_CONNECTOR_Composite:
>>         desc->connector_type = GUD_CONNECTOR_TYPE_COMPOSITE;
>>         break;
>>     case DRM_MODE_CONNECTOR_SVIDEO:
>>         desc->connector_type = GUD_CONNECTOR_TYPE_SVIDEO;
>>         break;
>>     case DRM_MODE_CONNECTOR_Component:
>>         desc->connector_type = GUD_CONNECTOR_TYPE_COMPONENT;
>>         break;
>>     case DRM_MODE_CONNECTOR_DisplayPort:
>>         desc->connector_type = GUD_CONNECTOR_TYPE_DISPLAYPORT;
>>         break;
>>     case DRM_MODE_CONNECTOR_HDMIA:
>>         fallthrough;
>>     case DRM_MODE_CONNECTOR_HDMIB:
>>         desc->connector_type = GUD_CONNECTOR_TYPE_HDMI;
>>         break;
>>     default:
>>         desc->connector_type = GUD_CONNECTOR_TYPE_PANEL;
>>         break;
>>     };
>>
>>
>> int gud_connector_create(struct gud_device *gdrm, unsigned int index)
>> {
>> ...
>>     switch (desc.connector_type) {
>>     case GUD_CONNECTOR_TYPE_PANEL:
>>         connector_type = DRM_MODE_CONNECTOR_USB;
>>         break;
>>     case GUD_CONNECTOR_TYPE_VGA:
>>         connector_type = DRM_MODE_CONNECTOR_VGA;
>>         break;
>>     case GUD_CONNECTOR_TYPE_DVI:
>>         connector_type = DRM_MODE_CONNECTOR_DVID;
>>         break;
>>     case GUD_CONNECTOR_TYPE_COMPOSITE:
>>         connector_type = DRM_MODE_CONNECTOR_Composite;
>>         break;
>>     case GUD_CONNECTOR_TYPE_SVIDEO:
>>         connector_type = DRM_MODE_CONNECTOR_SVIDEO;
>>         break;
>>     case GUD_CONNECTOR_TYPE_COMPONENT:
>>         connector_type = DRM_MODE_CONNECTOR_Component;
>>         break;
>>     case GUD_CONNECTOR_TYPE_DISPLAYPORT:
>>         connector_type = DRM_MODE_CONNECTOR_DisplayPort;
>>         break;
>>     case GUD_CONNECTOR_TYPE_HDMI:
>>         connector_type = DRM_MODE_CONNECTOR_HDMIA;
>>         break;
>>     default: /* future types */
>>         connector_type = DRM_MODE_CONNECTOR_USB;
> 
> The more I look at it the more I think it should be 'Unknown' here.
> 

I don't understand this, how will that be better for the user?

> BTW, can I try this out somehow? I do have an RPi3. Do I need a special
> disk image?

The Pi3 doesn'have a USB device/otg connector so I haven't made an image
for that one. Only the Pi Zero, model A and Pi 4 have that.

The Pi2 and Pi3 have a USB hub on the soc's single USB port.

Noralf.

> 
> Best regards
> Thomas
> 
>>         break;
>>     };
>>
>> Noralf.
>>
>>> Best regards
>>> Thomas
>>>
>>>> -Daniel
>>>>
>>>>>
>>>>> Best regards
>>>>> Thomas
>>>>>
>>>>>>
>>>>>> Beware, new connector types have in the past resulted in userspace
>>>>>> burning&crashing. Maybe it's become better ...
>>>>>>
>>>>>> Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
>>>>>>>
>>>>>>>     /**
>>>>>>>      * struct drm_mode_get_connector - Get connector metadata.
>>>>>>> -- 
>>>>>>> 2.23.0
>>>>>>>
>>>>>>> _______________________________________________
>>>>>>> dri-devel mailing list
>>>>>>> dri-devel@lists.freedesktop.org
>>>>>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>>>>>
>>>>>>
>>>>>>
>>>>>
>>>>> -- 
>>>>> Thomas Zimmermann
>>>>> Graphics Driver Developer
>>>>> SUSE Software Solutions Germany GmbH
>>>>> Maxfeldstr. 5, 90409 Nürnberg, Germany
>>>>> (HRB 36809, AG Nürnberg)
>>>>> Geschäftsführer: Felix Imendörffer
>>>>>
>>>>
>>>>
>>>
>>>
>>> _______________________________________________
>>> dri-devel mailing list
>>> dri-devel@lists.freedesktop.org
>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>>
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel@lists.freedesktop.org
>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>
> 
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 1/3] drm/uapi: Add USB connector type
  2021-01-22 11:44                 ` Noralf Trønnes
@ 2021-01-22 12:47                   ` Thomas Zimmermann
  -1 siblings, 0 replies; 53+ messages in thread
From: Thomas Zimmermann @ 2021-01-22 12:47 UTC (permalink / raw)
  To: Noralf Trønnes, Daniel Vetter
  Cc: hudson, markus, Sam Ravnborg, USB list, dri-devel, Tyler Hardin,
	Lubomir Rintel, pontus.fuchs, peter


[-- Attachment #1.1: Type: text/plain, Size: 7485 bytes --]

Hi

Am 22.01.21 um 12:44 schrieb Noralf Trønnes:
> 
> And wrt PCI it wouldn't be a PCI connector if the card has some other
> connector for the display, but if it was possible to connect a display
> directly to the PCI connector, then yes I would call that a PCI connector.

You're not connecting a display to the computer. You're connecting an 
RPi and then connect the display to the RPi. The RPi acts like an 
external graphics card.

> This begs the question: Why does the kernel provide info to userspace
> about the connector type?
> 
> My take is that it is so the user can know which display is connected to
> which port on the computer.

This exactly illustrates the problem with the current naming. For a 
single output the distinction between bus and connector might be fuzzy. 
As soon as a connected SoC contains multiple connectors. The user then 
sees names such as card1-USB-0 and card1-USB-1, which makes no sense.

> 
> What's your opinion?
> 
>>>
>>> Ofc as Daniel mentions it's a downside that userspace doesn't know about
>>> the connector type, and who knows when it will updated (if I don't do
>>> it).
>>> Weston will name it: "UNNAMED-%d"
>>> Mutter: "Unknown%d-%d"
>>> X: "Unknown%d-%d"
>>>
>>> Sam and Laurent has discussed adding a PANEL connector type instead of
>>> adding more connector types for panel connectors. I think that would
>>> have been a better choice instead of the SPI connector type that I added
>>> in 2019. But I think PANEL was meant for panels connected to an internal
>>> connector.
>>>
>>> Here's my protocol connector types and how it's mapped to DRM:
>>>
>>> #define GUD_CONNECTOR_TYPE_PANEL        0
>>> #define GUD_CONNECTOR_TYPE_VGA            1
>>> #define GUD_CONNECTOR_TYPE_COMPOSITE        2
>>> #define GUD_CONNECTOR_TYPE_SVIDEO        3
>>> #define GUD_CONNECTOR_TYPE_COMPONENT        4
>>> #define GUD_CONNECTOR_TYPE_DVI            5
>>> #define GUD_CONNECTOR_TYPE_DISPLAYPORT        6
>>> #define GUD_CONNECTOR_TYPE_HDMI            7
>>>
>>> static int gud_gadget_ctrl_get_connector(struct gud_gadget *gdg,
>>> unsigned int index,
>>>                       struct gud_connector_descriptor_req *desc)
>>> {
>>> ...
>>>      gconn = &gdg->connectors[index];
>>>
>>>      switch (gconn->connector->connector_type) {
>>>      case DRM_MODE_CONNECTOR_VGA:
>>>          desc->connector_type = GUD_CONNECTOR_TYPE_VGA;
>>>          break;
>>>      case DRM_MODE_CONNECTOR_DVII:
>>>          fallthrough;
>>>      case DRM_MODE_CONNECTOR_DVID:
>>>          fallthrough;
>>>      case DRM_MODE_CONNECTOR_DVIA:
>>>          desc->connector_type = GUD_CONNECTOR_TYPE_DVI;
>>>          break;
>>>      case DRM_MODE_CONNECTOR_Composite:
>>>          desc->connector_type = GUD_CONNECTOR_TYPE_COMPOSITE;
>>>          break;
>>>      case DRM_MODE_CONNECTOR_SVIDEO:
>>>          desc->connector_type = GUD_CONNECTOR_TYPE_SVIDEO;
>>>          break;
>>>      case DRM_MODE_CONNECTOR_Component:
>>>          desc->connector_type = GUD_CONNECTOR_TYPE_COMPONENT;
>>>          break;
>>>      case DRM_MODE_CONNECTOR_DisplayPort:
>>>          desc->connector_type = GUD_CONNECTOR_TYPE_DISPLAYPORT;
>>>          break;
>>>      case DRM_MODE_CONNECTOR_HDMIA:
>>>          fallthrough;
>>>      case DRM_MODE_CONNECTOR_HDMIB:
>>>          desc->connector_type = GUD_CONNECTOR_TYPE_HDMI;
>>>          break;
>>>      default:
>>>          desc->connector_type = GUD_CONNECTOR_TYPE_PANEL;
>>>          break;
>>>      };
>>>
>>>
>>> int gud_connector_create(struct gud_device *gdrm, unsigned int index)
>>> {
>>> ...
>>>      switch (desc.connector_type) {
>>>      case GUD_CONNECTOR_TYPE_PANEL:
>>>          connector_type = DRM_MODE_CONNECTOR_USB;
>>>          break;
>>>      case GUD_CONNECTOR_TYPE_VGA:
>>>          connector_type = DRM_MODE_CONNECTOR_VGA;
>>>          break;
>>>      case GUD_CONNECTOR_TYPE_DVI:
>>>          connector_type = DRM_MODE_CONNECTOR_DVID;
>>>          break;
>>>      case GUD_CONNECTOR_TYPE_COMPOSITE:
>>>          connector_type = DRM_MODE_CONNECTOR_Composite;
>>>          break;
>>>      case GUD_CONNECTOR_TYPE_SVIDEO:
>>>          connector_type = DRM_MODE_CONNECTOR_SVIDEO;
>>>          break;
>>>      case GUD_CONNECTOR_TYPE_COMPONENT:
>>>          connector_type = DRM_MODE_CONNECTOR_Component;
>>>          break;
>>>      case GUD_CONNECTOR_TYPE_DISPLAYPORT:
>>>          connector_type = DRM_MODE_CONNECTOR_DisplayPort;
>>>          break;
>>>      case GUD_CONNECTOR_TYPE_HDMI:
>>>          connector_type = DRM_MODE_CONNECTOR_HDMIA;
>>>          break;
>>>      default: /* future types */
>>>          connector_type = DRM_MODE_CONNECTOR_USB;
>>
>> The more I look at it the more I think it should be 'Unknown' here.
>>
> 
> I don't understand this, how will that be better for the user?

As I said before, the display is not connected via USB. The RPi (i.e., 
graphics card) is. The naming would be off.

Best regards
Thomas

> 
>> BTW, can I try this out somehow? I do have an RPi3. Do I need a special
>> disk image?
> 
> The Pi3 doesn'have a USB device/otg connector so I haven't made an image
> for that one. Only the Pi Zero, model A and Pi 4 have that.
> 
> The Pi2 and Pi3 have a USB hub on the soc's single USB port.
> 
> Noralf.
> 
>>
>> Best regards
>> Thomas
>>
>>>          break;
>>>      };
>>>
>>> Noralf.
>>>
>>>> Best regards
>>>> Thomas
>>>>
>>>>> -Daniel
>>>>>
>>>>>>
>>>>>> Best regards
>>>>>> Thomas
>>>>>>
>>>>>>>
>>>>>>> Beware, new connector types have in the past resulted in userspace
>>>>>>> burning&crashing. Maybe it's become better ...
>>>>>>>
>>>>>>> Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
>>>>>>>>
>>>>>>>>      /**
>>>>>>>>       * struct drm_mode_get_connector - Get connector metadata.
>>>>>>>> -- 
>>>>>>>> 2.23.0
>>>>>>>>
>>>>>>>> _______________________________________________
>>>>>>>> dri-devel mailing list
>>>>>>>> dri-devel@lists.freedesktop.org
>>>>>>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>
>>>>>> -- 
>>>>>> Thomas Zimmermann
>>>>>> Graphics Driver Developer
>>>>>> SUSE Software Solutions Germany GmbH
>>>>>> Maxfeldstr. 5, 90409 Nürnberg, Germany
>>>>>> (HRB 36809, AG Nürnberg)
>>>>>> Geschäftsführer: Felix Imendörffer
>>>>>>
>>>>>
>>>>>
>>>>
>>>>
>>>> _______________________________________________
>>>> dri-devel mailing list
>>>> dri-devel@lists.freedesktop.org
>>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>>>
>>> _______________________________________________
>>> dri-devel mailing list
>>> dri-devel@lists.freedesktop.org
>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>>
>>

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Felix Imendörffer


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 1/3] drm/uapi: Add USB connector type
@ 2021-01-22 12:47                   ` Thomas Zimmermann
  0 siblings, 0 replies; 53+ messages in thread
From: Thomas Zimmermann @ 2021-01-22 12:47 UTC (permalink / raw)
  To: Noralf Trønnes, Daniel Vetter
  Cc: hudson, markus, peter, USB list, dri-devel, Tyler Hardin,
	Lubomir Rintel, pontus.fuchs, Sam Ravnborg


[-- Attachment #1.1.1: Type: text/plain, Size: 7485 bytes --]

Hi

Am 22.01.21 um 12:44 schrieb Noralf Trønnes:
> 
> And wrt PCI it wouldn't be a PCI connector if the card has some other
> connector for the display, but if it was possible to connect a display
> directly to the PCI connector, then yes I would call that a PCI connector.

You're not connecting a display to the computer. You're connecting an 
RPi and then connect the display to the RPi. The RPi acts like an 
external graphics card.

> This begs the question: Why does the kernel provide info to userspace
> about the connector type?
> 
> My take is that it is so the user can know which display is connected to
> which port on the computer.

This exactly illustrates the problem with the current naming. For a 
single output the distinction between bus and connector might be fuzzy. 
As soon as a connected SoC contains multiple connectors. The user then 
sees names such as card1-USB-0 and card1-USB-1, which makes no sense.

> 
> What's your opinion?
> 
>>>
>>> Ofc as Daniel mentions it's a downside that userspace doesn't know about
>>> the connector type, and who knows when it will updated (if I don't do
>>> it).
>>> Weston will name it: "UNNAMED-%d"
>>> Mutter: "Unknown%d-%d"
>>> X: "Unknown%d-%d"
>>>
>>> Sam and Laurent has discussed adding a PANEL connector type instead of
>>> adding more connector types for panel connectors. I think that would
>>> have been a better choice instead of the SPI connector type that I added
>>> in 2019. But I think PANEL was meant for panels connected to an internal
>>> connector.
>>>
>>> Here's my protocol connector types and how it's mapped to DRM:
>>>
>>> #define GUD_CONNECTOR_TYPE_PANEL        0
>>> #define GUD_CONNECTOR_TYPE_VGA            1
>>> #define GUD_CONNECTOR_TYPE_COMPOSITE        2
>>> #define GUD_CONNECTOR_TYPE_SVIDEO        3
>>> #define GUD_CONNECTOR_TYPE_COMPONENT        4
>>> #define GUD_CONNECTOR_TYPE_DVI            5
>>> #define GUD_CONNECTOR_TYPE_DISPLAYPORT        6
>>> #define GUD_CONNECTOR_TYPE_HDMI            7
>>>
>>> static int gud_gadget_ctrl_get_connector(struct gud_gadget *gdg,
>>> unsigned int index,
>>>                       struct gud_connector_descriptor_req *desc)
>>> {
>>> ...
>>>      gconn = &gdg->connectors[index];
>>>
>>>      switch (gconn->connector->connector_type) {
>>>      case DRM_MODE_CONNECTOR_VGA:
>>>          desc->connector_type = GUD_CONNECTOR_TYPE_VGA;
>>>          break;
>>>      case DRM_MODE_CONNECTOR_DVII:
>>>          fallthrough;
>>>      case DRM_MODE_CONNECTOR_DVID:
>>>          fallthrough;
>>>      case DRM_MODE_CONNECTOR_DVIA:
>>>          desc->connector_type = GUD_CONNECTOR_TYPE_DVI;
>>>          break;
>>>      case DRM_MODE_CONNECTOR_Composite:
>>>          desc->connector_type = GUD_CONNECTOR_TYPE_COMPOSITE;
>>>          break;
>>>      case DRM_MODE_CONNECTOR_SVIDEO:
>>>          desc->connector_type = GUD_CONNECTOR_TYPE_SVIDEO;
>>>          break;
>>>      case DRM_MODE_CONNECTOR_Component:
>>>          desc->connector_type = GUD_CONNECTOR_TYPE_COMPONENT;
>>>          break;
>>>      case DRM_MODE_CONNECTOR_DisplayPort:
>>>          desc->connector_type = GUD_CONNECTOR_TYPE_DISPLAYPORT;
>>>          break;
>>>      case DRM_MODE_CONNECTOR_HDMIA:
>>>          fallthrough;
>>>      case DRM_MODE_CONNECTOR_HDMIB:
>>>          desc->connector_type = GUD_CONNECTOR_TYPE_HDMI;
>>>          break;
>>>      default:
>>>          desc->connector_type = GUD_CONNECTOR_TYPE_PANEL;
>>>          break;
>>>      };
>>>
>>>
>>> int gud_connector_create(struct gud_device *gdrm, unsigned int index)
>>> {
>>> ...
>>>      switch (desc.connector_type) {
>>>      case GUD_CONNECTOR_TYPE_PANEL:
>>>          connector_type = DRM_MODE_CONNECTOR_USB;
>>>          break;
>>>      case GUD_CONNECTOR_TYPE_VGA:
>>>          connector_type = DRM_MODE_CONNECTOR_VGA;
>>>          break;
>>>      case GUD_CONNECTOR_TYPE_DVI:
>>>          connector_type = DRM_MODE_CONNECTOR_DVID;
>>>          break;
>>>      case GUD_CONNECTOR_TYPE_COMPOSITE:
>>>          connector_type = DRM_MODE_CONNECTOR_Composite;
>>>          break;
>>>      case GUD_CONNECTOR_TYPE_SVIDEO:
>>>          connector_type = DRM_MODE_CONNECTOR_SVIDEO;
>>>          break;
>>>      case GUD_CONNECTOR_TYPE_COMPONENT:
>>>          connector_type = DRM_MODE_CONNECTOR_Component;
>>>          break;
>>>      case GUD_CONNECTOR_TYPE_DISPLAYPORT:
>>>          connector_type = DRM_MODE_CONNECTOR_DisplayPort;
>>>          break;
>>>      case GUD_CONNECTOR_TYPE_HDMI:
>>>          connector_type = DRM_MODE_CONNECTOR_HDMIA;
>>>          break;
>>>      default: /* future types */
>>>          connector_type = DRM_MODE_CONNECTOR_USB;
>>
>> The more I look at it the more I think it should be 'Unknown' here.
>>
> 
> I don't understand this, how will that be better for the user?

As I said before, the display is not connected via USB. The RPi (i.e., 
graphics card) is. The naming would be off.

Best regards
Thomas

> 
>> BTW, can I try this out somehow? I do have an RPi3. Do I need a special
>> disk image?
> 
> The Pi3 doesn'have a USB device/otg connector so I haven't made an image
> for that one. Only the Pi Zero, model A and Pi 4 have that.
> 
> The Pi2 and Pi3 have a USB hub on the soc's single USB port.
> 
> Noralf.
> 
>>
>> Best regards
>> Thomas
>>
>>>          break;
>>>      };
>>>
>>> Noralf.
>>>
>>>> Best regards
>>>> Thomas
>>>>
>>>>> -Daniel
>>>>>
>>>>>>
>>>>>> Best regards
>>>>>> Thomas
>>>>>>
>>>>>>>
>>>>>>> Beware, new connector types have in the past resulted in userspace
>>>>>>> burning&crashing. Maybe it's become better ...
>>>>>>>
>>>>>>> Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
>>>>>>>>
>>>>>>>>      /**
>>>>>>>>       * struct drm_mode_get_connector - Get connector metadata.
>>>>>>>> -- 
>>>>>>>> 2.23.0
>>>>>>>>
>>>>>>>> _______________________________________________
>>>>>>>> dri-devel mailing list
>>>>>>>> dri-devel@lists.freedesktop.org
>>>>>>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>
>>>>>> -- 
>>>>>> Thomas Zimmermann
>>>>>> Graphics Driver Developer
>>>>>> SUSE Software Solutions Germany GmbH
>>>>>> Maxfeldstr. 5, 90409 Nürnberg, Germany
>>>>>> (HRB 36809, AG Nürnberg)
>>>>>> Geschäftsführer: Felix Imendörffer
>>>>>>
>>>>>
>>>>>
>>>>
>>>>
>>>> _______________________________________________
>>>> dri-devel mailing list
>>>> dri-devel@lists.freedesktop.org
>>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>>>
>>> _______________________________________________
>>> dri-devel mailing list
>>> dri-devel@lists.freedesktop.org
>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>>
>>

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Felix Imendörffer


[-- Attachment #1.2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

[-- Attachment #2: Type: text/plain, Size: 160 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 1/3] drm/uapi: Add USB connector type
  2021-01-22 12:47                   ` Thomas Zimmermann
@ 2021-01-22 14:35                     ` Noralf Trønnes
  -1 siblings, 0 replies; 53+ messages in thread
From: Noralf Trønnes @ 2021-01-22 14:35 UTC (permalink / raw)
  To: Thomas Zimmermann, Daniel Vetter
  Cc: hudson, markus, Sam Ravnborg, USB list, dri-devel, Tyler Hardin,
	Lubomir Rintel, pontus.fuchs, peter



Den 22.01.2021 13.47, skrev Thomas Zimmermann:
> Hi
> 
> Am 22.01.21 um 12:44 schrieb Noralf Trønnes:
>>
>> And wrt PCI it wouldn't be a PCI connector if the card has some other
>> connector for the display, but if it was possible to connect a display
>> directly to the PCI connector, then yes I would call that a PCI
>> connector.
> 
> You're not connecting a display to the computer. You're connecting an
> RPi and then connect the display to the RPi. The RPi acts like an
> external graphics card.
> 
>> This begs the question: Why does the kernel provide info to userspace
>> about the connector type?
>>
>> My take is that it is so the user can know which display is connected to
>> which port on the computer.
> 
> This exactly illustrates the problem with the current naming. For a
> single output the distinction between bus and connector might be fuzzy.
> As soon as a connected SoC contains multiple connectors. The user then
> sees names such as card1-USB-0 and card1-USB-1, which makes no sense.
> 

If you look at the code I pasted in, you'll see that the SoC connector
types are passed through to the host driver as-is unless they are panel
connectors like DSI/DPI, which will be interpreted as USB (the protocol
does support multiple connectors, but only one can be used at a time).

So for the Pi4 as a display adapter, the host will see card1-HDMI-0 and
card1-HDMI-1, the same is true for the composite output (if enabled) it
shows up as card1-Composite-0 (can't be enabled together with HDMI).

If the Pi4 is used together with a DSI connected touchscreen, it makes
sense to disable the SoC HDMI outputs and the host only will see the
board as card1-USB-0 (I haven't done this exercise yet since there's
problems with getting the official Pi touchscreen to work with vc4 on Pi4).

The USB connector type is most important for tiny displays that is
microcontroller based without Linux running. There are lots of tiny SPI
displays and I expect this market to shift towards USB because it much
easier to connect and the display will be useable on a desktop/server
computer as status displays perhaps. But embedded will also benefit from
having these displays USB interfaced.

Another use case I see is repurposing old Android tablets as USB
displays that can be connected to any computer and become a touchscreen.
In this case I also want the connector to be called card1-USB-0 (I
haven't done any work in this area, old Android is fbdev so some work is
needed for this to happen).

Noralf.

>>
>> What's your opinion?
>>
>>>>
>>>> Ofc as Daniel mentions it's a downside that userspace doesn't know
>>>> about
>>>> the connector type, and who knows when it will updated (if I don't do
>>>> it).
>>>> Weston will name it: "UNNAMED-%d"
>>>> Mutter: "Unknown%d-%d"
>>>> X: "Unknown%d-%d"
>>>>
>>>> Sam and Laurent has discussed adding a PANEL connector type instead of
>>>> adding more connector types for panel connectors. I think that would
>>>> have been a better choice instead of the SPI connector type that I
>>>> added
>>>> in 2019. But I think PANEL was meant for panels connected to an
>>>> internal
>>>> connector.
>>>>
>>>> Here's my protocol connector types and how it's mapped to DRM:
>>>>
>>>> #define GUD_CONNECTOR_TYPE_PANEL        0
>>>> #define GUD_CONNECTOR_TYPE_VGA            1
>>>> #define GUD_CONNECTOR_TYPE_COMPOSITE        2
>>>> #define GUD_CONNECTOR_TYPE_SVIDEO        3
>>>> #define GUD_CONNECTOR_TYPE_COMPONENT        4
>>>> #define GUD_CONNECTOR_TYPE_DVI            5
>>>> #define GUD_CONNECTOR_TYPE_DISPLAYPORT        6
>>>> #define GUD_CONNECTOR_TYPE_HDMI            7
>>>>
>>>> static int gud_gadget_ctrl_get_connector(struct gud_gadget *gdg,
>>>> unsigned int index,
>>>>                       struct gud_connector_descriptor_req *desc)
>>>> {
>>>> ...
>>>>      gconn = &gdg->connectors[index];
>>>>
>>>>      switch (gconn->connector->connector_type) {
>>>>      case DRM_MODE_CONNECTOR_VGA:
>>>>          desc->connector_type = GUD_CONNECTOR_TYPE_VGA;
>>>>          break;
>>>>      case DRM_MODE_CONNECTOR_DVII:
>>>>          fallthrough;
>>>>      case DRM_MODE_CONNECTOR_DVID:
>>>>          fallthrough;
>>>>      case DRM_MODE_CONNECTOR_DVIA:
>>>>          desc->connector_type = GUD_CONNECTOR_TYPE_DVI;
>>>>          break;
>>>>      case DRM_MODE_CONNECTOR_Composite:
>>>>          desc->connector_type = GUD_CONNECTOR_TYPE_COMPOSITE;
>>>>          break;
>>>>      case DRM_MODE_CONNECTOR_SVIDEO:
>>>>          desc->connector_type = GUD_CONNECTOR_TYPE_SVIDEO;
>>>>          break;
>>>>      case DRM_MODE_CONNECTOR_Component:
>>>>          desc->connector_type = GUD_CONNECTOR_TYPE_COMPONENT;
>>>>          break;
>>>>      case DRM_MODE_CONNECTOR_DisplayPort:
>>>>          desc->connector_type = GUD_CONNECTOR_TYPE_DISPLAYPORT;
>>>>          break;
>>>>      case DRM_MODE_CONNECTOR_HDMIA:
>>>>          fallthrough;
>>>>      case DRM_MODE_CONNECTOR_HDMIB:
>>>>          desc->connector_type = GUD_CONNECTOR_TYPE_HDMI;
>>>>          break;
>>>>      default:
>>>>          desc->connector_type = GUD_CONNECTOR_TYPE_PANEL;
>>>>          break;
>>>>      };
>>>>
>>>>
>>>> int gud_connector_create(struct gud_device *gdrm, unsigned int index)
>>>> {
>>>> ...
>>>>      switch (desc.connector_type) {
>>>>      case GUD_CONNECTOR_TYPE_PANEL:
>>>>          connector_type = DRM_MODE_CONNECTOR_USB;
>>>>          break;
>>>>      case GUD_CONNECTOR_TYPE_VGA:
>>>>          connector_type = DRM_MODE_CONNECTOR_VGA;
>>>>          break;
>>>>      case GUD_CONNECTOR_TYPE_DVI:
>>>>          connector_type = DRM_MODE_CONNECTOR_DVID;
>>>>          break;
>>>>      case GUD_CONNECTOR_TYPE_COMPOSITE:
>>>>          connector_type = DRM_MODE_CONNECTOR_Composite;
>>>>          break;
>>>>      case GUD_CONNECTOR_TYPE_SVIDEO:
>>>>          connector_type = DRM_MODE_CONNECTOR_SVIDEO;
>>>>          break;
>>>>      case GUD_CONNECTOR_TYPE_COMPONENT:
>>>>          connector_type = DRM_MODE_CONNECTOR_Component;
>>>>          break;
>>>>      case GUD_CONNECTOR_TYPE_DISPLAYPORT:
>>>>          connector_type = DRM_MODE_CONNECTOR_DisplayPort;
>>>>          break;
>>>>      case GUD_CONNECTOR_TYPE_HDMI:
>>>>          connector_type = DRM_MODE_CONNECTOR_HDMIA;
>>>>          break;
>>>>      default: /* future types */
>>>>          connector_type = DRM_MODE_CONNECTOR_USB;
>>>
>>> The more I look at it the more I think it should be 'Unknown' here.
>>>
>>
>> I don't understand this, how will that be better for the user?
> 
> As I said before, the display is not connected via USB. The RPi (i.e.,
> graphics card) is. The naming would be off.
> 
> Best regards
> Thomas
> 
>>
>>> BTW, can I try this out somehow? I do have an RPi3. Do I need a special
>>> disk image?
>>
>> The Pi3 doesn'have a USB device/otg connector so I haven't made an image
>> for that one. Only the Pi Zero, model A and Pi 4 have that.
>>
>> The Pi2 and Pi3 have a USB hub on the soc's single USB port.
>>
>> Noralf.
>>
>>>
>>> Best regards
>>> Thomas
>>>
>>>>          break;
>>>>      };
>>>>
>>>> Noralf.
>>>>
>>>>> Best regards
>>>>> Thomas
>>>>>
>>>>>> -Daniel
>>>>>>
>>>>>>>
>>>>>>> Best regards
>>>>>>> Thomas
>>>>>>>
>>>>>>>>
>>>>>>>> Beware, new connector types have in the past resulted in userspace
>>>>>>>> burning&crashing. Maybe it's become better ...
>>>>>>>>
>>>>>>>> Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
>>>>>>>>>
>>>>>>>>>      /**
>>>>>>>>>       * struct drm_mode_get_connector - Get connector metadata.
>>>>>>>>> -- 
>>>>>>>>> 2.23.0
>>>>>>>>>
>>>>>>>>> _______________________________________________
>>>>>>>>> dri-devel mailing list
>>>>>>>>> dri-devel@lists.freedesktop.org
>>>>>>>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>> -- 
>>>>>>> Thomas Zimmermann
>>>>>>> Graphics Driver Developer
>>>>>>> SUSE Software Solutions Germany GmbH
>>>>>>> Maxfeldstr. 5, 90409 Nürnberg, Germany
>>>>>>> (HRB 36809, AG Nürnberg)
>>>>>>> Geschäftsführer: Felix Imendörffer
>>>>>>>
>>>>>>
>>>>>>
>>>>>
>>>>>
>>>>> _______________________________________________
>>>>> dri-devel mailing list
>>>>> dri-devel@lists.freedesktop.org
>>>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>>>>
>>>> _______________________________________________
>>>> dri-devel mailing list
>>>> dri-devel@lists.freedesktop.org
>>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>>>
>>>
> 

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 1/3] drm/uapi: Add USB connector type
@ 2021-01-22 14:35                     ` Noralf Trønnes
  0 siblings, 0 replies; 53+ messages in thread
From: Noralf Trønnes @ 2021-01-22 14:35 UTC (permalink / raw)
  To: Thomas Zimmermann, Daniel Vetter
  Cc: hudson, markus, peter, USB list, dri-devel, Tyler Hardin,
	Lubomir Rintel, pontus.fuchs, Sam Ravnborg



Den 22.01.2021 13.47, skrev Thomas Zimmermann:
> Hi
> 
> Am 22.01.21 um 12:44 schrieb Noralf Trønnes:
>>
>> And wrt PCI it wouldn't be a PCI connector if the card has some other
>> connector for the display, but if it was possible to connect a display
>> directly to the PCI connector, then yes I would call that a PCI
>> connector.
> 
> You're not connecting a display to the computer. You're connecting an
> RPi and then connect the display to the RPi. The RPi acts like an
> external graphics card.
> 
>> This begs the question: Why does the kernel provide info to userspace
>> about the connector type?
>>
>> My take is that it is so the user can know which display is connected to
>> which port on the computer.
> 
> This exactly illustrates the problem with the current naming. For a
> single output the distinction between bus and connector might be fuzzy.
> As soon as a connected SoC contains multiple connectors. The user then
> sees names such as card1-USB-0 and card1-USB-1, which makes no sense.
> 

If you look at the code I pasted in, you'll see that the SoC connector
types are passed through to the host driver as-is unless they are panel
connectors like DSI/DPI, which will be interpreted as USB (the protocol
does support multiple connectors, but only one can be used at a time).

So for the Pi4 as a display adapter, the host will see card1-HDMI-0 and
card1-HDMI-1, the same is true for the composite output (if enabled) it
shows up as card1-Composite-0 (can't be enabled together with HDMI).

If the Pi4 is used together with a DSI connected touchscreen, it makes
sense to disable the SoC HDMI outputs and the host only will see the
board as card1-USB-0 (I haven't done this exercise yet since there's
problems with getting the official Pi touchscreen to work with vc4 on Pi4).

The USB connector type is most important for tiny displays that is
microcontroller based without Linux running. There are lots of tiny SPI
displays and I expect this market to shift towards USB because it much
easier to connect and the display will be useable on a desktop/server
computer as status displays perhaps. But embedded will also benefit from
having these displays USB interfaced.

Another use case I see is repurposing old Android tablets as USB
displays that can be connected to any computer and become a touchscreen.
In this case I also want the connector to be called card1-USB-0 (I
haven't done any work in this area, old Android is fbdev so some work is
needed for this to happen).

Noralf.

>>
>> What's your opinion?
>>
>>>>
>>>> Ofc as Daniel mentions it's a downside that userspace doesn't know
>>>> about
>>>> the connector type, and who knows when it will updated (if I don't do
>>>> it).
>>>> Weston will name it: "UNNAMED-%d"
>>>> Mutter: "Unknown%d-%d"
>>>> X: "Unknown%d-%d"
>>>>
>>>> Sam and Laurent has discussed adding a PANEL connector type instead of
>>>> adding more connector types for panel connectors. I think that would
>>>> have been a better choice instead of the SPI connector type that I
>>>> added
>>>> in 2019. But I think PANEL was meant for panels connected to an
>>>> internal
>>>> connector.
>>>>
>>>> Here's my protocol connector types and how it's mapped to DRM:
>>>>
>>>> #define GUD_CONNECTOR_TYPE_PANEL        0
>>>> #define GUD_CONNECTOR_TYPE_VGA            1
>>>> #define GUD_CONNECTOR_TYPE_COMPOSITE        2
>>>> #define GUD_CONNECTOR_TYPE_SVIDEO        3
>>>> #define GUD_CONNECTOR_TYPE_COMPONENT        4
>>>> #define GUD_CONNECTOR_TYPE_DVI            5
>>>> #define GUD_CONNECTOR_TYPE_DISPLAYPORT        6
>>>> #define GUD_CONNECTOR_TYPE_HDMI            7
>>>>
>>>> static int gud_gadget_ctrl_get_connector(struct gud_gadget *gdg,
>>>> unsigned int index,
>>>>                       struct gud_connector_descriptor_req *desc)
>>>> {
>>>> ...
>>>>      gconn = &gdg->connectors[index];
>>>>
>>>>      switch (gconn->connector->connector_type) {
>>>>      case DRM_MODE_CONNECTOR_VGA:
>>>>          desc->connector_type = GUD_CONNECTOR_TYPE_VGA;
>>>>          break;
>>>>      case DRM_MODE_CONNECTOR_DVII:
>>>>          fallthrough;
>>>>      case DRM_MODE_CONNECTOR_DVID:
>>>>          fallthrough;
>>>>      case DRM_MODE_CONNECTOR_DVIA:
>>>>          desc->connector_type = GUD_CONNECTOR_TYPE_DVI;
>>>>          break;
>>>>      case DRM_MODE_CONNECTOR_Composite:
>>>>          desc->connector_type = GUD_CONNECTOR_TYPE_COMPOSITE;
>>>>          break;
>>>>      case DRM_MODE_CONNECTOR_SVIDEO:
>>>>          desc->connector_type = GUD_CONNECTOR_TYPE_SVIDEO;
>>>>          break;
>>>>      case DRM_MODE_CONNECTOR_Component:
>>>>          desc->connector_type = GUD_CONNECTOR_TYPE_COMPONENT;
>>>>          break;
>>>>      case DRM_MODE_CONNECTOR_DisplayPort:
>>>>          desc->connector_type = GUD_CONNECTOR_TYPE_DISPLAYPORT;
>>>>          break;
>>>>      case DRM_MODE_CONNECTOR_HDMIA:
>>>>          fallthrough;
>>>>      case DRM_MODE_CONNECTOR_HDMIB:
>>>>          desc->connector_type = GUD_CONNECTOR_TYPE_HDMI;
>>>>          break;
>>>>      default:
>>>>          desc->connector_type = GUD_CONNECTOR_TYPE_PANEL;
>>>>          break;
>>>>      };
>>>>
>>>>
>>>> int gud_connector_create(struct gud_device *gdrm, unsigned int index)
>>>> {
>>>> ...
>>>>      switch (desc.connector_type) {
>>>>      case GUD_CONNECTOR_TYPE_PANEL:
>>>>          connector_type = DRM_MODE_CONNECTOR_USB;
>>>>          break;
>>>>      case GUD_CONNECTOR_TYPE_VGA:
>>>>          connector_type = DRM_MODE_CONNECTOR_VGA;
>>>>          break;
>>>>      case GUD_CONNECTOR_TYPE_DVI:
>>>>          connector_type = DRM_MODE_CONNECTOR_DVID;
>>>>          break;
>>>>      case GUD_CONNECTOR_TYPE_COMPOSITE:
>>>>          connector_type = DRM_MODE_CONNECTOR_Composite;
>>>>          break;
>>>>      case GUD_CONNECTOR_TYPE_SVIDEO:
>>>>          connector_type = DRM_MODE_CONNECTOR_SVIDEO;
>>>>          break;
>>>>      case GUD_CONNECTOR_TYPE_COMPONENT:
>>>>          connector_type = DRM_MODE_CONNECTOR_Component;
>>>>          break;
>>>>      case GUD_CONNECTOR_TYPE_DISPLAYPORT:
>>>>          connector_type = DRM_MODE_CONNECTOR_DisplayPort;
>>>>          break;
>>>>      case GUD_CONNECTOR_TYPE_HDMI:
>>>>          connector_type = DRM_MODE_CONNECTOR_HDMIA;
>>>>          break;
>>>>      default: /* future types */
>>>>          connector_type = DRM_MODE_CONNECTOR_USB;
>>>
>>> The more I look at it the more I think it should be 'Unknown' here.
>>>
>>
>> I don't understand this, how will that be better for the user?
> 
> As I said before, the display is not connected via USB. The RPi (i.e.,
> graphics card) is. The naming would be off.
> 
> Best regards
> Thomas
> 
>>
>>> BTW, can I try this out somehow? I do have an RPi3. Do I need a special
>>> disk image?
>>
>> The Pi3 doesn'have a USB device/otg connector so I haven't made an image
>> for that one. Only the Pi Zero, model A and Pi 4 have that.
>>
>> The Pi2 and Pi3 have a USB hub on the soc's single USB port.
>>
>> Noralf.
>>
>>>
>>> Best regards
>>> Thomas
>>>
>>>>          break;
>>>>      };
>>>>
>>>> Noralf.
>>>>
>>>>> Best regards
>>>>> Thomas
>>>>>
>>>>>> -Daniel
>>>>>>
>>>>>>>
>>>>>>> Best regards
>>>>>>> Thomas
>>>>>>>
>>>>>>>>
>>>>>>>> Beware, new connector types have in the past resulted in userspace
>>>>>>>> burning&crashing. Maybe it's become better ...
>>>>>>>>
>>>>>>>> Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
>>>>>>>>>
>>>>>>>>>      /**
>>>>>>>>>       * struct drm_mode_get_connector - Get connector metadata.
>>>>>>>>> -- 
>>>>>>>>> 2.23.0
>>>>>>>>>
>>>>>>>>> _______________________________________________
>>>>>>>>> dri-devel mailing list
>>>>>>>>> dri-devel@lists.freedesktop.org
>>>>>>>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>> -- 
>>>>>>> Thomas Zimmermann
>>>>>>> Graphics Driver Developer
>>>>>>> SUSE Software Solutions Germany GmbH
>>>>>>> Maxfeldstr. 5, 90409 Nürnberg, Germany
>>>>>>> (HRB 36809, AG Nürnberg)
>>>>>>> Geschäftsführer: Felix Imendörffer
>>>>>>>
>>>>>>
>>>>>>
>>>>>
>>>>>
>>>>> _______________________________________________
>>>>> dri-devel mailing list
>>>>> dri-devel@lists.freedesktop.org
>>>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>>>>
>>>> _______________________________________________
>>>> dri-devel mailing list
>>>> dri-devel@lists.freedesktop.org
>>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>>>
>>>
> 
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 1/3] drm/uapi: Add USB connector type
  2021-01-22 14:35                     ` Noralf Trønnes
@ 2021-01-22 14:55                       ` Thomas Zimmermann
  -1 siblings, 0 replies; 53+ messages in thread
From: Thomas Zimmermann @ 2021-01-22 14:55 UTC (permalink / raw)
  To: Noralf Trønnes, Daniel Vetter
  Cc: hudson, markus, peter, USB list, dri-devel, Tyler Hardin,
	Lubomir Rintel, pontus.fuchs, Sam Ravnborg


[-- Attachment #1.1: Type: text/plain, Size: 10084 bytes --]

Hi

Am 22.01.21 um 15:35 schrieb Noralf Trønnes:
> 
> 
> Den 22.01.2021 13.47, skrev Thomas Zimmermann:
>> Hi
>>
>> Am 22.01.21 um 12:44 schrieb Noralf Trønnes:
>>>
>>> And wrt PCI it wouldn't be a PCI connector if the card has some other
>>> connector for the display, but if it was possible to connect a display
>>> directly to the PCI connector, then yes I would call that a PCI
>>> connector.
>>
>> You're not connecting a display to the computer. You're connecting an
>> RPi and then connect the display to the RPi. The RPi acts like an
>> external graphics card.
>>
>>> This begs the question: Why does the kernel provide info to userspace
>>> about the connector type?
>>>
>>> My take is that it is so the user can know which display is connected to
>>> which port on the computer.
>>
>> This exactly illustrates the problem with the current naming. For a
>> single output the distinction between bus and connector might be fuzzy.
>> As soon as a connected SoC contains multiple connectors. The user then
>> sees names such as card1-USB-0 and card1-USB-1, which makes no sense.
>>
> 
> If you look at the code I pasted in, you'll see that the SoC connector
> types are passed through to the host driver as-is unless they are panel
> connectors like DSI/DPI, which will be interpreted as USB (the protocol
> does support multiple connectors, but only one can be used at a time).
> 
> So for the Pi4 as a display adapter, the host will see card1-HDMI-0 and
> card1-HDMI-1, the same is true for the composite output (if enabled) it
> shows up as card1-Composite-0 (can't be enabled together with HDMI).
> 
> If the Pi4 is used together with a DSI connected touchscreen, it makes
> sense to disable the SoC HDMI outputs and the host only will see the
> board as card1-USB-0 (I haven't done this exercise yet since there's
> problems with getting the official Pi touchscreen to work with vc4 on Pi4).

I saw that. I can even get your point about using USB for panel (still 
don't agree). But you're also using USB as default case.

Best regards
Thomas

> 
> The USB connector type is most important for tiny displays that is
> microcontroller based without Linux running. There are lots of tiny SPI
> displays and I expect this market to shift towards USB because it much
> easier to connect and the display will be useable on a desktop/server
> computer as status displays perhaps. But embedded will also benefit from
> having these displays USB interfaced.
> 
> Another use case I see is repurposing old Android tablets as USB
> displays that can be connected to any computer and become a touchscreen.
> In this case I also want the connector to be called card1-USB-0 (I
> haven't done any work in this area, old Android is fbdev so some work is
> needed for this to happen).
> 
> Noralf.
> 
>>>
>>> What's your opinion?
>>>
>>>>>
>>>>> Ofc as Daniel mentions it's a downside that userspace doesn't know
>>>>> about
>>>>> the connector type, and who knows when it will updated (if I don't do
>>>>> it).
>>>>> Weston will name it: "UNNAMED-%d"
>>>>> Mutter: "Unknown%d-%d"
>>>>> X: "Unknown%d-%d"
>>>>>
>>>>> Sam and Laurent has discussed adding a PANEL connector type instead of
>>>>> adding more connector types for panel connectors. I think that would
>>>>> have been a better choice instead of the SPI connector type that I
>>>>> added
>>>>> in 2019. But I think PANEL was meant for panels connected to an
>>>>> internal
>>>>> connector.
>>>>>
>>>>> Here's my protocol connector types and how it's mapped to DRM:
>>>>>
>>>>> #define GUD_CONNECTOR_TYPE_PANEL        0
>>>>> #define GUD_CONNECTOR_TYPE_VGA            1
>>>>> #define GUD_CONNECTOR_TYPE_COMPOSITE        2
>>>>> #define GUD_CONNECTOR_TYPE_SVIDEO        3
>>>>> #define GUD_CONNECTOR_TYPE_COMPONENT        4
>>>>> #define GUD_CONNECTOR_TYPE_DVI            5
>>>>> #define GUD_CONNECTOR_TYPE_DISPLAYPORT        6
>>>>> #define GUD_CONNECTOR_TYPE_HDMI            7
>>>>>
>>>>> static int gud_gadget_ctrl_get_connector(struct gud_gadget *gdg,
>>>>> unsigned int index,
>>>>>                        struct gud_connector_descriptor_req *desc)
>>>>> {
>>>>> ...
>>>>>       gconn = &gdg->connectors[index];
>>>>>
>>>>>       switch (gconn->connector->connector_type) {
>>>>>       case DRM_MODE_CONNECTOR_VGA:
>>>>>           desc->connector_type = GUD_CONNECTOR_TYPE_VGA;
>>>>>           break;
>>>>>       case DRM_MODE_CONNECTOR_DVII:
>>>>>           fallthrough;
>>>>>       case DRM_MODE_CONNECTOR_DVID:
>>>>>           fallthrough;
>>>>>       case DRM_MODE_CONNECTOR_DVIA:
>>>>>           desc->connector_type = GUD_CONNECTOR_TYPE_DVI;
>>>>>           break;
>>>>>       case DRM_MODE_CONNECTOR_Composite:
>>>>>           desc->connector_type = GUD_CONNECTOR_TYPE_COMPOSITE;
>>>>>           break;
>>>>>       case DRM_MODE_CONNECTOR_SVIDEO:
>>>>>           desc->connector_type = GUD_CONNECTOR_TYPE_SVIDEO;
>>>>>           break;
>>>>>       case DRM_MODE_CONNECTOR_Component:
>>>>>           desc->connector_type = GUD_CONNECTOR_TYPE_COMPONENT;
>>>>>           break;
>>>>>       case DRM_MODE_CONNECTOR_DisplayPort:
>>>>>           desc->connector_type = GUD_CONNECTOR_TYPE_DISPLAYPORT;
>>>>>           break;
>>>>>       case DRM_MODE_CONNECTOR_HDMIA:
>>>>>           fallthrough;
>>>>>       case DRM_MODE_CONNECTOR_HDMIB:
>>>>>           desc->connector_type = GUD_CONNECTOR_TYPE_HDMI;
>>>>>           break;
>>>>>       default:
>>>>>           desc->connector_type = GUD_CONNECTOR_TYPE_PANEL;
>>>>>           break;
>>>>>       };
>>>>>
>>>>>
>>>>> int gud_connector_create(struct gud_device *gdrm, unsigned int index)
>>>>> {
>>>>> ...
>>>>>       switch (desc.connector_type) {
>>>>>       case GUD_CONNECTOR_TYPE_PANEL:
>>>>>           connector_type = DRM_MODE_CONNECTOR_USB;
>>>>>           break;
>>>>>       case GUD_CONNECTOR_TYPE_VGA:
>>>>>           connector_type = DRM_MODE_CONNECTOR_VGA;
>>>>>           break;
>>>>>       case GUD_CONNECTOR_TYPE_DVI:
>>>>>           connector_type = DRM_MODE_CONNECTOR_DVID;
>>>>>           break;
>>>>>       case GUD_CONNECTOR_TYPE_COMPOSITE:
>>>>>           connector_type = DRM_MODE_CONNECTOR_Composite;
>>>>>           break;
>>>>>       case GUD_CONNECTOR_TYPE_SVIDEO:
>>>>>           connector_type = DRM_MODE_CONNECTOR_SVIDEO;
>>>>>           break;
>>>>>       case GUD_CONNECTOR_TYPE_COMPONENT:
>>>>>           connector_type = DRM_MODE_CONNECTOR_Component;
>>>>>           break;
>>>>>       case GUD_CONNECTOR_TYPE_DISPLAYPORT:
>>>>>           connector_type = DRM_MODE_CONNECTOR_DisplayPort;
>>>>>           break;
>>>>>       case GUD_CONNECTOR_TYPE_HDMI:
>>>>>           connector_type = DRM_MODE_CONNECTOR_HDMIA;
>>>>>           break;
>>>>>       default: /* future types */
>>>>>           connector_type = DRM_MODE_CONNECTOR_USB;
>>>>
>>>> The more I look at it the more I think it should be 'Unknown' here.
>>>>
>>>
>>> I don't understand this, how will that be better for the user?
>>
>> As I said before, the display is not connected via USB. The RPi (i.e.,
>> graphics card) is. The naming would be off.
>>
>> Best regards
>> Thomas
>>
>>>
>>>> BTW, can I try this out somehow? I do have an RPi3. Do I need a special
>>>> disk image?
>>>
>>> The Pi3 doesn'have a USB device/otg connector so I haven't made an image
>>> for that one. Only the Pi Zero, model A and Pi 4 have that.
>>>
>>> The Pi2 and Pi3 have a USB hub on the soc's single USB port.
>>>
>>> Noralf.
>>>
>>>>
>>>> Best regards
>>>> Thomas
>>>>
>>>>>           break;
>>>>>       };
>>>>>
>>>>> Noralf.
>>>>>
>>>>>> Best regards
>>>>>> Thomas
>>>>>>
>>>>>>> -Daniel
>>>>>>>
>>>>>>>>
>>>>>>>> Best regards
>>>>>>>> Thomas
>>>>>>>>
>>>>>>>>>
>>>>>>>>> Beware, new connector types have in the past resulted in userspace
>>>>>>>>> burning&crashing. Maybe it's become better ...
>>>>>>>>>
>>>>>>>>> Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
>>>>>>>>>>
>>>>>>>>>>       /**
>>>>>>>>>>        * struct drm_mode_get_connector - Get connector metadata.
>>>>>>>>>> -- 
>>>>>>>>>> 2.23.0
>>>>>>>>>>
>>>>>>>>>> _______________________________________________
>>>>>>>>>> dri-devel mailing list
>>>>>>>>>> dri-devel@lists.freedesktop.org
>>>>>>>>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>
>>>>>>>> -- 
>>>>>>>> Thomas Zimmermann
>>>>>>>> Graphics Driver Developer
>>>>>>>> SUSE Software Solutions Germany GmbH
>>>>>>>> Maxfeldstr. 5, 90409 Nürnberg, Germany
>>>>>>>> (HRB 36809, AG Nürnberg)
>>>>>>>> Geschäftsführer: Felix Imendörffer
>>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>
>>>>>>
>>>>>> _______________________________________________
>>>>>> dri-devel mailing list
>>>>>> dri-devel@lists.freedesktop.org
>>>>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>>>>>
>>>>> _______________________________________________
>>>>> dri-devel mailing list
>>>>> dri-devel@lists.freedesktop.org
>>>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>>>>
>>>>
>>
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
> 

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Felix Imendörffer


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 1/3] drm/uapi: Add USB connector type
@ 2021-01-22 14:55                       ` Thomas Zimmermann
  0 siblings, 0 replies; 53+ messages in thread
From: Thomas Zimmermann @ 2021-01-22 14:55 UTC (permalink / raw)
  To: Noralf Trønnes, Daniel Vetter
  Cc: hudson, markus, Sam Ravnborg, USB list, dri-devel, Tyler Hardin,
	Lubomir Rintel, pontus.fuchs, peter


[-- Attachment #1.1.1: Type: text/plain, Size: 10084 bytes --]

Hi

Am 22.01.21 um 15:35 schrieb Noralf Trønnes:
> 
> 
> Den 22.01.2021 13.47, skrev Thomas Zimmermann:
>> Hi
>>
>> Am 22.01.21 um 12:44 schrieb Noralf Trønnes:
>>>
>>> And wrt PCI it wouldn't be a PCI connector if the card has some other
>>> connector for the display, but if it was possible to connect a display
>>> directly to the PCI connector, then yes I would call that a PCI
>>> connector.
>>
>> You're not connecting a display to the computer. You're connecting an
>> RPi and then connect the display to the RPi. The RPi acts like an
>> external graphics card.
>>
>>> This begs the question: Why does the kernel provide info to userspace
>>> about the connector type?
>>>
>>> My take is that it is so the user can know which display is connected to
>>> which port on the computer.
>>
>> This exactly illustrates the problem with the current naming. For a
>> single output the distinction between bus and connector might be fuzzy.
>> As soon as a connected SoC contains multiple connectors. The user then
>> sees names such as card1-USB-0 and card1-USB-1, which makes no sense.
>>
> 
> If you look at the code I pasted in, you'll see that the SoC connector
> types are passed through to the host driver as-is unless they are panel
> connectors like DSI/DPI, which will be interpreted as USB (the protocol
> does support multiple connectors, but only one can be used at a time).
> 
> So for the Pi4 as a display adapter, the host will see card1-HDMI-0 and
> card1-HDMI-1, the same is true for the composite output (if enabled) it
> shows up as card1-Composite-0 (can't be enabled together with HDMI).
> 
> If the Pi4 is used together with a DSI connected touchscreen, it makes
> sense to disable the SoC HDMI outputs and the host only will see the
> board as card1-USB-0 (I haven't done this exercise yet since there's
> problems with getting the official Pi touchscreen to work with vc4 on Pi4).

I saw that. I can even get your point about using USB for panel (still 
don't agree). But you're also using USB as default case.

Best regards
Thomas

> 
> The USB connector type is most important for tiny displays that is
> microcontroller based without Linux running. There are lots of tiny SPI
> displays and I expect this market to shift towards USB because it much
> easier to connect and the display will be useable on a desktop/server
> computer as status displays perhaps. But embedded will also benefit from
> having these displays USB interfaced.
> 
> Another use case I see is repurposing old Android tablets as USB
> displays that can be connected to any computer and become a touchscreen.
> In this case I also want the connector to be called card1-USB-0 (I
> haven't done any work in this area, old Android is fbdev so some work is
> needed for this to happen).
> 
> Noralf.
> 
>>>
>>> What's your opinion?
>>>
>>>>>
>>>>> Ofc as Daniel mentions it's a downside that userspace doesn't know
>>>>> about
>>>>> the connector type, and who knows when it will updated (if I don't do
>>>>> it).
>>>>> Weston will name it: "UNNAMED-%d"
>>>>> Mutter: "Unknown%d-%d"
>>>>> X: "Unknown%d-%d"
>>>>>
>>>>> Sam and Laurent has discussed adding a PANEL connector type instead of
>>>>> adding more connector types for panel connectors. I think that would
>>>>> have been a better choice instead of the SPI connector type that I
>>>>> added
>>>>> in 2019. But I think PANEL was meant for panels connected to an
>>>>> internal
>>>>> connector.
>>>>>
>>>>> Here's my protocol connector types and how it's mapped to DRM:
>>>>>
>>>>> #define GUD_CONNECTOR_TYPE_PANEL        0
>>>>> #define GUD_CONNECTOR_TYPE_VGA            1
>>>>> #define GUD_CONNECTOR_TYPE_COMPOSITE        2
>>>>> #define GUD_CONNECTOR_TYPE_SVIDEO        3
>>>>> #define GUD_CONNECTOR_TYPE_COMPONENT        4
>>>>> #define GUD_CONNECTOR_TYPE_DVI            5
>>>>> #define GUD_CONNECTOR_TYPE_DISPLAYPORT        6
>>>>> #define GUD_CONNECTOR_TYPE_HDMI            7
>>>>>
>>>>> static int gud_gadget_ctrl_get_connector(struct gud_gadget *gdg,
>>>>> unsigned int index,
>>>>>                        struct gud_connector_descriptor_req *desc)
>>>>> {
>>>>> ...
>>>>>       gconn = &gdg->connectors[index];
>>>>>
>>>>>       switch (gconn->connector->connector_type) {
>>>>>       case DRM_MODE_CONNECTOR_VGA:
>>>>>           desc->connector_type = GUD_CONNECTOR_TYPE_VGA;
>>>>>           break;
>>>>>       case DRM_MODE_CONNECTOR_DVII:
>>>>>           fallthrough;
>>>>>       case DRM_MODE_CONNECTOR_DVID:
>>>>>           fallthrough;
>>>>>       case DRM_MODE_CONNECTOR_DVIA:
>>>>>           desc->connector_type = GUD_CONNECTOR_TYPE_DVI;
>>>>>           break;
>>>>>       case DRM_MODE_CONNECTOR_Composite:
>>>>>           desc->connector_type = GUD_CONNECTOR_TYPE_COMPOSITE;
>>>>>           break;
>>>>>       case DRM_MODE_CONNECTOR_SVIDEO:
>>>>>           desc->connector_type = GUD_CONNECTOR_TYPE_SVIDEO;
>>>>>           break;
>>>>>       case DRM_MODE_CONNECTOR_Component:
>>>>>           desc->connector_type = GUD_CONNECTOR_TYPE_COMPONENT;
>>>>>           break;
>>>>>       case DRM_MODE_CONNECTOR_DisplayPort:
>>>>>           desc->connector_type = GUD_CONNECTOR_TYPE_DISPLAYPORT;
>>>>>           break;
>>>>>       case DRM_MODE_CONNECTOR_HDMIA:
>>>>>           fallthrough;
>>>>>       case DRM_MODE_CONNECTOR_HDMIB:
>>>>>           desc->connector_type = GUD_CONNECTOR_TYPE_HDMI;
>>>>>           break;
>>>>>       default:
>>>>>           desc->connector_type = GUD_CONNECTOR_TYPE_PANEL;
>>>>>           break;
>>>>>       };
>>>>>
>>>>>
>>>>> int gud_connector_create(struct gud_device *gdrm, unsigned int index)
>>>>> {
>>>>> ...
>>>>>       switch (desc.connector_type) {
>>>>>       case GUD_CONNECTOR_TYPE_PANEL:
>>>>>           connector_type = DRM_MODE_CONNECTOR_USB;
>>>>>           break;
>>>>>       case GUD_CONNECTOR_TYPE_VGA:
>>>>>           connector_type = DRM_MODE_CONNECTOR_VGA;
>>>>>           break;
>>>>>       case GUD_CONNECTOR_TYPE_DVI:
>>>>>           connector_type = DRM_MODE_CONNECTOR_DVID;
>>>>>           break;
>>>>>       case GUD_CONNECTOR_TYPE_COMPOSITE:
>>>>>           connector_type = DRM_MODE_CONNECTOR_Composite;
>>>>>           break;
>>>>>       case GUD_CONNECTOR_TYPE_SVIDEO:
>>>>>           connector_type = DRM_MODE_CONNECTOR_SVIDEO;
>>>>>           break;
>>>>>       case GUD_CONNECTOR_TYPE_COMPONENT:
>>>>>           connector_type = DRM_MODE_CONNECTOR_Component;
>>>>>           break;
>>>>>       case GUD_CONNECTOR_TYPE_DISPLAYPORT:
>>>>>           connector_type = DRM_MODE_CONNECTOR_DisplayPort;
>>>>>           break;
>>>>>       case GUD_CONNECTOR_TYPE_HDMI:
>>>>>           connector_type = DRM_MODE_CONNECTOR_HDMIA;
>>>>>           break;
>>>>>       default: /* future types */
>>>>>           connector_type = DRM_MODE_CONNECTOR_USB;
>>>>
>>>> The more I look at it the more I think it should be 'Unknown' here.
>>>>
>>>
>>> I don't understand this, how will that be better for the user?
>>
>> As I said before, the display is not connected via USB. The RPi (i.e.,
>> graphics card) is. The naming would be off.
>>
>> Best regards
>> Thomas
>>
>>>
>>>> BTW, can I try this out somehow? I do have an RPi3. Do I need a special
>>>> disk image?
>>>
>>> The Pi3 doesn'have a USB device/otg connector so I haven't made an image
>>> for that one. Only the Pi Zero, model A and Pi 4 have that.
>>>
>>> The Pi2 and Pi3 have a USB hub on the soc's single USB port.
>>>
>>> Noralf.
>>>
>>>>
>>>> Best regards
>>>> Thomas
>>>>
>>>>>           break;
>>>>>       };
>>>>>
>>>>> Noralf.
>>>>>
>>>>>> Best regards
>>>>>> Thomas
>>>>>>
>>>>>>> -Daniel
>>>>>>>
>>>>>>>>
>>>>>>>> Best regards
>>>>>>>> Thomas
>>>>>>>>
>>>>>>>>>
>>>>>>>>> Beware, new connector types have in the past resulted in userspace
>>>>>>>>> burning&crashing. Maybe it's become better ...
>>>>>>>>>
>>>>>>>>> Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
>>>>>>>>>>
>>>>>>>>>>       /**
>>>>>>>>>>        * struct drm_mode_get_connector - Get connector metadata.
>>>>>>>>>> -- 
>>>>>>>>>> 2.23.0
>>>>>>>>>>
>>>>>>>>>> _______________________________________________
>>>>>>>>>> dri-devel mailing list
>>>>>>>>>> dri-devel@lists.freedesktop.org
>>>>>>>>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>
>>>>>>>> -- 
>>>>>>>> Thomas Zimmermann
>>>>>>>> Graphics Driver Developer
>>>>>>>> SUSE Software Solutions Germany GmbH
>>>>>>>> Maxfeldstr. 5, 90409 Nürnberg, Germany
>>>>>>>> (HRB 36809, AG Nürnberg)
>>>>>>>> Geschäftsführer: Felix Imendörffer
>>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>
>>>>>>
>>>>>> _______________________________________________
>>>>>> dri-devel mailing list
>>>>>> dri-devel@lists.freedesktop.org
>>>>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>>>>>
>>>>> _______________________________________________
>>>>> dri-devel mailing list
>>>>> dri-devel@lists.freedesktop.org
>>>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>>>>
>>>>
>>
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
> 

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Felix Imendörffer


[-- Attachment #1.2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

[-- Attachment #2: Type: text/plain, Size: 160 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 1/3] drm/uapi: Add USB connector type
  2021-01-22 14:55                       ` Thomas Zimmermann
@ 2021-01-22 18:39                         ` Noralf Trønnes
  -1 siblings, 0 replies; 53+ messages in thread
From: Noralf Trønnes @ 2021-01-22 18:39 UTC (permalink / raw)
  To: Thomas Zimmermann, Daniel Vetter
  Cc: hudson, markus, peter, USB list, dri-devel, Tyler Hardin,
	Lubomir Rintel, pontus.fuchs, Sam Ravnborg



Den 22.01.2021 15.55, skrev Thomas Zimmermann:
> Hi
> 
> Am 22.01.21 um 15:35 schrieb Noralf Trønnes:
>>
>>
>> Den 22.01.2021 13.47, skrev Thomas Zimmermann:
>>> Hi
>>>
>>> Am 22.01.21 um 12:44 schrieb Noralf Trønnes:
>>>>
>>>> And wrt PCI it wouldn't be a PCI connector if the card has some other
>>>> connector for the display, but if it was possible to connect a display
>>>> directly to the PCI connector, then yes I would call that a PCI
>>>> connector.
>>>
>>> You're not connecting a display to the computer. You're connecting an
>>> RPi and then connect the display to the RPi. The RPi acts like an
>>> external graphics card.
>>>
>>>> This begs the question: Why does the kernel provide info to userspace
>>>> about the connector type?
>>>>
>>>> My take is that it is so the user can know which display is
>>>> connected to
>>>> which port on the computer.
>>>
>>> This exactly illustrates the problem with the current naming. For a
>>> single output the distinction between bus and connector might be fuzzy.
>>> As soon as a connected SoC contains multiple connectors. The user then
>>> sees names such as card1-USB-0 and card1-USB-1, which makes no sense.
>>>
>>
>> If you look at the code I pasted in, you'll see that the SoC connector
>> types are passed through to the host driver as-is unless they are panel
>> connectors like DSI/DPI, which will be interpreted as USB (the protocol
>> does support multiple connectors, but only one can be used at a time).
>>
>> So for the Pi4 as a display adapter, the host will see card1-HDMI-0 and
>> card1-HDMI-1, the same is true for the composite output (if enabled) it
>> shows up as card1-Composite-0 (can't be enabled together with HDMI).
>>
>> If the Pi4 is used together with a DSI connected touchscreen, it makes
>> sense to disable the SoC HDMI outputs and the host only will see the
>> board as card1-USB-0 (I haven't done this exercise yet since there's
>> problems with getting the official Pi touchscreen to work with vc4 on
>> Pi4).
> 
> I saw that. I can even get your point about using USB for panel (still
> don't agree). But you're also using USB as default case.
> 

In the host driver the default case is to catch future additions to the
protocol. In this case the driver doesn't know the connector type, but
by using USB at least the user knows where to look.

On the gadget side these are the current types that will hit the default
case:

These are panels:
DRM_MODE_CONNECTOR_LVDS
DRM_MODE_CONNECTOR_eDP
DRM_MODE_CONNECTOR_DSI
DRM_MODE_CONNECTOR_DPI
DRM_MODE_CONNECTOR_SPI

This also seems to be used primarily by panels:
DRM_MODE_CONNECTOR_Unknown

Only used by i915, amd, radeon, very unlikely on a gadget:
DRM_MODE_CONNECTOR_9PinDIN

This one I should probably map to GUD_CONNECTOR_TYPE_COMPOSITE:
DRM_MODE_CONNECTOR_TV

Very unlikely on a gadget:
DRM_MODE_CONNECTOR_VIRTUAL

Future connector types will also be reported in the protocol as
GUD_CONNECTOR_TYPE_PANEL and thus on the host as DRM_MODE_CONNECTOR_USB.
The gadget driver will ofc be updated when new non-panel connector types
show up.

And this makes sense to me :-)

Giving the user a connector named "Unknown-1" does not make sense to me.

Noralf.


> Best regards
> Thomas
> 
>>
>> The USB connector type is most important for tiny displays that is
>> microcontroller based without Linux running. There are lots of tiny SPI
>> displays and I expect this market to shift towards USB because it much
>> easier to connect and the display will be useable on a desktop/server
>> computer as status displays perhaps. But embedded will also benefit from
>> having these displays USB interfaced.
>>
>> Another use case I see is repurposing old Android tablets as USB
>> displays that can be connected to any computer and become a touchscreen.
>> In this case I also want the connector to be called card1-USB-0 (I
>> haven't done any work in this area, old Android is fbdev so some work is
>> needed for this to happen).
>>
>> Noralf.
>>
>>>>
>>>> What's your opinion?
>>>>
>>>>>>
>>>>>> Ofc as Daniel mentions it's a downside that userspace doesn't know
>>>>>> about
>>>>>> the connector type, and who knows when it will updated (if I don't do
>>>>>> it).
>>>>>> Weston will name it: "UNNAMED-%d"
>>>>>> Mutter: "Unknown%d-%d"
>>>>>> X: "Unknown%d-%d"
>>>>>>
>>>>>> Sam and Laurent has discussed adding a PANEL connector type
>>>>>> instead of
>>>>>> adding more connector types for panel connectors. I think that would
>>>>>> have been a better choice instead of the SPI connector type that I
>>>>>> added
>>>>>> in 2019. But I think PANEL was meant for panels connected to an
>>>>>> internal
>>>>>> connector.
>>>>>>
>>>>>> Here's my protocol connector types and how it's mapped to DRM:
>>>>>>
>>>>>> #define GUD_CONNECTOR_TYPE_PANEL        0
>>>>>> #define GUD_CONNECTOR_TYPE_VGA            1
>>>>>> #define GUD_CONNECTOR_TYPE_COMPOSITE        2
>>>>>> #define GUD_CONNECTOR_TYPE_SVIDEO        3
>>>>>> #define GUD_CONNECTOR_TYPE_COMPONENT        4
>>>>>> #define GUD_CONNECTOR_TYPE_DVI            5
>>>>>> #define GUD_CONNECTOR_TYPE_DISPLAYPORT        6
>>>>>> #define GUD_CONNECTOR_TYPE_HDMI            7
>>>>>>
>>>>>> static int gud_gadget_ctrl_get_connector(struct gud_gadget *gdg,
>>>>>> unsigned int index,
>>>>>>                        struct gud_connector_descriptor_req *desc)
>>>>>> {
>>>>>> ...
>>>>>>       gconn = &gdg->connectors[index];
>>>>>>
>>>>>>       switch (gconn->connector->connector_type) {
>>>>>>       case DRM_MODE_CONNECTOR_VGA:
>>>>>>           desc->connector_type = GUD_CONNECTOR_TYPE_VGA;
>>>>>>           break;
>>>>>>       case DRM_MODE_CONNECTOR_DVII:
>>>>>>           fallthrough;
>>>>>>       case DRM_MODE_CONNECTOR_DVID:
>>>>>>           fallthrough;
>>>>>>       case DRM_MODE_CONNECTOR_DVIA:
>>>>>>           desc->connector_type = GUD_CONNECTOR_TYPE_DVI;
>>>>>>           break;
>>>>>>       case DRM_MODE_CONNECTOR_Composite:
>>>>>>           desc->connector_type = GUD_CONNECTOR_TYPE_COMPOSITE;
>>>>>>           break;
>>>>>>       case DRM_MODE_CONNECTOR_SVIDEO:
>>>>>>           desc->connector_type = GUD_CONNECTOR_TYPE_SVIDEO;
>>>>>>           break;
>>>>>>       case DRM_MODE_CONNECTOR_Component:
>>>>>>           desc->connector_type = GUD_CONNECTOR_TYPE_COMPONENT;
>>>>>>           break;
>>>>>>       case DRM_MODE_CONNECTOR_DisplayPort:
>>>>>>           desc->connector_type = GUD_CONNECTOR_TYPE_DISPLAYPORT;
>>>>>>           break;
>>>>>>       case DRM_MODE_CONNECTOR_HDMIA:
>>>>>>           fallthrough;
>>>>>>       case DRM_MODE_CONNECTOR_HDMIB:
>>>>>>           desc->connector_type = GUD_CONNECTOR_TYPE_HDMI;
>>>>>>           break;
>>>>>>       default:
>>>>>>           desc->connector_type = GUD_CONNECTOR_TYPE_PANEL;
>>>>>>           break;
>>>>>>       };
>>>>>>
>>>>>>
>>>>>> int gud_connector_create(struct gud_device *gdrm, unsigned int index)
>>>>>> {
>>>>>> ...
>>>>>>       switch (desc.connector_type) {
>>>>>>       case GUD_CONNECTOR_TYPE_PANEL:
>>>>>>           connector_type = DRM_MODE_CONNECTOR_USB;
>>>>>>           break;
>>>>>>       case GUD_CONNECTOR_TYPE_VGA:
>>>>>>           connector_type = DRM_MODE_CONNECTOR_VGA;
>>>>>>           break;
>>>>>>       case GUD_CONNECTOR_TYPE_DVI:
>>>>>>           connector_type = DRM_MODE_CONNECTOR_DVID;
>>>>>>           break;
>>>>>>       case GUD_CONNECTOR_TYPE_COMPOSITE:
>>>>>>           connector_type = DRM_MODE_CONNECTOR_Composite;
>>>>>>           break;
>>>>>>       case GUD_CONNECTOR_TYPE_SVIDEO:
>>>>>>           connector_type = DRM_MODE_CONNECTOR_SVIDEO;
>>>>>>           break;
>>>>>>       case GUD_CONNECTOR_TYPE_COMPONENT:
>>>>>>           connector_type = DRM_MODE_CONNECTOR_Component;
>>>>>>           break;
>>>>>>       case GUD_CONNECTOR_TYPE_DISPLAYPORT:
>>>>>>           connector_type = DRM_MODE_CONNECTOR_DisplayPort;
>>>>>>           break;
>>>>>>       case GUD_CONNECTOR_TYPE_HDMI:
>>>>>>           connector_type = DRM_MODE_CONNECTOR_HDMIA;
>>>>>>           break;
>>>>>>       default: /* future types */
>>>>>>           connector_type = DRM_MODE_CONNECTOR_USB;
>>>>>
>>>>> The more I look at it the more I think it should be 'Unknown' here.
>>>>>
>>>>
>>>> I don't understand this, how will that be better for the user?
>>>
>>> As I said before, the display is not connected via USB. The RPi (i.e.,
>>> graphics card) is. The naming would be off.
>>>
>>> Best regards
>>> Thomas
>>>
>>>>
>>>>> BTW, can I try this out somehow? I do have an RPi3. Do I need a
>>>>> special
>>>>> disk image?
>>>>
>>>> The Pi3 doesn'have a USB device/otg connector so I haven't made an
>>>> image
>>>> for that one. Only the Pi Zero, model A and Pi 4 have that.
>>>>
>>>> The Pi2 and Pi3 have a USB hub on the soc's single USB port.
>>>>
>>>> Noralf.
>>>>
>>>>>
>>>>> Best regards
>>>>> Thomas
>>>>>
>>>>>>           break;
>>>>>>       };
>>>>>>
>>>>>> Noralf.
>>>>>>
>>>>>>> Best regards
>>>>>>> Thomas
>>>>>>>
>>>>>>>> -Daniel
>>>>>>>>
>>>>>>>>>
>>>>>>>>> Best regards
>>>>>>>>> Thomas
>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> Beware, new connector types have in the past resulted in
>>>>>>>>>> userspace
>>>>>>>>>> burning&crashing. Maybe it's become better ...
>>>>>>>>>>
>>>>>>>>>> Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
>>>>>>>>>>>
>>>>>>>>>>>       /**
>>>>>>>>>>>        * struct drm_mode_get_connector - Get connector metadata.
>>>>>>>>>>> -- 
>>>>>>>>>>> 2.23.0
>>>>>>>>>>>
>>>>>>>>>>> _______________________________________________
>>>>>>>>>>> dri-devel mailing list
>>>>>>>>>>> dri-devel@lists.freedesktop.org
>>>>>>>>>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>> -- 
>>>>>>>>> Thomas Zimmermann
>>>>>>>>> Graphics Driver Developer
>>>>>>>>> SUSE Software Solutions Germany GmbH
>>>>>>>>> Maxfeldstr. 5, 90409 Nürnberg, Germany
>>>>>>>>> (HRB 36809, AG Nürnberg)
>>>>>>>>> Geschäftsführer: Felix Imendörffer
>>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> _______________________________________________
>>>>>>> dri-devel mailing list
>>>>>>> dri-devel@lists.freedesktop.org
>>>>>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>>>>>>
>>>>>> _______________________________________________
>>>>>> dri-devel mailing list
>>>>>> dri-devel@lists.freedesktop.org
>>>>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>>>>>
>>>>>
>>>
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel@lists.freedesktop.org
>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>
> 

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 1/3] drm/uapi: Add USB connector type
@ 2021-01-22 18:39                         ` Noralf Trønnes
  0 siblings, 0 replies; 53+ messages in thread
From: Noralf Trønnes @ 2021-01-22 18:39 UTC (permalink / raw)
  To: Thomas Zimmermann, Daniel Vetter
  Cc: hudson, markus, Sam Ravnborg, USB list, dri-devel, Tyler Hardin,
	Lubomir Rintel, pontus.fuchs, peter



Den 22.01.2021 15.55, skrev Thomas Zimmermann:
> Hi
> 
> Am 22.01.21 um 15:35 schrieb Noralf Trønnes:
>>
>>
>> Den 22.01.2021 13.47, skrev Thomas Zimmermann:
>>> Hi
>>>
>>> Am 22.01.21 um 12:44 schrieb Noralf Trønnes:
>>>>
>>>> And wrt PCI it wouldn't be a PCI connector if the card has some other
>>>> connector for the display, but if it was possible to connect a display
>>>> directly to the PCI connector, then yes I would call that a PCI
>>>> connector.
>>>
>>> You're not connecting a display to the computer. You're connecting an
>>> RPi and then connect the display to the RPi. The RPi acts like an
>>> external graphics card.
>>>
>>>> This begs the question: Why does the kernel provide info to userspace
>>>> about the connector type?
>>>>
>>>> My take is that it is so the user can know which display is
>>>> connected to
>>>> which port on the computer.
>>>
>>> This exactly illustrates the problem with the current naming. For a
>>> single output the distinction between bus and connector might be fuzzy.
>>> As soon as a connected SoC contains multiple connectors. The user then
>>> sees names such as card1-USB-0 and card1-USB-1, which makes no sense.
>>>
>>
>> If you look at the code I pasted in, you'll see that the SoC connector
>> types are passed through to the host driver as-is unless they are panel
>> connectors like DSI/DPI, which will be interpreted as USB (the protocol
>> does support multiple connectors, but only one can be used at a time).
>>
>> So for the Pi4 as a display adapter, the host will see card1-HDMI-0 and
>> card1-HDMI-1, the same is true for the composite output (if enabled) it
>> shows up as card1-Composite-0 (can't be enabled together with HDMI).
>>
>> If the Pi4 is used together with a DSI connected touchscreen, it makes
>> sense to disable the SoC HDMI outputs and the host only will see the
>> board as card1-USB-0 (I haven't done this exercise yet since there's
>> problems with getting the official Pi touchscreen to work with vc4 on
>> Pi4).
> 
> I saw that. I can even get your point about using USB for panel (still
> don't agree). But you're also using USB as default case.
> 

In the host driver the default case is to catch future additions to the
protocol. In this case the driver doesn't know the connector type, but
by using USB at least the user knows where to look.

On the gadget side these are the current types that will hit the default
case:

These are panels:
DRM_MODE_CONNECTOR_LVDS
DRM_MODE_CONNECTOR_eDP
DRM_MODE_CONNECTOR_DSI
DRM_MODE_CONNECTOR_DPI
DRM_MODE_CONNECTOR_SPI

This also seems to be used primarily by panels:
DRM_MODE_CONNECTOR_Unknown

Only used by i915, amd, radeon, very unlikely on a gadget:
DRM_MODE_CONNECTOR_9PinDIN

This one I should probably map to GUD_CONNECTOR_TYPE_COMPOSITE:
DRM_MODE_CONNECTOR_TV

Very unlikely on a gadget:
DRM_MODE_CONNECTOR_VIRTUAL

Future connector types will also be reported in the protocol as
GUD_CONNECTOR_TYPE_PANEL and thus on the host as DRM_MODE_CONNECTOR_USB.
The gadget driver will ofc be updated when new non-panel connector types
show up.

And this makes sense to me :-)

Giving the user a connector named "Unknown-1" does not make sense to me.

Noralf.


> Best regards
> Thomas
> 
>>
>> The USB connector type is most important for tiny displays that is
>> microcontroller based without Linux running. There are lots of tiny SPI
>> displays and I expect this market to shift towards USB because it much
>> easier to connect and the display will be useable on a desktop/server
>> computer as status displays perhaps. But embedded will also benefit from
>> having these displays USB interfaced.
>>
>> Another use case I see is repurposing old Android tablets as USB
>> displays that can be connected to any computer and become a touchscreen.
>> In this case I also want the connector to be called card1-USB-0 (I
>> haven't done any work in this area, old Android is fbdev so some work is
>> needed for this to happen).
>>
>> Noralf.
>>
>>>>
>>>> What's your opinion?
>>>>
>>>>>>
>>>>>> Ofc as Daniel mentions it's a downside that userspace doesn't know
>>>>>> about
>>>>>> the connector type, and who knows when it will updated (if I don't do
>>>>>> it).
>>>>>> Weston will name it: "UNNAMED-%d"
>>>>>> Mutter: "Unknown%d-%d"
>>>>>> X: "Unknown%d-%d"
>>>>>>
>>>>>> Sam and Laurent has discussed adding a PANEL connector type
>>>>>> instead of
>>>>>> adding more connector types for panel connectors. I think that would
>>>>>> have been a better choice instead of the SPI connector type that I
>>>>>> added
>>>>>> in 2019. But I think PANEL was meant for panels connected to an
>>>>>> internal
>>>>>> connector.
>>>>>>
>>>>>> Here's my protocol connector types and how it's mapped to DRM:
>>>>>>
>>>>>> #define GUD_CONNECTOR_TYPE_PANEL        0
>>>>>> #define GUD_CONNECTOR_TYPE_VGA            1
>>>>>> #define GUD_CONNECTOR_TYPE_COMPOSITE        2
>>>>>> #define GUD_CONNECTOR_TYPE_SVIDEO        3
>>>>>> #define GUD_CONNECTOR_TYPE_COMPONENT        4
>>>>>> #define GUD_CONNECTOR_TYPE_DVI            5
>>>>>> #define GUD_CONNECTOR_TYPE_DISPLAYPORT        6
>>>>>> #define GUD_CONNECTOR_TYPE_HDMI            7
>>>>>>
>>>>>> static int gud_gadget_ctrl_get_connector(struct gud_gadget *gdg,
>>>>>> unsigned int index,
>>>>>>                        struct gud_connector_descriptor_req *desc)
>>>>>> {
>>>>>> ...
>>>>>>       gconn = &gdg->connectors[index];
>>>>>>
>>>>>>       switch (gconn->connector->connector_type) {
>>>>>>       case DRM_MODE_CONNECTOR_VGA:
>>>>>>           desc->connector_type = GUD_CONNECTOR_TYPE_VGA;
>>>>>>           break;
>>>>>>       case DRM_MODE_CONNECTOR_DVII:
>>>>>>           fallthrough;
>>>>>>       case DRM_MODE_CONNECTOR_DVID:
>>>>>>           fallthrough;
>>>>>>       case DRM_MODE_CONNECTOR_DVIA:
>>>>>>           desc->connector_type = GUD_CONNECTOR_TYPE_DVI;
>>>>>>           break;
>>>>>>       case DRM_MODE_CONNECTOR_Composite:
>>>>>>           desc->connector_type = GUD_CONNECTOR_TYPE_COMPOSITE;
>>>>>>           break;
>>>>>>       case DRM_MODE_CONNECTOR_SVIDEO:
>>>>>>           desc->connector_type = GUD_CONNECTOR_TYPE_SVIDEO;
>>>>>>           break;
>>>>>>       case DRM_MODE_CONNECTOR_Component:
>>>>>>           desc->connector_type = GUD_CONNECTOR_TYPE_COMPONENT;
>>>>>>           break;
>>>>>>       case DRM_MODE_CONNECTOR_DisplayPort:
>>>>>>           desc->connector_type = GUD_CONNECTOR_TYPE_DISPLAYPORT;
>>>>>>           break;
>>>>>>       case DRM_MODE_CONNECTOR_HDMIA:
>>>>>>           fallthrough;
>>>>>>       case DRM_MODE_CONNECTOR_HDMIB:
>>>>>>           desc->connector_type = GUD_CONNECTOR_TYPE_HDMI;
>>>>>>           break;
>>>>>>       default:
>>>>>>           desc->connector_type = GUD_CONNECTOR_TYPE_PANEL;
>>>>>>           break;
>>>>>>       };
>>>>>>
>>>>>>
>>>>>> int gud_connector_create(struct gud_device *gdrm, unsigned int index)
>>>>>> {
>>>>>> ...
>>>>>>       switch (desc.connector_type) {
>>>>>>       case GUD_CONNECTOR_TYPE_PANEL:
>>>>>>           connector_type = DRM_MODE_CONNECTOR_USB;
>>>>>>           break;
>>>>>>       case GUD_CONNECTOR_TYPE_VGA:
>>>>>>           connector_type = DRM_MODE_CONNECTOR_VGA;
>>>>>>           break;
>>>>>>       case GUD_CONNECTOR_TYPE_DVI:
>>>>>>           connector_type = DRM_MODE_CONNECTOR_DVID;
>>>>>>           break;
>>>>>>       case GUD_CONNECTOR_TYPE_COMPOSITE:
>>>>>>           connector_type = DRM_MODE_CONNECTOR_Composite;
>>>>>>           break;
>>>>>>       case GUD_CONNECTOR_TYPE_SVIDEO:
>>>>>>           connector_type = DRM_MODE_CONNECTOR_SVIDEO;
>>>>>>           break;
>>>>>>       case GUD_CONNECTOR_TYPE_COMPONENT:
>>>>>>           connector_type = DRM_MODE_CONNECTOR_Component;
>>>>>>           break;
>>>>>>       case GUD_CONNECTOR_TYPE_DISPLAYPORT:
>>>>>>           connector_type = DRM_MODE_CONNECTOR_DisplayPort;
>>>>>>           break;
>>>>>>       case GUD_CONNECTOR_TYPE_HDMI:
>>>>>>           connector_type = DRM_MODE_CONNECTOR_HDMIA;
>>>>>>           break;
>>>>>>       default: /* future types */
>>>>>>           connector_type = DRM_MODE_CONNECTOR_USB;
>>>>>
>>>>> The more I look at it the more I think it should be 'Unknown' here.
>>>>>
>>>>
>>>> I don't understand this, how will that be better for the user?
>>>
>>> As I said before, the display is not connected via USB. The RPi (i.e.,
>>> graphics card) is. The naming would be off.
>>>
>>> Best regards
>>> Thomas
>>>
>>>>
>>>>> BTW, can I try this out somehow? I do have an RPi3. Do I need a
>>>>> special
>>>>> disk image?
>>>>
>>>> The Pi3 doesn'have a USB device/otg connector so I haven't made an
>>>> image
>>>> for that one. Only the Pi Zero, model A and Pi 4 have that.
>>>>
>>>> The Pi2 and Pi3 have a USB hub on the soc's single USB port.
>>>>
>>>> Noralf.
>>>>
>>>>>
>>>>> Best regards
>>>>> Thomas
>>>>>
>>>>>>           break;
>>>>>>       };
>>>>>>
>>>>>> Noralf.
>>>>>>
>>>>>>> Best regards
>>>>>>> Thomas
>>>>>>>
>>>>>>>> -Daniel
>>>>>>>>
>>>>>>>>>
>>>>>>>>> Best regards
>>>>>>>>> Thomas
>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> Beware, new connector types have in the past resulted in
>>>>>>>>>> userspace
>>>>>>>>>> burning&crashing. Maybe it's become better ...
>>>>>>>>>>
>>>>>>>>>> Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
>>>>>>>>>>>
>>>>>>>>>>>       /**
>>>>>>>>>>>        * struct drm_mode_get_connector - Get connector metadata.
>>>>>>>>>>> -- 
>>>>>>>>>>> 2.23.0
>>>>>>>>>>>
>>>>>>>>>>> _______________________________________________
>>>>>>>>>>> dri-devel mailing list
>>>>>>>>>>> dri-devel@lists.freedesktop.org
>>>>>>>>>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>> -- 
>>>>>>>>> Thomas Zimmermann
>>>>>>>>> Graphics Driver Developer
>>>>>>>>> SUSE Software Solutions Germany GmbH
>>>>>>>>> Maxfeldstr. 5, 90409 Nürnberg, Germany
>>>>>>>>> (HRB 36809, AG Nürnberg)
>>>>>>>>> Geschäftsführer: Felix Imendörffer
>>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> _______________________________________________
>>>>>>> dri-devel mailing list
>>>>>>> dri-devel@lists.freedesktop.org
>>>>>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>>>>>>
>>>>>> _______________________________________________
>>>>>> dri-devel mailing list
>>>>>> dri-devel@lists.freedesktop.org
>>>>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>>>>>
>>>>>
>>>
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel@lists.freedesktop.org
>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>
> 
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 0/3] Generic USB Display driver
  2021-01-21 10:20     ` Simon Ser
@ 2021-01-23 11:46       ` Noralf Trønnes
  -1 siblings, 0 replies; 53+ messages in thread
From: Noralf Trønnes @ 2021-01-23 11:46 UTC (permalink / raw)
  To: Simon Ser, Thomas Zimmermann
  Cc: dri-devel, hudson, markus, peter, linux-usb, th020394, lkundrak,
	pontus.fuchs, sam



Den 21.01.2021 11.20, skrev Simon Ser:
> On Thursday, January 21st, 2021 at 10:59 AM, Thomas Zimmermann <tzimmermann@suse.de> wrote:
> 
>> Well, I'd strongly ask to not call it "generic", because it isn't. We
>> have other USB drivers and anyone can make a USB display with these
>> protocols as well. That doesn't make them generic. A USB-standardized
>> protocol would be generic. Maybe call it custom, or home-made.

I agree that Generic probably isn't the best term to use here. Naming
stuff is hard. Maybe: Open USB Display Protocol - oudp?

> 
> Maybe rename it to "GUD USB Display driver"? :P
> 

Oh that's a recursive acronym :-) That would save me all the work of
renaming. I think I'll go with that, thanks.

Noralf.

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 0/3] Generic USB Display driver
@ 2021-01-23 11:46       ` Noralf Trønnes
  0 siblings, 0 replies; 53+ messages in thread
From: Noralf Trønnes @ 2021-01-23 11:46 UTC (permalink / raw)
  To: Simon Ser, Thomas Zimmermann
  Cc: hudson, markus, sam, linux-usb, dri-devel, th020394, lkundrak,
	pontus.fuchs, peter



Den 21.01.2021 11.20, skrev Simon Ser:
> On Thursday, January 21st, 2021 at 10:59 AM, Thomas Zimmermann <tzimmermann@suse.de> wrote:
> 
>> Well, I'd strongly ask to not call it "generic", because it isn't. We
>> have other USB drivers and anyone can make a USB display with these
>> protocols as well. That doesn't make them generic. A USB-standardized
>> protocol would be generic. Maybe call it custom, or home-made.

I agree that Generic probably isn't the best term to use here. Naming
stuff is hard. Maybe: Open USB Display Protocol - oudp?

> 
> Maybe rename it to "GUD USB Display driver"? :P
> 

Oh that's a recursive acronym :-) That would save me all the work of
renaming. I think I'll go with that, thanks.

Noralf.
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 0/3] Generic USB Display driver
  2021-01-20 17:00 ` Noralf Trønnes
@ 2021-01-23 18:47   ` Lubomir Rintel
  -1 siblings, 0 replies; 53+ messages in thread
From: Lubomir Rintel @ 2021-01-23 18:47 UTC (permalink / raw)
  To: Noralf Trønnes
  Cc: dri-devel, linux-usb, sam, peter, markus, pontus.fuchs, hudson, th020394

On Wed, Jan 20, 2021 at 06:00:30PM +0100, Noralf Trønnes wrote:
> Hi,
> 
> A while back I had the idea to turn a Raspberry Pi Zero into a $5
> USB to HDMI/SDTV/DSI/DPI display adapter.
> 
> The reason for calling it 'Generic' is so anyone can make a USB
> display/adapter against this driver, all that's needed is to add a USB
> vid:pid.
> 
> Unfortunately I've had some compounding health problems that have
> severally limited the time I can spend in front of a computer. For this
> reason I've decided to keep the gadget driver out-of-tree and focus on
> getting the host driver merged first.
> 
> See the wiki[1] for more information and images for the Raspberry Pi
> Zero/4.
> 
> One big change this time is that I've followed Peter Stuge's advice to
> not let DRM stuff leak into the USB protocol. This has made the protocol
> easier to understand just from reading the header file.

Welcome back! I'm happy to see this patch set back on track. I've now
started putting together something that would work with GUD on
Lilygo T-Display-GD32, but it blew up in my face immediately (I had
CONFIG_DEBUG_SPINLOCK turned on):

  [   42.064362] usb 1-1.2: new full-speed USB device number 6 using mv-ehci
  [   42.228048] usb 1-1.2: New USB device found, idVendor=1d50, idProduct=614d, bcdDevice= 1.00
  [   42.236812] usb 1-1.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
  [   42.254298] usb 1-1.2: Product: LCD Display Interface
  [   42.259771] usb 1-1.2: Manufacturer: Lubomir Rintel <lkundrak@v3.sk>
  [   42.266395] usb 1-1.2: SerialNumber: FFFFFFFF3736030061294139
  [   42.284771] BUG: spinlock bad magic on CPU#0, kworker/0:0/5
  [   42.290394] 8<--- cut here ---
  [   42.293415] Unable to handle kernel NULL pointer dereference at virtual address 0000031c
  [   42.301417] pgd = e351f484
  [   42.304184] [0000031c] *pgd=00000000
  [   42.307826] Internal error: Oops: 15 [#1] PREEMPT SMP ARM
  [   42.313250] Modules linked in:
  [   42.316208] CPU: 0 PID: 5 Comm: kworker/0:0 Not tainted 5.11.0-rc4-00113-g8c3d05f1939a #1274
  [   42.324729] Hardware name: Marvell MMP3
  [   42.328540] Workqueue: usb_hub_wq hub_event
  [   42.332759] PC is at spin_dump+0x5c/0x88
  [   42.336556] LR is at spin_dump+0x48/0x88
  [   42.340644] pc : [<c0c3da6c>]    lr : [<c0c3da58>]    psr: 200f0193
  [   42.346895] sp : c810b910  ip : 00000000  fp : c1107b40
  [   42.352039] r10: ffffc005  r9 : 00000000  r8 : 001fffff
  [   42.357238] r7 : 00000000  r6 : 00000438  r5 : c14456f8  r4 : 00000014
  [   42.363837] r3 : 0000044c  r2 : c1091500  r1 : eefb8540  r0 : 0000002f
  [   42.370351] Flags: nzCv  IRQs off  FIQs on  Mode SVC_32  ISA ARM  Segment none
  [   42.377456] Control: 10c5387d  Table: 09f0806a  DAC: 00000051
  [   42.383286] Process kworker/0:0 (pid: 5, stack limit = 0xda2575c6)
  [   42.389392] Stack: (0xc810b910 to 0xc810c000)
  [   42.393801] b900:                                     00000005 c14456f8 c14456f8 c058f244
  [   42.401916] b920: a00f0113 c018b1cc c14456f8 c058f244 a00f0113 c0c4e454 00000001 00000000
  [   42.410117] b940: c058f244 00000003 c9d1d800 ca01f000 7fffffff c058f244 200f0113 00000001
  [   42.418267] b960: 00000cc0 c810a000 00000dc0 00000000 c1053148 00000023 c9d1d814 c14456f8
  [   42.426399] b980: 00000000 00000000 00000003 00000000 00000000 fcee162a 00000cc0 c9d1d800
  [   42.434607] b9a0: ca01f000 00000000 c810a000 00000014 c9d1d814 c14452e0 ca01f42c c0669f84
  [   42.442749] b9c0: c0668d98 00000000 00000dc0 00000dc0 00000000 c126dd90 00000dc0 ca01f24c
  [   42.450887] b9e0: 00000410 c14456f8 ca01f000 fcee162a 00000000 c9d1d800 00000000 00000000
  [   42.459087] ba00: 00000000 00000001 ca01f000 ca01fac8 00000002 c06965e0 00000000 00000000
  [   42.467220] ba20: 00000000 c03027b0 c8001600 00000001 c810a000 fcee162a 00000000 fcee162a
  [   42.475373] ba40: a00f0113 c9d25880 c0694894 a00f0113 c8000000 00007820 00000000 fcee162a
  [   42.483470] ba60: 00000002 00000000 ca01f000 c9ae7800 c810a000 00000001 c9d258c0 ca01fac8
  [   42.491685] ba80: 00000002 c0694a14 00000002 c0d68608 00000000 00000001 00000000 c0186460
  [   42.499882] baa0: c9d65878 c9d25940 c0d51198 00000001 00000000 36314752 34325241 00000000
  [   42.508038] bac0: 00000001 c9ae7820 00000000 34325258 c9ae7800 c810a000 00000022 c0c4e6b8
  [   42.516202] bae0: c98b1a00 fcee162a c9d65878 c9ae7820 c9d65878 c1446198 c9ae7800 c9d65800
  [   42.524347] bb00: 00000001 c0d68618 ffffffed c07eae44 c07ead58 c9ae7820 c18004e0 00000000
  [   42.532534] bb20: c18004e8 00000000 c1446198 c14ab308 00000022 c069f124 c9ae79cc 00000001
  [   42.540557] bb40: c9ae78dc 00000000 c14555b0 c9ae7820 c14ab308 c1446198 c069fba4 00000001
  [   42.548847] bb60: c1800494 00000000 c14555b0 c069f5bc 00000001 c069fb78 00000000 c810bbc4
  [   42.556874] bb80: c810a000 00000001 c1800494 00000000 c14555b0 c069cefc c14555b0 c8a964e4
  [   42.565179] bba0: c8e90d58 fcee162a c9d65934 c9ae7820 c810a000 c9ae7820 c9ae7864 c069f8f4
  [   42.573316] bbc0: c109aed0 c9ae7820 00000001 fcee162a c9ae7820 c14555c8 c9ae7820 c9d65878
  [   42.581503] bbe0: c1446248 c069e06c c9ae7820 00000000 c810a000 c069b76c 00000000 c0c49b8c
  [   42.589664] bc00: c9d65800 c9d65800 c9079800 00000000 c9d65800 00000001 c810bc14 fcee162a
  [   42.597796] bc20: 00000000 c9ae7800 c9bbb050 c14af400 c9d65800 c9bbb050 c10b6f28 00000001
  [   42.605979] bc40: c10b6f50 c07e9120 00000001 00000000 00000000 00000000 00001388 00000001
  [   42.614148] bc60: c810bc5c c98b1844 00000001 c9079800 c9d65878 c9d65804 c98b1844 c14557bc
  [   42.622314] bc80: c14555c8 c07e77e0 c9bbb00c c9bbb000 c98b1840 00000001 00000001 00000001
  [   42.630470] bca0: 00000004 c9bbb04c c10b6eac c9d72dd0 00000000 c9d65800 00000001 c145615c
  [   42.638629] bcc0: c9d65800 00000000 c145615c c14ab308 00000022 c07f4f34 c9d65878 c9d65878
  [   42.646777] bce0: c145615c c07ea6d4 c9d65878 c18004e0 00000000 c18004e8 00000000 c069f124
  [   42.654833] bd00: c9d65a24 00000001 c9d65934 00000000 c14552d0 c9d65878 c14ab308 c145615c
  [   42.663083] bd20: c069fba4 00000001 c1800494 00000000 c14552d0 c069f5bc 00000001 c069fb78
  [   42.671276] bd40: 00000000 c810bd8c c810a000 00000001 c1800494 00000000 c14552d0 c069cefc
  [   42.679443] bd60: c14552d0 c8a964e4 c8a98dd8 fcee162a c90af134 c9d65878 c810a000 c9d65878
  [   42.687596] bd80: c9d658bc c069f8f4 c109aed0 c9d65878 00000001 fcee162a c9d65878 c14555c8
  [   42.695758] bda0: c9d65878 c90af078 c1446248 c069e06c c9d65878 00000000 c810a000 c069b76c
  [   42.703831] bdc0: 60030013 c810a000 c98b1a80 c0631c34 c1096e00 39383128 c900353a c810bdec
  [   42.712078] bde0: ffff9b37 fcee162a c810be08 c9d65800 c9d65878 c98b1a80 c815d7c0 c90af000
  [   42.720211] be00: c90dcdbc 00000002 00000000 c07ddee8 00000003 00000000 00000002 c9d65800
  [   42.728403] be20: 00000002 00000000 c815d7c0 c90af000 c90dcdbc c07e0054 0000000a 00000002
  [   42.736572] be40: 00000000 00000000 000003e8 c8108e40 c81093a0 c90af000 c90dfc00 c90dccf6
  [   42.744732] be60: c90dcc6c c8162800 c90dcc70 c90dfc08 c90dcc00 c90dfeac c90dcc78 c90af0bc
  [   42.752829] be80: c810a000 00000006 c90dfeac c14aea10 c8162820 c9079800 c90af000 c10b5eb8
  [   42.761064] bea0: c1801ff0 00000064 00010101 c12554f8 00000000 c0185b20 00000000 00000000
  [   42.769217] bec0: c0143a20 00000000 00000000 fcee162a c80b6100 c90dcdbc c80b6100 eefc3680
  [   42.777340] bee0: ff7c9800 c149f400 c810a000 00000000 c1307fd0 c0143adc 00000001 00000000
  [   42.785540] bf00: c0143a20 00000000 00000000 00000000 00000001 eefc3680 c1802040 c15025a4
  [   42.793706] bf20: 00000000 c10b60a8 00000000 fcee162a eefc3680 c80b6100 eefc3680 c80b6114
  [   42.801867] bf40: eefc36b8 c1304d00 00000008 c810a000 eefc3680 c0144444 c80b60c4 c149e9e7
  [   42.809958] bf60: c810a000 c80b6080 c80ba500 c810a000 00000000 c80fde74 c01443e4 c80b6100
  [   42.818193] bf80: c80b60c4 c014932c 00000000 c80ba500 c01491c4 00000000 00000000 00000000
  [   42.826341] bfa0: 00000000 00000000 00000000 c0100150 00000000 00000000 00000000 00000000
  [   42.834464] bfc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
  [   42.842587] bfe0: 00000000 00000000 00000000 00000000 00000013 00000000 00000000 00000000
  [   42.850770] [<c0c3da6c>] (spin_dump) from [<c018b1cc>] (do_raw_spin_lock+0xc4/0x120)
  [   42.858556] [<c018b1cc>] (do_raw_spin_lock) from [<c0c4e454>] (_raw_spin_lock_irqsave+0x48/0x54)
  [   42.867394] [<c0c4e454>] (_raw_spin_lock_irqsave) from [<c058f244>] (ida_alloc_range+0xb0/0x3e4)
  [   42.876092] [<c058f244>] (ida_alloc_range) from [<c0669f84>] (drm_connector_init+0x13c/0x484)
  [   42.884667] [<c0669f84>] (drm_connector_init) from [<c06965e0>] (gud_connector_create+0x100/0x5f0)
  [   42.893673] [<c06965e0>] (gud_connector_create) from [<c0694a14>] (gud_probe+0x884/0x930)
  [   42.901860] [<c0694a14>] (gud_probe) from [<c07eae44>] (usb_probe_interface+0xec/0x2cc)
  [   42.909857] [<c07eae44>] (usb_probe_interface) from [<c069f124>] (really_probe+0x108/0x528)
  [   42.918208] [<c069f124>] (really_probe) from [<c069f5bc>] (driver_probe_device+0x78/0x1d8)
  [   42.926468] [<c069f5bc>] (driver_probe_device) from [<c069cefc>] (bus_for_each_drv+0x84/0xc8)
  [   42.935005] [<c069cefc>] (bus_for_each_drv) from [<c069f8f4>] (__device_attach+0xd0/0x1d0)
  [   42.943226] [<c069f8f4>] (__device_attach) from [<c069e06c>] (bus_probe_device+0x88/0x90)
  [   42.951424] [<c069e06c>] (bus_probe_device) from [<c069b76c>] (device_add+0x330/0x808)
  [   42.959342] [<c069b76c>] (device_add) from [<c07e9120>] (usb_set_configuration+0x4b0/0x8b4)
  [   42.967684] [<c07e9120>] (usb_set_configuration) from [<c07f4f34>] (usb_generic_driver_probe+0x50/0x8c)
  [   42.977007] [<c07f4f34>] (usb_generic_driver_probe) from [<c07ea6d4>] (usb_probe_device+0x44/0xf8)
  [   42.986016] [<c07ea6d4>] (usb_probe_device) from [<c069f124>] (really_probe+0x108/0x528)
  [   42.994109] [<c069f124>] (really_probe) from [<c069f5bc>] (driver_probe_device+0x78/0x1d8)
  [   43.002364] [<c069f5bc>] (driver_probe_device) from [<c069cefc>] (bus_for_each_drv+0x84/0xc8)
  [   43.010874] [<c069cefc>] (bus_for_each_drv) from [<c069f8f4>] (__device_attach+0xd0/0x1d0)
  [   43.019142] [<c069f8f4>] (__device_attach) from [<c069e06c>] (bus_probe_device+0x88/0x90)
  [   43.027252] [<c069e06c>] (bus_probe_device) from [<c069b76c>] (device_add+0x330/0x808)
  [   43.035225] [<c069b76c>] (device_add) from [<c07ddee8>] (usb_new_device+0x254/0x4ac)
  [   43.042972] [<c07ddee8>] (usb_new_device) from [<c07e0054>] (hub_event+0xd7c/0x1998)
  [   43.050707] [<c07e0054>] (hub_event) from [<c0143adc>] (process_one_work+0x2ac/0x7c8)
  [   43.058526] [<c0143adc>] (process_one_work) from [<c0144444>] (worker_thread+0x60/0x568)
  [   43.066597] [<c0144444>] (worker_thread) from [<c014932c>] (kthread+0x168/0x184)
  [   43.074009] [<c014932c>] (kthread) from [<c0100150>] (ret_from_fork+0x14/0x24)
  [   43.081238] Exception stack(0xc810bfb0 to 0xc810bff8)
  [   43.086278] bfa0:                                     00000000 00000000 00000000 00000000
  [   43.094436] bfc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
  [   43.102593] bfe0: 00000000 00000000 00000000 00000000 00000013 00000000
  [   43.109198] Code: e5952004 10843006 e595c008 03e00000 (15940308)
  [   43.115208] ---[ end trace aa41dc7cdb58205e ]---
  [   43.119821] note: kworker/0:0[5] exited with preempt_count 1

No big deal though it seems: just that connector_ida for then newly added USB
connector is not set up. This fixes the crash:

diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index 98b6ec45ef967..7631f76e7f345 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -94,6 +94,7 @@ static struct drm_conn_prop_enum_list drm_connector_enum_list[] = {
        { DRM_MODE_CONNECTOR_DPI, "DPI" },
        { DRM_MODE_CONNECTOR_WRITEBACK, "Writeback" },
        { DRM_MODE_CONNECTOR_SPI, "SPI" },
+       { DRM_MODE_CONNECTOR_USB, "USB" },
 };

Hopefully I'll be able to follow up with a Tested-by: once I'll make the
display side work on my MCU.

Take care
Lubo

> 
> Noralf.
> 
> [1] https://github.com/notro/gud/wiki
> 
> 
> Noralf Trønnes (3):
>   drm/uapi: Add USB connector type
>   drm/probe-helper: Check epoch counter in output_poll_execute()
>   drm: Add Generic USB Display driver
> 
>  MAINTAINERS                         |   8 +
>  drivers/gpu/drm/Kconfig             |   2 +
>  drivers/gpu/drm/Makefile            |   1 +
>  drivers/gpu/drm/drm_probe_helper.c  |   7 +-
>  drivers/gpu/drm/gud/Kconfig         |  14 +
>  drivers/gpu/drm/gud/Makefile        |   4 +
>  drivers/gpu/drm/gud/gud_connector.c | 722 ++++++++++++++++++++++++++++
>  drivers/gpu/drm/gud/gud_drv.c       | 620 ++++++++++++++++++++++++
>  drivers/gpu/drm/gud/gud_internal.h  | 148 ++++++
>  drivers/gpu/drm/gud/gud_pipe.c      | 472 ++++++++++++++++++
>  include/drm/gud.h                   | 356 ++++++++++++++
>  include/uapi/drm/drm_mode.h         |   1 +
>  12 files changed, 2354 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/gpu/drm/gud/Kconfig
>  create mode 100644 drivers/gpu/drm/gud/Makefile
>  create mode 100644 drivers/gpu/drm/gud/gud_connector.c
>  create mode 100644 drivers/gpu/drm/gud/gud_drv.c
>  create mode 100644 drivers/gpu/drm/gud/gud_internal.h
>  create mode 100644 drivers/gpu/drm/gud/gud_pipe.c
>  create mode 100644 include/drm/gud.h
> 
> -- 
> 2.23.0
> 

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 0/3] Generic USB Display driver
@ 2021-01-23 18:47   ` Lubomir Rintel
  0 siblings, 0 replies; 53+ messages in thread
From: Lubomir Rintel @ 2021-01-23 18:47 UTC (permalink / raw)
  To: Noralf Trønnes
  Cc: hudson, markus, sam, linux-usb, dri-devel, th020394, pontus.fuchs, peter

On Wed, Jan 20, 2021 at 06:00:30PM +0100, Noralf Trønnes wrote:
> Hi,
> 
> A while back I had the idea to turn a Raspberry Pi Zero into a $5
> USB to HDMI/SDTV/DSI/DPI display adapter.
> 
> The reason for calling it 'Generic' is so anyone can make a USB
> display/adapter against this driver, all that's needed is to add a USB
> vid:pid.
> 
> Unfortunately I've had some compounding health problems that have
> severally limited the time I can spend in front of a computer. For this
> reason I've decided to keep the gadget driver out-of-tree and focus on
> getting the host driver merged first.
> 
> See the wiki[1] for more information and images for the Raspberry Pi
> Zero/4.
> 
> One big change this time is that I've followed Peter Stuge's advice to
> not let DRM stuff leak into the USB protocol. This has made the protocol
> easier to understand just from reading the header file.

Welcome back! I'm happy to see this patch set back on track. I've now
started putting together something that would work with GUD on
Lilygo T-Display-GD32, but it blew up in my face immediately (I had
CONFIG_DEBUG_SPINLOCK turned on):

  [   42.064362] usb 1-1.2: new full-speed USB device number 6 using mv-ehci
  [   42.228048] usb 1-1.2: New USB device found, idVendor=1d50, idProduct=614d, bcdDevice= 1.00
  [   42.236812] usb 1-1.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
  [   42.254298] usb 1-1.2: Product: LCD Display Interface
  [   42.259771] usb 1-1.2: Manufacturer: Lubomir Rintel <lkundrak@v3.sk>
  [   42.266395] usb 1-1.2: SerialNumber: FFFFFFFF3736030061294139
  [   42.284771] BUG: spinlock bad magic on CPU#0, kworker/0:0/5
  [   42.290394] 8<--- cut here ---
  [   42.293415] Unable to handle kernel NULL pointer dereference at virtual address 0000031c
  [   42.301417] pgd = e351f484
  [   42.304184] [0000031c] *pgd=00000000
  [   42.307826] Internal error: Oops: 15 [#1] PREEMPT SMP ARM
  [   42.313250] Modules linked in:
  [   42.316208] CPU: 0 PID: 5 Comm: kworker/0:0 Not tainted 5.11.0-rc4-00113-g8c3d05f1939a #1274
  [   42.324729] Hardware name: Marvell MMP3
  [   42.328540] Workqueue: usb_hub_wq hub_event
  [   42.332759] PC is at spin_dump+0x5c/0x88
  [   42.336556] LR is at spin_dump+0x48/0x88
  [   42.340644] pc : [<c0c3da6c>]    lr : [<c0c3da58>]    psr: 200f0193
  [   42.346895] sp : c810b910  ip : 00000000  fp : c1107b40
  [   42.352039] r10: ffffc005  r9 : 00000000  r8 : 001fffff
  [   42.357238] r7 : 00000000  r6 : 00000438  r5 : c14456f8  r4 : 00000014
  [   42.363837] r3 : 0000044c  r2 : c1091500  r1 : eefb8540  r0 : 0000002f
  [   42.370351] Flags: nzCv  IRQs off  FIQs on  Mode SVC_32  ISA ARM  Segment none
  [   42.377456] Control: 10c5387d  Table: 09f0806a  DAC: 00000051
  [   42.383286] Process kworker/0:0 (pid: 5, stack limit = 0xda2575c6)
  [   42.389392] Stack: (0xc810b910 to 0xc810c000)
  [   42.393801] b900:                                     00000005 c14456f8 c14456f8 c058f244
  [   42.401916] b920: a00f0113 c018b1cc c14456f8 c058f244 a00f0113 c0c4e454 00000001 00000000
  [   42.410117] b940: c058f244 00000003 c9d1d800 ca01f000 7fffffff c058f244 200f0113 00000001
  [   42.418267] b960: 00000cc0 c810a000 00000dc0 00000000 c1053148 00000023 c9d1d814 c14456f8
  [   42.426399] b980: 00000000 00000000 00000003 00000000 00000000 fcee162a 00000cc0 c9d1d800
  [   42.434607] b9a0: ca01f000 00000000 c810a000 00000014 c9d1d814 c14452e0 ca01f42c c0669f84
  [   42.442749] b9c0: c0668d98 00000000 00000dc0 00000dc0 00000000 c126dd90 00000dc0 ca01f24c
  [   42.450887] b9e0: 00000410 c14456f8 ca01f000 fcee162a 00000000 c9d1d800 00000000 00000000
  [   42.459087] ba00: 00000000 00000001 ca01f000 ca01fac8 00000002 c06965e0 00000000 00000000
  [   42.467220] ba20: 00000000 c03027b0 c8001600 00000001 c810a000 fcee162a 00000000 fcee162a
  [   42.475373] ba40: a00f0113 c9d25880 c0694894 a00f0113 c8000000 00007820 00000000 fcee162a
  [   42.483470] ba60: 00000002 00000000 ca01f000 c9ae7800 c810a000 00000001 c9d258c0 ca01fac8
  [   42.491685] ba80: 00000002 c0694a14 00000002 c0d68608 00000000 00000001 00000000 c0186460
  [   42.499882] baa0: c9d65878 c9d25940 c0d51198 00000001 00000000 36314752 34325241 00000000
  [   42.508038] bac0: 00000001 c9ae7820 00000000 34325258 c9ae7800 c810a000 00000022 c0c4e6b8
  [   42.516202] bae0: c98b1a00 fcee162a c9d65878 c9ae7820 c9d65878 c1446198 c9ae7800 c9d65800
  [   42.524347] bb00: 00000001 c0d68618 ffffffed c07eae44 c07ead58 c9ae7820 c18004e0 00000000
  [   42.532534] bb20: c18004e8 00000000 c1446198 c14ab308 00000022 c069f124 c9ae79cc 00000001
  [   42.540557] bb40: c9ae78dc 00000000 c14555b0 c9ae7820 c14ab308 c1446198 c069fba4 00000001
  [   42.548847] bb60: c1800494 00000000 c14555b0 c069f5bc 00000001 c069fb78 00000000 c810bbc4
  [   42.556874] bb80: c810a000 00000001 c1800494 00000000 c14555b0 c069cefc c14555b0 c8a964e4
  [   42.565179] bba0: c8e90d58 fcee162a c9d65934 c9ae7820 c810a000 c9ae7820 c9ae7864 c069f8f4
  [   42.573316] bbc0: c109aed0 c9ae7820 00000001 fcee162a c9ae7820 c14555c8 c9ae7820 c9d65878
  [   42.581503] bbe0: c1446248 c069e06c c9ae7820 00000000 c810a000 c069b76c 00000000 c0c49b8c
  [   42.589664] bc00: c9d65800 c9d65800 c9079800 00000000 c9d65800 00000001 c810bc14 fcee162a
  [   42.597796] bc20: 00000000 c9ae7800 c9bbb050 c14af400 c9d65800 c9bbb050 c10b6f28 00000001
  [   42.605979] bc40: c10b6f50 c07e9120 00000001 00000000 00000000 00000000 00001388 00000001
  [   42.614148] bc60: c810bc5c c98b1844 00000001 c9079800 c9d65878 c9d65804 c98b1844 c14557bc
  [   42.622314] bc80: c14555c8 c07e77e0 c9bbb00c c9bbb000 c98b1840 00000001 00000001 00000001
  [   42.630470] bca0: 00000004 c9bbb04c c10b6eac c9d72dd0 00000000 c9d65800 00000001 c145615c
  [   42.638629] bcc0: c9d65800 00000000 c145615c c14ab308 00000022 c07f4f34 c9d65878 c9d65878
  [   42.646777] bce0: c145615c c07ea6d4 c9d65878 c18004e0 00000000 c18004e8 00000000 c069f124
  [   42.654833] bd00: c9d65a24 00000001 c9d65934 00000000 c14552d0 c9d65878 c14ab308 c145615c
  [   42.663083] bd20: c069fba4 00000001 c1800494 00000000 c14552d0 c069f5bc 00000001 c069fb78
  [   42.671276] bd40: 00000000 c810bd8c c810a000 00000001 c1800494 00000000 c14552d0 c069cefc
  [   42.679443] bd60: c14552d0 c8a964e4 c8a98dd8 fcee162a c90af134 c9d65878 c810a000 c9d65878
  [   42.687596] bd80: c9d658bc c069f8f4 c109aed0 c9d65878 00000001 fcee162a c9d65878 c14555c8
  [   42.695758] bda0: c9d65878 c90af078 c1446248 c069e06c c9d65878 00000000 c810a000 c069b76c
  [   42.703831] bdc0: 60030013 c810a000 c98b1a80 c0631c34 c1096e00 39383128 c900353a c810bdec
  [   42.712078] bde0: ffff9b37 fcee162a c810be08 c9d65800 c9d65878 c98b1a80 c815d7c0 c90af000
  [   42.720211] be00: c90dcdbc 00000002 00000000 c07ddee8 00000003 00000000 00000002 c9d65800
  [   42.728403] be20: 00000002 00000000 c815d7c0 c90af000 c90dcdbc c07e0054 0000000a 00000002
  [   42.736572] be40: 00000000 00000000 000003e8 c8108e40 c81093a0 c90af000 c90dfc00 c90dccf6
  [   42.744732] be60: c90dcc6c c8162800 c90dcc70 c90dfc08 c90dcc00 c90dfeac c90dcc78 c90af0bc
  [   42.752829] be80: c810a000 00000006 c90dfeac c14aea10 c8162820 c9079800 c90af000 c10b5eb8
  [   42.761064] bea0: c1801ff0 00000064 00010101 c12554f8 00000000 c0185b20 00000000 00000000
  [   42.769217] bec0: c0143a20 00000000 00000000 fcee162a c80b6100 c90dcdbc c80b6100 eefc3680
  [   42.777340] bee0: ff7c9800 c149f400 c810a000 00000000 c1307fd0 c0143adc 00000001 00000000
  [   42.785540] bf00: c0143a20 00000000 00000000 00000000 00000001 eefc3680 c1802040 c15025a4
  [   42.793706] bf20: 00000000 c10b60a8 00000000 fcee162a eefc3680 c80b6100 eefc3680 c80b6114
  [   42.801867] bf40: eefc36b8 c1304d00 00000008 c810a000 eefc3680 c0144444 c80b60c4 c149e9e7
  [   42.809958] bf60: c810a000 c80b6080 c80ba500 c810a000 00000000 c80fde74 c01443e4 c80b6100
  [   42.818193] bf80: c80b60c4 c014932c 00000000 c80ba500 c01491c4 00000000 00000000 00000000
  [   42.826341] bfa0: 00000000 00000000 00000000 c0100150 00000000 00000000 00000000 00000000
  [   42.834464] bfc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
  [   42.842587] bfe0: 00000000 00000000 00000000 00000000 00000013 00000000 00000000 00000000
  [   42.850770] [<c0c3da6c>] (spin_dump) from [<c018b1cc>] (do_raw_spin_lock+0xc4/0x120)
  [   42.858556] [<c018b1cc>] (do_raw_spin_lock) from [<c0c4e454>] (_raw_spin_lock_irqsave+0x48/0x54)
  [   42.867394] [<c0c4e454>] (_raw_spin_lock_irqsave) from [<c058f244>] (ida_alloc_range+0xb0/0x3e4)
  [   42.876092] [<c058f244>] (ida_alloc_range) from [<c0669f84>] (drm_connector_init+0x13c/0x484)
  [   42.884667] [<c0669f84>] (drm_connector_init) from [<c06965e0>] (gud_connector_create+0x100/0x5f0)
  [   42.893673] [<c06965e0>] (gud_connector_create) from [<c0694a14>] (gud_probe+0x884/0x930)
  [   42.901860] [<c0694a14>] (gud_probe) from [<c07eae44>] (usb_probe_interface+0xec/0x2cc)
  [   42.909857] [<c07eae44>] (usb_probe_interface) from [<c069f124>] (really_probe+0x108/0x528)
  [   42.918208] [<c069f124>] (really_probe) from [<c069f5bc>] (driver_probe_device+0x78/0x1d8)
  [   42.926468] [<c069f5bc>] (driver_probe_device) from [<c069cefc>] (bus_for_each_drv+0x84/0xc8)
  [   42.935005] [<c069cefc>] (bus_for_each_drv) from [<c069f8f4>] (__device_attach+0xd0/0x1d0)
  [   42.943226] [<c069f8f4>] (__device_attach) from [<c069e06c>] (bus_probe_device+0x88/0x90)
  [   42.951424] [<c069e06c>] (bus_probe_device) from [<c069b76c>] (device_add+0x330/0x808)
  [   42.959342] [<c069b76c>] (device_add) from [<c07e9120>] (usb_set_configuration+0x4b0/0x8b4)
  [   42.967684] [<c07e9120>] (usb_set_configuration) from [<c07f4f34>] (usb_generic_driver_probe+0x50/0x8c)
  [   42.977007] [<c07f4f34>] (usb_generic_driver_probe) from [<c07ea6d4>] (usb_probe_device+0x44/0xf8)
  [   42.986016] [<c07ea6d4>] (usb_probe_device) from [<c069f124>] (really_probe+0x108/0x528)
  [   42.994109] [<c069f124>] (really_probe) from [<c069f5bc>] (driver_probe_device+0x78/0x1d8)
  [   43.002364] [<c069f5bc>] (driver_probe_device) from [<c069cefc>] (bus_for_each_drv+0x84/0xc8)
  [   43.010874] [<c069cefc>] (bus_for_each_drv) from [<c069f8f4>] (__device_attach+0xd0/0x1d0)
  [   43.019142] [<c069f8f4>] (__device_attach) from [<c069e06c>] (bus_probe_device+0x88/0x90)
  [   43.027252] [<c069e06c>] (bus_probe_device) from [<c069b76c>] (device_add+0x330/0x808)
  [   43.035225] [<c069b76c>] (device_add) from [<c07ddee8>] (usb_new_device+0x254/0x4ac)
  [   43.042972] [<c07ddee8>] (usb_new_device) from [<c07e0054>] (hub_event+0xd7c/0x1998)
  [   43.050707] [<c07e0054>] (hub_event) from [<c0143adc>] (process_one_work+0x2ac/0x7c8)
  [   43.058526] [<c0143adc>] (process_one_work) from [<c0144444>] (worker_thread+0x60/0x568)
  [   43.066597] [<c0144444>] (worker_thread) from [<c014932c>] (kthread+0x168/0x184)
  [   43.074009] [<c014932c>] (kthread) from [<c0100150>] (ret_from_fork+0x14/0x24)
  [   43.081238] Exception stack(0xc810bfb0 to 0xc810bff8)
  [   43.086278] bfa0:                                     00000000 00000000 00000000 00000000
  [   43.094436] bfc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
  [   43.102593] bfe0: 00000000 00000000 00000000 00000000 00000013 00000000
  [   43.109198] Code: e5952004 10843006 e595c008 03e00000 (15940308)
  [   43.115208] ---[ end trace aa41dc7cdb58205e ]---
  [   43.119821] note: kworker/0:0[5] exited with preempt_count 1

No big deal though it seems: just that connector_ida for then newly added USB
connector is not set up. This fixes the crash:

diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index 98b6ec45ef967..7631f76e7f345 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -94,6 +94,7 @@ static struct drm_conn_prop_enum_list drm_connector_enum_list[] = {
        { DRM_MODE_CONNECTOR_DPI, "DPI" },
        { DRM_MODE_CONNECTOR_WRITEBACK, "Writeback" },
        { DRM_MODE_CONNECTOR_SPI, "SPI" },
+       { DRM_MODE_CONNECTOR_USB, "USB" },
 };

Hopefully I'll be able to follow up with a Tested-by: once I'll make the
display side work on my MCU.

Take care
Lubo

> 
> Noralf.
> 
> [1] https://github.com/notro/gud/wiki
> 
> 
> Noralf Trønnes (3):
>   drm/uapi: Add USB connector type
>   drm/probe-helper: Check epoch counter in output_poll_execute()
>   drm: Add Generic USB Display driver
> 
>  MAINTAINERS                         |   8 +
>  drivers/gpu/drm/Kconfig             |   2 +
>  drivers/gpu/drm/Makefile            |   1 +
>  drivers/gpu/drm/drm_probe_helper.c  |   7 +-
>  drivers/gpu/drm/gud/Kconfig         |  14 +
>  drivers/gpu/drm/gud/Makefile        |   4 +
>  drivers/gpu/drm/gud/gud_connector.c | 722 ++++++++++++++++++++++++++++
>  drivers/gpu/drm/gud/gud_drv.c       | 620 ++++++++++++++++++++++++
>  drivers/gpu/drm/gud/gud_internal.h  | 148 ++++++
>  drivers/gpu/drm/gud/gud_pipe.c      | 472 ++++++++++++++++++
>  include/drm/gud.h                   | 356 ++++++++++++++
>  include/uapi/drm/drm_mode.h         |   1 +
>  12 files changed, 2354 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/gpu/drm/gud/Kconfig
>  create mode 100644 drivers/gpu/drm/gud/Makefile
>  create mode 100644 drivers/gpu/drm/gud/gud_connector.c
>  create mode 100644 drivers/gpu/drm/gud/gud_drv.c
>  create mode 100644 drivers/gpu/drm/gud/gud_internal.h
>  create mode 100644 drivers/gpu/drm/gud/gud_pipe.c
>  create mode 100644 include/drm/gud.h
> 
> -- 
> 2.23.0
> 
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 3/3] drm: Add Generic USB Display driver
  2021-01-20 18:02   ` Daniel Vetter
@ 2021-01-24 16:17       ` Noralf Trønnes
  0 siblings, 0 replies; 53+ messages in thread
From: Noralf Trønnes @ 2021-01-24 16:17 UTC (permalink / raw)
  To: Daniel Vetter
  Cc: dri-devel, hudson, markus, Sam Ravnborg, USB list, Tyler Hardin,
	Lubomir Rintel, pontus.fuchs, peter



Den 20.01.2021 19.02, skrev Daniel Vetter:
> On Wed, Jan 20, 2021 at 6:11 PM Noralf Trønnes <noralf@tronnes.org> wrote:
>>
>> This adds a generic USB display driver with the intention that it can be
>> used with future USB interfaced low end displays/adapters. The Linux
>> gadget device driver will serve as the canonical device implementation.
>>
>> The following DRM properties are supported:
>> - Plane rotation
>> - Connector TV properties
>>
>> There is also support for backlight brightness exposed as a backlight
>> device.
>>
>> Display modes can be made available to the host driver either as DRM
>> display modes or through EDID. If both are present, EDID is just passed
>> on to userspace.
>>
>> Performance is preferred over color depth, so if the device supports
>> RGB565, DRM_CAP_DUMB_PREFERRED_DEPTH will return 16.
>>
>> If the device transfer buffer can't fit an uncompressed framebuffer
>> update, the update is split up into parts that do fit.
>>
>> Optimal user experience is achieved by providing damage reports either by
>> setting FB_DAMAGE_CLIPS on pageflips or calling DRM_IOCTL_MODE_DIRTYFB.
>>
>> LZ4 compression is used if the device supports it.
>>
>> The driver supports a one bit monochrome transfer format: R1. This is not
>> implemented in the gadget driver. It is added in preparation for future
>> monochrome e-ink displays.
>>
>> The driver is MIT licensed to smooth the path for any BSD port of the
>> driver.
>>
>> v2:
>> - Use devm_drm_dev_alloc() and drmm_mode_config_init()
>> - drm_fbdev_generic_setup: Use preferred_bpp=0, 16 was a copy paste error
>> - The drm_backlight_helper is dropped, copy in the code
> 
> I think the backlight is still a bit problematic, since you're using
> kms locks within the backlight callbacks. Other display drivers use
> backlight within their kms locks. This means inconsistent locking
> rules, which upsets lockdep.
> 
> Since you're already handling brightness as a special case in many
> places I don't think it's a big shuffle:
> - add a mutex to the connector struct
> - move brightness value to connector struct, out of the connector_state
> - use the new mutex to protect backlight state both from modeset side
> (if needed, I'm not entirely sure about that) and the backlight side
> 

It's not enough to store the value I need to send it to the device as
well. Currently I send the entire state each time there's a change. To
continue that I would need to keep a copy of the state that I can use
when brightness changes. Or I can treat backlight as an exception and
add a USB control request just for backlight.

There is some special treatment of the backlight in the driver, but I
would really like to handle the backlight brightness through the atomic
machinery. I want to avoid special treatment of backlight in the USB
protocol.

I can avoid the lockdep problem by letting a worker commit the state and
schedule it from the backlight update callback. I'll do that unless you
see other issues with that approach.

Is it ok to take the connection_mutex lock in the get_brightness
callback to get to the connector state and the brightness value?

i915 takes that lock in intel_backlight_device_update_status() and
intel_backlight_device_get_brightness().

> Some more things below, but in general I'd say Acked-by: Daniel Vetter
> <daniel.vetter> fwiw (probably not so much).
> 

Thanks for taking a look, much appreciated.

Noralf.


>> diff --git a/drivers/gpu/drm/gud/gud_connector.c b/drivers/gpu/drm/gud/gud_connector.c
>> new file mode 100644
>> index 000000000000..a4b9bbf48e19
>> --- /dev/null
>> +++ b/drivers/gpu/drm/gud/gud_connector.c
>> @@ -0,0 +1,722 @@
>> +// SPDX-License-Identifier: MIT
>> +/*
>> + * Copyright 2020 Noralf Trønnes
>> + */
>> +
>> +#include <linux/backlight.h>
>> +
>> +#include <drm/drm_atomic.h>
>> +#include <drm/drm_atomic_state_helper.h>
>> +#include <drm/drm_connector.h>
>> +#include <drm/drm_drv.h>
>> +#include <drm/drm_encoder.h>
>> +#include <drm/drm_file.h>
>> +#include <drm/drm_modeset_helper_vtables.h>
>> +#include <drm/drm_print.h>
>> +#include <drm/drm_probe_helper.h>
>> +#include <drm/drm_simple_kms_helper.h>
>> +#include <drm/gud.h>
>> +
>> +#include "gud_internal.h"
>> +
>> +struct gud_connector {
>> +       struct drm_connector connector;
>> +       struct drm_encoder encoder;
>> +       struct backlight_device *backlight;
>> +
>> +       /* Supported properties */
>> +       u16 *properties;
>> +       unsigned int num_properties;
>> +
>> +       /* Initial gadget tv state if applicable, applied on state reset */
>> +       struct drm_tv_connector_state initial_tv_state;
>> +
>> +       /*
>> +        * Initial gadget backlight brightness if applicable, applied on state reset.
>> +        * The value -ENODEV is used to signal no backlight.
>> +        */
>> +       int initial_brightness;
>> +
>> +       unsigned int num_modes;
>> +       size_t edid_len;
>> +};
>> +
>> +static inline struct gud_connector *to_gud_connector(struct drm_connector *connector)
>> +{
>> +       return container_of(connector, struct gud_connector, connector);
>> +}
>> +
>> +static int gud_connector_backlight_update_status(struct backlight_device *bd)
>> +{
>> +       struct drm_connector *connector = bl_get_data(bd);
>> +       struct drm_connector_state *connector_state;
>> +       struct drm_device *dev = connector->dev;
>> +       struct drm_modeset_acquire_ctx ctx;
>> +       struct drm_atomic_state *state;
>> +       int ret;
>> +
>> +       state = drm_atomic_state_alloc(dev);
>> +       if (!state)
>> +               return -ENOMEM;
>> +
>> +       drm_modeset_acquire_init(&ctx, 0);
>> +       state->acquire_ctx = &ctx;
>> +retry:
>> +       connector_state = drm_atomic_get_connector_state(state, connector);
>> +       if (IS_ERR(connector_state)) {
>> +               ret = PTR_ERR(connector_state);
>> +               goto out;
>> +       }
>> +
>> +       /* Reuse tv.brightness to avoid having to subclass */
>> +       connector_state->tv.brightness = bd->props.brightness;
>> +
>> +       ret = drm_atomic_commit(state);
>> +out:
>> +       if (ret == -EDEADLK) {
>> +               drm_atomic_state_clear(state);
>> +               drm_modeset_backoff(&ctx);
>> +               goto retry;
>> +       }
>> +
>> +       drm_atomic_state_put(state);
>> +
>> +       drm_modeset_drop_locks(&ctx);
>> +       drm_modeset_acquire_fini(&ctx);
>> +
>> +       return ret;
>> +}
>> +
>> +static int gud_connector_backlight_get_brightness(struct backlight_device *bd)
>> +{
>> +       struct drm_connector *connector = bl_get_data(bd);
>> +       struct drm_device *dev = connector->dev;
>> +       int brightness;
>> +
>> +       drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
>> +       brightness = connector->state->tv.brightness;
>> +       drm_modeset_unlock(&dev->mode_config.connection_mutex);
>> +
>> +       return brightness;
>> +}
>> +
>> +static const struct backlight_ops gud_connector_backlight_ops = {
>> +       .get_brightness = gud_connector_backlight_get_brightness,
>> +       .update_status  = gud_connector_backlight_update_status,
>> +};
>> +
>> +static int gud_connector_backlight_register(struct gud_connector *gconn)
>> +{
>> +       struct drm_connector *connector = &gconn->connector;
>> +       struct backlight_device *bd;
>> +       const char *name;
>> +       const struct backlight_properties props = {
>> +               .type = BACKLIGHT_RAW,
>> +               .scale = BACKLIGHT_SCALE_NON_LINEAR,
>> +               .max_brightness = 100,
>> +       };
>> +
>> +       name = kasprintf(GFP_KERNEL, "card%d-%s-backlight",
>> +                        connector->dev->primary->index, connector->name);
>> +       if (!name)
>> +               return -ENOMEM;
>> +
>> +       bd = backlight_device_register(name, connector->kdev, connector,
>> +                                      &gud_connector_backlight_ops, &props);
>> +       kfree(name);
>> +       if (IS_ERR(bd))
>> +               return PTR_ERR(bd);
>> +
>> +       gconn->backlight = bd;
>> +
>> +       return 0;
>> +}
>> +
>> +static int gud_connector_status_request(struct drm_connector *connector)
>> +{
>> +       struct gud_connector *gconn = to_gud_connector(connector);
>> +       struct gud_device *gdrm = to_gud_device(connector->dev);
>> +       struct gud_connector_status_req req;
>> +       u16 num_modes, edid_len;
>> +       int ret;
>> +
>> +       ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_STATUS,
>> +                         connector->index, &req, sizeof(req));
>> +       if (ret)
>> +               return ret;
>> +
>> +       switch (req.status & GUD_CONNECTOR_STATUS_CONNECTED_MASK) {
>> +       case GUD_CONNECTOR_STATUS_DISCONNECTED:
>> +               ret = connector_status_disconnected;
>> +               break;
>> +       case GUD_CONNECTOR_STATUS_CONNECTED:
>> +               ret = connector_status_connected;
>> +               break;
>> +       default:
>> +               ret = connector_status_unknown;
>> +               break;
>> +       };
>> +
>> +       num_modes = le16_to_cpu(req.num_modes);
>> +       edid_len = le16_to_cpu(req.edid_len);
>> +
>> +       if (edid_len % EDID_LENGTH) {
>> +               drm_err(connector->dev, "%s: Invalid EDID size: %u\n", connector->name, edid_len);
>> +               edid_len = 0;
>> +       }
>> +
>> +       if (req.status & GUD_CONNECTOR_STATUS_CHANGED ||
>> +           gconn->num_modes != num_modes || gconn->edid_len != edid_len)
>> +               connector->epoch_counter += 1;
>> +
>> +       gconn->num_modes = num_modes;
>> +       gconn->edid_len = edid_len;
>> +
>> +       if (!num_modes && !edid_len && ret != connector_status_disconnected)
>> +               drm_dbg_kms(connector->dev, "%s: No modes or EDID.\n", connector->name);
>> +
>> +       return ret;
>> +}
>> +
>> +static int gud_connector_detect(struct drm_connector *connector,
>> +                               struct drm_modeset_acquire_ctx *ctx, bool force)
>> +{
>> +       struct gud_device *gdrm = to_gud_device(connector->dev);
>> +       int idx, ret;
>> +
>> +       if (!drm_dev_enter(connector->dev, &idx))
>> +               return connector_status_disconnected;
>> +
>> +       if (force) {
>> +               ret = gud_usb_set(gdrm, GUD_REQ_SET_CONNECTOR_FORCE_DETECT,
>> +                                 connector->index, NULL, 0);
>> +               if (ret) {
>> +                       ret = connector_status_unknown;
>> +                       goto exit;
>> +               }
>> +       }
>> +
>> +       ret = gud_connector_status_request(connector);
>> +       if (ret < 0)
>> +               ret = connector_status_unknown;
>> +exit:
>> +       drm_dev_exit(idx);
>> +
>> +       return ret;
>> +}
>> +
>> +struct gud_connector_get_edid_ctx {
>> +       struct gud_connector *gconn;
>> +       void *buf;
>> +};
>> +
>> +static int gud_connector_get_edid_block(void *data, u8 *buf, unsigned int block, size_t len)
>> +{
>> +       struct gud_connector_get_edid_ctx *ctx = data;
>> +       struct gud_connector *gconn = ctx->gconn;
>> +       size_t start = block * EDID_LENGTH;
>> +
>> +       if (start + len > gconn->edid_len)
>> +               return -1;
>> +
>> +       if (!block) {
>> +               struct gud_device *gdrm = to_gud_device(gconn->connector.dev);
>> +               int ret;
>> +
>> +               /* Check because drm_do_get_edid() will retry on failure */
>> +               if (!ctx->buf)
>> +                       ctx->buf = kmalloc(gconn->edid_len, GFP_KERNEL);
>> +               if (!ctx->buf)
>> +                       return -1;
>> +
>> +               ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_EDID, gconn->connector.index,
>> +                                 ctx->buf, gconn->edid_len);
>> +               if (ret)
>> +                       return -1;
>> +       }
>> +
>> +       memcpy(buf, ctx->buf + start, len);
>> +
>> +       return 0;
>> +}
>> +
>> +static int gud_connector_get_modes(struct drm_connector *connector)
>> +{
>> +       struct gud_connector *gconn = to_gud_connector(connector);
>> +       struct gud_device *gdrm = to_gud_device(connector->dev);
>> +       struct gud_connector_get_edid_ctx edid_ctx = {
>> +               .gconn = gconn,
>> +       };
>> +       struct gud_display_mode_req *reqmodes = NULL;
>> +       unsigned int i, num_modes = 0;
>> +       struct edid *edid = NULL;
>> +       bool edid_override;
>> +       int idx, ret;
>> +
>> +       if (!drm_dev_enter(connector->dev, &idx))
>> +               return 0;
>> +
>> +       if (connector->force) {
>> +               ret = gud_connector_status_request(connector);
>> +               if (ret < 0)
>> +                       goto out;
>> +       }
>> +
>> +       edid = drm_do_get_edid(connector, gud_connector_get_edid_block, &edid_ctx);
>> +       edid_override = edid && !edid_ctx.buf;
>> +       kfree(edid_ctx.buf);
>> +       drm_connector_update_edid_property(connector, edid);
>> +
>> +       if (!gconn->num_modes || edid_override) {
>> +               num_modes = drm_add_edid_modes(connector, edid);
>> +               goto out;
>> +       }
>> +
>> +       reqmodes = kmalloc_array(gconn->num_modes, sizeof(*reqmodes), GFP_KERNEL);
>> +       if (!reqmodes)
>> +               goto out;
>> +
>> +       ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_MODES, connector->index,
>> +                         reqmodes, gconn->num_modes * sizeof(*reqmodes));
>> +       if (ret)
>> +               goto out;
>> +
>> +       for (i = 0; i < gconn->num_modes; i++) {
>> +               struct drm_display_mode *mode;
>> +
>> +               mode = drm_mode_create(connector->dev);
>> +               if (!mode)
>> +                       goto out;
>> +
>> +               gud_to_display_mode(mode, &reqmodes[i]);
>> +               drm_mode_probed_add(connector, mode);
>> +               num_modes++;
>> +       }
>> +out:
>> +       kfree(reqmodes);
>> +       kfree(edid);
>> +       drm_dev_exit(idx);
>> +
>> +       return num_modes;
>> +}
>> +
>> +static int gud_connector_atomic_check(struct drm_connector *connector,
>> +                                     struct drm_atomic_state *state)
>> +{
>> +       struct drm_connector_state *new_state;
>> +       struct drm_crtc_state *new_crtc_state;
>> +       struct drm_connector_state *old_state;
>> +
>> +       new_state = drm_atomic_get_new_connector_state(state, connector);
>> +       if (!new_state->crtc)
>> +               return 0;
>> +
>> +       old_state = drm_atomic_get_old_connector_state(state, connector);
>> +       new_crtc_state = drm_atomic_get_new_crtc_state(state, new_state->crtc);
>> +
>> +       if (old_state->tv.margins.left != new_state->tv.margins.left ||
>> +           old_state->tv.margins.right != new_state->tv.margins.right ||
>> +           old_state->tv.margins.top != new_state->tv.margins.top ||
>> +           old_state->tv.margins.bottom != new_state->tv.margins.bottom ||
>> +           old_state->tv.mode != new_state->tv.mode ||
>> +           old_state->tv.brightness != new_state->tv.brightness ||
>> +           old_state->tv.contrast != new_state->tv.contrast ||
>> +           old_state->tv.flicker_reduction != new_state->tv.flicker_reduction ||
>> +           old_state->tv.overscan != new_state->tv.overscan ||
>> +           old_state->tv.saturation != new_state->tv.saturation ||
>> +           old_state->tv.hue != new_state->tv.hue)
>> +               new_crtc_state->connectors_changed = true;
>> +
>> +       return 0;
>> +}
>> +
>> +static const struct drm_connector_helper_funcs gud_connector_helper_funcs = {
>> +       .detect_ctx = gud_connector_detect,
>> +       .get_modes = gud_connector_get_modes,
>> +       .atomic_check = gud_connector_atomic_check,
>> +};
>> +
>> +static int gud_connector_late_register(struct drm_connector *connector)
>> +{
>> +       struct gud_connector *gconn = to_gud_connector(connector);
>> +
>> +       if (gconn->initial_brightness < 0)
>> +               return 0;
>> +
>> +       return gud_connector_backlight_register(gconn);
>> +}
>> +
>> +static void gud_connector_early_unregister(struct drm_connector *connector)
>> +{
>> +       struct gud_connector *gconn = to_gud_connector(connector);
>> +
>> +       backlight_device_unregister(gconn->backlight);
>> +}
>> +
>> +static void gud_connector_destroy(struct drm_connector *connector)
>> +{
>> +       struct gud_connector *gconn = to_gud_connector(connector);
>> +
>> +       drm_connector_cleanup(connector);
>> +       kfree(gconn->properties);
>> +       kfree(gconn);
>> +}
>> +
>> +static void gud_connector_reset(struct drm_connector *connector)
>> +{
>> +       struct gud_connector *gconn = to_gud_connector(connector);
>> +
>> +       drm_atomic_helper_connector_reset(connector);
>> +       connector->state->tv = gconn->initial_tv_state;
>> +       /* Set margins from command line */
>> +       drm_atomic_helper_connector_tv_reset(connector);
>> +       if (gconn->initial_brightness >= 0)
>> +               connector->state->tv.brightness = gconn->initial_brightness;
>> +}
>> +
>> +static const struct drm_connector_funcs gud_connector_funcs = {
>> +       .fill_modes = drm_helper_probe_single_connector_modes,
>> +       .late_register = gud_connector_late_register,
>> +       .early_unregister = gud_connector_early_unregister,
>> +       .destroy = gud_connector_destroy,
>> +       .reset = gud_connector_reset,
>> +       .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
>> +       .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
>> +};
>> +
>> +/*
>> + * The tv.mode property is shared among the connectors and its enum names are
>> + * driver specific. This means that if more than one connector uses tv.mode,
>> + * the enum names has to be the same.
>> + */
>> +static int gud_connector_add_tv_mode(struct gud_device *gdrm,
>> +                                    struct drm_connector *connector, u64 val)
>> +{
>> +       unsigned int i, num_modes;
>> +       const char **modes;
>> +       size_t buf_len;
>> +       char *buf;
>> +       int ret;
>> +
>> +       num_modes = val >> GUD_CONNECTOR_TV_MODE_NUM_SHIFT;
>> +
>> +       if (!num_modes)
>> +               return -EINVAL;
>> +
>> +       buf_len = num_modes * GUD_CONNECTOR_TV_MODE_NAME_LEN;
>> +       modes = kmalloc_array(num_modes, sizeof(*modes), GFP_KERNEL);
>> +       buf = kmalloc(buf_len, GFP_KERNEL);
>> +       if (!modes || !buf) {
>> +               ret = -ENOMEM;
>> +               goto free;
>> +       }
>> +
>> +       ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_TV_MODE_VALUES,
>> +                         connector->index, buf, buf_len);
>> +       if (ret)
>> +               goto free;
>> +
>> +       for (i = 0; i < num_modes; i++)
>> +               modes[i] = &buf[i * GUD_CONNECTOR_TV_MODE_NAME_LEN];
>> +
>> +       ret = drm_mode_create_tv_properties(connector->dev, num_modes, modes);
>> +free:
>> +       kfree(modes);
>> +       kfree(buf);
>> +
>> +       return ret;
>> +}
>> +
>> +static struct drm_property *
>> +gud_connector_property_lookup(struct drm_connector *connector, u16 prop)
>> +{
>> +       struct drm_mode_config *config = &connector->dev->mode_config;
>> +
>> +       switch (prop) {
>> +       case GUD_PROPERTY_TV_LEFT_MARGIN:
>> +               return config->tv_left_margin_property;
>> +       case GUD_PROPERTY_TV_RIGHT_MARGIN:
>> +               return config->tv_right_margin_property;
>> +       case GUD_PROPERTY_TV_TOP_MARGIN:
>> +               return config->tv_top_margin_property;
>> +       case GUD_PROPERTY_TV_BOTTOM_MARGIN:
>> +               return config->tv_bottom_margin_property;
>> +       case GUD_PROPERTY_TV_MODE:
>> +               return config->tv_mode_property;
>> +       case GUD_PROPERTY_TV_BRIGHTNESS:
>> +               return config->tv_brightness_property;
>> +       case GUD_PROPERTY_TV_CONTRAST:
>> +               return config->tv_contrast_property;
>> +       case GUD_PROPERTY_TV_FLICKER_REDUCTION:
>> +               return config->tv_flicker_reduction_property;
>> +       case GUD_PROPERTY_TV_OVERSCAN:
>> +               return config->tv_overscan_property;
>> +       case GUD_PROPERTY_TV_SATURATION:
>> +               return config->tv_saturation_property;
>> +       case GUD_PROPERTY_TV_HUE:
>> +               return config->tv_hue_property;
>> +       default:
>> +               return ERR_PTR(-EINVAL);
>> +       }
>> +}
>> +
>> +static unsigned int *gud_connector_tv_state_val(u16 prop, struct drm_tv_connector_state *state)
>> +{
>> +       switch (prop) {
>> +       case GUD_PROPERTY_TV_LEFT_MARGIN:
>> +               return &state->margins.left;
>> +       case GUD_PROPERTY_TV_RIGHT_MARGIN:
>> +               return &state->margins.right;
>> +       case GUD_PROPERTY_TV_TOP_MARGIN:
>> +               return &state->margins.top;
>> +       case GUD_PROPERTY_TV_BOTTOM_MARGIN:
>> +               return &state->margins.bottom;
>> +       case GUD_PROPERTY_TV_MODE:
>> +               return &state->mode;
>> +       case GUD_PROPERTY_TV_BRIGHTNESS:
>> +               return &state->brightness;
>> +       case GUD_PROPERTY_TV_CONTRAST:
>> +               return &state->contrast;
>> +       case GUD_PROPERTY_TV_FLICKER_REDUCTION:
>> +               return &state->flicker_reduction;
>> +       case GUD_PROPERTY_TV_OVERSCAN:
>> +               return &state->overscan;
>> +       case GUD_PROPERTY_TV_SATURATION:
>> +               return &state->saturation;
>> +       case GUD_PROPERTY_TV_HUE:
>> +               return &state->hue;
>> +       default:
>> +               return ERR_PTR(-EINVAL);
>> +       }
>> +}
>> +
>> +static int gud_connector_add_properties(struct gud_device *gdrm, struct gud_connector *gconn,
>> +                                       unsigned int num_properties)
>> +{
>> +       struct drm_device *drm = &gdrm->drm;
>> +       struct drm_connector *connector = &gconn->connector;
>> +       struct gud_property_req *properties;
>> +       unsigned int i;
>> +       int ret;
>> +
>> +       gconn->properties = kcalloc(num_properties, sizeof(*gconn->properties), GFP_KERNEL);
>> +       if (!gconn->properties)
>> +               return -ENOMEM;
>> +
>> +       properties = kcalloc(num_properties, sizeof(*properties), GFP_KERNEL);
>> +       if (!properties)
>> +               return -ENOMEM;
>> +
>> +       ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_PROPERTIES, connector->index,
>> +                         properties, num_properties * sizeof(*properties));
>> +       if (ret)
>> +               goto out;
>> +
>> +       for (i = 0; i < num_properties; i++) {
>> +               u16 prop = le16_to_cpu(properties[i].prop);
>> +               u64 val = le64_to_cpu(properties[i].val);
>> +               struct drm_property *property;
>> +               unsigned int *state_val;
>> +
>> +               drm_dbg(drm, "property: %u = %llu(0x%llx)\n", prop, val, val);
>> +
>> +               switch (prop) {
>> +               case GUD_PROPERTY_TV_LEFT_MARGIN:
>> +                       fallthrough;
>> +               case GUD_PROPERTY_TV_RIGHT_MARGIN:
>> +                       fallthrough;
>> +               case GUD_PROPERTY_TV_TOP_MARGIN:
>> +                       fallthrough;
>> +               case GUD_PROPERTY_TV_BOTTOM_MARGIN:
>> +                       ret = drm_mode_create_tv_margin_properties(drm);
>> +                       if (ret)
>> +                               goto out;
>> +                       break;
>> +               case GUD_PROPERTY_TV_MODE:
>> +                       ret = gud_connector_add_tv_mode(gdrm, connector, val);
>> +                       if (ret)
>> +                               goto out;
>> +                       val = val & (BIT(GUD_CONNECTOR_TV_MODE_NUM_SHIFT) - 1);
>> +                       break;
>> +               case GUD_PROPERTY_TV_BRIGHTNESS:
>> +                       fallthrough;
>> +               case GUD_PROPERTY_TV_CONTRAST:
>> +                       fallthrough;
>> +               case GUD_PROPERTY_TV_FLICKER_REDUCTION:
>> +                       fallthrough;
>> +               case GUD_PROPERTY_TV_OVERSCAN:
>> +                       fallthrough;
>> +               case GUD_PROPERTY_TV_SATURATION:
>> +                       fallthrough;
>> +               case GUD_PROPERTY_TV_HUE:
>> +                       /* This is a no-op if already added. */
>> +                       ret = drm_mode_create_tv_properties(drm, 0, NULL);
>> +                       if (ret)
>> +                               goto out;
>> +                       break;
>> +               case GUD_PROPERTY_BACKLIGHT_BRIGHTNESS:
>> +                       if (val > 100) {
>> +                               ret = -EINVAL;
>> +                               goto out;
>> +                       }
>> +                       gconn->initial_brightness = val;
>> +                       break;
>> +               default:
>> +                       /* New ones might show up in future devices, skip those we don't know. */
>> +                       drm_dbg(drm, "Unknown property: %u\n", prop);
>> +                       continue;
>> +               }
>> +
>> +               gconn->properties[gconn->num_properties++] = prop;
>> +
>> +               if (prop == GUD_PROPERTY_BACKLIGHT_BRIGHTNESS)
>> +                       continue; /* not a DRM property */
>> +
>> +               property = gud_connector_property_lookup(connector, prop);
>> +               if (WARN_ON(IS_ERR(property)))
>> +                       continue;
>> +
>> +               state_val = gud_connector_tv_state_val(prop, &gconn->initial_tv_state);
>> +               if (WARN_ON(IS_ERR(state_val)))
>> +                       continue;
>> +
>> +               *state_val = val;
>> +               drm_object_attach_property(&connector->base, property, 0);
>> +       }
>> +out:
>> +       kfree(properties);
>> +
>> +       return ret;
>> +}
>> +
>> +int gud_connector_fill_properties(struct drm_connector *connector,
>> +                                 struct drm_connector_state *connector_state,
>> +                                 struct gud_property_req *properties)
>> +{
>> +       struct gud_connector *gconn;
>> +       unsigned int i;
>> +
>> +       gconn = to_gud_connector(connector);
>> +
>> +       /* Only interested in the count? */
>> +       if (!connector_state)
>> +               return gconn->num_properties;
>> +
>> +       for (i = 0; i < gconn->num_properties; i++) {
>> +               u16 prop = gconn->properties[i];
>> +               u64 val;
>> +
>> +               if (prop == GUD_PROPERTY_BACKLIGHT_BRIGHTNESS) {
>> +                       val = connector_state->tv.brightness;
>> +               } else {
>> +                       unsigned int *state_val;
>> +
>> +                       state_val = gud_connector_tv_state_val(prop, &connector_state->tv);
>> +                       if (WARN_ON_ONCE(IS_ERR(state_val)))
>> +                               return PTR_ERR(state_val);
>> +
>> +                       val = *state_val;
>> +               }
>> +
>> +               properties[i].prop = cpu_to_le16(prop);
>> +               properties[i].val = cpu_to_le64(val);
>> +       }
>> +
>> +       return gconn->num_properties;
>> +}
>> +
>> +int gud_connector_create(struct gud_device *gdrm, unsigned int index)
>> +{
>> +       struct gud_connector_descriptor_req desc;
>> +       struct drm_device *drm = &gdrm->drm;
>> +       struct gud_connector *gconn;
>> +       struct drm_connector *connector;
>> +       struct drm_encoder *encoder;
>> +       int ret, connector_type;
>> +       u32 flags;
>> +
>> +       ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR, index, &desc, sizeof(desc));
>> +       if (ret)
>> +               return ret;
>> +
>> +       gconn = kzalloc(sizeof(*gconn), GFP_KERNEL);
> 
> Would be nice to do that with drmm_, but we don't have the
> drmm_connector_alloc wrapper yet.
> 
>> +       if (!gconn)
>> +               return -ENOMEM;
>> +
>> +       gconn->initial_brightness = -ENODEV;
>> +       flags = le32_to_cpu(desc.flags);
>> +       connector = &gconn->connector;
>> +
>> +       drm_dbg(drm, "Connector: index=%u type=%u flags=0x%x num_properties=%u\n",
>> +               index, desc.connector_type, flags, desc.num_properties);
>> +
>> +       switch (desc.connector_type) {
>> +       case GUD_CONNECTOR_TYPE_PANEL:
>> +               connector_type = DRM_MODE_CONNECTOR_USB;
>> +               break;
>> +       case GUD_CONNECTOR_TYPE_VGA:
>> +               connector_type = DRM_MODE_CONNECTOR_VGA;
>> +               break;
>> +       case GUD_CONNECTOR_TYPE_DVI:
>> +               connector_type = DRM_MODE_CONNECTOR_DVID;
>> +               break;
>> +       case GUD_CONNECTOR_TYPE_COMPOSITE:
>> +               connector_type = DRM_MODE_CONNECTOR_Composite;
>> +               break;
>> +       case GUD_CONNECTOR_TYPE_SVIDEO:
>> +               connector_type = DRM_MODE_CONNECTOR_SVIDEO;
>> +               break;
>> +       case GUD_CONNECTOR_TYPE_COMPONENT:
>> +               connector_type = DRM_MODE_CONNECTOR_Component;
>> +               break;
>> +       case GUD_CONNECTOR_TYPE_DISPLAYPORT:
>> +               connector_type = DRM_MODE_CONNECTOR_DisplayPort;
>> +               break;
>> +       case GUD_CONNECTOR_TYPE_HDMI:
>> +               connector_type = DRM_MODE_CONNECTOR_HDMIA;
>> +               break;
>> +       default: /* future types */
>> +               connector_type = DRM_MODE_CONNECTOR_USB;
>> +               break;
>> +       };
>> +
>> +       drm_connector_helper_add(connector, &gud_connector_helper_funcs);
>> +       ret = drm_connector_init(drm, connector, &gud_connector_funcs, connector_type);
>> +       if (ret) {
>> +               kfree(connector);
>> +               return ret;
>> +       }
>> +
>> +       if (WARN_ON(connector->index != index))
>> +               return -EINVAL;
>> +
>> +       if (flags & GUD_CONNECTOR_FLAGS_POLL_STATUS)
>> +               connector->polled = (DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT);
>> +       if (flags & GUD_CONNECTOR_FLAGS_INTERLACE)
>> +               connector->interlace_allowed = true;
>> +       if (flags & GUD_CONNECTOR_FLAGS_DOUBLESCAN)
>> +               connector->doublescan_allowed = true;
>> +
>> +       if (desc.num_properties) {
>> +               ret = gud_connector_add_properties(gdrm, gconn, desc.num_properties);
>> +               if (ret) {
>> +                       dev_err(drm->dev, "Failed to add connector/%u properties\n", index);
>> +                       return ret;
>> +               }
>> +       }
>> +
>> +       /* The first connector is attached to the existing simple pipe encoder */
>> +       if (!connector->index) {
>> +               encoder = &gdrm->pipe.encoder;
>> +       } else {
>> +               encoder = &gconn->encoder;
>> +
>> +               ret = drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_NONE);
>> +               if (ret)
>> +                       return ret;
>> +
>> +               encoder->possible_crtcs = 1;
>> +       }
>> +
>> +       return drm_connector_attach_encoder(connector, encoder);
>> +}

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 3/3] drm: Add Generic USB Display driver
@ 2021-01-24 16:17       ` Noralf Trønnes
  0 siblings, 0 replies; 53+ messages in thread
From: Noralf Trønnes @ 2021-01-24 16:17 UTC (permalink / raw)
  To: Daniel Vetter
  Cc: hudson, markus, peter, USB list, dri-devel, Tyler Hardin,
	Lubomir Rintel, pontus.fuchs, Sam Ravnborg



Den 20.01.2021 19.02, skrev Daniel Vetter:
> On Wed, Jan 20, 2021 at 6:11 PM Noralf Trønnes <noralf@tronnes.org> wrote:
>>
>> This adds a generic USB display driver with the intention that it can be
>> used with future USB interfaced low end displays/adapters. The Linux
>> gadget device driver will serve as the canonical device implementation.
>>
>> The following DRM properties are supported:
>> - Plane rotation
>> - Connector TV properties
>>
>> There is also support for backlight brightness exposed as a backlight
>> device.
>>
>> Display modes can be made available to the host driver either as DRM
>> display modes or through EDID. If both are present, EDID is just passed
>> on to userspace.
>>
>> Performance is preferred over color depth, so if the device supports
>> RGB565, DRM_CAP_DUMB_PREFERRED_DEPTH will return 16.
>>
>> If the device transfer buffer can't fit an uncompressed framebuffer
>> update, the update is split up into parts that do fit.
>>
>> Optimal user experience is achieved by providing damage reports either by
>> setting FB_DAMAGE_CLIPS on pageflips or calling DRM_IOCTL_MODE_DIRTYFB.
>>
>> LZ4 compression is used if the device supports it.
>>
>> The driver supports a one bit monochrome transfer format: R1. This is not
>> implemented in the gadget driver. It is added in preparation for future
>> monochrome e-ink displays.
>>
>> The driver is MIT licensed to smooth the path for any BSD port of the
>> driver.
>>
>> v2:
>> - Use devm_drm_dev_alloc() and drmm_mode_config_init()
>> - drm_fbdev_generic_setup: Use preferred_bpp=0, 16 was a copy paste error
>> - The drm_backlight_helper is dropped, copy in the code
> 
> I think the backlight is still a bit problematic, since you're using
> kms locks within the backlight callbacks. Other display drivers use
> backlight within their kms locks. This means inconsistent locking
> rules, which upsets lockdep.
> 
> Since you're already handling brightness as a special case in many
> places I don't think it's a big shuffle:
> - add a mutex to the connector struct
> - move brightness value to connector struct, out of the connector_state
> - use the new mutex to protect backlight state both from modeset side
> (if needed, I'm not entirely sure about that) and the backlight side
> 

It's not enough to store the value I need to send it to the device as
well. Currently I send the entire state each time there's a change. To
continue that I would need to keep a copy of the state that I can use
when brightness changes. Or I can treat backlight as an exception and
add a USB control request just for backlight.

There is some special treatment of the backlight in the driver, but I
would really like to handle the backlight brightness through the atomic
machinery. I want to avoid special treatment of backlight in the USB
protocol.

I can avoid the lockdep problem by letting a worker commit the state and
schedule it from the backlight update callback. I'll do that unless you
see other issues with that approach.

Is it ok to take the connection_mutex lock in the get_brightness
callback to get to the connector state and the brightness value?

i915 takes that lock in intel_backlight_device_update_status() and
intel_backlight_device_get_brightness().

> Some more things below, but in general I'd say Acked-by: Daniel Vetter
> <daniel.vetter> fwiw (probably not so much).
> 

Thanks for taking a look, much appreciated.

Noralf.


>> diff --git a/drivers/gpu/drm/gud/gud_connector.c b/drivers/gpu/drm/gud/gud_connector.c
>> new file mode 100644
>> index 000000000000..a4b9bbf48e19
>> --- /dev/null
>> +++ b/drivers/gpu/drm/gud/gud_connector.c
>> @@ -0,0 +1,722 @@
>> +// SPDX-License-Identifier: MIT
>> +/*
>> + * Copyright 2020 Noralf Trønnes
>> + */
>> +
>> +#include <linux/backlight.h>
>> +
>> +#include <drm/drm_atomic.h>
>> +#include <drm/drm_atomic_state_helper.h>
>> +#include <drm/drm_connector.h>
>> +#include <drm/drm_drv.h>
>> +#include <drm/drm_encoder.h>
>> +#include <drm/drm_file.h>
>> +#include <drm/drm_modeset_helper_vtables.h>
>> +#include <drm/drm_print.h>
>> +#include <drm/drm_probe_helper.h>
>> +#include <drm/drm_simple_kms_helper.h>
>> +#include <drm/gud.h>
>> +
>> +#include "gud_internal.h"
>> +
>> +struct gud_connector {
>> +       struct drm_connector connector;
>> +       struct drm_encoder encoder;
>> +       struct backlight_device *backlight;
>> +
>> +       /* Supported properties */
>> +       u16 *properties;
>> +       unsigned int num_properties;
>> +
>> +       /* Initial gadget tv state if applicable, applied on state reset */
>> +       struct drm_tv_connector_state initial_tv_state;
>> +
>> +       /*
>> +        * Initial gadget backlight brightness if applicable, applied on state reset.
>> +        * The value -ENODEV is used to signal no backlight.
>> +        */
>> +       int initial_brightness;
>> +
>> +       unsigned int num_modes;
>> +       size_t edid_len;
>> +};
>> +
>> +static inline struct gud_connector *to_gud_connector(struct drm_connector *connector)
>> +{
>> +       return container_of(connector, struct gud_connector, connector);
>> +}
>> +
>> +static int gud_connector_backlight_update_status(struct backlight_device *bd)
>> +{
>> +       struct drm_connector *connector = bl_get_data(bd);
>> +       struct drm_connector_state *connector_state;
>> +       struct drm_device *dev = connector->dev;
>> +       struct drm_modeset_acquire_ctx ctx;
>> +       struct drm_atomic_state *state;
>> +       int ret;
>> +
>> +       state = drm_atomic_state_alloc(dev);
>> +       if (!state)
>> +               return -ENOMEM;
>> +
>> +       drm_modeset_acquire_init(&ctx, 0);
>> +       state->acquire_ctx = &ctx;
>> +retry:
>> +       connector_state = drm_atomic_get_connector_state(state, connector);
>> +       if (IS_ERR(connector_state)) {
>> +               ret = PTR_ERR(connector_state);
>> +               goto out;
>> +       }
>> +
>> +       /* Reuse tv.brightness to avoid having to subclass */
>> +       connector_state->tv.brightness = bd->props.brightness;
>> +
>> +       ret = drm_atomic_commit(state);
>> +out:
>> +       if (ret == -EDEADLK) {
>> +               drm_atomic_state_clear(state);
>> +               drm_modeset_backoff(&ctx);
>> +               goto retry;
>> +       }
>> +
>> +       drm_atomic_state_put(state);
>> +
>> +       drm_modeset_drop_locks(&ctx);
>> +       drm_modeset_acquire_fini(&ctx);
>> +
>> +       return ret;
>> +}
>> +
>> +static int gud_connector_backlight_get_brightness(struct backlight_device *bd)
>> +{
>> +       struct drm_connector *connector = bl_get_data(bd);
>> +       struct drm_device *dev = connector->dev;
>> +       int brightness;
>> +
>> +       drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
>> +       brightness = connector->state->tv.brightness;
>> +       drm_modeset_unlock(&dev->mode_config.connection_mutex);
>> +
>> +       return brightness;
>> +}
>> +
>> +static const struct backlight_ops gud_connector_backlight_ops = {
>> +       .get_brightness = gud_connector_backlight_get_brightness,
>> +       .update_status  = gud_connector_backlight_update_status,
>> +};
>> +
>> +static int gud_connector_backlight_register(struct gud_connector *gconn)
>> +{
>> +       struct drm_connector *connector = &gconn->connector;
>> +       struct backlight_device *bd;
>> +       const char *name;
>> +       const struct backlight_properties props = {
>> +               .type = BACKLIGHT_RAW,
>> +               .scale = BACKLIGHT_SCALE_NON_LINEAR,
>> +               .max_brightness = 100,
>> +       };
>> +
>> +       name = kasprintf(GFP_KERNEL, "card%d-%s-backlight",
>> +                        connector->dev->primary->index, connector->name);
>> +       if (!name)
>> +               return -ENOMEM;
>> +
>> +       bd = backlight_device_register(name, connector->kdev, connector,
>> +                                      &gud_connector_backlight_ops, &props);
>> +       kfree(name);
>> +       if (IS_ERR(bd))
>> +               return PTR_ERR(bd);
>> +
>> +       gconn->backlight = bd;
>> +
>> +       return 0;
>> +}
>> +
>> +static int gud_connector_status_request(struct drm_connector *connector)
>> +{
>> +       struct gud_connector *gconn = to_gud_connector(connector);
>> +       struct gud_device *gdrm = to_gud_device(connector->dev);
>> +       struct gud_connector_status_req req;
>> +       u16 num_modes, edid_len;
>> +       int ret;
>> +
>> +       ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_STATUS,
>> +                         connector->index, &req, sizeof(req));
>> +       if (ret)
>> +               return ret;
>> +
>> +       switch (req.status & GUD_CONNECTOR_STATUS_CONNECTED_MASK) {
>> +       case GUD_CONNECTOR_STATUS_DISCONNECTED:
>> +               ret = connector_status_disconnected;
>> +               break;
>> +       case GUD_CONNECTOR_STATUS_CONNECTED:
>> +               ret = connector_status_connected;
>> +               break;
>> +       default:
>> +               ret = connector_status_unknown;
>> +               break;
>> +       };
>> +
>> +       num_modes = le16_to_cpu(req.num_modes);
>> +       edid_len = le16_to_cpu(req.edid_len);
>> +
>> +       if (edid_len % EDID_LENGTH) {
>> +               drm_err(connector->dev, "%s: Invalid EDID size: %u\n", connector->name, edid_len);
>> +               edid_len = 0;
>> +       }
>> +
>> +       if (req.status & GUD_CONNECTOR_STATUS_CHANGED ||
>> +           gconn->num_modes != num_modes || gconn->edid_len != edid_len)
>> +               connector->epoch_counter += 1;
>> +
>> +       gconn->num_modes = num_modes;
>> +       gconn->edid_len = edid_len;
>> +
>> +       if (!num_modes && !edid_len && ret != connector_status_disconnected)
>> +               drm_dbg_kms(connector->dev, "%s: No modes or EDID.\n", connector->name);
>> +
>> +       return ret;
>> +}
>> +
>> +static int gud_connector_detect(struct drm_connector *connector,
>> +                               struct drm_modeset_acquire_ctx *ctx, bool force)
>> +{
>> +       struct gud_device *gdrm = to_gud_device(connector->dev);
>> +       int idx, ret;
>> +
>> +       if (!drm_dev_enter(connector->dev, &idx))
>> +               return connector_status_disconnected;
>> +
>> +       if (force) {
>> +               ret = gud_usb_set(gdrm, GUD_REQ_SET_CONNECTOR_FORCE_DETECT,
>> +                                 connector->index, NULL, 0);
>> +               if (ret) {
>> +                       ret = connector_status_unknown;
>> +                       goto exit;
>> +               }
>> +       }
>> +
>> +       ret = gud_connector_status_request(connector);
>> +       if (ret < 0)
>> +               ret = connector_status_unknown;
>> +exit:
>> +       drm_dev_exit(idx);
>> +
>> +       return ret;
>> +}
>> +
>> +struct gud_connector_get_edid_ctx {
>> +       struct gud_connector *gconn;
>> +       void *buf;
>> +};
>> +
>> +static int gud_connector_get_edid_block(void *data, u8 *buf, unsigned int block, size_t len)
>> +{
>> +       struct gud_connector_get_edid_ctx *ctx = data;
>> +       struct gud_connector *gconn = ctx->gconn;
>> +       size_t start = block * EDID_LENGTH;
>> +
>> +       if (start + len > gconn->edid_len)
>> +               return -1;
>> +
>> +       if (!block) {
>> +               struct gud_device *gdrm = to_gud_device(gconn->connector.dev);
>> +               int ret;
>> +
>> +               /* Check because drm_do_get_edid() will retry on failure */
>> +               if (!ctx->buf)
>> +                       ctx->buf = kmalloc(gconn->edid_len, GFP_KERNEL);
>> +               if (!ctx->buf)
>> +                       return -1;
>> +
>> +               ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_EDID, gconn->connector.index,
>> +                                 ctx->buf, gconn->edid_len);
>> +               if (ret)
>> +                       return -1;
>> +       }
>> +
>> +       memcpy(buf, ctx->buf + start, len);
>> +
>> +       return 0;
>> +}
>> +
>> +static int gud_connector_get_modes(struct drm_connector *connector)
>> +{
>> +       struct gud_connector *gconn = to_gud_connector(connector);
>> +       struct gud_device *gdrm = to_gud_device(connector->dev);
>> +       struct gud_connector_get_edid_ctx edid_ctx = {
>> +               .gconn = gconn,
>> +       };
>> +       struct gud_display_mode_req *reqmodes = NULL;
>> +       unsigned int i, num_modes = 0;
>> +       struct edid *edid = NULL;
>> +       bool edid_override;
>> +       int idx, ret;
>> +
>> +       if (!drm_dev_enter(connector->dev, &idx))
>> +               return 0;
>> +
>> +       if (connector->force) {
>> +               ret = gud_connector_status_request(connector);
>> +               if (ret < 0)
>> +                       goto out;
>> +       }
>> +
>> +       edid = drm_do_get_edid(connector, gud_connector_get_edid_block, &edid_ctx);
>> +       edid_override = edid && !edid_ctx.buf;
>> +       kfree(edid_ctx.buf);
>> +       drm_connector_update_edid_property(connector, edid);
>> +
>> +       if (!gconn->num_modes || edid_override) {
>> +               num_modes = drm_add_edid_modes(connector, edid);
>> +               goto out;
>> +       }
>> +
>> +       reqmodes = kmalloc_array(gconn->num_modes, sizeof(*reqmodes), GFP_KERNEL);
>> +       if (!reqmodes)
>> +               goto out;
>> +
>> +       ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_MODES, connector->index,
>> +                         reqmodes, gconn->num_modes * sizeof(*reqmodes));
>> +       if (ret)
>> +               goto out;
>> +
>> +       for (i = 0; i < gconn->num_modes; i++) {
>> +               struct drm_display_mode *mode;
>> +
>> +               mode = drm_mode_create(connector->dev);
>> +               if (!mode)
>> +                       goto out;
>> +
>> +               gud_to_display_mode(mode, &reqmodes[i]);
>> +               drm_mode_probed_add(connector, mode);
>> +               num_modes++;
>> +       }
>> +out:
>> +       kfree(reqmodes);
>> +       kfree(edid);
>> +       drm_dev_exit(idx);
>> +
>> +       return num_modes;
>> +}
>> +
>> +static int gud_connector_atomic_check(struct drm_connector *connector,
>> +                                     struct drm_atomic_state *state)
>> +{
>> +       struct drm_connector_state *new_state;
>> +       struct drm_crtc_state *new_crtc_state;
>> +       struct drm_connector_state *old_state;
>> +
>> +       new_state = drm_atomic_get_new_connector_state(state, connector);
>> +       if (!new_state->crtc)
>> +               return 0;
>> +
>> +       old_state = drm_atomic_get_old_connector_state(state, connector);
>> +       new_crtc_state = drm_atomic_get_new_crtc_state(state, new_state->crtc);
>> +
>> +       if (old_state->tv.margins.left != new_state->tv.margins.left ||
>> +           old_state->tv.margins.right != new_state->tv.margins.right ||
>> +           old_state->tv.margins.top != new_state->tv.margins.top ||
>> +           old_state->tv.margins.bottom != new_state->tv.margins.bottom ||
>> +           old_state->tv.mode != new_state->tv.mode ||
>> +           old_state->tv.brightness != new_state->tv.brightness ||
>> +           old_state->tv.contrast != new_state->tv.contrast ||
>> +           old_state->tv.flicker_reduction != new_state->tv.flicker_reduction ||
>> +           old_state->tv.overscan != new_state->tv.overscan ||
>> +           old_state->tv.saturation != new_state->tv.saturation ||
>> +           old_state->tv.hue != new_state->tv.hue)
>> +               new_crtc_state->connectors_changed = true;
>> +
>> +       return 0;
>> +}
>> +
>> +static const struct drm_connector_helper_funcs gud_connector_helper_funcs = {
>> +       .detect_ctx = gud_connector_detect,
>> +       .get_modes = gud_connector_get_modes,
>> +       .atomic_check = gud_connector_atomic_check,
>> +};
>> +
>> +static int gud_connector_late_register(struct drm_connector *connector)
>> +{
>> +       struct gud_connector *gconn = to_gud_connector(connector);
>> +
>> +       if (gconn->initial_brightness < 0)
>> +               return 0;
>> +
>> +       return gud_connector_backlight_register(gconn);
>> +}
>> +
>> +static void gud_connector_early_unregister(struct drm_connector *connector)
>> +{
>> +       struct gud_connector *gconn = to_gud_connector(connector);
>> +
>> +       backlight_device_unregister(gconn->backlight);
>> +}
>> +
>> +static void gud_connector_destroy(struct drm_connector *connector)
>> +{
>> +       struct gud_connector *gconn = to_gud_connector(connector);
>> +
>> +       drm_connector_cleanup(connector);
>> +       kfree(gconn->properties);
>> +       kfree(gconn);
>> +}
>> +
>> +static void gud_connector_reset(struct drm_connector *connector)
>> +{
>> +       struct gud_connector *gconn = to_gud_connector(connector);
>> +
>> +       drm_atomic_helper_connector_reset(connector);
>> +       connector->state->tv = gconn->initial_tv_state;
>> +       /* Set margins from command line */
>> +       drm_atomic_helper_connector_tv_reset(connector);
>> +       if (gconn->initial_brightness >= 0)
>> +               connector->state->tv.brightness = gconn->initial_brightness;
>> +}
>> +
>> +static const struct drm_connector_funcs gud_connector_funcs = {
>> +       .fill_modes = drm_helper_probe_single_connector_modes,
>> +       .late_register = gud_connector_late_register,
>> +       .early_unregister = gud_connector_early_unregister,
>> +       .destroy = gud_connector_destroy,
>> +       .reset = gud_connector_reset,
>> +       .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
>> +       .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
>> +};
>> +
>> +/*
>> + * The tv.mode property is shared among the connectors and its enum names are
>> + * driver specific. This means that if more than one connector uses tv.mode,
>> + * the enum names has to be the same.
>> + */
>> +static int gud_connector_add_tv_mode(struct gud_device *gdrm,
>> +                                    struct drm_connector *connector, u64 val)
>> +{
>> +       unsigned int i, num_modes;
>> +       const char **modes;
>> +       size_t buf_len;
>> +       char *buf;
>> +       int ret;
>> +
>> +       num_modes = val >> GUD_CONNECTOR_TV_MODE_NUM_SHIFT;
>> +
>> +       if (!num_modes)
>> +               return -EINVAL;
>> +
>> +       buf_len = num_modes * GUD_CONNECTOR_TV_MODE_NAME_LEN;
>> +       modes = kmalloc_array(num_modes, sizeof(*modes), GFP_KERNEL);
>> +       buf = kmalloc(buf_len, GFP_KERNEL);
>> +       if (!modes || !buf) {
>> +               ret = -ENOMEM;
>> +               goto free;
>> +       }
>> +
>> +       ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_TV_MODE_VALUES,
>> +                         connector->index, buf, buf_len);
>> +       if (ret)
>> +               goto free;
>> +
>> +       for (i = 0; i < num_modes; i++)
>> +               modes[i] = &buf[i * GUD_CONNECTOR_TV_MODE_NAME_LEN];
>> +
>> +       ret = drm_mode_create_tv_properties(connector->dev, num_modes, modes);
>> +free:
>> +       kfree(modes);
>> +       kfree(buf);
>> +
>> +       return ret;
>> +}
>> +
>> +static struct drm_property *
>> +gud_connector_property_lookup(struct drm_connector *connector, u16 prop)
>> +{
>> +       struct drm_mode_config *config = &connector->dev->mode_config;
>> +
>> +       switch (prop) {
>> +       case GUD_PROPERTY_TV_LEFT_MARGIN:
>> +               return config->tv_left_margin_property;
>> +       case GUD_PROPERTY_TV_RIGHT_MARGIN:
>> +               return config->tv_right_margin_property;
>> +       case GUD_PROPERTY_TV_TOP_MARGIN:
>> +               return config->tv_top_margin_property;
>> +       case GUD_PROPERTY_TV_BOTTOM_MARGIN:
>> +               return config->tv_bottom_margin_property;
>> +       case GUD_PROPERTY_TV_MODE:
>> +               return config->tv_mode_property;
>> +       case GUD_PROPERTY_TV_BRIGHTNESS:
>> +               return config->tv_brightness_property;
>> +       case GUD_PROPERTY_TV_CONTRAST:
>> +               return config->tv_contrast_property;
>> +       case GUD_PROPERTY_TV_FLICKER_REDUCTION:
>> +               return config->tv_flicker_reduction_property;
>> +       case GUD_PROPERTY_TV_OVERSCAN:
>> +               return config->tv_overscan_property;
>> +       case GUD_PROPERTY_TV_SATURATION:
>> +               return config->tv_saturation_property;
>> +       case GUD_PROPERTY_TV_HUE:
>> +               return config->tv_hue_property;
>> +       default:
>> +               return ERR_PTR(-EINVAL);
>> +       }
>> +}
>> +
>> +static unsigned int *gud_connector_tv_state_val(u16 prop, struct drm_tv_connector_state *state)
>> +{
>> +       switch (prop) {
>> +       case GUD_PROPERTY_TV_LEFT_MARGIN:
>> +               return &state->margins.left;
>> +       case GUD_PROPERTY_TV_RIGHT_MARGIN:
>> +               return &state->margins.right;
>> +       case GUD_PROPERTY_TV_TOP_MARGIN:
>> +               return &state->margins.top;
>> +       case GUD_PROPERTY_TV_BOTTOM_MARGIN:
>> +               return &state->margins.bottom;
>> +       case GUD_PROPERTY_TV_MODE:
>> +               return &state->mode;
>> +       case GUD_PROPERTY_TV_BRIGHTNESS:
>> +               return &state->brightness;
>> +       case GUD_PROPERTY_TV_CONTRAST:
>> +               return &state->contrast;
>> +       case GUD_PROPERTY_TV_FLICKER_REDUCTION:
>> +               return &state->flicker_reduction;
>> +       case GUD_PROPERTY_TV_OVERSCAN:
>> +               return &state->overscan;
>> +       case GUD_PROPERTY_TV_SATURATION:
>> +               return &state->saturation;
>> +       case GUD_PROPERTY_TV_HUE:
>> +               return &state->hue;
>> +       default:
>> +               return ERR_PTR(-EINVAL);
>> +       }
>> +}
>> +
>> +static int gud_connector_add_properties(struct gud_device *gdrm, struct gud_connector *gconn,
>> +                                       unsigned int num_properties)
>> +{
>> +       struct drm_device *drm = &gdrm->drm;
>> +       struct drm_connector *connector = &gconn->connector;
>> +       struct gud_property_req *properties;
>> +       unsigned int i;
>> +       int ret;
>> +
>> +       gconn->properties = kcalloc(num_properties, sizeof(*gconn->properties), GFP_KERNEL);
>> +       if (!gconn->properties)
>> +               return -ENOMEM;
>> +
>> +       properties = kcalloc(num_properties, sizeof(*properties), GFP_KERNEL);
>> +       if (!properties)
>> +               return -ENOMEM;
>> +
>> +       ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_PROPERTIES, connector->index,
>> +                         properties, num_properties * sizeof(*properties));
>> +       if (ret)
>> +               goto out;
>> +
>> +       for (i = 0; i < num_properties; i++) {
>> +               u16 prop = le16_to_cpu(properties[i].prop);
>> +               u64 val = le64_to_cpu(properties[i].val);
>> +               struct drm_property *property;
>> +               unsigned int *state_val;
>> +
>> +               drm_dbg(drm, "property: %u = %llu(0x%llx)\n", prop, val, val);
>> +
>> +               switch (prop) {
>> +               case GUD_PROPERTY_TV_LEFT_MARGIN:
>> +                       fallthrough;
>> +               case GUD_PROPERTY_TV_RIGHT_MARGIN:
>> +                       fallthrough;
>> +               case GUD_PROPERTY_TV_TOP_MARGIN:
>> +                       fallthrough;
>> +               case GUD_PROPERTY_TV_BOTTOM_MARGIN:
>> +                       ret = drm_mode_create_tv_margin_properties(drm);
>> +                       if (ret)
>> +                               goto out;
>> +                       break;
>> +               case GUD_PROPERTY_TV_MODE:
>> +                       ret = gud_connector_add_tv_mode(gdrm, connector, val);
>> +                       if (ret)
>> +                               goto out;
>> +                       val = val & (BIT(GUD_CONNECTOR_TV_MODE_NUM_SHIFT) - 1);
>> +                       break;
>> +               case GUD_PROPERTY_TV_BRIGHTNESS:
>> +                       fallthrough;
>> +               case GUD_PROPERTY_TV_CONTRAST:
>> +                       fallthrough;
>> +               case GUD_PROPERTY_TV_FLICKER_REDUCTION:
>> +                       fallthrough;
>> +               case GUD_PROPERTY_TV_OVERSCAN:
>> +                       fallthrough;
>> +               case GUD_PROPERTY_TV_SATURATION:
>> +                       fallthrough;
>> +               case GUD_PROPERTY_TV_HUE:
>> +                       /* This is a no-op if already added. */
>> +                       ret = drm_mode_create_tv_properties(drm, 0, NULL);
>> +                       if (ret)
>> +                               goto out;
>> +                       break;
>> +               case GUD_PROPERTY_BACKLIGHT_BRIGHTNESS:
>> +                       if (val > 100) {
>> +                               ret = -EINVAL;
>> +                               goto out;
>> +                       }
>> +                       gconn->initial_brightness = val;
>> +                       break;
>> +               default:
>> +                       /* New ones might show up in future devices, skip those we don't know. */
>> +                       drm_dbg(drm, "Unknown property: %u\n", prop);
>> +                       continue;
>> +               }
>> +
>> +               gconn->properties[gconn->num_properties++] = prop;
>> +
>> +               if (prop == GUD_PROPERTY_BACKLIGHT_BRIGHTNESS)
>> +                       continue; /* not a DRM property */
>> +
>> +               property = gud_connector_property_lookup(connector, prop);
>> +               if (WARN_ON(IS_ERR(property)))
>> +                       continue;
>> +
>> +               state_val = gud_connector_tv_state_val(prop, &gconn->initial_tv_state);
>> +               if (WARN_ON(IS_ERR(state_val)))
>> +                       continue;
>> +
>> +               *state_val = val;
>> +               drm_object_attach_property(&connector->base, property, 0);
>> +       }
>> +out:
>> +       kfree(properties);
>> +
>> +       return ret;
>> +}
>> +
>> +int gud_connector_fill_properties(struct drm_connector *connector,
>> +                                 struct drm_connector_state *connector_state,
>> +                                 struct gud_property_req *properties)
>> +{
>> +       struct gud_connector *gconn;
>> +       unsigned int i;
>> +
>> +       gconn = to_gud_connector(connector);
>> +
>> +       /* Only interested in the count? */
>> +       if (!connector_state)
>> +               return gconn->num_properties;
>> +
>> +       for (i = 0; i < gconn->num_properties; i++) {
>> +               u16 prop = gconn->properties[i];
>> +               u64 val;
>> +
>> +               if (prop == GUD_PROPERTY_BACKLIGHT_BRIGHTNESS) {
>> +                       val = connector_state->tv.brightness;
>> +               } else {
>> +                       unsigned int *state_val;
>> +
>> +                       state_val = gud_connector_tv_state_val(prop, &connector_state->tv);
>> +                       if (WARN_ON_ONCE(IS_ERR(state_val)))
>> +                               return PTR_ERR(state_val);
>> +
>> +                       val = *state_val;
>> +               }
>> +
>> +               properties[i].prop = cpu_to_le16(prop);
>> +               properties[i].val = cpu_to_le64(val);
>> +       }
>> +
>> +       return gconn->num_properties;
>> +}
>> +
>> +int gud_connector_create(struct gud_device *gdrm, unsigned int index)
>> +{
>> +       struct gud_connector_descriptor_req desc;
>> +       struct drm_device *drm = &gdrm->drm;
>> +       struct gud_connector *gconn;
>> +       struct drm_connector *connector;
>> +       struct drm_encoder *encoder;
>> +       int ret, connector_type;
>> +       u32 flags;
>> +
>> +       ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR, index, &desc, sizeof(desc));
>> +       if (ret)
>> +               return ret;
>> +
>> +       gconn = kzalloc(sizeof(*gconn), GFP_KERNEL);
> 
> Would be nice to do that with drmm_, but we don't have the
> drmm_connector_alloc wrapper yet.
> 
>> +       if (!gconn)
>> +               return -ENOMEM;
>> +
>> +       gconn->initial_brightness = -ENODEV;
>> +       flags = le32_to_cpu(desc.flags);
>> +       connector = &gconn->connector;
>> +
>> +       drm_dbg(drm, "Connector: index=%u type=%u flags=0x%x num_properties=%u\n",
>> +               index, desc.connector_type, flags, desc.num_properties);
>> +
>> +       switch (desc.connector_type) {
>> +       case GUD_CONNECTOR_TYPE_PANEL:
>> +               connector_type = DRM_MODE_CONNECTOR_USB;
>> +               break;
>> +       case GUD_CONNECTOR_TYPE_VGA:
>> +               connector_type = DRM_MODE_CONNECTOR_VGA;
>> +               break;
>> +       case GUD_CONNECTOR_TYPE_DVI:
>> +               connector_type = DRM_MODE_CONNECTOR_DVID;
>> +               break;
>> +       case GUD_CONNECTOR_TYPE_COMPOSITE:
>> +               connector_type = DRM_MODE_CONNECTOR_Composite;
>> +               break;
>> +       case GUD_CONNECTOR_TYPE_SVIDEO:
>> +               connector_type = DRM_MODE_CONNECTOR_SVIDEO;
>> +               break;
>> +       case GUD_CONNECTOR_TYPE_COMPONENT:
>> +               connector_type = DRM_MODE_CONNECTOR_Component;
>> +               break;
>> +       case GUD_CONNECTOR_TYPE_DISPLAYPORT:
>> +               connector_type = DRM_MODE_CONNECTOR_DisplayPort;
>> +               break;
>> +       case GUD_CONNECTOR_TYPE_HDMI:
>> +               connector_type = DRM_MODE_CONNECTOR_HDMIA;
>> +               break;
>> +       default: /* future types */
>> +               connector_type = DRM_MODE_CONNECTOR_USB;
>> +               break;
>> +       };
>> +
>> +       drm_connector_helper_add(connector, &gud_connector_helper_funcs);
>> +       ret = drm_connector_init(drm, connector, &gud_connector_funcs, connector_type);
>> +       if (ret) {
>> +               kfree(connector);
>> +               return ret;
>> +       }
>> +
>> +       if (WARN_ON(connector->index != index))
>> +               return -EINVAL;
>> +
>> +       if (flags & GUD_CONNECTOR_FLAGS_POLL_STATUS)
>> +               connector->polled = (DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT);
>> +       if (flags & GUD_CONNECTOR_FLAGS_INTERLACE)
>> +               connector->interlace_allowed = true;
>> +       if (flags & GUD_CONNECTOR_FLAGS_DOUBLESCAN)
>> +               connector->doublescan_allowed = true;
>> +
>> +       if (desc.num_properties) {
>> +               ret = gud_connector_add_properties(gdrm, gconn, desc.num_properties);
>> +               if (ret) {
>> +                       dev_err(drm->dev, "Failed to add connector/%u properties\n", index);
>> +                       return ret;
>> +               }
>> +       }
>> +
>> +       /* The first connector is attached to the existing simple pipe encoder */
>> +       if (!connector->index) {
>> +               encoder = &gdrm->pipe.encoder;
>> +       } else {
>> +               encoder = &gconn->encoder;
>> +
>> +               ret = drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_NONE);
>> +               if (ret)
>> +                       return ret;
>> +
>> +               encoder->possible_crtcs = 1;
>> +       }
>> +
>> +       return drm_connector_attach_encoder(connector, encoder);
>> +}
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 0/3] Generic USB Display driver
  2021-01-20 17:00 ` Noralf Trønnes
@ 2021-01-24 18:38   ` Lubomir Rintel
  -1 siblings, 0 replies; 53+ messages in thread
From: Lubomir Rintel @ 2021-01-24 18:38 UTC (permalink / raw)
  To: Noralf Trønnes
  Cc: dri-devel, linux-usb, sam, peter, markus, pontus.fuchs, hudson, th020394

On Wed, Jan 20, 2021 at 06:00:30PM +0100, Noralf Trønnes wrote:
> Hi,
> 
> A while back I had the idea to turn a Raspberry Pi Zero into a $5
> USB to HDMI/SDTV/DSI/DPI display adapter.
> 
> The reason for calling it 'Generic' is so anyone can make a USB
> display/adapter against this driver, all that's needed is to add a USB
> vid:pid.
> 
> Unfortunately I've had some compounding health problems that have
> severally limited the time I can spend in front of a computer. For this
> reason I've decided to keep the gadget driver out-of-tree and focus on
> getting the host driver merged first.
> 
> See the wiki[1] for more information and images for the Raspberry Pi
> Zero/4.
> 
> One big change this time is that I've followed Peter Stuge's advice to
> not let DRM stuff leak into the USB protocol. This has made the protocol
> easier to understand just from reading the header file.
> 
> Noralf.
> 
> [1] https://github.com/notro/gud/wiki

The patch set:

Tested-by: Lubomir Rintel <lkundrak@v3.sk>

Works like a charm with this board [1], though it didn't impress the girls
as much as I hoped. Code here [2], picture here [3].

[1] https://www.banggood.com/LILYGO-TTGO-T-Display-GD32-RISC-V-32-bit-Core-Minimal-Development-Board-1_14-IPS-p-1652870.html?rmmds=search&cur_warehouse=CN
[2] https://github.com/hackerspace/libopencm3-gf32v-examples/commit/7ef51b31b9
[3] https://people.freedesktop.org/~lkundrak/lilygo.jpeg

Had to apply a fix for the drm_connector_enum_list[] ommission I mentioned
elsewhere, and that I've now noticed you've noted previously.

Take care
Lubo

> 
> 
> Noralf Trønnes (3):
>   drm/uapi: Add USB connector type
>   drm/probe-helper: Check epoch counter in output_poll_execute()
>   drm: Add Generic USB Display driver
> 
>  MAINTAINERS                         |   8 +
>  drivers/gpu/drm/Kconfig             |   2 +
>  drivers/gpu/drm/Makefile            |   1 +
>  drivers/gpu/drm/drm_probe_helper.c  |   7 +-
>  drivers/gpu/drm/gud/Kconfig         |  14 +
>  drivers/gpu/drm/gud/Makefile        |   4 +
>  drivers/gpu/drm/gud/gud_connector.c | 722 ++++++++++++++++++++++++++++
>  drivers/gpu/drm/gud/gud_drv.c       | 620 ++++++++++++++++++++++++
>  drivers/gpu/drm/gud/gud_internal.h  | 148 ++++++
>  drivers/gpu/drm/gud/gud_pipe.c      | 472 ++++++++++++++++++
>  include/drm/gud.h                   | 356 ++++++++++++++
>  include/uapi/drm/drm_mode.h         |   1 +
>  12 files changed, 2354 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/gpu/drm/gud/Kconfig
>  create mode 100644 drivers/gpu/drm/gud/Makefile
>  create mode 100644 drivers/gpu/drm/gud/gud_connector.c
>  create mode 100644 drivers/gpu/drm/gud/gud_drv.c
>  create mode 100644 drivers/gpu/drm/gud/gud_internal.h
>  create mode 100644 drivers/gpu/drm/gud/gud_pipe.c
>  create mode 100644 include/drm/gud.h
> 
> -- 
> 2.23.0
> 

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 0/3] Generic USB Display driver
@ 2021-01-24 18:38   ` Lubomir Rintel
  0 siblings, 0 replies; 53+ messages in thread
From: Lubomir Rintel @ 2021-01-24 18:38 UTC (permalink / raw)
  To: Noralf Trønnes
  Cc: hudson, markus, sam, linux-usb, dri-devel, th020394, pontus.fuchs, peter

On Wed, Jan 20, 2021 at 06:00:30PM +0100, Noralf Trønnes wrote:
> Hi,
> 
> A while back I had the idea to turn a Raspberry Pi Zero into a $5
> USB to HDMI/SDTV/DSI/DPI display adapter.
> 
> The reason for calling it 'Generic' is so anyone can make a USB
> display/adapter against this driver, all that's needed is to add a USB
> vid:pid.
> 
> Unfortunately I've had some compounding health problems that have
> severally limited the time I can spend in front of a computer. For this
> reason I've decided to keep the gadget driver out-of-tree and focus on
> getting the host driver merged first.
> 
> See the wiki[1] for more information and images for the Raspberry Pi
> Zero/4.
> 
> One big change this time is that I've followed Peter Stuge's advice to
> not let DRM stuff leak into the USB protocol. This has made the protocol
> easier to understand just from reading the header file.
> 
> Noralf.
> 
> [1] https://github.com/notro/gud/wiki

The patch set:

Tested-by: Lubomir Rintel <lkundrak@v3.sk>

Works like a charm with this board [1], though it didn't impress the girls
as much as I hoped. Code here [2], picture here [3].

[1] https://www.banggood.com/LILYGO-TTGO-T-Display-GD32-RISC-V-32-bit-Core-Minimal-Development-Board-1_14-IPS-p-1652870.html?rmmds=search&cur_warehouse=CN
[2] https://github.com/hackerspace/libopencm3-gf32v-examples/commit/7ef51b31b9
[3] https://people.freedesktop.org/~lkundrak/lilygo.jpeg

Had to apply a fix for the drm_connector_enum_list[] ommission I mentioned
elsewhere, and that I've now noticed you've noted previously.

Take care
Lubo

> 
> 
> Noralf Trønnes (3):
>   drm/uapi: Add USB connector type
>   drm/probe-helper: Check epoch counter in output_poll_execute()
>   drm: Add Generic USB Display driver
> 
>  MAINTAINERS                         |   8 +
>  drivers/gpu/drm/Kconfig             |   2 +
>  drivers/gpu/drm/Makefile            |   1 +
>  drivers/gpu/drm/drm_probe_helper.c  |   7 +-
>  drivers/gpu/drm/gud/Kconfig         |  14 +
>  drivers/gpu/drm/gud/Makefile        |   4 +
>  drivers/gpu/drm/gud/gud_connector.c | 722 ++++++++++++++++++++++++++++
>  drivers/gpu/drm/gud/gud_drv.c       | 620 ++++++++++++++++++++++++
>  drivers/gpu/drm/gud/gud_internal.h  | 148 ++++++
>  drivers/gpu/drm/gud/gud_pipe.c      | 472 ++++++++++++++++++
>  include/drm/gud.h                   | 356 ++++++++++++++
>  include/uapi/drm/drm_mode.h         |   1 +
>  12 files changed, 2354 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/gpu/drm/gud/Kconfig
>  create mode 100644 drivers/gpu/drm/gud/Makefile
>  create mode 100644 drivers/gpu/drm/gud/gud_connector.c
>  create mode 100644 drivers/gpu/drm/gud/gud_drv.c
>  create mode 100644 drivers/gpu/drm/gud/gud_internal.h
>  create mode 100644 drivers/gpu/drm/gud/gud_pipe.c
>  create mode 100644 include/drm/gud.h
> 
> -- 
> 2.23.0
> 
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 0/3] Generic USB Display driver
  2021-01-24 18:38   ` Lubomir Rintel
@ 2021-01-24 20:51     ` Noralf Trønnes
  -1 siblings, 0 replies; 53+ messages in thread
From: Noralf Trønnes @ 2021-01-24 20:51 UTC (permalink / raw)
  To: Lubomir Rintel
  Cc: dri-devel, linux-usb, sam, peter, markus, pontus.fuchs, hudson, th020394



Den 24.01.2021 19.38, skrev Lubomir Rintel:
> On Wed, Jan 20, 2021 at 06:00:30PM +0100, Noralf Trønnes wrote:
>> Hi,
>>
>> A while back I had the idea to turn a Raspberry Pi Zero into a $5
>> USB to HDMI/SDTV/DSI/DPI display adapter.
>>
>> The reason for calling it 'Generic' is so anyone can make a USB
>> display/adapter against this driver, all that's needed is to add a USB
>> vid:pid.
>>
>> Unfortunately I've had some compounding health problems that have
>> severally limited the time I can spend in front of a computer. For this
>> reason I've decided to keep the gadget driver out-of-tree and focus on
>> getting the host driver merged first.
>>
>> See the wiki[1] for more information and images for the Raspberry Pi
>> Zero/4.
>>
>> One big change this time is that I've followed Peter Stuge's advice to
>> not let DRM stuff leak into the USB protocol. This has made the protocol
>> easier to understand just from reading the header file.
>>
>> Noralf.
>>
>> [1] https://github.com/notro/gud/wiki
> 
> The patch set:
> 
> Tested-by: Lubomir Rintel <lkundrak@v3.sk>
> 
> Works like a charm with this board [1], though it didn't impress the girls
> as much as I hoped. Code here [2], picture here [3].
> 

I have wondered what color display resolution it is possible to drive
over USB full speed. I can understand that your PoC wasn't that
impressive since it doesn't use DMA to drive the SPI bus.

The new $4 Raspberry Pi Pico that came out this week looks interesting
as a USB interface board for tiny panels. It can drive DPI panels
directly, has 2 cores @133MHz, 264K SRAM and USB full speed. Maybe lz4
decompression is even possible. Another good thing is that the board
will be around for a long time.

Thanks for testing, I have limited bandwith these days so I couldn't do
a test on an MCU myself.

Noralf.

> [1] https://www.banggood.com/LILYGO-TTGO-T-Display-GD32-RISC-V-32-bit-Core-Minimal-Development-Board-1_14-IPS-p-1652870.html?rmmds=search&cur_warehouse=CN
> [2] https://github.com/hackerspace/libopencm3-gf32v-examples/commit/7ef51b31b9
> [3] https://people.freedesktop.org/~lkundrak/lilygo.jpeg
> 
> Had to apply a fix for the drm_connector_enum_list[] ommission I mentioned
> elsewhere, and that I've now noticed you've noted previously.
> 
> Take care
> Lubo
> 
>>
>>
>> Noralf Trønnes (3):
>>   drm/uapi: Add USB connector type
>>   drm/probe-helper: Check epoch counter in output_poll_execute()
>>   drm: Add Generic USB Display driver
>>
>>  MAINTAINERS                         |   8 +
>>  drivers/gpu/drm/Kconfig             |   2 +
>>  drivers/gpu/drm/Makefile            |   1 +
>>  drivers/gpu/drm/drm_probe_helper.c  |   7 +-
>>  drivers/gpu/drm/gud/Kconfig         |  14 +
>>  drivers/gpu/drm/gud/Makefile        |   4 +
>>  drivers/gpu/drm/gud/gud_connector.c | 722 ++++++++++++++++++++++++++++
>>  drivers/gpu/drm/gud/gud_drv.c       | 620 ++++++++++++++++++++++++
>>  drivers/gpu/drm/gud/gud_internal.h  | 148 ++++++
>>  drivers/gpu/drm/gud/gud_pipe.c      | 472 ++++++++++++++++++
>>  include/drm/gud.h                   | 356 ++++++++++++++
>>  include/uapi/drm/drm_mode.h         |   1 +
>>  12 files changed, 2354 insertions(+), 1 deletion(-)
>>  create mode 100644 drivers/gpu/drm/gud/Kconfig
>>  create mode 100644 drivers/gpu/drm/gud/Makefile
>>  create mode 100644 drivers/gpu/drm/gud/gud_connector.c
>>  create mode 100644 drivers/gpu/drm/gud/gud_drv.c
>>  create mode 100644 drivers/gpu/drm/gud/gud_internal.h
>>  create mode 100644 drivers/gpu/drm/gud/gud_pipe.c
>>  create mode 100644 include/drm/gud.h
>>
>> -- 
>> 2.23.0
>>
> 

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 0/3] Generic USB Display driver
@ 2021-01-24 20:51     ` Noralf Trønnes
  0 siblings, 0 replies; 53+ messages in thread
From: Noralf Trønnes @ 2021-01-24 20:51 UTC (permalink / raw)
  To: Lubomir Rintel
  Cc: hudson, markus, sam, linux-usb, dri-devel, th020394, pontus.fuchs, peter



Den 24.01.2021 19.38, skrev Lubomir Rintel:
> On Wed, Jan 20, 2021 at 06:00:30PM +0100, Noralf Trønnes wrote:
>> Hi,
>>
>> A while back I had the idea to turn a Raspberry Pi Zero into a $5
>> USB to HDMI/SDTV/DSI/DPI display adapter.
>>
>> The reason for calling it 'Generic' is so anyone can make a USB
>> display/adapter against this driver, all that's needed is to add a USB
>> vid:pid.
>>
>> Unfortunately I've had some compounding health problems that have
>> severally limited the time I can spend in front of a computer. For this
>> reason I've decided to keep the gadget driver out-of-tree and focus on
>> getting the host driver merged first.
>>
>> See the wiki[1] for more information and images for the Raspberry Pi
>> Zero/4.
>>
>> One big change this time is that I've followed Peter Stuge's advice to
>> not let DRM stuff leak into the USB protocol. This has made the protocol
>> easier to understand just from reading the header file.
>>
>> Noralf.
>>
>> [1] https://github.com/notro/gud/wiki
> 
> The patch set:
> 
> Tested-by: Lubomir Rintel <lkundrak@v3.sk>
> 
> Works like a charm with this board [1], though it didn't impress the girls
> as much as I hoped. Code here [2], picture here [3].
> 

I have wondered what color display resolution it is possible to drive
over USB full speed. I can understand that your PoC wasn't that
impressive since it doesn't use DMA to drive the SPI bus.

The new $4 Raspberry Pi Pico that came out this week looks interesting
as a USB interface board for tiny panels. It can drive DPI panels
directly, has 2 cores @133MHz, 264K SRAM and USB full speed. Maybe lz4
decompression is even possible. Another good thing is that the board
will be around for a long time.

Thanks for testing, I have limited bandwith these days so I couldn't do
a test on an MCU myself.

Noralf.

> [1] https://www.banggood.com/LILYGO-TTGO-T-Display-GD32-RISC-V-32-bit-Core-Minimal-Development-Board-1_14-IPS-p-1652870.html?rmmds=search&cur_warehouse=CN
> [2] https://github.com/hackerspace/libopencm3-gf32v-examples/commit/7ef51b31b9
> [3] https://people.freedesktop.org/~lkundrak/lilygo.jpeg
> 
> Had to apply a fix for the drm_connector_enum_list[] ommission I mentioned
> elsewhere, and that I've now noticed you've noted previously.
> 
> Take care
> Lubo
> 
>>
>>
>> Noralf Trønnes (3):
>>   drm/uapi: Add USB connector type
>>   drm/probe-helper: Check epoch counter in output_poll_execute()
>>   drm: Add Generic USB Display driver
>>
>>  MAINTAINERS                         |   8 +
>>  drivers/gpu/drm/Kconfig             |   2 +
>>  drivers/gpu/drm/Makefile            |   1 +
>>  drivers/gpu/drm/drm_probe_helper.c  |   7 +-
>>  drivers/gpu/drm/gud/Kconfig         |  14 +
>>  drivers/gpu/drm/gud/Makefile        |   4 +
>>  drivers/gpu/drm/gud/gud_connector.c | 722 ++++++++++++++++++++++++++++
>>  drivers/gpu/drm/gud/gud_drv.c       | 620 ++++++++++++++++++++++++
>>  drivers/gpu/drm/gud/gud_internal.h  | 148 ++++++
>>  drivers/gpu/drm/gud/gud_pipe.c      | 472 ++++++++++++++++++
>>  include/drm/gud.h                   | 356 ++++++++++++++
>>  include/uapi/drm/drm_mode.h         |   1 +
>>  12 files changed, 2354 insertions(+), 1 deletion(-)
>>  create mode 100644 drivers/gpu/drm/gud/Kconfig
>>  create mode 100644 drivers/gpu/drm/gud/Makefile
>>  create mode 100644 drivers/gpu/drm/gud/gud_connector.c
>>  create mode 100644 drivers/gpu/drm/gud/gud_drv.c
>>  create mode 100644 drivers/gpu/drm/gud/gud_internal.h
>>  create mode 100644 drivers/gpu/drm/gud/gud_pipe.c
>>  create mode 100644 include/drm/gud.h
>>
>> -- 
>> 2.23.0
>>
> 
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 0/3] Generic USB Display driver
  2021-01-24 20:51     ` Noralf Trønnes
@ 2021-02-21 11:14       ` Noralf Trønnes
  -1 siblings, 0 replies; 53+ messages in thread
From: Noralf Trønnes @ 2021-02-21 11:14 UTC (permalink / raw)
  To: Lubomir Rintel
  Cc: hudson, markus, sam, linux-usb, dri-devel, th020394, pontus.fuchs, peter



Den 24.01.2021 21.51, skrev Noralf Trønnes:
> 
> 
> Den 24.01.2021 19.38, skrev Lubomir Rintel:
>> On Wed, Jan 20, 2021 at 06:00:30PM +0100, Noralf Trønnes wrote:
>>> Hi,
>>>
>>> A while back I had the idea to turn a Raspberry Pi Zero into a $5
>>> USB to HDMI/SDTV/DSI/DPI display adapter.
>>>
>>> The reason for calling it 'Generic' is so anyone can make a USB
>>> display/adapter against this driver, all that's needed is to add a USB
>>> vid:pid.
>>>
>>> Unfortunately I've had some compounding health problems that have
>>> severally limited the time I can spend in front of a computer. For this
>>> reason I've decided to keep the gadget driver out-of-tree and focus on
>>> getting the host driver merged first.
>>>
>>> See the wiki[1] for more information and images for the Raspberry Pi
>>> Zero/4.
>>>
>>> One big change this time is that I've followed Peter Stuge's advice to
>>> not let DRM stuff leak into the USB protocol. This has made the protocol
>>> easier to understand just from reading the header file.
>>>
>>> Noralf.
>>>
>>> [1] https://github.com/notro/gud/wiki
>>
>> The patch set:
>>
>> Tested-by: Lubomir Rintel <lkundrak@v3.sk>
>>
>> Works like a charm with this board [1], though it didn't impress the girls
>> as much as I hoped. Code here [2], picture here [3].
>>
> 
> I have wondered what color display resolution it is possible to drive
> over USB full speed. I can understand that your PoC wasn't that
> impressive since it doesn't use DMA to drive the SPI bus.
> 

I have now done a Raspberry Pi Pico implementation and driving SPI using
DMA was just marginally faster than letting the CPU fill the FIFO. Maybe
I shouldn't be so suprised since the CPU has nothing else to do, but
even so I didn't expect this. But then again I have very little
experience with microcontrollers.

I have the same size display[1] as you 240x135 and my display was quite
snappy (using X windows!), I even added lz4 decompression support. I
haven't done much testing so I can't say how much the actual improvement
is with the compression. The USB double buffering I was hoping for
didn't pan out, the bulk endpoint can only do 64 byte packest (ISO is
512), so I ended up storing the packets and then push the frame in its
entirety to the display. The Pico has 264kB of ram so I can afford to
have a framebuffer and a decompression buffer for this tiny display. My
target display is 320x240 and that means I can't use 2 buffers, so not
sure how that goes.

[1] https://shop.pimoroni.com/products/pico-display-pack

Noralf.

> The new $4 Raspberry Pi Pico that came out this week looks interesting
> as a USB interface board for tiny panels. It can drive DPI panels
> directly, has 2 cores @133MHz, 264K SRAM and USB full speed. Maybe lz4
> decompression is even possible. Another good thing is that the board
> will be around for a long time.
> 
> Thanks for testing, I have limited bandwith these days so I couldn't do
> a test on an MCU myself.
> 
> Noralf.
> 
>> [1] https://www.banggood.com/LILYGO-TTGO-T-Display-GD32-RISC-V-32-bit-Core-Minimal-Development-Board-1_14-IPS-p-1652870.html?rmmds=search&cur_warehouse=CN
>> [2] https://github.com/hackerspace/libopencm3-gf32v-examples/commit/7ef51b31b9
>> [3] https://people.freedesktop.org/~lkundrak/lilygo.jpeg
>>
>> Had to apply a fix for the drm_connector_enum_list[] ommission I mentioned
>> elsewhere, and that I've now noticed you've noted previously.
>>
>> Take care
>> Lubo
>>

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 0/3] Generic USB Display driver
@ 2021-02-21 11:14       ` Noralf Trønnes
  0 siblings, 0 replies; 53+ messages in thread
From: Noralf Trønnes @ 2021-02-21 11:14 UTC (permalink / raw)
  To: Lubomir Rintel
  Cc: hudson, markus, peter, linux-usb, dri-devel, th020394, pontus.fuchs, sam



Den 24.01.2021 21.51, skrev Noralf Trønnes:
> 
> 
> Den 24.01.2021 19.38, skrev Lubomir Rintel:
>> On Wed, Jan 20, 2021 at 06:00:30PM +0100, Noralf Trønnes wrote:
>>> Hi,
>>>
>>> A while back I had the idea to turn a Raspberry Pi Zero into a $5
>>> USB to HDMI/SDTV/DSI/DPI display adapter.
>>>
>>> The reason for calling it 'Generic' is so anyone can make a USB
>>> display/adapter against this driver, all that's needed is to add a USB
>>> vid:pid.
>>>
>>> Unfortunately I've had some compounding health problems that have
>>> severally limited the time I can spend in front of a computer. For this
>>> reason I've decided to keep the gadget driver out-of-tree and focus on
>>> getting the host driver merged first.
>>>
>>> See the wiki[1] for more information and images for the Raspberry Pi
>>> Zero/4.
>>>
>>> One big change this time is that I've followed Peter Stuge's advice to
>>> not let DRM stuff leak into the USB protocol. This has made the protocol
>>> easier to understand just from reading the header file.
>>>
>>> Noralf.
>>>
>>> [1] https://github.com/notro/gud/wiki
>>
>> The patch set:
>>
>> Tested-by: Lubomir Rintel <lkundrak@v3.sk>
>>
>> Works like a charm with this board [1], though it didn't impress the girls
>> as much as I hoped. Code here [2], picture here [3].
>>
> 
> I have wondered what color display resolution it is possible to drive
> over USB full speed. I can understand that your PoC wasn't that
> impressive since it doesn't use DMA to drive the SPI bus.
> 

I have now done a Raspberry Pi Pico implementation and driving SPI using
DMA was just marginally faster than letting the CPU fill the FIFO. Maybe
I shouldn't be so suprised since the CPU has nothing else to do, but
even so I didn't expect this. But then again I have very little
experience with microcontrollers.

I have the same size display[1] as you 240x135 and my display was quite
snappy (using X windows!), I even added lz4 decompression support. I
haven't done much testing so I can't say how much the actual improvement
is with the compression. The USB double buffering I was hoping for
didn't pan out, the bulk endpoint can only do 64 byte packest (ISO is
512), so I ended up storing the packets and then push the frame in its
entirety to the display. The Pico has 264kB of ram so I can afford to
have a framebuffer and a decompression buffer for this tiny display. My
target display is 320x240 and that means I can't use 2 buffers, so not
sure how that goes.

[1] https://shop.pimoroni.com/products/pico-display-pack

Noralf.

> The new $4 Raspberry Pi Pico that came out this week looks interesting
> as a USB interface board for tiny panels. It can drive DPI panels
> directly, has 2 cores @133MHz, 264K SRAM and USB full speed. Maybe lz4
> decompression is even possible. Another good thing is that the board
> will be around for a long time.
> 
> Thanks for testing, I have limited bandwith these days so I couldn't do
> a test on an MCU myself.
> 
> Noralf.
> 
>> [1] https://www.banggood.com/LILYGO-TTGO-T-Display-GD32-RISC-V-32-bit-Core-Minimal-Development-Board-1_14-IPS-p-1652870.html?rmmds=search&cur_warehouse=CN
>> [2] https://github.com/hackerspace/libopencm3-gf32v-examples/commit/7ef51b31b9
>> [3] https://people.freedesktop.org/~lkundrak/lilygo.jpeg
>>
>> Had to apply a fix for the drm_connector_enum_list[] ommission I mentioned
>> elsewhere, and that I've now noticed you've noted previously.
>>
>> Take care
>> Lubo
>>
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 53+ messages in thread

end of thread, other threads:[~2021-02-21 11:16 UTC | newest]

Thread overview: 53+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-01-20 17:00 [PATCH v4 0/3] Generic USB Display driver Noralf Trønnes
2021-01-20 17:00 ` Noralf Trønnes
2021-01-20 17:00 ` [PATCH v4 1/3] drm/uapi: Add USB connector type Noralf Trønnes
2021-01-20 17:00   ` Noralf Trønnes
2021-01-20 17:42   ` Daniel Vetter
2021-01-20 17:42     ` Daniel Vetter
2021-01-21  7:45     ` Thomas Zimmermann
2021-01-21  7:45       ` Thomas Zimmermann
2021-01-21  8:27       ` Daniel Vetter
2021-01-21  8:27         ` Daniel Vetter
2021-01-21  8:40         ` Simon Ser
2021-01-21  8:40           ` Simon Ser
2021-01-21 10:01         ` Thomas Zimmermann
2021-01-21 10:01           ` Thomas Zimmermann
2021-01-21 18:07           ` Noralf Trønnes
2021-01-21 18:07             ` Noralf Trønnes
2021-01-22  7:54             ` Thomas Zimmermann
2021-01-22  7:54               ` Thomas Zimmermann
2021-01-22  7:59               ` Thomas Zimmermann
2021-01-22  7:59                 ` Thomas Zimmermann
2021-01-22 11:44               ` Noralf Trønnes
2021-01-22 11:44                 ` Noralf Trønnes
2021-01-22 12:47                 ` Thomas Zimmermann
2021-01-22 12:47                   ` Thomas Zimmermann
2021-01-22 14:35                   ` Noralf Trønnes
2021-01-22 14:35                     ` Noralf Trønnes
2021-01-22 14:55                     ` Thomas Zimmermann
2021-01-22 14:55                       ` Thomas Zimmermann
2021-01-22 18:39                       ` Noralf Trønnes
2021-01-22 18:39                         ` Noralf Trønnes
2021-01-20 17:00 ` [PATCH v4 2/3] drm/probe-helper: Check epoch counter in output_poll_execute() Noralf Trønnes
2021-01-20 17:00   ` Noralf Trønnes
2021-01-20 17:41   ` Daniel Vetter
2021-01-20 17:41     ` Daniel Vetter
2021-01-20 17:00 ` [PATCH v4 3/3] drm: Add Generic USB Display driver Noralf Trønnes
2021-01-20 17:00   ` Noralf Trønnes
2021-01-20 18:02   ` Daniel Vetter
2021-01-24 16:17     ` Noralf Trønnes
2021-01-24 16:17       ` Noralf Trønnes
2021-01-21  9:59 ` [PATCH v4 0/3] " Thomas Zimmermann
2021-01-21  9:59   ` Thomas Zimmermann
2021-01-21 10:20   ` Simon Ser
2021-01-21 10:20     ` Simon Ser
2021-01-23 11:46     ` Noralf Trønnes
2021-01-23 11:46       ` Noralf Trønnes
2021-01-23 18:47 ` Lubomir Rintel
2021-01-23 18:47   ` Lubomir Rintel
2021-01-24 18:38 ` Lubomir Rintel
2021-01-24 18:38   ` Lubomir Rintel
2021-01-24 20:51   ` Noralf Trønnes
2021-01-24 20:51     ` Noralf Trønnes
2021-02-21 11:14     ` Noralf Trønnes
2021-02-21 11:14       ` Noralf Trønnes

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.