brintos

brintos / linux-shallow public Read only

0
0
Text · 19.4 KiB · 0f07d77 Raw
731 lines · c
1// SPDX-License-Identifier: MIT2/*3 * Copyright 2020 Noralf Trønnes4 */5 6#include <linux/backlight.h>7#include <linux/workqueue.h>8 9#include <drm/drm_atomic.h>10#include <drm/drm_atomic_state_helper.h>11#include <drm/drm_connector.h>12#include <drm/drm_drv.h>13#include <drm/drm_edid.h>14#include <drm/drm_encoder.h>15#include <drm/drm_file.h>16#include <drm/drm_modeset_helper_vtables.h>17#include <drm/drm_print.h>18#include <drm/drm_probe_helper.h>19#include <drm/drm_simple_kms_helper.h>20#include <drm/gud.h>21 22#include "gud_internal.h"23 24struct gud_connector {25	struct drm_connector connector;26	struct drm_encoder encoder;27	struct backlight_device *backlight;28	struct work_struct backlight_work;29 30	/* Supported properties */31	u16 *properties;32	unsigned int num_properties;33 34	/* Initial gadget tv state if applicable, applied on state reset */35	struct drm_tv_connector_state initial_tv_state;36 37	/*38	 * Initial gadget backlight brightness if applicable, applied on state reset.39	 * The value -ENODEV is used to signal no backlight.40	 */41	int initial_brightness;42};43 44static inline struct gud_connector *to_gud_connector(struct drm_connector *connector)45{46	return container_of(connector, struct gud_connector, connector);47}48 49static void gud_conn_err(struct drm_connector *connector, const char *msg, int ret)50{51	dev_err(connector->dev->dev, "%s: %s (ret=%d)\n", connector->name, msg, ret);52}53 54/*55 * Use a worker to avoid taking kms locks inside the backlight lock.56 * Other display drivers use backlight within their kms locks.57 * This avoids inconsistent locking rules, which would upset lockdep.58 */59static void gud_connector_backlight_update_status_work(struct work_struct *work)60{61	struct gud_connector *gconn = container_of(work, struct gud_connector, backlight_work);62	struct drm_connector *connector = &gconn->connector;63	struct drm_connector_state *connector_state;64	struct drm_device *drm = connector->dev;65	struct drm_modeset_acquire_ctx ctx;66	struct drm_atomic_state *state;67	int idx, ret;68 69	if (!drm_dev_enter(drm, &idx))70		return;71 72	state = drm_atomic_state_alloc(drm);73	if (!state) {74		ret = -ENOMEM;75		goto exit;76	}77 78	drm_modeset_acquire_init(&ctx, 0);79	state->acquire_ctx = &ctx;80retry:81	connector_state = drm_atomic_get_connector_state(state, connector);82	if (IS_ERR(connector_state)) {83		ret = PTR_ERR(connector_state);84		goto out;85	}86 87	/* Reuse tv.brightness to avoid having to subclass */88	connector_state->tv.brightness = gconn->backlight->props.brightness;89 90	ret = drm_atomic_commit(state);91out:92	if (ret == -EDEADLK) {93		drm_atomic_state_clear(state);94		drm_modeset_backoff(&ctx);95		goto retry;96	}97 98	drm_atomic_state_put(state);99 100	drm_modeset_drop_locks(&ctx);101	drm_modeset_acquire_fini(&ctx);102exit:103	drm_dev_exit(idx);104 105	if (ret)106		dev_err(drm->dev, "Failed to update backlight, err=%d\n", ret);107}108 109static int gud_connector_backlight_update_status(struct backlight_device *bd)110{111	struct drm_connector *connector = bl_get_data(bd);112	struct gud_connector *gconn = to_gud_connector(connector);113 114	/* The USB timeout is 5 seconds so use system_long_wq for worst case scenario */115	queue_work(system_long_wq, &gconn->backlight_work);116 117	return 0;118}119 120static const struct backlight_ops gud_connector_backlight_ops = {121	.update_status	= gud_connector_backlight_update_status,122};123 124static int gud_connector_backlight_register(struct gud_connector *gconn)125{126	struct drm_connector *connector = &gconn->connector;127	struct backlight_device *bd;128	const char *name;129	const struct backlight_properties props = {130		.type = BACKLIGHT_RAW,131		.scale = BACKLIGHT_SCALE_NON_LINEAR,132		.max_brightness = 100,133		.brightness = gconn->initial_brightness,134	};135 136	name = kasprintf(GFP_KERNEL, "card%d-%s-backlight",137			 connector->dev->primary->index, connector->name);138	if (!name)139		return -ENOMEM;140 141	bd = backlight_device_register(name, connector->kdev, connector,142				       &gud_connector_backlight_ops, &props);143	kfree(name);144	if (IS_ERR(bd))145		return PTR_ERR(bd);146 147	gconn->backlight = bd;148 149	return 0;150}151 152static int gud_connector_detect(struct drm_connector *connector,153				struct drm_modeset_acquire_ctx *ctx, bool force)154{155	struct gud_device *gdrm = to_gud_device(connector->dev);156	int idx, ret;157	u8 status;158 159	if (!drm_dev_enter(connector->dev, &idx))160		return connector_status_disconnected;161 162	if (force) {163		ret = gud_usb_set(gdrm, GUD_REQ_SET_CONNECTOR_FORCE_DETECT,164				  connector->index, NULL, 0);165		if (ret) {166			ret = connector_status_unknown;167			goto exit;168		}169	}170 171	ret = gud_usb_get_u8(gdrm, GUD_REQ_GET_CONNECTOR_STATUS, connector->index, &status);172	if (ret) {173		ret = connector_status_unknown;174		goto exit;175	}176 177	switch (status & GUD_CONNECTOR_STATUS_CONNECTED_MASK) {178	case GUD_CONNECTOR_STATUS_DISCONNECTED:179		ret = connector_status_disconnected;180		break;181	case GUD_CONNECTOR_STATUS_CONNECTED:182		ret = connector_status_connected;183		break;184	default:185		ret = connector_status_unknown;186		break;187	}188 189	if (status & GUD_CONNECTOR_STATUS_CHANGED)190		connector->epoch_counter += 1;191exit:192	drm_dev_exit(idx);193 194	return ret;195}196 197struct gud_connector_get_edid_ctx {198	void *buf;199	size_t len;200	bool edid_override;201};202 203static int gud_connector_get_edid_block(void *data, u8 *buf, unsigned int block, size_t len)204{205	struct gud_connector_get_edid_ctx *ctx = data;206	size_t start = block * EDID_LENGTH;207 208	ctx->edid_override = false;209 210	if (start + len > ctx->len)211		return -1;212 213	memcpy(buf, ctx->buf + start, len);214 215	return 0;216}217 218static int gud_connector_get_modes(struct drm_connector *connector)219{220	struct gud_device *gdrm = to_gud_device(connector->dev);221	struct gud_display_mode_req *reqmodes = NULL;222	struct gud_connector_get_edid_ctx edid_ctx;223	unsigned int i, num_modes = 0;224	const struct drm_edid *drm_edid = NULL;225	int idx, ret;226 227	if (!drm_dev_enter(connector->dev, &idx))228		return 0;229 230	edid_ctx.edid_override = true;231	edid_ctx.buf = kmalloc(GUD_CONNECTOR_MAX_EDID_LEN, GFP_KERNEL);232	if (!edid_ctx.buf)233		goto out;234 235	ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_EDID, connector->index,236			  edid_ctx.buf, GUD_CONNECTOR_MAX_EDID_LEN);237	if (ret > 0 && ret % EDID_LENGTH) {238		gud_conn_err(connector, "Invalid EDID size", ret);239	} else if (ret > 0) {240		edid_ctx.len = ret;241		drm_edid = drm_edid_read_custom(connector, gud_connector_get_edid_block, &edid_ctx);242	}243 244	kfree(edid_ctx.buf);245	drm_edid_connector_update(connector, drm_edid);246 247	if (drm_edid && edid_ctx.edid_override)248		goto out;249 250	reqmodes = kmalloc_array(GUD_CONNECTOR_MAX_NUM_MODES, sizeof(*reqmodes), GFP_KERNEL);251	if (!reqmodes)252		goto out;253 254	ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_MODES, connector->index,255			  reqmodes, GUD_CONNECTOR_MAX_NUM_MODES * sizeof(*reqmodes));256	if (ret <= 0)257		goto out;258	if (ret % sizeof(*reqmodes)) {259		gud_conn_err(connector, "Invalid display mode array size", ret);260		goto out;261	}262 263	num_modes = ret / sizeof(*reqmodes);264 265	for (i = 0; i < num_modes; i++) {266		struct drm_display_mode *mode;267 268		mode = drm_mode_create(connector->dev);269		if (!mode) {270			num_modes = i;271			goto out;272		}273 274		gud_to_display_mode(mode, &reqmodes[i]);275		drm_mode_probed_add(connector, mode);276	}277out:278	if (!num_modes)279		num_modes = drm_edid_connector_add_modes(connector);280 281	kfree(reqmodes);282	drm_edid_free(drm_edid);283	drm_dev_exit(idx);284 285	return num_modes;286}287 288static int gud_connector_atomic_check(struct drm_connector *connector,289				      struct drm_atomic_state *state)290{291	struct drm_connector_state *new_state;292	struct drm_crtc_state *new_crtc_state;293	struct drm_connector_state *old_state;294 295	new_state = drm_atomic_get_new_connector_state(state, connector);296	if (!new_state->crtc)297		return 0;298 299	old_state = drm_atomic_get_old_connector_state(state, connector);300	new_crtc_state = drm_atomic_get_new_crtc_state(state, new_state->crtc);301 302	if (old_state->tv.margins.left != new_state->tv.margins.left ||303	    old_state->tv.margins.right != new_state->tv.margins.right ||304	    old_state->tv.margins.top != new_state->tv.margins.top ||305	    old_state->tv.margins.bottom != new_state->tv.margins.bottom ||306	    old_state->tv.legacy_mode != new_state->tv.legacy_mode ||307	    old_state->tv.brightness != new_state->tv.brightness ||308	    old_state->tv.contrast != new_state->tv.contrast ||309	    old_state->tv.flicker_reduction != new_state->tv.flicker_reduction ||310	    old_state->tv.overscan != new_state->tv.overscan ||311	    old_state->tv.saturation != new_state->tv.saturation ||312	    old_state->tv.hue != new_state->tv.hue)313		new_crtc_state->connectors_changed = true;314 315	return 0;316}317 318static const struct drm_connector_helper_funcs gud_connector_helper_funcs = {319	.detect_ctx = gud_connector_detect,320	.get_modes = gud_connector_get_modes,321	.atomic_check = gud_connector_atomic_check,322};323 324static int gud_connector_late_register(struct drm_connector *connector)325{326	struct gud_connector *gconn = to_gud_connector(connector);327 328	if (gconn->initial_brightness < 0)329		return 0;330 331	return gud_connector_backlight_register(gconn);332}333 334static void gud_connector_early_unregister(struct drm_connector *connector)335{336	struct gud_connector *gconn = to_gud_connector(connector);337 338	backlight_device_unregister(gconn->backlight);339	cancel_work_sync(&gconn->backlight_work);340}341 342static void gud_connector_destroy(struct drm_connector *connector)343{344	struct gud_connector *gconn = to_gud_connector(connector);345 346	drm_connector_cleanup(connector);347	kfree(gconn->properties);348	kfree(gconn);349}350 351static void gud_connector_reset(struct drm_connector *connector)352{353	struct gud_connector *gconn = to_gud_connector(connector);354 355	drm_atomic_helper_connector_reset(connector);356	connector->state->tv = gconn->initial_tv_state;357	/* Set margins from command line */358	drm_atomic_helper_connector_tv_margins_reset(connector);359	if (gconn->initial_brightness >= 0)360		connector->state->tv.brightness = gconn->initial_brightness;361}362 363static const struct drm_connector_funcs gud_connector_funcs = {364	.fill_modes = drm_helper_probe_single_connector_modes,365	.late_register = gud_connector_late_register,366	.early_unregister = gud_connector_early_unregister,367	.destroy = gud_connector_destroy,368	.reset = gud_connector_reset,369	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,370	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,371};372 373/*374 * The tv.mode property is shared among the connectors and its enum names are375 * driver specific. This means that if more than one connector uses tv.mode,376 * the enum names has to be the same.377 */378static int gud_connector_add_tv_mode(struct gud_device *gdrm, struct drm_connector *connector)379{380	size_t buf_len = GUD_CONNECTOR_TV_MODE_MAX_NUM * GUD_CONNECTOR_TV_MODE_NAME_LEN;381	const char *modes[GUD_CONNECTOR_TV_MODE_MAX_NUM];382	unsigned int i, num_modes;383	char *buf;384	int ret;385 386	buf = kmalloc(buf_len, GFP_KERNEL);387	if (!buf)388		return -ENOMEM;389 390	ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_TV_MODE_VALUES,391			  connector->index, buf, buf_len);392	if (ret < 0)393		goto free;394	if (!ret || ret % GUD_CONNECTOR_TV_MODE_NAME_LEN) {395		ret = -EIO;396		goto free;397	}398 399	num_modes = ret / GUD_CONNECTOR_TV_MODE_NAME_LEN;400	for (i = 0; i < num_modes; i++)401		modes[i] = &buf[i * GUD_CONNECTOR_TV_MODE_NAME_LEN];402 403	ret = drm_mode_create_tv_properties_legacy(connector->dev, num_modes, modes);404free:405	kfree(buf);406	if (ret < 0)407		gud_conn_err(connector, "Failed to add TV modes", ret);408 409	return ret;410}411 412static struct drm_property *413gud_connector_property_lookup(struct drm_connector *connector, u16 prop)414{415	struct drm_mode_config *config = &connector->dev->mode_config;416 417	switch (prop) {418	case GUD_PROPERTY_TV_LEFT_MARGIN:419		return config->tv_left_margin_property;420	case GUD_PROPERTY_TV_RIGHT_MARGIN:421		return config->tv_right_margin_property;422	case GUD_PROPERTY_TV_TOP_MARGIN:423		return config->tv_top_margin_property;424	case GUD_PROPERTY_TV_BOTTOM_MARGIN:425		return config->tv_bottom_margin_property;426	case GUD_PROPERTY_TV_MODE:427		return config->legacy_tv_mode_property;428	case GUD_PROPERTY_TV_BRIGHTNESS:429		return config->tv_brightness_property;430	case GUD_PROPERTY_TV_CONTRAST:431		return config->tv_contrast_property;432	case GUD_PROPERTY_TV_FLICKER_REDUCTION:433		return config->tv_flicker_reduction_property;434	case GUD_PROPERTY_TV_OVERSCAN:435		return config->tv_overscan_property;436	case GUD_PROPERTY_TV_SATURATION:437		return config->tv_saturation_property;438	case GUD_PROPERTY_TV_HUE:439		return config->tv_hue_property;440	default:441		return ERR_PTR(-EINVAL);442	}443}444 445static unsigned int *gud_connector_tv_state_val(u16 prop, struct drm_tv_connector_state *state)446{447	switch (prop) {448	case GUD_PROPERTY_TV_LEFT_MARGIN:449		return &state->margins.left;450	case GUD_PROPERTY_TV_RIGHT_MARGIN:451		return &state->margins.right;452	case GUD_PROPERTY_TV_TOP_MARGIN:453		return &state->margins.top;454	case GUD_PROPERTY_TV_BOTTOM_MARGIN:455		return &state->margins.bottom;456	case GUD_PROPERTY_TV_MODE:457		return &state->legacy_mode;458	case GUD_PROPERTY_TV_BRIGHTNESS:459		return &state->brightness;460	case GUD_PROPERTY_TV_CONTRAST:461		return &state->contrast;462	case GUD_PROPERTY_TV_FLICKER_REDUCTION:463		return &state->flicker_reduction;464	case GUD_PROPERTY_TV_OVERSCAN:465		return &state->overscan;466	case GUD_PROPERTY_TV_SATURATION:467		return &state->saturation;468	case GUD_PROPERTY_TV_HUE:469		return &state->hue;470	default:471		return ERR_PTR(-EINVAL);472	}473}474 475static int gud_connector_add_properties(struct gud_device *gdrm, struct gud_connector *gconn)476{477	struct drm_connector *connector = &gconn->connector;478	struct drm_device *drm = &gdrm->drm;479	struct gud_property_req *properties;480	unsigned int i, num_properties;481	int ret;482 483	properties = kcalloc(GUD_CONNECTOR_PROPERTIES_MAX_NUM, sizeof(*properties), GFP_KERNEL);484	if (!properties)485		return -ENOMEM;486 487	ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_PROPERTIES, connector->index,488			  properties, GUD_CONNECTOR_PROPERTIES_MAX_NUM * sizeof(*properties));489	if (ret <= 0)490		goto out;491	if (ret % sizeof(*properties)) {492		ret = -EIO;493		goto out;494	}495 496	num_properties = ret / sizeof(*properties);497	ret = 0;498 499	gconn->properties = kcalloc(num_properties, sizeof(*gconn->properties), GFP_KERNEL);500	if (!gconn->properties) {501		ret = -ENOMEM;502		goto out;503	}504 505	for (i = 0; i < num_properties; i++) {506		u16 prop = le16_to_cpu(properties[i].prop);507		u64 val = le64_to_cpu(properties[i].val);508		struct drm_property *property;509		unsigned int *state_val;510 511		drm_dbg(drm, "property: %u = %llu(0x%llx)\n", prop, val, val);512 513		switch (prop) {514		case GUD_PROPERTY_TV_LEFT_MARGIN:515			fallthrough;516		case GUD_PROPERTY_TV_RIGHT_MARGIN:517			fallthrough;518		case GUD_PROPERTY_TV_TOP_MARGIN:519			fallthrough;520		case GUD_PROPERTY_TV_BOTTOM_MARGIN:521			ret = drm_mode_create_tv_margin_properties(drm);522			if (ret)523				goto out;524			break;525		case GUD_PROPERTY_TV_MODE:526			ret = gud_connector_add_tv_mode(gdrm, connector);527			if (ret)528				goto out;529			break;530		case GUD_PROPERTY_TV_BRIGHTNESS:531			fallthrough;532		case GUD_PROPERTY_TV_CONTRAST:533			fallthrough;534		case GUD_PROPERTY_TV_FLICKER_REDUCTION:535			fallthrough;536		case GUD_PROPERTY_TV_OVERSCAN:537			fallthrough;538		case GUD_PROPERTY_TV_SATURATION:539			fallthrough;540		case GUD_PROPERTY_TV_HUE:541			/* This is a no-op if already added. */542			ret = drm_mode_create_tv_properties_legacy(drm, 0, NULL);543			if (ret)544				goto out;545			break;546		case GUD_PROPERTY_BACKLIGHT_BRIGHTNESS:547			if (val > 100) {548				ret = -EINVAL;549				goto out;550			}551			gconn->initial_brightness = val;552			break;553		default:554			/* New ones might show up in future devices, skip those we don't know. */555			drm_dbg(drm, "Ignoring unknown property: %u\n", prop);556			continue;557		}558 559		gconn->properties[gconn->num_properties++] = prop;560 561		if (prop == GUD_PROPERTY_BACKLIGHT_BRIGHTNESS)562			continue; /* not a DRM property */563 564		property = gud_connector_property_lookup(connector, prop);565		if (WARN_ON(IS_ERR(property)))566			continue;567 568		state_val = gud_connector_tv_state_val(prop, &gconn->initial_tv_state);569		if (WARN_ON(IS_ERR(state_val)))570			continue;571 572		*state_val = val;573		drm_object_attach_property(&connector->base, property, 0);574	}575out:576	kfree(properties);577 578	return ret;579}580 581int gud_connector_fill_properties(struct drm_connector_state *connector_state,582				  struct gud_property_req *properties)583{584	struct gud_connector *gconn = to_gud_connector(connector_state->connector);585	unsigned int i;586 587	for (i = 0; i < gconn->num_properties; i++) {588		u16 prop = gconn->properties[i];589		u64 val;590 591		if (prop == GUD_PROPERTY_BACKLIGHT_BRIGHTNESS) {592			val = connector_state->tv.brightness;593		} else {594			unsigned int *state_val;595 596			state_val = gud_connector_tv_state_val(prop, &connector_state->tv);597			if (WARN_ON_ONCE(IS_ERR(state_val)))598				return PTR_ERR(state_val);599 600			val = *state_val;601		}602 603		properties[i].prop = cpu_to_le16(prop);604		properties[i].val = cpu_to_le64(val);605	}606 607	return gconn->num_properties;608}609 610static int gud_connector_create(struct gud_device *gdrm, unsigned int index,611				struct gud_connector_descriptor_req *desc)612{613	struct drm_device *drm = &gdrm->drm;614	struct gud_connector *gconn;615	struct drm_connector *connector;616	struct drm_encoder *encoder;617	int ret, connector_type;618	u32 flags;619 620	gconn = kzalloc(sizeof(*gconn), GFP_KERNEL);621	if (!gconn)622		return -ENOMEM;623 624	INIT_WORK(&gconn->backlight_work, gud_connector_backlight_update_status_work);625	gconn->initial_brightness = -ENODEV;626	flags = le32_to_cpu(desc->flags);627	connector = &gconn->connector;628 629	drm_dbg(drm, "Connector: index=%u type=%u flags=0x%x\n", index, desc->connector_type, flags);630 631	switch (desc->connector_type) {632	case GUD_CONNECTOR_TYPE_PANEL:633		connector_type = DRM_MODE_CONNECTOR_USB;634		break;635	case GUD_CONNECTOR_TYPE_VGA:636		connector_type = DRM_MODE_CONNECTOR_VGA;637		break;638	case GUD_CONNECTOR_TYPE_DVI:639		connector_type = DRM_MODE_CONNECTOR_DVID;640		break;641	case GUD_CONNECTOR_TYPE_COMPOSITE:642		connector_type = DRM_MODE_CONNECTOR_Composite;643		break;644	case GUD_CONNECTOR_TYPE_SVIDEO:645		connector_type = DRM_MODE_CONNECTOR_SVIDEO;646		break;647	case GUD_CONNECTOR_TYPE_COMPONENT:648		connector_type = DRM_MODE_CONNECTOR_Component;649		break;650	case GUD_CONNECTOR_TYPE_DISPLAYPORT:651		connector_type = DRM_MODE_CONNECTOR_DisplayPort;652		break;653	case GUD_CONNECTOR_TYPE_HDMI:654		connector_type = DRM_MODE_CONNECTOR_HDMIA;655		break;656	default: /* future types */657		connector_type = DRM_MODE_CONNECTOR_USB;658		break;659	}660 661	drm_connector_helper_add(connector, &gud_connector_helper_funcs);662	ret = drm_connector_init(drm, connector, &gud_connector_funcs, connector_type);663	if (ret) {664		kfree(connector);665		return ret;666	}667 668	if (WARN_ON(connector->index != index))669		return -EINVAL;670 671	if (flags & GUD_CONNECTOR_FLAGS_POLL_STATUS)672		connector->polled = (DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT);673	if (flags & GUD_CONNECTOR_FLAGS_INTERLACE)674		connector->interlace_allowed = true;675	if (flags & GUD_CONNECTOR_FLAGS_DOUBLESCAN)676		connector->doublescan_allowed = true;677 678	ret = gud_connector_add_properties(gdrm, gconn);679	if (ret) {680		gud_conn_err(connector, "Failed to add properties", ret);681		return ret;682	}683 684	/* The first connector is attached to the existing simple pipe encoder */685	if (!connector->index) {686		encoder = &gdrm->pipe.encoder;687	} else {688		encoder = &gconn->encoder;689 690		ret = drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_NONE);691		if (ret)692			return ret;693 694		encoder->possible_crtcs = 1;695	}696 697	return drm_connector_attach_encoder(connector, encoder);698}699 700int gud_get_connectors(struct gud_device *gdrm)701{702	struct gud_connector_descriptor_req *descs;703	unsigned int i, num_connectors;704	int ret;705 706	descs = kmalloc_array(GUD_CONNECTORS_MAX_NUM, sizeof(*descs), GFP_KERNEL);707	if (!descs)708		return -ENOMEM;709 710	ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTORS, 0,711			  descs, GUD_CONNECTORS_MAX_NUM * sizeof(*descs));712	if (ret < 0)713		goto free;714	if (!ret || ret % sizeof(*descs)) {715		ret = -EIO;716		goto free;717	}718 719	num_connectors = ret / sizeof(*descs);720 721	for (i = 0; i < num_connectors; i++) {722		ret = gud_connector_create(gdrm, i, &descs[i]);723		if (ret)724			goto free;725	}726free:727	kfree(descs);728 729	return ret;730}731