Created
February 26, 2021 12:01
-
-
Save notro/a43a93a3aa0cc75d930890b7b254fc0a to your computer and use it in GitHub Desktop.
gud: Don't pass length in request structs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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