File Index
Files and directories
Sign in
FILE

gud_connector.c

File preview with syntax highlighting.

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