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/drm_simple_kms_helper.h>
20 #include <drm/gud.h>
21 
22 #include "gud_internal.h"
23 
24 struct 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 
to_gud_connector(struct drm_connector * connector)44 static inline struct gud_connector *to_gud_connector(struct drm_connector *connector)
45 {
46 	return container_of(connector, struct gud_connector, connector);
47 }
48 
gud_conn_err(struct drm_connector * connector,const char * msg,int ret)49 static 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  */
gud_connector_backlight_update_status_work(struct work_struct * work)59 static 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;
80 retry:
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);
91 out:
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);
102 exit:
103 	drm_dev_exit(idx);
104 
105 	if (ret)
106 		dev_err(drm->dev, "Failed to update backlight, err=%d\n", ret);
107 }
108 
gud_connector_backlight_update_status(struct backlight_device * bd)109 static 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 
120 static const struct backlight_ops gud_connector_backlight_ops = {
121 	.update_status	= gud_connector_backlight_update_status,
122 };
123 
gud_connector_backlight_register(struct gud_connector * gconn)124 static 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 
gud_connector_detect(struct drm_connector * connector,struct drm_modeset_acquire_ctx * ctx,bool force)152 static 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;
191 exit:
192 	drm_dev_exit(idx);
193 
194 	return ret;
195 }
196 
197 struct gud_connector_get_edid_ctx {
198 	void *buf;
199 	size_t len;
200 	bool edid_override;
201 };
202 
gud_connector_get_edid_block(void * data,u8 * buf,unsigned int block,size_t len)203 static 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 
gud_connector_get_modes(struct drm_connector * connector)218 static 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 	struct edid *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 		edid = drm_do_get_edid(connector, gud_connector_get_edid_block, &edid_ctx);
242 	}
243 
244 	kfree(edid_ctx.buf);
245 	drm_connector_update_edid_property(connector, edid);
246 
247 	if (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 	}
277 out:
278 	if (!num_modes)
279 		num_modes = drm_add_edid_modes(connector, edid);
280 
281 	kfree(reqmodes);
282 	kfree(edid);
283 	drm_dev_exit(idx);
284 
285 	return num_modes;
286 }
287 
gud_connector_atomic_check(struct drm_connector * connector,struct drm_atomic_state * state)288 static 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.mode != new_state->tv.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 
318 static 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 
gud_connector_late_register(struct drm_connector * connector)324 static 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 
gud_connector_early_unregister(struct drm_connector * connector)334 static 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 
gud_connector_destroy(struct drm_connector * connector)342 static 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 
gud_connector_reset(struct drm_connector * connector)351 static 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_reset(connector);
359 	if (gconn->initial_brightness >= 0)
360 		connector->state->tv.brightness = gconn->initial_brightness;
361 }
362 
363 static 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 are
375  * driver specific. This means that if more than one connector uses tv.mode,
376  * the enum names has to be the same.
377  */
gud_connector_add_tv_mode(struct gud_device * gdrm,struct drm_connector * connector)378 static 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(connector->dev, num_modes, modes);
404 free:
405 	kfree(buf);
406 	if (ret < 0)
407 		gud_conn_err(connector, "Failed to add TV modes", ret);
408 
409 	return ret;
410 }
411 
412 static struct drm_property *
gud_connector_property_lookup(struct drm_connector * connector,u16 prop)413 gud_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->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 
gud_connector_tv_state_val(u16 prop,struct drm_tv_connector_state * state)445 static 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->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 
gud_connector_add_properties(struct gud_device * gdrm,struct gud_connector * gconn)475 static 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(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 	}
575 out:
576 	kfree(properties);
577 
578 	return ret;
579 }
580 
gud_connector_fill_properties(struct drm_connector_state * connector_state,struct gud_property_req * properties)581 int 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 
gud_connector_create(struct gud_device * gdrm,unsigned int index,struct gud_connector_descriptor_req * desc)610 static 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 
gud_get_connectors(struct gud_device * gdrm)700 int 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 	}
726 free:
727 	kfree(descs);
728 
729 	return ret;
730 }
731