xref: /linux/drivers/gpu/drm/gud/gud_connector.c (revision 546b928da0427b0d6c663cbb992bd7bfa9ac7971)
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 
23 struct 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 
to_gud_connector(struct drm_connector * connector)43 static inline struct gud_connector *to_gud_connector(struct drm_connector *connector)
44 {
45 	return container_of(connector, struct gud_connector, connector);
46 }
47 
gud_conn_err(struct drm_connector * connector,const char * msg,int ret)48 static 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  */
gud_connector_backlight_update_status_work(struct work_struct * work)58 static 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_commit *state;
66 	int idx, ret;
67 
68 	if (!drm_dev_enter(drm, &idx))
69 		return;
70 
71 	state = drm_atomic_commit_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;
79 retry:
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);
90 out:
91 	if (ret == -EDEADLK) {
92 		drm_atomic_commit_clear(state);
93 		drm_modeset_backoff(&ctx);
94 		goto retry;
95 	}
96 
97 	drm_atomic_commit_put(state);
98 
99 	drm_modeset_drop_locks(&ctx);
100 	drm_modeset_acquire_fini(&ctx);
101 exit:
102 	drm_dev_exit(idx);
103 
104 	if (ret)
105 		dev_err(drm->dev, "Failed to update backlight, err=%d\n", ret);
106 }
107 
gud_connector_backlight_update_status(struct backlight_device * bd)108 static 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 
119 static const struct backlight_ops gud_connector_backlight_ops = {
120 	.update_status	= gud_connector_backlight_update_status,
121 };
122 
gud_connector_backlight_register(struct gud_connector * gconn)123 static 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 
gud_connector_detect(struct drm_connector * connector,struct drm_modeset_acquire_ctx * ctx,bool force)151 static 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;
190 exit:
191 	drm_dev_exit(idx);
192 
193 	return ret;
194 }
195 
196 struct gud_connector_get_edid_ctx {
197 	void *buf;
198 	size_t len;
199 	bool edid_override;
200 };
201 
gud_connector_get_edid_block(void * data,u8 * buf,unsigned int block,size_t len)202 static 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 
gud_connector_get_modes(struct drm_connector * connector)217 static 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_objs(*reqmodes, GUD_CONNECTOR_MAX_NUM_MODES);
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 	}
276 out:
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 
gud_connector_atomic_check(struct drm_connector * connector,struct drm_atomic_commit * state)287 static int gud_connector_atomic_check(struct drm_connector *connector,
288 				      struct drm_atomic_commit *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 
317 static 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 
gud_connector_late_register(struct drm_connector * connector)323 static 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 
gud_connector_early_unregister(struct drm_connector * connector)333 static 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 
gud_connector_destroy(struct drm_connector * connector)341 static 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 
gud_connector_reset(struct drm_connector * connector)350 static 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 
362 static 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  */
gud_connector_add_tv_mode(struct gud_device * gdrm,struct drm_connector * connector)377 static 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 		char *mode = &buf[i * GUD_CONNECTOR_TV_MODE_NAME_LEN];
401 
402 		if (!memchr(mode, '\0', GUD_CONNECTOR_TV_MODE_NAME_LEN)) {
403 			ret = -EIO;
404 			goto free;
405 		}
406 
407 		modes[i] = mode;
408 	}
409 
410 	ret = drm_mode_create_tv_properties_legacy(connector->dev, num_modes, modes);
411 free:
412 	kfree(buf);
413 	if (ret < 0)
414 		gud_conn_err(connector, "Failed to add TV modes", ret);
415 
416 	return ret;
417 }
418 
419 static struct drm_property *
gud_connector_property_lookup(struct drm_connector * connector,u16 prop)420 gud_connector_property_lookup(struct drm_connector *connector, u16 prop)
421 {
422 	struct drm_mode_config *config = &connector->dev->mode_config;
423 
424 	switch (prop) {
425 	case GUD_PROPERTY_TV_LEFT_MARGIN:
426 		return config->tv_left_margin_property;
427 	case GUD_PROPERTY_TV_RIGHT_MARGIN:
428 		return config->tv_right_margin_property;
429 	case GUD_PROPERTY_TV_TOP_MARGIN:
430 		return config->tv_top_margin_property;
431 	case GUD_PROPERTY_TV_BOTTOM_MARGIN:
432 		return config->tv_bottom_margin_property;
433 	case GUD_PROPERTY_TV_MODE:
434 		return config->legacy_tv_mode_property;
435 	case GUD_PROPERTY_TV_BRIGHTNESS:
436 		return config->tv_brightness_property;
437 	case GUD_PROPERTY_TV_CONTRAST:
438 		return config->tv_contrast_property;
439 	case GUD_PROPERTY_TV_FLICKER_REDUCTION:
440 		return config->tv_flicker_reduction_property;
441 	case GUD_PROPERTY_TV_OVERSCAN:
442 		return config->tv_overscan_property;
443 	case GUD_PROPERTY_TV_SATURATION:
444 		return config->tv_saturation_property;
445 	case GUD_PROPERTY_TV_HUE:
446 		return config->tv_hue_property;
447 	default:
448 		return ERR_PTR(-EINVAL);
449 	}
450 }
451 
gud_connector_tv_state_val(u16 prop,struct drm_tv_connector_state * state)452 static unsigned int *gud_connector_tv_state_val(u16 prop, struct drm_tv_connector_state *state)
453 {
454 	switch (prop) {
455 	case GUD_PROPERTY_TV_LEFT_MARGIN:
456 		return &state->margins.left;
457 	case GUD_PROPERTY_TV_RIGHT_MARGIN:
458 		return &state->margins.right;
459 	case GUD_PROPERTY_TV_TOP_MARGIN:
460 		return &state->margins.top;
461 	case GUD_PROPERTY_TV_BOTTOM_MARGIN:
462 		return &state->margins.bottom;
463 	case GUD_PROPERTY_TV_MODE:
464 		return &state->legacy_mode;
465 	case GUD_PROPERTY_TV_BRIGHTNESS:
466 		return &state->brightness;
467 	case GUD_PROPERTY_TV_CONTRAST:
468 		return &state->contrast;
469 	case GUD_PROPERTY_TV_FLICKER_REDUCTION:
470 		return &state->flicker_reduction;
471 	case GUD_PROPERTY_TV_OVERSCAN:
472 		return &state->overscan;
473 	case GUD_PROPERTY_TV_SATURATION:
474 		return &state->saturation;
475 	case GUD_PROPERTY_TV_HUE:
476 		return &state->hue;
477 	default:
478 		return ERR_PTR(-EINVAL);
479 	}
480 }
481 
gud_connector_add_properties(struct gud_device * gdrm,struct gud_connector * gconn)482 static int gud_connector_add_properties(struct gud_device *gdrm, struct gud_connector *gconn)
483 {
484 	struct drm_connector *connector = &gconn->connector;
485 	struct drm_device *drm = &gdrm->drm;
486 	struct gud_property_req *properties;
487 	unsigned int i, num_properties;
488 	int ret;
489 
490 	properties = kzalloc_objs(*properties, GUD_CONNECTOR_PROPERTIES_MAX_NUM);
491 	if (!properties)
492 		return -ENOMEM;
493 
494 	ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_PROPERTIES, connector->index,
495 			  properties, GUD_CONNECTOR_PROPERTIES_MAX_NUM * sizeof(*properties));
496 	if (ret <= 0)
497 		goto out;
498 	if (ret % sizeof(*properties)) {
499 		ret = -EIO;
500 		goto out;
501 	}
502 
503 	num_properties = ret / sizeof(*properties);
504 	ret = 0;
505 
506 	gconn->properties = kcalloc(num_properties, sizeof(*gconn->properties), GFP_KERNEL);
507 	if (!gconn->properties) {
508 		ret = -ENOMEM;
509 		goto out;
510 	}
511 
512 	for (i = 0; i < num_properties; i++) {
513 		u16 prop = le16_to_cpu(properties[i].prop);
514 		u64 val = le64_to_cpu(properties[i].val);
515 		struct drm_property *property;
516 		unsigned int *state_val;
517 
518 		drm_dbg(drm, "property: %u = %llu(0x%llx)\n", prop, val, val);
519 
520 		switch (prop) {
521 		case GUD_PROPERTY_TV_LEFT_MARGIN:
522 			fallthrough;
523 		case GUD_PROPERTY_TV_RIGHT_MARGIN:
524 			fallthrough;
525 		case GUD_PROPERTY_TV_TOP_MARGIN:
526 			fallthrough;
527 		case GUD_PROPERTY_TV_BOTTOM_MARGIN:
528 			ret = drm_mode_create_tv_margin_properties(drm);
529 			if (ret)
530 				goto out;
531 			break;
532 		case GUD_PROPERTY_TV_MODE:
533 			ret = gud_connector_add_tv_mode(gdrm, connector);
534 			if (ret)
535 				goto out;
536 			break;
537 		case GUD_PROPERTY_TV_BRIGHTNESS:
538 			fallthrough;
539 		case GUD_PROPERTY_TV_CONTRAST:
540 			fallthrough;
541 		case GUD_PROPERTY_TV_FLICKER_REDUCTION:
542 			fallthrough;
543 		case GUD_PROPERTY_TV_OVERSCAN:
544 			fallthrough;
545 		case GUD_PROPERTY_TV_SATURATION:
546 			fallthrough;
547 		case GUD_PROPERTY_TV_HUE:
548 			/* This is a no-op if already added. */
549 			ret = drm_mode_create_tv_properties_legacy(drm, 0, NULL);
550 			if (ret)
551 				goto out;
552 			break;
553 		case GUD_PROPERTY_BACKLIGHT_BRIGHTNESS:
554 			if (val > 100) {
555 				ret = -EINVAL;
556 				goto out;
557 			}
558 			gconn->initial_brightness = val;
559 			break;
560 		default:
561 			/* New ones might show up in future devices, skip those we don't know. */
562 			drm_dbg(drm, "Ignoring unknown property: %u\n", prop);
563 			continue;
564 		}
565 
566 		gconn->properties[gconn->num_properties++] = prop;
567 
568 		if (prop == GUD_PROPERTY_BACKLIGHT_BRIGHTNESS)
569 			continue; /* not a DRM property */
570 
571 		property = gud_connector_property_lookup(connector, prop);
572 		if (drm_WARN_ON(drm, IS_ERR(property)))
573 			continue;
574 
575 		state_val = gud_connector_tv_state_val(prop, &gconn->initial_tv_state);
576 		if (drm_WARN_ON(drm, IS_ERR(state_val)))
577 			continue;
578 
579 		*state_val = val;
580 		drm_object_attach_property(&connector->base, property, 0);
581 	}
582 out:
583 	kfree(properties);
584 
585 	return ret;
586 }
587 
gud_connector_fill_properties(struct drm_connector_state * connector_state,struct gud_property_req * properties)588 int gud_connector_fill_properties(struct drm_connector_state *connector_state,
589 				  struct gud_property_req *properties)
590 {
591 	struct gud_connector *gconn = to_gud_connector(connector_state->connector);
592 	unsigned int i;
593 
594 	for (i = 0; i < gconn->num_properties; i++) {
595 		u16 prop = gconn->properties[i];
596 		u64 val;
597 
598 		if (prop == GUD_PROPERTY_BACKLIGHT_BRIGHTNESS) {
599 			val = connector_state->tv.brightness;
600 		} else {
601 			unsigned int *state_val;
602 
603 			state_val = gud_connector_tv_state_val(prop, &connector_state->tv);
604 			if (drm_WARN_ON_ONCE(connector_state->connector->dev, IS_ERR(state_val)))
605 				return PTR_ERR(state_val);
606 
607 			val = *state_val;
608 		}
609 
610 		properties[i].prop = cpu_to_le16(prop);
611 		properties[i].val = cpu_to_le64(val);
612 	}
613 
614 	return gconn->num_properties;
615 }
616 
617 static const struct drm_encoder_funcs gud_drm_simple_encoder_funcs_cleanup = {
618 	.destroy = drm_encoder_cleanup,
619 };
620 
gud_connector_create(struct gud_device * gdrm,unsigned int index,struct gud_connector_descriptor_req * desc)621 static int gud_connector_create(struct gud_device *gdrm, unsigned int index,
622 				struct gud_connector_descriptor_req *desc)
623 {
624 	struct drm_device *drm = &gdrm->drm;
625 	struct gud_connector *gconn;
626 	struct drm_connector *connector;
627 	int ret, connector_type;
628 	u32 flags;
629 
630 	gconn = kzalloc_obj(*gconn);
631 	if (!gconn)
632 		return -ENOMEM;
633 
634 	INIT_WORK(&gconn->backlight_work, gud_connector_backlight_update_status_work);
635 	gconn->initial_brightness = -ENODEV;
636 	flags = le32_to_cpu(desc->flags);
637 	connector = &gconn->connector;
638 
639 	drm_dbg(drm, "Connector: index=%u type=%u flags=0x%x\n", index, desc->connector_type, flags);
640 
641 	switch (desc->connector_type) {
642 	case GUD_CONNECTOR_TYPE_PANEL:
643 		connector_type = DRM_MODE_CONNECTOR_USB;
644 		break;
645 	case GUD_CONNECTOR_TYPE_VGA:
646 		connector_type = DRM_MODE_CONNECTOR_VGA;
647 		break;
648 	case GUD_CONNECTOR_TYPE_DVI:
649 		connector_type = DRM_MODE_CONNECTOR_DVID;
650 		break;
651 	case GUD_CONNECTOR_TYPE_COMPOSITE:
652 		connector_type = DRM_MODE_CONNECTOR_Composite;
653 		break;
654 	case GUD_CONNECTOR_TYPE_SVIDEO:
655 		connector_type = DRM_MODE_CONNECTOR_SVIDEO;
656 		break;
657 	case GUD_CONNECTOR_TYPE_COMPONENT:
658 		connector_type = DRM_MODE_CONNECTOR_Component;
659 		break;
660 	case GUD_CONNECTOR_TYPE_DISPLAYPORT:
661 		connector_type = DRM_MODE_CONNECTOR_DisplayPort;
662 		break;
663 	case GUD_CONNECTOR_TYPE_HDMI:
664 		connector_type = DRM_MODE_CONNECTOR_HDMIA;
665 		break;
666 	default: /* future types */
667 		connector_type = DRM_MODE_CONNECTOR_USB;
668 		break;
669 	}
670 
671 	drm_connector_helper_add(connector, &gud_connector_helper_funcs);
672 	ret = drm_connector_init(drm, connector, &gud_connector_funcs, connector_type);
673 	if (ret) {
674 		kfree(connector);
675 		return ret;
676 	}
677 
678 	if (drm_WARN_ON(drm, connector->index != index))
679 		return -EINVAL;
680 
681 	if (flags & GUD_CONNECTOR_FLAGS_POLL_STATUS)
682 		connector->polled = (DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT);
683 	if (flags & GUD_CONNECTOR_FLAGS_INTERLACE)
684 		connector->interlace_allowed = true;
685 	if (flags & GUD_CONNECTOR_FLAGS_DOUBLESCAN)
686 		connector->doublescan_allowed = true;
687 
688 	ret = gud_connector_add_properties(gdrm, gconn);
689 	if (ret) {
690 		gud_conn_err(connector, "Failed to add properties", ret);
691 		return ret;
692 	}
693 
694 	gconn->encoder.possible_crtcs = drm_crtc_mask(&gdrm->crtc);
695 	ret = drm_encoder_init(drm, &gconn->encoder, &gud_drm_simple_encoder_funcs_cleanup,
696 			       DRM_MODE_ENCODER_NONE, NULL);
697 	if (ret)
698 		return ret;
699 
700 	return drm_connector_attach_encoder(connector, &gconn->encoder);
701 }
702 
gud_get_connectors(struct gud_device * gdrm)703 int gud_get_connectors(struct gud_device *gdrm)
704 {
705 	struct gud_connector_descriptor_req *descs;
706 	unsigned int i, num_connectors;
707 	int ret;
708 
709 	descs = kmalloc_objs(*descs, GUD_CONNECTORS_MAX_NUM);
710 	if (!descs)
711 		return -ENOMEM;
712 
713 	ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTORS, 0,
714 			  descs, GUD_CONNECTORS_MAX_NUM * sizeof(*descs));
715 	if (ret < 0)
716 		goto free;
717 	if (!ret || ret % sizeof(*descs)) {
718 		ret = -EIO;
719 		goto free;
720 	}
721 
722 	num_connectors = ret / sizeof(*descs);
723 
724 	for (i = 0; i < num_connectors; i++) {
725 		ret = gud_connector_create(gdrm, i, &descs[i]);
726 		if (ret)
727 			goto free;
728 	}
729 free:
730 	kfree(descs);
731 
732 	return ret;
733 }
734