Skip to content

Instantly share code, notes, and snippets.

@notro
Created February 26, 2021 12:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save notro/a43a93a3aa0cc75d930890b7b254fc0a to your computer and use it in GitHub Desktop.
Save notro/a43a93a3aa0cc75d930890b7b254fc0a to your computer and use it in GitHub Desktop.
gud: Don't pass length in request structs
diff --git a/include/drm/gud.h b/include/drm/gud.h
index 7056f599a8e4..30bf2e5f2123 100644
--- a/include/drm/gud.h
+++ b/include/drm/gud.h
@@ -32,9 +32,6 @@
* @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.
@@ -53,9 +50,6 @@ struct gud_display_descriptor_req {
__le32 max_width;
__le32 min_height;
__le32 max_height;
- __u8 num_formats;
- __u8 num_properties;
- __u8 num_connectors;
} __packed;
/*
@@ -127,7 +121,6 @@ struct gud_display_mode_req {
* - 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;
@@ -143,38 +136,6 @@ struct gud_connector_descriptor_req {
#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;
/*
@@ -206,7 +167,6 @@ struct gud_set_buffer_req {
* @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.
@@ -215,7 +175,6 @@ struct gud_state_req {
struct gud_display_mode_req mode;
__u8 format;
__u8 connector;
- __u8 num_properties;
struct gud_property_req properties[];
} __packed;
@@ -226,9 +185,7 @@ struct gud_state_req {
#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 */
@@ -295,6 +252,7 @@ struct gud_state_req {
/* Get supported pixel formats as a byte array of GUD_PIXEL_FORMAT_* */
#define GUD_REQ_GET_FORMATS 0x40
+ #define GUD_FORMATS_MAX_NUM 32
/* 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
@@ -306,17 +264,20 @@ struct gud_state_req {
* gud_property_req.val often contains the initial value for the property.
*/
#define GUD_REQ_GET_PROPERTIES 0x41
+ #define GUD_PROPERTIES_MAX_NUM 32
/* 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 connector descriptors as an array of &gud_connector_descriptor_req */
+#define GUD_REQ_GET_CONNECTORS 0x50
+ #define GUD_CONNECTORS_MAX_NUM 32
/*
* 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
+ #define GUD_CONNECTOR_PROPERTIES_MAX_NUM 32
/*
* Issued when there's a TV_MODE property present.
@@ -325,18 +286,40 @@ struct gud_state_req {
*/
#define GUD_REQ_GET_CONNECTOR_TV_MODE_VALUES 0x52
#define GUD_CONNECTOR_TV_MODE_NAME_LEN 16
+ #define GUD_CONNECTOR_TV_MODE_MAX_NUM 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. */
+/*
+ * Get connector status. Value is u8.
+ *
+ * Userspace will get a HOTPLUG uevent if one of the following is true:
+ * - Connection status has changed since last
+ * - CHANGED is set
+ */
#define GUD_REQ_GET_CONNECTOR_STATUS 0x54
+ #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)
+
+/*
+ * Display modes can be fetched as either EDID data or an array of &gud_display_mode_req.
+ *
+ * If GUD_REQ_GET_CONNECTOR_MODES returns zero, EDID is used to create display modes.
+ * If both display modes and EDID are returned, EDID is just passed on to userspace
+ * in the EDID connector property.
+ */
/* Get &gud_display_mode_req array of supported display modes */
#define GUD_REQ_GET_CONNECTOR_MODES 0x55
+ #define GUD_CONNECTOR_MAX_NUM_MODES 128
/* Get Extended Display Identification Data */
#define GUD_REQ_GET_CONNECTOR_EDID 0x56
+ #define GUD_CONNECTOR_MAX_EDID_LEN 2048
/* Set buffer properties before bulk transfer as &gud_set_buffer_req */
#define GUD_REQ_SET_BUFFER 0x60
diff --git a/drivers/gpu/drm/gud/gud_internal.h b/drivers/gpu/drm/gud/gud_internal.h
index 45cb2b6d5bd3..f72430f6e859 100644
--- a/drivers/gpu/drm/gud/gud_internal.h
+++ b/drivers/gpu/drm/gud/gud_internal.h
@@ -55,7 +55,8 @@ static inline struct usb_device *gud_to_usb_device(struct gud_device *gdrm)
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);
+int gud_usb_get_u8(struct gud_device *gdrm, u8 request, u16 index, u8 *val);
+int gud_usb_set_u8(struct gud_device *gdrm, u8 request, u8 val);
void gud_clear_damage(struct gud_device *gdrm);
void gud_flush_work(struct work_struct *work);
@@ -67,7 +68,7 @@ void gud_pipe_update(struct drm_simple_display_pipe *pipe,
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);
+int gud_get_connectors(struct gud_device *gdrm);
/* Driver internal fourcc for 1-bit monochrome */
#define GUD_DRM_FORMAT_R1 0x00000122
diff --git a/drivers/gpu/drm/gud/gud_drv.c b/drivers/gpu/drm/gud/gud_drv.c
index 29a960b887be..e984b0d6c353 100644
--- a/drivers/gpu/drm/gud/gud_drv.c
+++ b/drivers/gpu/drm/gud/gud_drv.c
@@ -45,7 +45,6 @@ static int gud_usb_control_msg(struct usb_device *usb, u8 ifnum, bool in,
{
u8 requesttype = USB_TYPE_VENDOR | USB_RECIP_INTERFACE;
unsigned int pipe;
- int ret;
if (in) {
pipe = usb_rcvctrlpipe(usb, 0);
@@ -55,14 +54,8 @@ static int gud_usb_control_msg(struct usb_device *usb, u8 ifnum, bool in,
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;
+ return usb_control_msg(usb, pipe, request, requesttype, value,
+ ifnum, buf, len, USB_CTRL_GET_TIMEOUT);
}
static int gud_get_display_descriptor(struct usb_interface *interface,
@@ -80,20 +73,20 @@ static int gud_get_display_descriptor(struct usb_interface *interface,
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)
+ if (ret < 0)
return ret;
+ if (ret != sizeof(*desc))
+ return -EIO;
if (desc->magic != le32_to_cpu(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",
+ "version=%u flags=0x%x compression=0x%x 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 ||
+ if (!desc->version || !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;
@@ -101,22 +94,6 @@ static int gud_get_display_descriptor(struct usb_interface *interface,
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) {
@@ -137,11 +114,30 @@ static int gud_status_to_errno(u8 status)
}
}
+static int gud_usb_get_status(struct usb_device *usb, u8 ifnum)
+{
+ int ret, status = -EIO;
+ u8 *buf;
+
+ 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));
+ if (ret == sizeof(*buf))
+ status = gud_status_to_errno(*buf);
+ kfree(buf);
+
+ if (ret < 0)
+ return ret;
+
+ return status;
+}
+
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",
@@ -152,61 +148,88 @@ static int gud_usb_transfer(struct gud_device *gdrm, bool in, u8 request, u16 in
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, buf, len);
+ if (ret == -EPIPE || ((gdrm->flags & GUD_DISPLAY_FLAG_STATUS_ON_SET) && !in && ret >= 0)) {
+ int status;
+
+ status = gud_usb_get_status(usb, gdrm->ifnum);
+ if (status < 0) {
+ ret = status;
+ } else if (ret < 0) {
+ dev_err_once(gdrm->drm.dev,
+ "Unexpected status OK for failed transfer\n");
+ ret = -EPIPE;
}
}
- 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) {
+ if (ret < 0) {
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)
+/*
+ * @buf cannot be allocated on the stack.
+ * Returns number of bytes received or negative error code on failure.
+ */
+int gud_usb_get(struct gud_device *gdrm, u8 request, u16 index, void *buf, size_t max_len)
{
- return gud_usb_transfer(gdrm, true, request, index, buf, len);
+ return gud_usb_transfer(gdrm, true, request, index, buf, max_len);
}
+/*
+ * @buf can be allocated on the stack or NULL.
+ * Returns zero on success or negative error code on failure.
+ */
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);
+ void *trbuf = NULL;
+ int ret;
+
+ if (buf) {
+ trbuf = kmemdup(buf, len, GFP_KERNEL);
+ if (!trbuf)
+ return -ENOMEM;
+ }
+
+ ret = gud_usb_transfer(gdrm, false, request, index, trbuf, len);
+ kfree(trbuf);
+ if (ret < 0)
+ return ret;
+
+ return ret != len ? -EIO : 0;
}
-int gud_usb_write8(struct gud_device *gdrm, u8 request, u8 val)
+/*
+ * @val can be allocated on the stack.
+ * Returns zero on success or negative error code on failure.
+ */
+int gud_usb_get_u8(struct gud_device *gdrm, u8 request, u16 index, u8 *val)
+{
+ u8 *buf;
+ int ret;
+
+ buf = kmalloc(sizeof(*val), GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = gud_usb_get(gdrm, request, index, buf, sizeof(*val));
+ *val = *buf;
+ kfree(buf);
+ if (ret < 0)
+ return ret;
+
+ return ret != sizeof(*val) ? -EIO : 0;
+}
+
+/* Returns zero on success or negative error code on failure. */
+int gud_usb_set_u8(struct gud_device *gdrm, u8 request, u8 val)
{
return gud_usb_set(gdrm, request, 0, &val, sizeof(val));
}
@@ -225,42 +248,47 @@ static int gud_set_version(struct usb_device *usb, u8 ifnum, u32 flags, u8 versi
kfree(buf);
if (ret == -EPIPE)
return -EPROTONOSUPPORT;
- if (ret)
+ if (ret < 0)
return ret;
+ if (ret != sizeof(*buf))
+ return -EIO;
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;
+ ret = gud_usb_get_status(usb, ifnum);
+ if (ret)
+ return -EPROTONOSUPPORT;
}
- return ret;
+ return 0;
}
-static int gud_get_properties(struct gud_device *gdrm, unsigned int num_properties)
+static int gud_get_properties(struct gud_device *gdrm)
{
struct gud_property_req *properties;
- unsigned int i;
+ unsigned int i, num_properties;
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);
+ properties = kcalloc(GUD_PROPERTIES_MAX_NUM, 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)
+ properties, GUD_PROPERTIES_MAX_NUM * sizeof(*properties));
+ if (ret <= 0)
goto out;
+ if (ret % sizeof(*properties)) {
+ ret = -EIO;
+ goto out;
+ }
+
+ num_properties = ret / sizeof(*properties);
+
+ gdrm->properties = drmm_kcalloc(&gdrm->drm, num_properties, sizeof(*gdrm->properties),
+ GFP_KERNEL);
+ if (!gdrm->properties) {
+ ret = -ENOMEM;
+ goto out;
+ }
for (i = 0; i < num_properties; i++) {
u16 prop = le16_to_cpu(properties[i].prop);
@@ -381,11 +409,11 @@ static int gud_probe(struct usb_interface *interface, const struct usb_device_id
struct device *dev = &interface->dev;
const struct drm_format_info *xrgb8888_emulation_format = NULL;
bool rgb565_supported = false, xrgb8888_supported = false;
+ unsigned int num_formats_dev, num_formats = 0;
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 gud_device *gdrm;
struct drm_device *drm;
u8 *formats_dev;
u32 *formats;
@@ -446,17 +474,18 @@ static int gud_probe(struct usb_interface *interface, const struct usb_device_id
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);
+ formats_dev = devm_kmalloc(dev, GUD_FORMATS_MAX_NUM, GFP_KERNEL);
/* Add room for emulated XRGB8888 */
- formats = devm_kmalloc_array(dev, desc.num_formats + 1, sizeof(*formats), GFP_KERNEL);
+ formats = devm_kmalloc_array(dev, GUD_FORMATS_MAX_NUM + 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)
+ ret = gud_usb_get(gdrm, GUD_REQ_GET_FORMATS, 0, formats_dev, GUD_FORMATS_MAX_NUM);
+ if (ret < 0)
return ret;
- for (i = 0; i < desc.num_formats; i++) {
+ num_formats_dev = ret;
+ for (i = 0; i < num_formats_dev; i++) {
const struct drm_format_info *info;
size_t fmt_buf_size;
u32 format;
@@ -549,17 +578,15 @@ static int gud_probe(struct usb_interface *interface, const struct usb_device_id
devm_kfree(dev, formats);
devm_kfree(dev, formats_dev);
- ret = gud_get_properties(gdrm, desc.num_properties);
+ ret = gud_get_properties(gdrm);
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;
- }
+ ret = gud_get_connectors(gdrm);
+ if (ret)
+ return ret;
drm_mode_config_reset(drm);
diff --git a/drivers/gpu/drm/gud/gud_pipe.c b/drivers/gpu/drm/gud/gud_pipe.c
index 144953da5b50..da428cf515ba 100644
--- a/drivers/gpu/drm/gud/gud_pipe.c
+++ b/drivers/gpu/drm/gud/gud_pipe.c
@@ -393,7 +393,6 @@ int gud_pipe_check(struct drm_simple_display_pipe *pipe,
}
req->connector = drm_connector_index(connector);
- req->num_properties = num_properties;
num_properties = gud_connector_fill_properties(connector, connector_state,
req->properties);
@@ -455,13 +454,13 @@ void gud_pipe_update(struct drm_simple_display_pipe *pipe,
return;
if (!old_state->fb)
- gud_usb_write8(gdrm, GUD_REQ_SET_CONTROLLER_ENABLE, 1);
+ gud_usb_set_u8(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);
+ gud_usb_set_u8(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)
@@ -470,7 +469,7 @@ void gud_pipe_update(struct drm_simple_display_pipe *pipe,
}
if (!crtc->state->enable)
- gud_usb_write8(gdrm, GUD_REQ_SET_CONTROLLER_ENABLE, 0);
+ gud_usb_set_u8(gdrm, GUD_REQ_SET_CONTROLLER_ENABLE, 0);
drm_dev_exit(idx);
}
diff --git a/drivers/gpu/drm/gud/gud_connector.c b/drivers/gpu/drm/gud/gud_connector.c
index 64f323152976..87c0f6103cab 100644
--- a/drivers/gpu/drm/gud/gud_connector.c
+++ b/drivers/gpu/drm/gud/gud_connector.c
@@ -38,9 +38,6 @@ struct gud_connector {
* 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)
@@ -146,57 +143,12 @@ static int gud_connector_backlight_register(struct gud_connector *gconn)
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;
+ u8 status;
if (!drm_dev_enter(connector->dev, &idx))
return connector_status_disconnected;
@@ -210,9 +162,26 @@ static int gud_connector_detect(struct drm_connector *connector,
}
}
- ret = gud_connector_status_request(connector);
- if (ret < 0)
+ ret = gud_usb_get_u8(gdrm, GUD_REQ_GET_CONNECTOR_STATUS, connector->index, &status);
+ if (ret) {
ret = connector_status_unknown;
+ goto exit;
+ }
+
+ switch (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;
+ };
+
+ if (status & GUD_CONNECTOR_STATUS_CHANGED)
+ connector->epoch_counter += 1;
exit:
drm_dev_exit(idx);
@@ -220,35 +189,21 @@ static int gud_connector_detect(struct drm_connector *connector,
}
struct gud_connector_get_edid_ctx {
- struct gud_connector *gconn;
void *buf;
+ size_t len;
+ bool edid_override;
};
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)
+ ctx->edid_override = false;
+
+ if (start + len > ctx->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;
@@ -256,57 +211,70 @@ static int gud_connector_get_edid_block(void *data, u8 *buf, unsigned int block,
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;
+ struct gud_connector_get_edid_ctx edid_ctx;
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_ctx.edid_override = true;
+ edid_ctx.buf = kmalloc(GUD_CONNECTOR_MAX_EDID_LEN, GFP_KERNEL);
+ if (!edid_ctx.buf)
+ goto out;
+
+ ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_EDID, connector->index,
+ edid_ctx.buf, GUD_CONNECTOR_MAX_EDID_LEN);
+ if (ret > 0) {
+ if (ret % EDID_LENGTH) {
+ drm_err(connector->dev, "%s: Invalid EDID size: %d\n", connector->name, ret);
+ } else {
+ edid_ctx.len = ret;
+ edid = drm_do_get_edid(connector, gud_connector_get_edid_block, &edid_ctx);
+ }
}
- 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);
+ if (edid && edid_ctx.edid_override)
goto out;
- }
- reqmodes = kmalloc_array(gconn->num_modes, sizeof(*reqmodes), GFP_KERNEL);
+ reqmodes = kmalloc_array(GUD_CONNECTOR_MAX_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)
+ reqmodes, GUD_CONNECTOR_MAX_NUM_MODES * sizeof(*reqmodes));
+ if (ret > 0) {
+ if (ret % sizeof(*reqmodes)) {
+ drm_err(connector->dev, "%s: Invalid display mode array size: %d\n",
+ connector->name, ret);
goto out;
+ }
- gud_to_display_mode(mode, &reqmodes[i]);
- drm_mode_probed_add(connector, mode);
- num_modes++;
+ num_modes = ret / sizeof(*reqmodes);
+
+ for (i = 0; i < num_modes; i++) {
+ struct drm_display_mode *mode;
+
+ mode = drm_mode_create(connector->dev);
+ if (!mode) {
+ num_modes = i;
+ goto out;
+ }
+
+ gud_to_display_mode(mode, &reqmodes[i]);
+ drm_mode_probed_add(connector, mode);
+ }
}
out:
+ if (!num_modes)
+ num_modes = drm_add_edid_modes(connector, edid);
+
kfree(reqmodes);
kfree(edid);
drm_dev_exit(idx);
@@ -404,39 +372,33 @@ static const struct drm_connector_funcs gud_connector_funcs = {
* 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)
+static int gud_connector_add_tv_mode(struct gud_device *gdrm, struct drm_connector *connector)
{
+ size_t buf_len = GUD_CONNECTOR_TV_MODE_MAX_NUM * GUD_CONNECTOR_TV_MODE_NAME_LEN;
+ const char *modes[GUD_CONNECTOR_TV_MODE_MAX_NUM];
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;
- }
+ if (!buf)
+ return -ENOMEM;
ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_TV_MODE_VALUES,
connector->index, buf, buf_len);
- if (ret)
+ if (ret < 0)
goto free;
+ if (!ret || ret % GUD_CONNECTOR_TV_MODE_NAME_LEN) {
+ ret = -EIO;
+ goto free;
+ }
+ num_modes = ret / GUD_CONNECTOR_TV_MODE_NAME_LEN;
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;
@@ -505,27 +467,35 @@ static unsigned int *gud_connector_tv_state_val(u16 prop, struct drm_tv_connecto
}
}
-static int gud_connector_add_properties(struct gud_device *gdrm, struct gud_connector *gconn,
- unsigned int num_properties)
+static int gud_connector_add_properties(struct gud_device *gdrm, struct gud_connector *gconn)
{
- struct drm_device *drm = &gdrm->drm;
struct drm_connector *connector = &gconn->connector;
+ struct drm_device *drm = &gdrm->drm;
struct gud_property_req *properties;
- unsigned int i;
+ unsigned int i, num_properties;
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);
+ properties = kcalloc(GUD_CONNECTOR_PROPERTIES_MAX_NUM, 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)
+ properties, GUD_CONNECTOR_PROPERTIES_MAX_NUM * sizeof(*properties));
+ if (ret <= 0)
goto out;
+ if (ret % sizeof(*properties)) {
+ ret = -EIO;
+ goto out;
+ }
+
+ num_properties = ret / sizeof(*properties);
+ ret = 0;
+
+ gconn->properties = kcalloc(num_properties, sizeof(*gconn->properties), GFP_KERNEL);
+ if (!gconn->properties) {
+ ret = -ENOMEM;
+ goto out;
+ }
for (i = 0; i < num_properties; i++) {
u16 prop = le16_to_cpu(properties[i].prop);
@@ -548,10 +518,9 @@ static int gud_connector_add_properties(struct gud_device *gdrm, struct gud_conn
goto out;
break;
case GUD_PROPERTY_TV_MODE:
- ret = gud_connector_add_tv_mode(gdrm, connector, val);
+ ret = gud_connector_add_tv_mode(gdrm, connector);
if (ret)
goto out;
- val = val & (BIT(GUD_CONNECTOR_TV_MODE_NUM_SHIFT) - 1);
break;
case GUD_PROPERTY_TV_BRIGHTNESS:
fallthrough;
@@ -578,7 +547,7 @@ static int gud_connector_add_properties(struct gud_device *gdrm, struct gud_conn
break;
default:
/* New ones might show up in future devices, skip those we don't know. */
- drm_dbg(drm, "Unknown property: %u\n", prop);
+ drm_dbg(drm, "Ignoring unknown property: %u\n", prop);
continue;
}
@@ -640,9 +609,9 @@ int gud_connector_fill_properties(struct drm_connector *connector,
return gconn->num_properties;
}
-int gud_connector_create(struct gud_device *gdrm, unsigned int index)
+static int gud_connector_create(struct gud_device *gdrm, unsigned int index,
+ struct gud_connector_descriptor_req *desc)
{
- struct gud_connector_descriptor_req desc;
struct drm_device *drm = &gdrm->drm;
struct gud_connector *gconn;
struct drm_connector *connector;
@@ -650,23 +619,18 @@ int gud_connector_create(struct gud_device *gdrm, unsigned int index)
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;
INIT_WORK(&gconn->backlight_work, gud_connector_backlight_update_status_work);
gconn->initial_brightness = -ENODEV;
- flags = le32_to_cpu(desc.flags);
+ 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);
+ drm_dbg(drm, "Connector: index=%u type=%u flags=0x%x\n", index, desc->connector_type, flags);
- switch (desc.connector_type) {
+ switch (desc->connector_type) {
case GUD_CONNECTOR_TYPE_PANEL:
connector_type = DRM_MODE_CONNECTOR_USB;
break;
@@ -713,12 +677,10 @@ int gud_connector_create(struct gud_device *gdrm, unsigned int index)
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;
- }
+ ret = gud_connector_add_properties(gdrm, gconn);
+ 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 */
@@ -736,3 +698,35 @@ int gud_connector_create(struct gud_device *gdrm, unsigned int index)
return drm_connector_attach_encoder(connector, encoder);
}
+
+int gud_get_connectors(struct gud_device *gdrm)
+{
+ struct gud_connector_descriptor_req *descs;
+ unsigned int i, num_connectors;
+ int ret;
+
+ descs = kmalloc_array(GUD_CONNECTORS_MAX_NUM, sizeof(*descs), GFP_KERNEL);
+ if (!descs)
+ return -ENOMEM;
+
+ ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTORS, 0,
+ descs, GUD_CONNECTORS_MAX_NUM* sizeof(descs));
+ if (ret < 0)
+ goto free;
+ if (!ret || ret % sizeof(*descs)) {
+ ret = -EIO;
+ goto free;
+ }
+
+ num_connectors = ret / sizeof(*descs);
+
+ for (i = 0; i < num_connectors; i++) {
+ ret = gud_connector_create(gdrm, i, &descs[i]);
+ if (ret)
+ goto free;
+ }
+free:
+ kfree(descs);
+
+ return 0;
+}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment